@executioncontrolprotocol/cli 0.10.0 → 0.10.1

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
@@ -74,6 +74,23 @@ ecp describe --env examples/01-echo/environment.ts
74
74
  ecp search "echo" --env examples/01-echo/environment.ts
75
75
  ```
76
76
 
77
+ ### Local daemon (`ecp up`)
78
+
79
+ Start a loopback HTTP daemon that bridges Ollama for the browser demo (CORS + Private Network Access), then open the demo with a pairing token:
80
+
81
+ ```sh
82
+ ecp up
83
+ # Opens https://executioncontrolprotocol.github.io/browser-demo/?token=<uuid>&bridge=http://127.0.0.1:3090
84
+
85
+ ecp up --open-url http://localhost:5173/
86
+ ecp up --no-open
87
+ ```
88
+
89
+ - `GET /health` — `{ ok, version, ollamaReachable }` (no auth; used by the demo to enable Ollama)
90
+ - `POST /v1/invoke` — Bearer token required; body `{ capability, input?, provider? }`
91
+
92
+ Default port **3090** (ECP leet). The demo reads `?token=` (and optional `?bridge=`) automatically. Hosted HTTPS demos need **Chromium** (Chrome/Edge) for Private Network Access. Add extra page origins with `--cors-origin`.
93
+
77
94
  ### Encode / decode formats
78
95
 
79
96
  Encoding and decoding uses **format extensions** bound in your environment.
@@ -0,0 +1,18 @@
1
+ import { Command } from "@oclif/core";
2
+ /** Start the local ECP daemon (bridges Ollama for the browser demo). */
3
+ export default class Up extends Command {
4
+ static summary: string;
5
+ static description: string;
6
+ static examples: string[];
7
+ static flags: {
8
+ port: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
9
+ host: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ "ollama-url": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ "cors-origin": import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
12
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ "open-url": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ open: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=up.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"up.d.ts","sourceRoot":"","sources":["../../src/commands/up.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAA;AAU5C,wEAAwE;AACxE,MAAM,CAAC,OAAO,OAAO,EAAG,SAAQ,OAAO;IACrC,MAAM,CAAC,OAAO,SAA+B;IAE7C,MAAM,CAAC,WAAW,SAEmE;IAErF,MAAM,CAAC,QAAQ,WAId;IAED,MAAM,CAAC,KAAK;;;;;;;;MA8BX;IAEK,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAmD3B"}
@@ -0,0 +1,95 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { Command, Flags } from "@oclif/core";
3
+ import { DEFAULT_DEMO_OPEN_URL, DEFAULT_ECP_UP_HOST, DEFAULT_ECP_UP_PORT, DEFAULT_OLLAMA_URL, } from "../lib/up/constants.js";
4
+ import { startEcpUpDaemon } from "../lib/up/create-daemon.js";
5
+ import { buildDemoOpenUrl, openBrowserUrl, originFromUrl } from "../lib/up/open-browser.js";
6
+ /** Start the local ECP daemon (bridges Ollama for the browser demo). */
7
+ export default class Up extends Command {
8
+ static summary = "Start the local ECP daemon";
9
+ static description = "Run a loopback HTTP daemon that exposes ECP invoke for local Ollama " +
10
+ "(CORS + Private Network Access). Opens the browser demo with ?token= for pairing.";
11
+ static examples = [
12
+ "<%= config.bin %> <%= command.id %>",
13
+ "<%= config.bin %> <%= command.id %> --open-url http://localhost:5173/",
14
+ "<%= config.bin %> <%= command.id %> --no-open",
15
+ ];
16
+ static flags = {
17
+ port: Flags.integer({
18
+ description: "Listen port (ECP leet default 3090)",
19
+ default: DEFAULT_ECP_UP_PORT,
20
+ }),
21
+ host: Flags.string({
22
+ description: "Bind host (loopback only by default)",
23
+ default: DEFAULT_ECP_UP_HOST,
24
+ }),
25
+ "ollama-url": Flags.string({
26
+ description: "Ollama HTTP API base URL",
27
+ default: DEFAULT_OLLAMA_URL,
28
+ }),
29
+ "cors-origin": Flags.string({
30
+ description: "Extra allowed CORS origin (repeatable)",
31
+ multiple: true,
32
+ default: [],
33
+ }),
34
+ token: Flags.string({
35
+ description: "Pairing token for /v1/invoke (auto-generated if omitted)",
36
+ }),
37
+ "open-url": Flags.string({
38
+ description: "Browser demo URL to open with ?token= (pairing)",
39
+ default: DEFAULT_DEMO_OPEN_URL,
40
+ }),
41
+ open: Flags.boolean({
42
+ description: "Open the browser demo after the daemon starts",
43
+ allowNo: true,
44
+ default: true,
45
+ }),
46
+ };
47
+ async run() {
48
+ const { flags } = await this.parse(Up);
49
+ const token = flags.token ?? randomUUID();
50
+ const openUrl = flags["open-url"];
51
+ const openOrigin = originFromUrl(openUrl);
52
+ const corsOrigins = [
53
+ ...flags["cors-origin"],
54
+ ...(openOrigin ? [openOrigin] : []),
55
+ ];
56
+ const daemon = await startEcpUpDaemon({
57
+ port: flags.port,
58
+ host: flags.host,
59
+ ollamaUrl: flags["ollama-url"],
60
+ corsOrigins,
61
+ token,
62
+ });
63
+ const bridgeBaseURL = `http://${daemon.host}:${daemon.port}`;
64
+ const demoUrl = buildDemoOpenUrl(openUrl, {
65
+ token: daemon.token,
66
+ bridgeBaseURL,
67
+ });
68
+ this.log(`ECP daemon on ${bridgeBaseURL}`);
69
+ this.log(`Pairing token: ${daemon.token}`);
70
+ this.log("GET /health (no auth) · POST /v1/invoke (Bearer token)");
71
+ if (flags.open) {
72
+ this.log(`Opening ${demoUrl}`);
73
+ try {
74
+ openBrowserUrl(demoUrl);
75
+ }
76
+ catch (err) {
77
+ const message = err instanceof Error ? err.message : String(err);
78
+ this.warn(`Could not open browser: ${message}`);
79
+ this.log(`Open manually: ${demoUrl}`);
80
+ }
81
+ }
82
+ else {
83
+ this.log(`Demo URL: ${demoUrl}`);
84
+ }
85
+ this.log("Press Ctrl+C to stop.");
86
+ await new Promise((resolve) => {
87
+ const shutdown = () => {
88
+ void daemon.close().finally(() => resolve());
89
+ };
90
+ process.once("SIGINT", shutdown);
91
+ process.once("SIGTERM", shutdown);
92
+ });
93
+ }
94
+ }
95
+ //# sourceMappingURL=up.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"up.js","sourceRoot":"","sources":["../../src/commands/up.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /** Default loopback port for `ecp up` (ECP leet: E=3, C=0, P=9). @category CLI */
2
+ export declare const DEFAULT_ECP_UP_PORT = 3090;
3
+ /** Default bind host (loopback only). @category CLI */
4
+ export declare const DEFAULT_ECP_UP_HOST = "127.0.0.1";
5
+ /** Default Ollama HTTP API base URL. @category CLI */
6
+ export declare const DEFAULT_OLLAMA_URL = "http://127.0.0.1:11434";
7
+ /** Default browser demo URL opened by `ecp up`. @category CLI */
8
+ export declare const DEFAULT_DEMO_OPEN_URL = "https://executioncontrolprotocol.github.io/browser-demo/";
9
+ /** Built-in CORS origins for local Vite + hosted demo. @category CLI */
10
+ export declare const DEFAULT_CORS_ORIGINS: readonly string[];
11
+ /** JSON MIME type for daemon responses. @category CLI */
12
+ export declare const JSON_MIME = "application/json";
13
+ /** Daemon package version string for /health (from `@executioncontrolprotocol/cli` package.json). @category CLI */
14
+ export declare const ECP_UP_VERSION: string;
15
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/lib/up/constants.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,OAAO,CAAA;AAEvC,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,cAAc,CAAA;AAE9C,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,2BAA2B,CAAA;AAE1D,iEAAiE;AACjE,eAAO,MAAM,qBAAqB,6DAA6D,CAAA;AAE/F,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAIjD,CAAA;AAED,yDAAyD;AACzD,eAAO,MAAM,SAAS,qBAAqB,CAAA;AAI3C,mHAAmH;AACnH,eAAO,MAAM,cAAc,QAAoE,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { createRequire } from "node:module";
2
+ /** Default loopback port for `ecp up` (ECP leet: E=3, C=0, P=9). @category CLI */
3
+ export const DEFAULT_ECP_UP_PORT = 3090;
4
+ /** Default bind host (loopback only). @category CLI */
5
+ export const DEFAULT_ECP_UP_HOST = "127.0.0.1";
6
+ /** Default Ollama HTTP API base URL. @category CLI */
7
+ export const DEFAULT_OLLAMA_URL = "http://127.0.0.1:11434";
8
+ /** Default browser demo URL opened by `ecp up`. @category CLI */
9
+ export const DEFAULT_DEMO_OPEN_URL = "https://executioncontrolprotocol.github.io/browser-demo/";
10
+ /** Built-in CORS origins for local Vite + hosted demo. @category CLI */
11
+ export const DEFAULT_CORS_ORIGINS = [
12
+ "http://localhost:5173",
13
+ "http://127.0.0.1:5173",
14
+ "https://executioncontrolprotocol.github.io",
15
+ ];
16
+ /** JSON MIME type for daemon responses. @category CLI */
17
+ export const JSON_MIME = "application/json";
18
+ const require = createRequire(import.meta.url);
19
+ /** Daemon package version string for /health (from `@executioncontrolprotocol/cli` package.json). @category CLI */
20
+ export const ECP_UP_VERSION = require("../../../package.json").version;
21
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/lib/up/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,kFAAkF;AAClF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAA;AAEvC,uDAAuD;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAA;AAE9C,sDAAsD;AACtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAAwB,CAAA;AAE1D,iEAAiE;AACjE,MAAM,CAAC,MAAM,qBAAqB,GAAG,0DAA0D,CAAA;AAE/F,wEAAwE;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAsB;IACrD,uBAAuB;IACvB,uBAAuB;IACvB,4CAA4C;CAC7C,CAAA;AAED,yDAAyD;AACzD,MAAM,CAAC,MAAM,SAAS,GAAG,kBAAkB,CAAA;AAE3C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C,mHAAmH;AACnH,MAAM,CAAC,MAAM,cAAc,GAAI,OAAO,CAAC,uBAAuB,CAAyB,CAAC,OAAO,CAAA"}
@@ -0,0 +1,12 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ /**
3
+ * Apply CORS + Private Network Access headers for Chromium loopback access.
4
+ * @category CLI
5
+ */
6
+ export declare function setCorsAndPna(req: IncomingMessage, res: ServerResponse, allowOrigins: ReadonlySet<string>): void;
7
+ /**
8
+ * Extract Bearer token from Authorization header.
9
+ * @category CLI
10
+ */
11
+ export declare function readBearerToken(req: IncomingMessage): string | undefined;
12
+ //# sourceMappingURL=cors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/lib/up/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,IAAI,CAWN;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAKxE"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Apply CORS + Private Network Access headers for Chromium loopback access.
3
+ * @category CLI
4
+ */
5
+ export function setCorsAndPna(req, res, allowOrigins) {
6
+ const origin = req.headers.origin;
7
+ if (typeof origin === "string" && allowOrigins.has(origin)) {
8
+ res.setHeader("Access-Control-Allow-Origin", origin);
9
+ res.setHeader("Vary", "Origin");
10
+ }
11
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
12
+ res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
13
+ if (req.headers["access-control-request-private-network"] === "true") {
14
+ res.setHeader("Access-Control-Allow-Private-Network", "true");
15
+ }
16
+ }
17
+ /**
18
+ * Extract Bearer token from Authorization header.
19
+ * @category CLI
20
+ */
21
+ export function readBearerToken(req) {
22
+ const header = req.headers.authorization;
23
+ if (typeof header !== "string")
24
+ return undefined;
25
+ const match = /^Bearer\s+(.+)$/i.exec(header.trim());
26
+ return match?.[1]?.trim() || undefined;
27
+ }
28
+ //# sourceMappingURL=cors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cors.js","sourceRoot":"","sources":["../../../src/lib/up/cors.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAoB,EACpB,GAAmB,EACnB,YAAiC;IAEjC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAA;IACjC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAA;QACpD,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACjC,CAAC;IACD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,oBAAoB,CAAC,CAAA;IACnE,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,6BAA6B,CAAC,CAAA;IAC5E,IAAI,GAAG,CAAC,OAAO,CAAC,wCAAwC,CAAC,KAAK,MAAM,EAAE,CAAC;QACrE,GAAG,CAAC,SAAS,CAAC,sCAAsC,EAAE,MAAM,CAAC,CAAA;IAC/D,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAoB;IAClD,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAA;IACxC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAChD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACpD,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS,CAAA;AACxC,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type Server } from "node:http";
2
+ import type { Ecp } from "@executioncontrolprotocol/core";
3
+ import "@executioncontrolprotocol/extension-ollama";
4
+ /** Options for {@link startEcpUpDaemon}. @category CLI */
5
+ export interface StartEcpUpDaemonOptions {
6
+ /** Listen port (default 3090). */
7
+ port?: number;
8
+ /** Bind host (default 127.0.0.1). */
9
+ host?: string;
10
+ /** Ollama API base URL. */
11
+ ollamaUrl?: string;
12
+ /** Extra CORS origins (merged with Vite defaults). */
13
+ corsOrigins?: string[];
14
+ /** Pairing token for `/v1/invoke`. */
15
+ token: string;
16
+ }
17
+ /** Result of starting the daemon. @category CLI */
18
+ export interface EcpUpDaemon {
19
+ /** Listening HTTP server. */
20
+ server: Server;
21
+ /** Operational ECP instance. */
22
+ ecp: Ecp;
23
+ /** Resolved listen port. */
24
+ port: number;
25
+ /** Resolved bind host. */
26
+ host: string;
27
+ /** Pairing token. */
28
+ token: string;
29
+ /** Close the server and terminate ECP. */
30
+ close: () => Promise<void>;
31
+ }
32
+ /**
33
+ * Start the local ECP daemon (`ecp up`).
34
+ * @category CLI
35
+ */
36
+ export declare function startEcpUpDaemon(options: StartEcpUpDaemonOptions): Promise<EcpUpDaemon>;
37
+ //# sourceMappingURL=create-daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-daemon.d.ts","sourceRoot":"","sources":["../../../src/lib/up/create-daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAA;AAErD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAA;AACzD,OAAO,4CAA4C,CAAA;AAUnD,0DAA0D;AAC1D,MAAM,WAAW,uBAAuB;IACtC,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAA;CACd;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,gCAAgC;IAChC,GAAG,EAAE,GAAG,CAAA;IACR,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,WAAW,CAAC,CA0DtB"}
@@ -0,0 +1,63 @@
1
+ import { createServer } from "node:http";
2
+ import { environment, extension } from "@executioncontrolprotocol/node";
3
+ import "@executioncontrolprotocol/extension-ollama";
4
+ import { registerOllamaExtension } from "@executioncontrolprotocol/extension-ollama";
5
+ import { DEFAULT_CORS_ORIGINS, DEFAULT_ECP_UP_HOST, DEFAULT_ECP_UP_PORT, DEFAULT_OLLAMA_URL, } from "./constants.js";
6
+ import { createUpRequestHandler } from "./handle-request.js";
7
+ /**
8
+ * Start the local ECP daemon (`ecp up`).
9
+ * @category CLI
10
+ */
11
+ export async function startEcpUpDaemon(options) {
12
+ const port = options.port ?? DEFAULT_ECP_UP_PORT;
13
+ const host = options.host ?? DEFAULT_ECP_UP_HOST;
14
+ const ollamaUrl = options.ollamaUrl ?? DEFAULT_OLLAMA_URL;
15
+ const token = options.token;
16
+ await registerOllamaExtension();
17
+ const env = (await environment("ecp-up")).withExtensions([
18
+ extension("@executioncontrolprotocol/ollama").with({
19
+ baseURL: ollamaUrl,
20
+ }),
21
+ ]);
22
+ const ecp = await env.init();
23
+ const allowOrigins = new Set([
24
+ ...DEFAULT_CORS_ORIGINS,
25
+ ...(options.corsOrigins ?? []),
26
+ ]);
27
+ const handler = createUpRequestHandler({
28
+ ecp,
29
+ allowOrigins,
30
+ token,
31
+ ollamaUrl,
32
+ });
33
+ const server = createServer((req, res) => {
34
+ void handler(req, res).catch((err) => {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+ if (!res.headersSent) {
37
+ res.writeHead(500, { "Content-Type": "application/json" });
38
+ }
39
+ res.end(JSON.stringify({ error: message }));
40
+ });
41
+ });
42
+ await new Promise((resolve, reject) => {
43
+ server.once("error", reject);
44
+ server.listen(port, host, () => {
45
+ server.off("error", reject);
46
+ resolve();
47
+ });
48
+ });
49
+ return {
50
+ server,
51
+ ecp,
52
+ port,
53
+ host,
54
+ token,
55
+ close: async () => {
56
+ await new Promise((resolve, reject) => {
57
+ server.close((err) => (err ? reject(err) : resolve()));
58
+ });
59
+ await ecp.terminate();
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=create-daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-daemon.js","sourceRoot":"","sources":["../../../src/lib/up/create-daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { Ecp } from "@executioncontrolprotocol/core";
3
+ /** Dependencies for {@link createUpRequestHandler}. @category CLI */
4
+ export interface UpRequestHandlerOptions {
5
+ /** Operational ECP instance. */
6
+ ecp: Ecp;
7
+ /** Allowlisted CORS origins. */
8
+ allowOrigins: ReadonlySet<string>;
9
+ /** Pairing token for `/v1/*`. */
10
+ token: string;
11
+ /** Ollama base URL used for `/health` reachability. */
12
+ ollamaUrl: string;
13
+ }
14
+ /**
15
+ * Create the HTTP request handler for `ecp up`.
16
+ * @category CLI
17
+ */
18
+ export declare function createUpRequestHandler(options: UpRequestHandlerOptions): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
19
+ //# sourceMappingURL=handle-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle-request.d.ts","sourceRoot":"","sources":["../../../src/lib/up/handle-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAA;AAMzD,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACtC,gCAAgC;IAChC,GAAG,EAAE,GAAG,CAAA;IACR,gCAAgC;IAChC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAA;CAClB;AAQD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,IAInE,KAAK,eAAe,EACpB,KAAK,cAAc,KAClB,OAAO,CAAC,IAAI,CAAC,CA2DjB"}
@@ -0,0 +1,74 @@
1
+ import { listOllamaModels } from "@executioncontrolprotocol/extension-ollama";
2
+ import { ECP_UP_VERSION, JSON_MIME } from "./constants.js";
3
+ import { readBearerToken, setCorsAndPna } from "./cors.js";
4
+ import { readRequestBody } from "./read-body.js";
5
+ /**
6
+ * Create the HTTP request handler for `ecp up`.
7
+ * @category CLI
8
+ */
9
+ export function createUpRequestHandler(options) {
10
+ const { ecp, allowOrigins, token, ollamaUrl } = options;
11
+ return async function handleUpRequest(req, res) {
12
+ setCorsAndPna(req, res, allowOrigins);
13
+ if (req.method === "OPTIONS") {
14
+ res.writeHead(204);
15
+ res.end();
16
+ return;
17
+ }
18
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "127.0.0.1"}`);
19
+ const pathname = url.pathname;
20
+ if (req.method === "GET" && pathname === "/health") {
21
+ let ollamaReachable = false;
22
+ try {
23
+ await listOllamaModels(ollamaUrl, { signal: AbortSignal.timeout(2000) });
24
+ ollamaReachable = true;
25
+ }
26
+ catch {
27
+ ollamaReachable = false;
28
+ }
29
+ writeJson(res, 200, {
30
+ ok: true,
31
+ version: ECP_UP_VERSION,
32
+ ollamaReachable,
33
+ });
34
+ return;
35
+ }
36
+ if (req.method === "POST" && pathname === "/v1/invoke") {
37
+ const presented = readBearerToken(req);
38
+ if (!presented || presented !== token) {
39
+ writeJson(res, 401, { error: "Unauthorized" });
40
+ return;
41
+ }
42
+ let body;
43
+ try {
44
+ const raw = await readRequestBody(req);
45
+ body = raw.trim() ? JSON.parse(raw) : {};
46
+ }
47
+ catch {
48
+ writeJson(res, 400, { error: "Invalid JSON body" });
49
+ return;
50
+ }
51
+ if (typeof body.capability !== "string" || !body.capability.trim()) {
52
+ writeJson(res, 400, { error: "capability is required" });
53
+ return;
54
+ }
55
+ try {
56
+ const builder = ecp.invoke(body.capability).with(body.input ?? {});
57
+ const result = await (body.provider ? builder.uses(body.provider) : builder).process();
58
+ writeJson(res, 200, result);
59
+ }
60
+ catch (err) {
61
+ const message = err instanceof Error ? err.message : String(err);
62
+ writeJson(res, 500, { error: message });
63
+ }
64
+ return;
65
+ }
66
+ writeJson(res, 404, { error: "Not found" });
67
+ };
68
+ }
69
+ function writeJson(res, status, body) {
70
+ const payload = JSON.stringify(body);
71
+ res.writeHead(status, { "Content-Type": JSON_MIME });
72
+ res.end(payload);
73
+ }
74
+ //# sourceMappingURL=handle-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle-request.js","sourceRoot":"","sources":["../../../src/lib/up/handle-request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Open a URL in the default browser (best-effort, fire-and-forget).
3
+ * @category CLI
4
+ */
5
+ export declare function openBrowserUrl(url: string): void;
6
+ /**
7
+ * Build the demo open URL with pairing token (and optional bridge base).
8
+ * @category CLI
9
+ */
10
+ export declare function buildDemoOpenUrl(openUrl: string, options: {
11
+ token: string;
12
+ bridgeBaseURL?: string;
13
+ }): string;
14
+ /**
15
+ * Origin (scheme + host + port) for CORS allowlisting from an open URL.
16
+ * @category CLI
17
+ */
18
+ export declare function originFromUrl(raw: string): string | undefined;
19
+ //# sourceMappingURL=open-browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../../../src/lib/up/open-browser.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAWhD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,MAAM,CAOR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO7D"}
@@ -0,0 +1,43 @@
1
+ import { spawn } from "node:child_process";
2
+ /**
3
+ * Open a URL in the default browser (best-effort, fire-and-forget).
4
+ * @category CLI
5
+ */
6
+ export function openBrowserUrl(url) {
7
+ const platform = process.platform;
8
+ if (platform === "win32") {
9
+ spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
10
+ return;
11
+ }
12
+ if (platform === "darwin") {
13
+ spawn("open", [url], { detached: true, stdio: "ignore" }).unref();
14
+ return;
15
+ }
16
+ spawn("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
17
+ }
18
+ /**
19
+ * Build the demo open URL with pairing token (and optional bridge base).
20
+ * @category CLI
21
+ */
22
+ export function buildDemoOpenUrl(openUrl, options) {
23
+ const url = new URL(openUrl);
24
+ url.searchParams.set("token", options.token);
25
+ if (options.bridgeBaseURL) {
26
+ url.searchParams.set("bridge", options.bridgeBaseURL);
27
+ }
28
+ return url.toString();
29
+ }
30
+ /**
31
+ * Origin (scheme + host + port) for CORS allowlisting from an open URL.
32
+ * @category CLI
33
+ */
34
+ export function originFromUrl(raw) {
35
+ try {
36
+ const url = new URL(raw);
37
+ return url.origin;
38
+ }
39
+ catch {
40
+ return undefined;
41
+ }
42
+ }
43
+ //# sourceMappingURL=open-browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-browser.js","sourceRoot":"","sources":["../../../src/lib/up/open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;IACjC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,CAAA;QACnF,OAAM;IACR,CAAC;IACD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,CAAA;QACjE,OAAM;IACR,CAAC;IACD,KAAK,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,CAAA;AACvE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,OAAkD;IAElD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;IAC5B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAC5C,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QACxB,OAAO,GAAG,CAAC,MAAM,CAAA;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { IncomingMessage } from "node:http";
2
+ /**
3
+ * Read the full request body as UTF-8 text.
4
+ * @category CLI
5
+ */
6
+ export declare function readRequestBody(req: IncomingMessage): Promise<string>;
7
+ //# sourceMappingURL=read-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-body.d.ts","sourceRoot":"","sources":["../../../src/lib/up/read-body.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAM3E"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Read the full request body as UTF-8 text.
3
+ * @category CLI
4
+ */
5
+ export async function readRequestBody(req) {
6
+ const chunks = [];
7
+ for await (const chunk of req) {
8
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
9
+ }
10
+ return Buffer.concat(chunks).toString("utf8");
11
+ }
12
+ //# sourceMappingURL=read-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-body.js","sourceRoot":"","sources":["../../../src/lib/up/read-body.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAoB;IACxD,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IACrE,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AAC/C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@executioncontrolprotocol/cli",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "ECP command-line interface",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -19,18 +19,19 @@
19
19
  "start": "node ./bin/dev.js"
20
20
  },
21
21
  "peerDependencies": {
22
- "@executioncontrolprotocol/core": "^0.10.0",
23
- "@executioncontrolprotocol/types": "^0.10.0",
22
+ "@executioncontrolprotocol/core": "^0.10.1",
23
+ "@executioncontrolprotocol/types": "^0.10.1",
24
24
  "zod": "^3.24.0"
25
25
  },
26
26
  "dependencies": {
27
- "@executioncontrolprotocol/node": "0.10.0",
28
- "@executioncontrolprotocol/secrets": "0.10.0",
27
+ "@executioncontrolprotocol/node": "0.10.1",
28
+ "@executioncontrolprotocol/secrets": "0.10.1",
29
+ "@executioncontrolprotocol/extension-ollama": "0.10.1",
29
30
  "@oclif/core": "^4.10.0"
30
31
  },
31
32
  "devDependencies": {
32
- "@executioncontrolprotocol/core": "0.10.0",
33
- "@executioncontrolprotocol/types": "0.10.0",
33
+ "@executioncontrolprotocol/core": "0.10.1",
34
+ "@executioncontrolprotocol/types": "0.10.1",
34
35
  "typescript": "^5.7.0",
35
36
  "zod": "^3.24.0"
36
37
  },