@chatlychat/cli 0.2.0 → 0.4.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 (37) hide show
  1. package/dist/commands/config.d.ts.map +1 -1
  2. package/dist/commands/config.js +29 -3
  3. package/dist/commands/config.js.map +1 -1
  4. package/dist/commands/listen-bot.d.ts +52 -0
  5. package/dist/commands/listen-bot.d.ts.map +1 -0
  6. package/dist/commands/listen-bot.js +253 -0
  7. package/dist/commands/listen-bot.js.map +1 -0
  8. package/dist/commands/listen-core.d.ts +107 -0
  9. package/dist/commands/listen-core.d.ts.map +1 -0
  10. package/dist/commands/listen-core.js +334 -0
  11. package/dist/commands/listen-core.js.map +1 -0
  12. package/dist/commands/listen-relay-harness.d.ts +80 -0
  13. package/dist/commands/listen-relay-harness.d.ts.map +1 -0
  14. package/dist/commands/listen-relay-harness.js +163 -0
  15. package/dist/commands/listen-relay-harness.js.map +1 -0
  16. package/dist/commands/listen-relay-options.d.ts +78 -0
  17. package/dist/commands/listen-relay-options.d.ts.map +1 -0
  18. package/dist/commands/listen-relay-options.js +195 -0
  19. package/dist/commands/listen-relay-options.js.map +1 -0
  20. package/dist/commands/listen-relay-sign.d.ts +36 -0
  21. package/dist/commands/listen-relay-sign.d.ts.map +1 -0
  22. package/dist/commands/listen-relay-sign.js +136 -0
  23. package/dist/commands/listen-relay-sign.js.map +1 -0
  24. package/dist/commands/listen-relay.d.ts +43 -0
  25. package/dist/commands/listen-relay.d.ts.map +1 -0
  26. package/dist/commands/listen-relay.js +434 -0
  27. package/dist/commands/listen-relay.js.map +1 -0
  28. package/dist/commands/listen.d.ts +2 -39
  29. package/dist/commands/listen.d.ts.map +1 -1
  30. package/dist/commands/listen.js +73 -208
  31. package/dist/commands/listen.js.map +1 -1
  32. package/dist/help.d.ts +1 -1
  33. package/dist/help.d.ts.map +1 -1
  34. package/dist/help.js +19 -3
  35. package/dist/help.js.map +1 -1
  36. package/dist/run.js +1 -1
  37. package/package.json +3 -4
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The flags and the wire shapes of `chatly listen --relay`.
3
+ *
4
+ * Split out of `listen-relay.ts` for the 650-line ceiling. What is parsed
5
+ * here, and why each refusal exists, is documented on each function; the
6
+ * protocol the shapes belong to is `apps/cli/RELAY-SERVER-SPEC.md`.
7
+ */
8
+ import { type RelayKind } from './listen-relay-sign.js';
9
+ import { type ParsedArgs } from '../args.js';
10
+ export interface RelaySecrets {
11
+ /** `--secret <value>`: for every target without its own. */
12
+ readonly fallback: string | null;
13
+ /** `--secret <targetId>=<value>`. */
14
+ readonly byTarget: ReadonlyMap<string, string>;
15
+ }
16
+ export interface RelayOptions {
17
+ readonly forwardTo: string;
18
+ readonly tools: readonly string[];
19
+ readonly cards: readonly string[];
20
+ readonly canvases: readonly string[];
21
+ readonly secrets: RelaySecrets;
22
+ readonly json: boolean;
23
+ }
24
+ /** One relayed call, as `GET /v1/relay-sessions/{id}/frames` renders it. */
25
+ export interface RelayFrame {
26
+ readonly id: string;
27
+ readonly kind: RelayKind;
28
+ readonly targetId: string;
29
+ readonly method: string;
30
+ /** `pathname + search` of the CONFIGURED url. */
31
+ readonly path: string;
32
+ readonly headers: Readonly<Record<string, string>>;
33
+ /** Exactly the bytes the configured url would have received. `''` for none. */
34
+ readonly body: string;
35
+ /** When the waiting caller gives up. The CLI aborts the forward then. */
36
+ readonly deadlineAt: string;
37
+ }
38
+ /** What the CLI posts back. Exactly one of the two arms. */
39
+ export type RelayAnswer = {
40
+ readonly status: number;
41
+ readonly headers: Record<string, string>;
42
+ readonly body: string;
43
+ readonly durationMs: number;
44
+ } | {
45
+ readonly error: 'unreachable' | 'timeout' | 'too_large' | 'not_selected' | 'signing_failed' | 'interrupted';
46
+ readonly message: string;
47
+ readonly durationMs: number;
48
+ };
49
+ /** `--tool`, `--card` or `--canvas` on a command that is not `--relay`. */
50
+ export declare function relayOnlyFlagWithoutRelay(args: ParsedArgs): string | null;
51
+ /** Read the relay flags, refusing anything that cannot work, with the fix named. */
52
+ export declare function parseRelayOptions(args: ParsedArgs): RelayOptions;
53
+ /**
54
+ * Where one frame goes: `--forward-to`'s origin and path prefix, then the
55
+ * CONFIGURED url's path and query.
56
+ *
57
+ * The path is preserved because for a tool call it IS the request —
58
+ * `GET /orders/{{order_id}}` renders to `/orders/42`, and a handler that
59
+ * cannot see the 42 cannot answer. Cards and canvases are kept the same way
60
+ * so there is one rule, and a local server mounted under a prefix still works.
61
+ */
62
+ export declare function relayTargetUrl(forwardTo: string, framePath: string): string;
63
+ /**
64
+ * The frames on one poll, and how many were unreadable.
65
+ *
66
+ * Hand-checked rather than zod, because this package declares the SDK as its
67
+ * only dependency (CLAUDE.md §2: no new dependency without approval). An
68
+ * unreadable frame is COUNTED, not silently dropped — the server is waiting
69
+ * on it, and a relay that loses frames quietly reads as a slow handler.
70
+ */
71
+ export declare function readFrames(value: unknown): {
72
+ frames: RelayFrame[];
73
+ unreadable: number;
74
+ };
75
+ /** Which selected set a frame's kind is checked against. */
76
+ export declare function selectedFor(opts: RelayOptions, kind: RelayKind): readonly string[];
77
+ export declare function secretFor(opts: RelayOptions, targetId: string): string | null;
78
+ //# sourceMappingURL=listen-relay-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-relay-options.d.ts","sourceRoot":"","sources":["../../src/commands/listen-relay-options.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxD,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,4DAA4D;AAC5D,MAAM,MAAM,WAAW,GACnB;IACE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,GACD;IACE,QAAQ,CAAC,KAAK,EACV,aAAa,GACb,SAAS,GACT,WAAW,GACX,cAAc,GACd,gBAAgB,GAChB,aAAa,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAaN,2EAA2E;AAC3E,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAOzE;AAqDD,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY,CA6DhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQ3E;AAWD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CA8BvF;AAED,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,GAAG,SAAS,MAAM,EAAE,CAMlF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE7E"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * The flags and the wire shapes of `chatly listen --relay`.
3
+ *
4
+ * Split out of `listen-relay.ts` for the 650-line ceiling. What is parsed
5
+ * here, and why each refusal exists, is documented on each function; the
6
+ * protocol the shapes belong to is `apps/cli/RELAY-SERVER-SPEC.md`.
7
+ */
8
+ import { RELAY_KINDS } from './listen-relay-sign.js';
9
+ import { bool, str } from '../args.js';
10
+ const ID_FLAGS = ['tool', 'card', 'canvas'];
11
+ function listFlag(args, name) {
12
+ const raw = args.flags[name];
13
+ if (raw === undefined || raw === true || raw === false) {
14
+ return [];
15
+ }
16
+ return (Array.isArray(raw) ? raw : [String(raw)])
17
+ .flatMap((v) => String(v).split(','))
18
+ .map((s) => s.trim())
19
+ .filter((s) => s.length > 0);
20
+ }
21
+ /** `--tool`, `--card` or `--canvas` on a command that is not `--relay`. */
22
+ export function relayOnlyFlagWithoutRelay(args) {
23
+ for (const name of ID_FLAGS) {
24
+ if (args.flags[name] !== undefined) {
25
+ return name;
26
+ }
27
+ }
28
+ return null;
29
+ }
30
+ /** The ledger-tail flags, none of which mean anything on a relay. */
31
+ const LEDGER_ONLY_FLAGS = ['bot', 'endpoint', 'event', 'status', 'replay', 'interval', 'limit'];
32
+ function parseSecrets(args, ids) {
33
+ const raw = args.flags['secret'];
34
+ const values = raw === undefined || typeof raw === 'boolean' ? [] : Array.isArray(raw) ? raw : [raw];
35
+ let fallback = null;
36
+ const byTarget = new Map();
37
+ for (const value of values.map(String)) {
38
+ /*
39
+ * `<id>=<secret>` only when the left side IS one of the selected ids. A
40
+ * secret is operator-chosen on a tool, so it may itself contain `=`
41
+ * (standard base64 padding), and splitting every value on the first one
42
+ * would turn that secret into a target id nobody asked for.
43
+ */
44
+ const eq = value.indexOf('=');
45
+ const left = eq > 0 ? value.slice(0, eq) : '';
46
+ if (left.length > 0 && ids.has(left)) {
47
+ byTarget.set(left, value.slice(eq + 1));
48
+ continue;
49
+ }
50
+ if (fallback !== null) {
51
+ throw new Error('Two bare --secret values. With more than one target, name which secret is whose:\n' +
52
+ ' --secret <id>=<secret> (repeatable)');
53
+ }
54
+ fallback = value;
55
+ }
56
+ for (const secret of [fallback, ...byTarget.values()]) {
57
+ if (secret === null) {
58
+ continue;
59
+ }
60
+ /*
61
+ * The two prefixes this product mints for OTHER surfaces. Neither signs
62
+ * a tool call, a card fetch or a canvas step, and the symptom of passing
63
+ * one is a 401 from your own handler that looks like a verifier bug.
64
+ */
65
+ if (secret.startsWith('whsec_') || secret.startsWith('chsec_')) {
66
+ throw new Error(`--secret "${secret.slice(0, 6)}…" is a ${secret.startsWith('whsec_') ? 'webhook endpoint' : 'external agent'} secret, and it signs a different surface. A relay needs the secret configured on ` +
67
+ "the tool, the customer card (or its endpoint), or the channel's canvas.");
68
+ }
69
+ }
70
+ return { fallback, byTarget };
71
+ }
72
+ /** Read the relay flags, refusing anything that cannot work, with the fix named. */
73
+ export function parseRelayOptions(args) {
74
+ for (const name of LEDGER_ONLY_FLAGS) {
75
+ if (args.flags[name] !== undefined) {
76
+ throw new Error(`--${name} tails a delivery ledger and means nothing on --relay, which receives live ` +
77
+ 'calls instead of replaying logged ones.');
78
+ }
79
+ }
80
+ const tools = listFlag(args, 'tool');
81
+ const cards = listFlag(args, 'card');
82
+ const canvases = listFlag(args, 'canvas');
83
+ if (tools.length + cards.length + canvases.length === 0) {
84
+ throw new Error('--relay needs at least one explicit target, because it takes PRODUCTION traffic:\n' +
85
+ ' --tool <id> an AI tool (chatly ai tools list)\n' +
86
+ ' --card <id> a customer card (chatly customer-cards list)\n' +
87
+ " --canvas <channelId> a channel's canvas endpoint\n" +
88
+ 'There is deliberately no form that relays everything.');
89
+ }
90
+ const forwardTo = str(args.flags, 'forward-to') ?? null;
91
+ if (forwardTo === null) {
92
+ throw new Error('--relay needs --forward-to. A relayed call is waiting for an answer, so there is no ' +
93
+ 'print-only mode:\n chatly listen --relay --tool <id> --forward-to http://localhost:3000');
94
+ }
95
+ let parsed;
96
+ try {
97
+ parsed = new URL(forwardTo);
98
+ }
99
+ catch {
100
+ parsed = null;
101
+ }
102
+ // `localhost:3000` parses, as the scheme `localhost:` — see listen-core.ts.
103
+ if (parsed === null || (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')) {
104
+ throw new Error(`--forward-to "${forwardTo}" is not an http(s) URL. Include the scheme:\n` +
105
+ ' --forward-to http://localhost:3000');
106
+ }
107
+ if (parsed.search !== '' || parsed.hash !== '') {
108
+ // The configured url's own query is appended (see relayTargetUrl), and
109
+ // merging two query strings would sign one the handler does not receive.
110
+ throw new Error(`--forward-to "${forwardTo}" has a query or fragment. Give an origin, optionally with a ` +
111
+ "path prefix; each call's own path and query are appended to it.");
112
+ }
113
+ const ids = new Set([...tools, ...cards, ...canvases]);
114
+ return {
115
+ forwardTo,
116
+ tools,
117
+ cards,
118
+ canvases,
119
+ secrets: parseSecrets(args, ids),
120
+ json: bool(args.flags, 'json'),
121
+ };
122
+ }
123
+ /**
124
+ * Where one frame goes: `--forward-to`'s origin and path prefix, then the
125
+ * CONFIGURED url's path and query.
126
+ *
127
+ * The path is preserved because for a tool call it IS the request —
128
+ * `GET /orders/{{order_id}}` renders to `/orders/42`, and a handler that
129
+ * cannot see the 42 cannot answer. Cards and canvases are kept the same way
130
+ * so there is one rule, and a local server mounted under a prefix still works.
131
+ */
132
+ export function relayTargetUrl(forwardTo, framePath) {
133
+ const base = new URL(forwardTo);
134
+ const prefix = base.pathname.replace(/\/+$/, '');
135
+ const rel = framePath.startsWith('/') ? framePath : `/${framePath}`;
136
+ const q = rel.indexOf('?');
137
+ const pathname = q === -1 ? rel : rel.slice(0, q);
138
+ const search = q === -1 ? '' : rel.slice(q);
139
+ return `${base.origin}${prefix}${pathname}${search}`;
140
+ }
141
+ function isStringRecord(value) {
142
+ return (typeof value === 'object' &&
143
+ value !== null &&
144
+ !Array.isArray(value) &&
145
+ Object.values(value).every((v) => typeof v === 'string'));
146
+ }
147
+ /**
148
+ * The frames on one poll, and how many were unreadable.
149
+ *
150
+ * Hand-checked rather than zod, because this package declares the SDK as its
151
+ * only dependency (CLAUDE.md §2: no new dependency without approval). An
152
+ * unreadable frame is COUNTED, not silently dropped — the server is waiting
153
+ * on it, and a relay that loses frames quietly reads as a slow handler.
154
+ */
155
+ export function readFrames(value) {
156
+ const list = typeof value === 'object' && value !== null
157
+ ? value.frames
158
+ : undefined;
159
+ if (!Array.isArray(list)) {
160
+ return { frames: [], unreadable: 0 };
161
+ }
162
+ const frames = [];
163
+ let unreadable = 0;
164
+ for (const f of list) {
165
+ const r = (typeof f === 'object' && f !== null ? f : {});
166
+ if (typeof r['id'] === 'string' &&
167
+ typeof r['kind'] === 'string' &&
168
+ RELAY_KINDS.includes(r['kind']) &&
169
+ typeof r['targetId'] === 'string' &&
170
+ typeof r['method'] === 'string' &&
171
+ typeof r['path'] === 'string' &&
172
+ isStringRecord(r['headers']) &&
173
+ typeof r['body'] === 'string' &&
174
+ typeof r['deadlineAt'] === 'string' &&
175
+ !Number.isNaN(Date.parse(r['deadlineAt']))) {
176
+ frames.push(r);
177
+ }
178
+ else {
179
+ unreadable += 1;
180
+ }
181
+ }
182
+ return { frames, unreadable };
183
+ }
184
+ /** Which selected set a frame's kind is checked against. */
185
+ export function selectedFor(opts, kind) {
186
+ return kind === 'ai_tool_call'
187
+ ? opts.tools
188
+ : kind === 'customer_card'
189
+ ? opts.cards
190
+ : opts.canvases;
191
+ }
192
+ export function secretFor(opts, targetId) {
193
+ return opts.secrets.byTarget.get(targetId) ?? opts.secrets.fallback;
194
+ }
195
+ //# sourceMappingURL=listen-relay-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-relay-options.js","sourceRoot":"","sources":["../../src/commands/listen-relay-options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /** The three surfaces a relay carries. Values are the SDK's surface names. */
2
+ export declare const RELAY_KINDS: readonly ["ai_tool_call", "customer_card", "canvas_step"];
3
+ export type RelayKind = (typeof RELAY_KINDS)[number];
4
+ /**
5
+ * Header names this CLI owns on a relayed request.
6
+ *
7
+ * Stripped from whatever the frame carried before signing, so a server that
8
+ * got §5 wrong and sent the production signature cannot have it forwarded
9
+ * beside ours — a handler that reads the first `x-chatly-signature` it finds
10
+ * would then verify the server's MAC, which is over the PRODUCTION path, and
11
+ * fail for a reason nobody could see. `host` and `content-length` are
12
+ * `fetch`'s to set.
13
+ */
14
+ export declare const RELAY_OWNED_HEADERS: ReadonlySet<string>;
15
+ /**
16
+ * The signature headers for one relayed request, self-checked.
17
+ *
18
+ * Returns the headers to merge, or a message naming what failed — which can
19
+ * only be a bug in this CLI, since it signed the bytes itself.
20
+ */
21
+ export declare function signRelayedRequest(input: {
22
+ readonly kind: RelayKind;
23
+ readonly method: string;
24
+ /** The absolute LOCAL url this request is about to be sent to. */
25
+ readonly targetUrl: string;
26
+ readonly body: string;
27
+ readonly secret: string;
28
+ readonly nowMs: number;
29
+ }): {
30
+ ok: true;
31
+ headers: Record<string, string>;
32
+ } | {
33
+ ok: false;
34
+ message: string;
35
+ };
36
+ //# sourceMappingURL=listen-relay-sign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-relay-sign.d.ts","sourceRoot":"","sources":["../../src/commands/listen-relay-sign.ts"],"names":[],"mappings":"AA2CA,8EAA8E;AAC9E,eAAO,MAAM,WAAW,2DAA4D,CAAC;AACrF,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAQlD,CAAC;AAMH;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAgEjF"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * The signature a relayed SYNCHRONOUS call carries when it reaches localhost.
3
+ *
4
+ * ═══ WHY THE CLI SIGNS AND THE SERVER DOES NOT ════════════════════════════
5
+ *
6
+ * The server holds the real secret for every one of these surfaces, so the
7
+ * obvious design is for it to sign the frame and for this CLI to forward it
8
+ * byte for byte. That is rejected in `RELAY-SERVER-SPEC.md` §5 and the reason
9
+ * is worth repeating where the signing happens: a frame carrying a
10
+ * production-valid signature is a replayable credential, delivered to anyone
11
+ * holding a `ck_` key with the relay permission. For a tool call the MAC
12
+ * covers the method and the path, so the replay is narrow — but for a
13
+ * customer-card fetch and a canvas step it covers `<ts>.<body>` only, and the
14
+ * same signed bytes are accepted by the operator's REAL endpoint for the next
15
+ * five minutes. So frames arrive unsigned and are signed here, with the
16
+ * secret the developer passes in `--secret`, exactly as `listen` and
17
+ * `listen --bot` already do for the ledgers.
18
+ *
19
+ * ═══ THE THREE CONSTRUCTIONS, AS THE SENDERS USE THEM ═════════════════════
20
+ *
21
+ * Read against the senders, not against the docs:
22
+ *
23
+ * | kind | sender | header(s) | base | `t` unit |
24
+ * | --------------- | --------------------------------------------------- | --------------------------------------------------- | ----------------------------- | -------- |
25
+ * | `ai_tool_call` | `dialCustomTool`, `packages/shared/src/ai-tools` | `x-chatly-signature: t=…,v1=…` | `<t>.<METHOD>.<path>.<body>` | seconds |
26
+ * | `customer_card` | `customer-cards.fetcher.ts` `attempt()` | `x-chatly-signature: sha256=…` + `x-chatly-timestamp` | `<ts>.<body>` | **ms** |
27
+ * | `canvas_step` | `widget-canvas.service.ts` `callIntegrator()` | `x-chatly-canvas-signature: sha256=…` + `-ts` | `<ts>.<body>` | **ms** |
28
+ *
29
+ * Two of the three put `x-chatly-signature` on the wire with DIFFERENT
30
+ * formats, and two send milliseconds where the third sends seconds. Both
31
+ * traps are why each header is checked with the SDK's own
32
+ * `verifyChatlySignature` under its own `surface` before the request goes
33
+ * out, and a forward that does not verify is refused rather than sent — the
34
+ * reasoning is `listen-core.ts`'s, and so is the unified
35
+ * `x-chatly-signature-v2` minted beside each one.
36
+ *
37
+ * `path` in the tool-call base string is the LOCAL path, because that is the
38
+ * request line the developer's handler verifies.
39
+ */
40
+ import { createHmac } from 'node:crypto';
41
+ import { chatlyV2SigningPath, signChatlyV2, verifyChatlySignature } from '@chatlychat/sdk-node';
42
+ /** The three surfaces a relay carries. Values are the SDK's surface names. */
43
+ export const RELAY_KINDS = ['ai_tool_call', 'customer_card', 'canvas_step'];
44
+ /**
45
+ * Header names this CLI owns on a relayed request.
46
+ *
47
+ * Stripped from whatever the frame carried before signing, so a server that
48
+ * got §5 wrong and sent the production signature cannot have it forwarded
49
+ * beside ours — a handler that reads the first `x-chatly-signature` it finds
50
+ * would then verify the server's MAC, which is over the PRODUCTION path, and
51
+ * fail for a reason nobody could see. `host` and `content-length` are
52
+ * `fetch`'s to set.
53
+ */
54
+ export const RELAY_OWNED_HEADERS = new Set([
55
+ 'x-chatly-signature',
56
+ 'x-chatly-signature-v2',
57
+ 'x-chatly-timestamp',
58
+ 'x-chatly-canvas-signature',
59
+ 'x-chatly-canvas-ts',
60
+ 'host',
61
+ 'content-length',
62
+ ]);
63
+ function hmacHex(secret, base) {
64
+ return createHmac('sha256', secret).update(base).digest('hex');
65
+ }
66
+ /**
67
+ * The signature headers for one relayed request, self-checked.
68
+ *
69
+ * Returns the headers to merge, or a message naming what failed — which can
70
+ * only be a bug in this CLI, since it signed the bytes itself.
71
+ */
72
+ export function signRelayedRequest(input) {
73
+ const { kind, body, secret, nowMs } = input;
74
+ const method = input.method.toUpperCase();
75
+ const path = chatlyV2SigningPath(input.targetUrl);
76
+ if (path === null) {
77
+ return { ok: false, message: `"${input.targetUrl}" is not a URL` };
78
+ }
79
+ const seconds = Math.floor(nowMs / 1000);
80
+ const headers = {};
81
+ if (kind === 'ai_tool_call') {
82
+ const t = String(seconds);
83
+ headers['x-chatly-signature'] =
84
+ `t=${t},v1=${hmacHex(secret, `${t}.${method}.${path}.${body}`)}`;
85
+ }
86
+ else {
87
+ // MILLISECONDS, as both senders do: `Date.now().toString()`.
88
+ const ts = String(Math.floor(nowMs));
89
+ const value = `sha256=${hmacHex(secret, `${ts}.${body}`)}`;
90
+ if (kind === 'customer_card') {
91
+ headers['x-chatly-timestamp'] = ts;
92
+ headers['x-chatly-signature'] = value;
93
+ }
94
+ else {
95
+ headers['x-chatly-canvas-ts'] = ts;
96
+ headers['x-chatly-canvas-signature'] = value;
97
+ }
98
+ }
99
+ const legacyCheck = verifyChatlySignature({
100
+ surface: kind,
101
+ body,
102
+ headers,
103
+ secret,
104
+ method,
105
+ path,
106
+ nowMs,
107
+ // This check is of the surface's OWN construction; v2 is checked below.
108
+ v2: 'ignore',
109
+ });
110
+ if (!legacyCheck.ok) {
111
+ return { ok: false, message: `${legacyCheck.reason}: ${legacyCheck.message}` };
112
+ }
113
+ const v2 = signChatlyV2({
114
+ base: 'target',
115
+ secret,
116
+ timestampSeconds: seconds,
117
+ method,
118
+ path,
119
+ body,
120
+ });
121
+ const v2Check = verifyChatlySignature({
122
+ surface: 'unified_v2',
123
+ body,
124
+ headers: { 'x-chatly-signature-v2': v2 },
125
+ secret,
126
+ method,
127
+ path,
128
+ nowMs,
129
+ });
130
+ if (!v2Check.ok) {
131
+ return { ok: false, message: `${v2Check.reason}: ${v2Check.message}` };
132
+ }
133
+ headers['x-chatly-signature-v2'] = v2;
134
+ return { ok: true, headers };
135
+ }
136
+ //# sourceMappingURL=listen-relay-sign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-relay-sign.js","sourceRoot":"","sources":["../../src/commands/listen-relay-sign.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAEhG,8EAA8E;AAC9E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,cAAc,EAAE,eAAe,EAAE,aAAa,CAAU,CAAC;AAGrF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC;IAC9D,oBAAoB;IACpB,uBAAuB;IACvB,oBAAoB;IACpB,2BAA2B;IAC3B,oBAAoB;IACpB,MAAM;IACN,gBAAgB;CACjB,CAAC,CAAC;AAEH,SAAS,OAAO,CAAC,MAAc,EAAE,IAAY;IAC3C,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAQlC;IACC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;IAC1C,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAClD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,KAAK,CAAC,SAAS,gBAAgB,EAAE,CAAC;IACrE,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAEzC,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,CAAC,oBAAoB,CAAC;YAC3B,KAAK,CAAC,OAAO,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;IACrE,CAAC;SAAM,CAAC;QACN,6DAA6D;QAC7D,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,UAAU,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;QAC3D,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;YAC7B,OAAO,CAAC,oBAAoB,CAAC,GAAG,EAAE,CAAC;YACnC,OAAO,CAAC,oBAAoB,CAAC,GAAG,KAAK,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,oBAAoB,CAAC,GAAG,EAAE,CAAC;YACnC,OAAO,CAAC,2BAA2B,CAAC,GAAG,KAAK,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,qBAAqB,CAAC;QACxC,OAAO,EAAE,IAAI;QACb,IAAI;QACJ,OAAO;QACP,MAAM;QACN,MAAM;QACN,IAAI;QACJ,KAAK;QACL,wEAAwE;QACxE,EAAE,EAAE,QAAQ;KACb,CAAC,CAAC;IACH,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;QACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC,MAAM,KAAK,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,MAAM,EAAE,GAAG,YAAY,CAAC;QACtB,IAAI,EAAE,QAAQ;QACd,MAAM;QACN,gBAAgB,EAAE,OAAO;QACzB,MAAM;QACN,IAAI;QACJ,IAAI;KACL,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,qBAAqB,CAAC;QACpC,OAAO,EAAE,YAAY;QACrB,IAAI;QACJ,OAAO,EAAE,EAAE,uBAAuB,EAAE,EAAE,EAAE;QACxC,MAAM;QACN,MAAM;QACN,IAAI;QACJ,KAAK;KACN,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,CAAC,uBAAuB,CAAC,GAAG,EAAE,CAAC;IACtC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { type ChatlyClient } from '@chatlychat/sdk-node';
2
+ import { type RelayAnswer, type RelayFrame, type RelayOptions } from './listen-relay-options.js';
3
+ import type { ListenDeps } from './listen-core.js';
4
+ export { parseRelayOptions, readFrames, relayOnlyFlagWithoutRelay, relayTargetUrl, type RelayAnswer, type RelayFrame, type RelayOptions, type RelaySecrets, } from './listen-relay-options.js';
5
+ /** Detach after this long with no frame. See the header, rule 2. */
6
+ export declare const RELAY_IDLE_TIMEOUT_MS: number;
7
+ /** Long-poll wait. Under the SDK transport's 15s request timeout. */
8
+ export declare const RELAY_POLL_WAIT_SECONDS = 10;
9
+ /** A local answer larger than this is refused rather than relayed. */
10
+ export declare const RELAY_MAX_RESPONSE_BYTES = 1048576;
11
+ /** The protocol version this CLI speaks, sent on attach. */
12
+ export declare const RELAY_PROTOCOL_VERSION = 1;
13
+ export interface RelaySummary {
14
+ readonly frames: number;
15
+ readonly answered: number;
16
+ readonly failed: number;
17
+ readonly polls: number;
18
+ /** Why the session ended, in the server's vocabulary where it has one. */
19
+ readonly endReason: string;
20
+ }
21
+ /**
22
+ * Forward one frame to localhost and build the answer. Never throws: every
23
+ * failure is an answer, because the server is holding a visitor's request
24
+ * open until it gets one.
25
+ */
26
+ export declare function answerFrame(opts: RelayOptions, deps: ListenDeps, frame: RelayFrame, signal: AbortSignal): Promise<RelayAnswer>;
27
+ /**
28
+ * The 404 a server without this feature answers, as a sentence.
29
+ *
30
+ * Exported for the spec. A CLI released ahead of the API deploy gets this, so
31
+ * it has to say plainly that the route is missing rather than that the user
32
+ * typed something wrong.
33
+ */
34
+ export declare function explainMissingServer(err: unknown): Error | null;
35
+ /**
36
+ * The relay loop. Exported for the spec, bounded by `deps.maxPolls` there.
37
+ *
38
+ * Frames are answered CONCURRENTLY: each one has its own caller waiting with
39
+ * its own deadline, and a slow tool call must not make a card fetch behind it
40
+ * time out.
41
+ */
42
+ export declare function relay(chatly: ChatlyClient, opts: RelayOptions, deps: ListenDeps): Promise<RelaySummary>;
43
+ //# sourceMappingURL=listen-relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-relay.d.ts","sourceRoot":"","sources":["../../src/commands/listen-relay.ts"],"names":[],"mappings":"AAyDA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzE,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,yBAAyB,EACzB,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,2BAA2B,CAAC;AAEnC,oEAAoE;AACpE,eAAO,MAAM,qBAAqB,QAAc,CAAC;AACjD,qEAAqE;AACrE,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,UAAY,CAAC;AAClD,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAYxC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AA8GD;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,WAAW,CAAC,CAKtB;AAiCD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,GAAG,IAAI,CAe/D;AA0DD;;;;;;GAMG;AACH,wBAAsB,KAAK,CACzB,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,YAAY,CAAC,CAgKvB"}