@moda-ai/cli 1.22.0 → 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 -777
  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/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