@spark-strategic/helix-core 0.1.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.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @spark-strategic/helix-core
2
+
3
+ The local companion you run to connect a **personal agent** to Helix — and the
4
+ home for future local Helix tooling (e.g. an MCP server). Thin by design: it owns
5
+ a WebSocket to `agents-api` and the chat-turn protocol, and delegates the actual
6
+ answering to a pluggable **adapter**.
7
+
8
+ > This package is intended to be installed from public npm and run wherever your
9
+ > agent lives. It never needs to run inside the Helix cloud.
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ npm i -g @spark-strategic/helix-core
15
+
16
+ export HELIX_CONNECTOR_SLUG=my-agent # PersonalAgent.slug
17
+ export HELIX_CONNECTOR_KEY=… # connector shared secret
18
+ helix-core connect
19
+ ```
20
+
21
+ Config resolves from (low → high precedence): built-in defaults, a `connector`
22
+ block in `~/.helix/config.json`, environment variables, then CLI flags.
23
+
24
+ | Setting | Env | Flag |
25
+ |---|---|---|
26
+ | agents-api base URL | `HELIX_AGENTS_API_URL` | `--url` |
27
+ | PersonalAgent slug | `HELIX_CONNECTOR_SLUG` | `--slug` |
28
+ | Connector key | `HELIX_CONNECTOR_KEY` | `--key` |
29
+
30
+ ## Bring your own agent
31
+
32
+ The built-in `echo` adapter just streams your message back. To connect a real
33
+ agent, embed the library and pass your own `AgentAdapter`:
34
+
35
+ ```ts
36
+ import { Connector, resolveConfig, packageInfo, type AgentAdapter } from '@spark-strategic/helix-core';
37
+
38
+ const myAdapter: AgentAdapter = {
39
+ name: 'my-agent',
40
+ version: '1.0.0',
41
+ async runTurn(ctx, emit) {
42
+ emit.status('thinking');
43
+ for await (const chunk of myModel(ctx.message, { signal: ctx.signal })) {
44
+ emit.delta(chunk);
45
+ }
46
+ return { costUsd: 0 }; // optional attribution for the Helix ledger
47
+ },
48
+ };
49
+
50
+ new Connector({ config: resolveConfig(), adapter: myAdapter, client: packageInfo() }).start();
51
+ ```
52
+
53
+ - Resolve `runTurn` = the turn succeeded (a `chat_done` is emitted for you).
54
+ - Throw = it failed (a `chat_error` is emitted with the message).
55
+ - Honor `ctx.signal` — it aborts when the user hits stop or the socket drops.
56
+
57
+ ## Compatibility
58
+
59
+ The client and the Helix server version independently, so the wire protocol is
60
+ deliberately conservative:
61
+
62
+ - **`PROTOCOL_VERSION`** is bumped only on a *breaking* wire change. Additive
63
+ features are announced via `capabilities` on the connect handshake instead —
64
+ so an old connector and a new server (or vice-versa) still interoperate.
65
+ - **Unknown inbound frames are ignored**, never fatal. Helix can add new frames
66
+ without breaking older connectors.
67
+ - On connect the client sends a `connector_hello` with its package version, which
68
+ Helix records so it can nudge you when a newer release is available. You are
69
+ never *forced* to update unless a minimum version is enforced server-side.
70
+
71
+ ## Extending
72
+
73
+ Additional package tools are added as **new CLI subcommands** (beside `connect`),
74
+ not by overloading the adapter interface. The adapter is only the personal-agent
75
+ chat seam.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Agent adapter contract — the seam between the Helix transport and whatever
3
+ * actually answers a chat turn (an OpenClaw instance, a local LLM, a canned
4
+ * bot…). The connector owns the socket and the frame protocol; an adapter only
5
+ * has to turn a turn's text into streamed output. This keeps the transport
6
+ * reusable and adapters trivially unit-testable with no socket.
7
+ *
8
+ * Extensibility note: adapters are the intended extension point for personal
9
+ * agents. Additional package tools (e.g. a future Helix MCP server) are separate
10
+ * CLI subcommands, NOT adapters — see cli.ts. Do not overload this interface with
11
+ * tool-hosting concerns.
12
+ */
13
+ /** Everything an adapter is handed for one turn. `raw` carries the full inbound
14
+ * payload for adapters that want fields beyond the typed subset — but treat the
15
+ * typed fields as the stable contract; anything in `raw` may change. */
16
+ export interface TurnContext {
17
+ sessionId: string;
18
+ message: string;
19
+ agentId?: string;
20
+ model?: string;
21
+ providerRef?: string;
22
+ user?: {
23
+ id?: string;
24
+ email?: string;
25
+ name?: string;
26
+ };
27
+ org?: string;
28
+ agentName?: string;
29
+ designatedContact?: string;
30
+ scopeDirective?: string;
31
+ /** Full inbound `chat_message` payload, verbatim. Forward-compat escape hatch. */
32
+ raw: Record<string, unknown>;
33
+ /** Aborts when Helix sends `chat_close` (user hit stop) or the socket drops.
34
+ * A well-behaved adapter should stop streaming promptly when this fires. */
35
+ signal: AbortSignal;
36
+ }
37
+ /** How an adapter streams a turn back. The connector maps these to wire frames;
38
+ * it emits the terminal `chat_done`/`chat_error` itself (from runTurn resolving
39
+ * or throwing), so an adapter never touches transport. */
40
+ export interface TurnEmitter {
41
+ /** Append streamed assistant text. */
42
+ delta(text: string): void;
43
+ /** Surface a tool-use chip (cosmetic; the label is shown to the user as-is). */
44
+ tool(name: string, phase: 'call' | 'result', ok?: boolean): void;
45
+ /** Update the live "processing…" status line (optional, coalesced by the UI). */
46
+ status(text: string): void;
47
+ }
48
+ /** Optional attribution an adapter can return so Helix can ledger the turn. */
49
+ export interface TurnResult {
50
+ providerRef?: string;
51
+ model?: string;
52
+ costUsd?: number | null;
53
+ usage?: unknown;
54
+ durationMs?: number;
55
+ }
56
+ export interface AgentAdapter {
57
+ /** Stable adapter id, reported in the hello frame (e.g. 'echo', 'openclaw'). */
58
+ readonly name: string;
59
+ /** Adapter semver, reported in the hello frame. */
60
+ readonly version: string;
61
+ /** Extra capability flags this adapter contributes to the hello frame. */
62
+ readonly capabilities?: string[];
63
+ /** Run one turn. Resolve = the turn succeeded (connector emits chat_done);
64
+ * throw = it failed (connector emits chat_error). Honor `ctx.signal`. */
65
+ runTurn(ctx: TurnContext, emit: TurnEmitter): Promise<TurnResult | void>;
66
+ }
67
+ /**
68
+ * Reference adapter: streams the user's own message back a few characters at a
69
+ * time. Exists so the connector is runnable end-to-end out of the box and as the
70
+ * copy-paste starting point for a real adapter. Not meant for production use.
71
+ */
72
+ export declare class EchoAdapter implements AgentAdapter {
73
+ readonly name = "echo";
74
+ readonly version = "0.1.0";
75
+ readonly capabilities: never[];
76
+ runTurn(ctx: TurnContext, emit: TurnEmitter): Promise<TurnResult>;
77
+ }
78
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;yEAEyE;AACzE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B;iFAC6E;IAC7E,MAAM,EAAE,WAAW,CAAC;CACrB;AAED;;2DAE2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gFAAgF;IAChF,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACjE,iFAAiF;IACjF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC;8EAC0E;IAC1E,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;CAC1E;AAED;;;;GAIG;AACH,qBAAa,WAAY,YAAW,YAAY;IAC9C,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,YAAY,UAAM;IAErB,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;CAWxE"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /**
3
+ * Agent adapter contract — the seam between the Helix transport and whatever
4
+ * actually answers a chat turn (an OpenClaw instance, a local LLM, a canned
5
+ * bot…). The connector owns the socket and the frame protocol; an adapter only
6
+ * has to turn a turn's text into streamed output. This keeps the transport
7
+ * reusable and adapters trivially unit-testable with no socket.
8
+ *
9
+ * Extensibility note: adapters are the intended extension point for personal
10
+ * agents. Additional package tools (e.g. a future Helix MCP server) are separate
11
+ * CLI subcommands, NOT adapters — see cli.ts. Do not overload this interface with
12
+ * tool-hosting concerns.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.EchoAdapter = void 0;
16
+ /**
17
+ * Reference adapter: streams the user's own message back a few characters at a
18
+ * time. Exists so the connector is runnable end-to-end out of the box and as the
19
+ * copy-paste starting point for a real adapter. Not meant for production use.
20
+ */
21
+ class EchoAdapter {
22
+ name = 'echo';
23
+ version = '0.1.0';
24
+ capabilities = [];
25
+ async runTurn(ctx, emit) {
26
+ const started = Date.now();
27
+ const reply = `You said: ${ctx.message}`;
28
+ emit.status('echoing');
29
+ for (let i = 0; i < reply.length; i += 8) {
30
+ if (ctx.signal.aborted)
31
+ throw new Error('aborted');
32
+ emit.delta(reply.slice(i, i + 8));
33
+ await sleep(20);
34
+ }
35
+ return { costUsd: 0, durationMs: Date.now() - started };
36
+ }
37
+ }
38
+ exports.EchoAdapter = EchoAdapter;
39
+ function sleep(ms) {
40
+ return new Promise((resolve) => setTimeout(resolve, ms));
41
+ }
42
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAwDH;;;;GAIG;AACH,MAAa,WAAW;IACb,IAAI,GAAG,MAAM,CAAC;IACd,OAAO,GAAG,OAAO,CAAC;IAClB,YAAY,GAAG,EAAE,CAAC;IAE3B,KAAK,CAAC,OAAO,CAAC,GAAgB,EAAE,IAAiB;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,aAAa,GAAG,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;YACnD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IAC1D,CAAC;CACF;AAhBD,kCAgBC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `helix-core` CLI.
4
+ *
5
+ * Structured as a subcommand registry from day one so future package tools (e.g.
6
+ * a `helix-core mcp` server) slot in beside `connect` without reshaping the
7
+ * entrypoint. Each command is a small handler; unknown commands print help.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
package/dist/cli.js ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * `helix-core` CLI.
5
+ *
6
+ * Structured as a subcommand registry from day one so future package tools (e.g.
7
+ * a `helix-core mcp` server) slot in beside `connect` without reshaping the
8
+ * entrypoint. Each command is a small handler; unknown commands print help.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ const config_1 = require("./config");
12
+ const connector_1 = require("./connector");
13
+ const adapter_1 = require("./adapter");
14
+ const version_1 = require("./version");
15
+ /** Minimal flag parser: `--key value`, `--key=value`, and boolean `--flag`. */
16
+ function parseArgs(argv) {
17
+ const [command = 'connect', ...rest] = argv;
18
+ const flags = {};
19
+ for (let i = 0; i < rest.length; i += 1) {
20
+ const arg = rest[i];
21
+ if (!arg.startsWith('--'))
22
+ continue;
23
+ const body = arg.slice(2);
24
+ const eq = body.indexOf('=');
25
+ if (eq >= 0) {
26
+ flags[body.slice(0, eq)] = body.slice(eq + 1);
27
+ }
28
+ else if (i + 1 < rest.length && !rest[i + 1].startsWith('--')) {
29
+ flags[body] = rest[i + 1];
30
+ i += 1;
31
+ }
32
+ else {
33
+ flags[body] = true;
34
+ }
35
+ }
36
+ return { command, flags };
37
+ }
38
+ function overridesFromFlags(flags) {
39
+ const o = {};
40
+ if (typeof flags.url === 'string')
41
+ o.agentsApiUrl = flags.url;
42
+ if (typeof flags.slug === 'string')
43
+ o.slug = flags.slug;
44
+ if (typeof flags.key === 'string')
45
+ o.key = flags.key;
46
+ return o;
47
+ }
48
+ /** Resolve the adapter named by `--adapter`. Only the reference echo adapter ships
49
+ * in-box; real adapters are expected to embed the library, not go through the CLI.
50
+ * Kept as a lookup so additional built-ins can be registered later. */
51
+ function selectAdapter(name) {
52
+ switch ((name ?? 'echo').toLowerCase()) {
53
+ case 'echo':
54
+ return new adapter_1.EchoAdapter();
55
+ default:
56
+ throw new Error(`Unknown adapter "${name}". Built-in: echo. For a custom agent, embed the library and pass your own AgentAdapter.`);
57
+ }
58
+ }
59
+ function runConnect(flags) {
60
+ const config = (0, config_1.resolveConfig)(overridesFromFlags(flags));
61
+ const adapter = selectAdapter(typeof flags.adapter === 'string' ? flags.adapter : undefined);
62
+ const client = (0, version_1.packageInfo)();
63
+ const connector = new connector_1.Connector({ config, adapter, client });
64
+ connector.start();
65
+ const shutdown = (signal) => {
66
+ console.log(`\n[helix-core] ${signal} — shutting down`);
67
+ connector.stop();
68
+ process.exit(0);
69
+ };
70
+ process.on('SIGINT', () => shutdown('SIGINT'));
71
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
72
+ }
73
+ const HELP = `helix-core — connect a personal agent (and future tools) to Helix
74
+
75
+ Usage:
76
+ helix-core connect [options] Attach as a PersonalAgent and service chat turns
77
+ helix-core version Print package + protocol version
78
+ helix-core help Show this help
79
+
80
+ Options (connect):
81
+ --url <url> agents-api base URL (default: https://agents-api.spark-strategic.ai)
82
+ --slug <slug> PersonalAgent slug to attach as
83
+ --key <key> Connector shared secret
84
+ --adapter <name> Built-in adapter to run (default: echo)
85
+
86
+ Config also resolves from ~/.helix/config.json (a "connector" block) and env:
87
+ HELIX_AGENTS_API_URL, HELIX_CONNECTOR_SLUG, HELIX_CONNECTOR_KEY
88
+ `;
89
+ function main() {
90
+ const { command, flags } = parseArgs(process.argv.slice(2));
91
+ if (flags.help || command === 'help') {
92
+ console.log(HELP);
93
+ return;
94
+ }
95
+ if (flags.version || command === 'version') {
96
+ const info = (0, version_1.packageInfo)();
97
+ console.log(`${info.name} ${info.version} (protocol ${version_1.PROTOCOL_VERSION})`);
98
+ return;
99
+ }
100
+ switch (command) {
101
+ case 'connect':
102
+ try {
103
+ runConnect(flags);
104
+ }
105
+ catch (err) {
106
+ console.error(`[helix-core] ${err instanceof Error ? err.message : String(err)}`);
107
+ process.exit(1);
108
+ }
109
+ break;
110
+ default:
111
+ console.error(`[helix-core] unknown command "${command}"\n`);
112
+ console.log(HELP);
113
+ process.exit(1);
114
+ }
115
+ }
116
+ main();
117
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Connector configuration resolution. Precedence (low → high):
3
+ * built-in defaults < ~/.helix/config.json < environment < explicit opts
4
+ *
5
+ * Reuses the same `~/.helix/config.json` file and `HELIX_AGENTS_API_URL` env var
6
+ * as helix-os, so a box already running a Helix node needs only the two
7
+ * connector-specific secrets (slug + key) added.
8
+ */
9
+ export interface ConnectorConfig {
10
+ /** agents-api base URL (http/https; converted to ws/wss for the socket). */
11
+ agentsApiUrl: string;
12
+ /** PersonalAgent.slug — the routable handle this connector attaches as. */
13
+ slug: string;
14
+ /** Shared secret presented on the socket. Matches PersonalAgent.connectorKey. */
15
+ key: string;
16
+ }
17
+ export type ConfigOverrides = Partial<ConnectorConfig>;
18
+ /**
19
+ * Resolve the effective connector config. Throws a clear error if a required
20
+ * secret (slug/key) is missing after all sources are merged.
21
+ */
22
+ export declare function resolveConfig(overrides?: ConfigOverrides): ConnectorConfig;
23
+ /** Build the connector WebSocket URL from resolved config. */
24
+ export declare function connectorWsUrl(config: ConnectorConfig): string;
25
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;CACb;AAID,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAkBvD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,GAAE,eAAoB,GAAG,eAAe,CAiC9E;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAG9D"}
package/dist/config.js ADDED
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * Connector configuration resolution. Precedence (low → high):
4
+ * built-in defaults < ~/.helix/config.json < environment < explicit opts
5
+ *
6
+ * Reuses the same `~/.helix/config.json` file and `HELIX_AGENTS_API_URL` env var
7
+ * as helix-os, so a box already running a Helix node needs only the two
8
+ * connector-specific secrets (slug + key) added.
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.resolveConfig = resolveConfig;
45
+ exports.connectorWsUrl = connectorWsUrl;
46
+ const fs = __importStar(require("fs"));
47
+ const os = __importStar(require("os"));
48
+ const path = __importStar(require("path"));
49
+ const DEFAULT_AGENTS_API_URL = 'https://agents-api.spark-strategic.ai';
50
+ function readConfigFile() {
51
+ const configPath = path.join(os.homedir(), '.helix', 'config.json');
52
+ if (!fs.existsSync(configPath))
53
+ return {};
54
+ try {
55
+ const parsed = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
56
+ return parsed && typeof parsed === 'object' ? parsed : {};
57
+ }
58
+ catch (err) {
59
+ console.warn('helix-core: failed to parse ~/.helix/config.json:', err);
60
+ return {};
61
+ }
62
+ }
63
+ function str(v) {
64
+ return typeof v === 'string' && v.trim() ? v.trim() : undefined;
65
+ }
66
+ /**
67
+ * Resolve the effective connector config. Throws a clear error if a required
68
+ * secret (slug/key) is missing after all sources are merged.
69
+ */
70
+ function resolveConfig(overrides = {}) {
71
+ const file = readConfigFile();
72
+ // The connector reads its own block under `connector` in ~/.helix/config.json,
73
+ // falling back to top-level for the shared agentsApiUrl.
74
+ const fileConnector = (file.connector && typeof file.connector === 'object'
75
+ ? file.connector
76
+ : {});
77
+ const agentsApiUrl = overrides.agentsApiUrl ??
78
+ str(process.env.HELIX_AGENTS_API_URL) ??
79
+ str(fileConnector.agentsApiUrl) ??
80
+ str(file.agentsApiUrl) ??
81
+ DEFAULT_AGENTS_API_URL;
82
+ const slug = overrides.slug ??
83
+ str(process.env.HELIX_CONNECTOR_SLUG) ??
84
+ str(fileConnector.slug);
85
+ const key = overrides.key ??
86
+ str(process.env.HELIX_CONNECTOR_KEY) ??
87
+ str(fileConnector.key);
88
+ const missing = [];
89
+ if (!slug)
90
+ missing.push('slug (HELIX_CONNECTOR_SLUG or --slug)');
91
+ if (!key)
92
+ missing.push('key (HELIX_CONNECTOR_KEY or --key)');
93
+ if (missing.length) {
94
+ throw new Error(`helix-core: missing required config: ${missing.join(', ')}`);
95
+ }
96
+ return { agentsApiUrl, slug: slug, key: key };
97
+ }
98
+ /** Build the connector WebSocket URL from resolved config. */
99
+ function connectorWsUrl(config) {
100
+ const base = config.agentsApiUrl.replace(/^http/, 'ws').replace(/\/+$/, '');
101
+ return `${base}/ws/connector/${encodeURIComponent(config.slug)}?key=${encodeURIComponent(config.key)}`;
102
+ }
103
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The Helix local connector: a thin, long-lived WebSocket client that attaches a
3
+ * PersonalAgent to agents-api over `/ws/connector/:slug` and services chat turns
4
+ * by delegating to a pluggable {@link AgentAdapter}.
5
+ *
6
+ * Scope is deliberately narrow (cf. the much heavier helix-os node): no terminal,
7
+ * no logs, no workflow execution — just the chat_* turn lifecycle plus a hello
8
+ * handshake for version reporting. Everything agent-specific lives behind the
9
+ * adapter; everything transport-specific lives here.
10
+ */
11
+ import type { AgentAdapter } from './adapter';
12
+ import type { ConnectorConfig } from './config';
13
+ export interface Logger {
14
+ debug(msg: string, meta?: unknown): void;
15
+ info(msg: string, meta?: unknown): void;
16
+ warn(msg: string, meta?: unknown): void;
17
+ error(msg: string, meta?: unknown): void;
18
+ }
19
+ export interface ConnectorOptions {
20
+ config: ConnectorConfig;
21
+ adapter: AgentAdapter;
22
+ /** This package's identity, reported in the hello frame for update nudges. */
23
+ client: {
24
+ name: string;
25
+ version: string;
26
+ };
27
+ logger?: Logger;
28
+ pingIntervalMs?: number;
29
+ pongTimeoutMs?: number;
30
+ maxReconnectDelayMs?: number;
31
+ }
32
+ export declare class Connector {
33
+ private readonly opts;
34
+ private ws;
35
+ private stopped;
36
+ private reconnectAttempts;
37
+ private pingTimer;
38
+ private pongTimer;
39
+ /** In-flight turns, keyed by sessionId, so chat_close can abort the right one. */
40
+ private readonly turns;
41
+ private readonly log;
42
+ private readonly pingIntervalMs;
43
+ private readonly pongTimeoutMs;
44
+ private readonly maxReconnectDelayMs;
45
+ constructor(opts: ConnectorOptions);
46
+ /** Open the socket and keep it open (auto-reconnect) until {@link stop}. */
47
+ start(): void;
48
+ /** Close the socket for good and abort any in-flight turns. */
49
+ stop(): void;
50
+ private connect;
51
+ private scheduleReconnect;
52
+ private startPingLoop;
53
+ private onPong;
54
+ private clearTimers;
55
+ private sendHello;
56
+ private onMessage;
57
+ private onChatStart;
58
+ private onChatClose;
59
+ private onChatMessage;
60
+ private sendRaw;
61
+ }
62
+ //# sourceMappingURL=connector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAwC,MAAM,WAAW,CAAC;AACpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAehD,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC1C;AASD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,YAAY,CAAC;IACtB,8EAA8E;IAC9E,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAMD,qBAAa,SAAS;IAcR,OAAO,CAAC,QAAQ,CAAC,IAAI;IAbjC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,SAAS,CAA+B;IAChD,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsC;IAE5D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;gBAEhB,IAAI,EAAE,gBAAgB;IAOnD,4EAA4E;IAC5E,KAAK,IAAI,IAAI;IAKb,+DAA+D;IAC/D,IAAI,IAAI,IAAI;IAWZ,OAAO,CAAC,OAAO;IAqCf,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,SAAS;IAyBjB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,WAAW;YAML,aAAa;IAiD3B,OAAO,CAAC,OAAO;CAWhB"}
@@ -0,0 +1,295 @@
1
+ "use strict";
2
+ /**
3
+ * The Helix local connector: a thin, long-lived WebSocket client that attaches a
4
+ * PersonalAgent to agents-api over `/ws/connector/:slug` and services chat turns
5
+ * by delegating to a pluggable {@link AgentAdapter}.
6
+ *
7
+ * Scope is deliberately narrow (cf. the much heavier helix-os node): no terminal,
8
+ * no logs, no workflow execution — just the chat_* turn lifecycle plus a hello
9
+ * handshake for version reporting. Everything agent-specific lives behind the
10
+ * adapter; everything transport-specific lives here.
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ var __importDefault = (this && this.__importDefault) || function (mod) {
46
+ return (mod && mod.__esModule) ? mod : { "default": mod };
47
+ };
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.Connector = void 0;
50
+ const ws_1 = __importDefault(require("ws"));
51
+ const os = __importStar(require("os"));
52
+ const config_1 = require("./config");
53
+ const protocol_1 = require("./protocol");
54
+ const consoleLogger = {
55
+ debug: (m, x) => console.debug(`[helix-core] ${m}`, x ?? ''),
56
+ info: (m, x) => console.log(`[helix-core] ${m}`, x ?? ''),
57
+ warn: (m, x) => console.warn(`[helix-core] ${m}`, x ?? ''),
58
+ error: (m, x) => console.error(`[helix-core] ${m}`, x ?? ''),
59
+ };
60
+ const DEFAULT_PING_INTERVAL_MS = 30000;
61
+ const DEFAULT_PONG_TIMEOUT_MS = 10000;
62
+ const DEFAULT_MAX_RECONNECT_DELAY_MS = 60000;
63
+ class Connector {
64
+ opts;
65
+ ws = null;
66
+ stopped = false;
67
+ reconnectAttempts = 0;
68
+ pingTimer = null;
69
+ pongTimer = null;
70
+ /** In-flight turns, keyed by sessionId, so chat_close can abort the right one. */
71
+ turns = new Map();
72
+ log;
73
+ pingIntervalMs;
74
+ pongTimeoutMs;
75
+ maxReconnectDelayMs;
76
+ constructor(opts) {
77
+ this.opts = opts;
78
+ this.log = opts.logger ?? consoleLogger;
79
+ this.pingIntervalMs = opts.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
80
+ this.pongTimeoutMs = opts.pongTimeoutMs ?? DEFAULT_PONG_TIMEOUT_MS;
81
+ this.maxReconnectDelayMs = opts.maxReconnectDelayMs ?? DEFAULT_MAX_RECONNECT_DELAY_MS;
82
+ }
83
+ /** Open the socket and keep it open (auto-reconnect) until {@link stop}. */
84
+ start() {
85
+ this.stopped = false;
86
+ this.connect();
87
+ }
88
+ /** Close the socket for good and abort any in-flight turns. */
89
+ stop() {
90
+ this.stopped = true;
91
+ this.clearTimers();
92
+ for (const [, abort] of this.turns)
93
+ abort.abort();
94
+ this.turns.clear();
95
+ if (this.ws) {
96
+ try {
97
+ this.ws.close(1000, 'shutting down');
98
+ }
99
+ catch { /* ignore */ }
100
+ this.ws = null;
101
+ }
102
+ }
103
+ connect() {
104
+ const url = (0, config_1.connectorWsUrl)(this.opts.config);
105
+ this.log.info(`connecting as "${this.opts.config.slug}"`, { url: url.split('?')[0] });
106
+ const ws = new ws_1.default(url);
107
+ this.ws = ws;
108
+ ws.on('open', () => {
109
+ this.reconnectAttempts = 0;
110
+ this.log.info('connected');
111
+ this.sendHello();
112
+ this.startPingLoop();
113
+ });
114
+ // The `ws` client auto-replies to server WS-level pings with pong, which is
115
+ // what keeps agents-api's `lastPingAt` fresh. We track our OWN ping/pong to
116
+ // detect a dead server and force a reconnect.
117
+ ws.on('pong', () => { this.onPong(); });
118
+ ws.on('message', (data) => { this.onMessage(data); });
119
+ ws.on('close', (code, reason) => {
120
+ this.log.warn(`disconnected (${code} ${reason.toString() || ''})`);
121
+ this.clearTimers();
122
+ // A dropped socket kills every in-flight turn; abort so adapters unwind.
123
+ for (const [, abort] of this.turns)
124
+ abort.abort();
125
+ this.turns.clear();
126
+ if (this.ws === ws)
127
+ this.ws = null;
128
+ this.scheduleReconnect();
129
+ });
130
+ ws.on('error', (err) => {
131
+ this.log.error('socket error', err instanceof Error ? err.message : String(err));
132
+ // 'close' fires after 'error'; reconnect is scheduled there.
133
+ });
134
+ }
135
+ scheduleReconnect() {
136
+ if (this.stopped)
137
+ return;
138
+ this.reconnectAttempts += 1;
139
+ const delay = Math.min(this.maxReconnectDelayMs, 1000 * Math.pow(2, Math.min(this.reconnectAttempts, 6)));
140
+ this.log.info(`reconnecting in ${Math.round(delay / 1000)}s (attempt ${this.reconnectAttempts})`);
141
+ setTimeout(() => { if (!this.stopped)
142
+ this.connect(); }, delay);
143
+ }
144
+ // --- Keepalive -----------------------------------------------------------
145
+ startPingLoop() {
146
+ this.clearTimers();
147
+ this.pingTimer = setInterval(() => {
148
+ if (!this.ws || this.ws.readyState !== ws_1.default.OPEN)
149
+ return;
150
+ try {
151
+ this.ws.ping();
152
+ }
153
+ catch { /* ignore */ }
154
+ // If no pong before the timeout, the socket is a zombie — terminate it and
155
+ // let 'close' drive a reconnect.
156
+ if (this.pongTimer)
157
+ clearTimeout(this.pongTimer);
158
+ this.pongTimer = setTimeout(() => {
159
+ this.log.warn('pong timeout — terminating stale socket');
160
+ try {
161
+ this.ws?.terminate();
162
+ }
163
+ catch { /* ignore */ }
164
+ }, this.pongTimeoutMs);
165
+ }, this.pingIntervalMs);
166
+ }
167
+ onPong() {
168
+ if (this.pongTimer) {
169
+ clearTimeout(this.pongTimer);
170
+ this.pongTimer = null;
171
+ }
172
+ }
173
+ clearTimers() {
174
+ if (this.pingTimer) {
175
+ clearInterval(this.pingTimer);
176
+ this.pingTimer = null;
177
+ }
178
+ if (this.pongTimer) {
179
+ clearTimeout(this.pongTimer);
180
+ this.pongTimer = null;
181
+ }
182
+ }
183
+ // --- Handshake -----------------------------------------------------------
184
+ sendHello() {
185
+ const hello = {
186
+ type: 'connector_hello',
187
+ protocol: protocol_1.PROTOCOL_VERSION,
188
+ client: this.opts.client,
189
+ adapter: { name: this.opts.adapter.name, version: this.opts.adapter.version },
190
+ capabilities: ['chat', ...(this.opts.adapter.capabilities ?? [])],
191
+ host: { platform: os.platform(), arch: os.arch(), node: process.version },
192
+ };
193
+ this.sendRaw(hello);
194
+ }
195
+ // --- Inbound dispatch ----------------------------------------------------
196
+ onMessage(data) {
197
+ const frame = (0, protocol_1.parseServerFrame)(data.toString());
198
+ if (!frame) {
199
+ this.log.debug('dropping unparseable frame');
200
+ return;
201
+ }
202
+ switch (frame.type) {
203
+ case 'ping':
204
+ // App-level ping; WS-level pong already handled by the client. No-op.
205
+ return;
206
+ case 'chat_start':
207
+ this.onChatStart(frame.payload ?? {});
208
+ return;
209
+ case 'chat_message':
210
+ void this.onChatMessage(frame.payload ?? {});
211
+ return;
212
+ case 'chat_close':
213
+ this.onChatClose(frame.payload ?? {});
214
+ return;
215
+ default:
216
+ // Forward-compat: an unknown frame from a newer server is ignored, not
217
+ // fatal. This is the promise that lets Helix add frames without breaking
218
+ // older connectors (see protocol.ts).
219
+ this.log.debug(`ignoring unknown frame "${frame.type}"`);
220
+ }
221
+ }
222
+ onChatStart(payload) {
223
+ const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId : undefined;
224
+ if (!sessionId)
225
+ return;
226
+ // Ack so the surface can enable its composer; the turn itself spawns on the
227
+ // following chat_message (mirrors helix-os handleChatStart).
228
+ this.sendRaw((0, protocol_1.chatStarted)(sessionId));
229
+ }
230
+ onChatClose(payload) {
231
+ const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId : undefined;
232
+ if (!sessionId)
233
+ return;
234
+ this.turns.get(sessionId)?.abort();
235
+ }
236
+ async onChatMessage(payload) {
237
+ const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId : undefined;
238
+ const message = typeof payload.message === 'string' ? payload.message : undefined;
239
+ if (!sessionId || message === undefined)
240
+ return;
241
+ if (this.turns.has(sessionId)) {
242
+ this.sendRaw((0, protocol_1.chatError)(sessionId, 'A turn is already in progress for this session'));
243
+ return;
244
+ }
245
+ const abort = new AbortController();
246
+ this.turns.set(sessionId, abort);
247
+ const emit = {
248
+ delta: (text) => this.sendRaw((0, protocol_1.chatDelta)(sessionId, text)),
249
+ tool: (name, phase, ok) => this.sendRaw((0, protocol_1.chatTool)(sessionId, name, phase, ok)),
250
+ status: (text) => this.sendRaw((0, protocol_1.chatStatus)(sessionId, text)),
251
+ };
252
+ const ctx = {
253
+ sessionId,
254
+ message,
255
+ agentId: typeof payload.agentId === 'string' ? payload.agentId : undefined,
256
+ model: typeof payload.model === 'string' ? payload.model : undefined,
257
+ providerRef: typeof payload.providerRef === 'string' ? payload.providerRef : undefined,
258
+ user: (payload.user && typeof payload.user === 'object') ? payload.user : undefined,
259
+ org: typeof payload.org === 'string' ? payload.org : undefined,
260
+ agentName: typeof payload.agentName === 'string' ? payload.agentName : undefined,
261
+ designatedContact: typeof payload.designatedContact === 'string' ? payload.designatedContact : undefined,
262
+ scopeDirective: typeof payload.scopeDirective === 'string' ? payload.scopeDirective : undefined,
263
+ raw: payload,
264
+ signal: abort.signal,
265
+ };
266
+ this.log.info('turn starting', { sessionId, agentId: ctx.agentId });
267
+ try {
268
+ const result = (await this.opts.adapter.runTurn(ctx, emit));
269
+ this.sendRaw((0, protocol_1.chatDone)(sessionId, result ?? {}));
270
+ }
271
+ catch (err) {
272
+ const msg = err instanceof Error ? err.message : String(err);
273
+ this.sendRaw((0, protocol_1.chatError)(sessionId, msg));
274
+ }
275
+ finally {
276
+ this.turns.delete(sessionId);
277
+ this.log.info('turn ended', { sessionId });
278
+ }
279
+ }
280
+ // --- Outbound ------------------------------------------------------------
281
+ sendRaw(frame) {
282
+ if (!this.ws || this.ws.readyState !== ws_1.default.OPEN) {
283
+ this.log.debug('drop outbound frame (socket not open)', { type: frame.type });
284
+ return;
285
+ }
286
+ try {
287
+ this.ws.send(JSON.stringify(frame));
288
+ }
289
+ catch (err) {
290
+ this.log.warn('failed to send frame', err instanceof Error ? err.message : String(err));
291
+ }
292
+ }
293
+ }
294
+ exports.Connector = Connector;
295
+ //# sourceMappingURL=connector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.js","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Public API surface for `@spark-strategic/helix-core`.
3
+ *
4
+ * Embedders (e.g. an OpenClaw process that wants to be a personal agent) import
5
+ * the {@link Connector} and supply their own {@link AgentAdapter}, rather than
6
+ * shelling out to the CLI. The CLI (cli.ts) is just one consumer of this surface.
7
+ */
8
+ export * from './protocol';
9
+ export * from './adapter';
10
+ export * from './config';
11
+ export * from './connector';
12
+ export { packageInfo, PROTOCOL_VERSION } from './version';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * Public API surface for `@spark-strategic/helix-core`.
4
+ *
5
+ * Embedders (e.g. an OpenClaw process that wants to be a personal agent) import
6
+ * the {@link Connector} and supply their own {@link AgentAdapter}, rather than
7
+ * shelling out to the CLI. The CLI (cli.ts) is just one consumer of this surface.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
21
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.PROTOCOL_VERSION = exports.packageInfo = void 0;
25
+ __exportStar(require("./protocol"), exports);
26
+ __exportStar(require("./adapter"), exports);
27
+ __exportStar(require("./config"), exports);
28
+ __exportStar(require("./connector"), exports);
29
+ var version_1 = require("./version");
30
+ Object.defineProperty(exports, "packageInfo", { enumerable: true, get: function () { return version_1.packageInfo; } });
31
+ Object.defineProperty(exports, "PROTOCOL_VERSION", { enumerable: true, get: function () { return version_1.PROTOCOL_VERSION; } });
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;;;;;;;;;;;;;;;AAEH,6CAA2B;AAC3B,4CAA0B;AAC1B,2CAAyB;AACzB,8CAA4B;AAC5B,qCAA0D;AAAjD,sGAAA,WAAW,OAAA;AAAE,2GAAA,gBAAgB,OAAA"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Wire protocol for the Helix local connector.
3
+ *
4
+ * This is the ONE place the on-the-wire contract lives, and it is deliberately
5
+ * conservative because the server (agents-api) and this client version
6
+ * independently: a user's installed package may be older OR newer than the
7
+ * agents-api it dials into. Two rules keep that safe and are why nothing here
8
+ * should be made "clever":
9
+ *
10
+ * 1. Inbound frames are `{ type, payload, timestamp }` (see agents-api
11
+ * sendToConnector). We switch on `type` and hand `payload` to a handler.
12
+ * An UNKNOWN inbound `type` is ignored, never fatal — so the server can add
13
+ * new frames later without breaking older connectors.
14
+ *
15
+ * 2. Outbound chat frames are FLAT (`{ type, sessionId, ... }`), byte-identical
16
+ * to what a Helix node emits (helix-os chat.ts), because agents-api routes
17
+ * them home by `sessionId` through the SAME handler nodes use. Do not wrap
18
+ * them in an envelope.
19
+ *
20
+ * PROTOCOL_VERSION is an integer bumped ONLY on a breaking wire change. Additive,
21
+ * optional-field, or new-frame changes must NOT bump it — announce those via
22
+ * `capabilities` on the hello frame instead, so old and new peers interoperate.
23
+ */
24
+ /** Bump ONLY on a breaking wire change. Additive changes ride `capabilities`. */
25
+ export declare const PROTOCOL_VERSION = 1;
26
+ /** Inbound envelope as sent by agents-api (`sendToConnector`). */
27
+ export interface ServerFrame {
28
+ type: string;
29
+ payload?: Record<string, unknown>;
30
+ timestamp?: string;
31
+ }
32
+ /** Payload of a `chat_message` frame (the fields this client reads; more may ride). */
33
+ export interface ChatMessagePayload {
34
+ sessionId?: string;
35
+ message?: string;
36
+ agentId?: string;
37
+ model?: string;
38
+ providerRef?: string;
39
+ user?: {
40
+ id?: string;
41
+ email?: string;
42
+ name?: string;
43
+ };
44
+ org?: string;
45
+ agentName?: string;
46
+ designatedContact?: string;
47
+ scopeDirective?: string;
48
+ }
49
+ /** The connector's self-report, sent once on connect. Consumed by agents-api to
50
+ * populate `PersonalAgent.installMeta` so Helix can flag an out-of-date client.
51
+ * Every field is additive: unknown fields are ignored by an older server. */
52
+ export interface HelloFrame {
53
+ type: 'connector_hello';
54
+ /** Wire protocol this client speaks. */
55
+ protocol: number;
56
+ /** The published package: name + semver. This is what Helix compares to "latest
57
+ * known" to decide whether to nudge the user to update. */
58
+ client: {
59
+ name: string;
60
+ version: string;
61
+ };
62
+ /** The agent adapter backing this connector (echo, an OpenClaw bridge, …). */
63
+ adapter: {
64
+ name: string;
65
+ version: string;
66
+ };
67
+ /** Feature flags — the forward-compatible extension seam. New abilities are
68
+ * announced here (e.g. 'chat', later 'mcp') instead of bumping `protocol`. */
69
+ capabilities: string[];
70
+ /** Best-effort host identity for operator diagnostics. */
71
+ host: {
72
+ platform: string;
73
+ arch: string;
74
+ node: string;
75
+ };
76
+ }
77
+ /** Parse a raw socket message into a ServerFrame, or null if it isn't one we can
78
+ * use. Never throws — malformed input is dropped, not fatal. */
79
+ export declare function parseServerFrame(data: unknown): ServerFrame | null;
80
+ export declare const chatStarted: (sessionId: string) => Record<string, unknown>;
81
+ export declare const chatDelta: (sessionId: string, text: string) => Record<string, unknown>;
82
+ export declare const chatTool: (sessionId: string, name: string, phase: "call" | "result", ok?: boolean) => Record<string, unknown>;
83
+ export declare const chatStatus: (sessionId: string, text: string) => Record<string, unknown>;
84
+ export declare const chatDone: (sessionId: string, done?: {
85
+ usage?: unknown;
86
+ costUsd?: number | null;
87
+ providerRef?: string;
88
+ model?: string;
89
+ durationMs?: number;
90
+ }) => Record<string, unknown>;
91
+ export declare const chatError: (sessionId: string, message: string) => Record<string, unknown>;
92
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,uFAAuF;AACvF,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;8EAE8E;AAC9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,iBAAiB,CAAC;IACxB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB;gEAC4D;IAC5D,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,8EAA8E;IAC9E,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C;mFAC+E;IAC/E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,0DAA0D;IAC1D,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACxD;AAED;iEACiE;AACjE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAoBlE;AAID,eAAO,MAAM,WAAW,GAAI,WAAW,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAGpE,CAAC;AAEH,eAAO,MAAM,SAAS,GAAI,WAAW,MAAM,EAAE,MAAM,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAIhF,CAAC;AAEH,eAAO,MAAM,QAAQ,GACnB,WAAW,MAAM,EACjB,MAAM,MAAM,EACZ,OAAO,MAAM,GAAG,QAAQ,EACxB,KAAK,OAAO,KACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAMvB,CAAC;AAEH,eAAO,MAAM,UAAU,GAAI,WAAW,MAAM,EAAE,MAAM,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAIjF,CAAC;AAEH,eAAO,MAAM,QAAQ,GACnB,WAAW,MAAM,EACjB,OAAM;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,KACjH,MAAM,CAAC,MAAM,EAAE,OAAO,CAQvB,CAAC;AAEH,eAAO,MAAM,SAAS,GAAI,WAAW,MAAM,EAAE,SAAS,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAInF,CAAC"}
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * Wire protocol for the Helix local connector.
4
+ *
5
+ * This is the ONE place the on-the-wire contract lives, and it is deliberately
6
+ * conservative because the server (agents-api) and this client version
7
+ * independently: a user's installed package may be older OR newer than the
8
+ * agents-api it dials into. Two rules keep that safe and are why nothing here
9
+ * should be made "clever":
10
+ *
11
+ * 1. Inbound frames are `{ type, payload, timestamp }` (see agents-api
12
+ * sendToConnector). We switch on `type` and hand `payload` to a handler.
13
+ * An UNKNOWN inbound `type` is ignored, never fatal — so the server can add
14
+ * new frames later without breaking older connectors.
15
+ *
16
+ * 2. Outbound chat frames are FLAT (`{ type, sessionId, ... }`), byte-identical
17
+ * to what a Helix node emits (helix-os chat.ts), because agents-api routes
18
+ * them home by `sessionId` through the SAME handler nodes use. Do not wrap
19
+ * them in an envelope.
20
+ *
21
+ * PROTOCOL_VERSION is an integer bumped ONLY on a breaking wire change. Additive,
22
+ * optional-field, or new-frame changes must NOT bump it — announce those via
23
+ * `capabilities` on the hello frame instead, so old and new peers interoperate.
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.chatError = exports.chatDone = exports.chatStatus = exports.chatTool = exports.chatDelta = exports.chatStarted = exports.PROTOCOL_VERSION = void 0;
27
+ exports.parseServerFrame = parseServerFrame;
28
+ /** Bump ONLY on a breaking wire change. Additive changes ride `capabilities`. */
29
+ exports.PROTOCOL_VERSION = 1;
30
+ /** Parse a raw socket message into a ServerFrame, or null if it isn't one we can
31
+ * use. Never throws — malformed input is dropped, not fatal. */
32
+ function parseServerFrame(data) {
33
+ let obj = data;
34
+ if (typeof data === 'string') {
35
+ try {
36
+ obj = JSON.parse(data);
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ if (!obj || typeof obj !== 'object')
43
+ return null;
44
+ const type = obj.type;
45
+ if (typeof type !== 'string')
46
+ return null;
47
+ const payload = obj.payload;
48
+ return {
49
+ type,
50
+ payload: payload && typeof payload === 'object' ? payload : undefined,
51
+ timestamp: typeof obj.timestamp === 'string'
52
+ ? obj.timestamp
53
+ : undefined,
54
+ };
55
+ }
56
+ // --- Outbound flat chat frames (mirror helix-os chat.ts exactly) ------------
57
+ const chatStarted = (sessionId) => ({
58
+ type: 'chat_started',
59
+ sessionId,
60
+ });
61
+ exports.chatStarted = chatStarted;
62
+ const chatDelta = (sessionId, text) => ({
63
+ type: 'chat_delta',
64
+ sessionId,
65
+ text,
66
+ });
67
+ exports.chatDelta = chatDelta;
68
+ const chatTool = (sessionId, name, phase, ok) => ({
69
+ type: 'chat_tool',
70
+ sessionId,
71
+ name,
72
+ phase,
73
+ ...(ok === undefined ? {} : { ok }),
74
+ });
75
+ exports.chatTool = chatTool;
76
+ const chatStatus = (sessionId, text) => ({
77
+ type: 'chat_status',
78
+ sessionId,
79
+ text,
80
+ });
81
+ exports.chatStatus = chatStatus;
82
+ const chatDone = (sessionId, done = {}) => ({
83
+ type: 'chat_done',
84
+ sessionId,
85
+ usage: done.usage ?? null,
86
+ costUsd: done.costUsd ?? null,
87
+ ...(done.providerRef === undefined ? {} : { providerRef: done.providerRef }),
88
+ ...(done.model === undefined ? {} : { model: done.model }),
89
+ ...(done.durationMs === undefined ? {} : { durationMs: done.durationMs }),
90
+ });
91
+ exports.chatDone = chatDone;
92
+ const chatError = (sessionId, message) => ({
93
+ type: 'chat_error',
94
+ sessionId,
95
+ message,
96
+ });
97
+ exports.chatError = chatError;
98
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Resolve this package's own name + version at runtime (for the hello frame and
3
+ * `--version`). Read from package.json rather than hardcoded so a published build
4
+ * always reports the version it was actually cut at.
5
+ */
6
+ export { PROTOCOL_VERSION } from './protocol';
7
+ export declare function packageInfo(): {
8
+ name: string;
9
+ version: string;
10
+ };
11
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAI9C,wBAAgB,WAAW,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAqB/D"}
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ /**
3
+ * Resolve this package's own name + version at runtime (for the hello frame and
4
+ * `--version`). Read from package.json rather than hardcoded so a published build
5
+ * always reports the version it was actually cut at.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.PROTOCOL_VERSION = void 0;
42
+ exports.packageInfo = packageInfo;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ var protocol_1 = require("./protocol");
46
+ Object.defineProperty(exports, "PROTOCOL_VERSION", { enumerable: true, get: function () { return protocol_1.PROTOCOL_VERSION; } });
47
+ let cached = null;
48
+ function packageInfo() {
49
+ if (cached)
50
+ return cached;
51
+ // Compiled to dist/version.js, so package.json is one level up; guard a couple
52
+ // of layouts (ts-node dev, nested installs) before falling back.
53
+ const candidates = [
54
+ path.join(__dirname, '..', 'package.json'),
55
+ path.join(__dirname, '..', '..', 'package.json'),
56
+ ];
57
+ for (const p of candidates) {
58
+ try {
59
+ const pkg = JSON.parse(fs.readFileSync(p, 'utf-8'));
60
+ if (pkg && typeof pkg.name === 'string') {
61
+ cached = { name: pkg.name, version: typeof pkg.version === 'string' ? pkg.version : '0.0.0' };
62
+ return cached;
63
+ }
64
+ }
65
+ catch {
66
+ /* try next candidate */
67
+ }
68
+ }
69
+ cached = { name: '@spark-strategic/helix-core', version: '0.0.0' };
70
+ return cached;
71
+ }
72
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASH,kCAqBC;AA5BD,uCAAyB;AACzB,2CAA6B;AAE7B,uCAA8C;AAArC,4GAAA,gBAAgB,OAAA;AAEzB,IAAI,MAAM,GAA6C,IAAI,CAAC;AAE5D,SAAgB,WAAW;IACzB,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,iEAAiE;IACjE,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;KACjD,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YACpD,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACxC,MAAM,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC9F,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;IACD,MAAM,GAAG,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACnE,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@spark-strategic/helix-core",
3
+ "version": "0.1.0",
4
+ "description": "Helix Core — the minimal local runtime you run to connect a personal agent to Helix. Other tools (MCP, etc.) ship as their own packages that Core runs.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "helix-core": "./dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "ts-node src/cli.ts",
17
+ "start": "node dist/cli.js",
18
+ "clean": "rimraf dist",
19
+ "prepublishOnly": "npm run clean && npm run build"
20
+ },
21
+ "keywords": [
22
+ "helix",
23
+ "agent",
24
+ "connector",
25
+ "personal-agent"
26
+ ],
27
+ "author": "Spark Strategic",
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "ws": "^8.20.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^20.10.0",
34
+ "@types/ws": "^8.18.1",
35
+ "rimraf": "^5.0.5",
36
+ "ts-node": "^10.9.2",
37
+ "typescript": "^5.3.2"
38
+ },
39
+ "engines": {
40
+ "node": ">=18"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/Spark-Strategic/helix.spark-strategic.ai.git",
48
+ "directory": "helix-core"
49
+ }
50
+ }