@moda-ai/cli 1.22.1 → 1.23.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 (118) hide show
  1. package/dist/cli-c4se7a5d.js +762 -0
  2. package/dist/cli-mnqa4rp6.js +1563 -0
  3. package/dist/cli-stmjdgvy.js +2842 -0
  4. package/dist/cli-y55bgg14.js +6980 -0
  5. package/dist/cli-ysegdrm5.js +286 -0
  6. package/dist/cli.js +4779 -1159
  7. package/dist/harness-github-actions-7t3cncnt.js +95 -0
  8. package/dist/index-4ser01zr.js +2769 -0
  9. package/dist/provision-hm1ppwj6.js +74 -0
  10. package/package.json +3 -2
  11. package/skills/integration/index.json +2 -2
  12. package/dist/agent-schemas.d.ts +0 -3
  13. package/dist/agent-schemas.js +0 -358
  14. package/dist/agent.d.ts +0 -5
  15. package/dist/agent.js +0 -477
  16. package/dist/analyst-tui.d.ts +0 -81
  17. package/dist/analyst-tui.js +0 -314
  18. package/dist/api-client.d.ts +0 -46
  19. package/dist/api-client.js +0 -120
  20. package/dist/ask-stream.d.ts +0 -146
  21. package/dist/ask-stream.js +0 -473
  22. package/dist/auth-storage.d.ts +0 -35
  23. package/dist/auth-storage.js +0 -159
  24. package/dist/auth.d.ts +0 -50
  25. package/dist/auth.js +0 -694
  26. package/dist/cli-session.d.ts +0 -26
  27. package/dist/cli-session.js +0 -169
  28. package/dist/cli.d.ts +0 -44
  29. package/dist/command-context.d.ts +0 -62
  30. package/dist/command-context.js +0 -57
  31. package/dist/command-registry.d.ts +0 -22
  32. package/dist/command-registry.js +0 -15
  33. package/dist/config.d.ts +0 -60
  34. package/dist/config.js +0 -110
  35. package/dist/doctor.d.ts +0 -79
  36. package/dist/doctor.js +0 -1182
  37. package/dist/errors.d.ts +0 -50
  38. package/dist/errors.js +0 -64
  39. package/dist/exit-funnel.d.ts +0 -14
  40. package/dist/exit-funnel.js +0 -53
  41. package/dist/harness-artifact-bodies.d.ts +0 -28
  42. package/dist/harness-artifact-bodies.js +0 -276
  43. package/dist/harness-candidates.d.ts +0 -77
  44. package/dist/harness-candidates.js +0 -482
  45. package/dist/harness-remote.d.ts +0 -135
  46. package/dist/harness-remote.js +0 -540
  47. package/dist/harness-report.d.ts +0 -145
  48. package/dist/harness-report.js +0 -1579
  49. package/dist/harness-sync-limits.d.ts +0 -8
  50. package/dist/harness-sync-limits.js +0 -21
  51. package/dist/harness.d.ts +0 -463
  52. package/dist/harness.js +0 -4009
  53. package/dist/init/index.d.ts +0 -78
  54. package/dist/init/index.js +0 -1442
  55. package/dist/init/mcp.d.ts +0 -2
  56. package/dist/init/mcp.js +0 -27
  57. package/dist/init/progress-board.d.ts +0 -80
  58. package/dist/init/progress-board.js +0 -225
  59. package/dist/init/rules.d.ts +0 -6
  60. package/dist/init/rules.js +0 -171
  61. package/dist/init/sdk-catalog.d.ts +0 -10
  62. package/dist/init/sdk-catalog.js +0 -80
  63. package/dist/init/sdk-guide.d.ts +0 -19
  64. package/dist/init/sdk-guide.js +0 -185
  65. package/dist/init/sdk-integration.d.ts +0 -53
  66. package/dist/init/sdk-integration.js +0 -835
  67. package/dist/init/sdk-protocol.d.ts +0 -135
  68. package/dist/init/sdk-protocol.js +0 -152
  69. package/dist/init/sdk-verification.d.ts +0 -27
  70. package/dist/init/sdk-verification.js +0 -123
  71. package/dist/init/tenant.d.ts +0 -67
  72. package/dist/init/tenant.js +0 -421
  73. package/dist/intelligence-schema.d.ts +0 -349
  74. package/dist/intelligence-schema.js +0 -174
  75. package/dist/keys.d.ts +0 -47
  76. package/dist/keys.js +0 -136
  77. package/dist/manifest.d.ts +0 -5
  78. package/dist/manifest.js +0 -114
  79. package/dist/markdown-terminal.d.ts +0 -49
  80. package/dist/markdown-terminal.js +0 -212
  81. package/dist/output.d.ts +0 -191
  82. package/dist/output.js +0 -624
  83. package/dist/production-intelligence.d.ts +0 -175
  84. package/dist/production-intelligence.js +0 -1430
  85. package/dist/profile-commands.d.ts +0 -10
  86. package/dist/profile-commands.js +0 -342
  87. package/dist/profiles.d.ts +0 -112
  88. package/dist/profiles.js +0 -573
  89. package/dist/prompts-ab.d.ts +0 -71
  90. package/dist/prompts-ab.js +0 -313
  91. package/dist/prompts-propose.d.ts +0 -15
  92. package/dist/prompts-propose.js +0 -212
  93. package/dist/prompts.d.ts +0 -58
  94. package/dist/prompts.js +0 -418
  95. package/dist/provision.d.ts +0 -28
  96. package/dist/provision.js +0 -104
  97. package/dist/redact.d.ts +0 -2
  98. package/dist/redact.js +0 -90
  99. package/dist/schemas.d.ts +0 -225
  100. package/dist/schemas.js +0 -126
  101. package/dist/skills-bundled.d.ts +0 -35
  102. package/dist/skills-bundled.js +0 -157
  103. package/dist/skills-manifest.d.ts +0 -33
  104. package/dist/skills-manifest.js +0 -244
  105. package/dist/skills.d.ts +0 -57
  106. package/dist/skills.js +0 -783
  107. package/dist/sync.d.ts +0 -5
  108. package/dist/sync.js +0 -36
  109. package/dist/telemetry.d.ts +0 -56
  110. package/dist/telemetry.js +0 -167
  111. package/dist/transforms.d.ts +0 -25
  112. package/dist/transforms.js +0 -102
  113. package/dist/tui.d.ts +0 -48
  114. package/dist/tui.js +0 -393
  115. package/dist/update-check.d.ts +0 -31
  116. package/dist/update-check.js +0 -141
  117. package/dist/workspace.d.ts +0 -12
  118. package/dist/workspace.js +0 -1294
