@moda-ai/cli 1.22.1 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/cli-c4se7a5d.js +762 -0
  2. package/dist/cli-mnqa4rp6.js +1563 -0
  3. package/dist/cli-stmjdgvy.js +2842 -0
  4. package/dist/cli-y55bgg14.js +6980 -0
  5. package/dist/cli-ysegdrm5.js +286 -0
  6. package/dist/cli.js +4779 -1159
  7. package/dist/harness-github-actions-7t3cncnt.js +95 -0
  8. package/dist/index-4ser01zr.js +2769 -0
  9. package/dist/provision-hm1ppwj6.js +74 -0
  10. package/package.json +3 -2
  11. package/skills/integration/index.json +2 -2
  12. package/dist/agent-schemas.d.ts +0 -3
  13. package/dist/agent-schemas.js +0 -358
  14. package/dist/agent.d.ts +0 -5
  15. package/dist/agent.js +0 -477
  16. package/dist/analyst-tui.d.ts +0 -81
  17. package/dist/analyst-tui.js +0 -314
  18. package/dist/api-client.d.ts +0 -46
  19. package/dist/api-client.js +0 -120
  20. package/dist/ask-stream.d.ts +0 -146
  21. package/dist/ask-stream.js +0 -473
  22. package/dist/auth-storage.d.ts +0 -35
  23. package/dist/auth-storage.js +0 -159
  24. package/dist/auth.d.ts +0 -50
  25. package/dist/auth.js +0 -694
  26. package/dist/cli-session.d.ts +0 -26
  27. package/dist/cli-session.js +0 -169
  28. package/dist/cli.d.ts +0 -44
  29. package/dist/command-context.d.ts +0 -62
  30. package/dist/command-context.js +0 -57
  31. package/dist/command-registry.d.ts +0 -22
  32. package/dist/command-registry.js +0 -15
  33. package/dist/config.d.ts +0 -60
  34. package/dist/config.js +0 -110
  35. package/dist/doctor.d.ts +0 -79
  36. package/dist/doctor.js +0 -1182
  37. package/dist/errors.d.ts +0 -50
  38. package/dist/errors.js +0 -64
  39. package/dist/exit-funnel.d.ts +0 -14
  40. package/dist/exit-funnel.js +0 -53
  41. package/dist/harness-artifact-bodies.d.ts +0 -28
  42. package/dist/harness-artifact-bodies.js +0 -276
  43. package/dist/harness-candidates.d.ts +0 -77
  44. package/dist/harness-candidates.js +0 -482
  45. package/dist/harness-remote.d.ts +0 -135
  46. package/dist/harness-remote.js +0 -540
  47. package/dist/harness-report.d.ts +0 -145
  48. package/dist/harness-report.js +0 -1579
  49. package/dist/harness-sync-limits.d.ts +0 -8
  50. package/dist/harness-sync-limits.js +0 -21
  51. package/dist/harness.d.ts +0 -463
  52. package/dist/harness.js +0 -4009
  53. package/dist/init/index.d.ts +0 -78
  54. package/dist/init/index.js +0 -1442
  55. package/dist/init/mcp.d.ts +0 -2
  56. package/dist/init/mcp.js +0 -27
  57. package/dist/init/progress-board.d.ts +0 -80
  58. package/dist/init/progress-board.js +0 -225
  59. package/dist/init/rules.d.ts +0 -6
  60. package/dist/init/rules.js +0 -171
  61. package/dist/init/sdk-catalog.d.ts +0 -10
  62. package/dist/init/sdk-catalog.js +0 -80
  63. package/dist/init/sdk-guide.d.ts +0 -19
  64. package/dist/init/sdk-guide.js +0 -185
  65. package/dist/init/sdk-integration.d.ts +0 -53
  66. package/dist/init/sdk-integration.js +0 -835
  67. package/dist/init/sdk-protocol.d.ts +0 -135
  68. package/dist/init/sdk-protocol.js +0 -152
  69. package/dist/init/sdk-verification.d.ts +0 -27
  70. package/dist/init/sdk-verification.js +0 -123
  71. package/dist/init/tenant.d.ts +0 -67
  72. package/dist/init/tenant.js +0 -421
  73. package/dist/intelligence-schema.d.ts +0 -349
  74. package/dist/intelligence-schema.js +0 -174
  75. package/dist/keys.d.ts +0 -47
  76. package/dist/keys.js +0 -136
  77. package/dist/manifest.d.ts +0 -5
  78. package/dist/manifest.js +0 -114
  79. package/dist/markdown-terminal.d.ts +0 -49
  80. package/dist/markdown-terminal.js +0 -212
  81. package/dist/output.d.ts +0 -191
  82. package/dist/output.js +0 -624
  83. package/dist/production-intelligence.d.ts +0 -175
  84. package/dist/production-intelligence.js +0 -1430
  85. package/dist/profile-commands.d.ts +0 -10
  86. package/dist/profile-commands.js +0 -342
  87. package/dist/profiles.d.ts +0 -112
  88. package/dist/profiles.js +0 -573
  89. package/dist/prompts-ab.d.ts +0 -71
  90. package/dist/prompts-ab.js +0 -313
  91. package/dist/prompts-propose.d.ts +0 -15
  92. package/dist/prompts-propose.js +0 -212
  93. package/dist/prompts.d.ts +0 -58
  94. package/dist/prompts.js +0 -418
  95. package/dist/provision.d.ts +0 -28
  96. package/dist/provision.js +0 -104
  97. package/dist/redact.d.ts +0 -2
  98. package/dist/redact.js +0 -90
  99. package/dist/schemas.d.ts +0 -225
  100. package/dist/schemas.js +0 -126
  101. package/dist/skills-bundled.d.ts +0 -35
  102. package/dist/skills-bundled.js +0 -157
  103. package/dist/skills-manifest.d.ts +0 -33
  104. package/dist/skills-manifest.js +0 -244
  105. package/dist/skills.d.ts +0 -57
  106. package/dist/skills.js +0 -783
  107. package/dist/sync.d.ts +0 -5
  108. package/dist/sync.js +0 -36
  109. package/dist/telemetry.d.ts +0 -56
  110. package/dist/telemetry.js +0 -167
  111. package/dist/transforms.d.ts +0 -25
  112. package/dist/transforms.js +0 -102
  113. package/dist/tui.d.ts +0 -48
  114. package/dist/tui.js +0 -393
  115. package/dist/update-check.d.ts +0 -31
  116. package/dist/update-check.js +0 -141
  117. package/dist/workspace.d.ts +0 -12
  118. package/dist/workspace.js +0 -1294
package/dist/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