@vimhead.dev/norn-cli 0.1.0-tip.35614826665.1 → 0.1.0-tip.35724621678.1

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 (119) hide show
  1. package/assets/README.md +41 -13
  2. package/assets/docs/README.md +16 -16
  3. package/assets/docs/agents.md +23 -15
  4. package/assets/docs/cli.md +10 -8
  5. package/assets/docs/composition.md +7 -7
  6. package/assets/docs/persistence.md +22 -22
  7. package/assets/docs/projects.md +6 -6
  8. package/assets/docs/recovery.md +10 -10
  9. package/assets/docs/schemas.md +42 -36
  10. package/assets/docs/workflows.md +39 -32
  11. package/assets/examples/agent-then-analysis/input.json +3 -3
  12. package/assets/examples/agent-then-analysis/norn.project.json +2 -2
  13. package/assets/examples/agent-then-analysis/plugin.ts +37 -9
  14. package/assets/examples/caller-owned-routing/README.md +6 -6
  15. package/assets/examples/caller-owned-routing/assessment.ts +18 -4
  16. package/assets/examples/caller-owned-routing/contracts.ts +7 -2
  17. package/assets/examples/caller-owned-routing/input.json +12 -12
  18. package/assets/examples/caller-owned-routing/norn.project.json +2 -2
  19. package/assets/examples/caller-owned-routing/revision.ts +7 -2
  20. package/assets/examples/caller-owned-routing/router.ts +14 -3
  21. package/assets/examples/caller-selected-continuation/caller.ts +38 -12
  22. package/assets/examples/caller-selected-continuation/input.json +7 -7
  23. package/assets/examples/caller-selected-continuation/norn.project.json +2 -2
  24. package/assets/examples/caller-selected-continuation/producer.ts +10 -3
  25. package/assets/examples/coordinating-multiple-agents/README.md +6 -6
  26. package/assets/examples/coordinating-multiple-agents/input.json +20 -8
  27. package/assets/examples/coordinating-multiple-agents/norn.project.json +2 -2
  28. package/assets/examples/coordinating-multiple-agents/plugin.ts +165 -46
  29. package/assets/examples/coordinating-multiple-agents/queue-tools.ts +30 -7
  30. package/assets/examples/coordinating-multiple-agents/work-queue.ts +211 -63
  31. package/assets/examples/getting-started/norn.project.json +2 -2
  32. package/assets/examples/getting-started/plugin.ts +33 -13
  33. package/assets/examples/minimal-workflow/norn.project.json +2 -2
  34. package/assets/examples/minimal-workflow/plugin.ts +12 -3
  35. package/assets/examples/shared-state/README.md +6 -6
  36. package/assets/examples/shared-state/input.json +1 -1
  37. package/assets/examples/shared-state/norn.project.json +2 -2
  38. package/assets/examples/shared-state/plugin.ts +44 -15
  39. package/assets/examples/shared-state/shared-state.ts +46 -13
  40. package/assets/examples/shared-state/state-tools.ts +68 -16
  41. package/assets/examples/worktree-development-loop/README.md +12 -12
  42. package/assets/examples/worktree-development-loop/norn.project.json +7 -7
  43. package/assets/examples/worktree-development-loop/plugin.ts +7 -1
  44. package/assets/examples/worktree-development-loop/scope.ts +4 -1
  45. package/assets/examples/worktree-development-loop/shared/commands.ts +6 -2
  46. package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +18 -4
  47. package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +4 -1
  48. package/assets/examples/worktree-development-loop/workflows/development-loop/repository.ts +11 -1
  49. package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +6 -2
  50. package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +40 -10
  51. package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +5 -1
  52. package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +1 -1
  53. package/assets/examples/worktree-development-loop/workflows/planning/index.ts +5 -1
  54. package/assets/examples/worktree-development-loop/workflows/review/execute.ts +33 -9
  55. package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +51 -8
  56. package/assets/package.json +1 -1
  57. package/assets/packages/cli/src/build-info.ts +4 -1
  58. package/assets/packages/cli/src/bun/cli.ts +21 -5
  59. package/assets/packages/cli/src/cli.ts +585 -170
  60. package/assets/packages/cli/src/client.ts +194 -36
  61. package/assets/packages/cli/src/documentation.ts +135 -30
  62. package/assets/packages/cli/src/generated-build-info.ts +2 -2
  63. package/assets/packages/cli/src/internal/agent-directory.ts +7 -2
  64. package/assets/packages/cli/src/internal/agent-response-tool.ts +57 -15
  65. package/assets/packages/cli/src/internal/agents.ts +248 -60
  66. package/assets/packages/cli/src/internal/commands.ts +82 -20
  67. package/assets/packages/cli/src/internal/documentation-bundle.ts +44 -13
  68. package/assets/packages/cli/src/internal/engine.ts +396 -87
  69. package/assets/packages/cli/src/internal/errors.ts +13 -5
  70. package/assets/packages/cli/src/internal/execution-context.ts +20 -12
  71. package/assets/packages/cli/src/internal/file-coordinator.ts +95 -26
  72. package/assets/packages/cli/src/internal/file-names.ts +4 -1
  73. package/assets/packages/cli/src/internal/launch-request.ts +74 -22
  74. package/assets/packages/cli/src/internal/logs.ts +10 -2
  75. package/assets/packages/cli/src/internal/metrics.ts +247 -61
  76. package/assets/packages/cli/src/internal/pi-assets.ts +105 -23
  77. package/assets/packages/cli/src/internal/run-lease.ts +72 -22
  78. package/assets/packages/cli/src/internal/run-log.ts +49 -20
  79. package/assets/packages/cli/src/internal/run-names.ts +120 -12
  80. package/assets/packages/cli/src/internal/run-state.ts +306 -71
  81. package/assets/packages/cli/src/internal/run-store.ts +216 -53
  82. package/assets/packages/cli/src/internal/usage.ts +23 -7
  83. package/assets/packages/cli/src/internal/worker-directory.ts +9 -3
  84. package/assets/packages/cli/src/internal/workflow-registry.ts +281 -70
  85. package/assets/packages/cli/src/internal/working-directory.ts +2 -1
  86. package/assets/packages/cli/src/workflow-loader.ts +341 -74
  87. package/assets/packages/core/src/atomic-files.ts +13 -4
  88. package/assets/packages/core/src/workflow-transition.ts +13 -2
  89. package/assets/packages/sdk/src/api.ts +248 -80
  90. package/assets/packages/sdk/src/index.ts +6 -1
  91. package/assets/packages/sdk/src/schema.ts +75 -21
  92. package/assets/tests/workflow-ref.test.ts +242 -57
  93. package/bin/norn.mjs +13 -4
  94. package/dist/cli.js +344 -90
  95. package/dist/client.js +100 -22
  96. package/dist/documentation.js +102 -25
  97. package/dist/generated-build-info.d.ts +2 -2
  98. package/dist/generated-build-info.js +2 -2
  99. package/dist/internal/agent-directory.js +3 -1
  100. package/dist/internal/agent-response-tool.js +42 -11
  101. package/dist/internal/agents.js +149 -37
  102. package/dist/internal/commands.js +65 -16
  103. package/dist/internal/documentation-bundle.js +23 -11
  104. package/dist/internal/engine.js +304 -66
  105. package/dist/internal/errors.js +4 -2
  106. package/dist/internal/execution-context.js +2 -1
  107. package/dist/internal/file-coordinator.js +59 -19
  108. package/dist/internal/launch-request.js +32 -14
  109. package/dist/internal/metrics.js +138 -38
  110. package/dist/internal/pi-assets.js +70 -19
  111. package/dist/internal/run-lease.js +29 -9
  112. package/dist/internal/run-log.js +24 -12
  113. package/dist/internal/run-state.js +172 -48
  114. package/dist/internal/run-store.js +157 -45
  115. package/dist/internal/worker-directory.js +6 -2
  116. package/dist/internal/workflow-registry.js +123 -32
  117. package/dist/internal/working-directory.js +2 -1
  118. package/dist/workflow-loader.js +198 -41
  119. package/package.json +2 -2
