@malmhq/intermesh-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +198 -0
  2. package/README.md +77 -0
  3. package/dist/generation/.opencode/AGENTS.md +38 -0
  4. package/dist/generation/.opencode/agents/generation-builder.md +9 -0
  5. package/dist/generation/.opencode/agents/generation-fidelity.md +9 -0
  6. package/dist/generation/.opencode/agents/generation-source.md +13 -0
  7. package/dist/generation/.opencode/prompts/analyze-capabilities.md +61 -0
  8. package/dist/generation/.opencode/prompts/analyze-source.md +41 -0
  9. package/dist/generation/.opencode/prompts/generate.md +9 -0
  10. package/dist/generation/.opencode/prompts/repair-capability-proposal.md +5 -0
  11. package/dist/generation/.opencode/skills/generation-building/SKILL.md +32 -0
  12. package/dist/generation/.opencode/skills/generation-package-review/SKILL.md +36 -0
  13. package/dist/generation/.opencode/skills/generation-source-analysis/SKILL.md +36 -0
  14. package/dist/generation/AGENTS.md +38 -0
  15. package/dist/generation/agent-skill-installer/index.ts +4 -0
  16. package/dist/generation/agent-skill-installer/install.ts +503 -0
  17. package/dist/generation/agent-skill-installer/lock.ts +128 -0
  18. package/dist/generation/agent-skill-installer/manifest.ts +271 -0
  19. package/dist/generation/agent-skill-installer/targets.ts +169 -0
  20. package/dist/generation/runtime-entrypoint.js +39980 -0
  21. package/dist/generation/scripts/check-package.mjs +45 -0
  22. package/dist/generation/scripts/installed-help.mjs +98 -0
  23. package/dist/generation/scripts/lead-input.mjs +584 -0
  24. package/dist/generation/scripts/prepare-opencode.mjs +33 -0
  25. package/dist/generation/scripts/prepared-work.mjs +197 -0
  26. package/dist/generation/scripts/repair-loop.mjs +519 -0
  27. package/dist/generation/scripts/run-reviewers.mjs +306 -0
  28. package/dist/generation/scripts/runtime-capture.mjs +119 -0
  29. package/dist/generation/scripts/stage-timings.mjs +53 -0
  30. package/dist/generation/scripts/validate-source-brief.mjs +220 -0
  31. package/dist/generation/scripts/verify-package.mjs +365 -0
  32. package/dist/generation/templates/npm-cli/CONTRIBUTING.md +20 -0
  33. package/dist/generation/templates/npm-cli/GENERATION-PATTERNS.md +143 -0
  34. package/dist/generation/templates/npm-cli/README.md +67 -0
  35. package/dist/generation/templates/npm-cli/capabilities.json +1 -0
  36. package/dist/generation/templates/npm-cli/mock/routes.ts +3 -0
  37. package/dist/generation/templates/npm-cli/mock/server.ts +15 -0
  38. package/dist/generation/templates/npm-cli/mock/types.ts +7 -0
  39. package/dist/generation/templates/npm-cli/package.json +36 -0
  40. package/dist/generation/templates/npm-cli/pnpm-lock.yaml +495 -0
  41. package/dist/generation/templates/npm-cli/pnpm-workspace.yaml +2 -0
  42. package/dist/generation/templates/npm-cli/skills/customer-cli-management/SKILL.md +22 -0
  43. package/dist/generation/templates/npm-cli/skills/customer-workflow/SKILL.md +22 -0
  44. package/dist/generation/templates/npm-cli/src/auth/commands.ts +77 -0
  45. package/dist/generation/templates/npm-cli/src/auth/credentials.ts +66 -0
  46. package/dist/generation/templates/npm-cli/src/auth/prompt.ts +27 -0
  47. package/dist/generation/templates/npm-cli/src/cli.ts +53 -0
  48. package/dist/generation/templates/npm-cli/src/commands.ts +6 -0
  49. package/dist/generation/templates/npm-cli/src/customer.ts +16 -0
  50. package/dist/generation/templates/npm-cli/src/index.ts +11 -0
  51. package/dist/generation/templates/npm-cli/src/lifecycle/commands.ts +344 -0
  52. package/dist/generation/templates/npm-cli/src/lifecycle/config.ts +32 -0
  53. package/dist/generation/templates/npm-cli/src/shared/dry-run.ts +32 -0
  54. package/dist/generation/templates/npm-cli/src/shared/errors.ts +162 -0
  55. package/dist/generation/templates/npm-cli/src/shared/files.ts +45 -0
  56. package/dist/generation/templates/npm-cli/src/shared/http.ts +125 -0
  57. package/dist/generation/templates/npm-cli/src/shared/output.ts +33 -0
  58. package/dist/generation/templates/npm-cli/test/auth.test.ts +113 -0
  59. package/dist/generation/templates/npm-cli/test/fixture-cli.ts +51 -0
  60. package/dist/generation/templates/npm-cli/test/foundation.test.ts +316 -0
  61. package/dist/generation/templates/npm-cli/test/helpers.ts +34 -0
  62. package/dist/generation/templates/npm-cli/test/mock-lifecycle.test.ts +86 -0
  63. package/dist/generation/templates/npm-cli/test/run-with-mock.ts +142 -0
  64. package/dist/generation/templates/npm-cli/tsconfig.build.json +5 -0
  65. package/dist/generation/templates/npm-cli/tsconfig.json +11 -0
  66. package/dist/generation/templates/opencode-bootstrap/package-lock.json +402 -0
  67. package/dist/generation/templates/opencode-bootstrap/package.json +7 -0
  68. package/dist/index.js +51965 -0
  69. package/dist/runtime/candidate-inspection/inspect.Dockerfile +4 -0
  70. package/dist/runtime/candidate-inspection/install.Dockerfile +5 -0
  71. package/dist/runtime/candidate-inspection/registry-proxy.mjs +37 -0
  72. package/dist/runtime/harbor/bootstrap/package-lock.json +402 -0
  73. package/dist/runtime/harbor/bootstrap/package.json +7 -0
  74. package/dist/runtime/harbor/evaluation.Dockerfile +31 -0
  75. package/dist/runtime/harbor/harbor_launcher.py +62 -0
  76. package/dist/runtime/harbor/intermesh_opencode.py +44 -0
  77. package/dist/runtime/harbor/model-forwarder.mjs +52 -0
  78. package/dist/runtime/live-task.md +13 -0
  79. package/package.json +44 -0
  80. package/skills/inter-cli-management/SKILL.md +38 -0
  81. package/skills/inter-cli-management/evals/evals.json +40 -0
  82. package/skills/inter-context/SKILL.md +72 -0
  83. package/skills/inter-context/evals/cases.json +82 -0
  84. package/skills/inter-eval-authoring/SKILL.md +70 -0
  85. package/skills/inter-eval-authoring/evals/evals.json +69 -0
  86. package/skills/inter-eval-authoring/evals/trigger-queries.json +34 -0
  87. package/skills/inter-eval-authoring/references/authoring-guide.md +70 -0
  88. package/skills/inter-eval-authoring/references/eval-plan.example.json +92 -0
  89. package/skills/inter-eval-authoring/references/eval-plan.schema.json +304 -0
  90. package/skills/inter-evaluation/SKILL.md +51 -0
  91. package/skills/inter-evaluation/evals/evals.json +49 -0
  92. package/skills/inter-evaluation/evals/trigger-queries.json +30 -0
  93. package/skills/inter-generation/SKILL.md +42 -0
  94. package/skills/inter-generation/evals/evals.json +81 -0
  95. package/skills/inter-generation/evals/trigger-queries.json +58 -0
