@chatlychat/cli 0.1.3 → 0.1.5

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 (73) hide show
  1. package/dist/commands/ai.d.ts +4 -0
  2. package/dist/commands/ai.d.ts.map +1 -0
  3. package/dist/commands/ai.js +149 -0
  4. package/dist/commands/ai.js.map +1 -0
  5. package/dist/commands/common.d.ts +79 -0
  6. package/dist/commands/common.d.ts.map +1 -0
  7. package/dist/commands/common.js +194 -0
  8. package/dist/commands/common.js.map +1 -0
  9. package/dist/commands/config.d.ts +40 -0
  10. package/dist/commands/config.d.ts.map +1 -0
  11. package/dist/commands/config.js +336 -0
  12. package/dist/commands/config.js.map +1 -0
  13. package/dist/commands/insights.d.ts +5 -0
  14. package/dist/commands/insights.d.ts.map +1 -0
  15. package/dist/commands/insights.js +277 -0
  16. package/dist/commands/insights.js.map +1 -0
  17. package/dist/commands/integrations.d.ts +15 -0
  18. package/dist/commands/integrations.d.ts.map +1 -0
  19. package/dist/commands/integrations.js +256 -0
  20. package/dist/commands/integrations.js.map +1 -0
  21. package/dist/commands/resources.d.ts +19 -7
  22. package/dist/commands/resources.d.ts.map +1 -1
  23. package/dist/commands/resources.js +100 -191
  24. package/dist/commands/resources.js.map +1 -1
  25. package/dist/commands/workspace-as-code.d.ts +27 -0
  26. package/dist/commands/workspace-as-code.d.ts.map +1 -0
  27. package/dist/commands/workspace-as-code.js +377 -0
  28. package/dist/commands/workspace-as-code.js.map +1 -0
  29. package/dist/help.d.ts +25 -1
  30. package/dist/help.d.ts.map +1 -1
  31. package/dist/help.js +104 -8
  32. package/dist/help.js.map +1 -1
  33. package/dist/run.d.ts +17 -0
  34. package/dist/run.d.ts.map +1 -1
  35. package/dist/run.js +88 -41
  36. package/dist/run.js.map +1 -1
  37. package/dist/wac/__tests__/wac-fixtures.d.ts +20 -0
  38. package/dist/wac/__tests__/wac-fixtures.d.ts.map +1 -0
  39. package/dist/wac/__tests__/wac-fixtures.js +38 -0
  40. package/dist/wac/__tests__/wac-fixtures.js.map +1 -0
  41. package/dist/wac/apply.d.ts +113 -0
  42. package/dist/wac/apply.d.ts.map +1 -0
  43. package/dist/wac/apply.js +265 -0
  44. package/dist/wac/apply.js.map +1 -0
  45. package/dist/wac/diff.d.ts +137 -0
  46. package/dist/wac/diff.d.ts.map +1 -0
  47. package/dist/wac/diff.js +413 -0
  48. package/dist/wac/diff.js.map +1 -0
  49. package/dist/wac/document.d.ts +110 -0
  50. package/dist/wac/document.d.ts.map +1 -0
  51. package/dist/wac/document.js +429 -0
  52. package/dist/wac/document.js.map +1 -0
  53. package/dist/wac/render.d.ts +52 -0
  54. package/dist/wac/render.d.ts.map +1 -0
  55. package/dist/wac/render.js +156 -0
  56. package/dist/wac/render.js.map +1 -0
  57. package/dist/wac/resource-def.d.ts +116 -0
  58. package/dist/wac/resource-def.d.ts.map +1 -0
  59. package/dist/wac/resource-def.js +44 -0
  60. package/dist/wac/resource-def.js.map +1 -0
  61. package/dist/wac/resources.d.ts +104 -0
  62. package/dist/wac/resources.d.ts.map +1 -0
  63. package/dist/wac/resources.js +541 -0
  64. package/dist/wac/resources.js.map +1 -0
  65. package/dist/wac/values.d.ts +16 -0
  66. package/dist/wac/values.d.ts.map +1 -0
  67. package/dist/wac/values.js +53 -0
  68. package/dist/wac/values.js.map +1 -0
  69. package/package.json +3 -2
  70. package/dist/cli.spec.d.ts +0 -2
  71. package/dist/cli.spec.d.ts.map +0 -1
  72. package/dist/cli.spec.js +0 -226
  73. package/dist/cli.spec.js.map +0 -1