package/dist/tui.js DELETED
@@ -1,393 +0,0 @@
1
- const STATUS_LABELS = {
2
- pass: 'ok',
3
- warning: 'warn',
4
- fail: 'fail',
5
- skipped: 'skip',
6
- info: 'info',
7
- run: 'run',
8
- };
9
- const STATUS_COLORS = {
10
- pass: 32,
11
- warning: 33,
12
- fail: 31,
13
- skipped: 90,
14
- info: 36,
15
- run: 35,
16
- };
17
- export function renderDoctorHuman(report, options = {}) {
18
- const ui = createTerminalUi(options);
19
- const lines = [];
20
- lines.push(ui.title('Moda doctor'));
21
- lines.push(ui.dim(`Profile ${report.profile.active}${report.profile.requested ? ` (requested ${report.profile.requested})` : ''}`));
22
- lines.push('');
23
- lines.push(renderSummary(ui, report));
24
- for (const [group, checks] of groupChecks(report.checks)) {
25
- lines.push('');
26
- lines.push(ui.section(titleCase(group)));
27
- for (const check of checks) {
28
- lines.push(renderCheck(ui, check));
29
- }
30
- }
31
- if (report.nextCommands.length > 0) {
32
- lines.push('');
33
- lines.push(ui.section('Next commands'));
34
- for (const command of report.nextCommands.slice(0, 8)) {
35
- lines.push(` ${ui.command(command)}`);
36
- }
37
- }
38
- if (report.fixPlan.length > 0) {
39
- lines.push('');
40
- lines.push(ui.section('Repair plan'));
41
- for (const item of report.fixPlan.slice(0, 8)) {
42
- const command = item.command ? ` ${ui.command(item.command)}` : '';
43
- lines.push(ui.wrap(` - ${item.action}${command}`, 4));
44
- }
45
- }
46
- return lines.join('\n');
47
- }
48
- export function renderStatusHuman(status, options = {}) {
49
- const ui = createTerminalUi(options);
50
- const lines = [];
51
- lines.push(ui.title('Moda status'));
52
- lines.push(`${ui.status(status.status)} ${ui.dim(`profile ${status.activeProfile}`)}`);
53
- lines.push('');
54
- lines.push(`Auth ${status.auth.apiKey}`);
55
- lines.push(`Tenant ${status.workspace.tenantId ?? 'missing'}`);
56
- lines.push(`Base URL ${status.workspace.baseUrl}`);
57
- lines.push(`Ingest URL ${status.workspace.ingestUrl}`);
58
- lines.push('');
59
- lines.push(ui.section('Readiness'));
60
- lines.push(` Data flow ${ui.status(status.dataFlow)}`);
61
- lines.push(` Harness ${ui.status(status.harness)}`);
62
- lines.push(` Harness sync ${ui.status(status.harnessSync)}`);
63
- lines.push(` Harness report ${ui.status(status.harnessReport)}`);
64
- lines.push(` Shadow Spark ${ui.status(status.shadowSpark)}`);
65
- lines.push(` Prompts ${ui.status(status.prompts)}`);
66
- lines.push(` Skills ${ui.status(status.skills)}`);
67
- if (status.topBrokenCheck) {
68
- lines.push('');
69
- lines.push(ui.section('Attention'));
70
- lines.push(` ${ui.status(status.topBrokenCheck.status)} ${status.topBrokenCheck.id}`);
71
- if (status.topBrokenCheck.nextCommand) {
72
- lines.push(` Next: ${ui.command(status.topBrokenCheck.nextCommand)}`);
73
- }
74
- }
75
- if (status.nextCommands.length > 0) {
76
- lines.push('');
77
- lines.push(ui.section('Next commands'));
78
- for (const command of status.nextCommands) {
79
- lines.push(` ${ui.command(command)}`);
80
- }
81
- }
82
- return lines.join('\n');
83
- }
84
- export function renderInitBanner() {
85
- const ui = createTerminalUi();
86
- return [
87
- ui.title('Moda init'),
88
- ui.dim('LLM observability for agent harnesses'),
89
- '',
90
- ` ${ui.status('info')} Human-in-the-loop setup. Secrets stay untouched and file changes are shown before completion.`,
91
- ].join('\n');
92
- }
93
- export function renderInitStep(index, total, title, detail) {
94
- const ui = createTerminalUi();
95
- return [
96
- '',
97
- ui.section(`Step ${index}/${total}: ${title}`),
98
- ...(detail ? [ui.wrap(` ${detail}`, 2)] : []),
99
- ].join('\n');
100
- }
101
- export function renderInitNote(message, status = 'pass') {
102
- const ui = createTerminalUi();
103
- return ` ${ui.status(status)} ${message}`;
104
- }
105
- export function renderInitSetupPlan(actions, options = {}) {
106
- const ui = createTerminalUi();
107
- const lines = actions.map((action) => {
108
- const status = action.approved
109
- ? 'pass'
110
- : action.status === 'manual'
111
- ? 'skipped'
112
- : 'warning';
113
- const suffix = action.reason ? ` - ${action.reason}` : '';
114
- return ui.wrap(` ${ui.status(status)} ${action.title}${suffix}`, 4);
115
- });
116
- if (options.snapshotConsent) {
117
- lines.push('');
118
- lines.push(ui.wrap(` ${ui.status('info')} Moda will analyze a snapshot of this repo on Moda's servers to map your agent harness. Secrets and .env files are excluded and the snapshot is deleted after the run.`, 4));
119
- }
120
- return lines.join('\n');
121
- }
122
- export function renderInitComplete(options) {
123
- const ui = createTerminalUi();
124
- const dashboardUrl = options.dashboardUrl ?? 'https://moda.dev';
125
- const lines = [];
126
- lines.push('');
127
- lines.push(ui.title(`Workspace ready: ${options.tenantName}`));
128
- lines.push(`Dashboard: ${ui.command(dashboardUrl)}`);
129
- lines.push('');
130
- if (options.harness?.state === 'pending') {
131
- lines.push(ui.wrap(` ${ui.status('run')} Harness analysis is still running on Moda. Run ${ui.command('moda harness pull --sync')} in a few minutes.`, 4));
132
- }
133
- else if (options.harness) {
134
- lines.push(ui.wrap(` ${ui.status('pass')} Harness mapped: ${options.harness.agents ?? 0} agent(s), ${options.harness.artifacts ?? 0} artifact(s). View: ${ui.command(dashboardUrl)}`, 4));
135
- }
136
- lines.push('');
137
- lines.push(ui.section('Files changed'));
138
- if (options.filesChanged.length === 0) {
139
- lines.push(` ${ui.dim('none')}`);
140
- }
141
- else {
142
- for (const file of options.filesChanged)
143
- lines.push(` ${file}`);
144
- }
145
- if (options.skipped.length > 0) {
146
- lines.push('');
147
- lines.push(ui.section('Skipped or manual'));
148
- for (const action of options.skipped) {
149
- lines.push(ui.wrap(` - ${action.title}: ${action.reason ?? action.status}`, 4));
150
- }
151
- }
152
- lines.push('');
153
- lines.push(ui.section('Next steps'));
154
- if (options.telemetrySeen === true) {
155
- lines.push(` Telemetry is flowing. Explore it: ${ui.command('moda overview')}`);
156
- }
157
- else {
158
- lines.push(` Run your app to send telemetry, then: ${ui.command('moda doctor --online')}`);
159
- }
160
- lines.push(` Ask about your agents: ${ui.command('moda ask "what did my agents do today?"')}`);
161
- return lines.join('\n');
162
- }
163
- export function renderHarnessProgress(message, status = 'run') {
164
- const ui = createTerminalUi();
165
- return `${ui.status(status)} ${message}`;
166
- }
167
- export function renderHarnessAnalystPreflight(options) {
168
- const ui = createTerminalUi();
169
- const lines = [];
170
- lines.push(ui.section('Analyst read plan'));
171
- lines.push(` ${ui.status('info')} Moda Analyst will inspect this project before any sync.`);
172
- lines.push(` Root ${options.root}`);
173
- lines.push(ui.wrap(` Reads ${options.willRead.join(', ')}`, 12));
174
- lines.push(ui.wrap(` Skips ${options.willSkip.join(', ')}`, 12));
175
- lines.push(` Writes ${ui.dim('none during investigation; Moda writes .moda artifacts after validation')}`);
176
- return lines.join('\n');
177
- }
178
- export function renderHarnessAnalystLaunch(options) {
179
- const ui = createTerminalUi();
180
- const lines = [];
181
- lines.push(ui.section('Moda Analyst'));
182
- lines.push(` ${ui.status('run')} Launching ${options.adapterName}`);
183
- lines.push(` Command ${options.command}`);
184
- lines.push(` Mode read-only repository access`);
185
- lines.push(` Root ${options.root}`);
186
- lines.push(` Output ${options.reportPath}`);
187
- lines.push(` Boundary Agent can inspect files and print a cited report. Moda validates it, then writes .moda artifacts.`);
188
- return lines.join('\n');
189
- }
190
- export function renderHarnessAnalystChoice(options) {
191
- const ui = createTerminalUi();
192
- return [
193
- ui.section('Detected coding agents'),
194
- ` ${ui.status('info')} ${options.detected.join(', ')}`,
195
- ' Pick the agent that should investigate this repo in read-only mode.',
196
- ].join('\n');
197
- }
198
- export function renderHarnessAnalyzeHumanResult(data) {
199
- const ui = createTerminalUi();
200
- const value = asRecord(data);
201
- const summary = asRecord(value?.summary);
202
- const validation = asRecord(value?.validation);
203
- const written = Array.isArray(value?.written) ? value.written.map(String) : [];
204
- const lines = [];
205
- lines.push(ui.title('Moda Analyst report'));
206
- lines.push(`${ui.status(validation?.status === 'fail' ? 'fail' : 'pass')} validation ${String(validation?.status ?? 'unknown')}`);
207
- if (summary) {
208
- lines.push('');
209
- lines.push(ui.section('Harness summary'));
210
- lines.push(` Runtime agents ${numberOrZero(summary.runtimeAgentCount)}`);
211
- lines.push(` Agent families ${numberOrZero(summary.agentFamilyCount)}`);
212
- lines.push(` Artifacts ${numberOrZero(summary.artifactCount ?? summary.sharedArtifactCount)}`);
213
- lines.push(` Providers ${numberOrZero(summary.modelProviderCount)}`);
214
- lines.push(` Unknowns ${numberOrZero(summary.unknownCount)}`);
215
- }
216
- if (written.length > 0) {
217
- lines.push('');
218
- lines.push(ui.section('Artifacts'));
219
- for (const file of written.slice(0, 12))
220
- lines.push(` ${file}`);
221
- }
222
- lines.push('');
223
- lines.push(ui.section('Next commands'));
224
- lines.push(` ${ui.command('moda harness approve --yes')}`);
225
- lines.push(` ${ui.command('moda harness sync --from-report .moda/harness-report.json --dry-run')}`);
226
- return lines.join('\n');
227
- }
228
- export function renderOverviewHuman(data) {
229
- const ui = createTerminalUi();
230
- const record = asRecord(data);
231
- const lines = [];
232
- lines.push(ui.title('Moda overview'));
233
- lines.push(ui.dim('Production health briefing'));
234
- lines.push('');
235
- lines.push(ui.section('Signals'));
236
- const signals = overviewSignals(record);
237
- if (signals.length === 0) {
238
- lines.push(' No aggregate signal fields found in the response.');
239
- }
240
- else {
241
- for (const [label, value] of signals)
242
- lines.push(` ${label.padEnd(24)} ${value}`);
243
- }
244
- const recent = recentActivity(record);
245
- if (recent.length > 0) {
246
- lines.push('');
247
- lines.push(ui.section('Recent activity'));
248
- for (const item of recent.slice(0, 5))
249
- lines.push(ui.wrap(` - ${item}`, 4));
250
- }
251
- lines.push('');
252
- lines.push(ui.section('Next commands'));
253
- lines.push(` ${ui.command('moda doctor --online')}`);
254
- lines.push(` ${ui.command('moda conversations --limit=10')}`);
255
- lines.push(` ${ui.command('moda tool-failures')}`);
256
- return lines.join('\n');
257
- }
258
- function renderSummary(ui, report) {
259
- return [
260
- ui.status(report.summary.status),
261
- `pass ${report.summary.pass}`,
262
- `warn ${report.summary.warning}`,
263
- `fail ${report.summary.fail}`,
264
- `skip ${report.summary.skipped}`,
265
- ].join(' ');
266
- }
267
- function renderCheck(ui, check) {
268
- const lines = [];
269
- lines.push(` ${ui.status(check.status)} ${check.title}`);
270
- for (const evidence of check.evidence.slice(0, 4)) {
271
- lines.push(ui.wrap(` ${evidence.label}: ${evidence.value}`, 6));
272
- }
273
- if (check.remediation) {
274
- lines.push(ui.wrap(` fix: ${check.remediation}`, 6));
275
- }
276
- if (check.nextCommand) {
277
- lines.push(` next: ${ui.command(check.nextCommand)}`);
278
- }
279
- return lines.join('\n');
280
- }
281
- function groupChecks(checks) {
282
- const groups = new Map();
283
- for (const check of checks) {
284
- const existing = groups.get(check.group) ?? [];
285
- existing.push(check);
286
- groups.set(check.group, existing);
287
- }
288
- return [...groups.entries()];
289
- }
290
- function titleCase(value) {
291
- return value
292
- .split(/[-_\s]+/)
293
- .filter(Boolean)
294
- .map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
295
- .join(' ');
296
- }
297
- function overviewSignals(record) {
298
- if (!record)
299
- return [];
300
- const candidates = [
301
- ['Conversations', valueAt(record, ['conversations', 'total']) ?? record.conversations],
302
- ['Recent activity', Array.isArray(record.recent_activity) ? record.recent_activity.length : undefined],
303
- ['Tool failures', valueAt(record, ['tool_failures', 'total']) ?? valueAt(record, ['toolFailures', 'total']) ?? record.tool_failures],
304
- ['Frustrations', valueAt(record, ['frustrations', 'total']) ?? record.frustrations],
305
- ['Runs', valueAt(record, ['runs', 'total']) ?? record.runs],
306
- ['Workflows', valueAt(record, ['workflows', 'total']) ?? record.workflows],
307
- ];
308
- return candidates
309
- .map(([label, value]) => [label, metricText(value)])
310
- .filter((item) => item[1] !== undefined);
311
- }
312
- function recentActivity(record) {
313
- const activity = Array.isArray(record?.recent_activity) ? record.recent_activity : [];
314
- return activity.flatMap((item) => {
315
- const value = asRecord(item);
316
- if (!value)
317
- return [];
318
- const id = stringValue(value.conversation_id) ?? stringValue(value.run_id) ?? stringValue(value.id);
319
- const timestamp = stringValue(value.timestamp) ?? stringValue(value.created_at);
320
- const summary = stringValue(value.summary) ?? stringValue(value.title) ?? stringValue(value.event);
321
- return [[summary, id, timestamp].filter(Boolean).join(' - ')];
322
- });
323
- }
324
- function metricText(value) {
325
- if (typeof value === 'number' && Number.isFinite(value))
326
- return String(value);
327
- if (typeof value === 'string' && value.trim())
328
- return value;
329
- if (Array.isArray(value))
330
- return String(value.length);
331
- if (isRecord(value)) {
332
- const total = value.total ?? value.count ?? value.active;
333
- if (typeof total === 'number' || typeof total === 'string')
334
- return String(total);
335
- }
336
- return undefined;
337
- }
338
- function valueAt(record, path) {
339
- let cursor = record;
340
- for (const key of path) {
341
- if (!isRecord(cursor))
342
- return undefined;
343
- cursor = cursor[key];
344
- }
345
- return cursor;
346
- }
347
- function numberOrZero(value) {
348
- return typeof value === 'number' && Number.isFinite(value) ? String(value) : '0';
349
- }
350
- function asRecord(value) {
351
- return isRecord(value) ? value : undefined;
352
- }
353
- function isRecord(value) {
354
- return typeof value === 'object' && value !== null && !Array.isArray(value);
355
- }
356
- function stringValue(value) {
357
- return typeof value === 'string' && value.trim() ? value : undefined;
358
- }
359
- function createTerminalUi(options = {}) {
360
- const width = Math.max(48, Math.min(options.width ?? process.stdout.columns ?? 96, 140));
361
- const color = options.color ?? Boolean(process.stdout.isTTY && process.env.NO_COLOR !== '1');
362
- const paint = (code, text) => color ? `\u001b[${code}m${text}\u001b[0m` : text;
363
- return {
364
- title: (text) => paint(1, text),
365
- section: (text) => paint(36, text),
366
- status: (status) => paint(STATUS_COLORS[status], `[${STATUS_LABELS[status]}]`),
367
- command: (text) => paint(36, text),
368
- dim: (text) => paint(90, text),
369
- wrap: (text, indent) => wrapLine(text, width, indent),
370
- };
371
- }
372
- function wrapLine(text, width, indent) {
373
- if (text.length <= width)
374
- return text;
375
- const lead = text.match(/^\s*/)?.[0] ?? '';
376
- const words = text.trimStart().split(/\s+/);
377
- const lines = [];
378
- let current = '';
379
- for (const word of words) {
380
- const next = current ? `${current} ${word}` : `${lead}${word}`;
381
- if (next.length > width && current) {
382
- lines.push(current);
383
- current = `${' '.repeat(indent)}${word}`;
384
- }
385
- else {
386
- current = next;
387
- }
388
- }
389
- if (current)
390
- lines.push(current);
391
- return lines.join('\n');
392
- }
393
- //# sourceMappingURL=tui.js.map
@@ -1,31 +0,0 @@
1
- import { type ProfileIOOptions } from './profiles.js';
2
- export declare function stripUpdateCheckFlag(flags: Record<string, string>): Record<string, string>;
3
- export declare function isUpdateCheckDisabled(flags: Record<string, string>, env?: NodeJS.ProcessEnv): boolean;
4
- /**
5
- * Numeric semver comparison ignoring prerelease/build suffixes; npm's
6
- * `latest` dist-tag only carries stable releases, so this is sufficient
7
- * without pulling in a semver dependency.
8
- */
9
- export declare function isNewerVersion(candidate: string, current: string): boolean;
10
- /**
11
- * Refresh the cached latest version from the npm registry at most once per
12
- * 24 hours. Fire-and-forget from the CLI dispatcher. The abort timer is
13
- * unref'd so it can't keep the process alive after the socket closes (it does
14
- * not unref the in-flight fetch itself — a fast command may still wait up to
15
- * REGISTRY_TIMEOUT_MS for the request to resolve, matching the telemetry
16
- * module's trade-off). Every failure path is swallowed — an update check must
17
- * never break a command.
18
- */
19
- export declare function startUpdateCheck(options?: ProfileIOOptions, now?: number): Promise<void>;
20
- /**
21
- * Returns the upgrade notice when the cached registry version is newer than
22
- * the running build, or undefined. Reads only the local cache — never the
23
- * network — so it is safe to call synchronously at the end of every command.
24
- */
25
- export declare function getUpdateNotice(options?: ProfileIOOptions): string | undefined;
26
- /**
27
- * Print the upgrade notice to stderr in interactive terminals only: stdout
28
- * stays machine-readable, and piped/CI invocations stay noise-free.
29
- */
30
- export declare function printUpdateNotice(options?: ProfileIOOptions): void;
31
- //# sourceMappingURL=update-check.d.ts.map
@@ -1,141 +0,0 @@
1
- // src/update-check.ts — Daily npm-registry version check with an upgrade
2
- // notice. Mirrors the telemetry contract: fire-and-forget, short abort
3
- // timeout, never throws, and never touches stdout (notice goes to stderr so
4
- // JSON/agent output stays machine-readable).
5
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
6
- import { join } from 'node:path';
7
- import { VERSION } from './config.js';
8
- import { getModaDir } from './profiles.js';
9
- const UPDATE_CHECK_FLAG = 'no-update-check';
10
- const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
11
- const REGISTRY_TIMEOUT_MS = 1_500;
12
- const REGISTRY_LATEST_URL = 'https://registry.npmjs.org/@moda-ai/cli/latest';
13
- // npm's `latest` dist-tag is always a stable semver. Anchored, no control
14
- // chars: a registry response intercepted by a proxy can't smuggle ANSI escape
15
- // sequences into the cache (and thus the terminal notice) this way.
16
- const SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
17
- export function stripUpdateCheckFlag(flags) {
18
- const { [UPDATE_CHECK_FLAG]: _drop, ...rest } = flags;
19
- return rest;
20
- }
21
- export function isUpdateCheckDisabled(flags, env = process.env) {
22
- return (flags[UPDATE_CHECK_FLAG] === 'true' ||
23
- env.MODA_CLI_UPDATE_CHECK === '0' ||
24
- env.DO_NOT_TRACK === '1' ||
25
- // Treat CI as set only when it carries a real truthy value: `CI=false`
26
- // and `CI=0` are common ways to simulate a non-CI shell locally.
27
- (env.CI !== undefined &&
28
- env.CI !== '' &&
29
- env.CI !== 'false' &&
30
- env.CI !== '0'));
31
- }
32
- function cachePath(options = {}) {
33
- return join(getModaDir(options), 'update-check.json');
34
- }
35
- function readCache(options = {}) {
36
- try {
37
- const parsed = JSON.parse(readFileSync(cachePath(options), 'utf8'));
38
- if (parsed &&
39
- typeof parsed === 'object' &&
40
- typeof parsed.checked_at === 'number' &&
41
- typeof parsed.latest === 'string') {
42
- return parsed;
43
- }
44
- }
45
- catch {
46
- // Missing or corrupt cache means "never checked".
47
- }
48
- return undefined;
49
- }
50
- function writeCache(cache, options = {}) {
51
- try {
52
- mkdirSync(getModaDir(options), { recursive: true });
53
- writeFileSync(cachePath(options), `${JSON.stringify(cache, null, 2)}\n`, 'utf8');
54
- }
55
- catch {
56
- // Best-effort: an unwritable cache only means we re-check next run.
57
- }
58
- }
59
- /**
60
- * Numeric semver comparison ignoring prerelease/build suffixes; npm's
61
- * `latest` dist-tag only carries stable releases, so this is sufficient
62
- * without pulling in a semver dependency.
63
- */
64
- export function isNewerVersion(candidate, current) {
65
- const parse = (v) => v.split(/[-+]/)[0].split('.').map((part) => Number.parseInt(part, 10) || 0);
66
- const a = parse(candidate);
67
- const b = parse(current);
68
- for (let i = 0; i < 3; i++) {
69
- if ((a[i] ?? 0) > (b[i] ?? 0))
70
- return true;
71
- if ((a[i] ?? 0) < (b[i] ?? 0))
72
- return false;
73
- }
74
- return false;
75
- }
76
- /**
77
- * Refresh the cached latest version from the npm registry at most once per
78
- * 24 hours. Fire-and-forget from the CLI dispatcher. The abort timer is
79
- * unref'd so it can't keep the process alive after the socket closes (it does
80
- * not unref the in-flight fetch itself — a fast command may still wait up to
81
- * REGISTRY_TIMEOUT_MS for the request to resolve, matching the telemetry
82
- * module's trade-off). Every failure path is swallowed — an update check must
83
- * never break a command.
84
- */
85
- export async function startUpdateCheck(options = {}, now = Date.now()) {
86
- const cache = readCache(options);
87
- if (cache && now - cache.checked_at < CHECK_INTERVAL_MS) {
88
- return;
89
- }
90
- const controller = new AbortController();
91
- const timeout = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS);
92
- if (typeof timeout.unref === 'function') {
93
- timeout.unref();
94
- }
95
- try {
96
- const res = await fetch(REGISTRY_LATEST_URL, {
97
- headers: { Accept: 'application/json' },
98
- signal: controller.signal,
99
- });
100
- if (!res.ok)
101
- return;
102
- const body = (await res.json());
103
- if (typeof body.version !== 'string' || !SEMVER_RE.test(body.version))
104
- return;
105
- writeCache({ checked_at: now, latest: body.version }, options);
106
- }
107
- catch {
108
- // Network/timeout errors are expected offline; try again next run.
109
- }
110
- finally {
111
- clearTimeout(timeout);
112
- }
113
- }
114
- /**
115
- * Returns the upgrade notice when the cached registry version is newer than
116
- * the running build, or undefined. Reads only the local cache — never the
117
- * network — so it is safe to call synchronously at the end of every command.
118
- */
119
- export function getUpdateNotice(options = {}) {
120
- const cache = readCache(options);
121
- if (!cache || !isNewerVersion(cache.latest, VERSION)) {
122
- return undefined;
123
- }
124
- return [
125
- '',
126
- `Update available: moda-cli ${VERSION} → ${cache.latest}`,
127
- 'Run `npm install -g @moda-ai/cli@latest` to update.',
128
- ].join('\n');
129
- }
130
- /**
131
- * Print the upgrade notice to stderr in interactive terminals only: stdout
132
- * stays machine-readable, and piped/CI invocations stay noise-free.
133
- */
134
- export function printUpdateNotice(options = {}) {
135
- if (!process.stderr.isTTY)
136
- return;
137
- const notice = getUpdateNotice(options);
138
- if (notice)
139
- console.error(notice);
140
- }
141
- //# sourceMappingURL=update-check.js.map
@@ -1,12 +0,0 @@
1
- import type { ParsedArgs } from './cli.js';
2
- import type { CommandContext } from './command-context.js';
3
- interface WorkspaceOptions {
4
- cwd?: string;
5
- }
6
- export declare function runWorkspaceCommand(args: ParsedArgs, options?: WorkspaceOptions): Promise<number>;
7
- export declare function runSparkCommand(context: CommandContext): Promise<{
8
- exitCode: number;
9
- }>;
10
- export declare function runHooksInstall(args: ParsedArgs): Promise<number>;
11
- export {};
12
- //# sourceMappingURL=workspace.d.ts.map