@impetik/xeer-mcp 0.2.29 → 0.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,7 +63,7 @@ The generated registry is the complete MCP tool and exclusion surface:
63
63
  | `xeer_check` | `check` | `author` / `operator` | Validate a project and report structured diagnostics. | `directory?`: `string` | `read-source`<br>`write-generated` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
64
64
  | `xeer_build` | `build` | `author` / `operator` | Build and verify a content-addressed project artifact. | `directory?`: `string` | `read-source`<br>`write-generated`<br>`run-local` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
65
65
  | `xeer_test` | `test` | `author` / `operator` | Run the project test suite and record review evidence. | `directory?`: `string`<br>`timeoutMilliseconds?`: `integer` [1000..1800000] | `read-source`<br>`write-generated`<br>`run-local`<br>`write-state` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.dev.v0` | none |
66
- | `xeer_new` | `new` | `author` / `operator` | Create a new project from a supported scaffold. | `directory`: `string`<br>`template?`: `string`<br>`framework?`: `sveltekit`<br>`ui?`: `preact` / `react` | `write-source`<br>`network-read` | writes; non-idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
66
+ | `xeer_new` | `new` | `author` / `operator` | Create a new project from a supported scaffold. | `directory`: `string`<br>`template?`: `string`<br>`framework?`: `sveltekit` / `astro`<br>`ui?`: `preact` / `react` | `write-source`<br>`network-read` | writes; non-idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
67
67
  | `xeer_agent_context` | `agent.context` | `author` / `operator` | Read normalized project facts, operations, diagnostics, tests, and safe next actions. | `directory?`: `string` | `read-source` | read-only; idempotent; reversible; non-destructive | `project-relative` | `xeer.agent-context.v0` | none |
68
68
  | `xeer_docs_search` | `docs.search` | `author` / `operator` | Search the installed-version Xeer documentation index. | `query`: `string`<br>`limit?`: `integer` [1..20]; default `5` | none | read-only; idempotent; reversible; non-destructive | `none` | `xeer.docs-search.v0` | none |
69
69
  | `xeer_doctor` | `doctor` | `author` / `operator` | Diagnose the toolchain and generated project state. | `directory?`: `string` | `read-source`<br>`write-generated` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
@@ -76,11 +76,12 @@ The generated registry is the complete MCP tool and exclusion surface:
76
76
  | `xeer_dev_stop` | `dev.stop` | `author` / `operator` | Stop a local development session and release its lease. | `sessionId?`: `string`<br>`cursor?`: `integer` [0..9007199254740991]; default `0` | `run-local` | writes; idempotent; reversible; non-destructive | `none` | `xeer.dev.v0` | none |
77
77
  | `xeer_diagnostics` | `diagnostics` | `author` / `operator` | Explain one emitted diagnostic code. | `code`: `string` | none | read-only; idempotent; reversible; non-destructive | `none` | `xeer.command.v0` | none |
78
78
 
79
- **CLI actions intentionally excluded from MCP (36).**
79
+ **CLI actions intentionally excluded from MCP (37).**
80
80
 
81
81
  | CLI action | Action | Summary | Effects | Safety | Path policy | Output | Why no MCP tool |
82
82
  | --- | --- | --- | --- | --- | --- | --- | --- |
83
- | `xeer init` | `init` | Initialize Xeer in an existing SvelteKit project. | `read-source`<br>`write-source`<br>`run-local`<br>`network-read` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | Not exposed through MCP v0; use the CLI deliberately. |
83
+ | `xeer init` | `init` | Initialize Xeer in an existing SvelteKit or Astro project. | `read-source`<br>`write-source`<br>`run-local`<br>`network-read` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | Not exposed through MCP v0; use the CLI deliberately. |
84
+ | `xeer bootstrap` | `bootstrap` | Open owner-only EmDash setup for a deployed Astro project. | `read-source`<br>`network-read`<br>`network-write` | writes; non-idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | Not exposed through MCP v0; use the CLI deliberately. |
84
85
  | `xeer agent setup` | `agent.setup` | Install or verify project-confined agent adapters. | `read-source`<br>`write-source` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | Not exposed through MCP v0; use the CLI deliberately. |
85
86
  | `xeer deploy` | `deploy` | Build and deploy a project artifact. | `read-source`<br>`write-generated`<br>`run-local`<br>`network-read`<br>`network-write`<br>`production-change` | writes; non-idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | MCP exposes a separate preview-only deploy action; direct production deploy stays CLI-only. |
86
87
  | `xeer promote` | `promote` | Promote a preview artifact to production. | `network-read`<br>`network-write`<br>`production-change` | writes; non-idempotent; reversible; non-destructive | `project-or-url` | `xeer.command.v0` | MCP promotion is a separate action available only when the server starts in operator profile. |
@@ -25,6 +25,10 @@ export type DevSessionStatus = 'starting' | 'ready' | 'compile_failed' | 'stoppe
25
25
  */
26
26
  export interface PreviewUrls {
27
27
  url: string;
28
+ /** A framework project's framework, `sveltekit` or `astro`; absent for a Xeer application. */
29
+ framework?: string;
30
+ /** An EmDash project's local admin, on `localhost` because passkeys refuse an IP address. */
31
+ adminUrl?: string;
28
32
  healthUrl?: string;
29
33
  inspectorUrl?: string;
30
34
  debugUrl?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@impetik/xeer-mcp",
3
- "version": "0.2.29",
3
+ "version": "0.2.30",
4
4
  "type": "module",
5
5
  "description": "Model Context Protocol server for Xeer project context, diagnostics, scaffold, check, test, dev, build, and preview deployment.",
6
6
  "license": "MIT",
@@ -47,7 +47,7 @@
47
47
  "dependencies": {
48
48
  "@modelcontextprotocol/sdk": "^1.29.0",
49
49
  "zod": "^4.0.10",
50
- "@impetik/xeer": "0.2.29"
50
+ "@impetik/xeer": "0.2.30"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "^24.1.0"
@@ -194,7 +194,7 @@ declare const actions: readonly [{
194
194
  readonly command: readonly ["new"];
195
195
  readonly summary: "Create a new project from a supported scaffold.";
196
196
  readonly description: string;
197
- readonly usage: readonly ["new [directory] [--template <id>] [--framework sveltekit] [--ui preact|react] [--json]"];
197
+ readonly usage: readonly ["new [directory] [--template <id>] [--framework sveltekit|astro] [--ui preact|react] [--json]"];
198
198
  readonly helpOrder: 30;
199
199
  readonly outputProtocol: "xeer.command.v0";
200
200
  readonly effects: readonly ["write-source", "network-read"];
@@ -218,7 +218,7 @@ declare const actions: readonly [{
218
218
  readonly framework: {
219
219
  readonly description: string;
220
220
  readonly type: "string";
221
- readonly enum: readonly ["sveltekit"];
221
+ readonly enum: readonly ["sveltekit", "astro"];
222
222
  };
223
223
  readonly ui: {
224
224
  readonly description: string;
@@ -237,7 +237,7 @@ declare const actions: readonly [{
237
237
  }, {
238
238
  readonly id: "init";
239
239
  readonly command: readonly ["init"];
240
- readonly summary: "Initialize Xeer in an existing SvelteKit project.";
240
+ readonly summary: "Initialize Xeer in an existing SvelteKit or Astro project.";
241
241
  readonly description: string;
242
242
  readonly usage: readonly ["init [directory] [--dry-run] [--json]"];
243
243
  readonly helpOrder: 32;
@@ -252,6 +252,24 @@ declare const actions: readonly [{
252
252
  readonly cli: true;
253
253
  readonly mcpExclusion: "Not exposed through MCP v0; use the CLI deliberately.";
254
254
  };
255
+ }, {
256
+ readonly id: "bootstrap";
257
+ readonly command: readonly ["bootstrap"];
258
+ readonly summary: "Open owner-only EmDash setup for a deployed Astro project.";
259
+ readonly description: string;
260
+ readonly usage: readonly ["bootstrap [directory] [--environment prod|preview] [--control-url <url>] [--no-open] [--json]"];
261
+ readonly helpOrder: 51;
262
+ readonly outputProtocol: "xeer.command.v0";
263
+ readonly effects: readonly ["read-source", "network-read", "network-write"];
264
+ readonly idempotent: false;
265
+ readonly reversible: true;
266
+ readonly destructive: false;
267
+ readonly humanPrerequisites: readonly [string];
268
+ readonly pathPolicy: "project-relative";
269
+ readonly surfaces: {
270
+ readonly cli: true;
271
+ readonly mcpExclusion: "Not exposed through MCP v0; use the CLI deliberately.";
272
+ };
255
273
  }, {
256
274
  readonly id: "agent.setup";
257
275
  readonly command: readonly ["agent", "setup"];
@@ -1,6 +1,6 @@
1
1
  /** Stable protocol for the action catalogue consumed by CLI, MCP, and agent context surfaces. */
2
2
  import { REVIEW_RECEIPT_ID_SOURCE } from './review.js';
3
- import { FRAMEWORK_NAMES } from './scaffold-names.js';
3
+ import { SCAFFOLDABLE_FRAMEWORK_NAMES } from './scaffold-names.js';
4
4
  export const ACTION_REGISTRY_PROTOCOL = 'xeer.actions.v0';
5
5
  export const XEER_MCP_PROFILES = ['author', 'operator'];
6
6
  export const XEER_ARTIFACT_ID_PATTERN = '^sha256:[a-f0-9]{64}$';
@@ -216,12 +216,14 @@ const actions = [
216
216
  + 'scaffold sign-in UI: every visitor already has a verified identity, and the generated README '
217
217
  + 'explains how to opt in to a persistent account. The four bundled starter names resolve '
218
218
  + 'offline and accept `--ui`; every other exact template id resolves through the public catalog '
219
- + 'and owns its renderer. `--framework sveltekit` writes a framework '
220
- + 'project instead: SvelteKit owns the renderer and the routes, Xeer owns `xeer.config.json` '
221
- + 'and the deployment, so `result.framework` replaces both fields, `--ui` does not apply and '
222
- + 'is refused with `XE3004`, and `--template` alongside it is `XE3005`. To adopt a framework '
219
+ + 'and owns its renderer. `--template emdash` is the bundled EmDash blog: a complete Astro '
220
+ + 'framework project that resolves offline, declares `integration: "emdash"`, and refuses `--ui` '
221
+ + 'with `XE3013`. `--framework sveltekit|astro` writes a plain framework project instead: the '
222
+ + 'framework owns the renderer and the routes, Xeer owns `xeer.config.json` and the '
223
+ + 'deployment, so `result.framework` replaces both fields, `--ui` does not apply and is '
224
+ + 'refused with `XE3004`, and `--template` alongside it is `XE3005`. To adopt a framework '
223
225
  + 'project that already exists, use `xeer init` rather than this command.',
224
- usage: ['new [directory] [--template <id>] [--framework sveltekit] [--ui preact|react] [--json]'],
226
+ usage: ['new [directory] [--template <id>] [--framework sveltekit|astro] [--ui preact|react] [--json]'],
225
227
  helpOrder: 30,
226
228
  outputProtocol: 'xeer.command.v0', effects: ['write-source', 'network-read'], idempotent: false,
227
229
  reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'project-relative',
@@ -235,15 +237,16 @@ const actions = [
235
237
  type: 'string', description: 'Target directory, relative to the server root. Must be empty or absent.',
236
238
  },
237
239
  template: {
238
- description: 'A bundled starter name or exact template catalog id. Defaults to notes. '
239
- + 'Bundled starters resolve offline. Exclusive with framework, which is refused with XE3005.',
240
+ description: 'A bundled starter name, the bundled emdash framework template, or an exact template '
241
+ + 'catalog id. Defaults to notes. Bundled names resolve offline. Exclusive with framework, '
242
+ + 'which is refused with XE3005.',
240
243
  type: 'string',
241
244
  },
242
245
  framework: {
243
246
  description: 'Scaffold a fresh project for this framework instead of a Xeer '
244
247
  + 'application: it declares xeer.config.json, owns its own renderer and routes, and '
245
248
  + 'delegates check, dev, test, and build to its package scripts. Exclusive with template and ui.',
246
- type: 'string', enum: FRAMEWORK_NAMES,
249
+ type: 'string', enum: SCAFFOLDABLE_FRAMEWORK_NAMES,
247
250
  },
248
251
  ui: {
249
252
  description: 'Which UI provider the written manifest selects. Defaults to preact, and '
@@ -257,7 +260,7 @@ const actions = [
257
260
  },
258
261
  },
259
262
  {
260
- id: 'init', command: ['init'], summary: 'Initialize Xeer in an existing SvelteKit project.',
263
+ id: 'init', command: ['init'], summary: 'Initialize Xeer in an existing SvelteKit or Astro project.',
261
264
  description: 'Detects an existing SvelteKit project, runs Wrangler setup when Cloudflare is not '
262
265
  + 'configured, preserves the project\'s native dev/check/test/build scripts and application '
263
266
  + 'source, and creates the Xeer framework configuration. Use --dry-run to inspect the '
@@ -267,6 +270,19 @@ const actions = [
267
270
  idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
268
271
  pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
269
272
  },
273
+ {
274
+ id: 'bootstrap', command: ['bootstrap'], summary: 'Open owner-only EmDash setup for a deployed Astro project.',
275
+ description: 'Verifies builder ownership, obtains a private setup grant, then polls the deployed '
276
+ + 'site\x27s read-only `GET /_emdash/api/setup/status` for up to 240 seconds so a cold EmDash '
277
+ + 'database can finish its migrations. A ready site returns `setup: "required"` with the private '
278
+ + 'setup URL, or `setup: "complete"` with the admin URL once an owner exists. If the site is '
279
+ + 'still initializing when the wait ends, the command exits 1 with `XE5135` and a '
280
+ + '`readiness: "pending"` result naming the retry command. `--json` never opens a browser.',
281
+ usage: ['bootstrap [directory] [--environment prod|preview] [--control-url <url>] [--no-open] [--json]'],
282
+ helpOrder: 51, outputProtocol: 'xeer.command.v0', effects: ['read-source', 'network-read', 'network-write'],
283
+ idempotent: false, reversible: true, destructive: false, humanPrerequisites: [BUILDER_CREDENTIAL],
284
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
285
+ },
270
286
  {
271
287
  id: 'agent.setup', command: ['agent', 'setup'],
272
288
  summary: 'Install or verify project-confined agent adapters.',
@@ -13,7 +13,7 @@
13
13
  * undocumented and a retired code cannot linger here.
14
14
  */
15
15
  /** Where an agent observes a code, in the vocabulary of the CLI commands. */
16
- export type DiagnosticSurface = 'any' | 'check' | 'build' | 'dev' | 'preview' | 'test' | 'new' | 'init' | 'agent' | 'doctor' | 'inspect' | 'state' | 'auth' | 'deploy' | 'deployments' | 'promote' | 'rollback' | 'disable' | 'enable' | 'delete' | 'link' | 'env' | 'token' | 'domains' | 'export' | 'import' | 'db';
16
+ export type DiagnosticSurface = 'any' | 'check' | 'build' | 'dev' | 'preview' | 'test' | 'new' | 'init' | 'agent' | 'doctor' | 'inspect' | 'state' | 'auth' | 'deploy' | 'deployments' | 'promote' | 'rollback' | 'disable' | 'enable' | 'delete' | 'link' | 'env' | 'bootstrap' | 'token' | 'domains' | 'export' | 'import' | 'db';
17
17
  export interface DiagnosticFamily {
18
18
  /** Numeric prefix the family owns, as it appears in a code. */
19
19
  readonly prefix: string;
@@ -404,15 +404,16 @@ export const DIAGNOSTIC_DEFINITIONS = [
404
404
  + 'fix: wait out the advertised retry and read again. '
405
405
  + "Quote `detail.errorId` when correlating with the Worker's console output.", ['inspect', 'state', 'export']),
406
406
  define('XE3001', 'xeer new could not scaffold the project — most often a non-empty target directory.', 'Choose an empty or non-existent directory.', ['new']),
407
- define('XE3002', '`--template` named neither a bundled starter nor an exact id in the reachable catalog.', 'Check the exact catalog id, use a bundled starter, or omit --template for the default. A framework '
408
- + 'scaffold is selected with `--framework`.', ['new']),
407
+ define('XE3002', '`--template` named neither a bundled starter nor an exact id in the reachable catalog.', 'Check the exact catalog id, use a bundled name (notes, todo, blog, personal-site, or the emdash '
408
+ + 'framework template), or omit --template for the default. A plain framework scaffold is '
409
+ + 'selected with `--framework`.', ['new']),
409
410
  define('XE3003', '`--ui` named a UI provider that does not exist. Refused before the target '
410
411
  + 'directory is read, so nothing was written.', 'Use preact or react, or omit --ui for the default (preact). The choice is orthogonal to '
411
412
  + '--template for every Xeer application template: each scaffolds on either provider from the '
412
413
  + 'same client sources.', ['new']),
413
414
  define('XE3004', '`--ui` was passed with `--framework`, and a framework renders through its own '
414
415
  + 'toolchain rather than through a Xeer UI provider. Refused before the target directory is '
415
- + 'read, so nothing was written.', 'Drop --ui. `--framework sveltekit` owns its renderer: it writes no client.runtime.provider to '
416
+ + 'read, so nothing was written.', 'Drop --ui. A framework project owns its renderer: it writes no client.runtime.provider to '
416
417
  + 'select, and no value of --ui would change a file it writes.', ['new']),
417
418
  define('XE3005', '`--template` and `--framework` were passed together. They are two exclusive '
418
419
  + 'axes: one writes a Xeer application, the other a project Xeer runs rather than compiles. '
@@ -427,7 +428,8 @@ export const DIAGNOSTIC_DEFINITIONS = [
427
428
  define('XE3010', 'The downloaded template archive does not match the catalog SHA-256.', 'Do not use the bytes. Repeat later or report the template artifact.', ['new']),
428
429
  define('XE3011', 'The downloaded template archive contains unsafe or unsupported content.', 'Report the template artifact and choose another template.', ['new']),
429
430
  define('XE3012', 'The extracted project root does not match the project metadata declared by the catalog.', 'Report the template artifact and choose another template.', ['new']),
430
- define('XE3013', '`--ui` was passed with a catalog template, which owns its project kind and renderer.', 'Drop --ui and install the catalog template unchanged.', ['new']),
431
+ define('XE3013', '`--ui` was passed with a template that owns its renderer: a catalog template, or the '
432
+ + 'bundled emdash framework template. Refused before the target directory is read, so nothing was written.', 'Drop --ui and create the template unchanged.', ['new']),
431
433
  define('XE3101', 'Agent setup check found one or more generated files missing or stale. No files were written.', 'Run `xeer agent setup`, then repeat `xeer agent setup --check --json`.', ['agent']),
432
434
  define('XE3102', 'Agent setup found a path owned by the user or another tool. The entire write was refused.', 'Move, rename, or deliberately remove the conflicting path; setup never overwrites an unowned file.', ['agent']),
433
435
  define('XE3103', '`--target` named an agent adapter that Xeer does not support.', 'Use auto, agents, claude, codex, cursor, vscode, or mcp.', ['agent']),
@@ -509,6 +511,11 @@ export const DIAGNOSTIC_DEFINITIONS = [
509
511
  + 'entries have names or values a local run cannot load — so `ctx.env` is missing them.', 'Read `message`: it names the file and the exact problem — the shape to write is '
510
512
  + '{"format": "xeer.env-local.v0", "values": {"NAME": "value"}} with UPPER_SNAKE_CASE names '
511
513
  + 'and string values. `xeer env pull` writes the file correctly.', ['dev', 'preview', 'test']),
514
+ define('XE5135', 'xeer bootstrap could not confirm EmDash setup readiness within its fixed 240-second wait: '
515
+ + 'the deployed site never answered `GET /_emdash/api/setup/status` with a ready response. A fresh '
516
+ + 'database can take several minutes to run its migrations; no probe has a shorter deadline than what remains of that budget.', 'Wait for the site to finish initializing, then run the `retry` command from the result. The result '
517
+ + 'carries `readiness: "pending"` and `retryable: true`; no setup URL was returned, and a retry obtains a '
518
+ + 'fresh grant. Do not release a migration lock while initialization is still running.', ['bootstrap']),
512
519
  define('XE5139', 'xeer env failed without a more specific code.', 'Read `message`; it is the underlying error verbatim.', ['env']),
513
520
  define('XE5140', 'An `xeer deployments` invocation is wrong: an unusable --limit, or a directory that '
514
521
  + 'declares no app identity.', 'Read `message`. --limit takes a positive integer up to 200. In a directory with no appId, run '
@@ -1,18 +1,53 @@
1
1
  import { z } from 'zod';
2
2
  export declare const FRAMEWORK_CONFIG_SCHEMA_URL: "https://docs.xeer.run/config-v0.schema.json";
3
3
  export declare const FRAMEWORK_CONFIG_FORMAT: "xeer.config.v0";
4
+ /**
5
+ * What an EmDash deployment must bind. EmDash's own adapters fix the D1 and R2 binding names, its
6
+ * sign-in needs Astro's session KV, and its scheduled publishing needs the platform's one supported
7
+ * schedule. Stated once here so the config, the build, and the control plane refuse the same shape.
8
+ */
9
+ export declare const EMDASH_DEPLOYMENT: Readonly<{
10
+ readonly framework: "astro";
11
+ readonly database: "DB";
12
+ readonly storage: "MEDIA";
13
+ readonly schedule: "* * * * *";
14
+ }>;
4
15
  declare const frameworkConfigSchema: z.ZodObject<{
5
16
  $schema: z.ZodOptional<z.ZodString>;
6
17
  format: z.ZodLiteral<"xeer.config.v0">;
7
18
  name: z.ZodString;
8
19
  framework: z.ZodEnum<{
20
+ astro: "astro";
9
21
  sveltekit: "sveltekit";
10
22
  }>;
23
+ integration: z.ZodOptional<z.ZodEnum<{
24
+ emdash: "emdash";
25
+ }>>;
11
26
  capabilities: z.ZodObject<{
12
27
  auth: z.ZodOptional<z.ZodObject<{}, z.core.$strict>>;
28
+ database: z.ZodOptional<z.ZodObject<{}, z.core.$strict>>;
29
+ storage: z.ZodOptional<z.ZodObject<{}, z.core.$strict>>;
13
30
  }, z.core.$strict>;
14
31
  }, z.core.$strict>;
15
32
  export type FrameworkConfigV0 = z.infer<typeof frameworkConfigSchema>;
16
33
  export declare const frameworkConfigJsonSchema: Readonly<Record<string, unknown>>;
17
34
  export declare function parseFrameworkConfig(value: unknown): FrameworkConfigV0;
35
+ declare const frameworkDeploymentSchema: z.ZodObject<{
36
+ framework: z.ZodOptional<z.ZodEnum<{
37
+ astro: "astro";
38
+ sveltekit: "sveltekit";
39
+ }>>;
40
+ integration: z.ZodOptional<z.ZodEnum<{
41
+ emdash: "emdash";
42
+ }>>;
43
+ bindings: z.ZodOptional<z.ZodObject<{
44
+ database: z.ZodOptional<z.ZodString>;
45
+ storage: z.ZodOptional<z.ZodString>;
46
+ session: z.ZodOptional<z.ZodString>;
47
+ }, z.core.$strict>>;
48
+ schedule: z.ZodOptional<z.ZodLiteral<"* * * * *">>;
49
+ }, z.core.$strip>;
50
+ export type FrameworkDeployment = z.infer<typeof frameworkDeploymentSchema>;
51
+ export type FrameworkBindings = NonNullable<FrameworkDeployment['bindings']>;
52
+ export declare function parseFrameworkDeployment(value: unknown): FrameworkDeployment;
18
53
  export {};
@@ -1,16 +1,37 @@
1
1
  import { z } from 'zod';
2
- import { FRAMEWORK_NAMES } from './scaffold-names.js';
2
+ import { FRAMEWORK_NAMES, INTEGRATION_NAMES } from './scaffold-names.js';
3
3
  export const FRAMEWORK_CONFIG_SCHEMA_URL = 'https://docs.xeer.run/config-v0.schema.json';
4
4
  export const FRAMEWORK_CONFIG_FORMAT = 'xeer.config.v0';
5
+ /**
6
+ * What an EmDash deployment must bind. EmDash's own adapters fix the D1 and R2 binding names, its
7
+ * sign-in needs Astro's session KV, and its scheduled publishing needs the platform's one supported
8
+ * schedule. Stated once here so the config, the build, and the control plane refuse the same shape.
9
+ */
10
+ export const EMDASH_DEPLOYMENT = Object.freeze({
11
+ framework: 'astro', database: 'DB', storage: 'MEDIA', schedule: '* * * * *',
12
+ });
13
+ function requireIntegrationFramework(value, context) {
14
+ if (value.integration === 'emdash' && value.framework !== 'astro') {
15
+ context.addIssue({ code: 'custom', message: 'The emdash integration requires the astro framework.' });
16
+ }
17
+ }
5
18
  const frameworkConfigSchema = z.object({
6
19
  $schema: z.string().url().optional(),
7
20
  format: z.literal(FRAMEWORK_CONFIG_FORMAT),
8
21
  name: z.string().regex(/^[a-z][a-z0-9-]{0,61}[a-z0-9]$/u),
9
22
  framework: z.enum(FRAMEWORK_NAMES),
23
+ integration: z.enum(INTEGRATION_NAMES).optional(),
10
24
  capabilities: z.object({
11
25
  auth: z.object({}).strict().optional(),
26
+ database: z.object({}).strict().optional(),
27
+ storage: z.object({}).strict().optional(),
12
28
  }).strict(),
13
- }).strict();
29
+ }).strict().superRefine((value, context) => {
30
+ requireIntegrationFramework(value, context);
31
+ if (value.integration === 'emdash' && (!value.capabilities.database || !value.capabilities.storage)) {
32
+ context.addIssue({ code: 'custom', message: 'The emdash integration requires database and storage capabilities.' });
33
+ }
34
+ });
14
35
  export const frameworkConfigJsonSchema = Object.freeze({
15
36
  ...z.toJSONSchema(frameworkConfigSchema),
16
37
  $id: FRAMEWORK_CONFIG_SCHEMA_URL,
@@ -20,3 +41,29 @@ export const frameworkConfigJsonSchema = Object.freeze({
20
41
  export function parseFrameworkConfig(value) {
21
42
  return frameworkConfigSchema.parse(value);
22
43
  }
44
+ const bindingName = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/u)
45
+ .refine((name) => !name.startsWith('XEER_') && !['ASSETS', 'XeerState'].includes(name), 'Binding names must not shadow platform bindings.');
46
+ const frameworkDeploymentSchema = z.object({
47
+ framework: z.enum(FRAMEWORK_NAMES).optional(),
48
+ integration: z.enum(INTEGRATION_NAMES).optional(),
49
+ bindings: z.object({ database: bindingName.optional(), storage: bindingName.optional(),
50
+ session: bindingName.optional() }).strict().optional(),
51
+ schedule: z.literal('* * * * *').optional(),
52
+ }).superRefine((value, context) => {
53
+ const names = Object.values(value.bindings ?? {});
54
+ if (new Set(names).size !== names.length)
55
+ context.addIssue({ code: 'custom', message: 'Binding names must be distinct.' });
56
+ if ((value.bindings !== undefined || value.schedule !== undefined) && value.framework !== 'astro') {
57
+ context.addIssue({ code: 'custom', message: 'Declared bindings and schedules require an Astro framework bundle.' });
58
+ }
59
+ requireIntegrationFramework(value, context);
60
+ if (value.integration === 'emdash' && (value.bindings?.database !== EMDASH_DEPLOYMENT.database
61
+ || value.bindings.storage !== EMDASH_DEPLOYMENT.storage || value.bindings.session === undefined
62
+ || value.schedule !== EMDASH_DEPLOYMENT.schedule)) {
63
+ context.addIssue({ code: 'custom', message: 'The emdash integration requires the DB database binding, the '
64
+ + 'MEDIA storage binding, a session binding, and the * * * * * schedule.' });
65
+ }
66
+ });
67
+ export function parseFrameworkDeployment(value) {
68
+ return frameworkDeploymentSchema.parse(value);
69
+ }
@@ -26,3 +26,4 @@ export * from './framework-config.js';
26
26
  export * from './tunnel.js';
27
27
  export * from './type-check-profile.js';
28
28
  export * from './editor-settings.js';
29
+ export * from './platform-grants.js';
@@ -26,3 +26,4 @@ export * from './framework-config.js';
26
26
  export * from './tunnel.js';
27
27
  export * from './type-check-profile.js';
28
28
  export * from './editor-settings.js';
29
+ export * from './platform-grants.js';
@@ -0,0 +1,25 @@
1
+ /** Fixed by the endpoint, never selected by an incoming grant. */
2
+ export declare const PLATFORM_GRANTS: Readonly<{
3
+ inspector: Readonly<{
4
+ audience: "xeer-inspector";
5
+ maxTtlSeconds: 60;
6
+ }>;
7
+ scheduler: Readonly<{
8
+ audience: "xeer-scheduler";
9
+ maxTtlSeconds: 60;
10
+ }>;
11
+ bootstrap: Readonly<{
12
+ audience: "xeer-bootstrap";
13
+ maxTtlSeconds: 1800;
14
+ }>;
15
+ }>;
16
+ export type PlatformGrantScope = keyof typeof PLATFORM_GRANTS;
17
+ export type PlatformGrantEnvironment = 'production' | 'preview';
18
+ /** Includes cold framework migrations; four batches remain below a cron event's 15-minute lifetime. */
19
+ export declare const PLATFORM_SCHEDULE: Readonly<{
20
+ invocationDeadlineMs: 180000;
21
+ dispatchTimeoutMs: 185000;
22
+ leaseMs: 210000;
23
+ maxApplications: 20;
24
+ concurrency: 5;
25
+ }>;
@@ -0,0 +1,14 @@
1
+ /** Fixed by the endpoint, never selected by an incoming grant. */
2
+ export const PLATFORM_GRANTS = Object.freeze({
3
+ inspector: Object.freeze({ audience: 'xeer-inspector', maxTtlSeconds: 60 }),
4
+ scheduler: Object.freeze({ audience: 'xeer-scheduler', maxTtlSeconds: 60 }),
5
+ bootstrap: Object.freeze({ audience: 'xeer-bootstrap', maxTtlSeconds: 1800 }),
6
+ });
7
+ /** Includes cold framework migrations; four batches remain below a cron event's 15-minute lifetime. */
8
+ export const PLATFORM_SCHEDULE = Object.freeze({
9
+ invocationDeadlineMs: 180_000,
10
+ dispatchTimeoutMs: 185_000,
11
+ leaseMs: 210_000,
12
+ maxApplications: 20,
13
+ concurrency: 5,
14
+ });
@@ -110,6 +110,7 @@ export const LIVE_STREAM_PATH = '/__xeer/events';
110
110
  export const PUBLIC_RESERVED_ROUTES = new Set([
111
111
  '/__xeer/health',
112
112
  '/__xeer/identity',
113
+ '/__xeer/bootstrap',
113
114
  '/__xeer/rpc/query',
114
115
  '/__xeer/rpc/mutation',
115
116
  LIVE_STREAM_PATH,
@@ -13,6 +13,19 @@
13
13
  */
14
14
  export declare const BUNDLED_TEMPLATES: readonly ["notes", "todo", "blog", "personal-site"];
15
15
  export type BundledTemplateName = (typeof BUNDLED_TEMPLATES)[number];
16
+ /**
17
+ * The bundled framework templates: a complete framework project, written offline like the
18
+ * application starters above, but holding `xeer.config.json` and declaring which framework runs it.
19
+ *
20
+ * A template rather than a framework name because EmDash is an Astro application, not another
21
+ * runtime: `--template emdash` writes an Astro project whose config declares `framework: "astro"`
22
+ * and `integration: "emdash"`. Kept apart from {@link BUNDLED_TEMPLATES} because the two write
23
+ * different root contracts, and every application-only consumer of that list must stay narrow.
24
+ */
25
+ export declare const BUNDLED_FRAMEWORK_TEMPLATES: Readonly<{
26
+ readonly emdash: "astro";
27
+ }>;
28
+ export type BundledFrameworkTemplateName = keyof typeof BUNDLED_FRAMEWORK_TEMPLATES;
16
29
  /**
17
30
  * The frameworks Xeer runs rather than compiles: a `xeer.config.json` beside a project whose own
18
31
  * toolchain owns the renderer, the routes, and the build.
@@ -21,5 +34,14 @@ export type BundledTemplateName = (typeof BUNDLED_TEMPLATES)[number];
21
34
  * and every later command dispatches on which one is present. `--framework` selects one for a fresh
22
35
  * project; `xeer init` adopts an existing one; `xeer.config.json` declares which one a directory is.
23
36
  */
24
- export declare const FRAMEWORK_NAMES: readonly ["sveltekit"];
37
+ export declare const FRAMEWORK_NAMES: readonly ["sveltekit", "astro"];
25
38
  export type FrameworkName = (typeof FRAMEWORK_NAMES)[number];
39
+ export declare const SCAFFOLDABLE_FRAMEWORK_NAMES: readonly ["sveltekit", "astro"];
40
+ export type ScaffoldableFrameworkName = (typeof SCAFFOLDABLE_FRAMEWORK_NAMES)[number];
41
+ /**
42
+ * The integrations a framework project may declare on top of its framework. One today: EmDash, an
43
+ * Astro CMS whose deployment needs a fixed binding set and a computed site URL. Declared explicitly
44
+ * in `xeer.config.json`, never inferred from packages, routes, or resource names.
45
+ */
46
+ export declare const INTEGRATION_NAMES: readonly ["emdash"];
47
+ export type IntegrationName = (typeof INTEGRATION_NAMES)[number];
@@ -12,6 +12,16 @@
12
12
  * it names. Offline, deterministic, and resolved before any catalog request.
13
13
  */
14
14
  export const BUNDLED_TEMPLATES = Object.freeze(['notes', 'todo', 'blog', 'personal-site']);
15
+ /**
16
+ * The bundled framework templates: a complete framework project, written offline like the
17
+ * application starters above, but holding `xeer.config.json` and declaring which framework runs it.
18
+ *
19
+ * A template rather than a framework name because EmDash is an Astro application, not another
20
+ * runtime: `--template emdash` writes an Astro project whose config declares `framework: "astro"`
21
+ * and `integration: "emdash"`. Kept apart from {@link BUNDLED_TEMPLATES} because the two write
22
+ * different root contracts, and every application-only consumer of that list must stay narrow.
23
+ */
24
+ export const BUNDLED_FRAMEWORK_TEMPLATES = Object.freeze({ emdash: 'astro' });
15
25
  /**
16
26
  * The frameworks Xeer runs rather than compiles: a `xeer.config.json` beside a project whose own
17
27
  * toolchain owns the renderer, the routes, and the build.
@@ -20,4 +30,11 @@ export const BUNDLED_TEMPLATES = Object.freeze(['notes', 'todo', 'blog', 'person
20
30
  * and every later command dispatches on which one is present. `--framework` selects one for a fresh
21
31
  * project; `xeer init` adopts an existing one; `xeer.config.json` declares which one a directory is.
22
32
  */
23
- export const FRAMEWORK_NAMES = Object.freeze(['sveltekit']);
33
+ export const FRAMEWORK_NAMES = Object.freeze(['sveltekit', 'astro']);
34
+ export const SCAFFOLDABLE_FRAMEWORK_NAMES = Object.freeze(['sveltekit', 'astro']);
35
+ /**
36
+ * The integrations a framework project may declare on top of its framework. One today: EmDash, an
37
+ * Astro CMS whose deployment needs a fixed binding set and a computed site URL. Declared explicitly
38
+ * in `xeer.config.json`, never inferred from packages, routes, or resource names.
39
+ */
40
+ export const INTEGRATION_NAMES = Object.freeze(['emdash']);
@@ -63,6 +63,7 @@ export declare const templateDistributionEntrySchema: z.ZodObject<{
63
63
  "xeer-application": "xeer-application";
64
64
  }>;
65
65
  framework: z.ZodOptional<z.ZodEnum<{
66
+ astro: "astro";
66
67
  sveltekit: "sveltekit";
67
68
  }>>;
68
69
  }, z.core.$strict>>;
@@ -115,6 +116,7 @@ export declare const templateDistributionCatalogSchema: z.ZodObject<{
115
116
  "xeer-application": "xeer-application";
116
117
  }>;
117
118
  framework: z.ZodOptional<z.ZodEnum<{
119
+ astro: "astro";
118
120
  sveltekit: "sveltekit";
119
121
  }>>;
120
122
  }, z.core.$strict>>;