@@ -0,0 +1,110 @@
1
+ /**
2
+ * `chatly.json` — the document, and the only thing in this feature allowed to
3
+ * refuse you.
4
+ *
5
+ * ═══ WHY THERE IS NO zod HERE, AND WHAT TAKES ITS PLACE ══════════════════
6
+ *
7
+ * This repo validates with zod everywhere and this schema wants it. It is not
8
+ * here for one reason: `@chatlychat/cli` is a PUBLISHED package whose
9
+ * dependency list is exactly `@chatlychat/sdk-node`, zod is not reachable
10
+ * from `apps/cli` (it is in the workspace catalog, but no package here
11
+ * declares it, so it is not installed under `apps/cli/node_modules`), and
12
+ * CLAUDE.md §2 forbids adding a dependency without explicit approval. Adding
13
+ * one to a published CLI is a product decision about what customers install,
14
+ * not a tidying-up decision — the same reason `args.ts` is hand-written
15
+ * rather than `commander`.
16
+ *
17
+ * So the checks below are hand-written, and deliberately shaped so the swap
18
+ * is mechanical if that approval comes: one `parseDocument` entry point,
19
+ * issues collected as `{ path, message }` with a dotted path, and all of them
20
+ * reported at once rather than the first. A zod `.strict()` object and
21
+ * `ZodError.issues` is a drop-in for the body of this file.
22
+ *
23
+ * ═══ WHERE STRICTNESS APPLIES, AND WHERE IT DELIBERATELY DOES NOT ════════
24
+ *
25
+ * CLAUDE.md records the bug twice over: a key the reader has never heard of
26
+ * is STRIPPED, so the write answers 200 and did nothing, and `helpProfile` vs
27
+ * `profile` cost a complete feature that way. The cure is to refuse the key
28
+ * by name and say what the valid ones are. That is done for every part of the
29
+ * document whose vocabulary the CLI OWNS:
30
+ *
31
+ * - the top level (`version`, `workspace`, `resources`)
32
+ * - the resource-type keys inside `resources`
33
+ * - the per-row control keys the CLI invents (`bot:`, which is not an API
34
+ * field at all)
35
+ * - the natural key, which must be present and a non-empty string
36
+ * - any field the API RETURNS and no write path accepts (`unwritable` in
37
+ * `resources.ts`) — `triggers.brandId`, `brands.usage`,
38
+ * `macros.usageCount` and twenty more. These are the dangerous ones:
39
+ * they are spelled correctly, they come back on every read, and zod
40
+ * strips them on the way in.
41
+ *
42
+ * It is NOT done for the rest of a row's fields, and that is a decision
43
+ * rather than an omission. `common.ts`'s `bodyFrom` already states the
44
+ * position for every write on this CLI: "the body goes to the API exactly as
45
+ * written, so the schema in `apps/api` is the only spelling that matters…
46
+ * the CLI simply refuses to add a second renaming layer on top." A per-row
47
+ * allow-list here would be a second COPY of nine API schemas, in a package
48
+ * published on a different cadence, and it would reject a field the API
49
+ * shipped yesterday — a gate that fails on correct input is a gate somebody
50
+ * switches off.
51
+ *
52
+ * What replaces it is better evidence than a copied list: `plan` compares the
53
+ * fields a row declares against the fields the SERVER returns for rows of
54
+ * that type, and warns about one no live row has ever carried. That catches
55
+ * `helpProfle` from the server's own vocabulary, with nothing to keep in
56
+ * step. Its blind spot — a workspace with no rows of that type yet — is
57
+ * reported as such rather than passed off as a clean bill. See `diff.ts`.
58
+ */
59
+ import { type ResourceDef } from './resources.js';
60
+ /** The file format's version. Bumped only for a change that breaks a reader. */
61
+ export declare const DOCUMENT_VERSION = 1;
62
+ export interface Issue {
63
+ /** Dotted, and it is the path IN THE FILE: `resources.bots[2].slug`. */
64
+ readonly path: string;
65
+ readonly message: string;
66
+ }
67
+ export interface DocumentRow {
68
+ /** The natural key's value, as written. */
69
+ readonly key: string;
70
+ /** Where in the file this row came from, for an error message. */
71
+ readonly path: string;
72
+ /** The row's API fields, with the CLI's own control keys removed. */
73
+ readonly body: Readonly<Record<string, unknown>>;
74
+ /** `{ botId: { type: 'bots', key: 'support' } }` — resolved by `diff.ts`. */
75
+ readonly refs: Readonly<Record<string, {
76
+ readonly type: string;
77
+ readonly key: string;
78
+ }>>;
79
+ /**
80
+ * Top-level fields whose value came, wholly or partly, from `${env:…}`.
81
+ * Rendered as the variable's name rather than its value, so a plan can be
82
+ * pasted into a ticket.
83
+ */
84
+ readonly fromEnv: ReadonlyArray<string>;
85
+ }
86
+ export interface DocumentSection {
87
+ readonly def: ResourceDef;
88
+ readonly rows: ReadonlyArray<DocumentRow>;
89
+ }
90
+ export interface WacDocument {
91
+ readonly version: number;
92
+ /** Optional guard: `apply` refuses a workspace that is not this one. */
93
+ readonly workspace: string | undefined;
94
+ /** In `RESOURCES` order — which is dependency order. */
95
+ readonly sections: ReadonlyArray<DocumentSection>;
96
+ }
97
+ /** Thrown with every issue at once, not the first one found. */
98
+ export declare class DocumentError extends Error {
99
+ readonly issues: ReadonlyArray<Issue>;
100
+ constructor(issues: ReadonlyArray<Issue>);
101
+ }
102
+ /**
103
+ * Parse and validate. Throws `DocumentError` with every problem it found.
104
+ *
105
+ * Nothing here touches the network. `env` is the only ambient input and it is
106
+ * injected, which is what lets the spec drive this with a literal object and
107
+ * no transport at all.
108
+ */
109
+ export declare function parseDocument(raw: unknown, env?: Readonly<Record<string, string | undefined>>): WacDocument;
110
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/wac/document.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,OAAO,EAA+B,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE/E,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAyBlC,MAAM,WAAW,KAAK;IACpB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACjD,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACzF;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CACnD;AAED,gEAAgE;AAChE,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;gBAE1B,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC;CAQhD;AAwGD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,OAAO,EACZ,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAM,GACrD,WAAW,CAiFb"}
@@ -0,0 +1,429 @@
1
+ /**
2
+ * `chatly.json` — the document, and the only thing in this feature allowed to
3
+ * refuse you.
4
+ *
5
+ * ═══ WHY THERE IS NO zod HERE, AND WHAT TAKES ITS PLACE ══════════════════
6
+ *
7
+ * This repo validates with zod everywhere and this schema wants it. It is not
8
+ * here for one reason: `@chatlychat/cli` is a PUBLISHED package whose
9
+ * dependency list is exactly `@chatlychat/sdk-node`, zod is not reachable
10
+ * from `apps/cli` (it is in the workspace catalog, but no package here
11
+ * declares it, so it is not installed under `apps/cli/node_modules`), and
12
+ * CLAUDE.md §2 forbids adding a dependency without explicit approval. Adding
13
+ * one to a published CLI is a product decision about what customers install,
14
+ * not a tidying-up decision — the same reason `args.ts` is hand-written
15
+ * rather than `commander`.
16
+ *
17
+ * So the checks below are hand-written, and deliberately shaped so the swap
18
+ * is mechanical if that approval comes: one `parseDocument` entry point,
19
+ * issues collected as `{ path, message }` with a dotted path, and all of them
20
+ * reported at once rather than the first. A zod `.strict()` object and
21
+ * `ZodError.issues` is a drop-in for the body of this file.
22
+ *
23
+ * ═══ WHERE STRICTNESS APPLIES, AND WHERE IT DELIBERATELY DOES NOT ════════
24
+ *
25
+ * CLAUDE.md records the bug twice over: a key the reader has never heard of
26
+ * is STRIPPED, so the write answers 200 and did nothing, and `helpProfile` vs
27
+ * `profile` cost a complete feature that way. The cure is to refuse the key
28
+ * by name and say what the valid ones are. That is done for every part of the
29
+ * document whose vocabulary the CLI OWNS:
30
+ *
31
+ * - the top level (`version`, `workspace`, `resources`)
32
+ * - the resource-type keys inside `resources`
33
+ * - the per-row control keys the CLI invents (`bot:`, which is not an API
34
+ * field at all)
35
+ * - the natural key, which must be present and a non-empty string
36
+ * - any field the API RETURNS and no write path accepts (`unwritable` in
37
+ * `resources.ts`) — `triggers.brandId`, `brands.usage`,
38
+ * `macros.usageCount` and twenty more. These are the dangerous ones:
39
+ * they are spelled correctly, they come back on every read, and zod
40
+ * strips them on the way in.
41
+ *
42
+ * It is NOT done for the rest of a row's fields, and that is a decision
43
+ * rather than an omission. `common.ts`'s `bodyFrom` already states the
44
+ * position for every write on this CLI: "the body goes to the API exactly as
45
+ * written, so the schema in `apps/api` is the only spelling that matters…
46
+ * the CLI simply refuses to add a second renaming layer on top." A per-row
47
+ * allow-list here would be a second COPY of nine API schemas, in a package
48
+ * published on a different cadence, and it would reject a field the API
49
+ * shipped yesterday — a gate that fails on correct input is a gate somebody
50
+ * switches off.
51
+ *
52
+ * What replaces it is better evidence than a copied list: `plan` compares the
53
+ * fields a row declares against the fields the SERVER returns for rows of
54
+ * that type, and warns about one no live row has ever carried. That catches
55
+ * `helpProfle` from the server's own vocabulary, with nothing to keep in
56
+ * step. Its blind spot — a workspace with no rows of that type yet — is
57
+ * reported as such rather than passed off as a clean bill. See `diff.ts`.
58
+ */
59
+ import { resourceDef, RESOURCE_TYPES } from './resources.js';
60
+ /** The file format's version. Bumped only for a change that breaks a reader. */
61
+ export const DOCUMENT_VERSION = 1;
62
+ const TOP_LEVEL_KEYS = ['version', 'workspace', 'resources'];
63
+ /**
64
+ * `${env:NAME}` inside any string value.
65
+ *
66
+ * Two of these resources cannot be described at all without a secret — a
67
+ * documentation source needs its provider token on create, a customer card
68
+ * needs an `Authorization` header — and a config file in git is the one place
69
+ * those must not be. An interpolation is the smallest thing that makes the
70
+ * file usable, and it is narrow on purpose: this exact form, in a string, and
71
+ * nothing else. No defaults, no shell, no file reads, no nesting.
72
+ *
73
+ * An UNSET variable is refused by name. It is not substituted with an empty
74
+ * string, and that is the whole point: CLAUDE.md records both halves of this
75
+ * failure — "a shell `read` cannot fail", where empty vars beat the sentinel
76
+ * and a health check invented findings, and "env fallback reaches
77
+ * production", where an unset variable became a localhost canonical URL on
78
+ * every page. An empty token here would be POSTed, verified against the
79
+ * provider, and fail with a message about the provider rather than about the
80
+ * variable nobody exported.
81
+ */
82
+ const ENV_REF = /\$\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g;
83
+ /** Thrown with every issue at once, not the first one found. */
84
+ export class DocumentError extends Error {
85
+ issues;
86
+ constructor(issues) {
87
+ super(`${String(issues.length)} problem(s) in the document:\n` +
88
+ issues.map((i) => ` ${i.path}\n ${i.message}`).join('\n'));
89
+ this.name = 'DocumentError';
90
+ this.issues = issues;
91
+ }
92
+ }
93
+ function isPlainObject(value) {
94
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
95
+ }
96
+ /** `a`, `b` and `c` — these go in error messages, so they read as English. */
97
+ function list(names) {
98
+ if (names.length <= 1) {
99
+ return names[0] ?? '(none)';
100
+ }
101
+ return `${names.slice(0, -1).join(', ')} and ${String(names[names.length - 1])}`;
102
+ }
103
+ /**
104
+ * Did the author mean one of the valid keys?
105
+ *
106
+ * Levenshtein ≤ 2, which covers the real cases: a dropped letter, a doubled
107
+ * letter, a transposition, `botid` for `botId`, `eventTypes` for `events`.
108
+ * Anything further is not a typo and guessing would be noise.
109
+ */
110
+ function nearest(key, valid) {
111
+ let best;
112
+ for (const candidate of valid) {
113
+ const d = distance(key.toLowerCase(), candidate.toLowerCase());
114
+ if (d <= 2 && (best === undefined || d < best.d)) {
115
+ best = { name: candidate, d };
116
+ }
117
+ }
118
+ return best?.name;
119
+ }
120
+ function distance(a, b) {
121
+ let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
122
+ for (let i = 1; i <= a.length; i += 1) {
123
+ const row = [i];
124
+ for (let j = 1; j <= b.length; j += 1) {
125
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
126
+ row.push(Math.min((row[j - 1] ?? 0) + 1, (prev[j] ?? 0) + 1, (prev[j - 1] ?? 0) + cost));
127
+ }
128
+ prev = row;
129
+ }
130
+ return prev[b.length] ?? Math.max(a.length, b.length);
131
+ }
132
+ function refuseUnknown(issues, path, actual, valid, what) {
133
+ for (const key of actual) {
134
+ if (valid.includes(key)) {
135
+ continue;
136
+ }
137
+ const guess = nearest(key, valid);
138
+ issues.push({
139
+ path: `${path}.${key}`,
140
+ message: `unknown ${what} "${key}".` +
141
+ (guess === undefined ? '' : ` Did you mean "${guess}"?`) +
142
+ ` Valid: ${list(valid)}.`,
143
+ });
144
+ }
145
+ }
146
+ /**
147
+ * Substitute `${env:NAME}` throughout a value. Collects unset names.
148
+ *
149
+ * `env` is a parameter rather than a read of `process.env` so the spec can
150
+ * drive it: a test that monkey-patches the process environment is a test that
151
+ * leaks into its neighbours.
152
+ */
153
+ function interpolate(value, env, missing, used) {
154
+ if (typeof value === 'string') {
155
+ return value.replace(ENV_REF, (_match, name) => {
156
+ const found = env[name];
157
+ if (found === undefined || found === '') {
158
+ missing.add(name);
159
+ return '';
160
+ }
161
+ used.add(name);
162
+ return found;
163
+ });
164
+ }
165
+ if (Array.isArray(value)) {
166
+ return value.map((v) => interpolate(v, env, missing, used));
167
+ }
168
+ if (isPlainObject(value)) {
169
+ const out = {};
170
+ for (const [k, v] of Object.entries(value)) {
171
+ out[k] = interpolate(v, env, missing, used);
172
+ }
173
+ return out;
174
+ }
175
+ return value;
176
+ }
177
+ /**
178
+ * Parse and validate. Throws `DocumentError` with every problem it found.
179
+ *
180
+ * Nothing here touches the network. `env` is the only ambient input and it is
181
+ * injected, which is what lets the spec drive this with a literal object and
182
+ * no transport at all.
183
+ */
184
+ export function parseDocument(raw, env = {}) {
185
+ const issues = [];
186
+ if (!isPlainObject(raw)) {
187
+ throw new DocumentError([
188
+ {
189
+ path: '(root)',
190
+ message: 'the document must be an object with `version` and `resources`, not ' +
191
+ (Array.isArray(raw) ? 'an array' : typeof raw),
192
+ },
193
+ ]);
194
+ }
195
+ refuseUnknown(issues, '(root)', Object.keys(raw), [...TOP_LEVEL_KEYS], 'top-level key');
196
+ const version = raw['version'];
197
+ if (version === undefined) {
198
+ issues.push({
199
+ path: 'version',
200
+ message: `required. Write \`"version": ${String(DOCUMENT_VERSION)}\`.`,
201
+ });
202
+ }
203
+ else if (version !== DOCUMENT_VERSION) {
204
+ issues.push({
205
+ path: 'version',
206
+ message: `this CLI reads version ${String(DOCUMENT_VERSION)}; the document says ` +
207
+ `${JSON.stringify(version)}. Upgrade \`@chatlychat/cli\` rather than editing the number.`,
208
+ });
209
+ }
210
+ const workspaceRaw = raw['workspace'];
211
+ let workspace;
212
+ if (workspaceRaw !== undefined) {
213
+ if (typeof workspaceRaw !== 'string' || workspaceRaw.length === 0) {
214
+ issues.push({
215
+ path: 'workspace',
216
+ message: 'must be a workspace id string, or left out. It is a guard: `apply` refuses to run ' +
217
+ 'against any other workspace, which is what stops a staging file reaching production.',
218
+ });
219
+ }
220
+ else {
221
+ workspace = workspaceRaw;
222
+ }
223
+ }
224
+ const resourcesRaw = raw['resources'];
225
+ const sections = [];
226
+ if (resourcesRaw === undefined) {
227
+ issues.push({
228
+ path: 'resources',
229
+ message: `required. An object keyed by resource type: ${list(RESOURCE_TYPES)}.`,
230
+ });
231
+ }
232
+ else if (!isPlainObject(resourcesRaw)) {
233
+ issues.push({ path: 'resources', message: 'must be an object keyed by resource type.' });
234
+ }
235
+ else {
236
+ refuseUnknown(issues, 'resources', Object.keys(resourcesRaw), RESOURCE_TYPES, 'resource type');
237
+ /*
238
+ * Iterated in RESOURCES order, not in the file's key order.
239
+ *
240
+ * The order matters at apply time — a routing rule names a bot that this
241
+ * run may be about to create — and taking it from the file would make the
242
+ * result depend on how somebody chose to lay out their document.
243
+ */
244
+ for (const type of RESOURCE_TYPES) {
245
+ const value = resourcesRaw[type];
246
+ if (value === undefined) {
247
+ continue;
248
+ }
249
+ const def = resourceDef(type);
250
+ if (def === undefined) {
251
+ continue;
252
+ }
253
+ sections.push({ def, rows: parseSection(issues, def, value, env) });
254
+ }
255
+ }
256
+ if (issues.length > 0) {
257
+ throw new DocumentError(issues);
258
+ }
259
+ return { version: DOCUMENT_VERSION, workspace, sections };
260
+ }
261
+ function parseSection(issues, def, value, env) {
262
+ const at = `resources.${def.type}`;
263
+ /*
264
+ * The resource with no usable natural key is refused here, by name, with
265
+ * the reason.
266
+ *
267
+ * `channels` stays a VALID key under `resources` — dropping it would make
268
+ * the unknown-key message say "channels is not a resource type", which is
269
+ * wrong and sends the reader hunting for a spelling mistake. What it cannot
270
+ * be is non-empty. `publicId` is minted as a GLOBALLY unique reservation
271
+ * (another tenant may hold `web`), `type` and `publicId` are immutable
272
+ * afterwards, `config` reads back redacted, and the create response carries
273
+ * per-provider webhook registration results a diff cannot model.
274
+ *
275
+ * Half-supporting it — "we will update the ones we can match and ignore the
276
+ * rest" — is worse than refusing, because the thing it silently cannot do
277
+ * is the thing somebody put it in the file for.
278
+ */
279
+ if (def.keySource === 'server') {
280
+ if (Array.isArray(value) && value.length === 0) {
281
+ return [];
282
+ }
283
+ issues.push({
284
+ path: at,
285
+ message: `${def.label}s cannot be managed from a document. \`${def.naturalKey}\` is minted on ` +
286
+ 'create and is unique across every workspace, not just yours, so a row here has no key ' +
287
+ `it can safely claim; \`type\` and \`${def.naturalKey}\` are then immutable, and ` +
288
+ '`config` reads back with `__redacted__` in every credential key, so even an update ' +
289
+ `diff is mostly unknowable. Use \`chatly ${def.type} create\` and ` +
290
+ `\`chatly ${def.type} update <id>\`, and remove this section.`,
291
+ });
292
+ return [];
293
+ }
294
+ if (!Array.isArray(value)) {
295
+ issues.push({
296
+ path: at,
297
+ message: `must be an array of ${def.label} objects (it is ${typeof value}).`,
298
+ });
299
+ return [];
300
+ }
301
+ const rows = [];
302
+ const seen = new Map();
303
+ for (let i = 0; i < value.length; i += 1) {
304
+ const path = `${at}[${String(i)}]`;
305
+ const row = value[i];
306
+ if (!isPlainObject(row)) {
307
+ issues.push({ path, message: `must be an object, not ${typeof row}.` });
308
+ continue;
309
+ }
310
+ const keyValue = row[def.naturalKey];
311
+ if (typeof keyValue !== 'string' || keyValue.length === 0) {
312
+ /*
313
+ * The one field this file insists on, and the message says why rather
314
+ * than just "required": somebody reading it on a fresh checkout needs
315
+ * to know that the uuid they were about to paste is the wrong answer.
316
+ */
317
+ issues.push({
318
+ path: `${path}.${def.naturalKey}`,
319
+ message: `every ${def.label} needs \`${def.naturalKey}\`: a non-empty string. It is the ` +
320
+ 'natural key this file matches on, so `plan` can tell a create from an update ' +
321
+ 'without a server uuid — which does not exist yet in a new environment.' +
322
+ (def.keyEnforced
323
+ ? ''
324
+ : ` Note the database does NOT make \`${def.naturalKey}\` unique for this ` +
325
+ 'resource, so this file is the only thing keeping it unique.'),
326
+ });
327
+ continue;
328
+ }
329
+ const duplicate = seen.get(keyValue);
330
+ if (duplicate !== undefined) {
331
+ issues.push({
332
+ path: `${path}.${def.naturalKey}`,
333
+ message: `duplicate ${def.naturalKey} "${keyValue}" — also at ${duplicate}. Two rows with one ` +
334
+ 'key cannot both be applied, and guessing which was meant is how the wrong resource ' +
335
+ 'gets overwritten.',
336
+ });
337
+ continue;
338
+ }
339
+ seen.set(keyValue, path);
340
+ /*
341
+ * Fields the API RETURNS and no write path accepts.
342
+ *
343
+ * The dangerous class, and the reason `unwritable` exists in
344
+ * `resources.ts`. These are spelled correctly, they come back on every
345
+ * read, and copying a `chatly bots list --json` row into the document is
346
+ * the obvious way to start one — so `brands.usage`, `macros.usageCount`,
347
+ * `triggers.brandId` and twenty more arrive looking exactly like
348
+ * settings. zod strips every one of them and the write answers 200.
349
+ */
350
+ for (const field of Object.keys(row)) {
351
+ if (!def.unwritable.includes(field)) {
352
+ continue;
353
+ }
354
+ issues.push({
355
+ path: `${path}.${field}`,
356
+ message: `\`${field}\` is returned on a read and NO write path accepts it, so zod would strip ` +
357
+ 'it and the API would answer 200 having ignored you. Remove it. (A document is not a ' +
358
+ 'copy of a list row: it holds the settings you choose, not the state the server ' +
359
+ 'reports.)',
360
+ });
361
+ }
362
+ const missing = new Set();
363
+ const usedEnv = new Set();
364
+ const body = {};
365
+ const refs = {};
366
+ const fromEnv = [];
367
+ for (const [field, rawValue] of Object.entries(row)) {
368
+ if (def.unwritable.includes(field)) {
369
+ continue;
370
+ }
371
+ const ref = def.references?.[field];
372
+ if (ref === undefined) {
373
+ /*
374
+ * A FRESH set per field, not a shared one.
375
+ *
376
+ * This was one `usedEnv` for the whole row and the membership test
377
+ * was `size > before`, which is correct exactly once: two fields
378
+ * referencing the SAME variable left the second one unmarked, and an
379
+ * unmarked field is printed by value. The spec caught it with a
380
+ * document whose `token` and `baseUrl` both read `${env:X}` — the
381
+ * token was masked and the live credential went out beside it.
382
+ */
383
+ const perField = new Set();
384
+ body[field] = interpolate(rawValue, env, missing, perField);
385
+ if (perField.size > 0) {
386
+ fromEnv.push(field);
387
+ for (const name of perField) {
388
+ usedEnv.add(name);
389
+ }
390
+ }
391
+ continue;
392
+ }
393
+ /*
394
+ * A reference key. `bot: "support"` is not an API field — it resolves
395
+ * into `botId` — so it must never reach the body, and the wire field
396
+ * must not be written by hand beside it.
397
+ */
398
+ if (typeof rawValue !== 'string' || rawValue.length === 0) {
399
+ issues.push({
400
+ path: `${path}.${field}`,
401
+ message: `must be the \`${ref.type}\` natural key as a string.`,
402
+ });
403
+ continue;
404
+ }
405
+ if (row[ref.wire] !== undefined) {
406
+ issues.push({
407
+ path: `${path}.${ref.wire}`,
408
+ message: `remove it: this row has both \`${field}: "${rawValue}"\` and \`${ref.wire}\`. ` +
409
+ `\`${ref.wire}\` is a server uuid, which pins the document to one environment — ` +
410
+ `\`${field}\` is the portable form and is what this CLI resolves.`,
411
+ });
412
+ continue;
413
+ }
414
+ refs[ref.wire] = { type: ref.type, key: rawValue };
415
+ }
416
+ for (const name of missing) {
417
+ issues.push({
418
+ path,
419
+ message: `\${env:${name}} is used here and ${name} is unset or empty. It is NOT substituted ` +
420
+ 'with an empty string: an empty token would be sent, verified against the provider, ' +
421
+ 'and fail with a message about the provider rather than about the variable nobody ' +
422
+ `exported. Export ${name} and run again.`,
423
+ });
424
+ }
425
+ rows.push({ key: keyValue, path, body, refs, fromEnv });
426
+ }
427
+ return rows;
428
+ }
429
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/wac/document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Printing a plan, and one rule behind every choice here: a diff nobody reads
3
+ * is worse than no diff, because it gets approved.
4
+ *
5
+ * So the shape is terraform's, because that is the shape people already know
6
+ * how to read — a sigil per action, the resource addressed as `type/key`, and
7
+ * the changed fields indented under it with old on the left. What is NOT
8
+ * terraform's: unchanged resources are one line each rather than a collapsed
9
+ * count, because on this surface a workspace has tens of resources and not
10
+ * thousands, and "the thing I expected to be unchanged is listed as
11
+ * unchanged" is the second most useful sentence a plan can say.
12
+ *
13
+ * Values are JSON, truncated. A macro body is 20,000 characters and a
14
+ * trigger's `actions` array is a nested object; neither belongs on a terminal
15
+ * line in full, and `--json` exists for the whole thing.
16
+ */
17
+ import { type Plan } from './diff.js';
18
+ /** One value, as JSON, clipped. `undefined` is `(unset)`, not `undefined`. */
19
+ export declare function show(value: unknown): string;
20
+ export interface RenderOptions {
21
+ /** `--prune` was passed, so orphan lines say "delete" rather than "kept". */
22
+ readonly prune: boolean;
23
+ /** Types whose live list may be short, where prune is refused regardless. */
24
+ readonly pruneUnsafe: ReadonlyArray<string>;
25
+ }
26
+ /** The whole plan, as `chatly plan` prints it. */
27
+ export declare function renderPlan(plan: Plan, opts: RenderOptions): string;
28
+ /**
29
+ * The line that follows a plan, and it is load-bearing.
30
+ *
31
+ * `plan` is the half of this feature that is safe, and saying so explicitly
32
+ * is what makes somebody willing to run it against production. "Nothing was
33
+ * written" is a claim the spec holds this CLI to: a recording transport
34
+ * asserts zero non-GET requests over the whole plan path.
35
+ */
36
+ export declare function planFooter(plan: Plan, opts: RenderOptions): string;
37
+ /**
38
+ * The plan, for `--json`, with every env-sourced value taken back out.
39
+ *
40
+ * `plan --json | tee plan.json` is a thing somebody does on the first day,
41
+ * and a plan item carries `row.body` — which holds the RESOLVED value of
42
+ * every `${env:…}` reference, including a docs source's provider token. The
43
+ * human renderer already masks those (`fromEnv` on each row); the JSON
44
+ * renderer printed them, which would have written a live credential into a
45
+ * file somebody then commits.
46
+ *
47
+ * So `row` does not go out at all — a consumer wants the diff, not a copy of
48
+ * its own input — and any change whose field came from the environment is
49
+ * reported as having come from the environment rather than by value.
50
+ */
51
+ export declare function planForJson(plan: Plan): unknown;
52
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/wac/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAA2B,KAAK,IAAI,EAAiB,MAAM,WAAW,CAAC;AAY9E,8EAA8E;AAC9E,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAM3C;AAWD,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC7C;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAgElE;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAkBlE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAmB/C"}