@chatlychat/cli 0.4.1 → 0.5.2

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 (91) hide show
  1. package/dist/commands/auth.d.ts +1 -1
  2. package/dist/commands/auth.d.ts.map +1 -1
  3. package/dist/commands/auth.js +40 -3
  4. package/dist/commands/auth.js.map +1 -1
  5. package/dist/commands/deliveries.d.ts +18 -0
  6. package/dist/commands/deliveries.d.ts.map +1 -0
  7. package/dist/commands/deliveries.js +46 -0
  8. package/dist/commands/deliveries.js.map +1 -0
  9. package/dist/commands/init.d.ts +29 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +176 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/integrations.d.ts +27 -0
  14. package/dist/commands/integrations.d.ts.map +1 -1
  15. package/dist/commands/integrations.js +145 -4
  16. package/dist/commands/integrations.js.map +1 -1
  17. package/dist/commands/platform-as-code.d.ts +54 -0
  18. package/dist/commands/platform-as-code.d.ts.map +1 -0
  19. package/dist/commands/platform-as-code.js +185 -0
  20. package/dist/commands/platform-as-code.js.map +1 -0
  21. package/dist/commands/workspace-as-code.d.ts +52 -11
  22. package/dist/commands/workspace-as-code.d.ts.map +1 -1
  23. package/dist/commands/workspace-as-code.js +70 -189
  24. package/dist/commands/workspace-as-code.js.map +1 -1
  25. package/dist/help.d.ts +1 -1
  26. package/dist/help.d.ts.map +1 -1
  27. package/dist/help.js +36 -1
  28. package/dist/help.js.map +1 -1
  29. package/dist/init/shared.d.ts +61 -0
  30. package/dist/init/shared.d.ts.map +1 -0
  31. package/dist/init/shared.js +110 -0
  32. package/dist/init/shared.js.map +1 -0
  33. package/dist/init/templates.d.ts +29 -0
  34. package/dist/init/templates.d.ts.map +1 -0
  35. package/dist/init/templates.js +362 -0
  36. package/dist/init/templates.js.map +1 -0
  37. package/dist/pac/platform-document.d.ts +65 -0
  38. package/dist/pac/platform-document.d.ts.map +1 -0
  39. package/dist/pac/platform-document.js +149 -0
  40. package/dist/pac/platform-document.js.map +1 -0
  41. package/dist/pac/platform-live.d.ts +65 -0
  42. package/dist/pac/platform-live.d.ts.map +1 -0
  43. package/dist/pac/platform-live.js +263 -0
  44. package/dist/pac/platform-live.js.map +1 -0
  45. package/dist/pac/platform-resources.d.ts +141 -0
  46. package/dist/pac/platform-resources.d.ts.map +1 -0
  47. package/dist/pac/platform-resources.js +509 -0
  48. package/dist/pac/platform-resources.js.map +1 -0
  49. package/dist/run.d.ts.map +1 -1
  50. package/dist/run.js +26 -2
  51. package/dist/run.js.map +1 -1
  52. package/dist/wac/apply.d.ts.map +1 -1
  53. package/dist/wac/apply.js +56 -16
  54. package/dist/wac/apply.js.map +1 -1
  55. package/dist/wac/diff-warnings.d.ts +35 -0
  56. package/dist/wac/diff-warnings.d.ts.map +1 -0
  57. package/dist/wac/diff-warnings.js +88 -0
  58. package/dist/wac/diff-warnings.js.map +1 -0
  59. package/dist/wac/diff.d.ts +40 -70
  60. package/dist/wac/diff.d.ts.map +1 -1
  61. package/dist/wac/diff.js +114 -91
  62. package/dist/wac/diff.js.map +1 -1
  63. package/dist/wac/document-key.d.ts +30 -0
  64. package/dist/wac/document-key.d.ts.map +1 -0
  65. package/dist/wac/document-key.js +87 -0
  66. package/dist/wac/document-key.js.map +1 -0
  67. package/dist/wac/document.d.ts +75 -65
  68. package/dist/wac/document.d.ts.map +1 -1
  69. package/dist/wac/document.js +60 -46
  70. package/dist/wac/document.js.map +1 -1
  71. package/dist/wac/execute.d.ts +25 -0
  72. package/dist/wac/execute.d.ts.map +1 -0
  73. package/dist/wac/execute.js +126 -0
  74. package/dist/wac/execute.js.map +1 -0
  75. package/dist/wac/load.d.ts +17 -0
  76. package/dist/wac/load.d.ts.map +1 -0
  77. package/dist/wac/load.js +92 -0
  78. package/dist/wac/load.js.map +1 -0
  79. package/dist/wac/resource-def.d.ts +89 -1
  80. package/dist/wac/resource-def.d.ts.map +1 -1
  81. package/dist/wac/resource-def.js +4 -0
  82. package/dist/wac/resource-def.js.map +1 -1
  83. package/dist/wac/resources.d.ts +9 -11
  84. package/dist/wac/resources.d.ts.map +1 -1
  85. package/dist/wac/resources.js +93 -27
  86. package/dist/wac/resources.js.map +1 -1
  87. package/dist/wac/values.d.ts +20 -0
  88. package/dist/wac/values.d.ts.map +1 -1
  89. package/dist/wac/values.js +46 -0
  90. package/dist/wac/values.js.map +1 -1
  91. package/package.json +4 -3
