@honua/mcp-server 0.1.4-beta.0 → 0.1.9-beta.0

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.
Files changed (50) hide show
  1. package/README.md +30 -2
  2. package/dist/src/certification/admin-parity.d.ts +96 -0
  3. package/dist/src/certification/admin-parity.d.ts.map +1 -0
  4. package/dist/src/certification/admin-parity.js +235 -0
  5. package/dist/src/certification/target.d.ts.map +1 -1
  6. package/dist/src/certification/target.js +18 -26
  7. package/dist/src/credential-endpoint.d.ts +3 -0
  8. package/dist/src/credential-endpoint.d.ts.map +1 -0
  9. package/dist/src/credential-endpoint.js +24 -0
  10. package/dist/src/entrypoint.d.ts +20 -0
  11. package/dist/src/entrypoint.d.ts.map +1 -0
  12. package/dist/src/entrypoint.js +38 -0
  13. package/dist/src/index.d.ts +22 -1
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +33 -5
  16. package/dist/src/proxy.d.ts.map +1 -1
  17. package/dist/src/proxy.js +31 -16
  18. package/dist/src/release/zero-to-map-checkpoint.d.ts +34 -0
  19. package/dist/src/release/zero-to-map-checkpoint.d.ts.map +1 -0
  20. package/dist/src/release/zero-to-map-checkpoint.js +369 -0
  21. package/dist/src/release/zero-to-map-cli.d.ts +51 -0
  22. package/dist/src/release/zero-to-map-cli.d.ts.map +1 -0
  23. package/dist/src/release/zero-to-map-cli.js +881 -0
  24. package/dist/src/release/zero-to-map-provision.d.ts +37 -0
  25. package/dist/src/release/zero-to-map-provision.d.ts.map +1 -0
  26. package/dist/src/release/zero-to-map-provision.js +177 -0
  27. package/dist/src/release/zero-to-map.d.ts +296 -0
  28. package/dist/src/release/zero-to-map.d.ts.map +1 -0
  29. package/dist/src/release/zero-to-map.js +1069 -0
  30. package/dist/src/tools/admin-install-local.d.ts +30 -0
  31. package/dist/src/tools/admin-install-local.d.ts.map +1 -0
  32. package/dist/src/tools/admin-install-local.js +22 -0
  33. package/dist/src/tools/control-plane-command.d.ts +99 -0
  34. package/dist/src/tools/control-plane-command.d.ts.map +1 -0
  35. package/dist/src/tools/control-plane-command.js +180 -0
  36. package/dist/src/tools/docs-search.d.ts +100 -0
  37. package/dist/src/tools/docs-search.d.ts.map +1 -0
  38. package/dist/src/tools/docs-search.js +381 -0
  39. package/package.json +8 -3
  40. package/release/zero-to-map/README.md +174 -0
  41. package/release/zero-to-map/configs/claude-code.mcp.json +11 -0
  42. package/release/zero-to-map/configs/claude-desktop.json +11 -0
  43. package/release/zero-to-map/configs/cursor.json +11 -0
  44. package/release/zero-to-map/contracts/aws-ecs-provision-binding.schema.json +59 -0
  45. package/release/zero-to-map/contracts/checkpoint.schema.json +152 -0
  46. package/release/zero-to-map/contracts/console-receipt.schema.json +150 -0
  47. package/release/zero-to-map/contracts/esri-gp-mcp.v1.json +73 -0
  48. package/release/zero-to-map/fixtures/parcels.geojson +33 -0
  49. package/release/zero-to-map/fixtures/zoning.geojson +24 -0
  50. package/release/zero-to-map/journey.v1.json +1248 -0
package/dist/src/index.js CHANGED
@@ -1,16 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
3
  import { HonuaClient } from "@honua/sdk-js";
5
4
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
6
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { isMainEntrypoint } from "./entrypoint.js";
7
7
  import { registerNlMapControlMcpTools } from "./nl-map-control.js";
8
8
  import * as layerSchemaResource from "./resources/layer-schema.js";
9
9
  import * as servicesResource from "./resources/services.js";
10
10
  import * as stylesResource from "./resources/styles.js";
11
+ import * as adminInstallLocal from "./tools/admin-install-local.js";
11
12
  import * as applyStylePreset from "./tools/apply-style-preset.js";
13
+ import * as controlPlaneCommand from "./tools/control-plane-command.js";
12
14
  import * as countFeatures from "./tools/count-features.js";
13
15
  import * as describeLayer from "./tools/describe-layer.js";
16
+ import * as docsSearch from "./tools/docs-search.js";
14
17
  import * as explainCapabilityGap from "./tools/explain-capability-gap.js";
15
18
  import * as getExtent from "./tools/get-extent.js";
16
19
  import * as getStyle from "./tools/get-style.js";
@@ -19,6 +22,7 @@ import * as listSources from "./tools/list-sources.js";
19
22
  import * as queryFeatures from "./tools/query-features.js";
20
23
  import * as statistics from "./tools/statistics.js";
21
24
  export { createNlMapControlMcpHost, registerNlMapControlMcpTools, } from "./nl-map-control.js";
25
+ export { CONTROL_PLANE_COMMAND_TOOL_PREFIX, controlPlaneCommandToolName, controlPlaneCommandToolSchema, registerControlPlaneCommandTools, } from "./tools/control-plane-command.js";
22
26
  const DEFAULT_TIMEOUT_MS = 30_000;
23
27
  const DEFAULT_MAX_RETRIES = 2;
24
28
  const DEFAULT_SERVER_VERSION = "0.0.3-alpha.0";