@@ -22,12 +22,18 @@ import {
22
22
  } from "./agent-response-tool.js";
23
23
  import { errorMessage } from "./errors.js";
24
24
  import { safeFileName } from "./file-names.js";
25
- import { agentUsageFromValue, emptyAgentUsage, totalAgentUsage } from "./usage.js";
25
+ import {
26
+ agentUsageFromValue,
27
+ emptyAgentUsage,
28
+ totalAgentUsage
29
+ } from "./usage.js";
26
30
  var DEFAULT_AGENT_ATTEMPTS = 3;
27
31
  var NornAgentRunner = class {
28
32
  constructor(input) {
29
33
  this.input = input;
30
- this.responseToolFactory = new NornAgentResponseToolFactory(input.responseCollector);
34
+ this.responseToolFactory = new NornAgentResponseToolFactory(
35
+ input.responseCollector
36
+ );
31
37
  }
32
38
  input;
33
39
  responseToolFactory;
@@ -49,7 +55,9 @@ var NornAgentRunner = class {
49
55
  const loader = services.resourceLoader;
50
56
  const errors = [
51
57
  ...services.diagnostics.filter((diagnostic) => diagnostic.type === "error").map((diagnostic) => diagnostic.message),
52
- ...loader.getExtensions().errors.map((error) => `Failed to load extension ${error.path}: ${error.error}`)
58
+ ...loader.getExtensions().errors.map(
59
+ (error) => `Failed to load extension ${error.path}: ${error.error}`
60
+ )
53
61
  ];
54
62
  if (errors.length > 0) throw new Error(errors.join("\n"));
55
63
  const customTools = agentInput.customTools ?? [];
@@ -80,7 +88,11 @@ var NornAgentRunner = class {
80
88
  }));