@@ -17,5 +17,5 @@ export declare function logout(): number;
17
17
  * turns a mis-scoped key into an obvious error here rather than a 403 from an
18
18
  * unrelated endpoint three days into an integration.
19
19
  */
20
- export declare function whoami(chatly: ChatlyClient, json: boolean): Promise<number>;
20
+ export declare function whoami(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
21
21
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAO,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAIlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;GAOG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB7D;AAED,wBAAgB,MAAM,IAAI,MAAM,CAM/B;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CASjF"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;GAOG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB7D;AAED,wBAAgB,MAAM,IAAI,MAAM,CAM/B;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAepF"}
@@ -1,6 +1,6 @@
1
1
  import { createInterface } from 'node:readline/promises';
2
- import { str } from '../args.js';
3
- import { CONFIG_PATH, readConfig, writeConfig } from '../config.js';
2
+ import { bool, str } from '../args.js';
3
+ import { CONFIG_PATH, readConfig, resolveCredentials, writeConfig } from '../config.js';
4
4
  import { printJson } from '../output.js';
5
5
  /**
6
6
  * Store a key.
@@ -43,7 +43,13 @@ export function logout() {
43
43
  * turns a mis-scoped key into an obvious error here rather than a 403 from an
44
44
  * unrelated endpoint three days into an integration.
45
45
  */