@@ -0,0 +1,584 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { chmod, readFile, writeFile } from 'node:fs/promises';
3
+ import { dirname, join, resolve } from 'node:path';
4
+
5
+ import { validateSourceBrief } from './validate-source-brief.mjs';
6
+ import { createWorkPackets, responsePattern, scaffoldPreparedWork } from './prepared-work.mjs';
7
+
8
+ export const MAX_LEAD_INPUT_BYTES = 20 * 1024 * 1024;
9
+ const HASH = /^[a-f0-9]{64}$/u;
10
+
11
+ function fail(message) {
12
+ throw new TypeError(message);
13
+ }
14
+
15
+ function record(value, name) {
16
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
17
+ fail(`${name} is invalid`);
18
+ return value;
19
+ }
20
+
21
+ function exactKeys(value, expected, name) {
22
+ const actual = Object.keys(value).sort();
23
+ if (actual.join('\0') !== [...expected].sort().join('\0')) fail(`${name} has unexpected fields`);
24
+ }
25
+
26
+ function text(value, name) {
27
+ if (typeof value !== 'string' || value.trim() === '') fail(`${name} is invalid`);
28
+ return value;
29
+ }
30
+
31
+ function hash(value) {
32
+ return createHash('sha256').update(value).digest('hex');
33
+ }
34
+
35
+ async function businessContext(workspace, context) {
36
+ return Promise.all(
37
+ context.map(async ({ path, sha256 }) => {
38
+ const bytes = await readFile(resolve(workspace, path));
39
+ if (hash(bytes) !== sha256) fail(`Context hash does not match: ${path}`);
40
+ return { path, sha256, content: bytes.toString('utf8') };
41
+ }),
42
+ );
43
+ }
44
+
45
+ function generatorInputBytes(bundle, leadInputHash, context) {
46
+ const bytes = Buffer.from(
47
+ `${JSON.stringify(
48
+ {
49
+ schema_version: '1.0.0',
50
+ lead_input_sha256: leadInputHash,
51
+ customer: bundle.customer,
52
+ constraints: bundle.constraints,
53
+ business_context: context,
54
+ starter: {
55
+ guide_path: 'package/GENERATION-PATTERNS.md',
56
+ protected_paths: bundle.foundation.protected_paths,
57
+ },
58
+ packets: bundle.work_packets,
59
+ components_by_pointer: bundle.components_by_pointer,
60
+ },
61
+ null,
62
+ 2,
63
+ )}\n`,
64
+ );
65
+ if (bytes.length > MAX_LEAD_INPUT_BYTES) fail('Generator input exceeds 20 MiB');
66
+ return bytes;
67
+ }
68
+
69
+ function expectedHash(value, name) {
70
+ if (typeof value !== 'string' || !HASH.test(value)) fail(`${name} is invalid`);
71
+ return value;
72
+ }
73
+
74
+ async function jsonBytes(path, name) {
75
+ const bytes = await readFile(path);
76
+ let value;
77
+ try {
78
+ value = JSON.parse(bytes.toString('utf8'));
79
+ } catch {
80
+ fail(`${name} is invalid JSON`);
81
+ }
82
+ return { bytes, value: record(value, name) };
83
+ }
84
+
85
+ function decodePointer(pointer) {
86
+ if (typeof pointer !== 'string' || !pointer.startsWith('#/'))
87
+ fail(`Unsupported OpenAPI reference: ${String(pointer)}`);
88
+ return pointer
89
+ .slice(2)
90
+ .split('/')
91
+ .map((token) => {
92
+ if (/~(?:[^01]|$)/u.test(token)) fail(`Malformed OpenAPI reference: ${pointer}`);
93
+ return token.replaceAll('~1', '/').replaceAll('~0', '~');
94
+ });
95
+ }
96
+
97
+ function encodePointer(tokens) {
98
+ return `#/${tokens.map((token) => token.replaceAll('~', '~0').replaceAll('/', '~1')).join('/')}`;
99
+ }
100
+
101
+ function resolvePointer(document, pointer) {
102
+ const tokens = decodePointer(pointer);
103
+ let value = document;
104
+ for (const token of tokens) {
105
+ if (typeof value !== 'object' || value === null || !Object.hasOwn(value, token))
106
+ fail(`Unresolved OpenAPI reference: ${pointer}`);
107
+ value = value[token];
108
+ }
109
+ return { pointer: encodePointer(tokens), value };
110
+ }
111
+
112
+ function referencesIn(value, references) {
113
+ if (Array.isArray(value)) {
114
+ for (const item of value) referencesIn(item, references);
115
+ return;
116
+ }
117
+ if (typeof value !== 'object' || value === null) return;
118
+ for (const [key, item] of Object.entries(value)) {
119
+ if (key === '$ref') references.add(text(item, '$ref'));
120
+ else referencesIn(item, references);
121
+ }
122
+ }
123
+
124
+ function referenceClosure(document, operation) {
125
+ const pending = new Set();
126
+ referencesIn(operation, pending);
127
+ const values = new Map();
128
+ while (pending.size > 0) {
129
+ const requested = [...pending].sort()[0];
130
+ pending.delete(requested);
131
+ const resolved = resolvePointer(document, requested);
132
+ if (values.has(resolved.pointer)) continue;
133
+ values.set(resolved.pointer, resolved.value);
134
+ const nested = new Set();
135
+ referencesIn(resolved.value, nested);
136
+ for (const reference of nested) {
137
+ const normalized = resolvePointer(document, reference).pointer;
138
+ if (!values.has(normalized)) pending.add(normalized);
139
+ }
140
+ }
141
+ return values;
142
+ }
143
+
144
+ function validateEvidence(value, name) {
145
+ const evidence = record(value, name);
146
+ exactKeys(evidence, ['document', 'location', 'excerpt'], name);
147
+ text(evidence.document, `${name}.document`);
148
+ text(evidence.location, `${name}.location`);
149
+ text(evidence.excerpt, `${name}.excerpt`);
150
+ }
151
+
152
+ function validateSortedUniqueStrings(value, name, allowEmpty = true) {
153
+ if (!Array.isArray(value) || (!allowEmpty && value.length === 0)) fail(`${name} is invalid`);
154
+ for (const item of value) text(item, name);
155
+ if (value.join('\0') !== [...new Set(value)].sort().join('\0'))
156
+ fail(`${name} must be sorted and unique`);
157
+ }
158
+
159
+ export function validateLeadInput(value) {
160
+ const bundle = record(value, 'Lead input');
161
+ exactKeys(
162
+ bundle,
163
+ [
164
+ 'schema_version',
165
+ 'source_hashes',
166
+ 'customer',
167
+ 'foundation',
168
+ 'constraints',
169
+ 'components_by_pointer',
170
+ 'capabilities',
171
+ 'work_packets',
172
+ ],
173
+ 'Lead input',
174
+ );
175
+ if (bundle.schema_version !== '2.0.0') fail('Lead input schema version is invalid');
176
+ const hashes = record(bundle.source_hashes, 'source_hashes');
177
+ exactKeys(
178
+ hashes,
179
+ [
180
+ 'run_request',
181
+ 'openapi',
182
+ 'operation_inventory',
183
+ 'customer_profile',
184
+ 'source_brief',
185
+ 'starter_receipt',
186
+ 'starter_checks',
187
+ 'context',
188
+ ],
189
+ 'source_hashes',
190
+ );
191
+ for (const name of [
192
+ 'run_request',
193
+ 'openapi',
194
+ 'operation_inventory',
195
+ 'customer_profile',
196
+ 'source_brief',
197
+ 'starter_receipt',
198
+ 'starter_checks',
199
+ ])
200
+ expectedHash(hashes[name], `source_hashes.${name}`);
201
+ if (!Array.isArray(hashes.context) || hashes.context.length === 0)
202
+ fail('source_hashes.context is invalid');
203
+ for (const [index, value] of hashes.context.entries()) {
204
+ const item = record(value, `source_hashes.context[${index}]`);
205
+ exactKeys(item, ['path', 'sha256'], `source_hashes.context[${index}]`);
206
+ text(item.path, `source_hashes.context[${index}].path`);
207
+ expectedHash(item.sha256, `source_hashes.context[${index}].sha256`);
208
+ }
209
+ if (new Set(hashes.context.map((item) => item.path)).size !== hashes.context.length)
210
+ fail('source_hashes.context paths are duplicated');
211
+ const customer = record(bundle.customer, 'customer');
212
+ exactKeys(
213
+ customer,
214
+ [
215
+ 'integration_id',
216
+ 'package_name',
217
+ 'binary',
218
+ 'display_name',
219
+ 'base_url',
220
+ 'auth_environment_variable',
221
+ 'login_prompt',
222
+ ],
223
+ 'customer',
224
+ );
225
+ for (const name of Object.keys(customer)) text(customer[name], `customer.${name}`);
226
+ const foundation = record(bundle.foundation, 'foundation');
227
+ exactKeys(
228
+ foundation,
229
+ ['package_directory', 'starter_verified', 'protected_paths', 'prepared_hashes'],
230
+ 'foundation',
231
+ );
232
+ text(foundation.package_directory, 'foundation.package_directory');
233
+ if (foundation.starter_verified !== true) fail('foundation.starter_verified is invalid');
234
+ validateSortedUniqueStrings(foundation.protected_paths, 'foundation.protected_paths');
235
+ const preparedHashes = record(foundation.prepared_hashes, 'foundation.prepared_hashes');
236
+ for (const [path, value] of Object.entries(preparedHashes)) {
237
+ text(path, 'foundation.prepared_hashes path');
238
+ expectedHash(value, `foundation.prepared_hashes.${path}`);
239
+ }
240
+ const constraints = record(bundle.constraints, 'constraints');
241
+ exactKeys(
242
+ constraints,
243
+ ['no_live_api_calls', 'no_credentials', 'offline_tests', 'human_release_review_required'],
244
+ 'constraints',
245
+ );
246
+ if (Object.values(constraints).some((item) => item !== true))
247
+ fail('Lead input constraints are invalid');
248
+ const components = record(bundle.components_by_pointer, 'components_by_pointer');
249
+ const componentPointers = Object.keys(components);
250
+ if (componentPointers.join('\0') !== [...componentPointers].sort().join('\0'))
251
+ fail('components_by_pointer must be sorted');
252
+ for (const pointer of componentPointers) {
253
+ const normalized = encodePointer(decodePointer(pointer));
254
+ if (normalized !== pointer) fail(`Component pointer is not normalized: ${pointer}`);
255
+ }
256
+ if (!Array.isArray(bundle.capabilities) || bundle.capabilities.length === 0)
257
+ fail('Lead input capabilities are invalid');
258
+ const operationKeys = new Set();
259
+ const capabilityByKey = new Map();
260
+ for (const [index, value] of bundle.capabilities.entries()) {
261
+ const item = record(value, `capabilities[${index}]`);
262
+ exactKeys(
263
+ item,
264
+ [
265
+ 'operation_key',
266
+ 'effect',
267
+ 'business_purpose',
268
+ 'proposed_command',
269
+ 'context_evidence',
270
+ 'operation',
271
+ 'component_refs',
272
+ 'response_pattern',
273
+ ],
274
+ `capabilities[${index}]`,
275
+ );
276
+ for (const name of [
277
+ 'operation_key',
278
+ 'effect',
279
+ 'business_purpose',
280
+ 'proposed_command',
281
+ 'response_pattern',
282
+ ])
283
+ text(item[name], `capabilities[${index}].${name}`);
284
+ if (!['json', 'text', 'file'].includes(item.response_pattern))
285
+ fail(`capabilities[${index}].response_pattern is invalid`);
286
+ record(item.operation, `capabilities[${index}].operation`);
287
+ const expectedOperationKey = `${text(item.operation.method, 'operation.method').toUpperCase()} ${text(item.operation.path, 'operation.path')}`;
288
+ if (item.operation_key !== expectedOperationKey)
289
+ fail(`capabilities[${index}].operation_key does not match its operation`);
290
+ if (operationKeys.has(item.operation_key)) fail(`Duplicate capability: ${item.operation_key}`);
291
+ operationKeys.add(item.operation_key);
292
+ capabilityByKey.set(item.operation_key, item);
293
+ if (!Array.isArray(item.context_evidence) || item.context_evidence.length === 0)
294
+ fail(`capabilities[${index}].context_evidence is invalid`);
295
+ item.context_evidence.forEach((evidence, evidenceIndex) =>
296
+ validateEvidence(evidence, `capabilities[${index}].context_evidence[${evidenceIndex}]`),
297
+ );
298
+ validateSortedUniqueStrings(item.component_refs, `capabilities[${index}].component_refs`);
299
+ for (const pointer of item.component_refs)
300
+ if (!Object.hasOwn(components, pointer))
301
+ fail(`Capability references a missing component: ${pointer}`);
302
+ }
303
+ const packets = record(bundle.work_packets, 'work_packets');
304
+ exactKeys(packets, ['cli', 'skill'], 'work_packets');
305
+ if (!Array.isArray(packets.cli) || packets.cli.length === 0) fail('work_packets.cli is invalid');
306
+ const assigned = new Set();
307
+ const writable = new Set();
308
+ const packetIds = new Set();
309
+ for (const [index, value] of packets.cli.entries()) {
310
+ const packet = record(value, `work_packets.cli[${index}]`);
311
+ exactKeys(
312
+ packet,
313
+ [
314
+ 'id',
315
+ 'kind',
316
+ 'command_family',
317
+ 'capabilities',
318
+ 'writable_paths',
319
+ 'helper_signatures',
320
+ 'focused_check',
321
+ ],
322
+ `work_packets.cli[${index}]`,
323
+ );
324
+ if (packet.kind !== 'cli') fail(`work_packets.cli[${index}].kind is invalid`);
325
+ text(packet.id, 'packet.id');
326
+ if (packetIds.has(packet.id)) fail(`Duplicate work packet: ${packet.id}`);
327
+ packetIds.add(packet.id);
328
+ text(packet.command_family, 'packet.command_family');
329
+ if (!Array.isArray(packet.capabilities) || packet.capabilities.length === 0)
330
+ fail('packet.capabilities is invalid');
331
+ if (packet.capabilities.length > 3) fail('CLI work packet exceeds three operations');
332
+ for (const [capabilityIndex, packetCapabilityValue] of packet.capabilities.entries()) {
333
+ const packetCapability = record(
334
+ packetCapabilityValue,
335
+ `work_packets.cli[${index}].capabilities[${capabilityIndex}]`,
336
+ );
337
+ const key = text(packetCapability.operation_key, 'packet capability operation_key');
338
+ if (!operationKeys.has(key) || assigned.has(key)) fail(`Invalid packet operation: ${key}`);
339
+ const trusted = capabilityByKey.get(key);
340
+ for (const name of ['effect', 'business_purpose', 'proposed_command', 'response_pattern'])
341
+ if (packetCapability[name] !== trusted[name])
342
+ fail(`Packet capability does not match trusted capability: ${key}`);
343
+ assigned.add(key);
344
+ }
345
+ validateSortedUniqueStrings([...packet.writable_paths].sort(), 'packet.writable_paths', false);
346
+ for (const path of packet.writable_paths) {
347
+ const packagePath = path.slice('package/'.length);
348
+ if (
349
+ !path.startsWith('package/') ||
350
+ path.includes('..') ||
351
+ writable.has(path) ||
352
+ foundation.protected_paths.includes(packagePath)
353
+ )
354
+ fail(`Invalid or overlapping writable path: ${path}`);
355
+ writable.add(path);
356
+ }
357
+ if (!Array.isArray(packet.helper_signatures) || packet.helper_signatures.length === 0)
358
+ fail('packet.helper_signatures is invalid');
359
+ packet.helper_signatures.forEach((item) => text(item, 'packet.helper_signatures'));
360
+ text(packet.focused_check, 'packet.focused_check');
361
+ }
362
+ if (assigned.size !== operationKeys.size) fail('Work packets do not cover every capability');
363
+ const skill = record(packets.skill, 'work_packets.skill');
364
+ exactKeys(
365
+ skill,
366
+ ['id', 'kind', 'capabilities', 'writable_paths', 'excluded_paths', 'focused_check'],
367
+ 'work_packets.skill',
368
+ );
369
+ if (skill.kind !== 'skill') fail('work_packets.skill.kind is invalid');
370
+ validateSortedUniqueStrings(
371
+ [...skill.capabilities].sort(),
372
+ 'work_packets.skill.capabilities',
373
+ false,
374
+ );
375
+ if (
376
+ new Set(skill.capabilities).size !== operationKeys.size ||
377
+ skill.capabilities.some((key) => !operationKeys.has(key))
378
+ )
379
+ fail('Skill packet does not cover every capability');
380
+ validateSortedUniqueStrings(
381
+ [...skill.writable_paths].sort(),
382
+ 'work_packets.skill.writable_paths',
383
+ false,
384
+ );
385
+ validateSortedUniqueStrings(
386
+ [...skill.excluded_paths].sort(),
387
+ 'work_packets.skill.excluded_paths',
388
+ false,
389
+ );
390
+ if (!skill.excluded_paths.some((path) => path.includes('-cli-management/')))
391
+ fail('Skill packet does not protect the CLI-management Skill');
392
+ text(skill.focused_check, 'work_packets.skill.focused_check');
393
+ return bundle;
394
+ }
395
+
396
+ export async function createLeadInput(options) {
397
+ if (!options.skipValidation)
398
+ await validateSourceBrief(
399
+ options.requestPath,
400
+ options.briefPath,
401
+ options.inventoryPath,
402
+ options.expectedRequestHash,
403
+ );
404
+ const requestSource = await jsonBytes(options.requestPath, 'Run request');
405
+ const briefSource = await jsonBytes(options.briefPath, 'Source brief');
406
+ const inventorySource = await jsonBytes(options.inventoryPath, 'Operation inventory');
407
+ const starterSource = await jsonBytes(options.starterPath, 'Starter receipt');
408
+ const checksSource = await jsonBytes(options.starterChecksPath, 'Starter checks');
409
+ if (hash(starterSource.bytes) !== expectedHash(options.expectedStarterHash, 'Starter hash'))
410
+ fail('Starter receipt hash does not match');
411
+ if (
412
+ hash(checksSource.bytes) !==
413
+ expectedHash(options.expectedStarterChecksHash, 'Starter checks hash')
414
+ )
415
+ fail('Starter checks hash does not match');
416
+ if (checksSource.value.passed !== true) fail('Starter checks did not pass');
417
+
418
+ const request = requestSource.value;
419
+ const workspace = dirname(dirname(resolve(options.requestPath)));
420
+ const openapiSource = await jsonBytes(resolve(workspace, request.openapi), 'OpenAPI');
421
+ const profileSource = await jsonBytes(
422
+ resolve(workspace, request.customer_profile),
423
+ 'Customer profile',
424
+ );
425
+ const profile = profileSource.value;
426
+ const profilePackage = record(profile.package, 'Customer profile package');
427
+ const profileApi = record(profile.api, 'Customer profile API');
428
+ const profileAuth = record(profileApi.auth, 'Customer profile auth');
429
+ const inventory = inventorySource.value;
430
+ const operations = Array.isArray(inventory.operations)
431
+ ? inventory.operations
432
+ : fail('Operation inventory has no operations');
433
+ const operationMap = new Map();
434
+ for (const value of operations) {
435
+ const item = record(value, 'Operation inventory item');
436
+ const key = `${text(item.method, 'operation.method').toUpperCase()} ${text(item.path, 'operation.path')}`;
437
+ if (operationMap.has(key)) fail(`Operation inventory is duplicated: ${key}`);
438
+ operationMap.set(key, item);
439
+ }
440
+ const briefCapabilities = Array.isArray(briefSource.value.capabilities)
441
+ ? briefSource.value.capabilities
442
+ : fail('Source brief capabilities are invalid');
443
+ const components = new Map();
444
+ const capabilities = briefCapabilities.map((value, index) => {
445
+ const capability = record(value, `Source brief capability ${index}`);
446
+ const operationRef = record(capability.operation, `Source brief capability ${index} operation`);
447
+ const operationKey = `${text(operationRef.method, 'operation.method').toUpperCase()} ${text(operationRef.path, 'operation.path')}`;
448
+ const operation = operationMap.get(operationKey);
449
+ if (!operation) fail(`Source brief operation is unavailable: ${operationKey}`);
450
+ const closure = referenceClosure(openapiSource.value, operation);
451
+ for (const [pointer, component] of closure) components.set(pointer, component);
452
+ return {
453
+ operation_key: operationKey,
454
+ effect: capability.effect,
455
+ business_purpose: capability.business_purpose,
456
+ proposed_command: capability.proposed_command,
457
+ context_evidence: capability.context_evidence,
458
+ requested_response_pattern: capability.response_pattern,
459
+ operation,
460
+ component_refs: [...closure.keys()].sort(),
461
+ };
462
+ });
463
+ const starter = starterSource.value;
464
+ const foundation = record(starter.foundation, 'Starter foundation');
465
+ const context = Array.isArray(request.context)
466
+ ? request.context
467
+ : fail('Run request context is invalid');
468
+ const preparedCapabilities = capabilities.map((capability) => ({
469
+ ...capability,
470
+ response_pattern: responsePattern(capability),
471
+ }));
472
+ const workPackets = createWorkPackets(capabilities, profilePackage.binary);
473
+ const bundle = {
474
+ schema_version: '2.0.0',
475
+ source_hashes: {
476
+ run_request: hash(requestSource.bytes),
477
+ openapi: request.openapi_hash,
478
+ operation_inventory: request.operation_inventory_hash,
479
+ customer_profile: request.customer_profile_hash,
480
+ source_brief: hash(briefSource.bytes),
481
+ starter_receipt: hash(starterSource.bytes),
482
+ starter_checks: hash(checksSource.bytes),
483
+ context: context.map((value) => ({ path: value.path, sha256: value.hash })),
484
+ },
485
+ customer: {
486
+ integration_id: profile.integration_id,
487
+ package_name: profilePackage.name,
488
+ binary: profilePackage.binary,
489
+ display_name: profilePackage.display_name,
490
+ base_url: profileApi.base_url,
491
+ auth_environment_variable: profileAuth.environment_variable,
492
+ login_prompt: profileAuth.login_prompt,
493
+ },
494
+ foundation: {
495
+ package_directory: starter.package_directory,
496
+ starter_verified: true,
497
+ protected_paths: [
498
+ ...Object.keys(foundation),
499
+ 'capabilities.json',
500
+ 'mock/routes.ts',
501
+ 'src/commands.ts',
502
+ ].sort(),
503
+ prepared_hashes: {},
504
+ },
505
+ constraints: {
506
+ no_live_api_calls: true,
507
+ no_credentials: true,
508
+ offline_tests: true,
509
+ human_release_review_required: true,
510
+ },
511
+ components_by_pointer: Object.fromEntries(
512
+ [...components.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),
513
+ ),
514
+ capabilities: preparedCapabilities.map((item) => {
515
+ const capability = { ...item };
516
+ delete capability.requested_response_pattern;
517
+ return capability;
518
+ }),
519
+ work_packets: workPackets,
520
+ };
521
+ bundle.foundation.prepared_hashes = await scaffoldPreparedWork(
522
+ resolve(workspace, bundle.foundation.package_directory),
523
+ bundle,
524
+ );
525
+ validateLeadInput(bundle);
526
+ const bytes = Buffer.from(`${JSON.stringify(bundle, null, 2)}\n`);
527
+ if (bytes.length > MAX_LEAD_INPUT_BYTES) fail('Lead input exceeds 20 MiB');
528
+ const leadInputHash = hash(bytes);
529
+ await writeFile(options.outputPath, bytes, { flag: 'wx', mode: 0o444 });
530
+ await chmod(options.outputPath, 0o444);
531
+ const contextDocuments = await businessContext(workspace, bundle.source_hashes.context);
532
+ const generatorInput = generatorInputBytes(bundle, leadInputHash, contextDocuments);
533
+ const generatorInputPath = join(dirname(options.outputPath), 'generator-input.json');
534
+ await writeFile(generatorInputPath, generatorInput, {
535
+ flag: 'wx',
536
+ mode: 0o444,
537
+ });
538
+ await chmod(generatorInputPath, 0o444);
539
+ return { sha256: leadInputHash, sizeBytes: bytes.length, capabilityCount: capabilities.length };
540
+ }
541
+
542
+ export async function verifyLeadInput(path, expected) {
543
+ const bytes = await readFile(path);
544
+ if (bytes.length > MAX_LEAD_INPUT_BYTES) fail('Lead input exceeds 20 MiB');
545
+ if (hash(bytes) !== expectedHash(expected, 'Expected lead input hash'))
546
+ fail('Lead input hash does not match');
547
+ const bundle = validateLeadInput(JSON.parse(bytes.toString('utf8')));
548
+ const workspace = dirname(dirname(resolve(path)));
549
+ const contextDocuments = await businessContext(workspace, bundle.source_hashes.context);
550
+ const expectedGeneratorInput = generatorInputBytes(bundle, hash(bytes), contextDocuments);
551
+ const actualGeneratorInput = await readFile(join(dirname(path), 'generator-input.json'));
552
+ if (!actualGeneratorInput.equals(expectedGeneratorInput)) fail('Generator input is invalid');
553
+ for (const [file, expectedFileHash] of Object.entries(bundle.foundation.prepared_hashes)) {
554
+ const actual = await readFile(resolve(workspace, bundle.foundation.package_directory, file));
555
+ if (hash(actual) !== expectedFileHash) fail(`Prepared foundation changed: ${file}`);
556
+ }
557
+ return hash(bytes);
558
+ }
559
+
560
+ if (
561
+ globalThis.__INTERMESH_BUNDLED__ !== true &&
562
+ process.argv[1] === new URL(import.meta.url).pathname
563
+ ) {
564
+ const [command, ...args] = process.argv.slice(2);
565
+ if (command === 'create') {
566
+ if (args.length !== 9)
567
+ fail('Expected request, brief, inventory, starter, checks, output and hashes');
568
+ const result = await createLeadInput({
569
+ requestPath: args[0],
570
+ briefPath: args[1],
571
+ inventoryPath: args[2],
572
+ starterPath: args[3],
573
+ starterChecksPath: args[4],
574
+ outputPath: args[5],
575
+ expectedRequestHash: args[6],
576
+ expectedStarterHash: args[7],
577
+ expectedStarterChecksHash: args[8],
578
+ });
579
+ process.stdout.write(`${JSON.stringify(result)}\n`);
580
+ } else if (command === 'verify') {
581
+ if (args.length !== 2) fail('Expected lead input path and hash');
582
+ process.stdout.write(`${await verifyLeadInput(args[0], args[1])}\n`);
583
+ } else fail('Expected create or verify');
584
+ }
@@ -0,0 +1,33 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { access, copyFile, mkdir } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { promisify } from 'node:util';
6
+
7
+ const execute = promisify(execFile);
8
+ const bundledAssets = join(dirname(fileURLToPath(import.meta.url)), '..');
9
+
10
+ export async function prepareOpenCodeDependencies(directories) {
11
+ const environment = { ...process.env };
12
+ delete environment.DEEPSEEK_API_KEY;
13
+ delete environment.OPENROUTER_API_KEY;
14
+ for (const directory of directories) {
15
+ const installed = await access(join(directory, 'node_modules')).then(
16
+ () => true,
17
+ () => false,
18
+ );
19
+ if (installed) continue;
20
+ await mkdir(directory, { recursive: true });
21
+ const seed = join(
22
+ process.env.INTERMESH_ASSETS_ROOT ?? bundledAssets,
23
+ 'templates/opencode-bootstrap',
24
+ );
25
+ for (const file of ['package.json', 'package-lock.json'])
26
+ await copyFile(join(seed, file), join(directory, file));
27
+ await execute('npm', ['ci', '--ignore-scripts', '--no-audit', '--no-fund'], {
28
+ cwd: directory,
29
+ timeout: 120_000,
30
+ env: environment,
31
+ });
32
+ }
33
+ }