@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.
- package/dist/cli-c4se7a5d.js +762 -0
- package/dist/cli-mnqa4rp6.js +1563 -0
- package/dist/cli-stmjdgvy.js +2842 -0
- package/dist/cli-y55bgg14.js +6980 -0
- package/dist/cli-ysegdrm5.js +286 -0
- package/dist/cli.js +4779 -1159
- package/dist/harness-github-actions-7t3cncnt.js +95 -0
- package/dist/index-4ser01zr.js +2769 -0
- package/dist/provision-hm1ppwj6.js +74 -0
- package/package.json +3 -2
- package/skills/integration/index.json +2 -2
- package/dist/agent-schemas.d.ts +0 -3
- package/dist/agent-schemas.js +0 -358
- package/dist/agent.d.ts +0 -5
- package/dist/agent.js +0 -477
- package/dist/analyst-tui.d.ts +0 -81
- package/dist/analyst-tui.js +0 -314
- package/dist/api-client.d.ts +0 -46
- package/dist/api-client.js +0 -120
- package/dist/ask-stream.d.ts +0 -146
- package/dist/ask-stream.js +0 -473
- package/dist/auth-storage.d.ts +0 -35
- package/dist/auth-storage.js +0 -159
- package/dist/auth.d.ts +0 -50
- package/dist/auth.js +0 -694
- package/dist/cli-session.d.ts +0 -26
- package/dist/cli-session.js +0 -169
- package/dist/cli.d.ts +0 -44
- package/dist/command-context.d.ts +0 -62
- package/dist/command-context.js +0 -57
- package/dist/command-registry.d.ts +0 -22
- package/dist/command-registry.js +0 -15
- package/dist/config.d.ts +0 -60
- package/dist/config.js +0 -110
- package/dist/doctor.d.ts +0 -79
- package/dist/doctor.js +0 -1182
- package/dist/errors.d.ts +0 -50
- package/dist/errors.js +0 -64
- package/dist/exit-funnel.d.ts +0 -14
- package/dist/exit-funnel.js +0 -53
- package/dist/harness-artifact-bodies.d.ts +0 -28
- package/dist/harness-artifact-bodies.js +0 -276
- package/dist/harness-candidates.d.ts +0 -77
- package/dist/harness-candidates.js +0 -482
- package/dist/harness-remote.d.ts +0 -135
- package/dist/harness-remote.js +0 -540
- package/dist/harness-report.d.ts +0 -145
- package/dist/harness-report.js +0 -1579
- package/dist/harness-sync-limits.d.ts +0 -8
- package/dist/harness-sync-limits.js +0 -21
- package/dist/harness.d.ts +0 -463
- package/dist/harness.js +0 -4009
- package/dist/init/index.d.ts +0 -78
- package/dist/init/index.js +0 -1442
- package/dist/init/mcp.d.ts +0 -2
- package/dist/init/mcp.js +0 -27
- package/dist/init/progress-board.d.ts +0 -80
- package/dist/init/progress-board.js +0 -225
- package/dist/init/rules.d.ts +0 -6
- package/dist/init/rules.js +0 -171
- package/dist/init/sdk-catalog.d.ts +0 -10
- package/dist/init/sdk-catalog.js +0 -80
- package/dist/init/sdk-guide.d.ts +0 -19
- package/dist/init/sdk-guide.js +0 -185
- package/dist/init/sdk-integration.d.ts +0 -53
- package/dist/init/sdk-integration.js +0 -835
- package/dist/init/sdk-protocol.d.ts +0 -135
- package/dist/init/sdk-protocol.js +0 -152
- package/dist/init/sdk-verification.d.ts +0 -27
- package/dist/init/sdk-verification.js +0 -123
- package/dist/init/tenant.d.ts +0 -67
- package/dist/init/tenant.js +0 -421
- package/dist/intelligence-schema.d.ts +0 -349
- package/dist/intelligence-schema.js +0 -174
- package/dist/keys.d.ts +0 -47
- package/dist/keys.js +0 -136
- package/dist/manifest.d.ts +0 -5
- package/dist/manifest.js +0 -114
- package/dist/markdown-terminal.d.ts +0 -49
- package/dist/markdown-terminal.js +0 -212
- package/dist/output.d.ts +0 -191
- package/dist/output.js +0 -624
- package/dist/production-intelligence.d.ts +0 -175
- package/dist/production-intelligence.js +0 -1430
- package/dist/profile-commands.d.ts +0 -10
- package/dist/profile-commands.js +0 -342
- package/dist/profiles.d.ts +0 -112
- package/dist/profiles.js +0 -573
- package/dist/prompts-ab.d.ts +0 -71
- package/dist/prompts-ab.js +0 -313
- package/dist/prompts-propose.d.ts +0 -15
- package/dist/prompts-propose.js +0 -212
- package/dist/prompts.d.ts +0 -58
- package/dist/prompts.js +0 -418
- package/dist/provision.d.ts +0 -28
- package/dist/provision.js +0 -104
- package/dist/redact.d.ts +0 -2
- package/dist/redact.js +0 -90
- package/dist/schemas.d.ts +0 -225
- package/dist/schemas.js +0 -126
- package/dist/skills-bundled.d.ts +0 -35
- package/dist/skills-bundled.js +0 -157
- package/dist/skills-manifest.d.ts +0 -33
- package/dist/skills-manifest.js +0 -244
- package/dist/skills.d.ts +0 -57
- package/dist/skills.js +0 -783
- package/dist/sync.d.ts +0 -5
- package/dist/sync.js +0 -36
- package/dist/telemetry.d.ts +0 -56
- package/dist/telemetry.js +0 -167
- package/dist/transforms.d.ts +0 -25
- package/dist/transforms.js +0 -102
- package/dist/tui.d.ts +0 -48
- package/dist/tui.js +0 -393
- package/dist/update-check.d.ts +0 -31
- package/dist/update-check.js +0 -141
- package/dist/workspace.d.ts +0 -12
- 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
|
package/dist/prompts-ab.d.ts
DELETED
|
@@ -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
|