@@ -146,6 +150,15 @@ export function createClientFromEnv(env = process.env) {
146
150
  * mutation, jobs, and publishing — is a superset reached over streamable-HTTP and
147
151
  * mirrored to stdio by `honua-mcp-proxy` ({@link runProxy}); it requires a Honua
148
152
  * deployment. Standalone is the front door; the proxy is the upgrade path.
153
+ * Local installation is intentionally absent from this catalog and is exposed
154
+ * only by {@link createBootstrapServer}.
155
+ *
156
+ * Control-plane MUTATION is likewise absent by default, and when a host does
157
+ * enable it ({@link CreateServerOptions.controlPlaneCommands}) MCP does not get
158
+ * a workflow of its own: the tools are generated from the shared command
159
+ * catalog in `@honua/sdk-js/control-plane`, so an MCP invocation and the
160
+ * equivalent `honua` CLI, Studio, or direct-JS invocation share one
161
+ * implementation and one receipt `auditKey`.
149
162
  */
150
163
  export function createServer(client, options = {}) {
151
164
  const server = new McpServer({
@@ -166,6 +179,12 @@ export function createServer(client, options = {}) {
166
179
  server.tool("honua_explain_capability_gap", "Explain whether a source/protocol supports a capability and suggest a safe fallback when it does not.", explainCapabilityGap.schema.shape, async (args) => explainCapabilityGap.execute(client, explainCapabilityGap.schema.parse(args)));
167
180
  server.tool("honua_get_style", "Resolve a style from a server's OGC API – Styles surface. Pass styleId for the canonical StyleRef (style_id, title, encodings incl. inlined MapLibre); omit it to list available styles. On a plain FeatureServer with no styling surface, reports a structured 'not available on this target' result.", getStyle.schema.shape, async (args) => getStyle.execute(client, getStyle.schema.parse(args)));
168
181
  server.tool("honua_apply_style_preset", "Resolve a style preset and its MapLibre stylesheet for client-side application. Read-only: returns the stylesheet to apply locally; it does not mutate server state. Degrades gracefully on a target with no styling surface.", applyStylePreset.schema.shape, async (args) => applyStylePreset.execute(client, applyStylePreset.schema.parse(args)));
182
+ if (options.controlPlaneCommands) {
183
+ // Thin adapters generated from the shared command catalog. Registered last
184
+ // so the platform-free read-only surface keeps its stable tool order.
185
+ controlPlaneCommand.registerControlPlaneCommandTools(server, options.controlPlaneCommands);
186
+ }
187
+ server.tool("honua_docs_search", "Answer a Honua documentation question from the local versioned documentation corpus (llms-full.txt), returning ranked excerpts with a source-file and release-version citation for each hit. Local and offline: reads committed docs, never the network. Use it before guessing at an API, a CLI flag, or a contract. Reports a structured 'not available' result when the corpus is not present in this installation.", docsSearch.schema.shape, { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async (args) => docsSearch.execute(client, docsSearch.schema.parse(args)));
169
188
  // ── Resources ──────────────────────────────────────────────────
170
189
  server.resource("services-catalog", servicesResource.uri, async (_uri) => servicesResource.read(client));
171
190
  server.resource("layer-schema", new ResourceTemplate(layerSchemaResource.uriTemplate, { list: undefined }), async (_uri, params) => layerSchemaResource.read(client, params.encodedServiceId, params.layerId));
@@ -173,6 +192,15 @@ export function createServer(client, options = {}) {
173
192
  server.resource("style", new ResourceTemplate(stylesResource.uriTemplate, { list: undefined }), async (_uri, params) => stylesResource.read(client, params.styleId));
174
193
  return server;
175
194
  }
195
+ /** Bootstrap-only MCP surface for a laptop that does not have Honua yet. */
196
+ export function createBootstrapServer() {
197
+ const server = new McpServer({ name: "honua-bootstrap", version: SERVER_VERSION });
198
+ registerLocalInstallTool(server);
199
+ return server;
200
+ }
201
+ function registerLocalInstallTool(server) {
202
+ server.tool("honua_admin_install_local", "Install the manifest-pinned Honua Server, PostGIS, and Redis on this laptop, wait for readiness, bootstrap a scoped admin key, and write proxy MCP configuration. Requires confirm=true.", adminInstallLocal.schema.shape, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }, async (args) => adminInstallLocal.execute(adminInstallLocal.schema.parse(args)));
203
+ }
176
204
  /* v8 ignore start -- live-process entry: wires the stdio transport to a real
177
205
  SDK client against HONUA_BASE_URL; exercised by running the bin, not unit tests.
178
206
  The unit-testable logic (option resolution, client construction, server wiring)
@@ -181,14 +209,14 @@ export function createServer(client, options = {}) {
181
209
  * Run the platform-free standalone server end-to-end over stdio.
182
210
  *
183
211
  * Builds a `HonuaClient` from the environment (`HONUA_BASE_URL` + optional
184
- * transport/auth) and exposes {@link createServer} — the nine read-only tools —
212
+ * transport/auth) and exposes {@link createServer} — the ten read-only tools —
185
213
  * to a stdio MCP client (Claude Desktop / Claude Code / any MCP agent). Point
186
214
  * `HONUA_BASE_URL` at any public FeatureServer/OGC endpoint; no Honua server is
187
215
  * required. For the Honua-enhanced `/mcp` catalog, use `honua-mcp-proxy` instead.
188
216
  */
189
217
  export async function runStandalone(env = process.env) {
190
- const client = createClientFromEnv(env);
191
- const server = createServer(client);
218
+ const bootstrapOnly = env.HONUA_MCP_BOOTSTRAP === "1" || env.HONUA_MCP_BOOTSTRAP === "true";
219
+ const server = bootstrapOnly ? createBootstrapServer() : createServer(createClientFromEnv(env));
192
220
  const transport = new StdioServerTransport();
193
221
  await server.connect(transport);
194
222
  }
@@ -196,7 +224,7 @@ export async function runStandalone(env = process.env) {
196
224
  async function main() {
197
225
  await runStandalone();
198
226
  }
199
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
227
+ if (isMainEntrypoint(import.meta.url)) {
200
228
  main().catch((err) => {
201
229
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
202
230
  process.exit(1);
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAiBnE;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAqBtF;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASlF;AAED,oFAAoF;AACpF,wBAAsB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA+C1D;AAOD;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBlF"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAmBnE;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAsBD,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAqBtF;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUlF;AAED,oFAAoF;AACpF,wBAAsB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA+C1D;AAOD;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBlF"}
package/dist/src/proxy.js CHANGED
@@ -1,34 +1,48 @@
1
1
  #!/usr/bin/env node
2
- import { fileURLToPath } from "node:url";
3
2
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
3
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
5
4
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
6
  import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, PromptListChangedNotificationSchema, ReadResourceRequestSchema, ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema, } from "@modelcontextprotocol/sdk/types.js";
7
+ import { requireSecureCredentialEndpoint } from "./credential-endpoint.js";
8
+ import { isMainEntrypoint } from "./entrypoint.js";
8
9
  import { SERVER_VERSION } from "./index.js";
10
+ function isConfigured(value) {
11
+ return value !== undefined && value.length > 0;
12
+ }
13
+ function validateAuthentication(options) {
14
+ const hasBearer = isConfigured(options.authToken);
15
+ const hasApiKey = isConfigured(options.apiKey);
16
+ if (hasBearer && hasApiKey) {
17
+ throw new Error("Configure exactly one upstream authentication scheme: unset either authToken/HONUA_MCP_AUTH_TOKEN or apiKey/HONUA_ADMIN_KEY/HONUA_API_KEY");
18
+ }
19
+ return hasBearer ? "bearer" : hasApiKey ? "api-key" : "anonymous";
20
+ }
21
+ function validateProxyOptions(options) {
22
+ const authMode = validateAuthentication(options);
23
+ return requireSecureCredentialEndpoint(options.remoteUrl, "remoteUrl", authMode !== "anonymous");
24
+ }
9
25
  export function resolveProxyOptions(env = process.env) {
10
26
  const remoteUrl = env.HONUA_MCP_REMOTE_URL ?? env.HONUA_MCP_URL;
11
27
  if (!remoteUrl) {
12
28
  throw new Error("HONUA_MCP_REMOTE_URL environment variable is required (the remote honua /mcp endpoint to proxy).");
13
29
  }
14
- let parsed;
15
- try {
16
- parsed = new URL(remoteUrl);
17
- }
18
- catch {
19
- throw new Error(`HONUA_MCP_REMOTE_URL must be a valid absolute URL: ${remoteUrl}`);
20
- }
21
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
22
- throw new Error(`HONUA_MCP_REMOTE_URL must use http or https: ${remoteUrl}`);
30
+ const hasAdminKey = isConfigured(env.HONUA_ADMIN_KEY);
31
+ const hasApiKey = isConfigured(env.HONUA_API_KEY);
32
+ if (hasAdminKey && hasApiKey) {
33
+ throw new Error("Configure one API-key source: unset either HONUA_ADMIN_KEY or HONUA_API_KEY; credential precedence is not allowed");
23
34
  }
24
- return {
25
- remoteUrl: parsed.toString(),
35
+ const options = {
36
+ remoteUrl,
26
37
  authToken: env.HONUA_MCP_AUTH_TOKEN,
27
- apiKey: env.HONUA_API_KEY,
38
+ apiKey: hasAdminKey ? env.HONUA_ADMIN_KEY : hasApiKey ? env.HONUA_API_KEY : undefined,
28
39
  };
40
+ const parsed = validateProxyOptions(options);
41
+ return { ...options, remoteUrl: parsed.toString() };
29
42
  }
30
43
  /** Build request headers for the upstream connection from the resolved options. */
31
44
  export function buildUpstreamHeaders(options) {
45
+ validateProxyOptions(options);
32
46
  const headers = {};
33
47
  if (options.authToken) {
34
48
  headers.Authorization = `Bearer ${options.authToken}`;
@@ -41,8 +55,9 @@ export function buildUpstreamHeaders(options) {
41
55
  /** Connect an upstream MCP client to the remote honua /mcp over streamable HTTP. */
42
56
  export async function connectUpstream(options) {
43
57
  const headers = buildUpstreamHeaders(options);
44
- const transport = new StreamableHTTPClientTransport(new URL(options.remoteUrl), {
45
- requestInit: Object.keys(headers).length > 0 ? { headers } : undefined,
58
+ const remoteUrl = validateProxyOptions(options);
59
+ const transport = new StreamableHTTPClientTransport(remoteUrl, {
60
+ requestInit: { ...(Object.keys(headers).length > 0 ? { headers } : {}), redirect: "manual" },
46
61
  });
47
62
  const client = new Client({ name: "honua-mcp-stdio-proxy", version: SERVER_VERSION });
48
63
  await client.connect(transport);
@@ -115,7 +130,7 @@ export async function runProxy(env = process.env) {
115
130
  const transport = new StdioServerTransport();
116
131
  await server.connect(transport);
117
132
  }
118
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
133
+ if (isMainEntrypoint(import.meta.url)) {
119
134
  runProxy().catch((err) => {
120
135
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
121
136
  process.exit(1);
@@ -0,0 +1,34 @@
1
+ import { type JourneyPauseSnapshot, type JourneyReceiptRequest, type JourneyResumeState } from "./zero-to-map.js";
2
+ export declare const ZERO_TO_MAP_CHECKPOINT_SCHEMA: "honua.zero-to-map.checkpoint/v1";
3
+ export declare const ZERO_TO_MAP_CHECKPOINT_MAX_AGE_MS: number;
4
+ export interface ZeroToMapCheckpointBindings {
5
+ readonly journeyId: string;
6
+ readonly releaseContract: string;
7
+ readonly target: "local-docker" | "aws-ecs";
8
+ readonly planSha256: string;
9
+ readonly sourceRevision: string;
10
+ readonly mcpEndpointSha256: string;
11
+ readonly candidateId: string;
12
+ readonly releaseId: string;
13
+ readonly provisionReceiptSha256?: string;
14
+ }
15
+ export interface ZeroToMapCheckpoint extends ZeroToMapCheckpointBindings {
16
+ readonly schemaVersion: typeof ZERO_TO_MAP_CHECKPOINT_SCHEMA;
17
+ readonly state: "paused" | "consumed";
18
+ readonly createdAt: string;
19
+ readonly resume: JourneyResumeState;
20
+ readonly consoleReceiptRequest: JourneyReceiptRequest;
21
+ readonly consumedAt?: string;
22
+ readonly finalReceiptSha256?: string;
23
+ readonly integrity: {
24
+ readonly algorithm: "sha256";
25
+ readonly digest: string;
26
+ };
27
+ }
28
+ export declare function createZeroToMapCheckpoint(bindings: ZeroToMapCheckpointBindings, snapshot: JourneyPauseSnapshot, createdAt?: string): ZeroToMapCheckpoint;
29
+ export declare function consumeZeroToMapCheckpoint(checkpoint: ZeroToMapCheckpoint, finalReceiptSha256: string, consumedAt?: string): ZeroToMapCheckpoint;
30
+ export declare function parseZeroToMapCheckpoint(value: unknown): ZeroToMapCheckpoint;
31
+ export declare function assertZeroToMapCheckpointFresh(checkpoint: ZeroToMapCheckpoint, now?: Date): void;
32
+ export declare function assertZeroToMapCheckpointDigest(checkpoint: ZeroToMapCheckpoint, expectedDigest: string): void;
33
+ export declare function assertZeroToMapCheckpointBindings(checkpoint: ZeroToMapCheckpoint, expected: ZeroToMapCheckpointBindings): void;
34
+ //# sourceMappingURL=zero-to-map-checkpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zero-to-map-checkpoint.d.ts","sourceRoot":"","sources":["../../../src/release/zero-to-map-checkpoint.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAE1B,KAAK,kBAAkB,EAMxB,MAAM,kBAAkB,CAAC;AAE1B,eAAO,MAAM,6BAA6B,EAAG,iCAA0C,CAAC;AACxF,eAAO,MAAM,iCAAiC,QAAuB,CAAC;AAEtE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAC1C;AAED,MAAM,WAAW,mBAAoB,SAAQ,2BAA2B;IACtE,QAAQ,CAAC,aAAa,EAAE,OAAO,6BAA6B,CAAC;IAC7D,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE;QAClB,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC;QAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAID,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,2BAA2B,EACrC,QAAQ,EAAE,oBAAoB,EAC9B,SAAS,SAA2B,GACnC,mBAAmB,CAqBrB;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,mBAAmB,EAC/B,kBAAkB,EAAE,MAAM,EAC1B,UAAU,SAA2B,GACpC,mBAAmB,CAIrB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAkF5E;AAED,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,mBAAmB,EAAE,GAAG,OAAa,GAAG,IAAI,CAKtG;AAED,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAI7G;AAED,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,EAAE,2BAA2B,GACpC,IAAI,CAIN"}
@@ -0,0 +1,369 @@
1
+ import { createHash } from "node:crypto";
2
+ import { ZERO_TO_MAP_CATALOG_RECEIPT_SCHEMA, ZERO_TO_MAP_CONSOLE_RECEIPT_REQUEST_SCHEMA, } from "./zero-to-map.js";
3
+ export const ZERO_TO_MAP_CHECKPOINT_SCHEMA = "honua.zero-to-map.checkpoint/v1";
4
+ export const ZERO_TO_MAP_CHECKPOINT_MAX_AGE_MS = 24 * 60 * 60 * 1_000;
5
+ export function createZeroToMapCheckpoint(bindings, snapshot, createdAt = new Date().toISOString()) {
6
+ assertTargetBinding(bindings);
7
+ const payload = {
8
+ schemaVersion: ZERO_TO_MAP_CHECKPOINT_SCHEMA,
9
+ state: "paused",
10
+ createdAt,
11
+ ...bindings,
12
+ resume: {
13
+ startedAt: snapshot.startedAt,
14
+ capturedVariables: snapshot.capturedVariables,
15
+ completedStages: snapshot.completedStages,
16
+ resumeAt: snapshot.resumeAt,
17
+ // Carried across the Console boundary because no post-checkpoint stage
18
+ // can re-derive it; the resumed run's final receipt would otherwise
19
+ // overwrite the pre-pause receipt with one that has no catalog evidence.
20
+ ...(snapshot.catalog ? { catalog: snapshot.catalog } : {}),
21
+ },
22
+ consoleReceiptRequest: snapshot.consoleReceiptRequest,
23
+ };
24
+ assertSecretFree(payload);
25
+ return seal(payload);
26
+ }
27
+ export function consumeZeroToMapCheckpoint(checkpoint, finalReceiptSha256, consumedAt = new Date().toISOString()) {
28
+ if (checkpoint.state !== "paused")
29
+ throw new Error("checkpoint has already been consumed");
30
+ const { integrity: _integrity, ...prior } = checkpoint;
31
+ return seal({ ...prior, state: "consumed", consumedAt, finalReceiptSha256 });
32
+ }
33
+ export function parseZeroToMapCheckpoint(value) {
34
+ const checkpoint = object(value, "checkpoint");
35
+ exactKeys(checkpoint, [
36
+ "schemaVersion",
37
+ "state",
38
+ "createdAt",
39
+ "journeyId",
40
+ "releaseContract",
41
+ "target",
42
+ "planSha256",
43
+ "sourceRevision",
44
+ "mcpEndpointSha256",
45
+ "candidateId",
46
+ "releaseId",
47
+ "provisionReceiptSha256",
48
+ "resume",
49
+ "consoleReceiptRequest",
50
+ "consumedAt",
51
+ "finalReceiptSha256",
52
+ "integrity",
53
+ ], "checkpoint");
54
+ if (checkpoint.schemaVersion !== ZERO_TO_MAP_CHECKPOINT_SCHEMA) {
55
+ throw new Error(`checkpoint.schemaVersion must be ${ZERO_TO_MAP_CHECKPOINT_SCHEMA}`);
56
+ }
57
+ if (checkpoint.state !== "paused" && checkpoint.state !== "consumed") {
58
+ throw new Error("checkpoint.state must be paused or consumed");
59
+ }
60
+ const createdAt = timestamp(checkpoint.createdAt, "checkpoint.createdAt");
61
+ const bindings = {
62
+ journeyId: text(checkpoint.journeyId, "checkpoint.journeyId"),
63
+ releaseContract: text(checkpoint.releaseContract, "checkpoint.releaseContract"),
64
+ target: target(checkpoint.target),
65
+ planSha256: sha256(checkpoint.planSha256, "checkpoint.planSha256"),
66
+ sourceRevision: revision(checkpoint.sourceRevision, "checkpoint.sourceRevision"),
67
+ mcpEndpointSha256: sha256(checkpoint.mcpEndpointSha256, "checkpoint.mcpEndpointSha256"),
68
+ candidateId: text(checkpoint.candidateId, "checkpoint.candidateId"),
69
+ releaseId: text(checkpoint.releaseId, "checkpoint.releaseId"),
70
+ ...(checkpoint.provisionReceiptSha256 === undefined
71
+ ? {}
72
+ : {
73
+ provisionReceiptSha256: sha256(checkpoint.provisionReceiptSha256, "checkpoint.provisionReceiptSha256"),
74
+ }),
75
+ };
76
+ assertTargetBinding(bindings);
77
+ const resume = parseResume(checkpoint.resume);
78
+ const consoleReceiptRequest = parseReceiptRequest(checkpoint.consoleReceiptRequest);
79
+ const integrity = object(checkpoint.integrity, "checkpoint.integrity");
80
+ exactKeys(integrity, ["algorithm", "digest"], "checkpoint.integrity");
81
+ if (integrity.algorithm !== "sha256")
82
+ throw new Error("checkpoint.integrity.algorithm must be sha256");
83
+ const digest = sha256(integrity.digest, "checkpoint.integrity.digest");
84
+ const state = checkpoint.state;
85
+ const consumedAt = checkpoint.consumedAt;
86
+ const finalReceiptSha256 = checkpoint.finalReceiptSha256;
87
+ if (state === "paused" && (consumedAt !== undefined || finalReceiptSha256 !== undefined)) {
88
+ throw new Error("paused checkpoint cannot carry consumed receipt fields");
89
+ }
90
+ if (state === "consumed" && (consumedAt === undefined || finalReceiptSha256 === undefined)) {
91
+ throw new Error("consumed checkpoint must carry consumedAt and finalReceiptSha256");
92
+ }
93
+ const payload = {
94
+ schemaVersion: ZERO_TO_MAP_CHECKPOINT_SCHEMA,
95
+ state,
96
+ createdAt,
97
+ ...bindings,
98
+ resume,
99
+ consoleReceiptRequest,
100
+ ...(state === "consumed"
101
+ ? {
102
+ consumedAt: timestamp(consumedAt, "checkpoint.consumedAt"),
103
+ finalReceiptSha256: sha256(finalReceiptSha256, "checkpoint.finalReceiptSha256"),
104
+ }
105
+ : {}),
106
+ };
107
+ assertSecretFree(payload);
108
+ const expected = digestPayload(payload);
109
+ if (digest !== expected)
110
+ throw new Error("checkpoint integrity digest does not match its canonical payload");
111
+ return { ...payload, integrity: { algorithm: "sha256", digest } };
112
+ }
113
+ export function assertZeroToMapCheckpointFresh(checkpoint, now = new Date()) {
114
+ const age = now.getTime() - Date.parse(checkpoint.createdAt);
115
+ if (age < 0 || age > ZERO_TO_MAP_CHECKPOINT_MAX_AGE_MS) {
116
+ throw new Error("checkpoint is stale or has a future creation time");
117
+ }
118
+ }
119
+ export function assertZeroToMapCheckpointDigest(checkpoint, expectedDigest) {
120
+ if (!/^[0-9a-f]{64}$/.test(expectedDigest) || checkpoint.integrity.digest !== expectedDigest) {
121
+ throw new Error("externally carried checkpoint digest does not match");
122
+ }
123
+ }
124
+ export function assertZeroToMapCheckpointBindings(checkpoint, expected) {
125
+ for (const key of Object.keys(expected)) {
126
+ if (checkpoint[key] !== expected[key])
127
+ throw new Error(`checkpoint binding mismatch: ${key}`);
128
+ }
129
+ }
130
+ function assertTargetBinding(bindings) {
131
+ if (bindings.target === "aws-ecs" && bindings.provisionReceiptSha256 === undefined) {
132
+ throw new Error("AWS ECS checkpoint must bind provisionReceiptSha256");
133
+ }
134
+ if (bindings.target === "local-docker" && bindings.provisionReceiptSha256 !== undefined) {
135
+ throw new Error("local Docker checkpoint cannot bind an AWS provision receipt");
136
+ }
137
+ }
138
+ function seal(payload) {
139
+ return { ...payload, integrity: { algorithm: "sha256", digest: digestPayload(payload) } };
140
+ }
141
+ function digestPayload(payload) {
142
+ return createHash("sha256").update(canonicalJson(payload)).digest("hex");
143
+ }
144
+ /** Checkpoints are an allowlisted release handoff, never a credential transport. */
145
+ function assertSecretFree(value, path = "checkpoint") {
146
+ if (Array.isArray(value)) {
147
+ value.forEach((item, index) => assertSecretFree(item, `${path}[${index}]`));
148
+ return;
149
+ }
150
+ if (!value || typeof value !== "object")
151
+ return;
152
+ for (const [key, item] of Object.entries(value)) {
153
+ if (/(?:password|authorization|api[-_]?key|access[-_]?key|secret(?:[-_]?key)?|bearer|token|material)/i.test(key)) {
154
+ throw new Error(`checkpoint contains a forbidden credential field at ${path}.${key}`);
155
+ }
156
+ assertSecretFree(item, `${path}.${key}`);
157
+ }
158
+ }
159
+ function canonicalJson(value) {
160
+ if (value === null)
161
+ return "null";
162
+ if (Array.isArray(value))
163
+ return `[${value.map(canonicalJson).join(",")}]`;
164
+ if (typeof value === "object") {
165
+ const candidate = value;
166
+ return `{${Object.keys(candidate)
167
+ .filter((key) => candidate[key] !== undefined)
168
+ .sort()
169
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(candidate[key])}`)
170
+ .join(",")}}`;
171
+ }
172
+ const encoded = JSON.stringify(value);
173
+ if (encoded === undefined)
174
+ throw new Error("checkpoint contains a non-JSON value");
175
+ return encoded;
176
+ }
177
+ function parseResume(value) {
178
+ const resume = object(value, "checkpoint.resume");
179
+ exactKeys(resume, ["startedAt", "capturedVariables", "completedStages", "resumeAt", "catalog"], "checkpoint.resume");
180
+ const capturedVariables = object(resume.capturedVariables, "checkpoint.resume.capturedVariables");
181
+ if (!Array.isArray(resume.completedStages))
182
+ throw new Error("checkpoint.resume.completedStages must be an array");
183
+ return {
184
+ startedAt: timestamp(resume.startedAt, "checkpoint.resume.startedAt"),
185
+ capturedVariables,
186
+ completedStages: resume.completedStages.map(parseStageReceipt),
187
+ resumeAt: parseResumePoint(resume.resumeAt),
188
+ ...(resume.catalog === undefined ? {} : { catalog: parseCatalogReceipt(resume.catalog) }),
189
+ };
190
+ }
191
+ /**
192
+ * Catalog preflight evidence restored from the checkpoint. Validated as
193
+ * strictly as the rest of the payload: it is re-emitted onto the final release
194
+ * receipt, so a malformed or padded catalog block must not survive the parse.
195
+ */
196
+ function parseCatalogReceipt(value) {
197
+ const path = "checkpoint.resume.catalog";
198
+ const catalog = object(value, path);
199
+ exactKeys(catalog, [
200
+ "schemaVersion",
201
+ "activeProfiles",
202
+ "expectedTotalTools",
203
+ "advertisedTotalTools",
204
+ "baseStaticTools",
205
+ "baseAdminTools",
206
+ "auditedExclusions",
207
+ "profiles",
208
+ "catalogSha256",
209
+ "adminRosterSha256",
210
+ "staticRosterSha256",
211
+ "exclusionRosterSha256",
212
+ ], path);
213
+ if (catalog.schemaVersion !== ZERO_TO_MAP_CATALOG_RECEIPT_SCHEMA) {
214
+ throw new Error(`${path}.schemaVersion must be ${ZERO_TO_MAP_CATALOG_RECEIPT_SCHEMA}`);
215
+ }
216
+ if (!Array.isArray(catalog.profiles))
217
+ throw new Error(`${path}.profiles must be an array`);
218
+ return {
219
+ schemaVersion: ZERO_TO_MAP_CATALOG_RECEIPT_SCHEMA,
220
+ activeProfiles: stringList(catalog.activeProfiles, `${path}.activeProfiles`),
221
+ expectedTotalTools: count(catalog.expectedTotalTools, `${path}.expectedTotalTools`),
222
+ advertisedTotalTools: count(catalog.advertisedTotalTools, `${path}.advertisedTotalTools`),
223
+ baseStaticTools: count(catalog.baseStaticTools, `${path}.baseStaticTools`),
224
+ baseAdminTools: count(catalog.baseAdminTools, `${path}.baseAdminTools`),
225
+ auditedExclusions: count(catalog.auditedExclusions, `${path}.auditedExclusions`),
226
+ profiles: catalog.profiles.map((profile, index) => parseProfileReceipt(profile, `${path}.profiles[${index}]`)),
227
+ catalogSha256: sha256(catalog.catalogSha256, `${path}.catalogSha256`),
228
+ adminRosterSha256: sha256(catalog.adminRosterSha256, `${path}.adminRosterSha256`),
229
+ staticRosterSha256: sha256(catalog.staticRosterSha256, `${path}.staticRosterSha256`),
230
+ exclusionRosterSha256: sha256(catalog.exclusionRosterSha256, `${path}.exclusionRosterSha256`),
231
+ };
232
+ }
233
+ function parseProfileReceipt(value, path) {
234
+ const profile = object(value, path);
235
+ exactKeys(profile, ["id", "expectedMembers", "advertisedMembers", "confirmedMembers", "nameResolvedMembers", "rosterSha256"], path);
236
+ return {
237
+ id: text(profile.id, `${path}.id`),
238
+ expectedMembers: count(profile.expectedMembers, `${path}.expectedMembers`),
239
+ advertisedMembers: count(profile.advertisedMembers, `${path}.advertisedMembers`),
240
+ confirmedMembers: stringList(profile.confirmedMembers, `${path}.confirmedMembers`),
241
+ nameResolvedMembers: stringList(profile.nameResolvedMembers, `${path}.nameResolvedMembers`),
242
+ rosterSha256: sha256(profile.rosterSha256, `${path}.rosterSha256`),
243
+ };
244
+ }
245
+ function parseResumePoint(value) {
246
+ const point = object(value, "checkpoint.resume.resumeAt");
247
+ exactKeys(point, ["stageId", "actionId"], "checkpoint.resume.resumeAt");
248
+ return {
249
+ stageId: text(point.stageId, "checkpoint.resume.resumeAt.stageId"),
250
+ actionId: text(point.actionId, "checkpoint.resume.resumeAt.actionId"),
251
+ };
252
+ }
253
+ function parseReceiptRequest(value) {
254
+ const request = object(value, "checkpoint.consoleReceiptRequest");
255
+ exactKeys(request, ["schemaVersion", "actionId", "receiptSchema", "matches", "requiredPointers", "equalPointers"], "checkpoint.consoleReceiptRequest");
256
+ if (request.schemaVersion !== ZERO_TO_MAP_CONSOLE_RECEIPT_REQUEST_SCHEMA) {
257
+ throw new Error(`checkpoint.consoleReceiptRequest.schemaVersion must be ${ZERO_TO_MAP_CONSOLE_RECEIPT_REQUEST_SCHEMA}`);
258
+ }
259
+ const matches = object(request.matches, "checkpoint.consoleReceiptRequest.matches");
260
+ const requiredPointers = stringList(request.requiredPointers, "checkpoint.consoleReceiptRequest.requiredPointers");
261
+ if (!Array.isArray(request.equalPointers)) {
262
+ throw new Error("checkpoint.consoleReceiptRequest.equalPointers must be an array");
263
+ }
264
+ const equalPointers = request.equalPointers.map((value, index) => {
265
+ if (!Array.isArray(value) || value.length !== 2 || value.some((item) => typeof item !== "string" || !item)) {
266
+ throw new Error(`checkpoint.consoleReceiptRequest.equalPointers[${index}] must be a pointer pair`);
267
+ }
268
+ return [value[0], value[1]];
269
+ });
270
+ return {
271
+ schemaVersion: ZERO_TO_MAP_CONSOLE_RECEIPT_REQUEST_SCHEMA,
272
+ actionId: text(request.actionId, "checkpoint.consoleReceiptRequest.actionId"),
273
+ receiptSchema: text(request.receiptSchema, "checkpoint.consoleReceiptRequest.receiptSchema"),
274
+ matches,
275
+ requiredPointers,
276
+ equalPointers,
277
+ };
278
+ }
279
+ function parseStageReceipt(value, index) {
280
+ const path = `checkpoint.resume.completedStages[${index}]`;
281
+ const stage = object(value, path);
282
+ exactKeys(stage, ["number", "id", "title", "status", "actions"], path);
283
+ if (!Number.isInteger(stage.number) || stage.number < 1)
284
+ throw new Error(`${path}.number must be positive`);
285
+ if (!Array.isArray(stage.actions))
286
+ throw new Error(`${path}.actions must be an array`);
287
+ return {
288
+ number: stage.number,
289
+ id: text(stage.id, `${path}.id`),
290
+ title: text(stage.title, `${path}.title`),
291
+ status: actionStatus(stage.status, `${path}.status`),
292
+ actions: stage.actions.map((action, actionIndex) => parseActionReceipt(action, `${path}.actions[${actionIndex}]`)),
293
+ };
294
+ }
295
+ function parseActionReceipt(value, path) {
296
+ const action = object(value, path);
297
+ exactKeys(action, ["id", "kind", "status", "startedAt", "finishedAt", "code", "message", "evidence", "captures"], path);
298
+ const kind = action.kind;
299
+ if (!["cli", "mcp", "mcp-resource", "gpserver", "receipt", "http"].includes(String(kind))) {
300
+ throw new Error(`${path}.kind is invalid`);
301
+ }
302
+ return {
303
+ id: text(action.id, `${path}.id`),
304
+ kind: kind,
305
+ status: actionStatus(action.status, `${path}.status`),
306
+ startedAt: timestamp(action.startedAt, `${path}.startedAt`),
307
+ finishedAt: timestamp(action.finishedAt, `${path}.finishedAt`),
308
+ ...(action.code === undefined ? {} : { code: text(action.code, `${path}.code`) }),
309
+ ...(action.message === undefined ? {} : { message: text(action.message, `${path}.message`) }),
310
+ ...(action.evidence === undefined ? {} : { evidence: object(action.evidence, `${path}.evidence`) }),
311
+ ...(action.captures === undefined ? {} : { captures: object(action.captures, `${path}.captures`) }),
312
+ };
313
+ }
314
+ function actionStatus(value, path) {
315
+ if (value !== "passed" && value !== "blocked" && value !== "failed" && value !== "skipped") {
316
+ throw new Error(`${path} is invalid`);
317
+ }
318
+ return value;
319
+ }
320
+ function object(value, path) {
321
+ if (!value || typeof value !== "object" || Array.isArray(value))
322
+ throw new Error(`${path} must be an object`);
323
+ return value;
324
+ }
325
+ function exactKeys(value, allowed, path) {
326
+ const extras = Object.keys(value).filter((key) => !allowed.includes(key));
327
+ if (extras.length > 0)
328
+ throw new Error(`${path} has unknown fields: ${extras.join(", ")}`);
329
+ }
330
+ function text(value, path) {
331
+ if (typeof value !== "string" || !value)
332
+ throw new Error(`${path} must be a non-empty string`);
333
+ return value;
334
+ }
335
+ function timestamp(value, path) {
336
+ const result = text(value, path);
337
+ if (!Number.isFinite(Date.parse(result)))
338
+ throw new Error(`${path} must be an ISO timestamp`);
339
+ return result;
340
+ }
341
+ function sha256(value, path) {
342
+ const result = text(value, path);
343
+ if (!/^[0-9a-f]{64}$/.test(result))
344
+ throw new Error(`${path} must be a lowercase SHA-256 digest`);
345
+ return result;
346
+ }
347
+ function revision(value, path) {
348
+ const result = text(value, path);
349
+ if (!/^[0-9a-f]{40}$/.test(result))
350
+ throw new Error(`${path} must be a full lowercase git SHA`);
351
+ return result;
352
+ }
353
+ function target(value) {
354
+ if (value !== "local-docker" && value !== "aws-ecs") {
355
+ throw new Error("checkpoint.target must be local-docker or aws-ecs");
356
+ }
357
+ return value;
358
+ }
359
+ function count(value, path) {
360
+ if (!Number.isInteger(value) || value < 0)
361
+ throw new Error(`${path} must be a non-negative integer`);
362
+ return value;
363
+ }
364
+ function stringList(value, path) {
365
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item)) {
366
+ throw new Error(`${path} must be an array of non-empty strings`);
367
+ }
368
+ return value;
369
+ }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import { type AwsEcsProvisionBinding } from "./zero-to-map-provision.js";
3
+ import { type JourneyExecutionResult, type JourneyMcpResourceAction } from "./zero-to-map.js";
4
+ type JourneyTarget = "local-docker" | "aws-ecs";
5
+ export interface CliOptions {
6
+ readonly execute: boolean;
7
+ readonly confirmed: boolean;
8
+ readonly planPath: string;
9
+ readonly outputPath: string;
10
+ readonly honuaCommand: string;
11
+ readonly target: JourneyTarget;
12
+ readonly mcpUrl?: string;
13
+ readonly checkpointPath?: string;
14
+ readonly checkpointDigest?: string;
15
+ readonly provisionReceiptPath?: string;
16
+ readonly consoleReceiptPath?: string;
17
+ readonly provisionBinding?: AwsEcsProvisionBinding;
18
+ readonly provisionReceiptSha256?: string;
19
+ readonly sourceRevision?: string;
20
+ readonly variables: Readonly<Record<string, unknown>>;
21
+ readonly receiptPaths: Readonly<Record<string, string>>;
22
+ }
23
+ export declare function runZeroToMapCli(argv?: readonly string[], env?: NodeJS.ProcessEnv): Promise<number>;
24
+ export declare function parseZeroToMapCliArgs(argv: readonly string[], env: NodeJS.ProcessEnv): CliOptions;
25
+ /** Resolve the live mutating CLI without permitting Windows' cwd-first executable search. */
26
+ export declare function resolveLiveExecutable(command: string, env: NodeJS.ProcessEnv, excludedDirectory: string): Promise<string>;
27
+ /** Atomically transfer one paused checkpoint to a single resume owner. */
28
+ export declare function claimZeroToMapCheckpoint(path: string, digest: string): Promise<string>;
29
+ export declare function parseInstallAccessCredential(stdout: string): {
30
+ readonly id: string;
31
+ readonly name: string;
32
+ readonly status: "active";
33
+ readonly requestedGrants: readonly string[];
34
+ readonly effectiveGrants: readonly string[];
35
+ readonly canAuthenticate: true;
36
+ readonly referenceType: "private-env-file";
37
+ readonly referenceDigestSha256: string;
38
+ readonly provisioned: boolean;
39
+ };
40
+ /** Poll one MCP job resource to its declared successful terminal state. */
41
+ export declare function readJourneyMcpResource(action: JourneyMcpResourceAction, read: (uri: string) => Promise<unknown>): Promise<JourneyExecutionResult>;
42
+ export declare function requireCompletedPublishedOperation(tool: string, value: unknown): {
43
+ operationId: string;
44
+ status: string;
45
+ handleId?: string;
46
+ approvalLane?: string;
47
+ jobId?: string;
48
+ metadataRevision?: number;
49
+ } | undefined;
50
+ export {};
51
+ //# sourceMappingURL=zero-to-map-cli.d.ts.map