@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/profiles.js DELETED
@@ -1,573 +0,0 @@
1
- import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
2
- import { homedir } from 'node:os';
3
- import { dirname, isAbsolute, join, normalize, relative } from 'node:path';
4
- export const PROFILE_CONFIG_VERSION = 1;
5
- export const PROD_BASE_URL = 'https://moda.dev';
6
- export const DEFAULT_INGEST_URL = 'https://moda-ingest.modas.workers.dev';
7
- export const DEFAULT_PROFILE_NAME = 'prod';
8
- function getEnv(options = {}) {
9
- return options.env ?? process.env;
10
- }
11
- export function resolveModaHome(options = {}) {
12
- const env = getEnv(options);
13
- return options.home || env.HOME || env.USERPROFILE || homedir();
14
- }
15
- export function getModaPaths(options = {}) {
16
- const home = resolveModaHome(options);
17
- const dir = join(home, '.moda');
18
- return {
19
- home,
20
- dir,
21
- legacyConfigPath: join(dir, 'config.json'),
22
- legacyAuthPath: join(dir, 'auth.json'),
23
- profileConfigPath: join(dir, 'profiles.json'),
24
- secretsDir: join(dir, 'secrets'),
25
- };
26
- }
27
- export function getModaDir(options = {}) {
28
- return getModaPaths(options).dir;
29
- }
30
- export function getLegacyConfigPath(options = {}) {
31
- return getModaPaths(options).legacyConfigPath;
32
- }
33
- function isRecord(value) {
34
- return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
35
- }
36
- function optionalString(value) {
37
- return typeof value === 'string' && value.trim() ? value : undefined;
38
- }
39
- function readJsonFile(path) {
40
- return JSON.parse(readFileSync(path, 'utf8'));
41
- }
42
- function safeReadRecord(path) {
43
- try {
44
- if (!existsSync(path))
45
- return undefined;
46
- const parsed = readJsonFile(path);
47
- return isRecord(parsed) ? parsed : undefined;
48
- }
49
- catch {
50
- return undefined;
51
- }
52
- }
53
- export function loadLegacyCliConfig(options = {}) {
54
- const parsed = safeReadRecord(getModaPaths(options).legacyConfigPath);
55
- if (!parsed)
56
- return {};
57
- return {
58
- api_key: optionalString(parsed.api_key),
59
- ingest_url: optionalString(parsed.ingest_url),
60
- tenant_id: optionalString(parsed.tenant_id),
61
- };
62
- }
63
- export function writeLegacyCliConfig(update, options = {}) {
64
- const paths = getModaPaths(options);
65
- mkdirSync(paths.dir, { recursive: true });
66
- const merged = { ...loadLegacyCliConfig(options), ...update };
67
- writeSecretJsonFile(paths.legacyConfigPath, merged);
68
- return merged;
69
- }
70
- function profileDefaults(name, environment, now) {
71
- const local = environment === 'local';
72
- const staging = environment === 'staging';
73
- return {
74
- name,
75
- environment,
76
- base_url: local
77
- ? 'http://localhost:3000'
78
- : staging
79
- ? 'https://staging.moda.dev'
80
- : PROD_BASE_URL,
81
- ingest_url: local ? 'http://localhost:8787' : DEFAULT_INGEST_URL,
82
- metadata: {
83
- created_at: now.toISOString(),
84
- source: 'default',
85
- },
86
- };
87
- }
88
- export function createDefaultProfiles(options = {}) {
89
- const now = options.now ?? new Date();
90
- return {
91
- local: profileDefaults('local', 'local', now),
92
- staging: profileDefaults('staging', 'staging', now),
93
- prod: profileDefaults('prod', 'prod', now),
94
- };
95
- }
96
- function isLoopbackHostname(hostname) {
97
- const host = hostname.toLowerCase();
98
- return (host === 'localhost' ||
99
- host === '127.0.0.1' ||
100
- host === '::1' ||
101
- host === '[::1]');
102
- }
103
- export function isLoopbackBaseUrl(url) {
104
- try {
105
- return isLoopbackHostname(new URL(url).hostname);
106
- }
107
- catch {
108
- return false;
109
- }
110
- }
111
- function defaultBaseUrlForEnvironment(environment) {
112
- if (environment === 'local')
113
- return 'http://localhost:3000';
114
- if (environment === 'staging')
115
- return 'https://staging.moda.dev';
116
- return PROD_BASE_URL;
117
- }
118
- function defaultIngestUrlForEnvironment(environment) {
119
- if (environment === 'local')
120
- return 'http://localhost:8787';
121
- return DEFAULT_INGEST_URL;
122
- }
123
- function environmentAllowsLoopback(environment) {
124
- return environment !== 'prod';
125
- }
126
- function sanitizeProfileBaseUrl(url, environment) {
127
- const normalized = normalizeModaBaseUrl(url);
128
- if (environmentAllowsLoopback(environment) || !isLoopbackBaseUrl(normalized)) {
129
- return normalized;
130
- }
131
- return PROD_BASE_URL;
132
- }
133
- function sanitizeProfileIngestUrl(url, environment) {
134
- if (!url)
135
- return url;
136
- if (environmentAllowsLoopback(environment) || !isLoopbackBaseUrl(url)) {
137
- return url;
138
- }
139
- return DEFAULT_INGEST_URL;
140
- }
141
- function enforceNonLoopbackBaseUrl(url, environment) {
142
- return sanitizeProfileBaseUrl(url, environment);
143
- }
144
- function enforceNonLoopbackIngestUrl(url, environment) {
145
- return sanitizeProfileIngestUrl(url, environment) ?? defaultIngestUrlForEnvironment(environment);
146
- }
147
- function activeProfileEnvironment(options) {
148
- try {
149
- return getActiveProfile(options).environment;
150
- }
151
- catch {
152
- return 'prod';
153
- }
154
- }
155
- function normalizeEnvironment(value) {
156
- if (value === 'local' ||
157
- value === 'dev' ||
158
- value === 'staging' ||
159
- value === 'prod') {
160
- return value;
161
- }
162
- return 'prod';
163
- }
164
- function isSecretReference(value) {
165
- return (value.startsWith('env:') ||
166
- value.startsWith('file:') ||
167
- value === 'legacy:config.api_key' ||
168
- value === 'legacy:auth.access_token');
169
- }
170
- function normalizeSecretReference(value) {
171
- if (typeof value !== 'string')
172
- return undefined;
173
- return isSecretReference(value) ? value : undefined;
174
- }
175
- export function assertValidProfileName(name) {
176
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name)) {
177
- throw new Error(`Invalid profile name '${name}'. Use letters, numbers, dots, dashes, or underscores.`);
178
- }
179
- }
180
- function normalizeProfile(fallbackName, value, options) {
181
- if (!isRecord(value))
182
- return undefined;
183
- const name = optionalString(value.name) ?? fallbackName;
184
- try {
185
- assertValidProfileName(name);
186
- }
187
- catch {
188
- return undefined;
189
- }
190
- const defaults = profileDefaults(name, normalizeEnvironment(value.environment), options.now ?? new Date());
191
- return {
192
- ...defaults,
193
- base_url: sanitizeProfileBaseUrl(optionalString(value.base_url) ?? defaults.base_url, normalizeEnvironment(value.environment)),
194
- ingest_url: sanitizeProfileIngestUrl(optionalString(value.ingest_url) ?? defaults.ingest_url, normalizeEnvironment(value.environment)),
195
- tenant_id: optionalString(value.tenant_id),
196
- workspace_id: optionalString(value.workspace_id),
197
- api_key_ref: normalizeSecretReference(value.api_key_ref),
198
- auth_token_ref: normalizeSecretReference(value.auth_token_ref),
199
- metadata: isRecord(value.metadata) ? value.metadata : defaults.metadata,
200
- };
201
- }
202
- function inspectLegacyAuth(path) {
203
- const parsed = safeReadRecord(path);
204
- const expiresAt = typeof parsed?.expires_at === 'number' ? parsed.expires_at : undefined;
205
- return {
206
- path,
207
- present: Boolean(parsed),
208
- has_access_token: typeof parsed?.access_token === 'string',
209
- expires_at: expiresAt,
210
- expired: expiresAt === undefined ? undefined : Date.now() / 1000 > expiresAt - 60,
211
- };
212
- }
213
- export function inspectProfileMigrationState(options = {}) {
214
- const paths = getModaPaths(options);
215
- const legacyConfig = loadLegacyCliConfig(options);
216
- return {
217
- legacy_config: {
218
- path: paths.legacyConfigPath,
219
- present: existsSync(paths.legacyConfigPath),
220
- has_api_key: Boolean(legacyConfig.api_key),
221
- has_ingest_url: Boolean(legacyConfig.ingest_url),
222
- has_tenant_id: Boolean(legacyConfig.tenant_id),
223
- },
224
- legacy_auth: inspectLegacyAuth(paths.legacyAuthPath),
225
- };
226
- }
227
- function applyLegacyAwareness(config, options) {
228
- const legacy = loadLegacyCliConfig(options);
229
- const migration = inspectProfileMigrationState(options);
230
- const prod = config.profiles.prod;
231
- if (prod && (legacy.api_key || legacy.ingest_url || legacy.tenant_id)) {
232
- const migratedIngestUrl = legacy.ingest_url && !isLoopbackBaseUrl(legacy.ingest_url)
233
- ? legacy.ingest_url
234
- : prod.ingest_url;
235
- config.profiles.prod = {
236
- ...prod,
237
- ingest_url: migratedIngestUrl,
238
- tenant_id: legacy.tenant_id ?? prod.tenant_id,
239
- api_key_ref: legacy.api_key ? 'legacy:config.api_key' : prod.api_key_ref,
240
- auth_token_ref: migration.legacy_auth.has_access_token
241
- ? 'legacy:auth.access_token'
242
- : prod.auth_token_ref,
243
- metadata: {
244
- ...prod.metadata,
245
- source: 'migration',
246
- legacy_config_path: migration.legacy_config.path,
247
- legacy_auth_path: migration.legacy_auth.path,
248
- },
249
- };
250
- }
251
- return {
252
- ...config,
253
- migrations: migration,
254
- };
255
- }
256
- function normalizeProfileConfig(raw, options = {}) {
257
- const profiles = createDefaultProfiles(options);
258
- let activeProfile = DEFAULT_PROFILE_NAME;
259
- if (isRecord(raw)) {
260
- if (isRecord(raw.profiles)) {
261
- for (const [name, profile] of Object.entries(raw.profiles)) {
262
- const normalized = normalizeProfile(name, profile, options);
263
- if (normalized)
264
- profiles[normalized.name] = normalized;
265
- }
266
- }
267
- const configuredActive = optionalString(raw.active_profile);
268
- if (configuredActive && profiles[configuredActive]) {
269
- activeProfile = configuredActive;
270
- }
271
- }
272
- return applyLegacyAwareness({
273
- version: PROFILE_CONFIG_VERSION,
274
- active_profile: activeProfile,
275
- profiles,
276
- migrations: inspectProfileMigrationState(options),
277
- }, options);
278
- }
279
- export function loadProfileConfig(options = {}) {
280
- const path = getModaPaths(options).profileConfigPath;
281
- const parsed = safeReadRecord(path);
282
- return normalizeProfileConfig(parsed, options);
283
- }
284
- export function saveProfileConfig(config, options = {}) {
285
- const active = config.active_profile || DEFAULT_PROFILE_NAME;
286
- if (!config.profiles[active]) {
287
- throw new Error(`Cannot activate missing profile '${active}'.`);
288
- }
289
- const path = getModaPaths(options).profileConfigPath;
290
- writeSecretJsonFile(path, {
291
- version: PROFILE_CONFIG_VERSION,
292
- active_profile: active,
293
- profiles: config.profiles,
294
- migrations: inspectProfileMigrationState(options),
295
- });
296
- }
297
- export function getActiveProfile(options = {}) {
298
- const config = loadProfileConfig(options);
299
- const envProfile = optionalString(getEnv(options).MODA_PROFILE);
300
- const requested = options.profile || envProfile || config.active_profile;
301
- const profile = config.profiles[requested];
302
- if (!profile) {
303
- throw new Error(`Moda profile '${requested}' does not exist.`);
304
- }
305
- return profile;
306
- }
307
- export function setActiveProfile(name, options = {}) {
308
- assertValidProfileName(name);
309
- const config = loadProfileConfig(options);
310
- if (!config.profiles[name]) {
311
- throw new Error(`Moda profile '${name}' does not exist.`);
312
- }
313
- const next = { ...config, active_profile: name };
314
- saveProfileConfig(next, options);
315
- return next;
316
- }
317
- export function upsertProfile(profile, options = {}) {
318
- assertValidProfileName(profile.name);
319
- const config = loadProfileConfig(options);
320
- const next = {
321
- ...config,
322
- profiles: {
323
- ...config.profiles,
324
- [profile.name]: {
325
- ...profile,
326
- metadata: {
327
- ...profile.metadata,
328
- updated_at: (options.now ?? new Date()).toISOString(),
329
- },
330
- },
331
- },
332
- };
333
- saveProfileConfig(next, options);
334
- return next;
335
- }
336
- export function syncLegacyConfigToProfile(legacy, options = {}) {
337
- const active = getActiveProfile(options);
338
- return upsertProfile({
339
- ...active,
340
- ingest_url: legacy.ingest_url ?? active.ingest_url,
341
- tenant_id: legacy.tenant_id ?? active.tenant_id,
342
- api_key_ref: legacy.api_key ? 'legacy:config.api_key' : active.api_key_ref,
343
- metadata: {
344
- ...active.metadata,
345
- source: 'migration',
346
- legacy_config_path: getModaPaths(options).legacyConfigPath,
347
- },
348
- }, options);
349
- }
350
- /**
351
- * Forget the stored data-plane credentials (`moda_sk_` API key + tenant
352
- * binding written by `moda init`): the legacy config's api_key/tenant_id and
353
- * the target profile's api_key_ref/tenant_id, deleting `file:` key secrets
354
- * under ~/.moda. Called by `moda auth logout` — without this, analytics
355
- * commands keep authenticating as the old tenant after a re-login, because
356
- * they use the x-api-key plane, not the session token. MODA_API_KEY in the
357
- * environment still wins at resolve time.
358
- *
359
- * The legacy config must be cleared BEFORE reading the profile config:
360
- * applyLegacyAwareness re-derives api_key_ref onto the prod profile on every
361
- * load while config.json still holds an api_key.
362
- */
363
- export function clearStoredApiKeyCredentials(options = {}) {
364
- let cleared = false;
365
- const paths = getModaPaths(options);
366
- const legacy = loadLegacyCliConfig(options);
367
- if (legacy.api_key || legacy.tenant_id) {
368
- writeSecretJsonFile(paths.legacyConfigPath, legacy.ingest_url ? { ingest_url: legacy.ingest_url } : {});
369
- cleared = true;
370
- }
371
- const config = loadProfileConfig(options);
372
- const profileName = options.profile ||
373
- optionalString(getEnv(options).MODA_PROFILE) ||
374
- config.active_profile;
375
- const profile = config.profiles[profileName];
376
- if (profile && (profile.api_key_ref || profile.tenant_id)) {
377
- if (profile.api_key_ref?.startsWith('file:')) {
378
- const secretPath = resolveFileSecretPath(profile.api_key_ref, options);
379
- if (secretPath)
380
- rmSync(secretPath, { force: true });
381
- }
382
- const { api_key_ref: _key, tenant_id: _tenant, ...rest } = profile;
383
- saveProfileConfig({ ...config, profiles: { ...config.profiles, [profileName]: rest } }, options);
384
- cleared = true;
385
- }
386
- return cleared;
387
- }
388
- function resolveFileSecretPath(reference, options) {
389
- if (!reference.startsWith('file:'))
390
- return undefined;
391
- const rawPath = reference.slice('file:'.length);
392
- if (!rawPath)
393
- return undefined;
394
- const paths = getModaPaths(options);
395
- const secretPath = isAbsolute(rawPath)
396
- ? normalize(rawPath)
397
- : normalize(join(paths.dir, rawPath));
398
- const rel = relative(paths.dir, secretPath);
399
- if (rel.startsWith('..') || isAbsolute(rel)) {
400
- throw new Error(`Secret reference escapes the Moda directory: ${reference}`);
401
- }
402
- return secretPath;
403
- }
404
- export function secretFilePathFromRef(reference, options = {}) {
405
- return resolveFileSecretPath(reference, options);
406
- }
407
- export function resolveSecretReference(reference, options = {}) {
408
- if (!reference)
409
- return undefined;
410
- const env = getEnv(options);
411
- if (reference.startsWith('env:')) {
412
- return env[reference.slice('env:'.length)] || undefined;
413
- }
414
- if (reference === 'legacy:config.api_key') {
415
- return loadLegacyCliConfig(options).api_key;
416
- }
417
- if (reference === 'legacy:auth.access_token') {
418
- const parsed = safeReadRecord(getModaPaths(options).legacyAuthPath);
419
- return optionalString(parsed?.access_token);
420
- }
421
- const filePath = resolveFileSecretPath(reference, options);
422
- if (!filePath)
423
- return undefined;
424
- const parsed = safeReadRecord(filePath);
425
- if (parsed) {
426
- return (optionalString(parsed.value) ??
427
- optionalString(parsed.api_key) ??
428
- optionalString(parsed.access_token));
429
- }
430
- try {
431
- const raw = readFileSync(filePath, 'utf8').trim();
432
- return raw || undefined;
433
- }
434
- catch {
435
- return undefined;
436
- }
437
- }
438
- export function resolveApiKeyFromActiveProfile(options = {}) {
439
- try {
440
- return resolveSecretReference(getActiveProfile(options).api_key_ref, options);
441
- }
442
- catch {
443
- return undefined;
444
- }
445
- }
446
- /**
447
- * Vercel serves moda.dev as canonical and 308-redirects www to the apex. Node
448
- * fetch strips Authorization on cross-host redirects, which breaks moda init
449
- * step 3 when the base URL still points at www.
450
- */
451
- export function normalizeModaBaseUrl(url) {
452
- try {
453
- const parsed = new URL(url);
454
- if (parsed.hostname === 'www.moda.dev') {
455
- parsed.hostname = 'moda.dev';
456
- return parsed.toString().replace(/\/$/, '');
457
- }
458
- }
459
- catch {
460
- // Not a valid URL; return unchanged and let the caller surface the error.
461
- }
462
- return url.replace(/\/$/, '');
463
- }
464
- export function resolveBaseUrl(options = {}) {
465
- const environment = activeProfileEnvironment(options);
466
- const envBase = optionalString(getEnv(options).MODA_BASE_URL);
467
- if (envBase) {
468
- return enforceNonLoopbackBaseUrl(normalizeModaBaseUrl(envBase), environment);
469
- }
470
- try {
471
- return enforceNonLoopbackBaseUrl(normalizeModaBaseUrl(getActiveProfile(options).base_url || PROD_BASE_URL), environment);
472
- }
473
- catch {
474
- return PROD_BASE_URL;
475
- }
476
- }
477
- export function resolveIngestUrl(options = {}) {
478
- const environment = activeProfileEnvironment(options);
479
- const envIngest = optionalString(getEnv(options).MODA_INGEST_URL);
480
- if (envIngest) {
481
- return enforceNonLoopbackIngestUrl(envIngest, environment);
482
- }
483
- try {
484
- const profileIngest = getActiveProfile(options).ingest_url;
485
- if (profileIngest) {
486
- return enforceNonLoopbackIngestUrl(profileIngest, environment);
487
- }
488
- }
489
- catch {
490
- // Fall through to legacy/default below.
491
- }
492
- const legacyIngest = optionalString(loadLegacyCliConfig(options).ingest_url);
493
- if (legacyIngest) {
494
- return enforceNonLoopbackIngestUrl(legacyIngest, environment);
495
- }
496
- return defaultIngestUrlForEnvironment(environment);
497
- }
498
- export function resolveIngestUrlFromActiveProfile(options = {}) {
499
- try {
500
- return getActiveProfile(options).ingest_url;
501
- }
502
- catch {
503
- return undefined;
504
- }
505
- }
506
- export function resolveTenantIdFromActiveProfile(options = {}) {
507
- try {
508
- return getActiveProfile(options).tenant_id;
509
- }
510
- catch {
511
- return undefined;
512
- }
513
- }
514
- function modeString(mode) {
515
- return `0${(mode & 0o777).toString(8).padStart(3, '0')}`;
516
- }
517
- export function ensureSecretFilePermissions(path, repair = true) {
518
- if (!existsSync(path)) {
519
- return { path, exists: false, secure: false, repaired: false };
520
- }
521
- const before = statSync(path);
522
- if (!before.isFile()) {
523
- return {
524
- path,
525
- exists: true,
526
- secure: false,
527
- repaired: false,
528
- mode: modeString(before.mode),
529
- reason: 'not_file',
530
- };
531
- }
532
- if ((before.mode & 0o077) !== 0 && repair) {
533
- try {
534
- chmodSync(path, 0o600);
535
- const after = statSync(path);
536
- return {
537
- path,
538
- exists: true,
539
- secure: (after.mode & 0o077) === 0,
540
- repaired: true,
541
- mode: modeString(after.mode),
542
- };
543
- }
544
- catch {
545
- return {
546
- path,
547
- exists: true,
548
- secure: false,
549
- repaired: false,
550
- mode: modeString(before.mode),
551
- reason: 'chmod_failed',
552
- };
553
- }
554
- }
555
- return {
556
- path,
557
- exists: true,
558
- secure: (before.mode & 0o077) === 0,
559
- repaired: false,
560
- mode: modeString(before.mode),
561
- };
562
- }
563
- export function writeSecretJsonFile(path, value) {
564
- mkdirSync(dirname(path), { recursive: true });
565
- writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
566
- try {
567
- chmodSync(path, 0o600);
568
- }
569
- catch {
570
- // Some filesystems do not support chmod; write mode covers the common case.
571
- }
572
- }
573
- //# sourceMappingURL=profiles.js.map
@@ -1,71 +0,0 @@
1
- import type { CommandContext } from './command-context.js';
2
- import type { ProfileIOOptions } from './profiles.js';
3
- export declare const DEFAULT_POLL_INTERVAL_MS = 15000;
4
- export declare const DEFAULT_WAIT_TIMEOUT_MS: number;
5
- export declare const TERMINAL_RUN_STATUSES: Set<string>;
6
- export interface PromptArmPayload {
7
- content: string;
8
- prompt_version_id?: string;
9
- prompt_id?: string;
10
- prompt_label?: string;
11
- }
12
- export interface PromptAbResult {
13
- replaySetId: string;
14
- runId: string;
15
- status: string;
16
- verdict: string;
17
- run: Record<string, unknown> | null;
18
- cases: Array<Record<string, unknown>>;
19
- }
20
- export declare function runPromptAb(flags: Record<string, string>, profileOptions: ProfileIOOptions, context: CommandContext): Promise<void>;
21
- export interface ComparisonPayload {
22
- run: {
23
- runId: string;
24
- status: string;
25
- caseCount: number;
26
- prodPassCount: number;
27
- proposedPassCount: number;
28
- prodPassRate: number;
29
- proposedPassRate: number;
30
- notes: string;
31
- } | null;
32
- cases: Array<{
33
- caseId: string;
34
- prodPass: boolean | null;
35
- proposedPass: boolean | null;
36
- prodScore: number | null;
37
- proposedScore: number | null;
38
- }>;
39
- }
40
- export declare function resolvePromptArmSpec(source: string, label: string, opts?: {
41
- preferKey?: boolean;
42
- }): PromptArmPayload;
43
- export declare function fetchLatestRun(tenantId: string, replaySetId: string, runId: string, profileOptions: ProfileIOOptions): Promise<ComparisonPayload>;
44
- /**
45
- * Strict outcome of a completed comparison for gating: 'candidate' only when the
46
- * proposed arm passed strictly more cases than prod on a completed run. A tie,
47
- * regression, skip, error, or missing run is never a candidate win.
48
- */
49
- export declare function candidateOutcome(payload: ComparisonPayload): 'candidate' | 'baseline' | 'tie' | 'no-run';
50
- export declare function formatVerdict(payload: ComparisonPayload): string;
51
- /**
52
- * Enqueue a prod-vs-proposed replay comparison and poll `/runs/latest` (pinned to
53
- * OUR run id) until it reaches a terminal status or the deadline. Shared by
54
- * `prompts ab` and the `prompts propose --gate` auto-gate. Returns the terminal
55
- * comparison payload; throws on enqueue/poll timeout.
56
- */
57
- export declare function enqueueAndPollComparison(args: {
58
- tenantId: string;
59
- replaySetId: string;
60
- promptArms: {
61
- prod: PromptArmPayload;
62
- proposed: PromptArmPayload;
63
- };
64
- seedsPerCase?: number;
65
- assistantModel?: string;
66
- promotePrimary?: boolean;
67
- timeoutMs?: number;
68
- pollIntervalMs?: number;
69
- onWaiting?: (status: string) => void;
70
- }, profileOptions: ProfileIOOptions): Promise<ComparisonPayload>;
71
- //# sourceMappingURL=prompts-ab.d.ts.map