@moda-ai/cli 1.55.42 → 1.55.44
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/README.md +1 -1
- package/dist/{agent-86hghww1.js → agent-5ax7fqhe.js} +9 -8
- package/dist/{cli-daqkdnm1.js → cli-4pc0a18x.js} +1 -1
- package/dist/{cli-1z1sx8dg.js → cli-5aa48ry5.js} +5 -4
- package/dist/{cli-xddtv3yn.js → cli-8pcq2et6.js} +2 -40
- package/dist/{cli-hjm9151g.js → cli-8pksf2x7.js} +5 -5
- package/dist/{cli-vwpbspr5.js → cli-d4cdczhe.js} +5 -5
- package/dist/{cli-0xc5htv7.js → cli-dmm9jm9m.js} +1 -1
- package/dist/cli-e0kbye4s.js +39 -0
- package/dist/{cli-t0pb7qbs.js → cli-e24fjyrb.js} +118 -10
- package/dist/{cli-gt3yq1y5.js → cli-k2ka2kjb.js} +184 -2
- package/dist/{cli-pmzh2qjy.js → cli-sejqvvhv.js} +14 -6
- package/dist/{cli-srwzar82.js → cli-t4xfvrry.js} +34 -15
- package/dist/{cli-4sy4tytm.js → cli-tztzrh6b.js} +19 -5
- package/dist/{cli-he8csgsr.js → cli-wwbr5caw.js} +8 -8
- package/dist/{cli-77ekrx7d.js → cli-xcpwp0gb.js} +14 -7
- package/dist/{cli-mxfyq5nq.js → cli-xx0pjzkh.js} +4 -4
- package/dist/cli.js +82 -41
- package/dist/{doctor-c05njdv8.js → doctor-k4tdgvgv.js} +8 -7
- package/dist/{fix-aghp304m.js → fix-nk5xffz8.js} +4 -4
- package/dist/{harness-vabp33ak.js → harness-avynbq4f.js} +6 -5
- package/dist/{harness-github-actions-112503xs.js → harness-github-actions-gd6vyxb0.js} +6 -5
- package/dist/{index-dtxvvcq2.js → index-63k2a6gb.js} +12 -11
- package/dist/{production-intelligence-e5vsc6cw.js → production-intelligence-sv3qx7f4.js} +24 -7
- package/dist/{profile-commands-se9947c1.js → profile-commands-mh3kfqkn.js} +98 -20
- package/dist/{prompt-attribution-pbns1ya3.js → prompt-attribution-wvvnzw4m.js} +6 -5
- package/dist/prompt-source-mcp.js +6 -5
- package/dist/{prompts-ab-f5qrvpkt.js → prompts-ab-y7egdzg4.js} +8 -7
- package/dist/{prompts-propose-jg2j41wx.js → prompts-propose-5nw25v9a.js} +8 -7
- package/dist/{prompts-qppk0mhp.js → prompts-snnbyxmz.js} +6 -5
- package/dist/{provision-cya5rpxa.js → provision-t1dpzcjp.js} +6 -5
- package/dist/{registry-8a1940sr.js → registry-8zkxefjs.js} +3 -3
- package/dist/{skills-sh93b156.js → skills-d4215fa9.js} +6 -5
- package/dist/{sync-0pp1c8hs.js → sync-17f9yxb1.js} +8 -7
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +6 -3
- package/skills/moda-cli/references/auth.md +16 -7
- package/skills/moda-cli/references/commands.md +4 -2
- package/skills/moda-cli/references/dashboard-parity.md +3 -0
- package/skills/moda-cli/references/detections.md +20 -0
- package/skills/moda-cli/references/output-protocol.md +1 -1
- package/skills/moda-cli/references/production-intelligence.md +48 -0
- package/skills/moda-cli/references/search-and-traces.md +3 -1
- package/skills/moda-cli/references/troubleshooting.md +14 -0
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
probeApiKey,
|
|
3
3
|
redactApiKey,
|
|
4
4
|
resolveApiKeyCredential
|
|
5
|
-
} from "./cli-
|
|
5
|
+
} from "./cli-xcpwp0gb.js";
|
|
6
6
|
import {
|
|
7
7
|
authFetch,
|
|
8
8
|
clearAuthSession,
|
|
@@ -10,20 +10,29 @@ import {
|
|
|
10
10
|
loadAuthSession,
|
|
11
11
|
login,
|
|
12
12
|
revokeCliSessionToken
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-sejqvvhv.js";
|
|
14
|
+
import {
|
|
15
|
+
ApiError
|
|
16
|
+
} from "./cli-dmm9jm9m.js";
|
|
14
17
|
import {
|
|
15
18
|
DEFAULT_INGEST_URL,
|
|
16
19
|
EXIT_AUTH_REQUIRED,
|
|
17
20
|
EXIT_DEGRADED,
|
|
18
21
|
EXIT_OK,
|
|
19
22
|
PROD_BASE_URL,
|
|
23
|
+
apiForbiddenError,
|
|
24
|
+
apiForbiddenReason,
|
|
20
25
|
clearStoredApiKeyCredentials,
|
|
21
26
|
describeBaseUrlSource,
|
|
27
|
+
describeModaBaseUrl,
|
|
22
28
|
embeddedTenantIdFromApiKey,
|
|
23
29
|
ensureSecretFilePermissions,
|
|
30
|
+
errorBodyFields,
|
|
24
31
|
getActiveProfile,
|
|
25
32
|
getModaPaths,
|
|
26
33
|
loadProfileConfig,
|
|
34
|
+
recordApiRequest,
|
|
35
|
+
refusedBaseUrlMessage,
|
|
27
36
|
resolveApiKey,
|
|
28
37
|
resolveApiKeyFromActiveProfile,
|
|
29
38
|
resolveApiTenantId,
|
|
@@ -35,7 +44,7 @@ import {
|
|
|
35
44
|
setActiveProfile,
|
|
36
45
|
shellArg,
|
|
37
46
|
upsertProfile
|
|
38
|
-
} from "./cli-
|
|
47
|
+
} from "./cli-e24fjyrb.js";
|
|
39
48
|
import {
|
|
40
49
|
CliInputError
|
|
41
50
|
} from "./cli-k4tqwah4.js";
|
|
@@ -44,6 +53,30 @@ import"./cli-0v6na3yp.js";
|
|
|
44
53
|
|
|
45
54
|
// src/profile-commands.ts
|
|
46
55
|
var ONLINE_PROBE_TIMEOUT_MS = 5000;
|
|
56
|
+
function forbiddenProbeError(probe) {
|
|
57
|
+
if (probe.status !== 403)
|
|
58
|
+
return;
|
|
59
|
+
const fields = errorBodyFields(probe.body);
|
|
60
|
+
const reason = apiForbiddenReason(fields.code, fields.message);
|
|
61
|
+
if (!reason || reason === "read_only_key")
|
|
62
|
+
return;
|
|
63
|
+
return new ApiError(`API error (403): ${fields.message ?? "forbidden"}`, 403, probe.body ?? "");
|
|
64
|
+
}
|
|
65
|
+
async function probeDataApiReachable(baseUrl) {
|
|
66
|
+
try {
|
|
67
|
+
recordApiRequest();
|
|
68
|
+
const response = await fetch(`${baseUrl}/api/v1/data/whoami`, {
|
|
69
|
+
method: "GET",
|
|
70
|
+
signal: AbortSignal.timeout(ONLINE_PROBE_TIMEOUT_MS)
|
|
71
|
+
});
|
|
72
|
+
await response.body?.cancel().catch(() => {
|
|
73
|
+
return;
|
|
74
|
+
});
|
|
75
|
+
return { reachable: true, status: response.status, ok: false, error: "no API key to verify" };
|
|
76
|
+
} catch (error) {
|
|
77
|
+
return { reachable: false, ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
47
80
|
function subcommand(context) {
|
|
48
81
|
return context.positionals[0];
|
|
49
82
|
}
|
|
@@ -131,6 +164,9 @@ async function loginWithApiKey(context, candidate) {
|
|
|
131
164
|
const authOptions = { profile: context.profile, env: context.env };
|
|
132
165
|
const probe = await probeApiKey(key, authOptions);
|
|
133
166
|
if (probe.outcome === "rejected") {
|
|
167
|
+
const forbidden = forbiddenProbeError(probe);
|
|
168
|
+
if (forbidden)
|
|
169
|
+
throw forbidden;
|
|
134
170
|
throw new CliInputError(`API key rejected by the Moda API (HTTP ${probe.status}).`, 'The key is revoked, malformed, or belongs to another environment. Mint a new one with "moda provision --tenant-id=<org-id>", or check MODA_BASE_URL points at the right environment.');
|
|
135
171
|
}
|
|
136
172
|
if (probe.outcome === "unreachable") {
|
|
@@ -205,6 +241,9 @@ async function runAuthCommand(context) {
|
|
|
205
241
|
env: context.env
|
|
206
242
|
});
|
|
207
243
|
if (probe.outcome === "rejected") {
|
|
244
|
+
const forbidden = forbiddenProbeError(probe);
|
|
245
|
+
if (forbidden)
|
|
246
|
+
throw forbidden;
|
|
208
247
|
throw new CliInputError(`API key rejected by the Moda API (HTTP ${probe.status}).`, 'The key is revoked or belongs to another environment. Mint a new one with "moda provision --tenant-id=<org-id>", or run "moda auth login".');
|
|
209
248
|
}
|
|
210
249
|
context.output.writeData({
|
|
@@ -260,28 +299,67 @@ async function runAuthCommand(context) {
|
|
|
260
299
|
if (session?.user_email) {
|
|
261
300
|
data.user = { email: session.user_email };
|
|
262
301
|
}
|
|
302
|
+
const warnings = [];
|
|
263
303
|
if (boolFlag(context, "online")) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
304
|
+
const baseUrl = resolveModaBaseUrl(authOptions);
|
|
305
|
+
let sessionProbe;
|
|
306
|
+
let tenantCount;
|
|
307
|
+
if (session) {
|
|
308
|
+
sessionProbe = { reachable: false, ok: false };
|
|
309
|
+
try {
|
|
310
|
+
const response = await authFetch("/api/cli/context", { signal: AbortSignal.timeout(ONLINE_PROBE_TIMEOUT_MS) }, authOptions);
|
|
311
|
+
sessionProbe = { reachable: true, status: response.status, ok: response.ok };
|
|
312
|
+
if (response.ok) {
|
|
313
|
+
const body = await response.json();
|
|
314
|
+
if (Array.isArray(body.tenants))
|
|
315
|
+
tenantCount = body.tenants.length;
|
|
272
316
|
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
317
|
+
} catch {}
|
|
318
|
+
}
|
|
319
|
+
let dataApi;
|
|
276
320
|
if (credential && data.apiKeyCredential.present) {
|
|
277
321
|
const probe = await probeApiKey(credential.key, authOptions);
|
|
278
322
|
data.apiKeyCredential.verified = probe.outcome === "live";
|
|
279
|
-
if (probe.outcome === "
|
|
280
|
-
|
|
323
|
+
if (probe.outcome === "live") {
|
|
324
|
+
const identity = probe.identity;
|
|
325
|
+
dataApi = {
|
|
326
|
+
reachable: true,
|
|
327
|
+
status: 200,
|
|
328
|
+
ok: true,
|
|
329
|
+
...typeof identity?.tenant_id === "string" ? { tenantId: identity.tenant_id } : {},
|
|
330
|
+
...identity && "tenant_name" in identity ? { tenantName: typeof identity.tenant_name === "string" ? identity.tenant_name : null } : {},
|
|
331
|
+
...typeof identity?.key_kind === "string" ? { keyKind: identity.key_kind } : {},
|
|
332
|
+
...typeof identity?.scope === "string" ? { scope: identity.scope } : {}
|
|
333
|
+
};
|
|
334
|
+
} else if (probe.outcome === "rejected") {
|
|
335
|
+
const fields = errorBodyFields(probe.body);
|
|
336
|
+
dataApi = { reachable: true, status: probe.status, ok: false, error: fields.message ?? `HTTP ${probe.status}` };
|
|
337
|
+
const reason = apiForbiddenReason(fields.code, fields.message);
|
|
338
|
+
if (reason && reason !== "read_only_key") {
|
|
339
|
+
const described = apiForbiddenError(reason, fields.message);
|
|
340
|
+
warnings.push(`${described.message} ${described.cause}`);
|
|
341
|
+
} else {
|
|
342
|
+
data.authenticated = loggedIn;
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
dataApi = { reachable: probe.status !== undefined, ...probe.status !== undefined ? { status: probe.status } : {}, ok: false, error: probe.reason };
|
|
281
346
|
}
|
|
347
|
+
} else {
|
|
348
|
+
dataApi = await probeDataApiReachable(baseUrl);
|
|
349
|
+
}
|
|
350
|
+
data.online = {
|
|
351
|
+
baseUrl,
|
|
352
|
+
reachable: dataApi.reachable || sessionProbe?.reachable === true,
|
|
353
|
+
ok: (credential ? dataApi.ok : true) && (sessionProbe ? sessionProbe.ok : true) && Boolean(credential || sessionProbe),
|
|
354
|
+
...tenantCount !== undefined ? { tenantCount } : {},
|
|
355
|
+
...sessionProbe ? { session: sessionProbe } : {},
|
|
356
|
+
dataApi
|
|
357
|
+
};
|
|
358
|
+
if (sessionProbe?.status === 404 && dataApi.reachable) {
|
|
359
|
+
warnings.push(`${baseUrl} answers the Data API but has no /api/cli/context, so it looks like the backend. ` + "Data commands work against it; browser login and control-plane commands need the frontend URL (http://localhost:3000 locally).");
|
|
282
360
|
}
|
|
283
361
|
}
|
|
284
|
-
context.output.writeData(data);
|
|
362
|
+
context.output.writeData(data, warnings.length > 0 ? { warnings } : undefined);
|
|
285
363
|
return { exitCode: data.authenticated ? EXIT_OK : EXIT_AUTH_REQUIRED };
|
|
286
364
|
}
|
|
287
365
|
case "token": {
|
|
@@ -417,7 +495,7 @@ async function runConfigCommand(context) {
|
|
|
417
495
|
const options = { profile: profile.name, env: context.env };
|
|
418
496
|
const envProfile = context.env.MODA_PROFILE?.trim();
|
|
419
497
|
const apiKey = resolveApiKey(options);
|
|
420
|
-
const
|
|
498
|
+
const base = describeModaBaseUrl(options);
|
|
421
499
|
context.output.writeData({
|
|
422
500
|
moda_dir: getModaPaths({ env: context.env }).dir,
|
|
423
501
|
active_profile: profile.name,
|
|
@@ -427,14 +505,14 @@ async function runConfigCommand(context) {
|
|
|
427
505
|
resolved: {
|
|
428
506
|
api_key: redactSecret(apiKey),
|
|
429
507
|
api_key_source: context.env.MODA_API_KEY ? "env:MODA_API_KEY" : profile.api_key_ref === "legacy:config.api_key" && apiKey ? "legacy_config" : resolveApiKeyFromActiveProfile(options) ? "profile" : apiKey ? "legacy_config" : "none",
|
|
430
|
-
base_url:
|
|
508
|
+
base_url: base.refused ? null : base.url,
|
|
431
509
|
base_url_source: describeBaseUrlSource(options),
|
|
432
510
|
ingest_url: resolveIngestUrl(options),
|
|
433
511
|
tenant_id: resolveApiTenantId(options),
|
|
434
512
|
workspace_id: profile.workspace_id
|
|
435
513
|
},
|
|
436
514
|
migrations: config.migrations
|
|
437
|
-
});
|
|
515
|
+
}, base.refused ? { warnings: [`${refusedBaseUrlMessage(base.refused)} Data commands exit 5 until you re-run with --profile=local or unset MODA_BASE_URL.`] } : undefined);
|
|
438
516
|
return { exitCode: 0 };
|
|
439
517
|
}
|
|
440
518
|
export {
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
inventoryFiles,
|
|
3
3
|
safePromptEvidencePath
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-d4cdczhe.js";
|
|
5
5
|
import"./cli-xq45etg1.js";
|
|
6
6
|
import"./cli-97fnztpy.js";
|
|
7
7
|
import"./cli-cesds766.js";
|
|
8
8
|
import {
|
|
9
9
|
require_typescript
|
|
10
10
|
} from "./cli-qpq84eee.js";
|
|
11
|
-
import"./cli-
|
|
12
|
-
import"./cli-
|
|
13
|
-
import"./cli-
|
|
14
|
-
import"./cli-
|
|
11
|
+
import"./cli-8pcq2et6.js";
|
|
12
|
+
import"./cli-e0kbye4s.js";
|
|
13
|
+
import"./cli-tztzrh6b.js";
|
|
14
|
+
import"./cli-dmm9jm9m.js";
|
|
15
|
+
import"./cli-e24fjyrb.js";
|
|
15
16
|
import"./cli-k4tqwah4.js";
|
|
16
17
|
import"./cli-9kfxkxmk.js";
|
|
17
18
|
import"./cli-ns5fcnkz.js";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
checkPromptMapping,
|
|
4
4
|
discoverHarnessPromptSources,
|
|
5
5
|
safePromptEvidencePath
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-d4cdczhe.js";
|
|
7
7
|
import"./cli-xq45etg1.js";
|
|
8
8
|
import"./cli-97fnztpy.js";
|
|
9
9
|
import"./cli-cesds766.js";
|
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
inspectCodeSelectors,
|
|
12
12
|
inspectPythonSelectors
|
|
13
13
|
} from "./cli-qpq84eee.js";
|
|
14
|
-
import"./cli-
|
|
15
|
-
import"./cli-
|
|
16
|
-
import"./cli-
|
|
17
|
-
import"./cli-
|
|
14
|
+
import"./cli-8pcq2et6.js";
|
|
15
|
+
import"./cli-e0kbye4s.js";
|
|
16
|
+
import"./cli-tztzrh6b.js";
|
|
17
|
+
import"./cli-dmm9jm9m.js";
|
|
18
|
+
import"./cli-e24fjyrb.js";
|
|
18
19
|
import"./cli-k4tqwah4.js";
|
|
19
20
|
import"./cli-9kfxkxmk.js";
|
|
20
21
|
import"./cli-ns5fcnkz.js";
|
|
@@ -23,14 +23,15 @@ import {
|
|
|
23
23
|
runPromptAb,
|
|
24
24
|
selectProblemCases,
|
|
25
25
|
waitLabel
|
|
26
|
-
} from "./cli-
|
|
27
|
-
import"./cli-
|
|
28
|
-
import"./cli-
|
|
26
|
+
} from "./cli-8pksf2x7.js";
|
|
27
|
+
import"./cli-wwbr5caw.js";
|
|
28
|
+
import"./cli-4pc0a18x.js";
|
|
29
29
|
import"./cli-mm48br00.js";
|
|
30
|
-
import"./cli-
|
|
31
|
-
import"./cli-
|
|
32
|
-
import"./cli-
|
|
33
|
-
import"./cli-
|
|
30
|
+
import"./cli-8pcq2et6.js";
|
|
31
|
+
import"./cli-e0kbye4s.js";
|
|
32
|
+
import"./cli-tztzrh6b.js";
|
|
33
|
+
import"./cli-dmm9jm9m.js";
|
|
34
|
+
import"./cli-e24fjyrb.js";
|
|
34
35
|
import"./cli-k4tqwah4.js";
|
|
35
36
|
import"./cli-9kfxkxmk.js";
|
|
36
37
|
import"./cli-ns5fcnkz.js";
|
|
@@ -7,20 +7,21 @@ import {
|
|
|
7
7
|
formatVerdict,
|
|
8
8
|
rerunWithYes,
|
|
9
9
|
resolvePromptArmSpec
|
|
10
|
-
} from "./cli-
|
|
11
|
-
import"./cli-
|
|
12
|
-
import"./cli-
|
|
10
|
+
} from "./cli-8pksf2x7.js";
|
|
11
|
+
import"./cli-wwbr5caw.js";
|
|
12
|
+
import"./cli-4pc0a18x.js";
|
|
13
13
|
import"./cli-mm48br00.js";
|
|
14
|
-
import"./cli-
|
|
15
|
-
import"./cli-
|
|
14
|
+
import"./cli-8pcq2et6.js";
|
|
15
|
+
import"./cli-e0kbye4s.js";
|
|
16
|
+
import"./cli-tztzrh6b.js";
|
|
16
17
|
import {
|
|
17
18
|
callControlAPI
|
|
18
|
-
} from "./cli-
|
|
19
|
+
} from "./cli-dmm9jm9m.js";
|
|
19
20
|
import {
|
|
20
21
|
resolveApiTenantId,
|
|
21
22
|
shellArg,
|
|
22
23
|
validateConfig
|
|
23
|
-
} from "./cli-
|
|
24
|
+
} from "./cli-e24fjyrb.js";
|
|
24
25
|
import {
|
|
25
26
|
CliInputError
|
|
26
27
|
} from "./cli-k4tqwah4.js";
|
|
@@ -11,14 +11,15 @@ import {
|
|
|
11
11
|
runPromptSync,
|
|
12
12
|
runPromptsCommand,
|
|
13
13
|
writePromptSyncProgress
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-wwbr5caw.js";
|
|
15
15
|
import {
|
|
16
16
|
promptSourceHashPayload
|
|
17
17
|
} from "./cli-mm48br00.js";
|
|
18
|
-
import"./cli-
|
|
19
|
-
import"./cli-
|
|
20
|
-
import"./cli-
|
|
21
|
-
import"./cli-
|
|
18
|
+
import"./cli-8pcq2et6.js";
|
|
19
|
+
import"./cli-e0kbye4s.js";
|
|
20
|
+
import"./cli-tztzrh6b.js";
|
|
21
|
+
import"./cli-dmm9jm9m.js";
|
|
22
|
+
import"./cli-e24fjyrb.js";
|
|
22
23
|
import"./cli-k4tqwah4.js";
|
|
23
24
|
import"./cli-9kfxkxmk.js";
|
|
24
25
|
import"./cli-ns5fcnkz.js";
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
selectTenantAndCreateKey
|
|
3
|
-
} from "./cli-
|
|
4
|
-
import"./cli-
|
|
3
|
+
} from "./cli-5aa48ry5.js";
|
|
4
|
+
import"./cli-xcpwp0gb.js";
|
|
5
5
|
import {
|
|
6
6
|
loadAuthSession
|
|
7
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-sejqvvhv.js";
|
|
8
8
|
import {
|
|
9
9
|
stringOption
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-8pcq2et6.js";
|
|
11
|
+
import"./cli-e0kbye4s.js";
|
|
11
12
|
import {
|
|
12
13
|
resolveIngestUrl,
|
|
13
14
|
resolveModaBaseUrl
|
|
14
|
-
} from "./cli-
|
|
15
|
+
} from "./cli-e24fjyrb.js";
|
|
15
16
|
import {
|
|
16
17
|
CliAuthError
|
|
17
18
|
} from "./cli-k4tqwah4.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertFlags
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-tztzrh6b.js";
|
|
4
4
|
import {
|
|
5
5
|
callControlAPI
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-dmm9jm9m.js";
|
|
7
7
|
import {
|
|
8
8
|
explicitOutputMode,
|
|
9
9
|
resolveApiKey
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-e24fjyrb.js";
|
|
11
11
|
import {
|
|
12
12
|
CliInputError
|
|
13
13
|
} from "./cli-k4tqwah4.js";
|
|
@@ -3,14 +3,15 @@ import {
|
|
|
3
3
|
pullSkills,
|
|
4
4
|
runSkillSync,
|
|
5
5
|
runSkillsCommand
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-xx0pjzkh.js";
|
|
7
7
|
import"./cli-qm5b1vck.js";
|
|
8
8
|
import"./cli-rjav0vj1.js";
|
|
9
9
|
import"./cli-hcwdz9sd.js";
|
|
10
|
-
import"./cli-
|
|
11
|
-
import"./cli-
|
|
12
|
-
import"./cli-
|
|
13
|
-
import"./cli-
|
|
10
|
+
import"./cli-8pcq2et6.js";
|
|
11
|
+
import"./cli-e0kbye4s.js";
|
|
12
|
+
import"./cli-tztzrh6b.js";
|
|
13
|
+
import"./cli-dmm9jm9m.js";
|
|
14
|
+
import"./cli-e24fjyrb.js";
|
|
14
15
|
import"./cli-k4tqwah4.js";
|
|
15
16
|
import"./cli-ns5fcnkz.js";
|
|
16
17
|
import"./cli-0v6na3yp.js";
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runSkillsCommand
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-xx0pjzkh.js";
|
|
4
4
|
import"./cli-qm5b1vck.js";
|
|
5
5
|
import"./cli-rjav0vj1.js";
|
|
6
6
|
import"./cli-hcwdz9sd.js";
|
|
7
7
|
import {
|
|
8
8
|
runPromptsCommand
|
|
9
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-wwbr5caw.js";
|
|
10
10
|
import"./cli-mm48br00.js";
|
|
11
11
|
import {
|
|
12
12
|
runHarnessCommand
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-d4cdczhe.js";
|
|
14
14
|
import"./cli-xq45etg1.js";
|
|
15
15
|
import"./cli-97fnztpy.js";
|
|
16
16
|
import"./cli-cesds766.js";
|
|
17
17
|
import"./cli-qpq84eee.js";
|
|
18
|
-
import"./cli-
|
|
19
|
-
import"./cli-
|
|
20
|
-
import"./cli-
|
|
21
|
-
import"./cli-
|
|
18
|
+
import"./cli-8pcq2et6.js";
|
|
19
|
+
import"./cli-e0kbye4s.js";
|
|
20
|
+
import"./cli-tztzrh6b.js";
|
|
21
|
+
import"./cli-dmm9jm9m.js";
|
|
22
|
+
import"./cli-e24fjyrb.js";
|
|
22
23
|
import {
|
|
23
24
|
CliInputError
|
|
24
25
|
} from "./cli-k4tqwah4.js";
|
package/package.json
CHANGED
package/skills/moda-cli/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: moda-cli
|
|
3
|
-
version: 3.0.
|
|
3
|
+
version: 3.0.10
|
|
4
4
|
description: Investigate a production AI agent with the Moda CLI (`moda`) - find where something happened in real traces (traces search, then traces get), check agent health, rank what to fix (investigate, problems), and dig into tool failures, user frustration, and hallucinations. Also prompt registry sync/promote, replays, custom signals, and false-positive marking. Use when the user mentions Moda, production traces or conversations of their agent, agent failures, or asks what to fix.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -20,8 +20,9 @@ in `references/`; read one only when the task needs it.
|
|
|
20
20
|
only with an API key, so ready means `apiKeyCredential.verified` is `true`; a
|
|
21
21
|
browser session from `moda auth login` alone is not enough. With no key, or
|
|
22
22
|
a rejected one, ask the user to `export MODA_API_KEY=moda_sk_...` (or run
|
|
23
|
-
`moda auth login --api-key`).
|
|
24
|
-
|
|
23
|
+
`moda auth login --api-key`). `online.baseUrl` is where requests go and
|
|
24
|
+
`online.dataApi.tenantId` the tenant the key acts on. Do not run `moda init`
|
|
25
|
+
unprompted; it is an interactive wizard that writes to the repo.
|
|
25
26
|
- Not installed: `npx -y -p @moda-ai/cli moda <command>`.
|
|
26
27
|
- If a response carries `meta.update`, the installed CLI is out of date and
|
|
27
28
|
may not match this guide; tell the user how to update
|
|
@@ -60,6 +61,8 @@ Details: `references/output-protocol.md`.
|
|
|
60
61
|
| Where did X happen / traces about Y | `moda traces search "<words>"` (`--mode=keyword` for ids, error codes, exact strings; `--include-tool-io` for tool inputs/outputs) | `moda traces get <conversation_id> --msg-index=<message_index> --window=3` |
|
|
61
62
|
| Search returned nothing | Read `.warnings` (degraded mode, reason). Try a shorter phrase, `--mode=keyword`, a wider `--time-range` | `moda traces --search="<words>"` |
|
|
62
63
|
| Is the agent healthy? | `moda overview --days-back=7` | `moda investigate` |
|
|
64
|
+
| Did my change make it worse? | `moda overview --compare --since=<deploy time, ISO UTC>` (after the deploy against the same span before it; add `--agent-name=<sub-agent>` for one sub-agent) | `moda overview --series --since=<deploy time>` (per hour up to 2 days, else per day), `moda detections list --family=<f> --compare`, `moda problems get <problem_id> --trend` |
|
|
65
|
+
| Is X trending up? | `moda overview --compare --series --days-back=14` | `moda problems get <problem_id> --trend --days-back=30` (incidents per day) |
|
|
63
66
|
| What to fix first? | `moda investigate` or `moda problems list` (free; both rank the same list the dashboard shows, `data.problemsView` names it) | `moda problems get <problem_id>` (`--view=dream` on a dream-grouped tenant), then `--evidence` / `--traces` |
|
|
64
67
|
| Why is tool T failing? | `moda detections tools` | `moda detections tools <T>` (use each row's `anchor`) |
|
|
65
68
|
| How often is tool T called / what is its schema? | `moda tools list` (every tool: calls, failed calls, success rate; `--sort=failure_rate`) | `moda tools get <T>` (registered schema, observed payload fields, usage over time, top failure categories) |
|
|
@@ -85,7 +85,7 @@ printf %s "$KEY" | moda auth login --api-key-stdin # key never touches argv
|
|
|
85
85
|
moda auth login --api-key=<key> # inline; WARNS, key is visible in argv + shell history
|
|
86
86
|
moda auth logout # revoke the session server-side, then delete it locally
|
|
87
87
|
moda auth status # both credentials; exit 0 if either is usable, else 4
|
|
88
|
-
moda auth status --online #
|
|
88
|
+
moda auth status --online # also probe /api/v1/data/whoami (+ /api/cli/context with a session)
|
|
89
89
|
moda auth whoami # identity from the session, or from the API key
|
|
90
90
|
moda auth token # print the raw bearer token + newline, nothing else
|
|
91
91
|
curl -H "Authorization: Bearer $(moda auth token)" https://moda.dev/api/cli/context
|
|
@@ -156,12 +156,21 @@ two independent credentials and `auth` reports both:
|
|
|
156
156
|
a rejected key gets `verified: false`, stops counting toward `authenticated`,
|
|
157
157
|
and exits `4`; an unreachable API leaves it counting, so a blip never kills a
|
|
158
158
|
good credential. With `--online` the payload adds
|
|
159
|
-
`{ "online": { "
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
159
|
+
`{ "online": { "baseUrl": <url>, "reachable": <bool>, "ok": <bool>,
|
|
160
|
+
"tenantCount"?: <n>, "session"?: {...}, "dataApi": { "reachable", "status",
|
|
161
|
+
"ok", "tenantId", "tenantName", "keyKind", "scope", "error"? } } }`.
|
|
162
|
+
`baseUrl` is where every request goes. `dataApi` probes
|
|
163
|
+
`GET /api/v1/data/whoami`, which the frontend proxy and the backend both
|
|
164
|
+
serve, so it confirms a base URL pointed straight at the backend and names
|
|
165
|
+
the key's tenant; without a key it is an unauthenticated reachability check
|
|
166
|
+
(a live Data API answers 401). `session` (only with a browser session)
|
|
167
|
+
probes `GET /api/cli/context`, a frontend-only route: a 404 there with a
|
|
168
|
+
reachable `dataApi` means the URL is the backend, and a warning says so.
|
|
169
|
+
`reachable: true` means any HTTP response arrived (even a 401/500); `ok`
|
|
170
|
+
means every credential present was accepted. A network failure or probe
|
|
171
|
+
timeout (5 s) yields `reachable: false`. A 403 for an unknown or
|
|
172
|
+
inaccessible tenant (`MODA_TENANT_ID`) keeps the key counted and adds a
|
|
173
|
+
warning with the tenant fix.
|
|
165
174
|
- `moda auth token` writes exactly the bare token plus a trailing newline to
|
|
166
175
|
stdout in **every** output mode — including piped/json/agent, because
|
|
167
176
|
command substitution is a pipe — so `$(moda auth token)` composes safely
|
|
@@ -15,7 +15,7 @@ Loaded on demand from the moda-cli skill. This table can lag the CLI: `moda <com
|
|
|
15
15
|
| `moda auth status` | Both credentials; exit `0` if either is usable, `4` if neither (expected, not an error) |
|
|
16
16
|
| `moda auth token` | Print the raw bearer token for `$(moda auth token)` composition |
|
|
17
17
|
| `moda traces search "<query>"` | **Message-grain semantic/keyword/hybrid search (incl. tool calls); primary content-discovery path** |
|
|
18
|
-
| `moda overview` | Dashboard KPIs, top clusters, recent activity |
|
|
18
|
+
| `moda overview` | Dashboard KPIs, top clusters, recent activity. `--compare` adds `trends`: overview KPIs, per-unit rates, Problems and each detector family against the previous window of equal length; `--series` adds `[bucket, value]` arrays; `--since=<ISO> [--until=<ISO>]` sets the window (before/after a deploy); `--agent-name=<sub-agent>`; `--unit=conversation\|segment\|turn` |
|
|
19
19
|
| `moda ask "<question>"` | Natural-language production/harness answer with evidence (exit `3` = degraded local fallback) |
|
|
20
20
|
| `moda investigate` | Rank production issues (tool failures, frustrations, top Problems) with evidence + next commands |
|
|
21
21
|
| `moda clusters` | Browse topic cluster hierarchy; `--search="q"` finds clusters by meaning, `--node-id=ID` resolves a deep link |
|
|
@@ -25,7 +25,7 @@ Loaded on demand from the moda-cli skill. This table can lag the CLI: `moda <com
|
|
|
25
25
|
| `moda detections list --family=frustration` | User frustration detections with evidence (the frustration family of `emotions`, legacy field names) |
|
|
26
26
|
| `moda detections list --family=emotion` | Multi-family emotion detections: frustration, disapproval, sadness, confusion, anxiety, trust, positive (`--family=F`, `--user-id`, `--conversation-id`, limit 1–20) |
|
|
27
27
|
| `moda detections list --family=hallucination` | Grounding detections: contradicted/verified outputs, rule breakdown (`--kind=contradicted\|verified`, `--conversation-id=ID`) |
|
|
28
|
-
| `moda detections tools` | Tool failure overview |
|
|
28
|
+
| `moda detections tools` | Tool failure overview (`--compare` adds `summary.compare`) |
|
|
29
29
|
| `moda detections tools <tool_name>` | Per-tool failure breakdown + examples |
|
|
30
30
|
| `moda tools list` | Tool catalog: every tool called in the window with calls, failed calls, success rate, traces, effect direction, last seen (`--sort=calls\|failure_rate\|failures\|traces\|last_seen`, `--search`, `--direction`, `--limit` 1-200, `--offset`) |
|
|
31
31
|
| `moda tools get <tool_name>` | One tool: call stats, registered description and schema, observed payload fields, usage over time, top failure categories with the newest failure (`--days-back`, `--full`) |
|
|
@@ -35,6 +35,8 @@ Loaded on demand from the moda-cli skill. This table can lag the CLI: `moda <com
|
|
|
35
35
|
| `moda review <kind> <target>` | Write: kinds `problem\|problem-detection\|quote\|tool\|laziness\|hallucination\|custom-signal`; `--reason` optional; problem-detection needs `--conversation-id` + `--attribution-id`; quote needs `--family`, `--turn`, `--quote`; tool needs `--tool` plus `--tool-use-id` or `--msg-index`; custom-signal needs `--conversation-id`; `--verdict=clear` removes a mark (not for problem) |
|
|
36
36
|
| `moda reviews list [conversation_id]` | Current false-positive marks: all (`--kind=quote\|tool\|laziness\|hallucination\|custom-signal`, `--all` to include removed, `--limit`) or one trace with its problem detection reviews. Problem dismissals are read with `moda problems get <problem_id> --feedback`, detection reviews with `moda reviews list <conversation_id> --kind=problem-detection` |
|
|
37
37
|
| `moda detections list --family=laziness` | Laziness detections per trace (`--days-back`, `--limit` 1-50, `--conversation-id`, `--family`) |
|
|
38
|
+
| `moda detections list --family=<f> --compare` | Adds `summary.compare`: the family's dashboard KPI tiles and per-trace rate for the list's `--days-back` window against the previous window of equal length (whole family; not with `--conversation-id`/`--user-id`) |
|
|
39
|
+
| `moda problems get <problem_id> --trend` | Incidents and users per day (`[date, value]`) and incidents/signals against the previous window of equal length (`--days-back`, default 30; no comparison above 45) |
|
|
38
40
|
| `moda detections list --family=<f> --group-by=trace` | The dashboard's per-trace list for emotion, frustration and hallucination: totals count traces. Dashboard filters imply it: emotion/frustration `--search --status --elicitor --trajectory --signal --sort --order`; hallucination `--search --status --rule --source --sort --order`; laziness (always per trace) `--search --status --sort --order`. See detections.md 5a |
|
|
39
41
|
| `moda problems linear <problem_id>` | The Problem's linked Linear issue (key, state, URL) or `link: null`; needs Linear connected (Integrations → Linear) |
|
|
40
42
|
| `moda problems link <problem_id>` | Write: file the Problem as a Linear issue (`--team-id`, `--title`, `--priority=0-4`), or link an existing one with `--issue=ENG-123\|URL` (exclusive with the others). Idempotent per Problem |
|
|
@@ -14,6 +14,9 @@ Writes listed here accept `--dry-run`: it validates the call, prints the exact r
|
|
|
14
14
|
| Traces list: Processed, Use case confidence, Indexed columns | `moda traces` rows: `processing_status`, `error_message`, `membership_score`, `indexed_at`; `moda traces get <id>`: `trace_state` |
|
|
15
15
|
| Signals tabs: per-trace list, filters, sort | `moda detections list --family=<f> --group-by=trace` plus `--trajectory --elicitor --signal --status --search --sort --order` (emotion), `--rule --source` (hallucination) |
|
|
16
16
|
| Settings: which detectors run | `moda overview` → `detectors` (read-only; switching one is dashboard-only) |
|
|
17
|
+
| Home / Analytics / Signals KPI cards: change against the previous period, sparklines, agent selector | `moda overview --compare [--series] [--since=<ISO> --until=<ISO>] [--agent-name=<sub-agent>] [--unit=turn]` |
|
|
18
|
+
| Detector tab KPI tiles with deltas | `moda detections list --family=<f> --compare` (`summary.compare`) |
|
|
19
|
+
| Problem history per day | `moda problems get <problem_id> --trend [--days-back=N]` |
|
|
17
20
|
| Users page / user profile | `moda users [--window --cohort --sort --order --health-status]`, `moda user <id>` (`--cohort` takes `builtin:<key>`, a saved cohort id, or a bare built-in key) |
|
|
18
21
|
| Cohort selector on Problems, Traces, Signals tabs, custom signals, Topics | `--cohort=builtin:<key>\|<saved id>` on `problems list`, `traces list`, `detections list` (emotion, frustration and hallucination read the per-trace list; laziness; custom_signal `--signal-id`), `detections tools`, `clusters` (`--time-range=all\|3d\|7d\|30d`, also with `--search`). Output says which cohort scoped it (summary line and `cohort` in the data). `traces search` has no cohort scope |
|
|
19
22
|
| Cohort builder: list, fields, preview, compare, save, edit | `moda cohorts list [--include-archived]`, `get <ref>`, `fields`, `preview --file=<definition.json>`, `compare <a> <b> [--days=7\|30\|90]`, `create --name --file [--dry-run]`, `update <cohort_id> [--name --file --note] [--dry-run]` (archiving is dashboard-only) |
|
|
@@ -126,6 +126,26 @@ An empty detection list is not proof a trace is clean:
|
|
|
126
126
|
pending or errored traces: a pending trace may already have some detections,
|
|
127
127
|
but its detections may be incomplete.
|
|
128
128
|
|
|
129
|
+
### 5c. Is a family getting worse? (`--compare`)
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
moda detections list --family=frustration --compare --days-back=7
|
|
133
|
+
moda detections list --family=laziness:stub_body --compare
|
|
134
|
+
moda detections tools --compare --days-back=14
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`--compare` adds `summary.compare`, one more read (GET `/v1/data/trends`)
|
|
138
|
+
with the dashboard's KPI tiles for the family: `kpis` (for example emotion
|
|
139
|
+
analyzed traces, negative rate, negative detections, affected users and traces
|
|
140
|
+
per family) and `rate` (share of traces with a detection, the Signals rate
|
|
141
|
+
card; left out for `frustration` and a sub-family, since the card counts the
|
|
142
|
+
whole detector, and `notes` says so). Each figure is `{current, previous, delta, delta_pct, spike?}` for the
|
|
143
|
+
list's `--days-back` window against the window of equal length just before it.
|
|
144
|
+
It covers the whole family, so it rejects `--conversation-id` and `--user-id`;
|
|
145
|
+
other list filters do not narrow it. For a before/after-deploy window, or a
|
|
146
|
+
series per hour or day, use `moda overview --compare --since=<deploy time>`
|
|
147
|
+
and `--series` (see production-intelligence.md).
|
|
148
|
+
|
|
129
149
|
### 6. Tool failure investigation
|
|
130
150
|
|
|
131
151
|
```bash
|
|
@@ -42,7 +42,7 @@ Branch on the exit code; it is stable and cheaper than parsing the payload:
|
|
|
42
42
|
| `1` | error — bad input, unknown id (`not_found`), access denied, API/network failure | Read `errors[0].code`; retry only `network_error` / 5xx `api_error`. For `not_found`, find a valid id first (the `cause` names the command). |
|
|
43
43
|
| `3` | degraded — Moda Cloud was unavailable and the answer was synthesized from local Data API evidence (`ask`), or some sources failed to load (`investigate`/`failures`, named in `data.failedSources`) | Answer is present but lower-trust or partial; verify before acting, and never read a partial result as "no issues". |
|
|
44
44
|
| `4` | auth required — no API key, or the API rejected it (HTTP 401) | Set `MODA_API_KEY` or run the `login_command` from the `auth` payload (`moda auth login --api-key`), then re-run. |
|
|
45
|
-
| `5` | input required — a choice is needed that the non-interactive run could not answer | Pick a choice from the `input_request` payload and re-invoke the same command with that choice's `resume_flags` appended. |
|
|
45
|
+
| `5` | input required — a choice is needed that the non-interactive run could not answer (for example a tenant, or `--profile=local` for a loopback `MODA_BASE_URL` on a prod profile) | Pick a choice from the `input_request` payload and re-invoke the same command with that choice's `resume_flags` appended. |
|
|
46
46
|
| `130` | cancelled | Treat as interrupted or closed pipe; do not retry blindly. |
|
|
47
47
|
|
|
48
48
|
Exit codes `4` and `5` are recoverable-by-caller failures: the envelope's
|