@moda-ai/cli 1.22.1 → 1.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/cli-g8qs2tj9.js +9777 -0
  2. package/dist/cli-pwbvtes8.js +2312 -0
  3. package/dist/cli-qszcpda3.js +284 -0
  4. package/dist/cli.js +4775 -1159
  5. package/dist/index-6h0nc1r5.js +2765 -0
  6. package/dist/provision-j5gq8cvx.js +72 -0
  7. package/package.json +3 -2
  8. package/skills/integration/index.json +2 -2
  9. package/dist/agent-schemas.d.ts +0 -3
  10. package/dist/agent-schemas.js +0 -358
  11. package/dist/agent.d.ts +0 -5
  12. package/dist/agent.js +0 -477
  13. package/dist/analyst-tui.d.ts +0 -81
  14. package/dist/analyst-tui.js +0 -314
  15. package/dist/api-client.d.ts +0 -46
  16. package/dist/api-client.js +0 -120
  17. package/dist/ask-stream.d.ts +0 -146
  18. package/dist/ask-stream.js +0 -473
  19. package/dist/auth-storage.d.ts +0 -35
  20. package/dist/auth-storage.js +0 -159
  21. package/dist/auth.d.ts +0 -50
  22. package/dist/auth.js +0 -694
  23. package/dist/cli-session.d.ts +0 -26
  24. package/dist/cli-session.js +0 -169
  25. package/dist/cli.d.ts +0 -44
  26. package/dist/command-context.d.ts +0 -62
  27. package/dist/command-context.js +0 -57
  28. package/dist/command-registry.d.ts +0 -22
  29. package/dist/command-registry.js +0 -15
  30. package/dist/config.d.ts +0 -60
  31. package/dist/config.js +0 -110
  32. package/dist/doctor.d.ts +0 -79
  33. package/dist/doctor.js +0 -1182
  34. package/dist/errors.d.ts +0 -50
  35. package/dist/errors.js +0 -64
  36. package/dist/exit-funnel.d.ts +0 -14
  37. package/dist/exit-funnel.js +0 -53
  38. package/dist/harness-artifact-bodies.d.ts +0 -28
  39. package/dist/harness-artifact-bodies.js +0 -276
  40. package/dist/harness-candidates.d.ts +0 -77
  41. package/dist/harness-candidates.js +0 -482
  42. package/dist/harness-remote.d.ts +0 -135
  43. package/dist/harness-remote.js +0 -540
  44. package/dist/harness-report.d.ts +0 -145
  45. package/dist/harness-report.js +0 -1579
  46. package/dist/harness-sync-limits.d.ts +0 -8
  47. package/dist/harness-sync-limits.js +0 -21
  48. package/dist/harness.d.ts +0 -463
  49. package/dist/harness.js +0 -4009
  50. package/dist/init/index.d.ts +0 -78
  51. package/dist/init/index.js +0 -1442
  52. package/dist/init/mcp.d.ts +0 -2
  53. package/dist/init/mcp.js +0 -27
  54. package/dist/init/progress-board.d.ts +0 -80
  55. package/dist/init/progress-board.js +0 -225
  56. package/dist/init/rules.d.ts +0 -6
  57. package/dist/init/rules.js +0 -171
  58. package/dist/init/sdk-catalog.d.ts +0 -10
  59. package/dist/init/sdk-catalog.js +0 -80
  60. package/dist/init/sdk-guide.d.ts +0 -19
  61. package/dist/init/sdk-guide.js +0 -185
  62. package/dist/init/sdk-integration.d.ts +0 -53
  63. package/dist/init/sdk-integration.js +0 -835
  64. package/dist/init/sdk-protocol.d.ts +0 -135
  65. package/dist/init/sdk-protocol.js +0 -152
  66. package/dist/init/sdk-verification.d.ts +0 -27
  67. package/dist/init/sdk-verification.js +0 -123
  68. package/dist/init/tenant.d.ts +0 -67
  69. package/dist/init/tenant.js +0 -421
  70. package/dist/intelligence-schema.d.ts +0 -349
  71. package/dist/intelligence-schema.js +0 -174
  72. package/dist/keys.d.ts +0 -47
  73. package/dist/keys.js +0 -136
  74. package/dist/manifest.d.ts +0 -5
  75. package/dist/manifest.js +0 -114
  76. package/dist/markdown-terminal.d.ts +0 -49
  77. package/dist/markdown-terminal.js +0 -212
  78. package/dist/output.d.ts +0 -191
  79. package/dist/output.js +0 -624
  80. package/dist/production-intelligence.d.ts +0 -175
  81. package/dist/production-intelligence.js +0 -1430
  82. package/dist/profile-commands.d.ts +0 -10
  83. package/dist/profile-commands.js +0 -342
  84. package/dist/profiles.d.ts +0 -112
  85. package/dist/profiles.js +0 -573
  86. package/dist/prompts-ab.d.ts +0 -71
  87. package/dist/prompts-ab.js +0 -313
  88. package/dist/prompts-propose.d.ts +0 -15
  89. package/dist/prompts-propose.js +0 -212
  90. package/dist/prompts.d.ts +0 -58
  91. package/dist/prompts.js +0 -418
  92. package/dist/provision.d.ts +0 -28
  93. package/dist/provision.js +0 -104
  94. package/dist/redact.d.ts +0 -2
  95. package/dist/redact.js +0 -90
  96. package/dist/schemas.d.ts +0 -225
  97. package/dist/schemas.js +0 -126
  98. package/dist/skills-bundled.d.ts +0 -35
  99. package/dist/skills-bundled.js +0 -157
  100. package/dist/skills-manifest.d.ts +0 -33
  101. package/dist/skills-manifest.js +0 -244
  102. package/dist/skills.d.ts +0 -57
  103. package/dist/skills.js +0 -783
  104. package/dist/sync.d.ts +0 -5
  105. package/dist/sync.js +0 -36
  106. package/dist/telemetry.d.ts +0 -56
  107. package/dist/telemetry.js +0 -167
  108. package/dist/transforms.d.ts +0 -25
  109. package/dist/transforms.js +0 -102
  110. package/dist/tui.d.ts +0 -48
  111. package/dist/tui.js +0 -393
  112. package/dist/update-check.d.ts +0 -31
  113. package/dist/update-check.js +0 -141
  114. package/dist/workspace.d.ts +0 -12
  115. package/dist/workspace.js +0 -1294
