@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
@@ -0,0 +1,1563 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ // src/errors.ts
5
+ class CliInputError extends Error {
6
+ usage;
7
+ constructor(message, usage) {
8
+ super(message);
9
+ this.usage = usage;
10
+ this.name = "CliInputError";
11
+ }
12
+ }
13
+
14
+ class CliAccessError extends Error {
15
+ guidance;
16
+ constructor(message, options = {}) {
17
+ super(message);
18
+ this.name = "CliAccessError";
19
+ this.guidance = options.guidance;
20
+ }
21
+ }
22
+
23
+ class CliInputRequiredError extends Error {
24
+ input_request;
25
+ constructor(message, input_request, usage) {
26
+ super(message);
27
+ this.input_request = input_request;
28
+ this.name = "CliInputRequiredError";
29
+ if (usage)
30
+ this.usage = usage;
31
+ }
32
+ usage;
33
+ }
34
+
35
+ class CliAuthError extends Error {
36
+ auth;
37
+ constructor(message, auth, usage) {
38
+ super(message);
39
+ this.auth = auth;
40
+ this.name = "CliAuthError";
41
+ if (usage)
42
+ this.usage = usage;
43
+ }
44
+ usage;
45
+ }
46
+
47
+ class CliCancellationError extends Error {
48
+ constructor(message) {
49
+ super(message);
50
+ this.name = "CliCancellationError";
51
+ }
52
+ }
53
+ function isNetworkLikeError(error) {
54
+ return error instanceof Error && (error.message.startsWith("Network error:") || error.message.startsWith("Request timed out") || error.name === "AbortError" || /(?:ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|socket hang up)/i.test(error.message));
55
+ }
56
+ function isApiLikeError(error) {
57
+ return error instanceof Error && (error.name === "ApiError" || ("statusCode" in error) && typeof error.statusCode === "number");
58
+ }
59
+
60
+ // src/config.ts
61
+ import { readFileSync as readFileSync2 } from "node:fs";
62
+
63
+ // src/profiles.ts
64
+ import {
65
+ chmodSync,
66
+ existsSync,
67
+ mkdirSync,
68
+ readFileSync,
69
+ rmSync,
70
+ statSync,
71
+ writeFileSync
72
+ } from "node:fs";
73
+ import { homedir } from "node:os";
74
+ import { dirname, isAbsolute, join, normalize, relative } from "node:path";
75
+ var PROFILE_CONFIG_VERSION = 1;
76
+ var PROD_BASE_URL = "https://moda.dev";
77
+ var DEFAULT_INGEST_URL = "https://moda-ingest.modas.workers.dev";
78
+ var DEFAULT_PROFILE_NAME = "prod";
79
+ function getEnv(options = {}) {
80
+ return options.env ?? process.env;
81
+ }
82
+ function resolveModaHome(options = {}) {
83
+ const env = getEnv(options);
84
+ return options.home || env.HOME || env.USERPROFILE || homedir();
85
+ }
86
+ function getModaPaths(options = {}) {
87
+ const home = resolveModaHome(options);
88
+ const dir = join(home, ".moda");
89
+ return {
90
+ home,
91
+ dir,
92
+ legacyConfigPath: join(dir, "config.json"),
93
+ legacyAuthPath: join(dir, "auth.json"),
94
+ profileConfigPath: join(dir, "profiles.json"),
95
+ secretsDir: join(dir, "secrets")
96
+ };
97
+ }
98
+ function getModaDir(options = {}) {
99
+ return getModaPaths(options).dir;
100
+ }
101
+ function isRecord(value) {
102
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
103
+ }
104
+ function optionalString(value) {
105
+ return typeof value === "string" && value.trim() ? value : undefined;
106
+ }
107
+ function readJsonFile(path) {
108
+ return JSON.parse(readFileSync(path, "utf8"));
109
+ }
110
+ function safeReadRecord(path) {
111
+ try {
112
+ if (!existsSync(path))
113
+ return;
114
+ const parsed = readJsonFile(path);
115
+ return isRecord(parsed) ? parsed : undefined;
116
+ } catch {
117
+ return;
118
+ }
119
+ }
120
+ function loadLegacyCliConfig(options = {}) {
121
+ const parsed = safeReadRecord(getModaPaths(options).legacyConfigPath);
122
+ if (!parsed)
123
+ return {};
124
+ return {
125
+ api_key: optionalString(parsed.api_key),
126
+ ingest_url: optionalString(parsed.ingest_url),
127
+ tenant_id: optionalString(parsed.tenant_id)
128
+ };
129
+ }
130
+ function writeLegacyCliConfig(update, options = {}) {
131
+ const paths = getModaPaths(options);
132
+ mkdirSync(paths.dir, { recursive: true });
133
+ const merged = { ...loadLegacyCliConfig(options), ...update };
134
+ writeSecretJsonFile(paths.legacyConfigPath, merged);
135
+ return merged;
136
+ }
137
+ function profileDefaults(name, environment, now) {
138
+ const local = environment === "local";
139
+ const staging = environment === "staging";
140
+ return {
141
+ name,
142
+ environment,
143
+ base_url: local ? "http://localhost:3000" : staging ? "https://staging.moda.dev" : PROD_BASE_URL,
144
+ ingest_url: local ? "http://localhost:8787" : DEFAULT_INGEST_URL,
145
+ metadata: {
146
+ created_at: now.toISOString(),
147
+ source: "default"
148
+ }
149
+ };
150
+ }
151
+ function createDefaultProfiles(options = {}) {
152
+ const now = options.now ?? new Date;
153
+ return {
154
+ local: profileDefaults("local", "local", now),
155
+ staging: profileDefaults("staging", "staging", now),
156
+ prod: profileDefaults("prod", "prod", now)
157
+ };
158
+ }
159
+ function isLoopbackHostname(hostname) {
160
+ const host = hostname.toLowerCase();
161
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
162
+ }
163
+ function isLoopbackBaseUrl(url) {
164
+ try {
165
+ return isLoopbackHostname(new URL(url).hostname);
166
+ } catch {
167
+ return false;
168
+ }
169
+ }
170
+ function defaultIngestUrlForEnvironment(environment) {
171
+ if (environment === "local")
172
+ return "http://localhost:8787";
173
+ return DEFAULT_INGEST_URL;
174
+ }
175
+ function environmentAllowsLoopback(environment) {
176
+ return environment !== "prod";
177
+ }
178
+ function sanitizeProfileBaseUrl(url, environment) {
179
+ const normalized = normalizeModaBaseUrl(url);
180
+ if (environmentAllowsLoopback(environment) || !isLoopbackBaseUrl(normalized)) {
181
+ return normalized;
182
+ }
183
+ return PROD_BASE_URL;
184
+ }
185
+ function sanitizeProfileIngestUrl(url, environment) {
186
+ if (!url)
187
+ return url;
188
+ if (environmentAllowsLoopback(environment) || !isLoopbackBaseUrl(url)) {
189
+ return url;
190
+ }
191
+ return DEFAULT_INGEST_URL;
192
+ }
193
+ function enforceNonLoopbackBaseUrl(url, environment) {
194
+ return sanitizeProfileBaseUrl(url, environment);
195
+ }
196
+ function enforceNonLoopbackIngestUrl(url, environment) {
197
+ return sanitizeProfileIngestUrl(url, environment) ?? defaultIngestUrlForEnvironment(environment);
198
+ }
199
+ function activeProfileEnvironment(options) {
200
+ try {
201
+ return getActiveProfile(options).environment;
202
+ } catch {
203
+ return "prod";
204
+ }
205
+ }
206
+ function normalizeEnvironment(value) {
207
+ if (value === "local" || value === "dev" || value === "staging" || value === "prod") {
208
+ return value;
209
+ }
210
+ return "prod";
211
+ }
212
+ function isSecretReference(value) {
213
+ return value.startsWith("env:") || value.startsWith("file:") || value === "legacy:config.api_key" || value === "legacy:auth.access_token";
214
+ }
215
+ function normalizeSecretReference(value) {
216
+ if (typeof value !== "string")
217
+ return;
218
+ return isSecretReference(value) ? value : undefined;
219
+ }
220
+ function assertValidProfileName(name) {
221
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name)) {
222
+ throw new Error(`Invalid profile name '${name}'. Use letters, numbers, dots, dashes, or underscores.`);
223
+ }
224
+ }
225
+ function normalizeProfile(fallbackName, value, options) {
226
+ if (!isRecord(value))
227
+ return;
228
+ const name = optionalString(value.name) ?? fallbackName;
229
+ try {
230
+ assertValidProfileName(name);
231
+ } catch {
232
+ return;
233
+ }
234
+ const defaults = profileDefaults(name, normalizeEnvironment(value.environment), options.now ?? new Date);
235
+ return {
236
+ ...defaults,
237
+ base_url: sanitizeProfileBaseUrl(optionalString(value.base_url) ?? defaults.base_url, normalizeEnvironment(value.environment)),
238
+ ingest_url: sanitizeProfileIngestUrl(optionalString(value.ingest_url) ?? defaults.ingest_url, normalizeEnvironment(value.environment)),
239
+ tenant_id: optionalString(value.tenant_id),
240
+ workspace_id: optionalString(value.workspace_id),
241
+ api_key_ref: normalizeSecretReference(value.api_key_ref),
242
+ auth_token_ref: normalizeSecretReference(value.auth_token_ref),
243
+ metadata: isRecord(value.metadata) ? value.metadata : defaults.metadata
244
+ };
245
+ }
246
+ function inspectLegacyAuth(path) {
247
+ const parsed = safeReadRecord(path);
248
+ const expiresAt = typeof parsed?.expires_at === "number" ? parsed.expires_at : undefined;
249
+ return {
250
+ path,
251
+ present: Boolean(parsed),
252
+ has_access_token: typeof parsed?.access_token === "string",
253
+ expires_at: expiresAt,
254
+ expired: expiresAt === undefined ? undefined : Date.now() / 1000 > expiresAt - 60
255
+ };
256
+ }
257
+ function inspectProfileMigrationState(options = {}) {
258
+ const paths = getModaPaths(options);
259
+ const legacyConfig = loadLegacyCliConfig(options);
260
+ return {
261
+ legacy_config: {
262
+ path: paths.legacyConfigPath,
263
+ present: existsSync(paths.legacyConfigPath),
264
+ has_api_key: Boolean(legacyConfig.api_key),
265
+ has_ingest_url: Boolean(legacyConfig.ingest_url),
266
+ has_tenant_id: Boolean(legacyConfig.tenant_id)
267
+ },
268
+ legacy_auth: inspectLegacyAuth(paths.legacyAuthPath)
269
+ };
270
+ }
271
+ function applyLegacyAwareness(config, options) {
272
+ const legacy = loadLegacyCliConfig(options);
273
+ const migration = inspectProfileMigrationState(options);
274
+ const prod = config.profiles.prod;
275
+ if (prod && (legacy.api_key || legacy.ingest_url || legacy.tenant_id)) {
276
+ const migratedIngestUrl = legacy.ingest_url && !isLoopbackBaseUrl(legacy.ingest_url) ? legacy.ingest_url : prod.ingest_url;
277
+ config.profiles.prod = {
278
+ ...prod,
279
+ ingest_url: migratedIngestUrl,
280
+ tenant_id: legacy.tenant_id ?? prod.tenant_id,
281
+ api_key_ref: legacy.api_key ? "legacy:config.api_key" : prod.api_key_ref,
282
+ auth_token_ref: migration.legacy_auth.has_access_token ? "legacy:auth.access_token" : prod.auth_token_ref,
283
+ metadata: {
284
+ ...prod.metadata,
285
+ source: "migration",
286
+ legacy_config_path: migration.legacy_config.path,
287
+ legacy_auth_path: migration.legacy_auth.path
288
+ }
289
+ };
290
+ }
291
+ return {
292
+ ...config,
293
+ migrations: migration
294
+ };
295
+ }
296
+ function normalizeProfileConfig(raw, options = {}) {
297
+ const profiles = createDefaultProfiles(options);
298
+ let activeProfile = DEFAULT_PROFILE_NAME;
299
+ if (isRecord(raw)) {
300
+ if (isRecord(raw.profiles)) {
301
+ for (const [name, profile] of Object.entries(raw.profiles)) {
302
+ const normalized = normalizeProfile(name, profile, options);
303
+ if (normalized)
304
+ profiles[normalized.name] = normalized;
305
+ }
306
+ }
307
+ const configuredActive = optionalString(raw.active_profile);
308
+ if (configuredActive && profiles[configuredActive]) {
309
+ activeProfile = configuredActive;
310
+ }
311
+ }
312
+ return applyLegacyAwareness({
313
+ version: PROFILE_CONFIG_VERSION,
314
+ active_profile: activeProfile,
315
+ profiles,
316
+ migrations: inspectProfileMigrationState(options)
317
+ }, options);
318
+ }
319
+ function loadProfileConfig(options = {}) {
320
+ const path = getModaPaths(options).profileConfigPath;
321
+ const parsed = safeReadRecord(path);
322
+ return normalizeProfileConfig(parsed, options);
323
+ }
324
+ function saveProfileConfig(config, options = {}) {
325
+ const active = config.active_profile || DEFAULT_PROFILE_NAME;
326
+ if (!config.profiles[active]) {
327
+ throw new Error(`Cannot activate missing profile '${active}'.`);
328
+ }
329
+ const path = getModaPaths(options).profileConfigPath;
330
+ writeSecretJsonFile(path, {
331
+ version: PROFILE_CONFIG_VERSION,
332
+ active_profile: active,
333
+ profiles: config.profiles,
334
+ migrations: inspectProfileMigrationState(options)
335
+ });
336
+ }
337
+ function getActiveProfile(options = {}) {
338
+ const config = loadProfileConfig(options);
339
+ const envProfile = optionalString(getEnv(options).MODA_PROFILE);
340
+ const requested = options.profile || envProfile || config.active_profile;
341
+ const profile = config.profiles[requested];
342
+ if (!profile) {
343
+ throw new Error(`Moda profile '${requested}' does not exist.`);
344
+ }
345
+ return profile;
346
+ }
347
+ function setActiveProfile(name, options = {}) {
348
+ assertValidProfileName(name);
349
+ const config = loadProfileConfig(options);
350
+ if (!config.profiles[name]) {
351
+ throw new Error(`Moda profile '${name}' does not exist.`);
352
+ }
353
+ const next = { ...config, active_profile: name };
354
+ saveProfileConfig(next, options);
355
+ return next;
356
+ }
357
+ function upsertProfile(profile, options = {}) {
358
+ assertValidProfileName(profile.name);
359
+ const config = loadProfileConfig(options);
360
+ const next = {
361
+ ...config,
362
+ profiles: {
363
+ ...config.profiles,
364
+ [profile.name]: {
365
+ ...profile,
366
+ metadata: {
367
+ ...profile.metadata,
368
+ updated_at: (options.now ?? new Date).toISOString()
369
+ }
370
+ }
371
+ }
372
+ };
373
+ saveProfileConfig(next, options);
374
+ return next;
375
+ }
376
+ function syncLegacyConfigToProfile(legacy, options = {}) {
377
+ const active = getActiveProfile(options);
378
+ return upsertProfile({
379
+ ...active,
380
+ ingest_url: legacy.ingest_url ?? active.ingest_url,
381
+ tenant_id: legacy.tenant_id ?? active.tenant_id,
382
+ api_key_ref: legacy.api_key ? "legacy:config.api_key" : active.api_key_ref,
383
+ metadata: {
384
+ ...active.metadata,
385
+ source: "migration",
386
+ legacy_config_path: getModaPaths(options).legacyConfigPath
387
+ }
388
+ }, options);
389
+ }
390
+ function clearStoredApiKeyCredentials(options = {}) {
391
+ let cleared = false;
392
+ const paths = getModaPaths(options);
393
+ const legacy = loadLegacyCliConfig(options);
394
+ if (legacy.api_key || legacy.tenant_id) {
395
+ writeSecretJsonFile(paths.legacyConfigPath, legacy.ingest_url ? { ingest_url: legacy.ingest_url } : {});
396
+ cleared = true;
397
+ }
398
+ const config = loadProfileConfig(options);
399
+ const profileName = options.profile || optionalString(getEnv(options).MODA_PROFILE) || config.active_profile;
400
+ const profile = config.profiles[profileName];
401
+ if (profile && (profile.api_key_ref || profile.tenant_id)) {
402
+ if (profile.api_key_ref?.startsWith("file:")) {
403
+ const secretPath = resolveFileSecretPath(profile.api_key_ref, options);
404
+ if (secretPath)
405
+ rmSync(secretPath, { force: true });
406
+ }
407
+ const { api_key_ref: _key, tenant_id: _tenant, ...rest } = profile;
408
+ saveProfileConfig({ ...config, profiles: { ...config.profiles, [profileName]: rest } }, options);
409
+ cleared = true;
410
+ }
411
+ return cleared;
412
+ }
413
+ function resolveFileSecretPath(reference, options) {
414
+ if (!reference.startsWith("file:"))
415
+ return;
416
+ const rawPath = reference.slice("file:".length);
417
+ if (!rawPath)
418
+ return;
419
+ const paths = getModaPaths(options);
420
+ const secretPath = isAbsolute(rawPath) ? normalize(rawPath) : normalize(join(paths.dir, rawPath));
421
+ const rel = relative(paths.dir, secretPath);
422
+ if (rel.startsWith("..") || isAbsolute(rel)) {
423
+ throw new Error(`Secret reference escapes the Moda directory: ${reference}`);
424
+ }
425
+ return secretPath;
426
+ }
427
+ function secretFilePathFromRef(reference, options = {}) {
428
+ return resolveFileSecretPath(reference, options);
429
+ }
430
+ function resolveSecretReference(reference, options = {}) {
431
+ if (!reference)
432
+ return;
433
+ const env = getEnv(options);
434
+ if (reference.startsWith("env:")) {
435
+ return env[reference.slice("env:".length)] || undefined;
436
+ }
437
+ if (reference === "legacy:config.api_key") {
438
+ return loadLegacyCliConfig(options).api_key;
439
+ }
440
+ if (reference === "legacy:auth.access_token") {
441
+ const parsed2 = safeReadRecord(getModaPaths(options).legacyAuthPath);
442
+ return optionalString(parsed2?.access_token);
443
+ }
444
+ const filePath = resolveFileSecretPath(reference, options);
445
+ if (!filePath)
446
+ return;
447
+ const parsed = safeReadRecord(filePath);
448
+ if (parsed) {
449
+ return optionalString(parsed.value) ?? optionalString(parsed.api_key) ?? optionalString(parsed.access_token);
450
+ }
451
+ try {
452
+ const raw = readFileSync(filePath, "utf8").trim();
453
+ return raw || undefined;
454
+ } catch {
455
+ return;
456
+ }
457
+ }
458
+ function resolveApiKeyFromActiveProfile(options = {}) {
459
+ try {
460
+ return resolveSecretReference(getActiveProfile(options).api_key_ref, options);
461
+ } catch {
462
+ return;
463
+ }
464
+ }
465
+ function normalizeModaBaseUrl(url) {
466
+ try {
467
+ const parsed = new URL(url);
468
+ if (parsed.hostname === "www.moda.dev") {
469
+ parsed.hostname = "moda.dev";
470
+ return parsed.toString().replace(/\/$/, "");
471
+ }
472
+ } catch {}
473
+ return url.replace(/\/$/, "");
474
+ }
475
+ function resolveBaseUrl(options = {}) {
476
+ const environment = activeProfileEnvironment(options);
477
+ const envBase = optionalString(getEnv(options).MODA_BASE_URL);
478
+ if (envBase) {
479
+ return enforceNonLoopbackBaseUrl(normalizeModaBaseUrl(envBase), environment);
480
+ }
481
+ try {
482
+ return enforceNonLoopbackBaseUrl(normalizeModaBaseUrl(getActiveProfile(options).base_url || PROD_BASE_URL), environment);
483
+ } catch {
484
+ return PROD_BASE_URL;
485
+ }
486
+ }
487
+ function resolveIngestUrl(options = {}) {
488
+ const environment = activeProfileEnvironment(options);
489
+ const envIngest = optionalString(getEnv(options).MODA_INGEST_URL);
490
+ if (envIngest) {
491
+ return enforceNonLoopbackIngestUrl(envIngest, environment);
492
+ }
493
+ try {
494
+ const profileIngest = getActiveProfile(options).ingest_url;
495
+ if (profileIngest) {
496
+ return enforceNonLoopbackIngestUrl(profileIngest, environment);
497
+ }
498
+ } catch {}
499
+ const legacyIngest = optionalString(loadLegacyCliConfig(options).ingest_url);
500
+ if (legacyIngest) {
501
+ return enforceNonLoopbackIngestUrl(legacyIngest, environment);
502
+ }
503
+ return defaultIngestUrlForEnvironment(environment);
504
+ }
505
+ function resolveTenantIdFromActiveProfile(options = {}) {
506
+ try {
507
+ return getActiveProfile(options).tenant_id;
508
+ } catch {
509
+ return;
510
+ }
511
+ }
512
+ function modeString(mode) {
513
+ return `0${(mode & 511).toString(8).padStart(3, "0")}`;
514
+ }
515
+ function ensureSecretFilePermissions(path, repair = true) {
516
+ if (!existsSync(path)) {
517
+ return { path, exists: false, secure: false, repaired: false };
518
+ }
519
+ const before = statSync(path);
520
+ if (!before.isFile()) {
521
+ return {
522
+ path,
523
+ exists: true,
524
+ secure: false,
525
+ repaired: false,
526
+ mode: modeString(before.mode),
527
+ reason: "not_file"
528
+ };
529
+ }
530
+ if ((before.mode & 63) !== 0 && repair) {
531
+ try {
532
+ chmodSync(path, 384);
533
+ const after = statSync(path);
534
+ return {
535
+ path,
536
+ exists: true,
537
+ secure: (after.mode & 63) === 0,
538
+ repaired: true,
539
+ mode: modeString(after.mode)
540
+ };
541
+ } catch {
542
+ return {
543
+ path,
544
+ exists: true,
545
+ secure: false,
546
+ repaired: false,
547
+ mode: modeString(before.mode),
548
+ reason: "chmod_failed"
549
+ };
550
+ }
551
+ }
552
+ return {
553
+ path,
554
+ exists: true,
555
+ secure: (before.mode & 63) === 0,
556
+ repaired: false,
557
+ mode: modeString(before.mode)
558
+ };
559
+ }
560
+ function writeSecretJsonFile(path, value) {
561
+ mkdirSync(dirname(path), { recursive: true });
562
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}
563
+ `, { mode: 384 });
564
+ try {
565
+ chmodSync(path, 384);
566
+ } catch {}
567
+ }
568
+
569
+ // src/config.ts
570
+ var pkg = JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf8"));
571
+ var VERSION = pkg.version;
572
+ var MODA_BASE_URL = resolveBaseUrl();
573
+ function loadCliConfig(options = {}) {
574
+ return loadLegacyCliConfig(options);
575
+ }
576
+ function saveCliConfig(update, options = {}) {
577
+ const merged = writeLegacyCliConfig(update, options);
578
+ try {
579
+ syncLegacyConfigToProfile(merged, options);
580
+ } catch {}
581
+ }
582
+ function resolveApiKey(options = {}) {
583
+ const env = options.env ?? process.env;
584
+ return env.MODA_API_KEY || resolveApiKeyFromActiveProfile(options) || loadCliConfig(options).api_key || undefined;
585
+ }
586
+ var SIGNED_API_KEY_PREFIX = "moda_sk_";
587
+ var SIGNED_API_KEY_VERSION = "v1";
588
+ function embeddedTenantIdFromApiKey(key) {
589
+ if (!key.startsWith(SIGNED_API_KEY_PREFIX))
590
+ return;
591
+ try {
592
+ const decoded = Buffer.from(key.slice(SIGNED_API_KEY_PREFIX.length), "base64url").toString("utf8");
593
+ const parts = decoded.split(":");
594
+ if (parts.length !== 5 || parts[0] !== SIGNED_API_KEY_VERSION)
595
+ return;
596
+ return parts[2] || undefined;
597
+ } catch {
598
+ return;
599
+ }
600
+ }
601
+ function resolveIngestUrl2(options = {}) {
602
+ return resolveIngestUrl(options);
603
+ }
604
+ function resolveTenantId(options = {}) {
605
+ const env = options.env ?? process.env;
606
+ return env.MODA_TENANT_ID || resolveTenantIdFromActiveProfile(options) || loadCliConfig(options).tenant_id || undefined;
607
+ }
608
+ var MODA_API_KEY = process.env.MODA_API_KEY;
609
+ var MODA_INGEST_URL = process.env.MODA_INGEST_URL || DEFAULT_INGEST_URL;
610
+ function resolveModaBaseUrl(options = {}) {
611
+ return resolveBaseUrl(options) || PROD_BASE_URL;
612
+ }
613
+ function validateConfig() {
614
+ if (!resolveApiKey()) {
615
+ throw new CliAuthError('No Moda API key found. Set MODA_API_KEY or run "moda init".', {
616
+ reason: "no_stored_credentials",
617
+ login_command: ["moda", "init"]
618
+ }, "Get your API key from https://moda.dev/settings");
619
+ }
620
+ }
621
+
622
+ // src/output.ts
623
+ import { randomUUID } from "node:crypto";
624
+
625
+ // ../../packages/harness-snapshot/src/policy.ts
626
+ var MAX_SCANNED_FILE_BYTES = 512 * 1024;
627
+ var DEFAULT_MAX_INVENTORY_FILES = 12000;
628
+ var DEFAULT_MAX_INVENTORY_TEXT_BYTES = 48 * 1024 * 1024;
629
+ var MAX_SNAPSHOT_BODY_BYTES = 4500000;
630
+ var MAX_RECORDED_SKIPS = 1000;
631
+ var SKIPPED_DIRS = new Set([
632
+ ".angular",
633
+ ".cache",
634
+ ".expo",
635
+ ".git",
636
+ ".hg",
637
+ ".idea",
638
+ ".svn",
639
+ ".next",
640
+ ".nuxt",
641
+ ".output",
642
+ ".parcel-cache",
643
+ ".pytest_cache",
644
+ ".ruff_cache",
645
+ ".serverless",
646
+ ".sst",
647
+ ".turbo",
648
+ ".wrangler",
649
+ ".wrangler-shared",
650
+ ".moda",
651
+ ".moda-test",
652
+ ".moda-test-home",
653
+ ".moda-cli-workspaces",
654
+ ".moda-cli-benchmarks",
655
+ "node_modules",
656
+ "dist",
657
+ "build",
658
+ "coverage",
659
+ "out",
660
+ "target",
661
+ "logs",
662
+ "tmp",
663
+ "temp",
664
+ "__pycache__",
665
+ ".venv",
666
+ "venv"
667
+ ]);
668
+ var SKIPPED_PATH_PREFIXES = [
669
+ ".claude/worktrees",
670
+ ".codex/environments"
671
+ ];
672
+ function posixNormalize(path) {
673
+ const absolute = path.startsWith("/");
674
+ const parts = path.split("/").filter((part) => part !== "" && part !== ".");
675
+ const normalized = parts.join("/");
676
+ return absolute ? `/${normalized}` : normalized;
677
+ }
678
+ function basename(path) {
679
+ const normalized = posixNormalize(path);
680
+ const index = normalized.lastIndexOf("/");
681
+ return index === -1 ? normalized : normalized.slice(index + 1);
682
+ }
683
+ function extname(path) {
684
+ const name = basename(path);
685
+ const index = name.lastIndexOf(".");
686
+ return index <= 0 ? "" : name.slice(index);
687
+ }
688
+ function isSkippedDir(path) {
689
+ return SKIPPED_DIRS.has(basename(path));
690
+ }
691
+ function isSkippedPathPrefix(path) {
692
+ const normalized = posixNormalize(path);
693
+ return SKIPPED_PATH_PREFIXES.some((prefix) => normalized === prefix || normalized.startsWith(`${prefix}/`));
694
+ }
695
+ function isSourceFile(path) {
696
+ return /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(path);
697
+ }
698
+ function isPackageOrLockfile(path) {
699
+ return path === "package.json" || path === "pyproject.toml" || path === "bun.lock" || path === "bun.lockb" || path === "pnpm-lock.yaml" || path === "yarn.lock";
700
+ }
701
+ function isPromptLikePath(path) {
702
+ return /(^|\/)prompts?\//i.test(path) && /\.(prompt\.)?(md|json|ya?ml)$/i.test(path);
703
+ }
704
+ function isSkillLikePath(path) {
705
+ return /(^|\/)(\.claude\/skills|\.cursor\/rules|skills?)\//i.test(path) && /\.(md|mdc)$/i.test(path);
706
+ }
707
+ function isEvalLikePath(path) {
708
+ return /(^|\/)(evals?|tests?|__tests__)\//i.test(path) && /\.(ts|tsx|js|mjs|py|json|ya?ml)$/i.test(path);
709
+ }
710
+ function isDeploymentLikePath(path) {
711
+ return /(^|\/)(wrangler\.toml|vercel\.json|render\.ya?ml|railway\.json|Dockerfile|docker-compose\.ya?ml)$/i.test(path) || /^\.github\/workflows\/[^/]+\.ya?ml$/i.test(path);
712
+ }
713
+ function isDeveloperAgentConfigPath(path) {
714
+ return path === ".claude/settings.json" || path === ".codex/hooks.json" || path === ".cursor/hooks.json";
715
+ }
716
+ function isReadmeOrAgents(path) {
717
+ const name = basename(path);
718
+ return name === "README.md" || name === "AGENTS.md";
719
+ }
720
+ function shouldInventoryFile(path) {
721
+ const normalized = posixNormalize(path);
722
+ const name = basename(normalized);
723
+ const extension = extname(normalized).toLowerCase();
724
+ if (isSourceFile(normalized))
725
+ return true;
726
+ if (isPackageOrLockfile(normalized))
727
+ return true;
728
+ if (isPromptLikePath(normalized))
729
+ return true;
730
+ if (isSkillLikePath(normalized))
731
+ return true;
732
+ if (isEvalLikePath(normalized))
733
+ return true;
734
+ if (isDeploymentLikePath(normalized))
735
+ return true;
736
+ if (isDeveloperAgentConfigPath(normalized))
737
+ return true;
738
+ if (isReadmeOrAgents(normalized))
739
+ return true;
740
+ return extension === ".toml" && (name === "pyproject.toml" || name === "wrangler.toml");
741
+ }
742
+ function isTextFile(path) {
743
+ const extension = extname(path).toLowerCase();
744
+ return [
745
+ "",
746
+ ".cjs",
747
+ ".js",
748
+ ".json",
749
+ ".jsx",
750
+ ".md",
751
+ ".mdc",
752
+ ".mjs",
753
+ ".py",
754
+ ".toml",
755
+ ".ts",
756
+ ".tsx",
757
+ ".txt",
758
+ ".yaml",
759
+ ".yml"
760
+ ].includes(extension);
761
+ }
762
+ function isSecretishPath(path) {
763
+ const name = basename(path).toLowerCase();
764
+ return name === ".env" || name.startsWith(".env.") || name.includes("secret") || name.includes("private-key") || name.endsWith(".pem");
765
+ }
766
+
767
+ // ../../packages/harness-snapshot/src/redact.ts
768
+ var REDACTED = "[REDACTED]";
769
+ var MAX_DEPTH = 256;
770
+ var SECRET_KEYS = new Set([
771
+ "api_key",
772
+ "apikey",
773
+ "token",
774
+ "access_token",
775
+ "authorization",
776
+ "secret",
777
+ "password",
778
+ "cli_token"
779
+ ]);
780
+ var SECRET_PATTERNS = [
781
+ { pattern: /\bmoda_sk_[A-Za-z0-9_-]+\b/g, prefixLength: 8 },
782
+ { pattern: /\bmoda_ct_[A-Za-z0-9_-]+\b/g, prefixLength: 8 },
783
+ { pattern: /\bBearer\s+([A-Za-z0-9._~+/-]+=*)\b/gi, prefixLength: 8 },
784
+ { pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, prefixLength: 8 },
785
+ { pattern: /\bsk-ant-api\d{2}-[A-Za-z0-9_-]{20,}\b/g, prefixLength: 12 },
786
+ { pattern: /\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,}\b/g, prefixLength: 8 },
787
+ { pattern: /\bAKIA[A-Z0-9]{16}\b/g, prefixLength: 8 },
788
+ { pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g, prefixLength: 8 },
789
+ { pattern: /\bgh[pousr]_[A-Za-z0-9]{36,}\b/g, prefixLength: 8 },
790
+ { pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, prefixLength: 8 }
791
+ ];
792
+ function redactDeep(value) {
793
+ return redactValue(value, 0, new WeakSet);
794
+ }
795
+ function redactValue(value, depth, seen) {
796
+ if (typeof value === "string")
797
+ return redactString(value);
798
+ if (value === null || typeof value !== "object")
799
+ return value;
800
+ if (depth >= MAX_DEPTH)
801
+ return "[MaxDepth]";
802
+ if (seen.has(value))
803
+ return "[Circular]";
804
+ seen.add(value);
805
+ if (Array.isArray(value)) {
806
+ try {
807
+ return value.map((item) => redactValue(item, depth + 1, seen));
808
+ } finally {
809
+ seen.delete(value);
810
+ }
811
+ }
812
+ const out = {};
813
+ try {
814
+ for (const [key, item] of Object.entries(value)) {
815
+ out[key] = shouldRedactKey(key) ? redactSecretValue(item) : redactValue(item, depth + 1, seen);
816
+ }
817
+ } finally {
818
+ seen.delete(value);
819
+ }
820
+ return out;
821
+ }
822
+ function shouldRedactKey(key) {
823
+ return SECRET_KEYS.has(key.replace(/[-\s]/g, "_").toLowerCase());
824
+ }
825
+ function redactSecretValue(value) {
826
+ if (typeof value !== "string")
827
+ return REDACTED;
828
+ const redacted = redactString(value);
829
+ if (redacted !== value)
830
+ return redacted;
831
+ if (/^(configured|missing|not_configured|none|present|absent)$/i.test(value))
832
+ return value;
833
+ return REDACTED;
834
+ }
835
+ function redactStringWithCount(input) {
836
+ let text = input;
837
+ let redactionCount = 0;
838
+ for (const { pattern, prefixLength } of SECRET_PATTERNS) {
839
+ text = text.replace(pattern, (match) => {
840
+ redactionCount += 1;
841
+ if (/^Bearer\s+/i.test(match)) {
842
+ const token = match.replace(/^Bearer\s+/i, "");
843
+ return `Bearer ${token.slice(0, prefixLength)}...[REDACTED]`;
844
+ }
845
+ return `${match.slice(0, prefixLength)}...[REDACTED]`;
846
+ });
847
+ }
848
+ return { text, redactionCount };
849
+ }
850
+ function redactString(input) {
851
+ return redactStringWithCount(input).text;
852
+ }
853
+ function redactSnapshotText(text) {
854
+ const parts = text.split(/(\r\n|\r|\n)/);
855
+ let redactionCount = 0;
856
+ for (let index = 0;index < parts.length; index += 2) {
857
+ const redacted = redactStringWithCount(parts[index] ?? "");
858
+ parts[index] = redacted.text;
859
+ redactionCount += redacted.redactionCount;
860
+ }
861
+ return { text: parts.join(""), redactionCount };
862
+ }
863
+
864
+ // ../../packages/harness-snapshot/src/pack.ts
865
+ function utf8ByteLength(value) {
866
+ let bytes = 0;
867
+ for (let index = 0;index < value.length; index += 1) {
868
+ const code = value.charCodeAt(index);
869
+ if (code <= 127) {
870
+ bytes += 1;
871
+ } else if (code <= 2047) {
872
+ bytes += 2;
873
+ } else if (code >= 55296 && code <= 56319) {
874
+ const next = value.charCodeAt(index + 1);
875
+ if (next >= 56320 && next <= 57343) {
876
+ bytes += 4;
877
+ index += 1;
878
+ } else {
879
+ bytes += 3;
880
+ }
881
+ } else {
882
+ bytes += 3;
883
+ }
884
+ }
885
+ return bytes;
886
+ }
887
+ function isValidRelativePath(path) {
888
+ if (path.length === 0 || path.startsWith("/") || path.includes("\\"))
889
+ return false;
890
+ return !path.split("/").some((part) => part === "..");
891
+ }
892
+ function packSnapshot(inputs, opts) {
893
+ const maxFiles = opts.maxFiles ?? DEFAULT_MAX_INVENTORY_FILES;
894
+ const maxTextBytes = opts.maxTextBytes ?? DEFAULT_MAX_INVENTORY_TEXT_BYTES;
895
+ const maxSnapshotBytes = opts.maxSnapshotBytes ?? MAX_SNAPSHOT_BODY_BYTES;
896
+ const files = [];
897
+ const skips = [];
898
+ let inventoryTextBytes = 0;
899
+ let serializedBytes = 0;
900
+ let byteCount = 0;
901
+ let truncated = false;
902
+ const recordSkip = (path, reason) => {
903
+ if (skips.length < MAX_RECORDED_SKIPS)
904
+ skips.push({ path, reason });
905
+ };
906
+ const sorted = [...inputs].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
907
+ for (const input of sorted) {
908
+ if (!isValidRelativePath(input.path)) {
909
+ recordSkip(input.path, "invalid relative path");
910
+ continue;
911
+ }
912
+ const path = posixNormalize(input.path);
913
+ if (input.isSymlink) {
914
+ recordSkip(path, "symlink skipped for safety");
915
+ continue;
916
+ }
917
+ if (isSecretishPath(path)) {
918
+ recordSkip(path, "secret-bearing path");
919
+ continue;
920
+ }
921
+ if (!shouldInventoryFile(path)) {
922
+ recordSkip(path, "not an inventory file");
923
+ continue;
924
+ }
925
+ if (!isTextFile(path)) {
926
+ recordSkip(path, "non-text file");
927
+ continue;
928
+ }
929
+ if (input.byteSize > MAX_SCANNED_FILE_BYTES) {
930
+ recordSkip(path, "file too large");
931
+ truncated = true;
932
+ continue;
933
+ }
934
+ if (files.length >= maxFiles || inventoryTextBytes + input.byteSize > maxTextBytes) {
935
+ recordSkip(path, "inventory budget");
936
+ truncated = true;
937
+ continue;
938
+ }
939
+ const text = opts.redact === false ? input.text : redactSnapshotText(input.text).text;
940
+ const file = { path, text };
941
+ const serializedCost = utf8ByteLength(JSON.stringify(file)) + 1;
942
+ if (serializedBytes + serializedCost > maxSnapshotBytes) {
943
+ recordSkip(path, "snapshot budget");
944
+ truncated = true;
945
+ break;
946
+ }
947
+ files.push(file);
948
+ inventoryTextBytes += input.byteSize;
949
+ serializedBytes += serializedCost;
950
+ byteCount += utf8ByteLength(text);
951
+ }
952
+ return {
953
+ snapshot: {
954
+ repo: { name: opts.repoName, root: "." },
955
+ files,
956
+ truncated,
957
+ fileCount: files.length,
958
+ byteCount,
959
+ ...opts.gitCommit ? { gitCommit: opts.gitCommit } : {}
960
+ },
961
+ skips
962
+ };
963
+ }
964
+ // src/output.ts
965
+ var AGENT_SCHEMA_VERSION = "moda.agent.v1";
966
+ var AGENT_EVENT_SCHEMA_VERSION = "moda.agent_event.v1";
967
+ var 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.';
968
+ var EXIT_OK = 0;
969
+ var EXIT_ERROR = 1;
970
+ var EXIT_DEGRADED = 3;
971
+ var EXIT_AUTH_REQUIRED = 4;
972
+ var EXIT_INPUT_REQUIRED = 5;
973
+ var EXIT_CANCELLED = 130;
974
+ var AGENT_ERROR_CODES = [
975
+ "input_error",
976
+ "api_error",
977
+ "network_error",
978
+ "auth_required",
979
+ "input_required",
980
+ "cancelled",
981
+ "command_error",
982
+ "access_denied",
983
+ "unknown_error",
984
+ "cloud_unavailable"
985
+ ];
986
+ var FALLBACK_RUN_ID = randomUUID();
987
+ var fallbackSeq = 0;
988
+ var stdoutClosed = false;
989
+ var stderrClosed = false;
990
+ process.stdout.on("error", (error) => {
991
+ if (error.code === "EPIPE") {
992
+ stdoutClosed = true;
993
+ process.exitCode = EXIT_CANCELLED;
994
+ return;
995
+ }
996
+ throw error;
997
+ });
998
+ process.stderr.on("error", (error) => {
999
+ if (error.code === "EPIPE") {
1000
+ stderrClosed = true;
1001
+ process.exitCode = EXIT_CANCELLED;
1002
+ return;
1003
+ }
1004
+ throw error;
1005
+ });
1006
+ function wasOutputCancelled() {
1007
+ return stdoutClosed || stderrClosed;
1008
+ }
1009
+ function safeWriteStdout(text) {
1010
+ if (stdoutClosed)
1011
+ return false;
1012
+ try {
1013
+ if (false) {} else {
1014
+ process.stdout.write(text);
1015
+ }
1016
+ return true;
1017
+ } catch (error) {
1018
+ if (error.code === "EPIPE") {
1019
+ stdoutClosed = true;
1020
+ process.exitCode = EXIT_CANCELLED;
1021
+ return false;
1022
+ }
1023
+ throw error;
1024
+ }
1025
+ }
1026
+ function safeWriteStderr(text) {
1027
+ if (stderrClosed)
1028
+ return false;
1029
+ try {
1030
+ if (false) {} else {
1031
+ process.stderr.write(text);
1032
+ }
1033
+ return true;
1034
+ } catch (error) {
1035
+ if (error.code === "EPIPE") {
1036
+ stderrClosed = true;
1037
+ process.exitCode = EXIT_CANCELLED;
1038
+ return false;
1039
+ }
1040
+ throw error;
1041
+ }
1042
+ }
1043
+ function explicitModeFromFlags(flags) {
1044
+ if (flags.agent === "true" && (flags.stream === "true" || flags.format === "ndjson"))
1045
+ return "agent-stream";
1046
+ if (flags.agent === "true")
1047
+ return "agent";
1048
+ if (flags.json === "true")
1049
+ return "json";
1050
+ if (flags.human === "true" || flags.prose === "true")
1051
+ return "human";
1052
+ return;
1053
+ }
1054
+ function outputModeFromFlags(flags) {
1055
+ return explicitModeFromFlags(flags) ?? "human";
1056
+ }
1057
+ function outputModeFromEnv(env) {
1058
+ const fmt = (env.MODA_FORMAT ?? "").trim().toLowerCase();
1059
+ if (fmt === "human" || fmt === "json" || fmt === "agent")
1060
+ return fmt;
1061
+ if (fmt === "agent-stream" || fmt === "ndjson")
1062
+ return "agent-stream";
1063
+ return;
1064
+ }
1065
+ function truthyEnv(value) {
1066
+ return value != null && value !== "" && value !== "0" && value.toLowerCase() !== "false";
1067
+ }
1068
+ function isAgentEnvironment(env) {
1069
+ return truthyEnv(env.CLAUDECODE) || truthyEnv(env.CURSOR) || truthyEnv(env.CI) || truthyEnv(env.MODA_AGENT);
1070
+ }
1071
+ function resolveOutputMode(flags, opts = {}) {
1072
+ const explicit = explicitModeFromFlags(flags);
1073
+ if (explicit)
1074
+ return explicit;
1075
+ const env = opts.env ?? process.env;
1076
+ const fromEnv = outputModeFromEnv(env);
1077
+ if (fromEnv)
1078
+ return fromEnv;
1079
+ if (isAgentEnvironment(env))
1080
+ return "agent";
1081
+ const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);
1082
+ if (!isTTY)
1083
+ return "agent";
1084
+ return "human";
1085
+ }
1086
+ function createCliOutput(options) {
1087
+ const state = {
1088
+ runId: options.runId ?? randomUUID(),
1089
+ seq: 0,
1090
+ started: false,
1091
+ terminal: false,
1092
+ startedAt: Date.now(),
1093
+ lastEventAt: Date.now()
1094
+ };
1095
+ const emitEvent = (event) => {
1096
+ if (options.quiet || options.mode !== "agent-stream")
1097
+ return;
1098
+ if (state.terminal)
1099
+ return;
1100
+ if (event.event === "started" && state.started)
1101
+ return;
1102
+ if (event.event === "started")
1103
+ state.started = true;
1104
+ if (isTerminalEvent(event.event)) {
1105
+ state.terminal = true;
1106
+ clearHeartbeat(state);
1107
+ }
1108
+ state.lastEventAt = Date.now();
1109
+ safeWriteStdout(`${JSON.stringify(createAgentEvent(options.command, event, state))}
1110
+ `);
1111
+ };
1112
+ const ensureStarted = () => {
1113
+ if (options.mode !== "agent-stream" || state.started || options.quiet)
1114
+ return;
1115
+ startHeartbeat(options.command, state);
1116
+ emitEvent({
1117
+ event: "started",
1118
+ phase: options.command,
1119
+ message: `Starting ${options.command}.`,
1120
+ elapsed_ms: 0
1121
+ });
1122
+ };
1123
+ const writeEnvelope = (data, writeOptions = {}) => {
1124
+ return createAgentEnvelope({
1125
+ command: options.command,
1126
+ status: writeOptions.status === "error" ? "error" : writeOptions.status === "degraded" ? "degraded" : "ok",
1127
+ data,
1128
+ meta: {
1129
+ ...writeOptions.meta ?? {},
1130
+ run_id: state.runId,
1131
+ ...options.command === "feedback" ? {} : { tip: FEEDBACK_TIP }
1132
+ },
1133
+ summary: writeOptions.summary,
1134
+ findings: writeOptions.findings,
1135
+ evidenceRefs: writeOptions.evidenceRefs,
1136
+ recommendedActions: writeOptions.recommendedActions,
1137
+ nextCommands: writeOptions.nextCommands,
1138
+ artifacts: writeOptions.artifacts,
1139
+ warnings: writeOptions.warnings,
1140
+ errors: writeOptions.errors
1141
+ });
1142
+ };
1143
+ const output = {
1144
+ mode: options.mode,
1145
+ quiet: options.quiet === true,
1146
+ writeData(data, writeOptions = {}) {
1147
+ if (options.quiet)
1148
+ return;
1149
+ if (options.mode === "agent" || options.mode === "agent-stream") {
1150
+ if (state.terminal)
1151
+ return;
1152
+ const envelope = writeEnvelope(data, writeOptions);
1153
+ if (options.mode === "agent-stream") {
1154
+ ensureStarted();
1155
+ emitEvent({
1156
+ event: envelope.status === "error" ? "error" : "completed",
1157
+ status: envelope.status,
1158
+ result: envelope,
1159
+ ...envelope.errors[0] ? { error: envelope.errors[0] } : {}
1160
+ });
1161
+ } else {
1162
+ state.terminal = true;
1163
+ safeWriteStdout(`${JSON.stringify(envelope)}
1164
+ `);
1165
+ }
1166
+ return;
1167
+ }
1168
+ safeWriteStdout(`${JSON.stringify(redactDeep(data), null, 2)}
1169
+ `);
1170
+ },
1171
+ writeEvent(event) {
1172
+ if (options.quiet)
1173
+ return;
1174
+ if (options.mode !== "agent-stream")
1175
+ return;
1176
+ if (event.event === "started") {
1177
+ startHeartbeat(options.command, state);
1178
+ emitEvent(event);
1179
+ return;
1180
+ }
1181
+ ensureStarted();
1182
+ emitEvent(event);
1183
+ },
1184
+ writeError(error) {
1185
+ if (options.quiet || state.terminal)
1186
+ return;
1187
+ const agentError = errorToAgentError(error);
1188
+ const envelope = createAgentErrorEnvelope(options.command || "unknown", agentError, state.runId);
1189
+ if (options.mode === "agent-stream") {
1190
+ ensureStarted();
1191
+ if (error instanceof CliInputRequiredError) {
1192
+ emitEvent({
1193
+ event: "needs_input",
1194
+ status: "error",
1195
+ message: error.message,
1196
+ error: agentError,
1197
+ input_request: error.input_request,
1198
+ result: envelope
1199
+ });
1200
+ return;
1201
+ }
1202
+ if (error instanceof CliAuthError) {
1203
+ emitEvent({
1204
+ event: "auth_required",
1205
+ status: "error",
1206
+ message: error.message,
1207
+ error: agentError,
1208
+ auth: error.auth,
1209
+ result: envelope
1210
+ });
1211
+ return;
1212
+ }
1213
+ if (error instanceof CliCancellationError) {
1214
+ emitEvent({
1215
+ event: "cancelled",
1216
+ status: "error",
1217
+ message: error.message,
1218
+ error: agentError,
1219
+ result: envelope
1220
+ });
1221
+ return;
1222
+ }
1223
+ emitEvent({
1224
+ event: "error",
1225
+ status: "error",
1226
+ message: agentError.message,
1227
+ error: agentError,
1228
+ result: envelope
1229
+ });
1230
+ return;
1231
+ }
1232
+ if (options.mode === "agent" || options.mode === "json") {
1233
+ state.terminal = true;
1234
+ safeWriteStdout(`${JSON.stringify(envelope)}
1235
+ `);
1236
+ }
1237
+ },
1238
+ writeCancelled(reason = "Command cancelled.") {
1239
+ output.writeError(new CliCancellationError(reason));
1240
+ },
1241
+ hasTerminalOutput() {
1242
+ return state.terminal;
1243
+ },
1244
+ close() {
1245
+ clearHeartbeat(state);
1246
+ }
1247
+ };
1248
+ return output;
1249
+ }
1250
+ function createAgentEnvelope(options) {
1251
+ const safeData = redactDeep(options.data);
1252
+ const bounded = boundForAgent(safeData);
1253
+ const envelope = {
1254
+ schema_version: AGENT_SCHEMA_VERSION,
1255
+ command: options.command,
1256
+ status: options.status,
1257
+ generated_at: new Date().toISOString(),
1258
+ ...options.profile ? { profile: options.profile } : {},
1259
+ summary: normalizeSummary(options.summary, options.command, options.status, safeData),
1260
+ findings: options.findings ?? [],
1261
+ evidence_refs: options.evidenceRefs ?? [],
1262
+ recommended_actions: options.recommendedActions ?? [],
1263
+ next_commands: normalizeNextCommands(options.nextCommands ?? []),
1264
+ artifacts: options.artifacts ?? [],
1265
+ warnings: options.warnings ?? inferWarnings(safeData),
1266
+ errors: options.errors ?? [],
1267
+ data: bounded.data,
1268
+ ...options.meta ? { meta: options.meta } : {},
1269
+ ...bounded.truncation ? { truncation: bounded.truncation } : {}
1270
+ };
1271
+ return redactDeep(envelope);
1272
+ }
1273
+ function createAgentErrorEnvelope(command, error, runId) {
1274
+ return createAgentEnvelope({
1275
+ command,
1276
+ status: "error",
1277
+ data: null,
1278
+ meta: runId ? { run_id: runId } : undefined,
1279
+ summary: {
1280
+ text: error.message,
1281
+ confidence: "high"
1282
+ },
1283
+ errors: [error],
1284
+ nextCommands: (error.suggested_commands ?? []).map((commandText) => ({
1285
+ command: commandText,
1286
+ purpose: "Suggested recovery command.",
1287
+ mutability: commandText.includes(" sync") || commandText.startsWith("moda sync") ? "sync" : "read",
1288
+ requires_approval: commandText.includes(" --yes") || commandText.includes(" sync")
1289
+ }))
1290
+ });
1291
+ }
1292
+ function createAgentEvent(command, event, state) {
1293
+ const stamped = {
1294
+ schema_version: AGENT_EVENT_SCHEMA_VERSION,
1295
+ command,
1296
+ generated_at: new Date().toISOString(),
1297
+ run_id: state?.runId ?? FALLBACK_RUN_ID,
1298
+ seq: state ? ++state.seq : ++fallbackSeq,
1299
+ event_version: event.event_version ?? 1,
1300
+ ...event
1301
+ };
1302
+ return redactDeep(stamped);
1303
+ }
1304
+ function errorToAgentError(error) {
1305
+ if (error instanceof CliInputRequiredError) {
1306
+ return {
1307
+ code: "input_required",
1308
+ message: error.message,
1309
+ cause: error.usage,
1310
+ suggested_commands: error.input_request.resume_command ? [error.input_request.resume_command.join(" ")] : undefined,
1311
+ input_request: error.input_request,
1312
+ blocking: true
1313
+ };
1314
+ }
1315
+ if (error instanceof CliAuthError) {
1316
+ const suggested = [error.auth.login_command.join(" ")];
1317
+ if (error.auth.resume_command)
1318
+ suggested.push(error.auth.resume_command.join(" "));
1319
+ return {
1320
+ code: "auth_required",
1321
+ message: error.message,
1322
+ cause: error.usage,
1323
+ suggested_commands: suggested,
1324
+ auth: error.auth,
1325
+ blocking: true
1326
+ };
1327
+ }
1328
+ if (error instanceof CliCancellationError) {
1329
+ return {
1330
+ code: "cancelled",
1331
+ message: error.message,
1332
+ blocking: true
1333
+ };
1334
+ }
1335
+ if (error instanceof CliAccessError) {
1336
+ return {
1337
+ code: "access_denied",
1338
+ message: error.message,
1339
+ cause: error.guidance,
1340
+ blocking: true
1341
+ };
1342
+ }
1343
+ if (error instanceof Error) {
1344
+ if (error instanceof CliInputError || error.name === "ZodError") {
1345
+ return {
1346
+ code: "input_error",
1347
+ message: error.message,
1348
+ cause: "usage" in error && typeof error.usage === "string" ? error.usage : undefined,
1349
+ suggested_commands: "usage" in error && typeof error.usage === "string" ? extractSuggestedCommands(error.usage) : undefined,
1350
+ blocking: true
1351
+ };
1352
+ }
1353
+ if (isApiLikeError(error)) {
1354
+ const statusCode = "statusCode" in error ? error.statusCode : undefined;
1355
+ return {
1356
+ code: "api_error",
1357
+ message: error.message,
1358
+ cause: typeof statusCode === "number" ? `HTTP ${statusCode}` : undefined,
1359
+ blocking: true
1360
+ };
1361
+ }
1362
+ if (isNetworkLikeError(error)) {
1363
+ return {
1364
+ code: "network_error",
1365
+ message: error.message,
1366
+ blocking: true
1367
+ };
1368
+ }
1369
+ return {
1370
+ code: "command_error",
1371
+ message: error.message,
1372
+ cause: "usage" in error && typeof error.usage === "string" ? error.usage : undefined,
1373
+ suggested_commands: "usage" in error && typeof error.usage === "string" ? extractSuggestedCommands(error.usage) : undefined,
1374
+ blocking: true
1375
+ };
1376
+ }
1377
+ return {
1378
+ code: "unknown_error",
1379
+ message: String(error),
1380
+ blocking: true
1381
+ };
1382
+ }
1383
+ function isTerminalEvent(event) {
1384
+ return event === "completed" || event === "error" || event === "cancelled" || event === "needs_input" || event === "auth_required";
1385
+ }
1386
+ function startHeartbeat(command, state) {
1387
+ if (state.heartbeat)
1388
+ return;
1389
+ const intervalMs = Number(process.env.MODA_HEARTBEAT_MS ?? 1e4);
1390
+ if (!Number.isFinite(intervalMs) || intervalMs <= 0)
1391
+ return;
1392
+ state.heartbeat = setInterval(() => {
1393
+ if (state.terminal || stdoutClosed)
1394
+ return;
1395
+ const now = Date.now();
1396
+ if (now - state.lastEventAt < intervalMs)
1397
+ return;
1398
+ state.lastEventAt = now;
1399
+ safeWriteStdout(`${JSON.stringify(createAgentEvent(command, {
1400
+ event: "heartbeat",
1401
+ elapsed_ms: now - state.startedAt
1402
+ }, state))}
1403
+ `);
1404
+ }, intervalMs);
1405
+ state.heartbeat.unref();
1406
+ }
1407
+ function clearHeartbeat(state) {
1408
+ if (!state.heartbeat)
1409
+ return;
1410
+ clearInterval(state.heartbeat);
1411
+ state.heartbeat = undefined;
1412
+ }
1413
+ function normalizeSummary(summary, command, status, data) {
1414
+ if (typeof summary === "string") {
1415
+ return { text: summary, confidence: "high" };
1416
+ }
1417
+ if (summary?.text) {
1418
+ return {
1419
+ text: summary.text,
1420
+ confidence: summary.confidence ?? "high"
1421
+ };
1422
+ }
1423
+ return inferSummary(command, status, data);
1424
+ }
1425
+ function inferSummary(command, status, data) {
1426
+ const record = asRecord(data);
1427
+ if (status === "error") {
1428
+ return { text: `${command} failed.`, confidence: "high" };
1429
+ }
1430
+ const summary = asRecord(record?.summary);
1431
+ if (summary) {
1432
+ const statusValue = typeof summary.status === "string" ? summary.status : undefined;
1433
+ const fail = typeof summary.fail === "number" ? summary.fail : undefined;
1434
+ const warning = typeof summary.warning === "number" ? summary.warning : undefined;
1435
+ if (statusValue && fail !== undefined && warning !== undefined) {
1436
+ return {
1437
+ text: `${command} completed with status ${statusValue}: ${fail} failing checks and ${warning} warnings.`,
1438
+ confidence: "high"
1439
+ };
1440
+ }
1441
+ const runtimeAgentCount = typeof summary.runtimeAgentCount === "number" ? summary.runtimeAgentCount : undefined;
1442
+ if (runtimeAgentCount !== undefined) {
1443
+ return {
1444
+ text: `${command} completed. Harness summary includes ${runtimeAgentCount} runtime agent(s).`,
1445
+ confidence: "high"
1446
+ };
1447
+ }
1448
+ }
1449
+ if (typeof record?.status === "string") {
1450
+ return { text: `${command} completed with status ${record.status}.`, confidence: "high" };
1451
+ }
1452
+ return { text: `${command} completed.`, confidence: "medium" };
1453
+ }
1454
+ function normalizeNextCommands(commands) {
1455
+ return commands.map((command) => {
1456
+ if (typeof command !== "string")
1457
+ return command;
1458
+ return {
1459
+ command,
1460
+ purpose: "Suggested next Moda command.",
1461
+ mutability: inferMutability(command),
1462
+ requires_approval: command.includes(" --yes") || command.includes(" sync")
1463
+ };
1464
+ });
1465
+ }
1466
+ function inferMutability(command) {
1467
+ if (/\b(sync|approve|init|install|pull|gen|remove|promote)\b/.test(command)) {
1468
+ if (/\bsync\b/.test(command))
1469
+ return "sync";
1470
+ return "write";
1471
+ }
1472
+ return "read";
1473
+ }
1474
+ function inferWarnings(data) {
1475
+ const record = asRecord(data);
1476
+ const direct = record?.warnings;
1477
+ if (Array.isArray(direct))
1478
+ return direct.filter((item) => typeof item === "string");
1479
+ const validation = asRecord(record?.validation);
1480
+ if (Array.isArray(validation?.warnings)) {
1481
+ return validation.warnings.filter((item) => typeof item === "string");
1482
+ }
1483
+ return [];
1484
+ }
1485
+ function boundForAgent(data) {
1486
+ const maxBytes = Number(process.env.MODA_AGENT_MAX_OUTPUT_BYTES ?? 200000);
1487
+ const serialized = JSON.stringify(data);
1488
+ const bytes = Buffer.byteLength(serialized, "utf8");
1489
+ if (bytes <= maxBytes)
1490
+ return { data };
1491
+ const preview = serialized.slice(0, Math.max(0, maxBytes - 256));
1492
+ return {
1493
+ data: {
1494
+ truncated: true,
1495
+ original_bytes: bytes,
1496
+ max_bytes: maxBytes,
1497
+ preview
1498
+ },
1499
+ truncation: {
1500
+ data: {
1501
+ original_bytes: bytes,
1502
+ max_bytes: maxBytes,
1503
+ reason: "agent_output_byte_limit"
1504
+ }
1505
+ }
1506
+ };
1507
+ }
1508
+ function asRecord(value) {
1509
+ return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
1510
+ }
1511
+ function extractSuggestedCommands(text) {
1512
+ const commands = [...text.matchAll(/`(moda [^`]+)`/g)].map((match) => match[1]);
1513
+ return commands.length > 0 ? commands : undefined;
1514
+ }
1515
+
1516
+ // src/command-context.ts
1517
+ function flagEnabled(flags, key) {
1518
+ return flags[key] === "true";
1519
+ }
1520
+ function stringOption(value) {
1521
+ if (value === undefined || value === null)
1522
+ return;
1523
+ const text = String(value).trim();
1524
+ return text.length > 0 ? text : undefined;
1525
+ }
1526
+ function createCommandContext(input, options = {}) {
1527
+ const flags = options.commandFlags ?? input.flags;
1528
+ const rawFlags = options.rawFlags ?? input.flags;
1529
+ const outputMode = options.outputMode ?? outputModeFromFlags(flags);
1530
+ const quiet = flagEnabled(flags, "quiet");
1531
+ const interactive = outputMode === "human" && !quiet && !flagEnabled(flags, "yes") && process.stdin.isTTY === true && process.stderr.isTTY === true;
1532
+ const telemetryMeta = {};
1533
+ return {
1534
+ command: options.commandName ?? input.command,
1535
+ positional: input.positional,
1536
+ positionals: input.positionals,
1537
+ flags,
1538
+ rawFlags,
1539
+ startedAt: options.startedAt ?? Date.now(),
1540
+ cwd: options.cwd ?? process.cwd(),
1541
+ env: options.env ?? process.env,
1542
+ outputMode,
1543
+ output: createCliOutput({
1544
+ command: options.commandName ?? input.command,
1545
+ mode: outputMode,
1546
+ quiet
1547
+ }),
1548
+ profile: flags.profile,
1549
+ dryRun: flagEnabled(flags, "dry-run"),
1550
+ yes: flagEnabled(flags, "yes"),
1551
+ quiet,
1552
+ verbose: flagEnabled(flags, "verbose"),
1553
+ interactive,
1554
+ recordTelemetry(meta) {
1555
+ Object.assign(telemetryMeta, meta);
1556
+ },
1557
+ getTelemetryMeta() {
1558
+ return telemetryMeta;
1559
+ }
1560
+ };
1561
+ }
1562
+
1563
+ export { __require, PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, assertValidProfileName, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, CliAccessError, CliInputRequiredError, CliAuthError, CliCancellationError, isNetworkLikeError, isApiLikeError, VERSION, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveModaBaseUrl, validateConfig, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, redactDeep, packSnapshot, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, resolveOutputMode, createCliOutput, stringOption, createCommandContext };