From 2bcac623202d71f23687c3fe5035b0ab8a1118bf Mon Sep 17 00:00:00 2001 From: chuckcchen Date: Fri, 4 Sep 2026 14:51:35 +0800 Subject: [PATCH 1/8] docs(makers-frameworks): add the web framework support matrix Which framework a request names decides seven things: the platform adapter, the scaffold command, the preview asset-prefix option, the build output, the 404 convention, the version floor, and the unsupported features. Nothing carried them before, so the adapter in particular was left to memory. That one is the expensive gap. `edgeone makers dev` runs the framework's own dev server and never loads the platform adapter, so a full-stack project missing one previews perfectly and deploys broken, with every gate green. Astro, React Router, SvelteKit, TanStack Start and Vike each need one; Next.js and Nuxt are handled by the builder directly. The router SKILL.md also carries the profiles as a fenced JSON block between HTML comment markers, so a host can read the adapter package, where it is wired, and whether a static build needs it, without a second copy of the same table drifting out of sync with the prose. --- AGENTS.md | 1 + CLAUDE.md | 1 + README.md | 4 + SKILL.md | 1 + _meta.json | 11 + codex/makers-frameworks.md | 214 ++++++++++++++++++ cursor/rules/makers-frameworks.mdc | 214 ++++++++++++++++++ skills/edgeone-makers-tools/SKILL.md | 5 + .../references/makers-frameworks/SKILL.md | 214 ++++++++++++++++++ .../makers-frameworks/references/astro.md | 138 +++++++++++ .../makers-frameworks/references/nextjs.md | 82 +++++++ .../makers-frameworks/references/nuxt.md | 118 ++++++++++ .../references/other-frameworks.md | 79 +++++++ .../references/react-router.md | 159 +++++++++++++ .../references/static-generators.md | 82 +++++++ .../makers-frameworks/references/sveltekit.md | 155 +++++++++++++ .../references/tanstack-start.md | 146 ++++++++++++ .../makers-frameworks/references/vike.md | 129 +++++++++++ .../makers-frameworks/references/vite-spa.md | 81 +++++++ 19 files changed, 1834 insertions(+) create mode 100644 codex/makers-frameworks.md create mode 100644 cursor/rules/makers-frameworks.mdc create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/astro.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/other-frameworks.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/static-generators.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md create mode 100644 skills/edgeone-makers-tools/references/makers-frameworks/references/vite-spa.md diff --git a/AGENTS.md b/AGENTS.md index 4b42dae..e3be1d7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,6 +4,7 @@ When you need EdgeOne Makers platform development guidance, read the matching Sk | Task | Read | |------|------| +| Web framework support — which adapter, build output, 404, and unsupported features (Next.js, Nuxt, Astro, SvelteKit, React Router, TanStack Start, Vike, Vite, Hugo) | skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md | | AI Agent development (DeepAgents, LangGraph, Claude SDK, OpenAI Agents, CrewAI) | skills/edgeone-makers-tools/references/makers-agents/SKILL.md | | Migrate existing agent project to EdgeOne Makers format | skills/edgeone-makers-tools/references/makers-migration/SKILL.md | | Deploy project to EdgeOne | skills/edgeone-makers-tools/references/makers-deploy/SKILL.md | diff --git a/CLAUDE.md b/CLAUDE.md index b34fa99..ff5a257 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,6 +4,7 @@ When you need EdgeOne Makers platform development guidance, read the matching Sk | Task | Read | |------|------| +| Web framework support — which adapter, build output, 404, and unsupported features (Next.js, Nuxt, Astro, SvelteKit, React Router, TanStack Start, Vike, Vite, Hugo) | skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md | | AI Agent development (DeepAgents, LangGraph, Claude SDK, OpenAI Agents, CrewAI) | skills/edgeone-makers-tools/references/makers-agents/SKILL.md | | Migrate existing agent project to EdgeOne Makers format | skills/edgeone-makers-tools/references/makers-migration/SKILL.md | | Deploy project to EdgeOne | skills/edgeone-makers-tools/references/makers-deploy/SKILL.md | diff --git a/README.md b/README.md index 3d0d355..5eca962 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ The single `edgeone-makers-tools` skill routes to these capabilities (each lives | Capability | Description | |-------|-------------| +| `makers-frameworks` | Web framework support matrix — platform adapters, build output, 404 conventions | | `makers-agents` | AI Agent development (DeepAgents, LangGraph, Claude SDK, OpenAI Agents, CrewAI) | | `makers-deploy` | Deploy projects to EdgeOne | | `makers-edge-functions` | Edge Functions (V8 lightweight runtime) | @@ -100,6 +101,9 @@ skills/ └── edgeone-makers-tools/ ├── SKILL.md # Router — matches the task, loads a capability below └── references/ + ├── makers-frameworks/ # Web framework support matrix + │ ├── SKILL.md # Adapter table + machine-readable profiles + │ └── references/ # One document per framework ├── makers-agents/ # AI Agent development │ ├── SKILL.md # Decision tree, red lines, framework routing │ └── references/ diff --git a/SKILL.md b/SKILL.md index aeb684a..783444d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -20,6 +20,7 @@ When you need EdgeOne Makers platform development guidance, read the matching Sk | Task | Read | |------|------| +| Web framework support — which adapter, build output, 404, and unsupported features (Next.js, Nuxt, Astro, SvelteKit, React Router, TanStack Start, Vike, Vite, Hugo) | skills/makers-frameworks/SKILL.md | | AI Agent development (DeepAgents, LangGraph, Claude SDK, OpenAI Agents, CrewAI) | skills/makers-agents/SKILL.md | | Deploy project to EdgeOne | skills/makers-deploy/SKILL.md | | Edge Functions (V8 lightweight functions) | skills/makers-edge-functions/SKILL.md | diff --git a/_meta.json b/_meta.json index 7098cd8..4631755 100644 --- a/_meta.json +++ b/_meta.json @@ -37,6 +37,17 @@ "skills/edgeone-makers-tools/references/makers-deploy/references/command-reference.md", "skills/edgeone-makers-tools/references/makers-edge-functions/SKILL.md", "skills/edgeone-makers-tools/references/makers-env-adaption/SKILL.md", + "skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/astro.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/other-frameworks.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/static-generators.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md", + "skills/edgeone-makers-tools/references/makers-frameworks/references/vite-spa.md", "skills/edgeone-makers-tools/references/makers-middleware/SKILL.md", "skills/edgeone-makers-tools/references/makers-migration/SKILL.md", "skills/edgeone-makers-tools/references/makers-migration/references/api-route-to-makers.md", diff --git a/codex/makers-frameworks.md b/codex/makers-frameworks.md new file mode 100644 index 0000000..c9e8dad --- /dev/null +++ b/codex/makers-frameworks.md @@ -0,0 +1,214 @@ +--- +name: edgeone-makers-frameworks +description: >- + Web framework support matrix for EdgeOne Makers — which platform adapter each + full-stack framework needs, where it plugs in, the build output directory, the + preview asset-prefix option, the 404 convention, version floors, and the + features the platform does not support yet. Read this whenever a request names + a web framework: Next.js, Nuxt, Astro, React Router, SvelteKit, TanStack Start, + Vike, Vite / React / Vue, Hugo, or any other frontend framework. +pathPatterns: + - astro.config.mjs + - astro.config.js + - astro.config.ts + - svelte.config.js + - svelte.config.ts + - vite.config.js + - vite.config.ts + - vite.config.mjs + - next.config.js + - next.config.mjs + - nuxt.config.ts + - nuxt.config.js + - react-router.config.ts +validate: + - pattern: "@astrojs/(?:vercel|netlify|cloudflare|node)" + message: "this is another platform's Astro adapter. EdgeOne Makers needs @edgeone/astro; a foreign adapter builds output this platform cannot serve." + - pattern: "@sveltejs/adapter-(?:vercel|netlify|cloudflare|node|auto)" + message: "this is not the EdgeOne SvelteKit adapter. Use @edgeone/sveltekit; adapter-auto cannot detect this platform and adapter-static only works for a fully prerendered site." + - pattern: "serverWrapper" + message: "serverWrapper is the @edgeone/vite 1.x API. Version 2.x discovers the framework entry through the Universal Deploy protocol, so the option is both unnecessary and ignored." + - pattern: "(?:async\\s+)?(?:redirects|rewrites)\\s*(?:\\(|:)" + message: "this platform does not run Next.js redirects or rewrites. Declare them in edgeone.json instead, which is the only place they take effect." + - pattern: "nitro(?:V2)?Plugin|@tanstack/start-plugin-nitro" + message: "the EdgeOne TanStack Start adapter cannot be used alongside a Nitro plugin. Drop the Nitro plugin; an official preset is not published yet." +metadata: + author: edgeone + version: "1.0.0" +--- + +# Web Frameworks + +Which framework a request names decides seven things and nothing else. Every framework +runs the same flow — prepare the workspace, write files, install, start the preview, +verify — and differs only in the values below. + +| Slot | Why it matters | +|------|----------------| +| Platform adapter | Without it, `deploy` produces output the platform cannot serve. **The preview does not catch this.** | +| Scaffold command | One command holds both the structure and a working version set | +| Preview asset-prefix option | The single option that moves framework-emitted asset URLs | +| Build command + output directory | What `edgeone.json` must declare when it is not the default | +| 404 convention | Different file in every framework | +| Version floor | Below it the platform's adapter or builder does not apply | +| Unsupported features | Generated code that uses one produces a broken site, not an error | + +## The adapter is a deploy-time contract, not a preview-time one + +`edgeone makers dev` starts **the framework's own dev server** (it reads `devCommand` +from `edgeone.json`, falling back to the `dev` script in `package.json`). The platform +adapter takes no part in that. `edgeone makers deploy` is different: it runs the build +and expects the adapter to have written platform output. + +So a project that needs an adapter and does not have one **previews perfectly and +deploys broken**. Every gate is green. This is the single most expensive mistake +available in this skill, and it is why the adapter column below comes first. + +Five frameworks need one: Astro, React Router, SvelteKit, TanStack Start, Vike. +Next.js and Nuxt are supported by the builder directly and need none. + +## Read the one document for the framework in the request + +| Framework | Read | +|-----------|------| +| Astro | [references/astro.md](references/astro.md) | +| React Router v7 | [references/react-router.md](references/react-router.md) | +| SvelteKit | [references/sveltekit.md](references/sveltekit.md) | +| TanStack Start | [references/tanstack-start.md](references/tanstack-start.md) | +| Vike | [references/vike.md](references/vike.md) | +| Next.js | [references/nextjs.md](references/nextjs.md) | +| Nuxt | [references/nuxt.md](references/nuxt.md) | +| Vite / React / Vue single-page app | [references/vite-spa.md](references/vite-spa.md) | +| Hugo and other static site generators | [references/static-generators.md](references/static-generators.md) | +| Anything else (Docusaurus, Angular, Gatsby, Hexo, Qwik, Remix, Solid) | [references/other-frameworks.md](references/other-frameworks.md) | + +Load one. Loading the whole set is 10 documents to answer a question about one framework. + +## Custom 404 pages + +| Project shape | Where the 404 lives | +|---------------|---------------------| +| Static site generator | any `404.html` in the build output directory | +| Single-page app | a catch-all client route. **Do not** put `404.html` at the output root — it shadows the client router | +| Next.js App Router | `app/not-found.tsx` | +| Nuxt | `app/error.vue` | +| Astro | `src/pages/404.astro` | +| React Router v7 | export `ErrorBoundary` from `app/root.tsx` | +| SvelteKit | `src/routes/+error.svelte`, or one per route subtree | + +## Machine-readable profiles + +The host reads this block to decide which adapter a project is missing, which package to +add to the first install, and which output directory to expect. It is the same data the +documents above describe in prose — kept here in one parseable place so the compatibility +lint and the install warmup do not each carry their own copy. + +Keep it in sync with the per-framework documents. + +- `detect` names the dependencies that prove the framework is in use. +- `adapter.configFiles` is where the adapter gets wired in. +- `adapter.required` is `always` when the framework cannot build without an adapter, or + `server-output` when a fully static build needs none. +- `serverOutput` answers "does this project render on a server", which is often a + *different file* from the one the adapter goes in — React Router declares `ssr` in + `react-router.config.ts` while its adapter is a `vite.config.ts` plugin. `default` + mirrors the framework's own default when nothing says otherwise, and the pattern named + for the other mode is what overrides it. + + +```json +[ + { + "id": "astro", + "label": "Astro", + "detect": ["astro"], + "adapter": { + "package": "@edgeone/astro", + "configFiles": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], + "required": "server-output" + }, + "serverOutput": { + "files": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], + "default": "static", + "serverPattern": "output\\s*:\\s*['\"](?:server|hybrid)['\"]" + }, + "outputDirectory": ".edgeone", + "unsupported": ["Image component optimization", "platform ISR"] + }, + { + "id": "react-router", + "label": "React Router v7", + "detect": ["@react-router/dev"], + "adapter": { + "package": "@edgeone/react-router", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "server-output" + }, + "serverOutput": { + "files": ["react-router.config.ts", "react-router.config.js"], + "default": "server", + "staticPattern": "ssr\\s*:\\s*false" + }, + "outputDirectory": "build", + "unsupported": [] + }, + { + "id": "sveltekit", + "label": "SvelteKit", + "detect": ["@sveltejs/kit"], + "adapter": { + "package": "@edgeone/sveltekit", + "configFiles": ["svelte.config.js", "svelte.config.ts"], + "required": "always" + }, + "outputDirectory": "", + "unsupported": ["observability"] + }, + { + "id": "tanstack-start", + "label": "TanStack Start", + "detect": ["@tanstack/react-start", "@tanstack/solid-start"], + "adapter": { + "package": "@edgeone/tanstack-start", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "always" + }, + "outputDirectory": "", + "unsupported": ["Nitro plugins alongside the adapter"] + }, + { + "id": "vike", + "label": "Vike", + "detect": ["vike"], + "adapter": { + "package": "@edgeone/vite", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "server-output" + }, + "serverOutput": { + "files": ["vite.config.ts", "vite.config.js", "vite.config.mjs", "pages/+config.js", "pages/+config.ts"], + "default": "server", + "staticPattern": "prerender\\s*:\\s*true" + }, + "outputDirectory": "dist", + "unsupported": ["server-side API routes — put them in cloud-functions/ instead"] + }, + { + "id": "nextjs", + "label": "Next.js", + "detect": ["next"], + "adapter": null, + "outputDirectory": ".next", + "unsupported": ["redirects and rewrites in next.config — declare them in edgeone.json"] + }, + { + "id": "nuxt", + "label": "Nuxt", + "detect": ["nuxt"], + "adapter": null, + "outputDirectory": ".output", + "unsupported": ["Nuxt Layers", "@nuxt/image optimization"] + } +] +``` + diff --git a/cursor/rules/makers-frameworks.mdc b/cursor/rules/makers-frameworks.mdc new file mode 100644 index 0000000..c9e8dad --- /dev/null +++ b/cursor/rules/makers-frameworks.mdc @@ -0,0 +1,214 @@ +--- +name: edgeone-makers-frameworks +description: >- + Web framework support matrix for EdgeOne Makers — which platform adapter each + full-stack framework needs, where it plugs in, the build output directory, the + preview asset-prefix option, the 404 convention, version floors, and the + features the platform does not support yet. Read this whenever a request names + a web framework: Next.js, Nuxt, Astro, React Router, SvelteKit, TanStack Start, + Vike, Vite / React / Vue, Hugo, or any other frontend framework. +pathPatterns: + - astro.config.mjs + - astro.config.js + - astro.config.ts + - svelte.config.js + - svelte.config.ts + - vite.config.js + - vite.config.ts + - vite.config.mjs + - next.config.js + - next.config.mjs + - nuxt.config.ts + - nuxt.config.js + - react-router.config.ts +validate: + - pattern: "@astrojs/(?:vercel|netlify|cloudflare|node)" + message: "this is another platform's Astro adapter. EdgeOne Makers needs @edgeone/astro; a foreign adapter builds output this platform cannot serve." + - pattern: "@sveltejs/adapter-(?:vercel|netlify|cloudflare|node|auto)" + message: "this is not the EdgeOne SvelteKit adapter. Use @edgeone/sveltekit; adapter-auto cannot detect this platform and adapter-static only works for a fully prerendered site." + - pattern: "serverWrapper" + message: "serverWrapper is the @edgeone/vite 1.x API. Version 2.x discovers the framework entry through the Universal Deploy protocol, so the option is both unnecessary and ignored." + - pattern: "(?:async\\s+)?(?:redirects|rewrites)\\s*(?:\\(|:)" + message: "this platform does not run Next.js redirects or rewrites. Declare them in edgeone.json instead, which is the only place they take effect." + - pattern: "nitro(?:V2)?Plugin|@tanstack/start-plugin-nitro" + message: "the EdgeOne TanStack Start adapter cannot be used alongside a Nitro plugin. Drop the Nitro plugin; an official preset is not published yet." +metadata: + author: edgeone + version: "1.0.0" +--- + +# Web Frameworks + +Which framework a request names decides seven things and nothing else. Every framework +runs the same flow — prepare the workspace, write files, install, start the preview, +verify — and differs only in the values below. + +| Slot | Why it matters | +|------|----------------| +| Platform adapter | Without it, `deploy` produces output the platform cannot serve. **The preview does not catch this.** | +| Scaffold command | One command holds both the structure and a working version set | +| Preview asset-prefix option | The single option that moves framework-emitted asset URLs | +| Build command + output directory | What `edgeone.json` must declare when it is not the default | +| 404 convention | Different file in every framework | +| Version floor | Below it the platform's adapter or builder does not apply | +| Unsupported features | Generated code that uses one produces a broken site, not an error | + +## The adapter is a deploy-time contract, not a preview-time one + +`edgeone makers dev` starts **the framework's own dev server** (it reads `devCommand` +from `edgeone.json`, falling back to the `dev` script in `package.json`). The platform +adapter takes no part in that. `edgeone makers deploy` is different: it runs the build +and expects the adapter to have written platform output. + +So a project that needs an adapter and does not have one **previews perfectly and +deploys broken**. Every gate is green. This is the single most expensive mistake +available in this skill, and it is why the adapter column below comes first. + +Five frameworks need one: Astro, React Router, SvelteKit, TanStack Start, Vike. +Next.js and Nuxt are supported by the builder directly and need none. + +## Read the one document for the framework in the request + +| Framework | Read | +|-----------|------| +| Astro | [references/astro.md](references/astro.md) | +| React Router v7 | [references/react-router.md](references/react-router.md) | +| SvelteKit | [references/sveltekit.md](references/sveltekit.md) | +| TanStack Start | [references/tanstack-start.md](references/tanstack-start.md) | +| Vike | [references/vike.md](references/vike.md) | +| Next.js | [references/nextjs.md](references/nextjs.md) | +| Nuxt | [references/nuxt.md](references/nuxt.md) | +| Vite / React / Vue single-page app | [references/vite-spa.md](references/vite-spa.md) | +| Hugo and other static site generators | [references/static-generators.md](references/static-generators.md) | +| Anything else (Docusaurus, Angular, Gatsby, Hexo, Qwik, Remix, Solid) | [references/other-frameworks.md](references/other-frameworks.md) | + +Load one. Loading the whole set is 10 documents to answer a question about one framework. + +## Custom 404 pages + +| Project shape | Where the 404 lives | +|---------------|---------------------| +| Static site generator | any `404.html` in the build output directory | +| Single-page app | a catch-all client route. **Do not** put `404.html` at the output root — it shadows the client router | +| Next.js App Router | `app/not-found.tsx` | +| Nuxt | `app/error.vue` | +| Astro | `src/pages/404.astro` | +| React Router v7 | export `ErrorBoundary` from `app/root.tsx` | +| SvelteKit | `src/routes/+error.svelte`, or one per route subtree | + +## Machine-readable profiles + +The host reads this block to decide which adapter a project is missing, which package to +add to the first install, and which output directory to expect. It is the same data the +documents above describe in prose — kept here in one parseable place so the compatibility +lint and the install warmup do not each carry their own copy. + +Keep it in sync with the per-framework documents. + +- `detect` names the dependencies that prove the framework is in use. +- `adapter.configFiles` is where the adapter gets wired in. +- `adapter.required` is `always` when the framework cannot build without an adapter, or + `server-output` when a fully static build needs none. +- `serverOutput` answers "does this project render on a server", which is often a + *different file* from the one the adapter goes in — React Router declares `ssr` in + `react-router.config.ts` while its adapter is a `vite.config.ts` plugin. `default` + mirrors the framework's own default when nothing says otherwise, and the pattern named + for the other mode is what overrides it. + + +```json +[ + { + "id": "astro", + "label": "Astro", + "detect": ["astro"], + "adapter": { + "package": "@edgeone/astro", + "configFiles": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], + "required": "server-output" + }, + "serverOutput": { + "files": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], + "default": "static", + "serverPattern": "output\\s*:\\s*['\"](?:server|hybrid)['\"]" + }, + "outputDirectory": ".edgeone", + "unsupported": ["Image component optimization", "platform ISR"] + }, + { + "id": "react-router", + "label": "React Router v7", + "detect": ["@react-router/dev"], + "adapter": { + "package": "@edgeone/react-router", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "server-output" + }, + "serverOutput": { + "files": ["react-router.config.ts", "react-router.config.js"], + "default": "server", + "staticPattern": "ssr\\s*:\\s*false" + }, + "outputDirectory": "build", + "unsupported": [] + }, + { + "id": "sveltekit", + "label": "SvelteKit", + "detect": ["@sveltejs/kit"], + "adapter": { + "package": "@edgeone/sveltekit", + "configFiles": ["svelte.config.js", "svelte.config.ts"], + "required": "always" + }, + "outputDirectory": "", + "unsupported": ["observability"] + }, + { + "id": "tanstack-start", + "label": "TanStack Start", + "detect": ["@tanstack/react-start", "@tanstack/solid-start"], + "adapter": { + "package": "@edgeone/tanstack-start", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "always" + }, + "outputDirectory": "", + "unsupported": ["Nitro plugins alongside the adapter"] + }, + { + "id": "vike", + "label": "Vike", + "detect": ["vike"], + "adapter": { + "package": "@edgeone/vite", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "server-output" + }, + "serverOutput": { + "files": ["vite.config.ts", "vite.config.js", "vite.config.mjs", "pages/+config.js", "pages/+config.ts"], + "default": "server", + "staticPattern": "prerender\\s*:\\s*true" + }, + "outputDirectory": "dist", + "unsupported": ["server-side API routes — put them in cloud-functions/ instead"] + }, + { + "id": "nextjs", + "label": "Next.js", + "detect": ["next"], + "adapter": null, + "outputDirectory": ".next", + "unsupported": ["redirects and rewrites in next.config — declare them in edgeone.json"] + }, + { + "id": "nuxt", + "label": "Nuxt", + "detect": ["nuxt"], + "adapter": null, + "outputDirectory": ".output", + "unsupported": ["Nuxt Layers", "@nuxt/image optimization"] + } +] +``` + diff --git a/skills/edgeone-makers-tools/SKILL.md b/skills/edgeone-makers-tools/SKILL.md index bf8c05f..cc9cf6b 100644 --- a/skills/edgeone-makers-tools/SKILL.md +++ b/skills/edgeone-makers-tools/SKILL.md @@ -24,6 +24,7 @@ When you need EdgeOne Makers platform development guidance, read the matching Sk | Task | Read | |------|------| +| Web framework support — which adapter, build output, 404, and unsupported features (Next.js, Nuxt, Astro, SvelteKit, React Router, TanStack Start, Vike, Vite, Hugo) | references/makers-frameworks/SKILL.md | | AI Agent development (DeepAgents, LangGraph, Claude SDK, OpenAI Agents, CrewAI) | references/makers-agents/SKILL.md | | Deploy project to EdgeOne | references/makers-deploy/SKILL.md | | Edge Functions (V8 lightweight functions) | references/makers-edge-functions/SKILL.md | @@ -36,3 +37,7 @@ When you need EdgeOne Makers platform development guidance, read the matching Sk | Environment adaptation (WorkBuddy / sandbox / CI) | references/makers-env-adaption/SKILL.md | ⚠️ Only read the Skill relevant to the current task. Do not load all skills at once. + +A full-stack framework that needs a platform adapter previews correctly and deploys +broken without one, and no gate catches it — so when a request names a framework, read +`makers-frameworks` before writing the config. diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md b/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md new file mode 100644 index 0000000..c9e8dad --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md @@ -0,0 +1,214 @@ +--- +name: edgeone-makers-frameworks +description: >- + Web framework support matrix for EdgeOne Makers — which platform adapter each + full-stack framework needs, where it plugs in, the build output directory, the + preview asset-prefix option, the 404 convention, version floors, and the + features the platform does not support yet. Read this whenever a request names + a web framework: Next.js, Nuxt, Astro, React Router, SvelteKit, TanStack Start, + Vike, Vite / React / Vue, Hugo, or any other frontend framework. +pathPatterns: + - astro.config.mjs + - astro.config.js + - astro.config.ts + - svelte.config.js + - svelte.config.ts + - vite.config.js + - vite.config.ts + - vite.config.mjs + - next.config.js + - next.config.mjs + - nuxt.config.ts + - nuxt.config.js + - react-router.config.ts +validate: + - pattern: "@astrojs/(?:vercel|netlify|cloudflare|node)" + message: "this is another platform's Astro adapter. EdgeOne Makers needs @edgeone/astro; a foreign adapter builds output this platform cannot serve." + - pattern: "@sveltejs/adapter-(?:vercel|netlify|cloudflare|node|auto)" + message: "this is not the EdgeOne SvelteKit adapter. Use @edgeone/sveltekit; adapter-auto cannot detect this platform and adapter-static only works for a fully prerendered site." + - pattern: "serverWrapper" + message: "serverWrapper is the @edgeone/vite 1.x API. Version 2.x discovers the framework entry through the Universal Deploy protocol, so the option is both unnecessary and ignored." + - pattern: "(?:async\\s+)?(?:redirects|rewrites)\\s*(?:\\(|:)" + message: "this platform does not run Next.js redirects or rewrites. Declare them in edgeone.json instead, which is the only place they take effect." + - pattern: "nitro(?:V2)?Plugin|@tanstack/start-plugin-nitro" + message: "the EdgeOne TanStack Start adapter cannot be used alongside a Nitro plugin. Drop the Nitro plugin; an official preset is not published yet." +metadata: + author: edgeone + version: "1.0.0" +--- + +# Web Frameworks + +Which framework a request names decides seven things and nothing else. Every framework +runs the same flow — prepare the workspace, write files, install, start the preview, +verify — and differs only in the values below. + +| Slot | Why it matters | +|------|----------------| +| Platform adapter | Without it, `deploy` produces output the platform cannot serve. **The preview does not catch this.** | +| Scaffold command | One command holds both the structure and a working version set | +| Preview asset-prefix option | The single option that moves framework-emitted asset URLs | +| Build command + output directory | What `edgeone.json` must declare when it is not the default | +| 404 convention | Different file in every framework | +| Version floor | Below it the platform's adapter or builder does not apply | +| Unsupported features | Generated code that uses one produces a broken site, not an error | + +## The adapter is a deploy-time contract, not a preview-time one + +`edgeone makers dev` starts **the framework's own dev server** (it reads `devCommand` +from `edgeone.json`, falling back to the `dev` script in `package.json`). The platform +adapter takes no part in that. `edgeone makers deploy` is different: it runs the build +and expects the adapter to have written platform output. + +So a project that needs an adapter and does not have one **previews perfectly and +deploys broken**. Every gate is green. This is the single most expensive mistake +available in this skill, and it is why the adapter column below comes first. + +Five frameworks need one: Astro, React Router, SvelteKit, TanStack Start, Vike. +Next.js and Nuxt are supported by the builder directly and need none. + +## Read the one document for the framework in the request + +| Framework | Read | +|-----------|------| +| Astro | [references/astro.md](references/astro.md) | +| React Router v7 | [references/react-router.md](references/react-router.md) | +| SvelteKit | [references/sveltekit.md](references/sveltekit.md) | +| TanStack Start | [references/tanstack-start.md](references/tanstack-start.md) | +| Vike | [references/vike.md](references/vike.md) | +| Next.js | [references/nextjs.md](references/nextjs.md) | +| Nuxt | [references/nuxt.md](references/nuxt.md) | +| Vite / React / Vue single-page app | [references/vite-spa.md](references/vite-spa.md) | +| Hugo and other static site generators | [references/static-generators.md](references/static-generators.md) | +| Anything else (Docusaurus, Angular, Gatsby, Hexo, Qwik, Remix, Solid) | [references/other-frameworks.md](references/other-frameworks.md) | + +Load one. Loading the whole set is 10 documents to answer a question about one framework. + +## Custom 404 pages + +| Project shape | Where the 404 lives | +|---------------|---------------------| +| Static site generator | any `404.html` in the build output directory | +| Single-page app | a catch-all client route. **Do not** put `404.html` at the output root — it shadows the client router | +| Next.js App Router | `app/not-found.tsx` | +| Nuxt | `app/error.vue` | +| Astro | `src/pages/404.astro` | +| React Router v7 | export `ErrorBoundary` from `app/root.tsx` | +| SvelteKit | `src/routes/+error.svelte`, or one per route subtree | + +## Machine-readable profiles + +The host reads this block to decide which adapter a project is missing, which package to +add to the first install, and which output directory to expect. It is the same data the +documents above describe in prose — kept here in one parseable place so the compatibility +lint and the install warmup do not each carry their own copy. + +Keep it in sync with the per-framework documents. + +- `detect` names the dependencies that prove the framework is in use. +- `adapter.configFiles` is where the adapter gets wired in. +- `adapter.required` is `always` when the framework cannot build without an adapter, or + `server-output` when a fully static build needs none. +- `serverOutput` answers "does this project render on a server", which is often a + *different file* from the one the adapter goes in — React Router declares `ssr` in + `react-router.config.ts` while its adapter is a `vite.config.ts` plugin. `default` + mirrors the framework's own default when nothing says otherwise, and the pattern named + for the other mode is what overrides it. + + +```json +[ + { + "id": "astro", + "label": "Astro", + "detect": ["astro"], + "adapter": { + "package": "@edgeone/astro", + "configFiles": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], + "required": "server-output" + }, + "serverOutput": { + "files": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], + "default": "static", + "serverPattern": "output\\s*:\\s*['\"](?:server|hybrid)['\"]" + }, + "outputDirectory": ".edgeone", + "unsupported": ["Image component optimization", "platform ISR"] + }, + { + "id": "react-router", + "label": "React Router v7", + "detect": ["@react-router/dev"], + "adapter": { + "package": "@edgeone/react-router", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "server-output" + }, + "serverOutput": { + "files": ["react-router.config.ts", "react-router.config.js"], + "default": "server", + "staticPattern": "ssr\\s*:\\s*false" + }, + "outputDirectory": "build", + "unsupported": [] + }, + { + "id": "sveltekit", + "label": "SvelteKit", + "detect": ["@sveltejs/kit"], + "adapter": { + "package": "@edgeone/sveltekit", + "configFiles": ["svelte.config.js", "svelte.config.ts"], + "required": "always" + }, + "outputDirectory": "", + "unsupported": ["observability"] + }, + { + "id": "tanstack-start", + "label": "TanStack Start", + "detect": ["@tanstack/react-start", "@tanstack/solid-start"], + "adapter": { + "package": "@edgeone/tanstack-start", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "always" + }, + "outputDirectory": "", + "unsupported": ["Nitro plugins alongside the adapter"] + }, + { + "id": "vike", + "label": "Vike", + "detect": ["vike"], + "adapter": { + "package": "@edgeone/vite", + "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "required": "server-output" + }, + "serverOutput": { + "files": ["vite.config.ts", "vite.config.js", "vite.config.mjs", "pages/+config.js", "pages/+config.ts"], + "default": "server", + "staticPattern": "prerender\\s*:\\s*true" + }, + "outputDirectory": "dist", + "unsupported": ["server-side API routes — put them in cloud-functions/ instead"] + }, + { + "id": "nextjs", + "label": "Next.js", + "detect": ["next"], + "adapter": null, + "outputDirectory": ".next", + "unsupported": ["redirects and rewrites in next.config — declare them in edgeone.json"] + }, + { + "id": "nuxt", + "label": "Nuxt", + "detect": ["nuxt"], + "adapter": null, + "outputDirectory": ".output", + "unsupported": ["Nuxt Layers", "@nuxt/image optimization"] + } +] +``` + diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/astro.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/astro.md new file mode 100644 index 0000000..9bdc943 --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/astro.md @@ -0,0 +1,138 @@ +# Astro + +## Contents + +- [The adapter](#the-adapter) +- [Scaffold](#scaffold) +- [Preview asset prefix](#preview-asset-prefix) +- [Build settings](#build-settings) +- [Rendering modes](#rendering-modes) +- [Incremental static regeneration](#incremental-static-regeneration) +- [404 page](#404-page) +- [Not supported yet](#not-supported-yet) +- [Feature support](#feature-support) + +Astro 4+ is supported, 5 recommended. The platform runtime is Node.js 22+. + +## The adapter + +Required whenever `output` is `server` or `hybrid`. A pure `output: 'static'` site builds +to plain files and needs none — but if you later add an API route or middleware, the +adapter becomes required and nothing in the preview will tell you. + +```bash +npm install @edgeone/astro +``` + +```javascript +// astro.config.mjs +import { defineConfig } from "astro/config"; +import edgeoneAdapter from "@edgeone/astro"; + +export default defineConfig({ + output: "server", + adapter: edgeoneAdapter(), +}); +``` + +Never install `@astrojs/vercel`, `@astrojs/netlify`, `@astrojs/cloudflare`, or +`@astrojs/node` — they emit output this platform cannot serve. + +### Adapter options + +| Option | Meaning | +|--------|---------| +| `outDir` | Build output directory. Defaults to `.edgeone` | +| `includeFiles` | Glob list of files to force into the server bundle, e.g. `["src/locales/**"]` | +| `excludeFiles` | Glob list to keep out of the server bundle, e.g. `["node_modules/.cache/**"]` | +| `isr` | Per-route revalidation, see below | + +## Scaffold + +```bash +npm create astro@latest . -- --yes --template minimal --install --no-git +``` + +## Preview asset prefix + +Astro's option is `base`. Read it from the environment so a deployment, which never sets +the variable, still serves the site from `/`: + +```javascript +export default defineConfig({ + base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX, + output: "server", + adapter: edgeoneAdapter(), +}); +``` + +Omit the key entirely when the variable is unset. Never write the prefix as a literal. + +## Build settings + +- Build command: `npm run build` +- Output directory: `.edgeone` (the adapter's default `outDir`) + +## Rendering modes + +Static export — no server runtime at all, so API routes under `src/pages/api/*` and +`src/middleware.ts` will not run: + +```javascript +export default defineConfig({ output: "static" }); +``` + +Server rendering, with per-page opt-out back to static: + +```javascript +export default defineConfig({ output: "server", adapter: edgeoneAdapter() }); +``` + +```astro +--- +// any page — prerender this one at build time +export const prerender = true; +--- +``` + +## Incremental static regeneration + +Astro has no page-level ISR export, so it is declared on the adapter. Only applies to +SSR routes that are not prerendered. + +```javascript +adapter: edgeoneAdapter({ + isr: { + routes: { + "/blog/**": { expiration: 60 }, + }, + }, +}), +``` + +## 404 page + +`src/pages/404.astro`. Astro picks it up at build time and serves it with a 404 status. + +## Not supported yet + +- **`` optimization** — the component renders, the optimization does not happen. +- **Platform ISR** as a platform feature is separate from the adapter `isr` option above; + do not expect console-level ISR controls. + +Both are on the roadmap; track `@edgeone/astro` releases rather than working around them. + +## Feature support + +| Feature | Supported | +|---------|-----------| +| Islands architecture | yes | +| Server-side rendering | yes | +| Static site generation | yes | +| Multiple UI frameworks in one project | yes | +| Content collections (MD, MDX) | yes | +| API routes | yes | +| Actions routes | yes | +| Middleware | yes, Astro's own | +| `` optimization | no | +| Platform ISR | no | diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md new file mode 100644 index 0000000..8044ebb --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md @@ -0,0 +1,82 @@ +# Next.js + +Next.js 13, 14, and 15 are supported, App Router and Pages Router both. The builder +handles Next.js directly — **no platform adapter, no plugin, nothing to install.** + +## Scaffold + +```bash +npx create-next-app@latest . --typescript --tailwind --app --eslint --use-npm --yes +``` + +## Preview asset prefix + +Next.js has two related options and only one of them is right here. Use `assetPrefix`, +which moves asset URLs while leaving routes at `/`. **Do not use `basePath`** — it moves +the routes too, which breaks the preview proxy and, if it survives into a deployment, +breaks the deployed site. + +```javascript +// next.config.js +const nextConfig = { + assetPrefix: process.env.EDGEONE_PREVIEW_ASSET_PREFIX, +}; + +export default nextConfig; +``` + +Reading it from the environment matters: a deployment never sets the variable, so the +value collapses to undefined and assets resolve from the root. + +## Build settings + +- Build command: `npm run build` +- Output directory: `.next` + +## Rendering modes + +Everything the framework offers works: server components, client components, static +generation, incremental static regeneration, streaming with Suspense, route handlers, +server actions, and middleware. + +```typescript +// app/blog/[slug]/page.tsx — ISR +export const revalidate = 60; + +export async function generateStaticParams() { + const posts = await fetchPosts(); + return posts.map((post) => ({ slug: post.slug })); +} +``` + +## 404 page + +`app/not-found.tsx` in the App Router, `pages/404.tsx` in the Pages Router. + +## Not supported + +- **`redirects` and `rewrites` in `next.config.js` do not run.** The platform does not + read them. Declare both in `edgeone.json`, which is the only place they take effect. + +```json +{ + "redirects": [{ "source": "/old", "destination": "/new", "statusCode": 301 }], + "rewrites": [{ "source": "/api/proxy/:path*", "destination": "/api/:path*" }] +} +``` + +## Feature support + +| Feature | Supported | +|---------|-----------| +| App Router | yes | +| Pages Router | yes | +| Server components | yes | +| Static generation | yes | +| Incremental static regeneration | yes | +| Streaming | yes | +| Route handlers | yes | +| Server actions | yes | +| Middleware | yes | +| Image optimization | yes | +| `next.config` redirects / rewrites | no — use `edgeone.json` | diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md new file mode 100644 index 0000000..ac7403f --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md @@ -0,0 +1,118 @@ +# Nuxt + +## Contents + +- [Scaffold](#scaffold) +- [Preview asset prefix](#preview-asset-prefix) +- [Build settings](#build-settings) +- [Rendering modes](#rendering-modes) +- [Server routes](#server-routes) +- [404 page](#404-page) +- [Not supported yet](#not-supported-yet) +- [Feature support](#feature-support) + +Nuxt 3 and 4 are supported. The builder handles Nuxt directly — **no platform adapter to +install.** Nitro's own preset detection does the work. + +## Scaffold + +```bash +npx nuxi@latest init . --packageManager npm --no-gitInit --force +``` + +## Preview asset prefix + +Nuxt's option is `app.baseURL`. It moves both routes and assets; the preview proxy +detects that from the dev server's redirect and adjusts, so no extra configuration is +needed. + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + app: { + baseURL: process.env.EDGEONE_PREVIEW_ASSET_PREFIX, + }, +}); +``` + +An unset variable leaves `baseURL` undefined and Nuxt falls back to `/`. + +## Build settings + +| Mode | Build command | Output directory | +|------|---------------|------------------| +| Server rendering (default) | `npm run build` | `.output` | +| Static generation | `npm run generate` | `.output/public` | + +## Rendering modes + +Server rendering is the default. Turn a route into a single-page app or prerender it +through `routeRules`: + +```typescript +export default defineNuxtConfig({ + routeRules: { + "/": { prerender: true }, + "/blog/**": { isr: 60 }, + "/admin/**": { ssr: false }, + }, +}); +``` + +Full static generation for the whole site: + +```typescript +export default defineNuxtConfig({ ssr: true, nitro: { prerender: { crawlLinks: true } } }); +``` + +## Server routes + +Nitro server routes under `server/api/` work: + +```typescript +// server/api/posts.get.ts +export default defineEventHandler(async () => { + const posts = await $fetch("https://api.example.com/posts"); + return posts; +}); +``` + +## 404 page + +`app/error.vue` (Nuxt 4) or `error.vue` at the project root (Nuxt 3). Check +`error.statusCode` to separate 404 from 500: + +```vue + + + +``` + +## Not supported yet + +- **Nuxt Layers.** `extends` in `nuxt.config.ts` does not resolve here. Inline whatever + the layer provided directly into the project. +- **`@nuxt/image` optimization.** The component renders and the image loads; the + optimization pipeline does not run. + +## Feature support + +| Feature | Supported | +|---------|-----------| +| Server-side rendering | yes | +| Static site generation | yes | +| Incremental static regeneration | yes | +| Hybrid rendering via `routeRules` | yes | +| Server routes (`server/api`) | yes | +| Auto-imports | yes | +| Nuxt modules | yes | +| Nuxt Layers | no | +| `@nuxt/image` optimization | no | diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/other-frameworks.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/other-frameworks.md new file mode 100644 index 0000000..434a3c4 --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/other-frameworks.md @@ -0,0 +1,79 @@ +# Other frameworks + +No framework allowlist exists. Deployment detects the framework, runs its build, and +uploads the output — so a framework's absence from this skill is not evidence that it +does not work. It means nobody wrote down its seven values yet. + +When a request names a framework not covered by a document in this skill, derive the +values instead of refusing: + +1. **Does it emit a server bundle?** If yes, it needs a platform adapter, and if + `@edgeone/` does not exist on npm, `@edgeone/vite` covers any + Vite-based framework through the Universal Deploy protocol. If it emits static files + only, no adapter is needed. +2. **Build command and output directory** — read them from the framework's own docs and + declare them in `edgeone.json` when they are not `npm run build` into a conventional + directory. +3. **Preview asset prefix** — nearly every framework has one option for this. It is + `base` in anything Vite-based, `baseURL` / `baseUrl` / `basePath` elsewhere. Read it + from `EDGEONE_PREVIEW_ASSET_PREFIX` and omit the key when unset. + +One preview attempt settles what the docs leave ambiguous. Build it and report what +happened rather than asking the user to pick a different framework. + +## Known values + +### Angular + +- Scaffold: `npx @angular/cli@latest new app --directory . --skip-git --defaults` +- Build command: `npm run build` +- Output directory: `dist//browser` for Angular 17+, `dist/` before that +- Adapter: none for a static build +- 404: a wildcard route `{ path: '**', component: NotFoundComponent }` plus the + single-page app rewrite in `edgeone.json` +- Preview asset prefix: `baseHref` in `angular.json`, or `--base-href` on the build + +### Gatsby + +- Scaffold: `npx gatsby new .` +- Build command: `npm run build` +- Output directory: `public` +- Adapter: none — Gatsby builds static output +- 404: `src/pages/404.js` +- Preview asset prefix: `pathPrefix` in `gatsby-config.js`, built with `--prefix-paths` + +### Remix (v2) + +- Build command: `npm run build` +- Output directory: `build/client` +- Adapter: Remix v2 needs a server adapter for SSR. Prefer migrating to React Router v7, + which has a first-party platform adapter — see + [react-router.md](react-router.md). +- 404: a splat route `app/routes/$.tsx` + +### Solid Start + +- Scaffold: `npm init solid@latest` +- Build command: `npm run build` +- Output directory: `.output/public` for a static preset +- Adapter: Vite-based, so `@edgeone/vite` for a server build +- 404: `src/routes/*404.tsx` + +### Qwik + +- Scaffold: `npm create qwik@latest` +- Build command: `npm run build` +- Output directory: `dist` +- Adapter: `@edgeone/vite` when server rendering +- 404: `src/routes/[...404]/index.tsx` + +### Preact / Lit / Alpine + +Vite single-page apps in every practical respect — see +[vite-spa.md](vite-spa.md), including the catch-all rewrite that client-side +routing requires. + +## When something genuinely does not work + +Report what failed with the output that proves it: the build log line, the lint rule ID, +the failing request. A limit you cannot cite is not a limit worth telling the user about. diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md new file mode 100644 index 0000000..b1acf92 --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md @@ -0,0 +1,159 @@ +# React Router v7 + +## Contents + +- [The adapter](#the-adapter) +- [Scaffold](#scaffold) +- [Preview asset prefix](#preview-asset-prefix) +- [Build settings](#build-settings) +- [Rendering modes](#rendering-modes) +- [Streaming](#streaming) +- [404 page](#404-page) +- [Feature support](#feature-support) + +React Router 7+ is supported with full-stack deployment. EdgeOne CLI must be 1.2.0 or +newer. Version 7 is a Vite-based framework, not just the routing library. + +## The adapter + +Required for server rendering. A project with `ssr: false` builds to static client output +and needs none — but adding a loader that must run on the server makes it required. + +```bash +npm install @edgeone/react-router +``` + +```typescript +// vite.config.ts +import { reactRouter } from "@react-router/dev/vite"; +import { defineConfig } from "vite"; +import { edgeoneAdapter } from "@edgeone/react-router"; + +export default defineConfig({ + plugins: [ + reactRouter(), + edgeoneAdapter(), + ], +}); +``` + +Note the **named** export `edgeoneAdapter`, called as a plugin. + +## Scaffold + +```bash +npx create-react-router@latest . --yes --no-git-init --install +``` + +## Preview asset prefix + +This is a Vite project, so the option is `base`: + +```typescript +export default defineConfig({ + base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX, + plugins: [reactRouter(), edgeoneAdapter()], +}); +``` + +Omit it when the variable is unset so the deployed site stays at `/`. + +## Build settings + +The output directory depends on the rendering mode, which is the one thing about this +framework that is easy to get wrong: + +| Mode | Build command | Output directory | +|------|---------------|------------------| +| Server rendering | `npm run build` | `build` | +| Static generation (`prerender`) | `npm run build` | `build/client` | +| Single-page app (`ssr: false`) | `npm run build` | `build/client` | + +## Rendering modes + +Server rendering — fetch in a `loader`: + +```typescript +// routes/post.tsx +import type { Route } from "./+types/post"; + +export async function loader({ params }: Route.LoaderArgs) { + const post = await fetchPost(params.id); + return { post }; +} + +export default function Post({ loaderData }: Route.ComponentProps) { + return ( +
+

{loaderData.post.title}

+
{loaderData.post.content}
+
+ ); +} +``` + +Static generation — list the routes to prerender in `react-router.config.ts`: + +```typescript +import type { Config } from "@react-router/dev/config"; + +export default { + async prerender() { + const posts = await fetchAllPosts(); + return ["/", "/about", ...posts.map((post) => `/blog/${post.slug}`)]; + }, +} satisfies Config; +``` + +Single-page app: + +```typescript +import type { Config } from "@react-router/dev/config"; + +export default { ssr: false } satisfies Config; +``` + +## Streaming + +Return promises from the loader and resolve them with `Await`: + +```typescript +import { Suspense } from "react"; +import { Await } from "react-router"; + +export async function loader() { + return { posts: fetchPosts(), weather: fetchWeather() }; +} + +export default function Dashboard({ loaderData }) { + return ( +
+ Loading posts…

}> + + {(posts) => } + +
+
+ ); +} +``` + +## 404 page + +Export an `ErrorBoundary` from the root route file `app/root.tsx`. React Router's built-in +error boundary mechanism catches unmatched routes, throws a 404 response, and renders +what the boundary returns. + +## Feature support + +| Feature | Supported | +|---------|-----------| +| Server-side rendering | yes | +| Static site generation | yes | +| Single-page app | yes | +| Route loaders | yes | +| Route actions | yes | +| Nested routes | yes | +| File-based routing | yes | +| Streaming | yes | +| Experimental features | partly | diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/static-generators.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/static-generators.md new file mode 100644 index 0000000..63d0dbb --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/static-generators.md @@ -0,0 +1,82 @@ +# Hugo and other static site generators + +Static site generators produce plain files. **No platform adapter, no `package.json` +required** — the build command and output directory are the entire integration. + +## Hugo + +```bash +hugo new site . --force +``` + +- Build command: `hugo --minify` +- Output directory: `public` + +Hugo is a Go binary, not an npm package, so there is no `npm install` step and no +`package.json` unless a theme's asset pipeline needs one. Declare the build explicitly +because the default `npm run build` does not exist here: + +```json +{ + "buildCommand": "hugo --minify", + "outputDirectory": "public", + "devCommand": "hugo server -D --bind 0.0.0.0 --port $PORT" +} +``` + +Preview asset prefix: Hugo's option is `baseURL`, settable on the command line as +`hugo --baseURL "$EDGEONE_PREVIEW_ASSET_PREFIX"`. + +A theme is usually required — `hugo new site` alone produces an empty site that renders a +blank page. Either add a theme or write the layouts in `layouts/`. + +## Jekyll + +- Build command: `bundle exec jekyll build` +- Output directory: `_site` + +## Eleventy + +- Build command: `npx @11ty/eleventy` +- Output directory: `_site` + +## Hexo + +- Build command: `npx hexo generate` +- Output directory: `public` + +## VitePress + +- Scaffold: `npx vitepress init` +- Build command: `npm run docs:build` +- Output directory: `docs/.vitepress/dist` +- Preview asset prefix: `base` in `.vitepress/config.ts` + +## Docusaurus + +- Scaffold: `npx create-docusaurus@latest . classic --typescript` +- Build command: `npm run build` +- Output directory: `build` +- Preview asset prefix: `baseUrl` in `docusaurus.config.ts` + +## MkDocs + +- Build command: `mkdocs build` +- Output directory: `site` + +## 404 page + +Any `404.html` at the root of the build output directory. The platform serves it for +unmatched paths with a 404 status. Each generator has its own way to produce that file — +Hugo uses `layouts/404.html`, Jekyll a page with `permalink: /404.html`, Eleventy a +template named `404`. + +## Feature support + +| Feature | Supported | +|---------|-----------| +| Static build | yes | +| Custom build command | yes, via `edgeone.json` | +| Non-npm toolchains (Go, Ruby, Python) | yes | +| Server-side rendering | no — nothing to render | +| Incremental static regeneration | no — rebuild and redeploy instead | diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md new file mode 100644 index 0000000..bad962d --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md @@ -0,0 +1,155 @@ +# SvelteKit + +## Contents + +- [The adapter](#the-adapter) +- [Scaffold](#scaffold) +- [Preview asset prefix](#preview-asset-prefix) +- [Build settings](#build-settings) +- [Rendering modes](#rendering-modes) +- [Incremental static regeneration](#incremental-static-regeneration) +- [Streaming](#streaming) +- [404 page](#404-page) +- [Not supported yet](#not-supported-yet) +- [Feature support](#feature-support) + +SvelteKit 2.4+ is supported. EdgeOne CLI must be 1.2.0 or newer. + +## The adapter + +**Always required.** SvelteKit cannot build without an adapter, and the one that produces +platform output is `@edgeone/sveltekit`. + +```bash +npm install @edgeone/sveltekit +``` + +```javascript +// svelte.config.js +import adapter from '@edgeone/sveltekit'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + adapter: adapter(), + }, +}; + +export default config; +``` + +`@sveltejs/adapter-auto` cannot detect this platform, and `@sveltejs/adapter-vercel`, +`-netlify`, `-cloudflare`, `-node` all emit output the builder does not understand. The +one legitimate alternative is `@sveltejs/adapter-static`, and only when every route in +the app is prerendered. + +## Scaffold + +```bash +npx sv create . --template minimal --types ts --no-add-ons --install npm +``` + +## Preview asset prefix + +SvelteKit's option is `kit.paths.base`: + +```javascript +const config = { + kit: { + adapter: adapter(), + ...(process.env.EDGEONE_PREVIEW_ASSET_PREFIX + ? { paths: { base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX } } + : {}), + }, +}; +``` + +Note that `paths.base` moves served routes as well as asset URLs — the preview proxy +detects that from the redirect the dev server issues and stops stripping the prefix, so +no further configuration is needed. + +## Build settings + +- Build command: `npm run build` +- Output directory: whatever `@edgeone/sveltekit` writes; do not override + `outputDirectory` in `edgeone.json` for a SvelteKit project. + +## Rendering modes + +Server rendering is the default. Per-page and per-layout overrides go in +`+page.js` / `+page.server.js` / `+layout.js` / `+layout.server.js`: + +```javascript +// +page.js — client-side rendering for this route +export const ssr = false; +``` + +```javascript +// +page.js — prerender this route at build time +export const prerender = true; +``` + +## Incremental static regeneration + +Declared per route, and only effective on SSR routes that are not prerendered: + +```javascript +// +page.server.js +export const config = { + isr: { + expiration: 60, // revalidate every 60s; false caches indefinitely + }, +}; + +export const prerender = false; // ISR only applies to SSR routes +``` + +## Streaming + +Return promises from `load` instead of awaiting them: + +```javascript +// +page.server.js +export const load = async () => ({ + post: fetch('/api/post').then((r) => r.json()), + comments: fetch('/api/comments').then((r) => r.json()), +}); +``` + +```svelte + + + +{#await data.post} +

Loading...

+{:then post} +

{post.title}

+{/await} +``` + +## 404 page + +`src/routes/+error.svelte` catches every unmatched route. A `+error.svelte` inside a +route subtree scopes error handling to that subtree — `src/routes/dashboard/+error.svelte` +handles everything under `/dashboard/*` that a deeper boundary did not catch. + +## Not supported yet + +- **Observability** — platform metrics and log analysis do not cover SvelteKit routes. + +## Feature support + +| Feature | Supported | +|---------|-----------| +| Server-side rendering | yes | +| Static site generation | yes | +| Prerender | yes | +| Filesystem-based router | yes | +| Streaming with promises | yes | +| Form actions | yes | +| Hooks | yes | +| Observability | no | diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md new file mode 100644 index 0000000..356c474 --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md @@ -0,0 +1,146 @@ +# TanStack Start + +## Contents + +- [The adapter](#the-adapter) +- [Scaffold](#scaffold) +- [Preview asset prefix](#preview-asset-prefix) +- [Build settings](#build-settings) +- [Server functions](#server-functions) +- [API routes](#api-routes) +- [Streaming](#streaming) +- [404 page](#404-page) +- [Feature support](#feature-support) + +TanStack Start 1.132+ is supported. EdgeOne CLI must be 1.2.0 or newer. + +## The adapter + +**Always required.** TanStack Start needs a deployment target to emit a server bundle. + +```bash +npm install @edgeone/tanstack-start +``` + +```typescript +// vite.config.ts +import { defineConfig } from "vite"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import { edgeoneAdapter } from "@edgeone/tanstack-start"; + +export default defineConfig({ + plugins: [ + tanstackStart(), + edgeoneAdapter(), + ], +}); +``` + +The adapter and a Nitro plugin cannot coexist — an official Nitro preset is not published, +so remove `@tanstack/start-plugin-nitro` and any `nitroPlugin` / `nitroV2Plugin` call +rather than trying to configure both. + +## Scaffold + +```bash +npx create-tsrouter-app@latest . --template file-router --framework react --tailwind --no-git +``` + +## Preview asset prefix + +A Vite project, so `base`: + +```typescript +export default defineConfig({ + base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX, + plugins: [tanstackStart(), edgeoneAdapter()], +}); +``` + +## Build settings + +- Build command: `npm run build` +- Output directory: whatever the adapter writes; leave `outputDirectory` out of + `edgeone.json` for a TanStack Start project. + +## Server functions + +`createServerFn` is the framework's server boundary and it works here. Everything inside +the handler runs on the server only, so secrets stay out of the client bundle: + +```typescript +import { createServerFn } from "@tanstack/react-start"; + +export const getPosts = createServerFn({ method: "GET" }).handler(async () => { + const res = await fetch("https://api.example.com/posts", { + headers: { Authorization: `Bearer ${process.env.API_TOKEN}` }, + }); + return res.json(); +}); +``` + +Call it from a route loader: + +```typescript +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/posts")({ + loader: () => getPosts(), + component: Posts, +}); + +function Posts() { + const posts = Route.useLoaderData(); + return
    {posts.map((p) =>
  • {p.title}
  • )}
; +} +``` + +## API routes + +Server routes live alongside page routes: + +```typescript +// src/routes/api/hello.ts +import { createServerFileRoute } from "@tanstack/react-start/server"; + +export const ServerRoute = createServerFileRoute("/api/hello").methods({ + GET: async () => Response.json({ message: "hello" }), +}); +``` + +## Streaming + +Return a promise from the loader and resolve it with `Await`: + +```typescript +export const Route = createFileRoute("/dashboard")({ + loader: () => ({ slow: getSlowData() }), + component: Dashboard, +}); + +function Dashboard() { + const { slow } = Route.useLoaderData(); + return ( + Loading…

}> + {(data) => } +
+ ); +} +``` + +## 404 page + +Use the router's `notFoundComponent` on the root route, or throw `notFound()` from a +loader when a record does not exist. + +## Feature support + +| Feature | Supported | +|---------|-----------| +| Server-side rendering | yes | +| Streaming | yes | +| Server functions | yes | +| Server routes (API) | yes | +| Type-safe routing | yes | +| File-based routing | yes | +| Nitro presets | no | diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md new file mode 100644 index 0000000..f89bc91 --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md @@ -0,0 +1,129 @@ +# Vike + +## Contents + +- [The adapter](#the-adapter) +- [Scaffold](#scaffold) +- [Preview asset prefix](#preview-asset-prefix) +- [Build settings](#build-settings) +- [Rendering modes](#rendering-modes) +- [404 page](#404-page) +- [Not supported yet](#not-supported-yet) +- [Feature support](#feature-support) + +Vike 0.4.235+ is supported. EdgeOne CLI must be 1.2.0 or newer. Vike is a Vite plugin, +so the platform adapter is the generic Vite one. + +## The adapter + +Required when the app renders on the server. A fully pre-rendered Vike site is static +output and needs none. + +```bash +npm install @edgeone/vite +``` + +```typescript +// vite.config.ts +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import vike from "vike/plugin"; +import { edgeone } from "@edgeone/vite"; + +export default defineConfig({ + plugins: [ + react(), + vike({ prerender: true }), + edgeone(), + ], +}); +``` + +`@edgeone/vite` 2.x discovers the framework entry through the Universal Deploy protocol. +The 1.x `serverWrapper` option no longer does anything — if you find it in a config, the +config was written against the old major and the option should be deleted. + +## Scaffold + +```bash +npm create vike@latest . -- --react --typescript +``` + +## Preview asset prefix + +```typescript +export default defineConfig({ + base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX, + plugins: [react(), vike(), edgeone()], +}); +``` + +## Build settings + +- Build command: `npm run build` +- Output directory: `dist` + +## Rendering modes + +Vike sets rendering per page through `+config.js` files, and the setting cascades down the +directory tree. + +Pre-render everything: + +```javascript +// pages/+config.js +export default { prerender: true }; +``` + +Server rendering for one subtree, overriding a prerendering default: + +```javascript +// pages/dashboard/+config.js +export default { prerender: false }; +``` + +Client-only rendering for a page: + +```javascript +// pages/admin/+config.js +export default { ssr: false }; +``` + +Per-page data fetching goes in a `+data.js` beside the page: + +```javascript +// pages/product/@id/+data.js +export async function data(pageContext) { + const res = await fetch(`https://api.example.com/products/${pageContext.routeParams.id}`); + return { product: await res.json() }; +} +``` + +## 404 page + +Vike routes unmatched URLs to `pages/_error/+Page.jsx`. Read `pageContext.is404` there to +distinguish a missing page from a server error: + +```jsx +export default function Page({ is404 }) { + return is404 ?

404 — page not found

:

500 — something broke

; +} +``` + +## Not supported yet + +- **Server-side API routes.** Vike's own server route mechanism does not run here. Put + HTTP endpoints in `cloud-functions/` instead and call them from the page with `fetch`. + +## Feature support + +| Feature | Supported | +|---------|-----------| +| Server-side rendering | yes | +| Pre-rendering / SSG | yes | +| Client-only rendering | yes | +| Per-page render control | yes | +| Filesystem routing | yes | +| Data fetching hooks | yes | +| Multiple UI frameworks | yes | +| Server-side API routes | no — use `cloud-functions/` | diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/vite-spa.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/vite-spa.md new file mode 100644 index 0000000..522e59d --- /dev/null +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/vite-spa.md @@ -0,0 +1,81 @@ +# Vite single-page apps (React, Vue, Svelte, Solid, vanilla) + +A plain Vite app builds to static files. **No platform adapter is needed** — there is no +server bundle to emit. Install `@edgeone/vite` only when the app grows a server entry. + +This is the right default for most requests that do not explicitly ask for server +rendering: it is the fastest to build, the fastest to deploy, and has the fewest ways to +fail. + +## Scaffold + +```bash +npm create vite@latest . -- --template react-ts +``` + +Templates: `react-ts`, `react`, `vue-ts`, `vue`, `svelte-ts`, `solid-ts`, `vanilla-ts`, +`preact-ts`, `lit-ts`, `qwik-ts`. + +## Preview asset prefix + +```typescript +// vite.config.ts +export default defineConfig({ + base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX, + plugins: [react()], +}); +``` + +Vite treats an undefined `base` as `/`, so a deployment needs no special case. + +## Build settings + +- Build command: `npm run build` +- Output directory: `dist` + +## Client-side routing needs a rewrite + +This is the one thing a single-page app must get right. Without it, a deep link like +`/dashboard/settings` returns 404 on refresh because no such file exists in `dist` — the +client router never gets a chance to run. + +```json +{ + "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] +} +``` + +Put that in `edgeone.json`. Use `rewrites`, not `redirects`: a rewrite serves +`index.html` while the browser keeps the original URL, which is what the client router +needs to read. A redirect would change the URL and lose the route. + +## 404 page + +A catch-all route in the client router: + +```tsx + + } /> + } /> + +``` + +**Do not** add a `404.html` to the output root. Combined with the catch-all rewrite above +it produces conflicting behaviour, and on its own it shadows the client router for every +unmatched path. + +## Adding a backend + +A Vite app has no server. When the request needs one, put HTTP endpoints in +`cloud-functions/` and call them with `fetch` from the client. Reach for a full-stack +framework only when the request actually needs server rendering. + +## Feature support + +| Feature | Supported | +|---------|-----------| +| Static build | yes | +| Client-side routing | yes, with the rewrite above | +| Environment variables (`VITE_` prefix) | yes | +| Code splitting | yes | +| Server-side rendering | not in this shape — use a full-stack framework | From e065dedf066409924610bd8a67fe27da557f38c0 Mon Sep 17 00:00:00 2001 From: chuckcchen Date: Mon, 7 Sep 2026 10:25:03 +0800 Subject: [PATCH 2/8] docs(makers-recipes): load static assets with page-relative URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A generated guestbook wrote and ` — **not** `href="/style.css"` or `src="/script.js"`. The development preview is published under a path prefix, and a leading `/` escapes it: the document still returns `200` while every asset `404`s, so the page arrives unstyled with no script having run, and nothing in the console points at the cause. Relative URLs are correct there *and* at `/` after deploy. Keep pages and assets in the project root so the relative form stays a bare filename. This is only about what the markup loads — `fetch('/api/messages')` and `` stay root-absolute. + > ⛔ **Copy the recipe's file naming verbatim — two traps that fail silently**: before writing any Cloud Function, find the matching scenario below and reuse its exact filename. Getting the name wrong usually does NOT throw a clear error — it falls back silently: > 1. **Every function file MUST carry its language extension** — `.js` (Node), `.py` (Python), `.go` (Go). A file with no extension (e.g. `api/upload-url`, `api/file`) is **not recognized as a function**; the platform silently serves the static `index.html` fallback, so `/api/*` "mysteriously" returns HTML instead of JSON. Name them `api/upload-url.js`, `api/file.js`. > 2. **`[[default]].js` is the catch-all for its own directory (`api/[[default]].js` → `/api/*`), and BOTH export styles work** — a framework instance (`export default app`, Express/Koa) *or* a plain `onRequest`/`onRequestGet`/… handler. Verified locally with `edgeone makers dev`: a bare `onRequest` in `[[default]].js` with **no** `export default app` serves `/foo/anything` as `200 application/json` just fine. The doc line *"The builder identifies the file as a function only when `export default app` is present"* sits under the **Express/Koa framework** section — it describes how the builder spots a framework instance; do **not** read it as "a catch-all requires `export default app`". ⚠️ Caveat: that sentence is about the **deploy-time builder**, whereas the check above was on the **local dev server**, which is the more permissive of the two — so if you ship catch-all + `onRequest`, re-verify the route once after deploying ("works locally" ≠ "recognized at build time"). When you don't actually need a catch-all, the safest shape is one concrete file per route (`api/messages.js`, `api/artworks/[id]/like.js`), params via `[id]` folders/files, extra args as query strings (`/api/file?key=...`). @@ -79,7 +81,12 @@ export async function onRequest({ request }) { } ``` -**index.html** frontend calls it like any API: +**index.html** loads its assets relative to itself, and calls the API root-absolute: +```html + + +``` + ```javascript await fetch('/api/messages', { // post method: 'POST', From 34a6129323d3bff8fb6452fd8bee62da7f63a620 Mon Sep 17 00:00:00 2001 From: chuckcchen Date: Mon, 7 Sep 2026 11:44:57 +0800 Subject: [PATCH 3/8] docs(makers-frameworks): fix the Vike and TanStack Start scaffold commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both documented commands fail as written. An agent copies them verbatim — that is what the flags are for, since a scaffolder that stops to ask a question has nobody to answer it — so neither framework could be scaffolded at all, and a run that tried degraded to writing the boilerplate by hand. Vike: there is no --typescript. The scaffolder writes TypeScript either way and rejects the flag outright with "Unknown option typescript". Replaced with --edgeone, its own EdgeOne Pages integration, which declares @edgeone/vite and registers the plugin — the work "The adapter" section otherwise asks for by hand — plus --skip-git to match the other scaffolders here. TanStack Start: create-tsrouter-app is deprecated and says so on every run. The rename is the smaller half. That package defaults to router-only compatibility mode, so the command scaffolded file-based routing with none of Start in it — no @tanstack/react-start for this document's adapter to attach to. `tanstack create` without --router-only scaffolds Start itself. Tailwind needs no flag now (always on; --tailwind is a deprecated no-op), and --no-intent skips the TanStack Intent step, which reaches the network to write agent config and fails the scaffold when it cannot. Both verified by running them into an empty directory. Co-authored-by: Cursor --- .../makers-frameworks/references/tanstack-start.md | 12 +++++++++++- .../references/makers-frameworks/references/vike.md | 8 +++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md index 356c474..3d4b89a 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md @@ -43,9 +43,19 @@ rather than trying to configure both. ## Scaffold ```bash -npx create-tsrouter-app@latest . --template file-router --framework react --tailwind --no-git +npx @tanstack/cli@latest create . --framework react --non-interactive --no-git --no-intent ``` +This replaces `create-tsrouter-app`, which is deprecated and now prints a notice saying so. +The rename is the smaller half of the change: that package defaults to router-only +compatibility mode — file-based routing with none of Start — so it scaffolded a project +this document does not describe, with no `@tanstack/react-start` to attach an adapter to. +The command above scaffolds Start itself. + +Tailwind needs no flag; a standard scaffold always enables it, and `--tailwind` survives +only as a deprecated no-op. `--no-intent` skips TanStack Intent, which reaches the network +to write agent config and fails the scaffold when it cannot. + ## Preview asset prefix A Vite project, so `base`: diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md index f89bc91..ad52e11 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/vike.md @@ -46,9 +46,15 @@ config was written against the old major and the option should be deleted. ## Scaffold ```bash -npm create vike@latest . -- --react --typescript +npm create vike@latest . -- --react --edgeone --skip-git ``` +`--edgeone` is the scaffolder's own EdgeOne Pages integration: it declares `@edgeone/vite` +and registers the plugin, so a project scaffolded this way already has everything under +"The adapter" above and only needs the asset prefix added. There is no `--typescript` — +this scaffolder writes TypeScript either way, and passing it stops the run with +"Unknown option typescript". + ## Preview asset prefix ```typescript From 61ce099faa65adc7b804bd0e7bd3af88208dab7c Mon Sep 17 00:00:00 2001 From: chuckcchen Date: Mon, 7 Sep 2026 12:20:27 +0800 Subject: [PATCH 4/8] fix(makers-frameworks): give nuxi init the template it now requires nuxi init stops with "Non-interactive terminal detected. Missing required argument: --template" and prints usage instead of scaffolding. In a sandbox that reads as a scaffolder that exited without producing a project. Co-authored-by: Cursor --- .../references/makers-frameworks/references/nuxt.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md index ac7403f..b405035 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/nuxt.md @@ -17,9 +17,14 @@ install.** Nitro's own preset detection does the work. ## Scaffold ```bash -npx nuxi@latest init . --packageManager npm --no-gitInit --force +npx nuxi@latest init . --template minimal --packageManager npm --no-gitInit --force ``` +`--template` is required. Without it `nuxi` stops with "Non-interactive terminal detected. +Missing required argument: --template" and prints its usage instead of scaffolding, which +in a sandbox reads as a scaffolder that produced nothing. `minimal` is the Nuxt 4 starter; +`content`, `module`, and `ui` are the other choices it offers. + ## Preview asset prefix Nuxt's option is `app.baseURL`. It moves both routes and assets; the preview proxy From 7060228f8041fdd644b90f3463aacbcfa02cbc92 Mon Sep 17 00:00:00 2001 From: chuckcchen Date: Mon, 7 Sep 2026 15:36:31 +0800 Subject: [PATCH 5/8] fix(makers-frameworks): correct the TanStack Start adapter import and pin adapter ranges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documented import does not exist. @edgeone/tanstack-start exports edgeoneTanStackStartAdapter, as a named and as the default export; there is no edgeoneAdapter, so the snippet here failed the build of every project that copied it. The scaffolder's Vite is out of the adapter's range too. It writes vite ^8 and @vitejs/plugin-react ^6, while the adapter peers on ^5 || ^6 || ^7 and plugin-react 6 requires the 8 the adapter rejects — so the two have to move together, and neither constraint was written down anywhere. One session spent four installs and seven minutes rediscovering that by trial. Adapter profiles now carry the range to declare, so a host adding an adapter to package.json on a project's behalf does not have to write `latest`. A floating tag re-resolves against the registry on every install and dates any lockfile next to it, which costs a full re-resolution before the project is touched. Co-authored-by: Cursor --- .../references/makers-frameworks/SKILL.md | 9 ++++++++ .../references/tanstack-start.md | 22 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md b/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md index c9e8dad..be65d4c 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md @@ -106,6 +106,10 @@ lint and the install warmup do not each carry their own copy. Keep it in sync with the per-framework documents. - `detect` names the dependencies that prove the framework is in use. +- `adapter.version` is the range to declare when a host adds the adapter to `package.json` + on the project's behalf. It is here so that declaring it does not mean `latest`: a + floating tag re-resolves against the registry on every install and dates any lockfile + next to it, which costs a full dependency re-resolution before the project is touched. - `adapter.configFiles` is where the adapter gets wired in. - `adapter.required` is `always` when the framework cannot build without an adapter, or `server-output` when a fully static build needs none. @@ -124,6 +128,7 @@ Keep it in sync with the per-framework documents. "detect": ["astro"], "adapter": { "package": "@edgeone/astro", + "version": "^1.1.5", "configFiles": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], "required": "server-output" }, @@ -141,6 +146,7 @@ Keep it in sync with the per-framework documents. "detect": ["@react-router/dev"], "adapter": { "package": "@edgeone/react-router", + "version": "^1.1.10", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "server-output" }, @@ -158,6 +164,7 @@ Keep it in sync with the per-framework documents. "detect": ["@sveltejs/kit"], "adapter": { "package": "@edgeone/sveltekit", + "version": "^1.1.1", "configFiles": ["svelte.config.js", "svelte.config.ts"], "required": "always" }, @@ -170,6 +177,7 @@ Keep it in sync with the per-framework documents. "detect": ["@tanstack/react-start", "@tanstack/solid-start"], "adapter": { "package": "@edgeone/tanstack-start", + "version": "^1.1.0", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "always" }, @@ -182,6 +190,7 @@ Keep it in sync with the per-framework documents. "detect": ["vike"], "adapter": { "package": "@edgeone/vite", + "version": "^2.0.1", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "server-output" }, diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md index 3d4b89a..d6501d4 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/tanstack-start.md @@ -26,16 +26,34 @@ npm install @edgeone/tanstack-start // vite.config.ts import { defineConfig } from "vite"; import { tanstackStart } from "@tanstack/react-start/plugin/vite"; -import { edgeoneAdapter } from "@edgeone/tanstack-start"; +import { edgeoneTanStackStartAdapter } from "@edgeone/tanstack-start"; export default defineConfig({ plugins: [ tanstackStart(), - edgeoneAdapter(), + edgeoneTanStackStartAdapter(), ], }); ``` +The export is named `edgeoneTanStackStartAdapter`, and it is also the default export, so +`import edgeoneAdapter from "@edgeone/tanstack-start"` works too. There is no export named +`edgeoneAdapter`; importing that name is a build error, not a lint warning. + +### Vite 7 is the ceiling + +`@edgeone/tanstack-start` peers on `vite@^5 || ^6 || ^7`. Vite 8 is not in range, and the +scaffolder's own `package.json` asks for it — so a fresh scaffold has to be corrected +before the first install, not after: + +| Package | Scaffolder writes | Use instead | Why | +| --- | --- | --- | --- | +| `vite` | `^8.0.0` | `^7.0.0` | the adapter does not accept 8 | +| `@vitejs/plugin-react` | `^6.0.1` | `^5.2.0` | 6 requires Vite 8; 5.2 spans 4 through 8 | + +Change both together. Downgrading Vite alone leaves `@vitejs/plugin-react` demanding the +version just removed, and each attempt costs a full reinstall. + The adapter and a Nitro plugin cannot coexist — an official Nitro preset is not published, so remove `@tanstack/start-plugin-nitro` and any `nitroPlugin` / `nitroV2Plugin` call rather than trying to configure both. From 9c4e72c1468554516ec1e5ca786226097119a96d Mon Sep 17 00:00:00 2001 From: chuckcchen Date: Tue, 8 Sep 2026 11:15:05 +0800 Subject: [PATCH 6/8] docs(makers-frameworks): correct the preview guidance three real sessions failed on Each was found by generating an app end to end and watching the preview fail, not by reading the docs. Next.js: 16 runs, but 15 is what the platform recommends, and create-next-app@latest now resolves to 16. Say 13 through 16 supported, 15 recommended, pin the scaffold. React Router: Vite's base and the router's basename are separate settings, and the dev adapter restores req.originalUrl after Vite strips the base. Setting base alone leaves the handler matching /preview against routes that never declare it, and every preview 404s with 'No route matches URL "/preview"'. SvelteKit: sveltekit() called with any options makes @sveltejs/load-config ignore svelte.config.js entirely, so an adapter configured there is dropped without a word and a clean-looking build produces no server output. Document vite.config.ts as where it goes, and teach the profile the precedence so MKR018 checks the file that actually decides. The mirrors also pick up the adapter.version fields from 7060228, which landed without a rebuild. Co-authored-by: Cursor --- codex/makers-frameworks.md | 22 +++++- cursor/rules/makers-frameworks.mdc | 22 +++++- .../references/makers-frameworks/SKILL.md | 13 +++- .../makers-frameworks/references/nextjs.md | 12 +++- .../references/react-router.md | 55 ++++++++++++++- .../makers-frameworks/references/sveltekit.md | 67 ++++++++++++------- 6 files changed, 159 insertions(+), 32 deletions(-) diff --git a/codex/makers-frameworks.md b/codex/makers-frameworks.md index c9e8dad..5dacc34 100644 --- a/codex/makers-frameworks.md +++ b/codex/makers-frameworks.md @@ -106,6 +106,10 @@ lint and the install warmup do not each carry their own copy. Keep it in sync with the per-framework documents. - `detect` names the dependencies that prove the framework is in use. +- `adapter.version` is the range to declare when a host adds the adapter to `package.json` + on the project's behalf. It is here so that declaring it does not mean `latest`: a + floating tag re-resolves against the registry on every install and dates any lockfile + next to it, which costs a full dependency re-resolution before the project is touched. - `adapter.configFiles` is where the adapter gets wired in. - `adapter.required` is `always` when the framework cannot build without an adapter, or `server-output` when a fully static build needs none. @@ -124,6 +128,7 @@ Keep it in sync with the per-framework documents. "detect": ["astro"], "adapter": { "package": "@edgeone/astro", + "version": "^1.1.5", "configFiles": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], "required": "server-output" }, @@ -141,6 +146,7 @@ Keep it in sync with the per-framework documents. "detect": ["@react-router/dev"], "adapter": { "package": "@edgeone/react-router", + "version": "^1.1.10", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "server-output" }, @@ -158,7 +164,19 @@ Keep it in sync with the per-framework documents. "detect": ["@sveltejs/kit"], "adapter": { "package": "@edgeone/sveltekit", - "configFiles": ["svelte.config.js", "svelte.config.ts"], + "version": "^1.1.1", + "configFiles": [ + "svelte.config.js", + "svelte.config.ts", + "vite.config.ts", + "vite.config.js", + "vite.config.mjs" + ], + "configOverride": { + "files": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "pattern": "sveltekit\\(\\s*[^)\\s]", + "reason": "SvelteKit reads one config and prefers the Vite one: because sveltekit() is called with options here, a svelte.config.js beside it is ignored whole, adapter and all. Wire the adapter into this call, or empty the call and keep everything in svelte.config.js." + }, "required": "always" }, "outputDirectory": "", @@ -170,6 +188,7 @@ Keep it in sync with the per-framework documents. "detect": ["@tanstack/react-start", "@tanstack/solid-start"], "adapter": { "package": "@edgeone/tanstack-start", + "version": "^1.1.0", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "always" }, @@ -182,6 +201,7 @@ Keep it in sync with the per-framework documents. "detect": ["vike"], "adapter": { "package": "@edgeone/vite", + "version": "^2.0.1", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "server-output" }, diff --git a/cursor/rules/makers-frameworks.mdc b/cursor/rules/makers-frameworks.mdc index c9e8dad..5dacc34 100644 --- a/cursor/rules/makers-frameworks.mdc +++ b/cursor/rules/makers-frameworks.mdc @@ -106,6 +106,10 @@ lint and the install warmup do not each carry their own copy. Keep it in sync with the per-framework documents. - `detect` names the dependencies that prove the framework is in use. +- `adapter.version` is the range to declare when a host adds the adapter to `package.json` + on the project's behalf. It is here so that declaring it does not mean `latest`: a + floating tag re-resolves against the registry on every install and dates any lockfile + next to it, which costs a full dependency re-resolution before the project is touched. - `adapter.configFiles` is where the adapter gets wired in. - `adapter.required` is `always` when the framework cannot build without an adapter, or `server-output` when a fully static build needs none. @@ -124,6 +128,7 @@ Keep it in sync with the per-framework documents. "detect": ["astro"], "adapter": { "package": "@edgeone/astro", + "version": "^1.1.5", "configFiles": ["astro.config.mjs", "astro.config.js", "astro.config.ts"], "required": "server-output" }, @@ -141,6 +146,7 @@ Keep it in sync with the per-framework documents. "detect": ["@react-router/dev"], "adapter": { "package": "@edgeone/react-router", + "version": "^1.1.10", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "server-output" }, @@ -158,7 +164,19 @@ Keep it in sync with the per-framework documents. "detect": ["@sveltejs/kit"], "adapter": { "package": "@edgeone/sveltekit", - "configFiles": ["svelte.config.js", "svelte.config.ts"], + "version": "^1.1.1", + "configFiles": [ + "svelte.config.js", + "svelte.config.ts", + "vite.config.ts", + "vite.config.js", + "vite.config.mjs" + ], + "configOverride": { + "files": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "pattern": "sveltekit\\(\\s*[^)\\s]", + "reason": "SvelteKit reads one config and prefers the Vite one: because sveltekit() is called with options here, a svelte.config.js beside it is ignored whole, adapter and all. Wire the adapter into this call, or empty the call and keep everything in svelte.config.js." + }, "required": "always" }, "outputDirectory": "", @@ -170,6 +188,7 @@ Keep it in sync with the per-framework documents. "detect": ["@tanstack/react-start", "@tanstack/solid-start"], "adapter": { "package": "@edgeone/tanstack-start", + "version": "^1.1.0", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "always" }, @@ -182,6 +201,7 @@ Keep it in sync with the per-framework documents. "detect": ["vike"], "adapter": { "package": "@edgeone/vite", + "version": "^2.0.1", "configFiles": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], "required": "server-output" }, diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md b/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md index be65d4c..5dacc34 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/SKILL.md @@ -165,7 +165,18 @@ Keep it in sync with the per-framework documents. "adapter": { "package": "@edgeone/sveltekit", "version": "^1.1.1", - "configFiles": ["svelte.config.js", "svelte.config.ts"], + "configFiles": [ + "svelte.config.js", + "svelte.config.ts", + "vite.config.ts", + "vite.config.js", + "vite.config.mjs" + ], + "configOverride": { + "files": ["vite.config.ts", "vite.config.js", "vite.config.mjs"], + "pattern": "sveltekit\\(\\s*[^)\\s]", + "reason": "SvelteKit reads one config and prefers the Vite one: because sveltekit() is called with options here, a svelte.config.js beside it is ignored whole, adapter and all. Wire the adapter into this call, or empty the call and keep everything in svelte.config.js." + }, "required": "always" }, "outputDirectory": "", diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md index 8044ebb..09ffcd0 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/nextjs.md @@ -1,12 +1,18 @@ # Next.js -Next.js 13, 14, and 15 are supported, App Router and Pages Router both. The builder -handles Next.js directly — **no platform adapter, no plugin, nothing to install.** +Next.js 13 through 16 are supported, App Router and Pages Router both, and 15 is the +recommended version. The builder handles Next.js directly — **no platform adapter, no +plugin, nothing to install.** + +The scaffold command below pins that recommendation rather than taking `@latest`, which +now produces 16. Unlike `create-react-router`, the pin holds the framework and not just +the generator: `create-next-app` ships its templates inside the published package instead +of fetching the newest from a branch. ## Scaffold ```bash -npx create-next-app@latest . --typescript --tailwind --app --eslint --use-npm --yes +npx create-next-app@15 . --typescript --tailwind --app --eslint --use-npm --yes ``` ## Preview asset prefix diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md index b1acf92..9b6f347 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/react-router.md @@ -11,9 +11,37 @@ - [404 page](#404-page) - [Feature support](#feature-support) -React Router 7+ is supported with full-stack deployment. EdgeOne CLI must be 1.2.0 or +React Router 7 is supported with full-stack deployment. EdgeOne CLI must be 1.2.0 or newer. Version 7 is a Vite-based framework, not just the routing library. +Not 8. `@edgeone/react-router` peers on `react-router@^7` and `@react-router/dev@^7`, and +on Vite 5, 6, or 7 — so the current default scaffold, which is version 8 on Vite 8, falls +outside both. The adapter is a deploy-time contract rather than a preview-time one, so a +project like that previews perfectly and deploys broken with every gate green. + +Pinning the CLI does not pin the framework. `create-react-router` downloads its default +template from the `main` branch of `remix-run/react-router-templates`, which tracks the +newest major, so `create-react-router@7` still writes a version 8 manifest. Either pass +`--template` with a ref of your own, or correct the versions after scaffolding. + +Correcting the versions is not the whole correction. The scaffolded `vite.config.ts` sets +`resolve.tsconfigPaths`, which is Vite 8's built-in alias resolution and absent in 7 — +where it is not rejected either, because Vite does not validate `resolve`. So `~/*` stays +in tsconfig.json, tsc stays happy, and the first import through the alias fails the build +naming only the import. Going back to Vite 7 means going back to the plugin the template +used before 8: + +```typescript +// vite.config.ts +import { reactRouter } from "@react-router/dev/vite"; +import { defineConfig } from "vite"; +import tsconfigPaths from "vite-tsconfig-paths"; + +export default defineConfig({ + plugins: [reactRouter(), tsconfigPaths(), edgeoneAdapter()], +}); +``` + ## The adapter Required for server rendering. A project with `ssr: false` builds to static client output @@ -47,16 +75,37 @@ npx create-react-router@latest . --yes --no-git-init --install ## Preview asset prefix -This is a Vite project, so the option is `base`: +Two options in two files, and the Vite one alone does nothing. + +`base` moves the asset URLs: ```typescript +// vite.config.ts export default defineConfig({ base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX, plugins: [reactRouter(), edgeoneAdapter()], }); ``` -Omit it when the variable is unset so the deployed site stays at `/`. +`basename` is what matches the requests: + +```typescript +// react-router.config.ts +export default { + ssr: true, + basename: process.env.EDGEONE_PREVIEW_ASSET_PREFIX ?? "/", +} satisfies Config; +``` + +Vite strips the prefix off the request before the framework sees it, and React +Router's dev adapter puts it straight back — on purpose, so the router is given +the full path. Against a basename still defaulting to `/` that path matches +nothing, so the assets load and every navigation answers `No route matches URL +"/preview"`. React Router additionally refuses to start in dev unless the +basename begins with the base, which is why both read the same variable. + +Both fall back to `/` when the variable is unset, so the deployed site stays at +the root. ## Build settings diff --git a/skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md b/skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md index bad962d..4bb07d1 100644 --- a/skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md +++ b/skills/edgeone-makers-tools/references/makers-frameworks/references/sveltekit.md @@ -24,22 +24,37 @@ platform output is `@edgeone/sveltekit`. npm install @edgeone/sveltekit ``` -```javascript -// svelte.config.js -import adapter from '@edgeone/sveltekit'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +### Where the config lives -/** @type {import('@sveltejs/kit').Config} */ -const config = { - preprocess: vitePreprocess(), - kit: { - adapter: adapter(), - }, -}; +`sv create` no longer writes a `svelte.config.js`. It puts the SvelteKit config inside the +`sveltekit()` call in `vite.config.ts`, and that is where the adapter goes: -export default config; +```typescript +// vite.config.ts +import adapter from '@edgeone/sveltekit'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter(), + }), + ], +}); ``` +A `svelte.config.js` still works, but **only in a project whose `sveltekit()` takes no +arguments at all.** The two are not merged. `@sveltejs/load-config` resolves the Vite +config first and returns as soon as the plugin hands back any options, so one option in +`vite.config.ts` discards the whole of `svelte.config.js` — adapter included, leaving the +build with no adapter and no error naming the file it ignored. + +The trap that follows is specific: the preview asset prefix below is also an option on +`sveltekit()`. Adding the adapter to a new `svelte.config.js` while leaving that prefix +where it is means the adapter never loads. If a project already has options in +`vite.config.ts`, add the adapter there rather than starting a second config file. + `@sveltejs/adapter-auto` cannot detect this platform, and `@sveltejs/adapter-vercel`, `-netlify`, `-cloudflare`, `-node` all emit output the builder does not understand. The one legitimate alternative is `@sveltejs/adapter-static`, and only when every route in @@ -53,19 +68,25 @@ npx sv create . --template minimal --types ts --no-add-ons --install npm ## Preview asset prefix -SvelteKit's option is `kit.paths.base`: - -```javascript -const config = { - kit: { - adapter: adapter(), - ...(process.env.EDGEONE_PREVIEW_ASSET_PREFIX - ? { paths: { base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX } } - : {}), - }, -}; +SvelteKit's option is `paths.base`, alongside the adapter in the same `sveltekit()` call: + +```typescript +// vite.config.ts +export default defineConfig({ + plugins: [ + sveltekit({ + adapter: adapter(), + ...(process.env.EDGEONE_PREVIEW_ASSET_PREFIX + ? { paths: { base: process.env.EDGEONE_PREVIEW_ASSET_PREFIX } } + : {}), + }), + ], +}); ``` +In a project that still keeps its config in `svelte.config.js`, both go under `kit` +instead — and `sveltekit()` must then be called with no arguments, or neither is read. + Note that `paths.base` moves served routes as well as asset URLs — the preview proxy detects that from the redirect the dev server issues and stops stripping the prefix, so no further configuration is needed. From cc76e004fcad8d3287c7289fb232495245447387 Mon Sep 17 00:00:00 2001 From: chuckcchen Date: Tue, 8 Sep 2026 11:15:13 +0800 Subject: [PATCH 7/8] docs(makers-agents): name the request body a /chat endpoint receives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A generated chat app answered every message 400 while its own preview probe passed. The handler read `body.messages ?? body.message ? [...] : []`, which parses as `(body.messages ?? body.message) ? ...`: the array picks the branch and is then discarded, and the content is taken from a `message` no client sends. The probe had been sending both shapes at once — the one body that feeds that branch — so it went green while every real turn failed. State that `messages` is the only shape, that accepting a singular `message` beside it gives the handler a second path nothing exercises, and name the precedence trap outright. Contents list is what the length gate asks of the file at its new size. Co-authored-by: Cursor --- .../references/platform/conversation-id.md | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/skills/edgeone-makers-tools/references/makers-agents/references/platform/conversation-id.md b/skills/edgeone-makers-tools/references/makers-agents/references/platform/conversation-id.md index ca13f6d..6a2b55d 100644 --- a/skills/edgeone-makers-tools/references/makers-agents/references/platform/conversation-id.md +++ b/skills/edgeone-makers-tools/references/makers-agents/references/platform/conversation-id.md @@ -1,5 +1,13 @@ # Conversation ID + Frontend Convention +## Contents + +- [Principle](#principle) +- [⭐ Conversation ID and the `makers-conversation-id` Header (Iron Rule)](#conversation-id-and-the-makers-conversation-id-header-iron-rule) +- [⭐ Request body for `/chat` (the shape the preview probe sends)](#request-body-for-chat-the-shape-the-preview-probe-sends) +- [Endpoint → Frontend Call Style Cheat Sheet](#endpoint-frontend-call-style-cheat-sheet) +- [i18n](#i18n) + > Covers: makers-conversation-id dual-channel contract, /stop inverted rule, frontend call patterns, endpoint cheat sheet. --- @@ -49,10 +57,46 @@ const resp = await fetch('/chat', { 'Content-Type': 'application/json', 'makers-conversation-id': conversationId, // ⭐ required }, - body: JSON.stringify({ message, files }), + body: JSON.stringify({ messages, files }), }); ``` +### ⭐ Request body for `/chat` (the shape the preview probe sends) + +`messages` is `{ role: 'user' | 'assistant', content: string }[]`, oldest first, +and it is the **only** body a chat UI sends — a one-element array on the first +turn, not a bare string. + +```typescript +body: JSON.stringify({ + messages: [ + { role: 'user', content: '你好' }, + { role: 'assistant', content: '你好,有什么可以帮你的?' }, + { role: 'user', content: '介绍一下 EdgeOne' }, + ], +}) +``` + +**Read `messages` and nothing else.** Accepting a singular `message` as well +gives the handler two shapes to keep straight, and the preview probe sends only +this one — a mistake in the other branch reaches production unseen. + +**Never write `body.messages ?? body.message ? [...] : []`.** `??` binds tighter +than `?:`, so it reads as `(body.messages ?? body.message) ? ... : ...`: the +array decides the branch and is then thrown away, and the content is taken from +a `message` that no client sent. It compiles, it type-checks, `npm run build` +is clean, and every request is answered 400. + +Where the history lives is the choice this shape settles: + +| History held by | How | Use when | +|---|---|---| +| The client | the `messages` array above | a plain chat UI — nothing else to configure | +| The platform | `context.store.openaiSession()` / `claudeSessionStore`, keyed by `makers-conversation-id` | an SDK route resumes the conversation and the frontend sends only the new turn | + +Single-shot endpoints that take one prompt and are finished (`/outline`, +`/create`) keep the singular `{ message, files }` — they carry no conversation. + **Calling `/stop` (⚠️ inverted: never carry the header)**: ```typescript // Note: fetch /stop must NOT carry makers-conversation-id. From 4aa641b5bed18875795a8a1c0832655cb104e6a6 Mon Sep 17 00:00:00 2001 From: chuckcchen Date: Tue, 8 Sep 2026 11:28:29 +0800 Subject: [PATCH 8/8] docs(skills): state an operating contract so a host agent acts instead of asking The router gains Autonomy, Clarification, Approval and Done sections. Preview and deploy proceed when the request already asked for them; the site is inferred from whoami, a token, or the CLI rather than asked; a token the user just handed over is persisted without a second question; and work is done at a URL the user can click, not at a summary. The sub-skills drop the questions the contract removes. makers-deploy no longer asks china vs global, no longer asks whether to save a token, and no longer opens with a registration explanation. makers-env-adaption infers the site flag and goes on to deploy when deploy was already requested. makers-recipes and makers-agents compress the preview rule down to its constraints. Three corrections ride along: makers-agents pointed at edgeone-pages-dev and edgeone-pages-deploy, which are not skills in this repo; makers-recipes showed getStore with a bare string rather than the object form the platform takes; and the CLI floor is 1.6.0, with anonymous deploy called out separately at 1.6.29. codex/ and cursor/rules/ are build output of the four sub-skills above. The repository-root SKILL.md, AGENTS.md and CLAUDE.md hold a hand-maintained copy of the same contract. They are left uncommitted for review: nothing installs them, and nothing vendors them into the vibe coding agent. Co-authored-by: Cursor --- codex/makers-agents.md | 12 +- codex/makers-deploy.md | 126 +++++------------- codex/makers-env-adaption.md | 15 +-- codex/makers-recipes.md | 13 +- cursor/rules/makers-agents.mdc | 12 +- cursor/rules/makers-deploy.mdc | 126 +++++------------- cursor/rules/makers-env-adaption.mdc | 15 +-- cursor/rules/makers-recipes.mdc | 13 +- skills/edgeone-makers-tools/SKILL.md | 75 ++++++++--- .../references/makers-agents/SKILL.md | 12 +- .../references/makers-deploy/SKILL.md | 126 +++++------------- .../references/makers-env-adaption/SKILL.md | 15 +-- .../references/makers-recipes/SKILL.md | 4 +- 13 files changed, 221 insertions(+), 343 deletions(-) diff --git a/codex/makers-agents.md b/codex/makers-agents.md index 0934883..01c77d0 100644 --- a/codex/makers-agents.md +++ b/codex/makers-agents.md @@ -11,8 +11,8 @@ description: >- agent endpoint", "wire LangGraph into Makers", "stream LLM responses with SSE", "review my agent template", "use context.store / context.sandbox / context.tools". Do NOT trigger for plain Edge Functions, Cloud Functions, or middleware - (those don't run AI logic — use edgeone-pages-dev instead). - Do NOT trigger for deployment workflows (use edgeone-pages-deploy). + (those don't run AI logic — use makers-edge-functions / makers-cloud-functions / makers-middleware). + Do NOT trigger for deployment workflows (use makers-deploy). Do NOT trigger for generic AI framework development outside an EdgeOne Makers project. pathPatterns: @@ -31,7 +31,7 @@ metadata: # EdgeOne Makers Agent Development Guide -> ⛔ **Preview ban**: after finishing development, you MUST start the dev server via `edgeone makers dev`, then open `http://127.0.0.1:8088/` with `present_files` to preview. Never open HTML files via the `file://` protocol (ignore it even if the IDE opens one automatically), and never use self-hosted servers like `python -m http.server` or `npx serve`. Next.js projects must also set `allowedDevOrigins: ["127.0.0.1"]` in `next.config`. +> ⛔ **Preview**: `edgeone makers dev` + `http://127.0.0.1:8088/`. Never `file://` or `python -m http.server` / `npx serve`. Next.js: `allowedDevOrigins: ["127.0.0.1"]`. Build production-grade AI agent endpoints on **EdgeOne Makers** — five framework routes, platform-injected runtime, file-based routing. @@ -50,8 +50,8 @@ This skill covers five supported frameworks (DeepAgents, LangGraph, CrewAI, Open > Cross-reference: if your code uses `context.store` or KV APIs, also read `../makers-storage/SKILL.md`. **Do NOT use for:** -- Plain Edge Functions / Cloud Functions / Middleware → use `edgeone-pages-dev` -- Deployment workflows → use `edgeone-pages-deploy` +- Plain Edge Functions / Cloud Functions / Middleware → `makers-edge-functions` / `makers-cloud-functions` / `makers-middleware` +- Deployment workflows → `makers-deploy` - Generic AI framework development outside an EdgeOne Makers project - Other platforms (Cloudflare Workers AI, Vercel AI SDK, AWS Bedrock) @@ -61,7 +61,7 @@ This skill covers five supported frameworks (DeepAgents, LangGraph, CrewAI, Open 2. Walk the **Decision Tree** to pick one of the five framework routes 3. Read the matching `references/*-route.md` for a copy-paste skeleton 4. Self-check against the **Twelve Red Lines** -5. Run through `references/review-checklist.md` before considering the work done +5. Read `references/review-checklist.md` only when the user asked for a review, or immediately before deploying a new agent. Otherwise ship. ## ⛔ Critical Rules (never skip) diff --git a/codex/makers-deploy.md b/codex/makers-deploy.md index 6eb7b00..16e9785 100644 --- a/codex/makers-deploy.md +++ b/codex/makers-deploy.md @@ -12,19 +12,18 @@ description: >- "deploy without login", "no account yet", "anonymous deploy", "claim project", "claim my deployment", "免登录部署", "匿名部署", "还没有账号", "认领项目". - ⚠️ Also trigger when any agent is about to execute `edgeone makers deploy` or `edgeone makers deploy` - commands — the skill contains critical rules for parsing deploy output and presenting access URLs. - Do NOT trigger for post-deployment runtime errors (e.g. CORS issues, 500 errors after deploy — - use edgeone-makers-dev for troubleshooting). + ⚠️ Also trigger when any agent is about to execute `edgeone makers deploy`. + Do NOT trigger for post-deployment runtime errors (CORS, 500 after deploy) — + use makers-env-adaption / makers-recipes for local troubleshooting. pathPatterns: - "*.sh" - .github/workflows/** validate: - pattern: "whoami[^\\n]*\\s-t\\s" message: "edgeone whoami does not accept -t. Check the exit code instead: 0 = logged in, 1 = not." -metadata: + metadata: author: edgeone - version: "2.12.3" + version: "2.13.0" --- # EdgeOne Makers Deployment Skill @@ -56,12 +55,12 @@ Deploy any project to **EdgeOne Makers**. Then append any other notes (console URL, caveats, etc.). **Self-check before ending the turn (MANDATORY)** — read back what the user will actually SEE (NOT your thinking / reasoning content). Two questions: (a) Is the complete `.edgeone.cool` URL present at the top of the visible reply, in a code block or heading? (b) Was `present_files` called with that URL? If either answer is no, send an **additional short message** containing ONLY the `🌐 Live URL: ` block and call `present_files`. Do not end the turn until both channels carry the URL. "I already mentioned it in my reasoning" is NOT a substitute for placing it in the visible body. -5. **Ask the user to choose China or Global site** before browser login. Never assume. (Token login via `edgeone login --token` auto-detects site, no need to ask.) -6. **Prefer Browser Login; fall back to Token only after browser login is confirmed to fail** (see Login section for the ~60s fallback threshold and the Agent-in-IDE clarification — WorkBuddy is NOT headless). Token-first only when the user explicitly requests it. -7. **After token login, ask if the user wants to save the token locally** for future use. -8. **Before triggering any browser popup (login / registration), explain the reason and the benefits to the user first** — never silently launch a browser window. +5. **Browser login needs `--site`.** Infer china/global from `whoami`, an existing credential, or the user's context. Ask only when you must run `edgeone login` and the site is still unknown. Token login and anonymous deploy detect the site themselves — do not ask. +6. **Prefer Browser Login; fall back to Token only after browser login is confirmed to fail** (see Login section for the ~60s fallback threshold — WorkBuddy is NOT headless). Token-first only when the user explicitly requests it or provides a token. +7. **If the user provides a token, persist it** with `edgeone login --token` (or `--local` in a sandbox). Do not ask whether to save it. Never commit credentials. Do not treat `.edgeone/.token` as the CLI source of truth — credentials live in `~/.edgeone/` or `.edgeone/auth.json`. +8. **Before a browser popup, one sentence of why**, then run the command. Do not write a registration essay. 9. **On any CLI failure, surface the actual error text to the user before retrying, switching commands, or proposing a workaround.** Do NOT paraphrase (e.g. don't rewrite `Makers project exceeds 40 limit` into "maybe a name conflict or permission"). Do NOT silently pivot from `makers dev` to `makers deploy` (or vice versa) hoping to bypass — a systemic failure (auth / quota / permission) hits both with the same cause. Quote the raw error, name the root cause, then propose the fix or ask the user. -10. **Write every user-facing message in the user's own language** — **all** prose, blockquotes, and prompts in this skill are written in English purely to specify *meaning*; none of it is a string to paste. If the user writes to you in Chinese, speak Chinese — the login explanation, the site choice, the token question, the deploy result, all of it. Likewise for any other language. Emitting this skill's English strings into a non-English conversation is a bug. Only literal CLI commands, flags, env var names, and JSON field names stay verbatim. ⚠️ Where this skill gives a **fixed template** (Steps 2 and 4 of Anonymous Deploy), translating it is required but rewriting or extending it is not allowed — match it line for line. +10. **Write every user-facing message in the user's own language** — **all** prose, blockquotes, and prompts in this skill are written in English purely to specify *meaning*; none of it is a string to paste. If the user writes to you in Chinese, speak Chinese — the login explanation, any site choice, the deploy result, all of it. Likewise for any other language. Emitting this skill's English strings into a non-English conversation is a bug. Only literal CLI commands, flags, env var names, and JSON field names stay verbatim. ⚠️ Where this skill gives a **fixed template** (Steps 2 and 4 of Anonymous Deploy), translating it is required but rewriting or extending it is not allowed — match it line for line. **Rules 11-14 apply to the anonymous deploy / claim flow only:** @@ -124,7 +123,7 @@ cat edgeone.json 2>/dev/null | Not installed or < 1.6.0 | — | → Go to **Install CLI** | | `≥ 1.6.0` ✓ | Logged in (or token present) | → Go to **Deploy** | | `≥ 1.6.0` ✓ | Not logged in, has saved token | → Go to **Deploy with Token** (use saved token) | -| `≥ 1.6.29` ✓ | Not logged in, no saved token | → Go to **Anonymous Deploy** — default to anonymous only when the task is anonymous-native (scheduled / disposable / user asked for it); otherwise ask the user to choose anonymous deploy or login. If there is no way to ask, deploy with `--anonymous --json` and surface the claim link and the 60-minute window in the result (see Step 2) | +| `≥ 1.6.29` ✓ | Not logged in, no saved token | → Go to **Anonymous Deploy**. If the repo needs login (Agent / Blob / KV / AI Gateway), go to **Login** instead and say why. Ask the two-option choice only when publish intent is genuinely ambiguous. | | `1.6.0`–`1.6.28` | Not logged in, no saved token | Anonymous deploy is unavailable on this version → **Try Browser Login first** (see Login section). If the browser doesn't open or nothing happens within ~60 seconds, fall back to **Token Login**. Do NOT preemptively skip browser login by guessing "this looks like an Agent/CI environment" — that guess is often wrong; in particular, **WorkBuddy is a desktop IDE sandbox and fully supports browser login** | | `≥ 1.6.0` ✓ | User explicitly provides a token or requests token login | → Go to **Deploy with Token** / **Token Login** | @@ -142,34 +141,20 @@ Verify: `edgeone -v` — confirm output is `1.6.0` or higher. Retry installation ## Login -### 0. Explain the registration/login step +### 0. One sentence, then login -Before triggering any login flow, explain to the user **why** this step is needed and **what** to expect. Do not silently launch a browser window. +Before a browser login, say one sentence: you need an EdgeOne Makers account so the site lands on their account with a live URL. Then run the command. Do not silently launch a browser, and do not write a four-bullet registration pitch. -Tell the user: +If nothing happens within ~60 seconds (no popup, no CLI progress), switch to Token login. Do not wait indefinitely, and do not poll the user every turn. -> You need to log in or register an EdgeOne Makers account. Here's what to expect: -> - **Why login is required**: Deployment uploads your build output to your own account, generating a unique access URL and project record. -> - **What you get for free**: EdgeOne Makers offers a free tier with global CDN acceleration, automatic HTTPS, and custom domain binding — typically more than enough for personal projects. -> - **What happens next**: I'll run `edgeone login`, and your default browser will open the Tencent Cloud login page. Please complete the login/registration and authorize access, then come back here. -> - **If you get stuck**: If the browser doesn't open, or the CLI keeps waiting after you've logged in, let me know — I'll switch to Token login instead. +### 1. Pass `--site` — infer first, ask only if unknown -If the user does not respond within ~60 seconds (no browser popup or no progress reported), **proactively ask** about their status (whether the browser opened, any errors, or if they want to switch to Token login). Do not wait indefinitely. +`edgeone login` in a non-interactive context **requires** `--site `. Never run a bare `edgeone login`. -### 1. Ask the user to choose a site, then ALWAYS pass `--site` +Infer the site from `whoami`, an existing credential, or the user's context (language + console links they already used). Ask with a two-option control only when the site is still unknown: -Use the IDE's selection control (`ask_followup_question`) before running any login command: - -> Choose your EdgeOne Makers site: -> - **China** — For users in mainland China (console.cloud.tencent.com) -> - **Global** — For users outside China (console.intl.cloud.tencent.com) - -⚠️ **CRITICAL**: After the user chooses, you MUST invoke login with an explicit -`--site ` flag (e.g. `edgeone login --site china`). -**NEVER run a bare `edgeone login` (without `--site`) when driven by an Agent / skill.** -A bare `login` in a non-interactive context fails fast asking for -`--site` (it no longer pops an interactive site-picker that would hang). The site choice -is meant to happen here in the conversation, not inside the CLI. +> - **China** — console.cloud.tencent.com +> - **Global** — console.intl.cloud.tencent.com ### 2. Login methods reference @@ -230,23 +215,9 @@ Guide the user to obtain a token: ⚠️ Remind the user: the token has account-level permissions. Never commit it to a repository. -### 3. Offer to save the token locally - -After the user provides a token, ask: - -> Save this token locally for future deployments? -> - **Yes** — Save to `.edgeone/.token` (auto-used next time) -> - **No** — Use for this deployment only - -**If Yes:** - -```bash -mkdir -p .edgeone -echo "" > .edgeone/.token -grep -q '.edgeone/.token' .gitignore 2>/dev/null || echo '.edgeone/.token' >> .gitignore -``` +### 3. Persist a user-provided token -Confirm to the user: "✅ Token saved to `.edgeone/.token` and added to `.gitignore`." +If the user hands you a token, run `edgeone login --token ` (or `--local` in a sandbox that cannot write `~/.edgeone/`). Ensure `.edgeone/` is gitignored. Do not ask whether to save it, and do not invent a `.edgeone/.token` file — that is not the CLI credential store. --- @@ -266,26 +237,17 @@ edgeone makers deploy -n ### Token-based deploy (Makers projects) -First check for a saved token: - -```bash -cat .edgeone/.token 2>/dev/null -``` - -- Saved token found → use it, tell the user: "Using saved token from `.edgeone/.token`" -- No saved token → ask the user to provide one (see Token Login above) +Use a token already in the environment (`EDGEONE_PAGES_API_TOKEN`), one the user just provided, or the CLI store from `edgeone login --token` / `--local`. Do not `cat .edgeone/.token`. ```bash # Project already linked -edgeone makers deploy -t +edgeone makers deploy -t --json # New project -edgeone makers deploy -n -t +edgeone makers deploy -n -t --json ``` -The token already contains site info — no `--site` flag needed. - -After a successful deploy with a manually-entered token, ask if the user wants to save it (see "Offer to save the token locally" above). +The token already contains site info — no `--site` flag needed. If they typed the token in chat, persist it (see Login §3) and then deploy. ### Deploy to preview environment @@ -344,11 +306,9 @@ Non-empty output from either grep means the project uses Blob. ⛔ **All checks in this flow are silent — never narrate them to the user.** Do not report things like "CLI 版本 x.x(支持匿名部署)", "页面是纯静态、无 Blob/KV 依赖,可直接发布", or any other version/eligibility check result. These are internal reasoning; a non-technical user cannot act on them and should never see them. The only time you speak about a check is when it **changes the outcome** — e.g. the project needs login (below), or the CLI is too old and needs upgrading. Passing checks produce no message at all. -**KV cannot be detected this way — you must ask.** A KV namespace is bound in the console and exposed as a *global variable* whose name the user chose (e.g. `my_kv`), so there is no package import to grep for. There is no `@edgeone/pages-kv` package. Ask the user directly: +**KV has no npm package.** Infer it from the workspace: a console-bound global (e.g. `my_kv.get` / `my_kv.put`) in `edge-functions/`, or a KV mention in `edgeone.json` / docs. Ask "does this project use KV?" only when there is no source to read. -> Does this project use KV storage? - -If either check hits, or the user says the project uses KV, **do not deploy anonymously.** Go to **Login** and tell the user why (note the user-facing wording: "without login", never "anonymous"): +If Blob, Agent, AI Gateway, or KV is present, **do not deploy anonymously.** Go to **Login** and tell the user why (user-facing wording: "without login", never "anonymous"): > This project needs environment variables / AI gateway credentials or a storage binding, which a login-free deploy cannot provide. The site would load but those features would fail. Let's log in so it works properly. @@ -356,19 +316,16 @@ Plain static sites and frontend-framework projects with no such dependency may p If the user acknowledges the limitation and still wants to publish without logging in, you may proceed — but state prominently in your result that AI and storage features will not work until the project is claimed and configured (again: phrase it as "login-free / 免登录", never "anonymous / 匿名"). -### Step 2: Decide the path — default to anonymous only when the task fits, otherwise ask - -**Go anonymous directly, without asking, only when the task itself is anonymous-native** — disposable or unattended by nature. Clear signals: +### Step 2: Decide the path — publish means publish -- **Scheduled / automated jobs** — e.g. "每日定时生成一个页面并部署", cron pipelines, any task that must run with nobody watching -- **Disposable one-offs** — a throwaway preview, a quick demo, "先看看效果", anything the user frames as temporary or just-for-now -- **Explicit anonymous intent** — the user says "免登录", "不用登录", "anonymous", or "don't make me sign up" +If the user asked to deploy / 上线 / 发布 and they are not logged in: -In these cases the 60-minute expiry + claim-later model *is* the right answer, so asking is pure friction. Go straight to Step 3, and present the result with the fixed template in Step 4 (claim link + 60-minute window) — the template already tells the user how to keep it if they change their mind. +- Repo needs credentials (Agent / Blob / KV / AI Gateway) → **Login**, say why, then deploy +- Otherwise → **Anonymous deploy** (Step 3). Do not ask first. The Step 4 template already tells them how to keep the project. -**Everything else — ask.** If the task isn't obviously disposable — anything the user might want to keep, share widely, or build on — present the choice. Do NOT try to judge the *environment* to skip the question: sandbox/TTY signals are unreliable and guessing wrong either needlessly interrupts a human or silently deploys something they wanted to keep. +Ask the two-option choice only when intent is genuinely ambiguous (they have not said deploy, or they mixed "save it to my account" with "don't make me sign up"). Do not judge the *environment* to invent a question. -If the environment genuinely gives you no way to ask at all (no TTY, no question tool), asking is impossible — that's a constraint, not a judgment call. Deploy with `--anonymous --json` and make the claim link and the 60-minute window unmissable in your result. +If there is no way to ask at all, deploy with `--anonymous --json` and make the claim link and the 60-minute window unmissable. **How to ask (when asking):** present the choice with the IDE's selection control (e.g. `ask_followup_question`). The option labels deliberately avoid the word "anonymous" — it is jargon and confuses non-technical users. Present these two options **exactly**, in the user's language. Do not paraphrase the labels, do not add caveats to the options themselves, and do not mention "anonymous" to the user at all — when you must name the concept, call it **"login-free deployment" / 「免登录部署」**, never "anonymous deploy / 匿名部署". The option text must be placed in the `label` exactly as is; it must not be split into the `description`. @@ -498,24 +455,9 @@ https://console.cloud.tencent.com/edgeone/pages/project/pages-xxxxxxxx/deploymen | **Project ID** | Value after `EDGEONE_PROJECT_ID=` | — | | **Console URL** | Line after "You can view your deployment..." | — | -**Show the user — the deploy URL MUST be at the very top of the visible reply AND pinned via `present_files` to the side panel (see Rule 4 for why both channels are required):** - -⚠️ **URL Integrity Rules (read before composing your reply):** - -| Rule | Detail | -|------|--------| -| **Every mention must be complete** | If you write the URL in a table, a list, a footnote, a comparison, or any secondary location — it MUST still include the full query string. No exceptions. | -| **No visual "cleanup"** | Do not shorten the URL to make a table look nicer. A truncated URL is broken, not clean. | -| **Concrete, not abstract** | Use the actual URL from deploy output. Do not replace query params with `...` or `(params omitted)` or any placeholder in user-facing text. | -| **Self-check before sending** | Search your draft for `.edgeone.cool` — every hit must have `?eo_token=`. | +**Show the user — complete URL at the top of the visible reply AND via `present_files` (Rule 4). Every `.edgeone.cool` mention must keep `?eo_token=`.** > 🌐 **Live URL**: `https://my-project-abc123.edgeone.cool?eo_token=abc123&eo_time=1234567890` -> -> --- -> -> - **Console URL**: `https://console.cloud.tencent.com/edgeone/pages/project/...` -> -> ℹ️ Note: This preview URL is for quick deployment verification. When accessed from mainland China, the link may become restricted (e.g., 401) after some time or when shared, due to domain ICP filing status or CDN acceleration policies. For long-term stable public access, bind a custom domain with proper ICP filing. --- diff --git a/codex/makers-env-adaption.md b/codex/makers-env-adaption.md index 995aa0f..0999893 100644 --- a/codex/makers-env-adaption.md +++ b/codex/makers-env-adaption.md @@ -17,7 +17,7 @@ validate: message: "Use 127.0.0.1, not localhost — in the sandbox localhost resolves to ::1 and yields false 404s." metadata: author: edgeone - version: "1.4.2" + version: "1.5.0" --- # Runtime Environment Adaptation Guide @@ -105,9 +105,9 @@ edgeone makers dev **Recommended approach**: browser login + the `--local` flag: ```bash -edgeone login --site china --local +edgeone login --site --local ``` -`--local` writes credentials to the project directory at `/.edgeone/auth.json`, bypassing home-directory write restrictions. +Infer `--site` from `whoami` or user context; ask only if unknown. `--local` writes credentials to `/.edgeone/auth.json`, bypassing home-directory write restrictions. **Login status detection**: ```bash @@ -116,7 +116,7 @@ edgeone whoami # exit 0 = logged in, exit 1 = not logged in (does not hang) **When is login actually required?** Login is only needed when the project uses **Blob** or other credentialed backends — and strictly because of the dependency chain: **Blob requires the project to be linked, and linking requires a logged-in account first.** So `edgeone makers dev` for a **pure-static** site runs fine without login — **do NOT force a login prompt for static-only previews**. Login (or `-t `) becomes mandatory the moment dev/deploy must touch Blob storage. (The trigger condition and the link chain live in makers-storage.) -**CLI version requirement**: >= 1.6.7 (older versions lack the non-interactive fixes; whoami will hang) +**CLI version requirement**: >= 1.6.0 (anonymous deploy / `claim`: >= 1.6.29) --- @@ -209,9 +209,8 @@ After finishing development, **start the dev server and preview directly** — d 1. Start `edgeone makers dev --name --skip-env-sync` (**background async**, see §7) 2. Wait 2-3 seconds for the dev server to be ready 3. **Pass `http://127.0.0.1:8088/` to `present_files`** (note it is `127.0.0.1`, **not** `localhost` — see §4) -4. Tell the user: "The project's local preview is running, please check it out. If everything looks good, I can deploy it live for you directly." - -Only after the user confirms, run `edgeone makers deploy -n --json` (**background async**, see §7 and §7.2 — it exceeds the foreground wall-clock budget). +4. If the user already asked to deploy / 上线 / 发布, deploy next (`edgeone makers deploy -n --json`, background — see §7). Do not ask again. +5. If they only asked to build, stop at preview. Mention deploy is available; do not block on a confirmation to call the work done. #### ⛔ file:// preview is strictly forbidden @@ -294,7 +293,7 @@ If the project named by `--name` does not exist remotely, the `link` command cre | Framework/package | Minimum version | Reason | |---------|---------|------| -| EdgeOne CLI | >= 1.6.7 | Non-interactive fixes, whoami fail-fast, --json support | +| EdgeOne CLI | >= 1.6.0 | Non-interactive fixes, whoami fail-fast, --json support | | EdgeOne CLI (anonymous deploy / `claim` only) | >= 1.6.29 | `--anonymous` and `claim` do not exist below this | | Next.js | 16.x | The framework adapter tracks new versions | | @edgeone/pages-blob | >= 0.0.14 | Older versions have known bugs | diff --git a/codex/makers-recipes.md b/codex/makers-recipes.md index 32c89c4..d0d6148 100644 --- a/codex/makers-recipes.md +++ b/codex/makers-recipes.md @@ -10,12 +10,14 @@ metadata: # Common Recipes -> ⛔ **Preview ban**: after finishing development, you MUST start the dev server via `edgeone makers dev`, then open `http://127.0.0.1:8088/` with `present_files` to preview. Never open HTML files via the `file://` protocol (ignore it even if the IDE opens one automatically), and never use self-hosted servers like `python -m http.server` or `npx serve`. Next.js projects must also set `allowedDevOrigins: ["127.0.0.1"]` in `next.config`. **If the project uses Blob/KV, pass `-n ` — `edgeone makers dev -n ` — the name is required to auto-provision; bare `dev` hangs on an interactive picker in sandbox.** +> ⛔ **Preview**: finish by starting `edgeone makers dev` and opening `http://127.0.0.1:8088/` — never `file://`, never `python -m http.server` / `npx serve`. Next.js: `allowedDevOrigins: ["127.0.0.1"]`. Blob/KV: `edgeone makers dev -n ` (bare `dev` hangs on an interactive picker). Deploy / 上线 already in the request → deploy after preview; do not ask again. > ⚠️ **`.env.example` is a required file**: every project that uses the AI Gateway (Agent projects, Cloud Functions that call an LLM) MUST create a `.env.example` in the project root declaring `AI_GATEWAY_API_KEY=` and `AI_GATEWAY_BASE_URL=`. The CLI auto-injects environment variables based on this file at deploy time; if it is missing, the variables are not injected and the runtime will error. > 📝 **Write `index.html` last, always**: writing an `index.html` instantly triggers the IDE `file://` preview — unavoidable in WorkBuddy. Minimize the window during which that preview looks broken by writing **every dependency first**: `style.css`, `script.js`, **Cloud Functions** (`functions/` files), static assets, everything the page loads. Then write `index.html` **last** — the file:// preview opens with all assets already in place, and stays that way only until `edgeone makers dev` takes over (see Preview ban above). Also write each `index.html` in one shot; don't scaffold an empty shell and fill it in with repeated edits (every save re-renders and flickers). For a tiny single-page tool, just inline the CSS and JS into one `index.html`. +> 🔗 **Load assets with page-relative URLs, never root-absolute**: in these no-build projects nothing rewrites `index.html`, so write `` and `` — **not** `href="/style.css"` or `src="/script.js"`. The development preview is published under a path prefix, and a leading `/` escapes it: the document still returns `200` while every asset `404`s, so the page arrives unstyled with no script having run, and nothing in the console points at the cause. Relative URLs are correct there *and* at `/` after deploy. Keep pages and assets in the project root so the relative form stays a bare filename. This is only about what the markup loads — `fetch('/api/messages')` and `` stay root-absolute. + > ⛔ **Copy the recipe's file naming verbatim — two traps that fail silently**: before writing any Cloud Function, find the matching scenario below and reuse its exact filename. Getting the name wrong usually does NOT throw a clear error — it falls back silently: > 1. **Every function file MUST carry its language extension** — `.js` (Node), `.py` (Python), `.go` (Go). A file with no extension (e.g. `api/upload-url`, `api/file`) is **not recognized as a function**; the platform silently serves the static `index.html` fallback, so `/api/*` "mysteriously" returns HTML instead of JSON. Name them `api/upload-url.js`, `api/file.js`. > 2. **`[[default]].js` is the catch-all for its own directory (`api/[[default]].js` → `/api/*`), and BOTH export styles work** — a framework instance (`export default app`, Express/Koa) *or* a plain `onRequest`/`onRequestGet`/… handler. Verified locally with `edgeone makers dev`: a bare `onRequest` in `[[default]].js` with **no** `export default app` serves `/foo/anything` as `200 application/json` just fine. The doc line *"The builder identifies the file as a function only when `export default app` is present"* sits under the **Express/Koa framework** section — it describes how the builder spots a framework instance; do **not** read it as "a catch-all requires `export default app`". ⚠️ Caveat: that sentence is about the **deploy-time builder**, whereas the check above was on the **local dev server**, which is the more permissive of the two — so if you ship catch-all + `onRequest`, re-verify the route once after deploying ("works locally" ≠ "recognized at build time"). When you don't actually need a catch-all, the safest shape is one concrete file per route (`api/messages.js`, `api/artworks/[id]/like.js`), params via `[id]` folders/files, extra args as query strings (`/api/file?key=...`). @@ -63,7 +65,7 @@ my-app/ import { getStore } from "@edgeone/pages-blob"; export async function onRequest({ request }) { - const store = getStore("guestbook"); + const store = getStore({ name: "guestbook", consistency: "strong" }); if (request.method === "POST") { const { name, text } = await request.json(); @@ -79,7 +81,12 @@ export async function onRequest({ request }) { } ``` -**index.html** frontend calls it like any API: +**index.html** loads its assets relative to itself, and calls the API root-absolute: +```html + + +``` + ```javascript await fetch('/api/messages', { // post method: 'POST', diff --git a/cursor/rules/makers-agents.mdc b/cursor/rules/makers-agents.mdc index 0934883..01c77d0 100644 --- a/cursor/rules/makers-agents.mdc +++ b/cursor/rules/makers-agents.mdc @@ -11,8 +11,8 @@ description: >- agent endpoint", "wire LangGraph into Makers", "stream LLM responses with SSE", "review my agent template", "use context.store / context.sandbox / context.tools". Do NOT trigger for plain Edge Functions, Cloud Functions, or middleware - (those don't run AI logic — use edgeone-pages-dev instead). - Do NOT trigger for deployment workflows (use edgeone-pages-deploy). + (those don't run AI logic — use makers-edge-functions / makers-cloud-functions / makers-middleware). + Do NOT trigger for deployment workflows (use makers-deploy). Do NOT trigger for generic AI framework development outside an EdgeOne Makers project. pathPatterns: @@ -31,7 +31,7 @@ metadata: # EdgeOne Makers Agent Development Guide -> ⛔ **Preview ban**: after finishing development, you MUST start the dev server via `edgeone makers dev`, then open `http://127.0.0.1:8088/` with `present_files` to preview. Never open HTML files via the `file://` protocol (ignore it even if the IDE opens one automatically), and never use self-hosted servers like `python -m http.server` or `npx serve`. Next.js projects must also set `allowedDevOrigins: ["127.0.0.1"]` in `next.config`. +> ⛔ **Preview**: `edgeone makers dev` + `http://127.0.0.1:8088/`. Never `file://` or `python -m http.server` / `npx serve`. Next.js: `allowedDevOrigins: ["127.0.0.1"]`. Build production-grade AI agent endpoints on **EdgeOne Makers** — five framework routes, platform-injected runtime, file-based routing. @@ -50,8 +50,8 @@ This skill covers five supported frameworks (DeepAgents, LangGraph, CrewAI, Open > Cross-reference: if your code uses `context.store` or KV APIs, also read `../makers-storage/SKILL.md`. **Do NOT use for:** -- Plain Edge Functions / Cloud Functions / Middleware → use `edgeone-pages-dev` -- Deployment workflows → use `edgeone-pages-deploy` +- Plain Edge Functions / Cloud Functions / Middleware → `makers-edge-functions` / `makers-cloud-functions` / `makers-middleware` +- Deployment workflows → `makers-deploy` - Generic AI framework development outside an EdgeOne Makers project - Other platforms (Cloudflare Workers AI, Vercel AI SDK, AWS Bedrock) @@ -61,7 +61,7 @@ This skill covers five supported frameworks (DeepAgents, LangGraph, CrewAI, Open 2. Walk the **Decision Tree** to pick one of the five framework routes 3. Read the matching `references/*-route.md` for a copy-paste skeleton 4. Self-check against the **Twelve Red Lines** -5. Run through `references/review-checklist.md` before considering the work done +5. Read `references/review-checklist.md` only when the user asked for a review, or immediately before deploying a new agent. Otherwise ship. ## ⛔ Critical Rules (never skip) diff --git a/cursor/rules/makers-deploy.mdc b/cursor/rules/makers-deploy.mdc index 6eb7b00..16e9785 100644 --- a/cursor/rules/makers-deploy.mdc +++ b/cursor/rules/makers-deploy.mdc @@ -12,19 +12,18 @@ description: >- "deploy without login", "no account yet", "anonymous deploy", "claim project", "claim my deployment", "免登录部署", "匿名部署", "还没有账号", "认领项目". - ⚠️ Also trigger when any agent is about to execute `edgeone makers deploy` or `edgeone makers deploy` - commands — the skill contains critical rules for parsing deploy output and presenting access URLs. - Do NOT trigger for post-deployment runtime errors (e.g. CORS issues, 500 errors after deploy — - use edgeone-makers-dev for troubleshooting). + ⚠️ Also trigger when any agent is about to execute `edgeone makers deploy`. + Do NOT trigger for post-deployment runtime errors (CORS, 500 after deploy) — + use makers-env-adaption / makers-recipes for local troubleshooting. pathPatterns: - "*.sh" - .github/workflows/** validate: - pattern: "whoami[^\\n]*\\s-t\\s" message: "edgeone whoami does not accept -t. Check the exit code instead: 0 = logged in, 1 = not." -metadata: + metadata: author: edgeone - version: "2.12.3" + version: "2.13.0" --- # EdgeOne Makers Deployment Skill @@ -56,12 +55,12 @@ Deploy any project to **EdgeOne Makers**. Then append any other notes (console URL, caveats, etc.). **Self-check before ending the turn (MANDATORY)** — read back what the user will actually SEE (NOT your thinking / reasoning content). Two questions: (a) Is the complete `.edgeone.cool` URL present at the top of the visible reply, in a code block or heading? (b) Was `present_files` called with that URL? If either answer is no, send an **additional short message** containing ONLY the `🌐 Live URL: ` block and call `present_files`. Do not end the turn until both channels carry the URL. "I already mentioned it in my reasoning" is NOT a substitute for placing it in the visible body. -5. **Ask the user to choose China or Global site** before browser login. Never assume. (Token login via `edgeone login --token` auto-detects site, no need to ask.) -6. **Prefer Browser Login; fall back to Token only after browser login is confirmed to fail** (see Login section for the ~60s fallback threshold and the Agent-in-IDE clarification — WorkBuddy is NOT headless). Token-first only when the user explicitly requests it. -7. **After token login, ask if the user wants to save the token locally** for future use. -8. **Before triggering any browser popup (login / registration), explain the reason and the benefits to the user first** — never silently launch a browser window. +5. **Browser login needs `--site`.** Infer china/global from `whoami`, an existing credential, or the user's context. Ask only when you must run `edgeone login` and the site is still unknown. Token login and anonymous deploy detect the site themselves — do not ask. +6. **Prefer Browser Login; fall back to Token only after browser login is confirmed to fail** (see Login section for the ~60s fallback threshold — WorkBuddy is NOT headless). Token-first only when the user explicitly requests it or provides a token. +7. **If the user provides a token, persist it** with `edgeone login --token` (or `--local` in a sandbox). Do not ask whether to save it. Never commit credentials. Do not treat `.edgeone/.token` as the CLI source of truth — credentials live in `~/.edgeone/` or `.edgeone/auth.json`. +8. **Before a browser popup, one sentence of why**, then run the command. Do not write a registration essay. 9. **On any CLI failure, surface the actual error text to the user before retrying, switching commands, or proposing a workaround.** Do NOT paraphrase (e.g. don't rewrite `Makers project exceeds 40 limit` into "maybe a name conflict or permission"). Do NOT silently pivot from `makers dev` to `makers deploy` (or vice versa) hoping to bypass — a systemic failure (auth / quota / permission) hits both with the same cause. Quote the raw error, name the root cause, then propose the fix or ask the user. -10. **Write every user-facing message in the user's own language** — **all** prose, blockquotes, and prompts in this skill are written in English purely to specify *meaning*; none of it is a string to paste. If the user writes to you in Chinese, speak Chinese — the login explanation, the site choice, the token question, the deploy result, all of it. Likewise for any other language. Emitting this skill's English strings into a non-English conversation is a bug. Only literal CLI commands, flags, env var names, and JSON field names stay verbatim. ⚠️ Where this skill gives a **fixed template** (Steps 2 and 4 of Anonymous Deploy), translating it is required but rewriting or extending it is not allowed — match it line for line. +10. **Write every user-facing message in the user's own language** — **all** prose, blockquotes, and prompts in this skill are written in English purely to specify *meaning*; none of it is a string to paste. If the user writes to you in Chinese, speak Chinese — the login explanation, any site choice, the deploy result, all of it. Likewise for any other language. Emitting this skill's English strings into a non-English conversation is a bug. Only literal CLI commands, flags, env var names, and JSON field names stay verbatim. ⚠️ Where this skill gives a **fixed template** (Steps 2 and 4 of Anonymous Deploy), translating it is required but rewriting or extending it is not allowed — match it line for line. **Rules 11-14 apply to the anonymous deploy / claim flow only:** @@ -124,7 +123,7 @@ cat edgeone.json 2>/dev/null | Not installed or < 1.6.0 | — | → Go to **Install CLI** | | `≥ 1.6.0` ✓ | Logged in (or token present) | → Go to **Deploy** | | `≥ 1.6.0` ✓ | Not logged in, has saved token | → Go to **Deploy with Token** (use saved token) | -| `≥ 1.6.29` ✓ | Not logged in, no saved token | → Go to **Anonymous Deploy** — default to anonymous only when the task is anonymous-native (scheduled / disposable / user asked for it); otherwise ask the user to choose anonymous deploy or login. If there is no way to ask, deploy with `--anonymous --json` and surface the claim link and the 60-minute window in the result (see Step 2) | +| `≥ 1.6.29` ✓ | Not logged in, no saved token | → Go to **Anonymous Deploy**. If the repo needs login (Agent / Blob / KV / AI Gateway), go to **Login** instead and say why. Ask the two-option choice only when publish intent is genuinely ambiguous. | | `1.6.0`–`1.6.28` | Not logged in, no saved token | Anonymous deploy is unavailable on this version → **Try Browser Login first** (see Login section). If the browser doesn't open or nothing happens within ~60 seconds, fall back to **Token Login**. Do NOT preemptively skip browser login by guessing "this looks like an Agent/CI environment" — that guess is often wrong; in particular, **WorkBuddy is a desktop IDE sandbox and fully supports browser login** | | `≥ 1.6.0` ✓ | User explicitly provides a token or requests token login | → Go to **Deploy with Token** / **Token Login** | @@ -142,34 +141,20 @@ Verify: `edgeone -v` — confirm output is `1.6.0` or higher. Retry installation ## Login -### 0. Explain the registration/login step +### 0. One sentence, then login -Before triggering any login flow, explain to the user **why** this step is needed and **what** to expect. Do not silently launch a browser window. +Before a browser login, say one sentence: you need an EdgeOne Makers account so the site lands on their account with a live URL. Then run the command. Do not silently launch a browser, and do not write a four-bullet registration pitch. -Tell the user: +If nothing happens within ~60 seconds (no popup, no CLI progress), switch to Token login. Do not wait indefinitely, and do not poll the user every turn. -> You need to log in or register an EdgeOne Makers account. Here's what to expect: -> - **Why login is required**: Deployment uploads your build output to your own account, generating a unique access URL and project record. -> - **What you get for free**: EdgeOne Makers offers a free tier with global CDN acceleration, automatic HTTPS, and custom domain binding — typically more than enough for personal projects. -> - **What happens next**: I'll run `edgeone login`, and your default browser will open the Tencent Cloud login page. Please complete the login/registration and authorize access, then come back here. -> - **If you get stuck**: If the browser doesn't open, or the CLI keeps waiting after you've logged in, let me know — I'll switch to Token login instead. +### 1. Pass `--site` — infer first, ask only if unknown -If the user does not respond within ~60 seconds (no browser popup or no progress reported), **proactively ask** about their status (whether the browser opened, any errors, or if they want to switch to Token login). Do not wait indefinitely. +`edgeone login` in a non-interactive context **requires** `--site `. Never run a bare `edgeone login`. -### 1. Ask the user to choose a site, then ALWAYS pass `--site` +Infer the site from `whoami`, an existing credential, or the user's context (language + console links they already used). Ask with a two-option control only when the site is still unknown: -Use the IDE's selection control (`ask_followup_question`) before running any login command: - -> Choose your EdgeOne Makers site: -> - **China** — For users in mainland China (console.cloud.tencent.com) -> - **Global** — For users outside China (console.intl.cloud.tencent.com) - -⚠️ **CRITICAL**: After the user chooses, you MUST invoke login with an explicit -`--site ` flag (e.g. `edgeone login --site china`). -**NEVER run a bare `edgeone login` (without `--site`) when driven by an Agent / skill.** -A bare `login` in a non-interactive context fails fast asking for -`--site` (it no longer pops an interactive site-picker that would hang). The site choice -is meant to happen here in the conversation, not inside the CLI. +> - **China** — console.cloud.tencent.com +> - **Global** — console.intl.cloud.tencent.com ### 2. Login methods reference @@ -230,23 +215,9 @@ Guide the user to obtain a token: ⚠️ Remind the user: the token has account-level permissions. Never commit it to a repository. -### 3. Offer to save the token locally - -After the user provides a token, ask: - -> Save this token locally for future deployments? -> - **Yes** — Save to `.edgeone/.token` (auto-used next time) -> - **No** — Use for this deployment only - -**If Yes:** - -```bash -mkdir -p .edgeone -echo "" > .edgeone/.token -grep -q '.edgeone/.token' .gitignore 2>/dev/null || echo '.edgeone/.token' >> .gitignore -``` +### 3. Persist a user-provided token -Confirm to the user: "✅ Token saved to `.edgeone/.token` and added to `.gitignore`." +If the user hands you a token, run `edgeone login --token ` (or `--local` in a sandbox that cannot write `~/.edgeone/`). Ensure `.edgeone/` is gitignored. Do not ask whether to save it, and do not invent a `.edgeone/.token` file — that is not the CLI credential store. --- @@ -266,26 +237,17 @@ edgeone makers deploy -n ### Token-based deploy (Makers projects) -First check for a saved token: - -```bash -cat .edgeone/.token 2>/dev/null -``` - -- Saved token found → use it, tell the user: "Using saved token from `.edgeone/.token`" -- No saved token → ask the user to provide one (see Token Login above) +Use a token already in the environment (`EDGEONE_PAGES_API_TOKEN`), one the user just provided, or the CLI store from `edgeone login --token` / `--local`. Do not `cat .edgeone/.token`. ```bash # Project already linked -edgeone makers deploy -t +edgeone makers deploy -t --json # New project -edgeone makers deploy -n -t +edgeone makers deploy -n -t --json ``` -The token already contains site info — no `--site` flag needed. - -After a successful deploy with a manually-entered token, ask if the user wants to save it (see "Offer to save the token locally" above). +The token already contains site info — no `--site` flag needed. If they typed the token in chat, persist it (see Login §3) and then deploy. ### Deploy to preview environment @@ -344,11 +306,9 @@ Non-empty output from either grep means the project uses Blob. ⛔ **All checks in this flow are silent — never narrate them to the user.** Do not report things like "CLI 版本 x.x(支持匿名部署)", "页面是纯静态、无 Blob/KV 依赖,可直接发布", or any other version/eligibility check result. These are internal reasoning; a non-technical user cannot act on them and should never see them. The only time you speak about a check is when it **changes the outcome** — e.g. the project needs login (below), or the CLI is too old and needs upgrading. Passing checks produce no message at all. -**KV cannot be detected this way — you must ask.** A KV namespace is bound in the console and exposed as a *global variable* whose name the user chose (e.g. `my_kv`), so there is no package import to grep for. There is no `@edgeone/pages-kv` package. Ask the user directly: +**KV has no npm package.** Infer it from the workspace: a console-bound global (e.g. `my_kv.get` / `my_kv.put`) in `edge-functions/`, or a KV mention in `edgeone.json` / docs. Ask "does this project use KV?" only when there is no source to read. -> Does this project use KV storage? - -If either check hits, or the user says the project uses KV, **do not deploy anonymously.** Go to **Login** and tell the user why (note the user-facing wording: "without login", never "anonymous"): +If Blob, Agent, AI Gateway, or KV is present, **do not deploy anonymously.** Go to **Login** and tell the user why (user-facing wording: "without login", never "anonymous"): > This project needs environment variables / AI gateway credentials or a storage binding, which a login-free deploy cannot provide. The site would load but those features would fail. Let's log in so it works properly. @@ -356,19 +316,16 @@ Plain static sites and frontend-framework projects with no such dependency may p If the user acknowledges the limitation and still wants to publish without logging in, you may proceed — but state prominently in your result that AI and storage features will not work until the project is claimed and configured (again: phrase it as "login-free / 免登录", never "anonymous / 匿名"). -### Step 2: Decide the path — default to anonymous only when the task fits, otherwise ask - -**Go anonymous directly, without asking, only when the task itself is anonymous-native** — disposable or unattended by nature. Clear signals: +### Step 2: Decide the path — publish means publish -- **Scheduled / automated jobs** — e.g. "每日定时生成一个页面并部署", cron pipelines, any task that must run with nobody watching -- **Disposable one-offs** — a throwaway preview, a quick demo, "先看看效果", anything the user frames as temporary or just-for-now -- **Explicit anonymous intent** — the user says "免登录", "不用登录", "anonymous", or "don't make me sign up" +If the user asked to deploy / 上线 / 发布 and they are not logged in: -In these cases the 60-minute expiry + claim-later model *is* the right answer, so asking is pure friction. Go straight to Step 3, and present the result with the fixed template in Step 4 (claim link + 60-minute window) — the template already tells the user how to keep it if they change their mind. +- Repo needs credentials (Agent / Blob / KV / AI Gateway) → **Login**, say why, then deploy +- Otherwise → **Anonymous deploy** (Step 3). Do not ask first. The Step 4 template already tells them how to keep the project. -**Everything else — ask.** If the task isn't obviously disposable — anything the user might want to keep, share widely, or build on — present the choice. Do NOT try to judge the *environment* to skip the question: sandbox/TTY signals are unreliable and guessing wrong either needlessly interrupts a human or silently deploys something they wanted to keep. +Ask the two-option choice only when intent is genuinely ambiguous (they have not said deploy, or they mixed "save it to my account" with "don't make me sign up"). Do not judge the *environment* to invent a question. -If the environment genuinely gives you no way to ask at all (no TTY, no question tool), asking is impossible — that's a constraint, not a judgment call. Deploy with `--anonymous --json` and make the claim link and the 60-minute window unmissable in your result. +If there is no way to ask at all, deploy with `--anonymous --json` and make the claim link and the 60-minute window unmissable. **How to ask (when asking):** present the choice with the IDE's selection control (e.g. `ask_followup_question`). The option labels deliberately avoid the word "anonymous" — it is jargon and confuses non-technical users. Present these two options **exactly**, in the user's language. Do not paraphrase the labels, do not add caveats to the options themselves, and do not mention "anonymous" to the user at all — when you must name the concept, call it **"login-free deployment" / 「免登录部署」**, never "anonymous deploy / 匿名部署". The option text must be placed in the `label` exactly as is; it must not be split into the `description`. @@ -498,24 +455,9 @@ https://console.cloud.tencent.com/edgeone/pages/project/pages-xxxxxxxx/deploymen | **Project ID** | Value after `EDGEONE_PROJECT_ID=` | — | | **Console URL** | Line after "You can view your deployment..." | — | -**Show the user — the deploy URL MUST be at the very top of the visible reply AND pinned via `present_files` to the side panel (see Rule 4 for why both channels are required):** - -⚠️ **URL Integrity Rules (read before composing your reply):** - -| Rule | Detail | -|------|--------| -| **Every mention must be complete** | If you write the URL in a table, a list, a footnote, a comparison, or any secondary location — it MUST still include the full query string. No exceptions. | -| **No visual "cleanup"** | Do not shorten the URL to make a table look nicer. A truncated URL is broken, not clean. | -| **Concrete, not abstract** | Use the actual URL from deploy output. Do not replace query params with `...` or `(params omitted)` or any placeholder in user-facing text. | -| **Self-check before sending** | Search your draft for `.edgeone.cool` — every hit must have `?eo_token=`. | +**Show the user — complete URL at the top of the visible reply AND via `present_files` (Rule 4). Every `.edgeone.cool` mention must keep `?eo_token=`.** > 🌐 **Live URL**: `https://my-project-abc123.edgeone.cool?eo_token=abc123&eo_time=1234567890` -> -> --- -> -> - **Console URL**: `https://console.cloud.tencent.com/edgeone/pages/project/...` -> -> ℹ️ Note: This preview URL is for quick deployment verification. When accessed from mainland China, the link may become restricted (e.g., 401) after some time or when shared, due to domain ICP filing status or CDN acceleration policies. For long-term stable public access, bind a custom domain with proper ICP filing. --- diff --git a/cursor/rules/makers-env-adaption.mdc b/cursor/rules/makers-env-adaption.mdc index 995aa0f..0999893 100644 --- a/cursor/rules/makers-env-adaption.mdc +++ b/cursor/rules/makers-env-adaption.mdc @@ -17,7 +17,7 @@ validate: message: "Use 127.0.0.1, not localhost — in the sandbox localhost resolves to ::1 and yields false 404s." metadata: author: edgeone - version: "1.4.2" + version: "1.5.0" --- # Runtime Environment Adaptation Guide @@ -105,9 +105,9 @@ edgeone makers dev **Recommended approach**: browser login + the `--local` flag: ```bash -edgeone login --site china --local +edgeone login --site --local ``` -`--local` writes credentials to the project directory at `/.edgeone/auth.json`, bypassing home-directory write restrictions. +Infer `--site` from `whoami` or user context; ask only if unknown. `--local` writes credentials to `/.edgeone/auth.json`, bypassing home-directory write restrictions. **Login status detection**: ```bash @@ -116,7 +116,7 @@ edgeone whoami # exit 0 = logged in, exit 1 = not logged in (does not hang) **When is login actually required?** Login is only needed when the project uses **Blob** or other credentialed backends — and strictly because of the dependency chain: **Blob requires the project to be linked, and linking requires a logged-in account first.** So `edgeone makers dev` for a **pure-static** site runs fine without login — **do NOT force a login prompt for static-only previews**. Login (or `-t `) becomes mandatory the moment dev/deploy must touch Blob storage. (The trigger condition and the link chain live in makers-storage.) -**CLI version requirement**: >= 1.6.7 (older versions lack the non-interactive fixes; whoami will hang) +**CLI version requirement**: >= 1.6.0 (anonymous deploy / `claim`: >= 1.6.29) --- @@ -209,9 +209,8 @@ After finishing development, **start the dev server and preview directly** — d 1. Start `edgeone makers dev --name --skip-env-sync` (**background async**, see §7) 2. Wait 2-3 seconds for the dev server to be ready 3. **Pass `http://127.0.0.1:8088/` to `present_files`** (note it is `127.0.0.1`, **not** `localhost` — see §4) -4. Tell the user: "The project's local preview is running, please check it out. If everything looks good, I can deploy it live for you directly." - -Only after the user confirms, run `edgeone makers deploy -n --json` (**background async**, see §7 and §7.2 — it exceeds the foreground wall-clock budget). +4. If the user already asked to deploy / 上线 / 发布, deploy next (`edgeone makers deploy -n --json`, background — see §7). Do not ask again. +5. If they only asked to build, stop at preview. Mention deploy is available; do not block on a confirmation to call the work done. #### ⛔ file:// preview is strictly forbidden @@ -294,7 +293,7 @@ If the project named by `--name` does not exist remotely, the `link` command cre | Framework/package | Minimum version | Reason | |---------|---------|------| -| EdgeOne CLI | >= 1.6.7 | Non-interactive fixes, whoami fail-fast, --json support | +| EdgeOne CLI | >= 1.6.0 | Non-interactive fixes, whoami fail-fast, --json support | | EdgeOne CLI (anonymous deploy / `claim` only) | >= 1.6.29 | `--anonymous` and `claim` do not exist below this | | Next.js | 16.x | The framework adapter tracks new versions | | @edgeone/pages-blob | >= 0.0.14 | Older versions have known bugs | diff --git a/cursor/rules/makers-recipes.mdc b/cursor/rules/makers-recipes.mdc index 32c89c4..d0d6148 100644 --- a/cursor/rules/makers-recipes.mdc +++ b/cursor/rules/makers-recipes.mdc @@ -10,12 +10,14 @@ metadata: # Common Recipes -> ⛔ **Preview ban**: after finishing development, you MUST start the dev server via `edgeone makers dev`, then open `http://127.0.0.1:8088/` with `present_files` to preview. Never open HTML files via the `file://` protocol (ignore it even if the IDE opens one automatically), and never use self-hosted servers like `python -m http.server` or `npx serve`. Next.js projects must also set `allowedDevOrigins: ["127.0.0.1"]` in `next.config`. **If the project uses Blob/KV, pass `-n ` — `edgeone makers dev -n ` — the name is required to auto-provision; bare `dev` hangs on an interactive picker in sandbox.** +> ⛔ **Preview**: finish by starting `edgeone makers dev` and opening `http://127.0.0.1:8088/` — never `file://`, never `python -m http.server` / `npx serve`. Next.js: `allowedDevOrigins: ["127.0.0.1"]`. Blob/KV: `edgeone makers dev -n ` (bare `dev` hangs on an interactive picker). Deploy / 上线 already in the request → deploy after preview; do not ask again. > ⚠️ **`.env.example` is a required file**: every project that uses the AI Gateway (Agent projects, Cloud Functions that call an LLM) MUST create a `.env.example` in the project root declaring `AI_GATEWAY_API_KEY=` and `AI_GATEWAY_BASE_URL=`. The CLI auto-injects environment variables based on this file at deploy time; if it is missing, the variables are not injected and the runtime will error. > 📝 **Write `index.html` last, always**: writing an `index.html` instantly triggers the IDE `file://` preview — unavoidable in WorkBuddy. Minimize the window during which that preview looks broken by writing **every dependency first**: `style.css`, `script.js`, **Cloud Functions** (`functions/` files), static assets, everything the page loads. Then write `index.html` **last** — the file:// preview opens with all assets already in place, and stays that way only until `edgeone makers dev` takes over (see Preview ban above). Also write each `index.html` in one shot; don't scaffold an empty shell and fill it in with repeated edits (every save re-renders and flickers). For a tiny single-page tool, just inline the CSS and JS into one `index.html`. +> 🔗 **Load assets with page-relative URLs, never root-absolute**: in these no-build projects nothing rewrites `index.html`, so write `` and `` — **not** `href="/style.css"` or `src="/script.js"`. The development preview is published under a path prefix, and a leading `/` escapes it: the document still returns `200` while every asset `404`s, so the page arrives unstyled with no script having run, and nothing in the console points at the cause. Relative URLs are correct there *and* at `/` after deploy. Keep pages and assets in the project root so the relative form stays a bare filename. This is only about what the markup loads — `fetch('/api/messages')` and `` stay root-absolute. + > ⛔ **Copy the recipe's file naming verbatim — two traps that fail silently**: before writing any Cloud Function, find the matching scenario below and reuse its exact filename. Getting the name wrong usually does NOT throw a clear error — it falls back silently: > 1. **Every function file MUST carry its language extension** — `.js` (Node), `.py` (Python), `.go` (Go). A file with no extension (e.g. `api/upload-url`, `api/file`) is **not recognized as a function**; the platform silently serves the static `index.html` fallback, so `/api/*` "mysteriously" returns HTML instead of JSON. Name them `api/upload-url.js`, `api/file.js`. > 2. **`[[default]].js` is the catch-all for its own directory (`api/[[default]].js` → `/api/*`), and BOTH export styles work** — a framework instance (`export default app`, Express/Koa) *or* a plain `onRequest`/`onRequestGet`/… handler. Verified locally with `edgeone makers dev`: a bare `onRequest` in `[[default]].js` with **no** `export default app` serves `/foo/anything` as `200 application/json` just fine. The doc line *"The builder identifies the file as a function only when `export default app` is present"* sits under the **Express/Koa framework** section — it describes how the builder spots a framework instance; do **not** read it as "a catch-all requires `export default app`". ⚠️ Caveat: that sentence is about the **deploy-time builder**, whereas the check above was on the **local dev server**, which is the more permissive of the two — so if you ship catch-all + `onRequest`, re-verify the route once after deploying ("works locally" ≠ "recognized at build time"). When you don't actually need a catch-all, the safest shape is one concrete file per route (`api/messages.js`, `api/artworks/[id]/like.js`), params via `[id]` folders/files, extra args as query strings (`/api/file?key=...`). @@ -63,7 +65,7 @@ my-app/ import { getStore } from "@edgeone/pages-blob"; export async function onRequest({ request }) { - const store = getStore("guestbook"); + const store = getStore({ name: "guestbook", consistency: "strong" }); if (request.method === "POST") { const { name, text } = await request.json(); @@ -79,7 +81,12 @@ export async function onRequest({ request }) { } ``` -**index.html** frontend calls it like any API: +**index.html** loads its assets relative to itself, and calls the API root-absolute: +```html + + +``` + ```javascript await fetch('/api/messages', { // post method: 'POST', diff --git a/skills/edgeone-makers-tools/SKILL.md b/skills/edgeone-makers-tools/SKILL.md index cc9cf6b..3374ede 100644 --- a/skills/edgeone-makers-tools/SKILL.md +++ b/skills/edgeone-makers-tools/SKILL.md @@ -9,35 +9,76 @@ description: >- develops, scaffolds, or deploys anything on EdgeOne Makers / EdgeOne Pages: AI agents (DeepAgents, LangGraph, CrewAI, OpenAI/Claude SDK), Cloud Functions (Node/Go/Python), Edge Functions (V8), KV + Blob storage, middleware, CLI usage, project scaffolding, - and — importantly — persisting dynamic site data (messages, uploads, votes, save-state) - where there is NO managed database, so Blob is used as the backend. Also trigger on - "deploy to EdgeOne", "上线", "发布", "部署到 EdgeOne". This SKILL is a routing table; - read only the sub-skill relevant to the current task, never all of them at once. + framework adapters, migration, and persisting dynamic site data (messages, uploads, + votes, save-state) where there is NO managed database, so Blob is the backend. + Also trigger on "deploy to EdgeOne", "上线", "发布", "部署到 EdgeOne". This SKILL is a + routing table plus an operating contract; read only the sub-skill relevant to the + current task, never all of them at once. metadata: author: edgeone - version: "2.0.0" + version: "2.1.0" --- # EdgeOne Makers Skills -When you need EdgeOne Makers platform development guidance, read the matching Skill based on the task: +These files are platform facts and hard constraints for a frontier model. +They are not a tutorial and not a permission script. Infer, then act. + +## Operating contract + +### Autonomy + +Execute the request. Do not narrate version / login / eligibility checks, and do not ask whether to continue a step the user already asked for. + +- Build or iterate → start `edgeone makers dev` and preview on HTTP. Do not ask "要预览吗?" +- Deploy / 上线 / 发布 is already in the request → deploy after a working preview. Do not wait for a second confirmation. +- You have the shell. Never print a command for the user to type unless the only remaining step is a browser login or claim. + +### Clarification + +Ask only when a wrong guess is expensive **and** the answer is not in the repo, the conversation, `whoami`, or the CLI. + +Do **not** ask: + +- China vs Global when `whoami` already bound a site, a token is in use, or the CLI can detect the site (anonymous deploy) +- Whether the project uses KV / Blob / AI Gateway when the workspace is readable — grep it +- Whether to save a token the user just provided — persist with `edgeone login --token` or `--local`, and keep `.edgeone/` gitignored +- Whether to preview, or whether to deploy when they already said deploy + +### Approval + +Require an explicit user decision only for: + +- Deleting or overwriting a production project they did not name +- Using a secret they have not provided +- Account-destructive actions + +Do **not** require approval for: installing the CLI, `makers dev`, linking a new project named from the directory, login-free deploy when they asked to publish and are not logged in, saving credentials locally. + +### Done + +- Dev work is done when the preview is `http://127.0.0.1:8088/` via `edgeone makers dev` — never `file://`, never `python -m http.server` / `npx serve`. +- Deploy work is done when the user can click a complete `.edgeone.cool` URL (keep `?eo_token=` / `?eo_time=`). Then stop. Do not add a lecture. +- If a constraint blocks completion, quote the raw CLI error and the one unblock action. Do not stop to ask "要继续吗?" + +Hard constraints that still apply: never truncate a deploy URL; never delete projects yourself; never hand-edit `.edgeone/agent-node/config.json`; Blob uses `getStore({ name, consistency: "strong" })`. + +When a request names a full-stack framework, read `makers-frameworks` before writing config — a missing adapter previews correctly and deploys broken. + +## Load one skill | Task | Read | |------|------| -| Web framework support — which adapter, build output, 404, and unsupported features (Next.js, Nuxt, Astro, SvelteKit, React Router, TanStack Start, Vike, Vite, Hugo) | references/makers-frameworks/SKILL.md | +| Web framework support — adapter, build output, 404, unsupported features | references/makers-frameworks/SKILL.md | | AI Agent development (DeepAgents, LangGraph, Claude SDK, OpenAI Agents, CrewAI) | references/makers-agents/SKILL.md | -| Deploy project to EdgeOne | references/makers-deploy/SKILL.md | -| Edge Functions (V8 lightweight functions) | references/makers-edge-functions/SKILL.md | -| Cloud Functions (Node.js / Go / Python APIs) | references/makers-cloud-functions/SKILL.md | -| KV + Blob Storage | references/makers-storage/SKILL.md | -| Persist dynamic data for a site (messages, uploads, votes, save-state) — **no database; use Blob** | references/makers-storage/SKILL.md | +| Migrate an existing agent project to EdgeOne Makers | references/makers-migration/SKILL.md | +| Deploy to EdgeOne | references/makers-deploy/SKILL.md | +| Edge Functions (V8) | references/makers-edge-functions/SKILL.md | +| Cloud Functions (Node.js / Go / Python) | references/makers-cloud-functions/SKILL.md | +| KV + Blob, or persist dynamic site data (no database — use Blob) | references/makers-storage/SKILL.md | | Middleware (auth, rewrites, routing) | references/makers-middleware/SKILL.md | | CLI command reference | references/makers-cli/SKILL.md | | Project structure / scaffolding | references/makers-recipes/SKILL.md | | Environment adaptation (WorkBuddy / sandbox / CI) | references/makers-env-adaption/SKILL.md | -⚠️ Only read the Skill relevant to the current task. Do not load all skills at once. - -A full-stack framework that needs a platform adapter previews correctly and deploys -broken without one, and no gate catches it — so when a request names a framework, read -`makers-frameworks` before writing the config. +Read only the skill that matches the current task. diff --git a/skills/edgeone-makers-tools/references/makers-agents/SKILL.md b/skills/edgeone-makers-tools/references/makers-agents/SKILL.md index 0934883..01c77d0 100644 --- a/skills/edgeone-makers-tools/references/makers-agents/SKILL.md +++ b/skills/edgeone-makers-tools/references/makers-agents/SKILL.md @@ -11,8 +11,8 @@ description: >- agent endpoint", "wire LangGraph into Makers", "stream LLM responses with SSE", "review my agent template", "use context.store / context.sandbox / context.tools". Do NOT trigger for plain Edge Functions, Cloud Functions, or middleware - (those don't run AI logic — use edgeone-pages-dev instead). - Do NOT trigger for deployment workflows (use edgeone-pages-deploy). + (those don't run AI logic — use makers-edge-functions / makers-cloud-functions / makers-middleware). + Do NOT trigger for deployment workflows (use makers-deploy). Do NOT trigger for generic AI framework development outside an EdgeOne Makers project. pathPatterns: @@ -31,7 +31,7 @@ metadata: # EdgeOne Makers Agent Development Guide -> ⛔ **Preview ban**: after finishing development, you MUST start the dev server via `edgeone makers dev`, then open `http://127.0.0.1:8088/` with `present_files` to preview. Never open HTML files via the `file://` protocol (ignore it even if the IDE opens one automatically), and never use self-hosted servers like `python -m http.server` or `npx serve`. Next.js projects must also set `allowedDevOrigins: ["127.0.0.1"]` in `next.config`. +> ⛔ **Preview**: `edgeone makers dev` + `http://127.0.0.1:8088/`. Never `file://` or `python -m http.server` / `npx serve`. Next.js: `allowedDevOrigins: ["127.0.0.1"]`. Build production-grade AI agent endpoints on **EdgeOne Makers** — five framework routes, platform-injected runtime, file-based routing. @@ -50,8 +50,8 @@ This skill covers five supported frameworks (DeepAgents, LangGraph, CrewAI, Open > Cross-reference: if your code uses `context.store` or KV APIs, also read `../makers-storage/SKILL.md`. **Do NOT use for:** -- Plain Edge Functions / Cloud Functions / Middleware → use `edgeone-pages-dev` -- Deployment workflows → use `edgeone-pages-deploy` +- Plain Edge Functions / Cloud Functions / Middleware → `makers-edge-functions` / `makers-cloud-functions` / `makers-middleware` +- Deployment workflows → `makers-deploy` - Generic AI framework development outside an EdgeOne Makers project - Other platforms (Cloudflare Workers AI, Vercel AI SDK, AWS Bedrock) @@ -61,7 +61,7 @@ This skill covers five supported frameworks (DeepAgents, LangGraph, CrewAI, Open 2. Walk the **Decision Tree** to pick one of the five framework routes 3. Read the matching `references/*-route.md` for a copy-paste skeleton 4. Self-check against the **Twelve Red Lines** -5. Run through `references/review-checklist.md` before considering the work done +5. Read `references/review-checklist.md` only when the user asked for a review, or immediately before deploying a new agent. Otherwise ship. ## ⛔ Critical Rules (never skip) diff --git a/skills/edgeone-makers-tools/references/makers-deploy/SKILL.md b/skills/edgeone-makers-tools/references/makers-deploy/SKILL.md index 6eb7b00..16e9785 100644 --- a/skills/edgeone-makers-tools/references/makers-deploy/SKILL.md +++ b/skills/edgeone-makers-tools/references/makers-deploy/SKILL.md @@ -12,19 +12,18 @@ description: >- "deploy without login", "no account yet", "anonymous deploy", "claim project", "claim my deployment", "免登录部署", "匿名部署", "还没有账号", "认领项目". - ⚠️ Also trigger when any agent is about to execute `edgeone makers deploy` or `edgeone makers deploy` - commands — the skill contains critical rules for parsing deploy output and presenting access URLs. - Do NOT trigger for post-deployment runtime errors (e.g. CORS issues, 500 errors after deploy — - use edgeone-makers-dev for troubleshooting). + ⚠️ Also trigger when any agent is about to execute `edgeone makers deploy`. + Do NOT trigger for post-deployment runtime errors (CORS, 500 after deploy) — + use makers-env-adaption / makers-recipes for local troubleshooting. pathPatterns: - "*.sh" - .github/workflows/** validate: - pattern: "whoami[^\\n]*\\s-t\\s" message: "edgeone whoami does not accept -t. Check the exit code instead: 0 = logged in, 1 = not." -metadata: + metadata: author: edgeone - version: "2.12.3" + version: "2.13.0" --- # EdgeOne Makers Deployment Skill @@ -56,12 +55,12 @@ Deploy any project to **EdgeOne Makers**. Then append any other notes (console URL, caveats, etc.). **Self-check before ending the turn (MANDATORY)** — read back what the user will actually SEE (NOT your thinking / reasoning content). Two questions: (a) Is the complete `.edgeone.cool` URL present at the top of the visible reply, in a code block or heading? (b) Was `present_files` called with that URL? If either answer is no, send an **additional short message** containing ONLY the `🌐 Live URL: ` block and call `present_files`. Do not end the turn until both channels carry the URL. "I already mentioned it in my reasoning" is NOT a substitute for placing it in the visible body. -5. **Ask the user to choose China or Global site** before browser login. Never assume. (Token login via `edgeone login --token` auto-detects site, no need to ask.) -6. **Prefer Browser Login; fall back to Token only after browser login is confirmed to fail** (see Login section for the ~60s fallback threshold and the Agent-in-IDE clarification — WorkBuddy is NOT headless). Token-first only when the user explicitly requests it. -7. **After token login, ask if the user wants to save the token locally** for future use. -8. **Before triggering any browser popup (login / registration), explain the reason and the benefits to the user first** — never silently launch a browser window. +5. **Browser login needs `--site`.** Infer china/global from `whoami`, an existing credential, or the user's context. Ask only when you must run `edgeone login` and the site is still unknown. Token login and anonymous deploy detect the site themselves — do not ask. +6. **Prefer Browser Login; fall back to Token only after browser login is confirmed to fail** (see Login section for the ~60s fallback threshold — WorkBuddy is NOT headless). Token-first only when the user explicitly requests it or provides a token. +7. **If the user provides a token, persist it** with `edgeone login --token` (or `--local` in a sandbox). Do not ask whether to save it. Never commit credentials. Do not treat `.edgeone/.token` as the CLI source of truth — credentials live in `~/.edgeone/` or `.edgeone/auth.json`. +8. **Before a browser popup, one sentence of why**, then run the command. Do not write a registration essay. 9. **On any CLI failure, surface the actual error text to the user before retrying, switching commands, or proposing a workaround.** Do NOT paraphrase (e.g. don't rewrite `Makers project exceeds 40 limit` into "maybe a name conflict or permission"). Do NOT silently pivot from `makers dev` to `makers deploy` (or vice versa) hoping to bypass — a systemic failure (auth / quota / permission) hits both with the same cause. Quote the raw error, name the root cause, then propose the fix or ask the user. -10. **Write every user-facing message in the user's own language** — **all** prose, blockquotes, and prompts in this skill are written in English purely to specify *meaning*; none of it is a string to paste. If the user writes to you in Chinese, speak Chinese — the login explanation, the site choice, the token question, the deploy result, all of it. Likewise for any other language. Emitting this skill's English strings into a non-English conversation is a bug. Only literal CLI commands, flags, env var names, and JSON field names stay verbatim. ⚠️ Where this skill gives a **fixed template** (Steps 2 and 4 of Anonymous Deploy), translating it is required but rewriting or extending it is not allowed — match it line for line. +10. **Write every user-facing message in the user's own language** — **all** prose, blockquotes, and prompts in this skill are written in English purely to specify *meaning*; none of it is a string to paste. If the user writes to you in Chinese, speak Chinese — the login explanation, any site choice, the deploy result, all of it. Likewise for any other language. Emitting this skill's English strings into a non-English conversation is a bug. Only literal CLI commands, flags, env var names, and JSON field names stay verbatim. ⚠️ Where this skill gives a **fixed template** (Steps 2 and 4 of Anonymous Deploy), translating it is required but rewriting or extending it is not allowed — match it line for line. **Rules 11-14 apply to the anonymous deploy / claim flow only:** @@ -124,7 +123,7 @@ cat edgeone.json 2>/dev/null | Not installed or < 1.6.0 | — | → Go to **Install CLI** | | `≥ 1.6.0` ✓ | Logged in (or token present) | → Go to **Deploy** | | `≥ 1.6.0` ✓ | Not logged in, has saved token | → Go to **Deploy with Token** (use saved token) | -| `≥ 1.6.29` ✓ | Not logged in, no saved token | → Go to **Anonymous Deploy** — default to anonymous only when the task is anonymous-native (scheduled / disposable / user asked for it); otherwise ask the user to choose anonymous deploy or login. If there is no way to ask, deploy with `--anonymous --json` and surface the claim link and the 60-minute window in the result (see Step 2) | +| `≥ 1.6.29` ✓ | Not logged in, no saved token | → Go to **Anonymous Deploy**. If the repo needs login (Agent / Blob / KV / AI Gateway), go to **Login** instead and say why. Ask the two-option choice only when publish intent is genuinely ambiguous. | | `1.6.0`–`1.6.28` | Not logged in, no saved token | Anonymous deploy is unavailable on this version → **Try Browser Login first** (see Login section). If the browser doesn't open or nothing happens within ~60 seconds, fall back to **Token Login**. Do NOT preemptively skip browser login by guessing "this looks like an Agent/CI environment" — that guess is often wrong; in particular, **WorkBuddy is a desktop IDE sandbox and fully supports browser login** | | `≥ 1.6.0` ✓ | User explicitly provides a token or requests token login | → Go to **Deploy with Token** / **Token Login** | @@ -142,34 +141,20 @@ Verify: `edgeone -v` — confirm output is `1.6.0` or higher. Retry installation ## Login -### 0. Explain the registration/login step +### 0. One sentence, then login -Before triggering any login flow, explain to the user **why** this step is needed and **what** to expect. Do not silently launch a browser window. +Before a browser login, say one sentence: you need an EdgeOne Makers account so the site lands on their account with a live URL. Then run the command. Do not silently launch a browser, and do not write a four-bullet registration pitch. -Tell the user: +If nothing happens within ~60 seconds (no popup, no CLI progress), switch to Token login. Do not wait indefinitely, and do not poll the user every turn. -> You need to log in or register an EdgeOne Makers account. Here's what to expect: -> - **Why login is required**: Deployment uploads your build output to your own account, generating a unique access URL and project record. -> - **What you get for free**: EdgeOne Makers offers a free tier with global CDN acceleration, automatic HTTPS, and custom domain binding — typically more than enough for personal projects. -> - **What happens next**: I'll run `edgeone login`, and your default browser will open the Tencent Cloud login page. Please complete the login/registration and authorize access, then come back here. -> - **If you get stuck**: If the browser doesn't open, or the CLI keeps waiting after you've logged in, let me know — I'll switch to Token login instead. +### 1. Pass `--site` — infer first, ask only if unknown -If the user does not respond within ~60 seconds (no browser popup or no progress reported), **proactively ask** about their status (whether the browser opened, any errors, or if they want to switch to Token login). Do not wait indefinitely. +`edgeone login` in a non-interactive context **requires** `--site `. Never run a bare `edgeone login`. -### 1. Ask the user to choose a site, then ALWAYS pass `--site` +Infer the site from `whoami`, an existing credential, or the user's context (language + console links they already used). Ask with a two-option control only when the site is still unknown: -Use the IDE's selection control (`ask_followup_question`) before running any login command: - -> Choose your EdgeOne Makers site: -> - **China** — For users in mainland China (console.cloud.tencent.com) -> - **Global** — For users outside China (console.intl.cloud.tencent.com) - -⚠️ **CRITICAL**: After the user chooses, you MUST invoke login with an explicit -`--site ` flag (e.g. `edgeone login --site china`). -**NEVER run a bare `edgeone login` (without `--site`) when driven by an Agent / skill.** -A bare `login` in a non-interactive context fails fast asking for -`--site` (it no longer pops an interactive site-picker that would hang). The site choice -is meant to happen here in the conversation, not inside the CLI. +> - **China** — console.cloud.tencent.com +> - **Global** — console.intl.cloud.tencent.com ### 2. Login methods reference @@ -230,23 +215,9 @@ Guide the user to obtain a token: ⚠️ Remind the user: the token has account-level permissions. Never commit it to a repository. -### 3. Offer to save the token locally - -After the user provides a token, ask: - -> Save this token locally for future deployments? -> - **Yes** — Save to `.edgeone/.token` (auto-used next time) -> - **No** — Use for this deployment only - -**If Yes:** - -```bash -mkdir -p .edgeone -echo "" > .edgeone/.token -grep -q '.edgeone/.token' .gitignore 2>/dev/null || echo '.edgeone/.token' >> .gitignore -``` +### 3. Persist a user-provided token -Confirm to the user: "✅ Token saved to `.edgeone/.token` and added to `.gitignore`." +If the user hands you a token, run `edgeone login --token ` (or `--local` in a sandbox that cannot write `~/.edgeone/`). Ensure `.edgeone/` is gitignored. Do not ask whether to save it, and do not invent a `.edgeone/.token` file — that is not the CLI credential store. --- @@ -266,26 +237,17 @@ edgeone makers deploy -n ### Token-based deploy (Makers projects) -First check for a saved token: - -```bash -cat .edgeone/.token 2>/dev/null -``` - -- Saved token found → use it, tell the user: "Using saved token from `.edgeone/.token`" -- No saved token → ask the user to provide one (see Token Login above) +Use a token already in the environment (`EDGEONE_PAGES_API_TOKEN`), one the user just provided, or the CLI store from `edgeone login --token` / `--local`. Do not `cat .edgeone/.token`. ```bash # Project already linked -edgeone makers deploy -t +edgeone makers deploy -t --json # New project -edgeone makers deploy -n -t +edgeone makers deploy -n -t --json ``` -The token already contains site info — no `--site` flag needed. - -After a successful deploy with a manually-entered token, ask if the user wants to save it (see "Offer to save the token locally" above). +The token already contains site info — no `--site` flag needed. If they typed the token in chat, persist it (see Login §3) and then deploy. ### Deploy to preview environment @@ -344,11 +306,9 @@ Non-empty output from either grep means the project uses Blob. ⛔ **All checks in this flow are silent — never narrate them to the user.** Do not report things like "CLI 版本 x.x(支持匿名部署)", "页面是纯静态、无 Blob/KV 依赖,可直接发布", or any other version/eligibility check result. These are internal reasoning; a non-technical user cannot act on them and should never see them. The only time you speak about a check is when it **changes the outcome** — e.g. the project needs login (below), or the CLI is too old and needs upgrading. Passing checks produce no message at all. -**KV cannot be detected this way — you must ask.** A KV namespace is bound in the console and exposed as a *global variable* whose name the user chose (e.g. `my_kv`), so there is no package import to grep for. There is no `@edgeone/pages-kv` package. Ask the user directly: +**KV has no npm package.** Infer it from the workspace: a console-bound global (e.g. `my_kv.get` / `my_kv.put`) in `edge-functions/`, or a KV mention in `edgeone.json` / docs. Ask "does this project use KV?" only when there is no source to read. -> Does this project use KV storage? - -If either check hits, or the user says the project uses KV, **do not deploy anonymously.** Go to **Login** and tell the user why (note the user-facing wording: "without login", never "anonymous"): +If Blob, Agent, AI Gateway, or KV is present, **do not deploy anonymously.** Go to **Login** and tell the user why (user-facing wording: "without login", never "anonymous"): > This project needs environment variables / AI gateway credentials or a storage binding, which a login-free deploy cannot provide. The site would load but those features would fail. Let's log in so it works properly. @@ -356,19 +316,16 @@ Plain static sites and frontend-framework projects with no such dependency may p If the user acknowledges the limitation and still wants to publish without logging in, you may proceed — but state prominently in your result that AI and storage features will not work until the project is claimed and configured (again: phrase it as "login-free / 免登录", never "anonymous / 匿名"). -### Step 2: Decide the path — default to anonymous only when the task fits, otherwise ask - -**Go anonymous directly, without asking, only when the task itself is anonymous-native** — disposable or unattended by nature. Clear signals: +### Step 2: Decide the path — publish means publish -- **Scheduled / automated jobs** — e.g. "每日定时生成一个页面并部署", cron pipelines, any task that must run with nobody watching -- **Disposable one-offs** — a throwaway preview, a quick demo, "先看看效果", anything the user frames as temporary or just-for-now -- **Explicit anonymous intent** — the user says "免登录", "不用登录", "anonymous", or "don't make me sign up" +If the user asked to deploy / 上线 / 发布 and they are not logged in: -In these cases the 60-minute expiry + claim-later model *is* the right answer, so asking is pure friction. Go straight to Step 3, and present the result with the fixed template in Step 4 (claim link + 60-minute window) — the template already tells the user how to keep it if they change their mind. +- Repo needs credentials (Agent / Blob / KV / AI Gateway) → **Login**, say why, then deploy +- Otherwise → **Anonymous deploy** (Step 3). Do not ask first. The Step 4 template already tells them how to keep the project. -**Everything else — ask.** If the task isn't obviously disposable — anything the user might want to keep, share widely, or build on — present the choice. Do NOT try to judge the *environment* to skip the question: sandbox/TTY signals are unreliable and guessing wrong either needlessly interrupts a human or silently deploys something they wanted to keep. +Ask the two-option choice only when intent is genuinely ambiguous (they have not said deploy, or they mixed "save it to my account" with "don't make me sign up"). Do not judge the *environment* to invent a question. -If the environment genuinely gives you no way to ask at all (no TTY, no question tool), asking is impossible — that's a constraint, not a judgment call. Deploy with `--anonymous --json` and make the claim link and the 60-minute window unmissable in your result. +If there is no way to ask at all, deploy with `--anonymous --json` and make the claim link and the 60-minute window unmissable. **How to ask (when asking):** present the choice with the IDE's selection control (e.g. `ask_followup_question`). The option labels deliberately avoid the word "anonymous" — it is jargon and confuses non-technical users. Present these two options **exactly**, in the user's language. Do not paraphrase the labels, do not add caveats to the options themselves, and do not mention "anonymous" to the user at all — when you must name the concept, call it **"login-free deployment" / 「免登录部署」**, never "anonymous deploy / 匿名部署". The option text must be placed in the `label` exactly as is; it must not be split into the `description`. @@ -498,24 +455,9 @@ https://console.cloud.tencent.com/edgeone/pages/project/pages-xxxxxxxx/deploymen | **Project ID** | Value after `EDGEONE_PROJECT_ID=` | — | | **Console URL** | Line after "You can view your deployment..." | — | -**Show the user — the deploy URL MUST be at the very top of the visible reply AND pinned via `present_files` to the side panel (see Rule 4 for why both channels are required):** - -⚠️ **URL Integrity Rules (read before composing your reply):** - -| Rule | Detail | -|------|--------| -| **Every mention must be complete** | If you write the URL in a table, a list, a footnote, a comparison, or any secondary location — it MUST still include the full query string. No exceptions. | -| **No visual "cleanup"** | Do not shorten the URL to make a table look nicer. A truncated URL is broken, not clean. | -| **Concrete, not abstract** | Use the actual URL from deploy output. Do not replace query params with `...` or `(params omitted)` or any placeholder in user-facing text. | -| **Self-check before sending** | Search your draft for `.edgeone.cool` — every hit must have `?eo_token=`. | +**Show the user — complete URL at the top of the visible reply AND via `present_files` (Rule 4). Every `.edgeone.cool` mention must keep `?eo_token=`.** > 🌐 **Live URL**: `https://my-project-abc123.edgeone.cool?eo_token=abc123&eo_time=1234567890` -> -> --- -> -> - **Console URL**: `https://console.cloud.tencent.com/edgeone/pages/project/...` -> -> ℹ️ Note: This preview URL is for quick deployment verification. When accessed from mainland China, the link may become restricted (e.g., 401) after some time or when shared, due to domain ICP filing status or CDN acceleration policies. For long-term stable public access, bind a custom domain with proper ICP filing. --- diff --git a/skills/edgeone-makers-tools/references/makers-env-adaption/SKILL.md b/skills/edgeone-makers-tools/references/makers-env-adaption/SKILL.md index 995aa0f..0999893 100644 --- a/skills/edgeone-makers-tools/references/makers-env-adaption/SKILL.md +++ b/skills/edgeone-makers-tools/references/makers-env-adaption/SKILL.md @@ -17,7 +17,7 @@ validate: message: "Use 127.0.0.1, not localhost — in the sandbox localhost resolves to ::1 and yields false 404s." metadata: author: edgeone - version: "1.4.2" + version: "1.5.0" --- # Runtime Environment Adaptation Guide @@ -105,9 +105,9 @@ edgeone makers dev **Recommended approach**: browser login + the `--local` flag: ```bash -edgeone login --site china --local +edgeone login --site --local ``` -`--local` writes credentials to the project directory at `/.edgeone/auth.json`, bypassing home-directory write restrictions. +Infer `--site` from `whoami` or user context; ask only if unknown. `--local` writes credentials to `/.edgeone/auth.json`, bypassing home-directory write restrictions. **Login status detection**: ```bash @@ -116,7 +116,7 @@ edgeone whoami # exit 0 = logged in, exit 1 = not logged in (does not hang) **When is login actually required?** Login is only needed when the project uses **Blob** or other credentialed backends — and strictly because of the dependency chain: **Blob requires the project to be linked, and linking requires a logged-in account first.** So `edgeone makers dev` for a **pure-static** site runs fine without login — **do NOT force a login prompt for static-only previews**. Login (or `-t `) becomes mandatory the moment dev/deploy must touch Blob storage. (The trigger condition and the link chain live in makers-storage.) -**CLI version requirement**: >= 1.6.7 (older versions lack the non-interactive fixes; whoami will hang) +**CLI version requirement**: >= 1.6.0 (anonymous deploy / `claim`: >= 1.6.29) --- @@ -209,9 +209,8 @@ After finishing development, **start the dev server and preview directly** — d 1. Start `edgeone makers dev --name --skip-env-sync` (**background async**, see §7) 2. Wait 2-3 seconds for the dev server to be ready 3. **Pass `http://127.0.0.1:8088/` to `present_files`** (note it is `127.0.0.1`, **not** `localhost` — see §4) -4. Tell the user: "The project's local preview is running, please check it out. If everything looks good, I can deploy it live for you directly." - -Only after the user confirms, run `edgeone makers deploy -n --json` (**background async**, see §7 and §7.2 — it exceeds the foreground wall-clock budget). +4. If the user already asked to deploy / 上线 / 发布, deploy next (`edgeone makers deploy -n --json`, background — see §7). Do not ask again. +5. If they only asked to build, stop at preview. Mention deploy is available; do not block on a confirmation to call the work done. #### ⛔ file:// preview is strictly forbidden @@ -294,7 +293,7 @@ If the project named by `--name` does not exist remotely, the `link` command cre | Framework/package | Minimum version | Reason | |---------|---------|------| -| EdgeOne CLI | >= 1.6.7 | Non-interactive fixes, whoami fail-fast, --json support | +| EdgeOne CLI | >= 1.6.0 | Non-interactive fixes, whoami fail-fast, --json support | | EdgeOne CLI (anonymous deploy / `claim` only) | >= 1.6.29 | `--anonymous` and `claim` do not exist below this | | Next.js | 16.x | The framework adapter tracks new versions | | @edgeone/pages-blob | >= 0.0.14 | Older versions have known bugs | diff --git a/skills/edgeone-makers-tools/references/makers-recipes/SKILL.md b/skills/edgeone-makers-tools/references/makers-recipes/SKILL.md index e4d448d..d0d6148 100644 --- a/skills/edgeone-makers-tools/references/makers-recipes/SKILL.md +++ b/skills/edgeone-makers-tools/references/makers-recipes/SKILL.md @@ -10,7 +10,7 @@ metadata: # Common Recipes -> ⛔ **Preview ban**: after finishing development, you MUST start the dev server via `edgeone makers dev`, then open `http://127.0.0.1:8088/` with `present_files` to preview. Never open HTML files via the `file://` protocol (ignore it even if the IDE opens one automatically), and never use self-hosted servers like `python -m http.server` or `npx serve`. Next.js projects must also set `allowedDevOrigins: ["127.0.0.1"]` in `next.config`. **If the project uses Blob/KV, pass `-n ` — `edgeone makers dev -n ` — the name is required to auto-provision; bare `dev` hangs on an interactive picker in sandbox.** +> ⛔ **Preview**: finish by starting `edgeone makers dev` and opening `http://127.0.0.1:8088/` — never `file://`, never `python -m http.server` / `npx serve`. Next.js: `allowedDevOrigins: ["127.0.0.1"]`. Blob/KV: `edgeone makers dev -n ` (bare `dev` hangs on an interactive picker). Deploy / 上线 already in the request → deploy after preview; do not ask again. > ⚠️ **`.env.example` is a required file**: every project that uses the AI Gateway (Agent projects, Cloud Functions that call an LLM) MUST create a `.env.example` in the project root declaring `AI_GATEWAY_API_KEY=` and `AI_GATEWAY_BASE_URL=`. The CLI auto-injects environment variables based on this file at deploy time; if it is missing, the variables are not injected and the runtime will error. @@ -65,7 +65,7 @@ my-app/ import { getStore } from "@edgeone/pages-blob"; export async function onRequest({ request }) { - const store = getStore("guestbook"); + const store = getStore({ name: "guestbook", consistency: "strong" }); if (request.method === "POST") { const { name, text } = await request.json();