package/dist/output.js DELETED
@@ -1,624 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { CliAccessError, CliAuthError, CliCancellationError, CliInputError, CliInputRequiredError, isApiLikeError, isNetworkLikeError, } from './errors.js';
3
- import { redactDeep } from './redact.js';
4
- export const AGENT_SCHEMA_VERSION = 'moda.agent.v1';
5
- export const AGENT_EVENT_SCHEMA_VERSION = 'moda.agent_event.v1';
6
- /**
7
- * One-line feedback nudge stamped into every non-feedback success envelope's
8
- * `meta.tip` (agents are the CLI's most common callers, and the envelope is
9
- * where they actually look). Deliberately conditional wording so agents flag
10
- * genuine observations instead of habitually running the command. The
11
- * human-TTY twin is printed on stderr by the dispatcher in cli.ts.
12
- */
13
- export const FEEDBACK_TIP = 'Help us improve! If a result in this response looks wrong or is missing data, flag it: moda feedback "<describe what looked wrong>" [--category=...] [--conversation-id=... | --cluster-id=... | --tool-name=...]. Only submit genuine observations.';
14
- /**
15
- * Stable process exit codes that an external agent can branch on:
16
- * 0 ok — grounded answer from Moda Cloud intelligence.
17
- * 3 degraded — answer produced from a local/degraded fallback (lower trust).
18
- * 1 error — command/input/unexpected failure (see envelope `errors[]`).
19
- * 4 auth — login or token renewal required before retrying.
20
- * 5 input — non-interactive caller must choose and re-invoke.
21
- * 130 cancel — interrupted or the output consumer closed the pipe.
22
- * `EXIT_DEGRADED` is intentionally distinct so a caller can tell a degraded
23
- * answer apart from both success and hard failure without parsing the payload.
24
- */
25
- export const EXIT_OK = 0;
26
- export const EXIT_ERROR = 1;
27
- export const EXIT_DEGRADED = 3;
28
- export const EXIT_AUTH_REQUIRED = 4;
29
- export const EXIT_INPUT_REQUIRED = 5;
30
- export const EXIT_CANCELLED = 130;
31
- export const AGENT_ERROR_CODES = [
32
- 'input_error',
33
- 'api_error',
34
- 'network_error',
35
- 'auth_required',
36
- 'input_required',
37
- 'cancelled',
38
- 'command_error',
39
- 'access_denied',
40
- 'unknown_error',
41
- 'cloud_unavailable',
42
- ];
43
- const FALLBACK_RUN_ID = randomUUID();
44
- let fallbackSeq = 0;
45
- let stdoutClosed = false;
46
- let stderrClosed = false;
47
- process.stdout.on('error', (error) => {
48
- if (error.code === 'EPIPE') {
49
- stdoutClosed = true;
50
- process.exitCode = EXIT_CANCELLED;
51
- return;
52
- }
53
- throw error;
54
- });
55
- process.stderr.on('error', (error) => {
56
- if (error.code === 'EPIPE') {
57
- stderrClosed = true;
58
- process.exitCode = EXIT_CANCELLED;
59
- return;
60
- }
61
- throw error;
62
- });
63
- /**
64
- * True once an EPIPE from a closed stdout/stderr consumer has been observed.
65
- * The stream error listeners above own this state; expose it so callers can
66
- * detect consumer-cancelled runs explicitly instead of sniffing the global
67
- * `process.exitCode`.
68
- */
69
- export function wasOutputCancelled() {
70
- return stdoutClosed || stderrClosed;
71
- }
72
- export function safeWriteStdout(text) {
73
- if (stdoutClosed)
74
- return false;
75
- try {
76
- if (process.env.NODE_ENV === 'test') {
77
- console.log(text.endsWith('\n') ? text.slice(0, -1) : text);
78
- }
79
- else {
80
- process.stdout.write(text);
81
- }
82
- return true;
83
- }
84
- catch (error) {
85
- if (error.code === 'EPIPE') {
86
- stdoutClosed = true;
87
- process.exitCode = EXIT_CANCELLED;
88
- return false;
89
- }
90
- throw error;
91
- }
92
- }
93
- export function safeWriteStderr(text) {
94
- if (stderrClosed)
95
- return false;
96
- try {
97
- if (process.env.NODE_ENV === 'test') {
98
- console.error(text.endsWith('\n') ? text.slice(0, -1) : text);
99
- }
100
- else {
101
- process.stderr.write(text);
102
- }
103
- return true;
104
- }
105
- catch (error) {
106
- if (error.code === 'EPIPE') {
107
- stderrClosed = true;
108
- process.exitCode = EXIT_CANCELLED;
109
- return false;
110
- }
111
- throw error;
112
- }
113
- }
114
- /**
115
- * Output mode requested by explicit flags only, or `undefined` when the caller
116
- * did not pick one. Explicit flags always win over auto-detection.
117
- */
118
- function explicitModeFromFlags(flags) {
119
- if (flags.agent === 'true' && (flags.stream === 'true' || flags.format === 'ndjson'))
120
- return 'agent-stream';
121
- if (flags.agent === 'true')
122
- return 'agent';
123
- if (flags.json === 'true')
124
- return 'json';
125
- if (flags.human === 'true' || flags.prose === 'true')
126
- return 'human';
127
- return undefined;
128
- }
129
- /** Flag-only resolution (default human). Kept for callers that only have flags. */
130
- export function outputModeFromFlags(flags) {
131
- return explicitModeFromFlags(flags) ?? 'human';
132
- }
133
- function outputModeFromEnv(env) {
134
- const fmt = (env.MODA_FORMAT ?? '').trim().toLowerCase();
135
- if (fmt === 'human' || fmt === 'json' || fmt === 'agent')
136
- return fmt;
137
- if (fmt === 'agent-stream' || fmt === 'ndjson')
138
- return 'agent-stream';
139
- return undefined;
140
- }
141
- function truthyEnv(value) {
142
- return value != null && value !== '' && value !== '0' && value.toLowerCase() !== 'false';
143
- }
144
- /**
145
- * True when the process appears to be driven by an agent / automation rather
146
- * than an interactive human (Claude Code, Cursor, generic CI, or an explicit
147
- * `MODA_AGENT` opt-in). Such callers get machine-parseable output by default.
148
- */
149
- function isAgentEnvironment(env) {
150
- return (truthyEnv(env.CLAUDECODE) ||
151
- truthyEnv(env.CURSOR) ||
152
- truthyEnv(env.CI) ||
153
- truthyEnv(env.MODA_AGENT));
154
- }
155
- /**
156
- * Resolve the effective output mode for a real CLI invocation, in priority
157
- * order: explicit flags -> `MODA_FORMAT` -> agent-environment detection ->
158
- * TTY detection (piped/redirected => agent JSON) -> human prose. This is what
159
- * makes the CLI agent-safe: a piped/automated caller gets structured output
160
- * with zero flags, while a human at a terminal still gets prose.
161
- */
162
- export function resolveOutputMode(flags, opts = {}) {
163
- const explicit = explicitModeFromFlags(flags);
164
- if (explicit)
165
- return explicit;
166
- const env = opts.env ?? process.env;
167
- const fromEnv = outputModeFromEnv(env);
168
- if (fromEnv)
169
- return fromEnv;
170
- if (isAgentEnvironment(env))
171
- return 'agent';
172
- const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);
173
- if (!isTTY)
174
- return 'agent';
175
- return 'human';
176
- }
177
- export function createCliOutput(options) {
178
- const state = {
179
- runId: options.runId ?? randomUUID(),
180
- seq: 0,
181
- started: false,
182
- terminal: false,
183
- startedAt: Date.now(),
184
- lastEventAt: Date.now(),
185
- };
186
- const emitEvent = (event) => {
187
- if (options.quiet || options.mode !== 'agent-stream')
188
- return;
189
- if (state.terminal)
190
- return;
191
- if (event.event === 'started' && state.started)
192
- return;
193
- if (event.event === 'started')
194
- state.started = true;
195
- if (isTerminalEvent(event.event)) {
196
- state.terminal = true;
197
- clearHeartbeat(state);
198
- }
199
- state.lastEventAt = Date.now();
200
- safeWriteStdout(`${JSON.stringify(createAgentEvent(options.command, event, state))}\n`);
201
- };
202
- const ensureStarted = () => {
203
- if (options.mode !== 'agent-stream' || state.started || options.quiet)
204
- return;
205
- startHeartbeat(options.command, state);
206
- emitEvent({
207
- event: 'started',
208
- phase: options.command,
209
- message: `Starting ${options.command}.`,
210
- elapsed_ms: 0,
211
- });
212
- };
213
- const writeEnvelope = (data, writeOptions = {}) => {
214
- return createAgentEnvelope({
215
- command: options.command,
216
- status: writeOptions.status === 'error'
217
- ? 'error'
218
- : writeOptions.status === 'degraded'
219
- ? 'degraded'
220
- : 'ok',
221
- data,
222
- meta: {
223
- ...(writeOptions.meta ?? {}),
224
- run_id: state.runId,
225
- // No tip on the feedback command itself; it would point at the
226
- // command that just ran.
227
- ...(options.command === 'feedback' ? {} : { tip: FEEDBACK_TIP }),
228
- },
229
- summary: writeOptions.summary,
230
- findings: writeOptions.findings,
231
- evidenceRefs: writeOptions.evidenceRefs,
232
- recommendedActions: writeOptions.recommendedActions,
233
- nextCommands: writeOptions.nextCommands,
234
- artifacts: writeOptions.artifacts,
235
- warnings: writeOptions.warnings,
236
- errors: writeOptions.errors,
237
- });
238
- };
239
- const output = {
240
- mode: options.mode,
241
- quiet: options.quiet === true,
242
- writeData(data, writeOptions = {}) {
243
- if (options.quiet)
244
- return;
245
- if (options.mode === 'agent' || options.mode === 'agent-stream') {
246
- if (state.terminal)
247
- return;
248
- const envelope = writeEnvelope(data, writeOptions);
249
- if (options.mode === 'agent-stream') {
250
- ensureStarted();
251
- emitEvent({
252
- event: envelope.status === 'error' ? 'error' : 'completed',
253
- status: envelope.status,
254
- result: envelope,
255
- ...(envelope.errors[0] ? { error: envelope.errors[0] } : {}),
256
- });
257
- }
258
- else {
259
- state.terminal = true;
260
- safeWriteStdout(`${JSON.stringify(envelope)}\n`);
261
- }
262
- return;
263
- }
264
- safeWriteStdout(`${JSON.stringify(redactDeep(data), null, 2)}\n`);
265
- },
266
- writeEvent(event) {
267
- if (options.quiet)
268
- return;
269
- if (options.mode !== 'agent-stream')
270
- return;
271
- if (event.event === 'started') {
272
- startHeartbeat(options.command, state);
273
- emitEvent(event);
274
- return;
275
- }
276
- ensureStarted();
277
- emitEvent(event);
278
- },
279
- writeError(error) {
280
- if (options.quiet || state.terminal)
281
- return;
282
- const agentError = errorToAgentError(error);
283
- const envelope = createAgentErrorEnvelope(options.command || 'unknown', agentError, state.runId);
284
- if (options.mode === 'agent-stream') {
285
- ensureStarted();
286
- if (error instanceof CliInputRequiredError) {
287
- emitEvent({
288
- event: 'needs_input',
289
- status: 'error',
290
- message: error.message,
291
- error: agentError,
292
- input_request: error.input_request,
293
- result: envelope,
294
- });
295
- return;
296
- }
297
- if (error instanceof CliAuthError) {
298
- emitEvent({
299
- event: 'auth_required',
300
- status: 'error',
301
- message: error.message,
302
- error: agentError,
303
- auth: error.auth,
304
- result: envelope,
305
- });
306
- return;
307
- }
308
- if (error instanceof CliCancellationError) {
309
- emitEvent({
310
- event: 'cancelled',
311
- status: 'error',
312
- message: error.message,
313
- error: agentError,
314
- result: envelope,
315
- });
316
- return;
317
- }
318
- emitEvent({
319
- event: 'error',
320
- status: 'error',
321
- message: agentError.message,
322
- error: agentError,
323
- result: envelope,
324
- });
325
- return;
326
- }
327
- if (options.mode === 'agent' || options.mode === 'json') {
328
- state.terminal = true;
329
- safeWriteStdout(`${JSON.stringify(envelope)}\n`);
330
- }
331
- },
332
- writeCancelled(reason = 'Command cancelled.') {
333
- output.writeError(new CliCancellationError(reason));
334
- },
335
- hasTerminalOutput() {
336
- return state.terminal;
337
- },
338
- close() {
339
- clearHeartbeat(state);
340
- },
341
- };
342
- return output;
343
- }
344
- export function createAgentEnvelope(options) {
345
- const safeData = redactDeep(options.data);
346
- const bounded = boundForAgent(safeData);
347
- const envelope = {
348
- schema_version: AGENT_SCHEMA_VERSION,
349
- command: options.command,
350
- status: options.status,
351
- generated_at: new Date().toISOString(),
352
- ...(options.profile ? { profile: options.profile } : {}),
353
- summary: normalizeSummary(options.summary, options.command, options.status, safeData),
354
- findings: options.findings ?? [],
355
- evidence_refs: options.evidenceRefs ?? [],
356
- recommended_actions: options.recommendedActions ?? [],
357
- next_commands: normalizeNextCommands(options.nextCommands ?? []),
358
- artifacts: options.artifacts ?? [],
359
- warnings: options.warnings ?? inferWarnings(safeData),
360
- errors: options.errors ?? [],
361
- data: bounded.data,
362
- ...(options.meta ? { meta: options.meta } : {}),
363
- ...(bounded.truncation ? { truncation: bounded.truncation } : {}),
364
- };
365
- return redactDeep(envelope);
366
- }
367
- export function createAgentErrorEnvelope(command, error, runId) {
368
- return createAgentEnvelope({
369
- command,
370
- status: 'error',
371
- data: null,
372
- meta: runId ? { run_id: runId } : undefined,
373
- summary: {
374
- text: error.message,
375
- confidence: 'high',
376
- },
377
- errors: [error],
378
- nextCommands: (error.suggested_commands ?? []).map((commandText) => ({
379
- command: commandText,
380
- purpose: 'Suggested recovery command.',
381
- mutability: commandText.includes(' sync') || commandText.startsWith('moda sync') ? 'sync' : 'read',
382
- requires_approval: commandText.includes(' --yes') || commandText.includes(' sync'),
383
- })),
384
- });
385
- }
386
- export function createAgentEvent(command, event, state) {
387
- const stamped = {
388
- schema_version: AGENT_EVENT_SCHEMA_VERSION,
389
- command,
390
- generated_at: new Date().toISOString(),
391
- run_id: state?.runId ?? FALLBACK_RUN_ID,
392
- seq: state ? ++state.seq : ++fallbackSeq,
393
- event_version: event.event_version ?? 1,
394
- ...event,
395
- };
396
- return redactDeep(stamped);
397
- }
398
- export function errorToAgentError(error) {
399
- if (error instanceof CliInputRequiredError) {
400
- return {
401
- code: 'input_required',
402
- message: error.message,
403
- cause: error.usage,
404
- suggested_commands: error.input_request.resume_command
405
- ? [error.input_request.resume_command.join(' ')]
406
- : undefined,
407
- input_request: error.input_request,
408
- blocking: true,
409
- };
410
- }
411
- if (error instanceof CliAuthError) {
412
- const suggested = [error.auth.login_command.join(' ')];
413
- if (error.auth.resume_command)
414
- suggested.push(error.auth.resume_command.join(' '));
415
- return {
416
- code: 'auth_required',
417
- message: error.message,
418
- cause: error.usage,
419
- suggested_commands: suggested,
420
- auth: error.auth,
421
- blocking: true,
422
- };
423
- }
424
- if (error instanceof CliCancellationError) {
425
- return {
426
- code: 'cancelled',
427
- message: error.message,
428
- blocking: true,
429
- };
430
- }
431
- if (error instanceof CliAccessError) {
432
- // Access problem (missing organization or insufficient role), not
433
- // auth-missing: exit stays EXIT_ERROR and no login command is suggested.
434
- return {
435
- code: 'access_denied',
436
- message: error.message,
437
- cause: error.guidance,
438
- blocking: true,
439
- };
440
- }
441
- if (error instanceof Error) {
442
- if (error instanceof CliInputError || error.name === 'ZodError') {
443
- return {
444
- code: 'input_error',
445
- message: error.message,
446
- cause: 'usage' in error && typeof error.usage === 'string' ? error.usage : undefined,
447
- suggested_commands: 'usage' in error && typeof error.usage === 'string'
448
- ? extractSuggestedCommands(error.usage)
449
- : undefined,
450
- blocking: true,
451
- };
452
- }
453
- if (isApiLikeError(error)) {
454
- const statusCode = 'statusCode' in error ? error.statusCode : undefined;
455
- return {
456
- code: 'api_error',
457
- message: error.message,
458
- cause: typeof statusCode === 'number' ? `HTTP ${statusCode}` : undefined,
459
- blocking: true,
460
- };
461
- }
462
- if (isNetworkLikeError(error)) {
463
- return {
464
- code: 'network_error',
465
- message: error.message,
466
- blocking: true,
467
- };
468
- }
469
- return {
470
- code: 'command_error',
471
- message: error.message,
472
- cause: 'usage' in error && typeof error.usage === 'string' ? error.usage : undefined,
473
- suggested_commands: 'usage' in error && typeof error.usage === 'string'
474
- ? extractSuggestedCommands(error.usage)
475
- : undefined,
476
- blocking: true,
477
- };
478
- }
479
- return {
480
- code: 'unknown_error',
481
- message: String(error),
482
- blocking: true,
483
- };
484
- }
485
- function isTerminalEvent(event) {
486
- return event === 'completed' ||
487
- event === 'error' ||
488
- event === 'cancelled' ||
489
- event === 'needs_input' ||
490
- event === 'auth_required';
491
- }
492
- function startHeartbeat(command, state) {
493
- if (state.heartbeat)
494
- return;
495
- const intervalMs = Number(process.env.MODA_HEARTBEAT_MS ?? 10_000);
496
- if (!Number.isFinite(intervalMs) || intervalMs <= 0)
497
- return;
498
- state.heartbeat = setInterval(() => {
499
- if (state.terminal || stdoutClosed)
500
- return;
501
- const now = Date.now();
502
- if (now - state.lastEventAt < intervalMs)
503
- return;
504
- state.lastEventAt = now;
505
- safeWriteStdout(`${JSON.stringify(createAgentEvent(command, {
506
- event: 'heartbeat',
507
- elapsed_ms: now - state.startedAt,
508
- }, state))}\n`);
509
- }, intervalMs);
510
- state.heartbeat.unref();
511
- }
512
- function clearHeartbeat(state) {
513
- if (!state.heartbeat)
514
- return;
515
- clearInterval(state.heartbeat);
516
- state.heartbeat = undefined;
517
- }
518
- function normalizeSummary(summary, command, status, data) {
519
- if (typeof summary === 'string') {
520
- return { text: summary, confidence: 'high' };
521
- }
522
- if (summary?.text) {
523
- return {
524
- text: summary.text,
525
- confidence: summary.confidence ?? 'high',
526
- };
527
- }
528
- return inferSummary(command, status, data);
529
- }
530
- function inferSummary(command, status, data) {
531
- const record = asRecord(data);
532
- if (status === 'error') {
533
- return { text: `${command} failed.`, confidence: 'high' };
534
- }
535
- const summary = asRecord(record?.summary);
536
- if (summary) {
537
- const statusValue = typeof summary.status === 'string' ? summary.status : undefined;
538
- const fail = typeof summary.fail === 'number' ? summary.fail : undefined;
539
- const warning = typeof summary.warning === 'number' ? summary.warning : undefined;
540
- if (statusValue && fail !== undefined && warning !== undefined) {
541
- return {
542
- text: `${command} completed with status ${statusValue}: ${fail} failing checks and ${warning} warnings.`,
543
- confidence: 'high',
544
- };
545
- }
546
- const runtimeAgentCount = typeof summary.runtimeAgentCount === 'number'
547
- ? summary.runtimeAgentCount
548
- : undefined;
549
- if (runtimeAgentCount !== undefined) {
550
- return {
551
- text: `${command} completed. Harness summary includes ${runtimeAgentCount} runtime agent(s).`,
552
- confidence: 'high',
553
- };
554
- }
555
- }
556
- if (typeof record?.status === 'string') {
557
- return { text: `${command} completed with status ${record.status}.`, confidence: 'high' };
558
- }
559
- return { text: `${command} completed.`, confidence: 'medium' };
560
- }
561
- function normalizeNextCommands(commands) {
562
- return commands.map((command) => {
563
- if (typeof command !== 'string')
564
- return command;
565
- return {
566
- command,
567
- purpose: 'Suggested next Moda command.',
568
- mutability: inferMutability(command),
569
- requires_approval: command.includes(' --yes') || command.includes(' sync'),
570
- };
571
- });
572
- }
573
- function inferMutability(command) {
574
- if (/\b(sync|approve|init|install|pull|gen|remove|promote)\b/.test(command)) {
575
- if (/\bsync\b/.test(command))
576
- return 'sync';
577
- return 'write';
578
- }
579
- return 'read';
580
- }
581
- function inferWarnings(data) {
582
- const record = asRecord(data);
583
- const direct = record?.warnings;
584
- if (Array.isArray(direct))
585
- return direct.filter((item) => typeof item === 'string');
586
- const validation = asRecord(record?.validation);
587
- if (Array.isArray(validation?.warnings)) {
588
- return validation.warnings.filter((item) => typeof item === 'string');
589
- }
590
- return [];
591
- }
592
- function boundForAgent(data) {
593
- const maxBytes = Number(process.env.MODA_AGENT_MAX_OUTPUT_BYTES ?? 200_000);
594
- const serialized = JSON.stringify(data);
595
- const bytes = Buffer.byteLength(serialized, 'utf8');
596
- if (bytes <= maxBytes)
597
- return { data };
598
- const preview = serialized.slice(0, Math.max(0, maxBytes - 256));
599
- return {
600
- data: {
601
- truncated: true,
602
- original_bytes: bytes,
603
- max_bytes: maxBytes,
604
- preview,
605
- },
606
- truncation: {
607
- data: {
608
- original_bytes: bytes,
609
- max_bytes: maxBytes,
610
- reason: 'agent_output_byte_limit',
611
- },
612
- },
613
- };
614
- }
615
- function asRecord(value) {
616
- return value && typeof value === 'object' && !Array.isArray(value)
617
- ? value
618
- : undefined;
619
- }
620
- function extractSuggestedCommands(text) {
621
- const commands = [...text.matchAll(/`(moda [^`]+)`/g)].map((match) => match[1]);
622
- return commands.length > 0 ? commands : undefined;
623
- }
624
- //# sourceMappingURL=output.js.map