81
89
  await agentInput.beforeSessionStart?.({ events: eventBus });
82
90
  await session.bindExtensions({});
83
- await this.input.logger.record({ type: "agent.spawned", label: agentInput.label, cwd });
91
+ await this.input.logger.record({
92
+ type: "agent.spawned",
93
+ label: agentInput.label,
94
+ cwd
95
+ });
84
96
  return new CreatedNornAgentSession({
85
97
  ...this.input,
86
98
  label: agentInput.label,
@@ -95,7 +107,8 @@ var NornAgentRunner = class {
95
107
  } catch (cleanupError) {
96
108
  errors2.push(cleanupError);
97
109
  }
98
- if (errors2.length > 1) throw new AggregateError(errors2, "Agent creation and cleanup failed");
110
+ if (errors2.length > 1)
111
+ throw new AggregateError(errors2, "Agent creation and cleanup failed");
99
112
  throw error;
100
113
  }
101
114
  }
@@ -132,22 +145,42 @@ var CreatedNornAgentSession = class {
132
145
  return (await this.promptWithResult(agentInput)).response;
133
146
  }
134
147
  async promptWithResult(agentInput) {
135
- if (this.isDisposed) throw new Error(`Workflow agent session is disposed: ${this.label}`);
136
- const maxAttempts = Math.max(1, Math.floor(agentInput.maxAttempts ?? DEFAULT_AGENT_ATTEMPTS));
148
+ if (this.isDisposed)
149
+ throw new Error(`Workflow agent session is disposed: ${this.label}`);
150
+ const maxAttempts = Math.max(
151
+ 1,
152
+ Math.floor(agentInput.maxAttempts ?? DEFAULT_AGENT_ATTEMPTS)
153
+ );
137
154
  const attempts = [];
138
155
  const startedAtMs = Date.now();
139
156
  let isTerminalEventRecorded = false;
140
- await this.input.logger.record({ type: "agent.started", label: this.label, cwd: this.cwd, maxAttempts });
157
+ await this.input.logger.record({
158
+ type: "agent.started",
159
+ label: this.label,
160
+ cwd: this.cwd,
161
+ maxAttempts
162
+ });
141
163
  try {
142
164
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
143
- await this.input.logger.record({ type: "agent.attempt.started", label: this.label, attempt });
144
- const raw = await this.runAttempt(agentInput, attempt, attempts.at(-1)?.error);
165
+ await this.input.logger.record({
166
+ type: "agent.attempt.started",
167
+ label: this.label,
168
+ attempt
169
+ });
170
+ const raw = await this.runAttempt(
171
+ agentInput,
172
+ attempt,
173
+ attempts.at(-1)?.error
174
+ );
145
175
  const rawAttempt = { attempt, ...raw };
146
176
  attempts.push(rawAttempt);
147
177
  try {
148
- if (!raw.responseToolCalled) throw new Error(`Agent did not call ${AGENT_RESPONSE_TOOL_NAME}`);
178
+ if (!raw.responseToolCalled)
179
+ throw new Error(`Agent did not call ${AGENT_RESPONSE_TOOL_NAME}`);
149
180
  const response = raw.toolResponse;
150
- const usage = totalAgentUsage(attempts.map((candidate) => candidate.usage));
181
+ const usage = totalAgentUsage(
182
+ attempts.map((candidate) => candidate.usage)
183
+ );
151
184
  const result = {
152
185
  label: this.label,
153
186
  cwd: this.cwd,
@@ -155,22 +188,49 @@ var CreatedNornAgentSession = class {
155
188
  usage,
156
189
  raw: { ...raw, usage, attempts }
157
190
  };
158
- await this.input.logs.write(`agents/${safeFileName(this.label)}.json`, JSON.stringify(result, null, 2));
191
+ await this.input.logs.write(
192
+ `agents/${safeFileName(this.label)}.json`,
193
+ JSON.stringify(result, null, 2)
194
+ );
159
195
  isTerminalEventRecorded = true;
160
- await this.input.logger.record({ type: "agent.completed", label: this.label, attempts: attempt, durationMs: Date.now() - startedAtMs, usage });
196
+ await this.input.logger.record({
197
+ type: "agent.completed",
198
+ label: this.label,
199
+ attempts: attempt,
200
+ durationMs: Date.now() - startedAtMs,
201
+ usage
202
+ });
161
203
  return result;
162
204
  } catch (error) {
163
205
  const message = errorMessage(error);
164
206
  attempts[attempts.length - 1] = { ...rawAttempt, error: message };
165
207
  await this.input.logs.write(
166
208
  `agents/${safeFileName(this.label)}.attempt-${attempt}.raw.json`,
167
- JSON.stringify({ label: this.label, cwd: this.cwd, raw: attempts.at(-1) }, null, 2)
209
+ JSON.stringify(
210
+ { label: this.label, cwd: this.cwd, raw: attempts.at(-1) },
211
+ null,
212
+ 2
213
+ )
168
214
  );
169
- await this.input.logger.record({ type: "agent.attempt.failed", label: this.label, attempt, error: message });
215
+ await this.input.logger.record({
216
+ type: "agent.attempt.failed",
217
+ label: this.label,
218
+ attempt,
219
+ error: message
220
+ });
170
221
  if (attempt === maxAttempts) {
171
- const usage = totalAgentUsage(attempts.map((candidate) => candidate.usage));
222
+ const usage = totalAgentUsage(
223
+ attempts.map((candidate) => candidate.usage)
224
+ );
172
225
  isTerminalEventRecorded = true;
173
- await this.input.logger.record({ type: "agent.failed", label: this.label, attempts: attempt, durationMs: Date.now() - startedAtMs, usage, error: message });
226
+ await this.input.logger.record({
227
+ type: "agent.failed",
228
+ label: this.label,
229
+ attempts: attempt,
230
+ durationMs: Date.now() - startedAtMs,
231
+ usage,
232
+ error: message
233
+ });
174
234
  throw new Error(
175
235
  `Agent ${this.label} did not return a valid structured response after ${attempt} attempt(s). Raw output saved to logs/agents/${safeFileName(this.label)}.attempt-${attempt}.raw.json: ${message}`
176
236
  );
@@ -206,35 +266,61 @@ var CreatedNornAgentSession = class {
206
266
  } catch (error) {
207
267
  errors.push(error);
208
268
  }
209
- if (errors.length) throw new AggregateError(errors, "Agent disposal failed");
210
- await this.input.logger.record({ type: "agent.disposed", label: this.label });
269
+ if (errors.length)
270
+ throw new AggregateError(errors, "Agent disposal failed");
271
+ await this.input.logger.record({
272
+ type: "agent.disposed",
273
+ label: this.label
274
+ });
211
275
  }
212
276
  async runAttempt(agentInput, attempt, previousError) {
213
277
  const responseRunId = `${this.input.id}:${attempt}:${randomUUID()}`;
214
278
  const messages = [];
215
279
  let text = "";
216
- const releaseResponseSlot = this.input.responseCollector.begin(responseRunId, this.label, agentInput.response);
280
+ const releaseResponseSlot = this.input.responseCollector.begin(
281
+ responseRunId,
282
+ this.label,
283
+ agentInput.response
284
+ );
217
285
  const unsubscribe = this.input.session.subscribe((event) => {
218
286
  if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
219
287
  text += event.assistantMessageEvent.delta;
220
288
  }
221
- if (event.type === "message_end" && event.message) messages.push(event.message);
289
+ if (event.type === "message_end" && event.message)
290
+ messages.push(event.message);
222
291
  });
223
292
  const abortNestedSession = () => {
224
293
  void this.input.session.abort();
225
294
  };
226
- this.input.signal?.addEventListener("abort", abortNestedSession, { once: true });
295
+ this.input.signal?.addEventListener("abort", abortNestedSession, {
296
+ once: true
297
+ });
227
298
  let capturedResponse = { called: false };
228
299
  try {
229
300
  this.input.signal?.throwIfAborted();
230
- await this.input.session.prompt(withResponseToolInstruction(agentInput.prompt, agentInput.response, this.label, responseRunId, attempt, previousError), {
231
- ...agentInput.options,
232
- source: agentInput.options?.source ?? "extension"
233
- });
301
+ await this.input.session.prompt(
302
+ withResponseToolInstruction(
303
+ agentInput.prompt,
304
+ agentInput.response,
305
+ this.label,
306
+ responseRunId,
307
+ attempt,
308
+ previousError
309
+ ),
310
+ {
311
+ ...agentInput.options,
312
+ source: agentInput.options?.source ?? "extension"
313
+ }
314
+ );
234
315
  this.input.signal?.throwIfAborted();
235
316
  capturedResponse = this.input.responseCollector.get(responseRunId);
236
317
  if (!capturedResponse.called) {
237
- await this.promptForStructuredResponse(agentInput, responseRunId, attempt, previousError);
318
+ await this.promptForStructuredResponse(
319
+ agentInput,
320
+ responseRunId,
321
+ attempt,
322
+ previousError
323
+ );
238
324
  this.input.signal?.throwIfAborted();
239
325
  capturedResponse = this.input.responseCollector.get(responseRunId);
240
326
  }
@@ -254,16 +340,38 @@ var CreatedNornAgentSession = class {
254
340
  }
255
341
  async promptForStructuredResponse(agentInput, responseRunId, attempt, previousError) {
256
342
  const activeToolNames = this.input.session.getActiveToolNames();
257
- await this.input.logger.record({ type: "agent.response-finalization.started", label: this.label, attempt });
343
+ await this.input.logger.record({
344
+ type: "agent.response-finalization.started",
345
+ label: this.label,
346
+ attempt
347
+ });
258
348
  this.input.session.setActiveToolsByName([AGENT_RESPONSE_TOOL_NAME]);
259
349
  try {
260
- await this.input.session.prompt(withResponseToolFinalizationInstruction(agentInput.response, this.label, responseRunId, attempt, previousError), {
261
- ...agentInput.options,
262
- source: agentInput.options?.source ?? "extension"
350
+ await this.input.session.prompt(
351
+ withResponseToolFinalizationInstruction(
352
+ agentInput.response,
353
+ this.label,
354
+ responseRunId,
355
+ attempt,
356
+ previousError
357
+ ),
358
+ {
359
+ ...agentInput.options,
360
+ source: agentInput.options?.source ?? "extension"
361
+ }
362
+ );
363
+ await this.input.logger.record({
364
+ type: "agent.response-finalization.completed",
365
+ label: this.label,
366
+ attempt
263
367
  });
264
- await this.input.logger.record({ type: "agent.response-finalization.completed", label: this.label, attempt });
265
368
  } catch (error) {
266
- await this.input.logger.record({ type: "agent.response-finalization.failed", label: this.label, attempt, error: errorMessage(error) });
369
+ await this.input.logger.record({
370
+ type: "agent.response-finalization.failed",
371
+ label: this.label,
372
+ attempt,
373
+ error: errorMessage(error)
374
+ });
267
375
  throw error;
268
376
  } finally {
269
377
  this.input.session.setActiveToolsByName(activeToolNames);
@@ -271,10 +379,13 @@ var CreatedNornAgentSession = class {
271
379
  }
272
380
  };
273
381
  function usageFromMessages(messages) {
274
- return totalAgentUsage(messages.map(usageFromMessage).filter((usage) => usage !== void 0));
382
+ return totalAgentUsage(
383
+ messages.map(usageFromMessage).filter((usage) => usage !== void 0)
384
+ );
275
385
  }
276
386
  function usageFromMessage(message) {
277
- if (!message || typeof message !== "object" || !("usage" in message)) return void 0;
387
+ if (!message || typeof message !== "object" || !("usage" in message))
388
+ return void 0;
278
389
  return agentUsageFromValue(message.usage) ?? emptyAgentUsage();
279
390
  }
280
391
  function withResponseToolInstruction(prompt, responseSchema, label, responseRunId, attempt, previousError) {
@@ -311,7 +422,8 @@ function withAgentResponseTool(tools) {
311
422
  function assertCustomToolNames(input) {
312
423
  const names = new Set(input.reservedNames);
313
424
  for (const tool of input.tools) {
314
- if (names.has(tool.name)) throw new Error(`Custom tool name collision: ${tool.name}`);
425
+ if (names.has(tool.name))
426
+ throw new Error(`Custom tool name collision: ${tool.name}`);
315
427
  names.add(tool.name);
316
428
  }
317
429
  }
@@ -13,9 +13,25 @@ var NornCommandRunner = class {
13
13
  const cwd = requireAbsoluteWorkingDirectory(commandInput.cwd);
14
14
  const startedAtMs = Date.now();
15
15
  const invocationId = randomUUID();
16
- const stdoutRef = commandLog({ label: commandInput.label, invocationId, stream: "stdout" });
17
- const stderrRef = commandLog({ label: commandInput.label, invocationId, stream: "stderr" });
18
- await this.input.logger.record({ type: "command.started", invocationId, label: commandInput.label, command: commandInput.command, cwd, stdoutLogId: stdoutRef.id, stderrLogId: stderrRef.id });
16
+ const stdoutRef = commandLog({
17
+ label: commandInput.label,
18
+ invocationId,
19
+ stream: "stdout"
20
+ });
21
+ const stderrRef = commandLog({
22
+ label: commandInput.label,
23
+ invocationId,
24
+ stream: "stderr"
25
+ });
26
+ await this.input.logger.record({
27
+ type: "command.started",
28
+ invocationId,
29
+ label: commandInput.label,
30
+ command: commandInput.command,
31
+ cwd,
32
+ stdoutLogId: stdoutRef.id,
33
+ stderrLogId: stderrRef.id
34
+ });
19
35
  let result;
20
36
  const stdoutLog = await this.input.logs.createWriteStream(stdoutRef);
21
37
  const stderrLog = await this.input.logs.createWriteStream(stderrRef);
@@ -30,7 +46,13 @@ var NornCommandRunner = class {
30
46
  stderrStream: stderrLog.stream
31
47
  });
32
48
  } catch (error) {
33
- await this.input.logger.record({ type: "command.failed", invocationId, label: commandInput.label, durationMs: Date.now() - startedAtMs, error: errorMessage(error) });
49
+ await this.input.logger.record({
50
+ type: "command.failed",
51
+ invocationId,
52
+ label: commandInput.label,
53
+ durationMs: Date.now() - startedAtMs,
54
+ error: errorMessage(error)
55
+ });
34
56
  throw error;
35
57
  }
36
58
  const commandResult = {
@@ -64,7 +86,11 @@ async function spawnCommand(input) {
64
86
  const stdout = new BoundedTextBuffer();
65
87
  const stderr = new BoundedTextBuffer();
66
88
  return new Promise((resolvePromise, reject) => {
67
- const child = spawn(command, args, { cwd: input.cwd, env: input.env, shell: false });
89
+ const child = spawn(command, args, {
90
+ cwd: input.cwd,
91
+ env: input.env,
92
+ shell: false
93
+ });
68
94
  let timeout;
69
95
  let escalation;
70
96
  let isSettled = false;
@@ -77,24 +103,32 @@ async function spawnCommand(input) {
77
103
  if (isSettled) return;
78
104
  isSettled = true;
79
105
  cleanup();
80
- void closeStreams(input.stdoutStream, input.stderrStream).then(() => resolvePromise(result), reject);
106
+ void closeStreams(input.stdoutStream, input.stderrStream).then(
107
+ () => resolvePromise(result),
108
+ reject
109
+ );
81
110
  };
82
111
  const fail = (error) => {
83
112
  if (isSettled) return;
84
113
  isSettled = true;
85
114
  cleanup();
86
- void closeStreams(input.stdoutStream, input.stderrStream).then(() => reject(error), reject);
115
+ void closeStreams(input.stdoutStream, input.stderrStream).then(
116
+ () => reject(error),
117
+ reject
118
+ );
87
119
  };
88
120
  const killChild = () => {
89
121
  if (isSettled || killed) return;
90
122
  killed = true;
91
123
  child.kill("SIGTERM");
92
124
  escalation = setTimeout(() => {
93
- if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
125
+ if (child.exitCode === null && child.signalCode === null)
126
+ child.kill("SIGKILL");
94
127
  }, 5e3);
95
128
  escalation.unref?.();
96
129
  };
97
- if (input.timeoutMs !== void 0) timeout = setTimeout(killChild, input.timeoutMs);
130
+ if (input.timeoutMs !== void 0)
131
+ timeout = setTimeout(killChild, input.timeoutMs);
98
132
  if (input.signal?.aborted) killChild();
99
133
  input.signal?.addEventListener("abort", killChild, { once: true });
100
134
  child.stdout?.on("data", (chunk) => {
@@ -108,11 +142,21 @@ async function spawnCommand(input) {
108
142
  input.stderrStream.write(text);
109
143
  });
110
144
  child.on("error", fail);
111
- child.on("close", (code) => finish({ exitCode: code, stdoutTail: stdout.value(), stderrTail: stderr.value(), killed }));
145
+ child.on(
146
+ "close",
147
+ (code) => finish({
148
+ exitCode: code,
149
+ stdoutTail: stdout.value(),
150
+ stderrTail: stderr.value(),
151
+ killed
152
+ })
153
+ );
112
154
  });
113
155
  }
114
156
  function commandLog(input) {
115
- return { id: `commands/${safeFileName(input.label)}-${input.invocationId}.${input.stream}` };
157
+ return {
158
+ id: `commands/${safeFileName(input.label)}-${input.invocationId}.${input.stream}`
159
+ };
116
160
  }
117
161
  var BoundedTextBuffer = class {
118
162
  constructor(maxChars = 128e3) {
@@ -122,17 +166,22 @@ var BoundedTextBuffer = class {
122
166
  text = "";
123
167
  append(value) {
124
168
  this.text += value;
125
- if (this.text.length > this.maxChars) this.text = this.text.slice(this.text.length - this.maxChars);
169
+ if (this.text.length > this.maxChars)
170
+ this.text = this.text.slice(this.text.length - this.maxChars);
126
171
  }
127
172
  value() {
128
173
  return this.text;
129
174
  }
130
175
  };
131
176
  async function closeStreams(...streams) {
132
- await Promise.all(streams.map((stream) => new Promise((resolvePromise, reject) => {
133
- stream.on("error", reject);
134
- stream.end(resolvePromise);
135
- })));
177
+ await Promise.all(
178
+ streams.map(
179
+ (stream) => new Promise((resolvePromise, reject) => {
180
+ stream.on("error", reject);
181
+ stream.end(resolvePromise);
182
+ })
183
+ )
184
+ );
136
185
  }
137
186
  export {
138
187
  NornCommandRunner
@@ -7,33 +7,45 @@ var DOCUMENTATION_PATHS = {
7
7
  examples: "examples"
8
8
  };
9
9
  function validateDocumentationBundle(bundle) {
10
- if (typeof bundle.version !== "string" || bundle.version.length === 0) throw new Error("Documentation bundle version must not be empty");
10
+ if (typeof bundle.version !== "string" || bundle.version.length === 0)
11
+ throw new Error("Documentation bundle version must not be empty");
11
12
  const paths = /* @__PURE__ */ new Set();
12
13
  for (const file of bundle.files) {
13
- if (typeof file.content !== "string") throw new Error(`Documentation asset must contain text: ${file.path}`);
14
- if (typeof file.path !== "string" || !/^[a-zA-Z0-9._/-]+$/.test(file.path) || file.path.split("/").some((part) => part === "" || part === "." || part === ".." || part.endsWith(".") || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part))) {
14
+ if (typeof file.content !== "string")
15
+ throw new Error(`Documentation asset must contain text: ${file.path}`);
16
+ if (typeof file.path !== "string" || !/^[a-zA-Z0-9._/-]+$/.test(file.path) || file.path.split("/").some(
17
+ (part) => part === "" || part === "." || part === ".." || part.endsWith(".") || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part)
18
+ )) {
15
19
  throw new Error(`Invalid documentation asset path: ${file.path}`);
16
20
  }
17
21
  const portablePath = file.path.toLowerCase();
18
- if (paths.has(portablePath)) throw new Error(`Duplicate documentation asset path: ${file.path}`);
22
+ if (paths.has(portablePath))
23
+ throw new Error(`Duplicate documentation asset path: ${file.path}`);
19
24
  paths.add(portablePath);
20
25
  }
21
26
  for (const path of paths) {
22
27
  const parts = path.split("/");
23
28
  while (parts.pop() && parts.length > 0) {
24
- if (paths.has(parts.join("/"))) throw new Error(`Documentation file/directory collision: ${path}`);
29
+ if (paths.has(parts.join("/")))
30
+ throw new Error(`Documentation file/directory collision: ${path}`);
25
31
  }
26
32
  }
27
33
  for (const path of [DOCUMENTATION_PATHS.readme, DOCUMENTATION_PATHS.index]) {
28
- if (!bundle.files.some((file) => file.path === path)) throw new Error(`Missing documentation asset: ${path}`);
34
+ if (!bundle.files.some((file) => file.path === path))
35
+ throw new Error(`Missing documentation asset: ${path}`);
29
36
  }
30
- if (!bundle.files.some((file) => file.path.startsWith("examples/"))) throw new Error("Missing documentation examples");
37
+ if (!bundle.files.some((file) => file.path.startsWith("examples/")))
38
+ throw new Error("Missing documentation examples");
31
39
  }
32
40
  function hashDocumentationBundle(bundle) {
33
- return createHash("sha256").update(JSON.stringify({
34
- version: bundle.version,
35
- files: bundle.files.map((file) => ({ path: file.path, content: file.content })).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0)
36
- })).digest("hex");
41
+ return createHash("sha256").update(
42
+ JSON.stringify({
43
+ version: bundle.version,
44
+ files: bundle.files.map((file) => ({ path: file.path, content: file.content })).sort(
45
+ (left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0
46
+ )
47
+ })
48
+ ).digest("hex");
37
49
  }
38
50
  export {
39
51
  DOCUMENTATION_PATHS,