46
- export async function whoami(chatly, json) {
46
+ export async function whoami(chatly, args) {
47
+ const json = bool(args.flags, 'json');
48
+ // Resolved the same way run.ts resolved the key this client holds.
49
+ const key = resolveCredentials(str(args.flags, 'key'), undefined).apiKey;
50
+ if (key.startsWith('cp_')) {
51
+ return platformWhoami(chatly, json);
52
+ }
47
53
  const me = await chatly.whoami();
48
54
  if (json) {
49
55
  printJson(me);
@@ -53,4 +59,35 @@ export async function whoami(chatly, json) {
53
59
  process.stdout.write(`scopes ${me.scopes.length === 0 ? '(none)' : me.scopes.join(', ')}\n`);
54
60
  return 0;
55
61
  }
62
+ /**
63
+ * A `cp_` platform credential: `GET /v1/platform/whoami`, with the CHT-S01
64
+ * guardrails an operator set on it. They are shown because a credential can
65
+ * read but never change them, and "why was my login link refused" or "why
66
+ * a 403 from CI" is otherwise a support ticket.
67
+ */
68
+ async function platformWhoami(chatly, json) {
69
+ const me = await chatly.request('GET', '/v1/platform/whoami');
70
+ if (json) {
71
+ printJson(me);
72
+ return 0;
73
+ }
74
+ const restriction = me.restrictedToWorkspaceIds !== null
75
+ ? `${String(me.restrictedToWorkspaceIds.length)} workspace(s)`
76
+ : me.restrictedToSlugPrefix !== null
77
+ ? `slugs starting "${me.restrictedToSlugPrefix}"`
78
+ : 'none';
79
+ const lines = [
80
+ ['platform', `${me.name} (${me.prefix}…)`],
81
+ ['scopes', me.scopes.length === 0 ? '(none)' : me.scopes.join(', ')],
82
+ ['expires', me.expiresAt ?? 'never'],
83
+ ['restricted', restriction],
84
+ ['login links', me.loginLinks ?? 'enabled'],
85
+ ['ip allow', me.ipAllowlist == null ? 'any address' : me.ipAllowlist.join(', ')],
86
+ ['contact', me.contactEmail ?? '(none — nobody is warned before expiry)'],
87
+ ];
88
+ for (const [label, value] of lines) {
89
+ process.stdout.write(`${label.padEnd(13)}${value}\n`);
90
+ }
91
+ return 0;
92
+ }
56
93
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,GAAG,EAAmB,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAgB;IAC1C,IAAI,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACjC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oFAAoF;YAClF,mFAAmF,CACtF,CAAC;QACF,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4EAA4E,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ;YACjG,uFAAuF,CAC1F,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,WAAW,+CAA+C,CAAC,CAAC;IAC7F,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,OAAO,QAAQ,CAAC,MAAM,CAAC;IACvB,WAAW,CAAC,QAAQ,CAAC,CAAC;IACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,WAAW,KAAK,CAAC,CAAC;IACtE,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,MAAoB,EAAE,IAAa;IAC9D,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IACjC,IAAI,IAAI,EAAE,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,WAAW,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjG,OAAO,CAAC,CAAC;AACX,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `chatly deliveries list | show | replay` — an external agent's delivery log,
3
+ * addressed by the `eventId` its endpoint saw.
4
+ *
5
+ * The same three routes as `chatly bots deliveries | delivery | redeliver`,
6
+ * under the name people reach for when debugging an agent rather than managing
7
+ * a bot, and with the bot as a flag because the thing you have in hand is an
8
+ * event id from your own logs. The delivery id IS the `eventId` (there is no
9
+ * second column), so `show` and `replay` take it verbatim.
10
+ *
11
+ * `replay` re-sends the ORIGINAL body with the ORIGINAL eventId, so an agent
12
+ * that dedupes on it sees a repeat. Bodies are kept 7 days; a purged one is
13
+ * refused by the server with a message saying so.
14
+ */
15
+ import { type ParsedArgs } from '../args.js';
16
+ import type { ChatlyClient } from '@chatlychat/sdk-node';
17
+ export declare function deliveries(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
18
+ //# sourceMappingURL=deliveries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliveries.d.ts","sourceRoot":"","sources":["../../src/commands/deliveries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAO,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAGlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAYzD,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCxF"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `chatly deliveries list | show | replay` — an external agent's delivery log,
3
+ * addressed by the `eventId` its endpoint saw.
4
+ *
5
+ * The same three routes as `chatly bots deliveries | delivery | redeliver`,
6
+ * under the name people reach for when debugging an agent rather than managing
7
+ * a bot, and with the bot as a flag because the thing you have in hand is an
8
+ * event id from your own logs. The delivery id IS the `eventId` (there is no
9
+ * second column), so `show` and `replay` take it verbatim.
10
+ *
11
+ * `replay` re-sends the ORIGINAL body with the ORIGINAL eventId, so an agent
12
+ * that dedupes on it sees a repeat. Bodies are kept 7 days; a purged one is
13
+ * refused by the server with a message saying so.
14
+ */
15
+ import { str } from '../args.js';
16
+ import { need, pagedVia, renderList, showObject } from './common.js';
17
+ function botId(args) {
18
+ const bot = str(args.flags, 'bot');
19
+ if (bot === undefined) {
20
+ throw new Error('Pass the external agent with --bot <bot-id>.');
21
+ }
22
+ return encodeURIComponent(bot);
23
+ }
24
+ const botPath = (args) => `/v1/bots/${botId(args)}/deliveries`;
25
+ export async function deliveries(chatly, args) {
26
+ const sub = args.path[1] ?? 'list';
27
+ switch (sub) {
28
+ case 'list': {
29
+ const status = str(args.flags, 'status');
30
+ const pages = pagedVia(chatly, botPath(args), status === undefined ? {} : { status });
31
+ return renderList(args, ['id', 'status', 'attempt', 'httpStatus', 'durationMs', 'createdAt', 'error'], pages.onePage, pages.everyPage);
32
+ }
33
+ case 'show':
34
+ // With `payload` (or `{ purged: true }`) and the agent's `replies`.
35
+ return showObject(await chatly.request('GET', `${botPath(args)}/${encodeURIComponent(need(args, 2, 'an event id'))}`));
36
+ case 'replay': {
37
+ // A literal path, so `check-api-route-callers` can see this is the
38
+ // route's caller.
39
+ const eventId = encodeURIComponent(need(args, 2, 'an event id'));
40
+ return showObject(await chatly.request('POST', `/v1/bots/${botId(args)}/deliveries/${eventId}/replay`));
41
+ }
42
+ default:
43
+ throw new Error(`Unknown deliveries command "${sub}". Try: list, show, replay.`);
44
+ }
45
+ }
46
+ //# sourceMappingURL=deliveries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/commands/deliveries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,GAAG,EAAmB,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIrE,SAAS,KAAK,CAAC,IAAgB;IAC7B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACnC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,OAAO,GAAG,CAAC,IAAgB,EAAU,EAAE,CAAC,YAAY,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC;AAEnF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAoB,EAAE,IAAgB;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IACnC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YACtF,OAAO,UAAU,CACf,IAAI,EACJ,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,CAAC,EAC7E,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,SAAS,CAChB,CAAC;QACJ,CAAC;QACD,KAAK,MAAM;YACT,oEAAoE;YACpE,OAAO,UAAU,CACf,MAAM,MAAM,CAAC,OAAO,CAClB,KAAK,EACL,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC,EAAE,CACvE,CACF,CAAC;QACJ,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,mEAAmE;YACnE,kBAAkB;YAClB,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC;YACjE,OAAO,UAAU,CACf,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,KAAK,CAAC,IAAI,CAAC,eAAe,OAAO,SAAS,CAAC,CACrF,CAAC;QACJ,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,6BAA6B,CAAC,CAAC;IACrF,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type ParsedArgs } from '../args.js';
2
+ import { type Framework } from '../init/templates.js';
3
+ import type { EmbedBlock, ScaffoldFile } from '../init/shared.js';
4
+ import type { ChatlyClient } from '@chatlychat/sdk-node';
5
+ interface ChannelRow {
6
+ readonly type?: unknown;
7
+ readonly name?: unknown;
8
+ readonly publicId?: unknown;
9
+ readonly enabled?: unknown;
10
+ readonly embed?: unknown;
11
+ }
12
+ /** The web channel to scaffold for, or a sentence saying why there is none. */
13
+ export declare function pickChannel(rows: ReadonlyArray<ChannelRow>, publicId: string | undefined): {
14
+ publicId: string;
15
+ embed: EmbedBlock;
16
+ } | {
17
+ error: string;
18
+ };
19
+ /** What to do next, in the order it has to be done. */
20
+ export declare function nextSteps(framework: Framework, identity: 'hmac' | 'atlas'): ReadonlyArray<string>;
21
+ /** Write every file, or none: existing targets are checked first. */
22
+ export declare function writeAll(dir: string, files: ReadonlyArray<ScaffoldFile>, force: boolean): {
23
+ written: ReadonlyArray<string>;
24
+ } | {
25
+ existing: ReadonlyArray<string>;
26
+ };
27
+ export declare function init(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
28
+ export {};
29
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtF,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAMzD,UAAU,UAAU;IAClB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CACzB,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,EAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CA8B7D;AAyBD,uDAAuD;AACvD,wBAAgB,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAmBjG;AAED,qEAAqE;AACrE,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,EAClC,KAAK,EAAE,OAAO,GACb;IAAE,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,CAW1E;AAED,wBAAsB,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAiFlF"}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * `chatly init <framework>` — the install wizard (CHT-F02).
3
+ *
4
+ * Writes the identity route, the launcher, the CSP sources and the privacy
5
+ * table for one web channel into an existing project, from that channel's own
6
+ * `embed` block. One read, `GET /v1/channels`, with the caller's `ck_` key —
7
+ * the route the dashboard's install screen reads, so the CSP and the storage
8
+ * table come from the server rather than from a list in this CLI that could
9
+ * drift from it.
10
+ *
11
+ * ═══ WHAT IT WILL NOT DO ══════════════════════════════════════════════════
12
+ *
13
+ * - **Overwrite a file.** Every target is checked before anything is
14
+ * written, and one existing file stops the whole run with the list, unless
15
+ * `--force`. Half a scaffold is worse than none: the summary would name
16
+ * files that are not the ones on disk.
17
+ * - **Edit the integrator's own files.** `next.config`, the root layout and
18
+ * the Express app are THEIRS; the summary says which line goes where.
19
+ * A wizard that rewrites a config it did not create is a wizard nobody
20
+ * re-runs.
21
+ * - **Write a secret.** `CHATLY_IDV_SECRET` is named, never fetched: the
22
+ * channel read redacts it, and a secret in a scaffolded file is a secret
23
+ * in git.
24
+ * - **Generate the e2e fixture.** CHT-F02 asks for one built on CHT-D09's
25
+ * `@chatlychat/widget-testing`, which is not on npm. A file importing it
26
+ * would scaffold a project that does not install, so the summary says so
27
+ * instead.
28
+ */
29
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
30
+ import { dirname, join, resolve } from 'node:path';
31
+ import { bool, str } from '../args.js';
32
+ import { FRAMEWORKS, NO_ATLAS, scaffold } from '../init/templates.js';
33
+ import { printJson } from '../output.js';
34
+ const USAGE = `Usage: chatly init <${FRAMEWORKS.join('|')}> [--public-id <id>] [--identity hmac|atlas] ` +
35
+ '[--dir <path>] [--dry-run] [--force]\n';
36
+ /** The web channel to scaffold for, or a sentence saying why there is none. */
37
+ export function pickChannel(rows, publicId) {
38
+ const web = rows.filter((r) => r.type === 'web' && typeof r.publicId === 'string');
39
+ const chosen = publicId === undefined
40
+ ? web.length === 1
41
+ ? web[0]
42
+ : undefined
43
+ : web.find((r) => r.publicId === publicId);
44
+ if (chosen === undefined) {
45
+ if (publicId !== undefined) {
46
+ return { error: `No web channel with publicId "${publicId}" in this workspace.` };
47
+ }
48
+ if (web.length === 0) {
49
+ return { error: 'This workspace has no web channel. Create one in the dashboard first.' };
50
+ }
51
+ return {
52
+ error: `This workspace has ${String(web.length)} web channels; name one with --public-id:\n` +
53
+ web.map((r) => ` ${String(r.publicId)} ${String(r.name ?? '')}`).join('\n'),
54
+ };
55
+ }
56
+ const embed = chosen.embed;
57
+ if (embed === null || embed === undefined) {
58
+ return {
59
+ error: `Channel ${String(chosen.publicId)} has no embed block: this deployment has not been told ` +
60
+ 'where its widget is served from. Nothing was written.',
61
+ };
62
+ }
63
+ return { publicId: String(chosen.publicId), embed };
64
+ }
65
+ /** Where each framework's identity stub lives, and what to do with it. */
66
+ const WIRE_IDENTITY = {
67
+ next: 'Wire `currentUserId` in app/api/chatly/identity/route.ts to your auth. Until you do, every visitor is anonymous.',
68
+ remix: 'Wire `currentUserId` in app/routes/api.chatly.identity.ts to your session. Until you do, every visitor is anonymous. ' +
69
+ 'On React Router v7, app/routes.ts must use flatRoutes() from @react-router/fs-routes for the file to be routed.',
70
+ astro: 'Wire `currentUserId` in src/pages/api/chatly/identity.ts to your auth. Until you do, every visitor is anonymous. ' +
71
+ 'The endpoint is `prerender = false`, so the site needs a server adapter (e.g. @astrojs/node).',
72
+ express: "Mount the handler: app.get('/api/chatly/identity', chatlyIdentity((req) => req.user?.id ?? null)).",
73
+ };
74
+ /** Where the widget goes on the page. */
75
+ const RENDER = {
76
+ next: 'Render <Chatly /> from components/chatly.tsx once, in app/layout.tsx. Install @chatlychat/widget-react.',
77
+ remix: 'Render <Chatly /> from app/components/chatly.tsx once, in app/root.tsx. Install @chatlychat/widget-react.',
78
+ astro: 'Render <Chatly /> from src/components/Chatly.astro once, before </body> in your layout.',
79
+ express: 'Paste chatly/snippet.html before </body> on every page that shows the widget.',
80
+ };
81
+ /** What to do next, in the order it has to be done. */
82
+ export function nextSteps(framework, identity) {
83
+ const steps = [];
84
+ if (identity === 'hmac') {
85
+ steps.push(WIRE_IDENTITY[framework], "Set CHATLY_IDV_SECRET on the server — the channel's identity-verification secret, from the dashboard. Never in a client bundle.");
86
+ }
87
+ else {
88
+ steps.push('Set the channel to identityVerification.mode "atlas" with your instance id, and create the `chatly` JWT template in Atlas.');
89
+ }
90
+ steps.push(RENDER[framework], 'Merge chatly.csp.mjs into your Content-Security-Policy with withChatly(…) — do not add a second CSP header.', 'Copy the table in CHATLY-PRIVACY.md into your privacy policy.', 'No e2e fixture was generated: @chatlychat/widget-testing (CHT-D09) is not on npm yet.');
91
+ return steps;
92
+ }
93
+ /** Write every file, or none: existing targets are checked first. */
94
+ export function writeAll(dir, files, force) {
95
+ const existing = files.filter((f) => existsSync(join(dir, f.path))).map((f) => f.path);
96
+ if (existing.length > 0 && !force) {
97
+ return { existing };
98
+ }
99
+ for (const file of files) {
100
+ const target = join(dir, file.path);
101
+ mkdirSync(dirname(target), { recursive: true });
102
+ writeFileSync(target, file.contents);
103
+ }
104
+ return { written: files.map((f) => f.path) };
105
+ }
106
+ export async function init(chatly, args) {
107
+ const framework = args.path[1];
108
+ if (framework === undefined) {
109
+ process.stderr.write(USAGE);
110
+ return 2;
111
+ }
112
+ if (!FRAMEWORKS.includes(framework)) {
113
+ process.stderr.write(`Unknown framework "${framework}".\n${USAGE}`);
114
+ return 2;
115
+ }
116
+ const fw = framework;
117
+ const identity = str(args.flags, 'identity') ?? 'hmac';
118
+ if (identity !== 'hmac' && identity !== 'atlas') {
119
+ process.stderr.write(`--identity must be hmac or atlas, not "${identity}".\n`);
120
+ return 2;
121
+ }
122
+ if (NO_ATLAS.includes(fw) && identity === 'atlas') {
123
+ process.stderr.write(`\`chatly init ${fw} --identity atlas\` is not generated: an Atlas token is minted in the ` +
124
+ "browser, so there is no server half. Call Chatly('identify', { getToken }) from your page — " +
125
+ 'https://chatlychat.com/guides/identity-verification#passing-the-token\n');
126
+ return 2;
127
+ }
128
+ const rows = await chatly.request('GET', '/v1/channels');
129
+ const picked = pickChannel(Array.isArray(rows) ? rows : [], str(args.flags, 'public-id'));
130
+ if ('error' in picked) {
131
+ process.stderr.write(`${picked.error}\n`);
132
+ return 1;
133
+ }
134
+ const files = scaffold(fw, {
135
+ publicId: picked.publicId,
136
+ embed: picked.embed,
137
+ identity,
138
+ generatedOn: new Date().toISOString().slice(0, 10),
139
+ });
140
+ const steps = nextSteps(fw, identity);
141
+ const dir = resolve(str(args.flags, 'dir') ?? '.');
142
+ if (bool(args.flags, 'dry-run')) {
143
+ if (bool(args.flags, 'json')) {
144
+ printJson({ publicId: picked.publicId, files, nextSteps: steps, written: false });
145
+ }
146
+ else {
147
+ for (const file of files) {
148
+ process.stdout.write(`── ${file.path} ──\n${file.contents}\n`);
149
+ }
150
+ process.stdout.write(`(dry run: nothing written to ${dir})\n`);
151
+ }
152
+ return 0;
153
+ }
154
+ const result = writeAll(dir, files, bool(args.flags, 'force'));
155
+ if ('existing' in result) {
156
+ process.stderr.write(`Nothing written — these already exist in ${dir}:\n` +
157
+ result.existing.map((p) => ` ${p}\n`).join('') +
158
+ 'Move them, or pass --force to overwrite. --dry-run prints what would be written.\n');
159
+ return 1;
160
+ }
161
+ if (bool(args.flags, 'json')) {
162
+ printJson({
163
+ publicId: picked.publicId,
164
+ files: result.written,
165
+ nextSteps: steps,
166
+ written: true,
167
+ });
168
+ return 0;
169
+ }
170
+ process.stdout.write(`Scaffolded Chatly for web channel ${picked.publicId} in ${dir}:\n` +
171
+ result.written.map((p) => ` + ${p}\n`).join('') +
172
+ '\nNext:\n' +
173
+ steps.map((s, i) => ` ${String(i + 1)}. ${s}\n`).join(''));
174
+ return 0;
175
+ }
176
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -11,5 +11,32 @@ import type { ChatlyClient } from '@chatlychat/sdk-node';
11
11
  export declare function webhooks(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
12
12
  export declare function customerCards(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
13
13
  export declare function docsSources(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
14
+ /**
15
+ * `chatly privacy …` — the data-rights executor's own commands (CHT-X03).
16
+ *
17
+ * ═══ WHY THE EXECUTOR KEY NEEDS A CLI COMMAND AT ALL ══════════════════════
18
+ *
19
+ * Because without one, `POST /v1/privacy/executor-key` had no caller.
20
+ * `check-api-route-callers` flagged it, and CLAUDE.md §5 is explicit that the
21
+ * fix is a caller and NOT an allow-list entry: an endpoint nothing can reach
22
+ * is a feature nobody can use, which is how the Gmail and Outlook mailbox
23
+ * sign-in shipped complete and unreachable.
24
+ *
25
+ * It is a good fit rather than a box-tick. The route exists to be run ONCE,
26
+ * by an operator, while they are filling in a privacy platform's executor
27
+ * registration form — exactly the shape of thing a CLI is for, and exactly
28
+ * the shape of thing that does not deserve a dashboard screen. The response
29
+ * is three fields a human pastes.
30
+ *
31
+ * ═══ WHY IT PRINTS A WARNING AND NOT JUST THE OBJECT ══════════════════════
32
+ *
33
+ * The secret is shown ONCE. `showObject` on a shown-once credential is the
34
+ * ergonomic trap every key-minting surface here has: the value scrolls past
35
+ * in a terminal somebody then closes. So the human-readable form labels it,
36
+ * says it will not be shown again, and names the sealed alternative — while
37
+ * `--json` stays exactly the API response, because that is what a script
38
+ * pipes into `jq`.
39
+ */
40
+ export declare function privacy(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
14
41
  export declare function channels(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
15
42
  //# sourceMappingURL=integrations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../src/commands/integrations.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAuHtF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmD3F;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmFzF;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA2DtF"}
1
+ {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../src/commands/integrations.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAYxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AASzD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmItF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmD3F;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmFzF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA0FrF;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAuGtF"}
@@ -8,19 +8,38 @@
8
8
  */
9
9
  import { bool, str } from '../args.js';
10
10
  import { itemsOf, printJson, printTable } from '../output.js';
11
- import { bodyFrom, need, renderList, showObject, showRows } from './common.js';
11
+ import { bodyFrom, need, queryFrom, renderList, showObject, showRows, } from './common.js';
12
+ /** The delivery log's filters: `--trace`, `--endpoint`, `--status`. */
13
+ const DELIVERY_FILTERS = [
14
+ ['trace', 'trace'],
15
+ ['endpoint', 'endpointId'],
16
+ ['status', 'status'],
17
+ ];
12
18
  export async function webhooks(chatly, args) {
13
19
  const sub = args.path[1] ?? 'deliveries';
14
20
  switch (sub) {
15
21
  case 'deliveries':
16
- return renderList(args, ['id', 'event', 'status', 'responseStatus', 'attempts', 'createdAt'],
22
+ return renderList(args,
23
+ /*
24
+ * The API's own field names. This read `event`, `responseStatus` and
25
+ * `attempts` — none of which `GET /v1/webhooks/deliveries` sends — so
26
+ * three of six columns printed blank on every row. `trace` is the
27
+ * `X-Chatly-Trace` each attempt carried (CHT-SI13).
28
+ */
29
+ ['id', 'eventType', 'status', 'httpStatus', 'attempt', 'trace', 'createdAt'],
17
30
  /*
18
31
  * `deliveries`, not `list`. On the extended surface `webhooks.list`
19
32
  * is the SUBSCRIPTION list (`GET /v1/webhooks`) and the delivery log
20
33
  * is `deliveries`/`deliveriesAll` — this called `list`/`listAll` and
21
34
  * would have rendered subscriptions under delivery column headings.
22
35
  */
23
- (q) => chatly.webhooks.deliveries(q), () => chatly.webhooks.deliveriesAll());
36
+ /*
37
+ * `--trace <ulid>` answers "what did Chatly send because of this
38
+ * chain?" from the id a partner product logged. `--endpoint` and
39
+ * `--status` are the two filters the route already had and this
40
+ * command never forwarded.
41
+ */
42
+ (q) => chatly.webhooks.deliveries({ ...q, ...queryFrom(args, DELIVERY_FILTERS) }), () => chatly.webhooks.deliveriesAll(queryFrom(args, DELIVERY_FILTERS)));
24
43
  case 'delivery':
25
44
  return showObject(await chatly.webhooks.delivery(need(args, 2, 'a delivery id')));
26
45
  case 'redeliver':
@@ -206,6 +225,93 @@ export async function docsSources(chatly, args) {
206
225
  throw new Error(`Unknown: docs-sources ${sub}`);
207
226
  }
208
227
  }
228
+ /**
229
+ * `chatly privacy …` — the data-rights executor's own commands (CHT-X03).
230
+ *
231
+ * ═══ WHY THE EXECUTOR KEY NEEDS A CLI COMMAND AT ALL ══════════════════════
232
+ *
233
+ * Because without one, `POST /v1/privacy/executor-key` had no caller.
234
+ * `check-api-route-callers` flagged it, and CLAUDE.md §5 is explicit that the
235
+ * fix is a caller and NOT an allow-list entry: an endpoint nothing can reach
236
+ * is a feature nobody can use, which is how the Gmail and Outlook mailbox
237
+ * sign-in shipped complete and unreachable.
238
+ *
239
+ * It is a good fit rather than a box-tick. The route exists to be run ONCE,
240
+ * by an operator, while they are filling in a privacy platform's executor
241
+ * registration form — exactly the shape of thing a CLI is for, and exactly
242
+ * the shape of thing that does not deserve a dashboard screen. The response
243
+ * is three fields a human pastes.
244
+ *
245
+ * ═══ WHY IT PRINTS A WARNING AND NOT JUST THE OBJECT ══════════════════════
246
+ *
247
+ * The secret is shown ONCE. `showObject` on a shown-once credential is the
248
+ * ergonomic trap every key-minting surface here has: the value scrolls past
249
+ * in a terminal somebody then closes. So the human-readable form labels it,
250
+ * says it will not be shown again, and names the sealed alternative — while
251
+ * `--json` stays exactly the API response, because that is what a script
252
+ * pipes into `jq`.
253
+ */
254
+ export async function privacy(chatly, args) {
255
+ const sub = args.path[1] ?? 'help';
256
+ switch (sub) {
257
+ case 'executor-key': {
258
+ /*
259
+ * `@RequireIdempotencyKey()` on the route, so a key is MANDATORY: a
260
+ * retried POST without one answers 428 in production. The SDK mints one
261
+ * per write, which is why this is a plain `request` and not a special
262
+ * case — see `packages/sdk-spec/CONTRACT.md`.
263
+ */
264
+ const res = await chatly.request('POST', '/v1/privacy/executor-key');
265
+ if (bool(args.flags, 'json')) {
266
+ return showObject(res);
267
+ }
268
+ process.stdout.write(`Executor URL ${res.executorUrl}\n` +
269
+ `Workspace ${res.workspaceId}\n` +
270
+ `Scopes ${res.scopes.join(', ')}\n` +
271
+ `Key id ${res.id}\n`);
272
+ if (res.token !== null) {
273
+ process.stdout.write(`Secret ${res.token}\n` +
274
+ '\nThe secret is shown ONCE and is not recoverable. Register the three\n' +
275
+ 'values above with your privacy platform now.\n' +
276
+ 'To receive it encrypted instead, send an X-Secret-Recipient header\n' +
277
+ '(see /docs/api-rest, "Never printing a key").\n');
278
+ }
279
+ else {
280
+ process.stdout.write(`Sealed secret ${res.sealedSecret ?? '(none)'}\n` +
281
+ `Sealed scheme ${res.sealedScheme ?? '(none)'}\n` +
282
+ '\nSealed to the public key you sent. Decrypt it with the matching\n' +
283
+ 'private key; refuse a scheme you do not recognise rather than\n' +
284
+ 'guessing from the length.\n');
285
+ }
286
+ return 0;
287
+ }
288
+ case 'lookup':
289
+ /*
290
+ * Resolve a subject. A POST rather than a GET because an OIDC issuer is
291
+ * a URL and a subject claim is arbitrary text, neither of which survives
292
+ * a query string reliably — the route's own header makes the argument.
293
+ */
294
+ return showObject(await chatly.request('POST', '/v1/privacy/lookup', {
295
+ body: bodyFrom(args, 'the subject (issuer/subject, externalId or emails)'),
296
+ }));
297
+ case 'request':
298
+ return showObject(await chatly.request('POST', '/v1/privacy/requests', {
299
+ body: bodyFrom(args, 'the request (caseId, type and subject)'),
300
+ }));
301
+ case 'status':
302
+ return showObject(await chatly.request('GET', `/v1/privacy/requests/${encodeURIComponent(need(args, 2, 'a request id'))}`));
303
+ case 'cancel':
304
+ return showObject(await chatly.request('DELETE', `/v1/privacy/requests/${encodeURIComponent(need(args, 2, 'a request id'))}`));
305
+ default:
306
+ process.stderr.write('Usage: chatly privacy <executor-key|lookup|request|status|cancel>\n' +
307
+ ' executor-key mint the credential a privacy platform registers\n' +
308
+ ' lookup --body <json> resolve a person to a stable subjectId\n' +
309
+ ' request --body <json> file a data-rights request\n' +
310
+ ' status <id> read one back\n' +
311
+ ' cancel <id> withdraw one that has not run\n');
312
+ return 2;
313
+ }
314
+ }
209
315
  export async function channels(chatly, args) {
210
316
  const sub = args.path[1] ?? 'list';
211
317
  switch (sub) {
@@ -231,6 +337,33 @@ export async function channels(chatly, args) {
231
337
  return showObject(await chatly.request('POST', '/v1/channels', { body: bodyFrom(args, 'the channel') }));
232
338
  case 'update':
233
339
  return showObject(await chatly.request('PATCH', `/v1/channels/${encodeURIComponent(need(args, 2, 'a channel id'))}`, { body: bodyFrom(args, 'the fields to change') }));
340
+ case 'set-key':
341
+ // Adopt an existing channel into config-as-code: `chatly.json` matches channels on `key`.
342
+ return showObject(await chatly.request('PATCH', `/v1/channels/${encodeURIComponent(need(args, 2, 'a channel id'))}`, { body: { key: need(args, 3, 'a key (^[a-z][a-z0-9-]{0,63}$)') } }));
343
+ case 'brand': {
344
+ /*
345
+ * A DTCG `brand.json` (CHT-SI14). With `--url` or `--file`/`--body`
346
+ * it APPLIES the document; with neither it reads back the resolved
347
+ * mapping — each role's value and which layer it came from, because
348
+ * `brand.json` ranks below the workspace theme and the channel's own
349
+ * settings and "why didn't my accent change?" is answered by `source`.
350
+ */
351
+ const path = `/v1/channels/${encodeURIComponent(need(args, 2, 'a channel id'))}/brand`;
352
+ const url = str(args.flags, 'url');
353
+ const hasDocument = str(args.flags, 'file') !== undefined || str(args.flags, 'body') !== undefined;
354
+ if (url !== undefined && hasDocument) {
355
+ throw new Error('Pass --url or a document (--file/--body), not both.');
356
+ }
357
+ if (url !== undefined) {
358
+ return showObject(await chatly.request('PUT', path, { body: { brandUrl: url } }));
359
+ }
360
+ if (hasDocument) {
361
+ return showObject(await chatly.request('PUT', path, {
362
+ body: { brand: bodyFrom(args, 'the brand document') },
363
+ }));
364
+ }
365
+ return showObject(await chatly.request('GET', path));
366
+ }
234
367
  case 'rotate-secret':
235
368
  // The inbound webhook signing secret, shown once.
236
369
  return showObject(await chatly.request('POST', `/v1/channels/${encodeURIComponent(need(args, 2, 'a channel id'))}/secret/rotate`));
@@ -246,7 +379,15 @@ export async function channels(chatly, args) {
246
379
  * the only field that distinguishes "locked to these sites" from "open
247
380
  * to every site".
248
381
  */
249
- printTable(itemsOf(res), ['id', 'publicId', 'type', 'name', 'status', 'originsRestricted']);
382
+ printTable(itemsOf(res), [
383
+ 'id',
384
+ 'key',
385
+ 'publicId',
386
+ 'type',
387
+ 'name',
388
+ 'status',
389
+ 'originsRestricted',
390
+ ]);
250
391
  return 0;
251
392
  }
252
393
  default: