codiedev 0.9.78 → 0.9.80
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/assets/opencode-plugin.mjs +6 -0
- package/assets/session-key.mjs +48 -0
- package/dist/cli.js +70 -10
- package/dist/cliSession.d.ts +108 -0
- package/dist/cliSession.js +513 -0
- package/dist/codie.js +21 -1
- package/dist/commands/traces.js +57 -1
- package/dist/commands/tracesLabel.d.ts +24 -0
- package/dist/commands/tracesLabel.js +356 -0
- package/dist/configDir.d.ts +9 -0
- package/dist/configDir.js +25 -1
- package/dist/connect.js +45 -2
- package/dist/connectFlow.d.ts +21 -0
- package/dist/connectFlow.js +41 -0
- package/dist/hook.js +7 -0
- package/dist/launcher.d.ts +7 -2
- package/dist/launcher.js +36 -2
- package/dist/managedPi.js +18 -2
- package/dist/mcp.js +3 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +16 -8
- package/package.json +1 -1
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `codiedev traces overview | runs | run | score | scores | score-kinds |
|
|
3
|
+
// queue | judge | import | add-example | versions | save-version`: what the
|
|
4
|
+
// Traces portal shows and does (agents, runs, failures, scores, review
|
|
5
|
+
// queues, automatic scoring, datasets), so a coding agent can do it from
|
|
6
|
+
// plain words ("put this week's failed runs in the Week 44 queue"). Each
|
|
7
|
+
// calls a /api/cli/traces/* route that runs the portal's own code, with the
|
|
8
|
+
// same permissions.
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.parseArgs = parseArgs;
|
|
44
|
+
exports.scoreValue = scoreValue;
|
|
45
|
+
exports.runScore = runScore;
|
|
46
|
+
exports.runScores = runScores;
|
|
47
|
+
exports.runQueue = runQueue;
|
|
48
|
+
exports.runJudge = runJudge;
|
|
49
|
+
exports.runImport = runImport;
|
|
50
|
+
exports.runAddExample = runAddExample;
|
|
51
|
+
exports.runVersions = runVersions;
|
|
52
|
+
exports.runSaveVersion = runSaveVersion;
|
|
53
|
+
exports.runOverview = runOverview;
|
|
54
|
+
exports.runRuns = runRuns;
|
|
55
|
+
exports.runRun = runRun;
|
|
56
|
+
exports.runScoreKinds = runScoreKinds;
|
|
57
|
+
exports.runQueueCreate = runQueueCreate;
|
|
58
|
+
const fs = __importStar(require("fs"));
|
|
59
|
+
const path = __importStar(require("path"));
|
|
60
|
+
const shared_1 = require("./shared");
|
|
61
|
+
const BOOL_FLAGS = new Set(["--failed", "--on", "--off", "--json"]);
|
|
62
|
+
function parseArgs(args) {
|
|
63
|
+
const out = { positional: [], flags: {}, bools: new Set(), runs: [] };
|
|
64
|
+
for (let i = 0; i < args.length; i++) {
|
|
65
|
+
const a = args[i];
|
|
66
|
+
if (BOOL_FLAGS.has(a)) {
|
|
67
|
+
out.bools.add(a);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (a.startsWith("--")) {
|
|
71
|
+
const eq = a.indexOf("=");
|
|
72
|
+
const name = eq > 0 ? a.slice(0, eq) : a;
|
|
73
|
+
const value = eq > 0 ? a.slice(eq + 1) : args[++i];
|
|
74
|
+
if (value === undefined)
|
|
75
|
+
throw new Error(`${name} needs a value`);
|
|
76
|
+
if (name === "--run")
|
|
77
|
+
out.runs.push(value);
|
|
78
|
+
else
|
|
79
|
+
out.flags[name] = value;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
out.positional.push(a);
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
/** "4" → 4, "true" → true, anything else as text (a category). */
|
|
87
|
+
function scoreValue(raw) {
|
|
88
|
+
const t = raw.trim();
|
|
89
|
+
if (/^(true|false)$/i.test(t))
|
|
90
|
+
return t.toLowerCase() === "true";
|
|
91
|
+
if (t !== "" && Number.isFinite(Number(t)))
|
|
92
|
+
return Number(t);
|
|
93
|
+
return t;
|
|
94
|
+
}
|
|
95
|
+
async function call(config, tool, method, route, opts) {
|
|
96
|
+
const start = Date.now();
|
|
97
|
+
try {
|
|
98
|
+
const res = await (0, shared_1.apiRequest)(method, `/api/cli/traces/${route}`, { config, ...opts });
|
|
99
|
+
(0, shared_1.recordAgentEvent)(config, { tool, ok: true, latencyMs: Date.now() - start });
|
|
100
|
+
return res;
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
const e = err;
|
|
104
|
+
(0, shared_1.recordAgentEvent)(config, { tool, ok: false, latencyMs: Date.now() - start, error: e.message });
|
|
105
|
+
console.error(e.message);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function usage(text) {
|
|
110
|
+
console.error(`Usage: ${text}`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
async function runScore(args, config) {
|
|
114
|
+
const p = parseArgs(args);
|
|
115
|
+
const [run, name, value] = p.positional;
|
|
116
|
+
if (!run || !name || value === undefined)
|
|
117
|
+
usage("codiedev traces score <run> <score> <value> [--agent name] [--comment text] [--step n]");
|
|
118
|
+
const res = await call(config, "codiedev_traces_score", "POST", "score", {
|
|
119
|
+
body: { run, name, value: scoreValue(value), agent: p.flags["--agent"], comment: p.flags["--comment"], ...(p.flags["--step"] ? { step: Number(p.flags["--step"]) } : {}) },
|
|
120
|
+
});
|
|
121
|
+
console.log(`✓ Scored ${res.runId}: ${name} = ${value}`);
|
|
122
|
+
}
|
|
123
|
+
async function runScores(args, config) {
|
|
124
|
+
const p = parseArgs(args);
|
|
125
|
+
const agent = p.positional[0];
|
|
126
|
+
if (!agent)
|
|
127
|
+
usage("codiedev traces scores <agent> [--days n] [--json]");
|
|
128
|
+
const res = await call(config, "codiedev_traces_scores", "GET", "scores", { query: { agent, days: p.flags["--days"] ?? "7" } });
|
|
129
|
+
if (p.bools.has("--json")) {
|
|
130
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!res.summary.length)
|
|
134
|
+
console.log(`No scores on ${res.agent} in the last ${p.flags["--days"] ?? 7} days.`);
|
|
135
|
+
for (const s of res.summary) {
|
|
136
|
+
const head = s.dataType === "boolean" && s.average !== null ? `${Math.round(s.average * 100)}% true`
|
|
137
|
+
: s.dataType === "numeric" && s.average !== null ? `avg ${Math.round(s.average * 100) / 100}`
|
|
138
|
+
: s.categories.map((c) => `${c.label} ${c.count}`).join(", ");
|
|
139
|
+
const from = [s.bySource.annotation ? `${s.bySource.annotation} people` : "", s.bySource.api ? `${s.bySource.api} your app` : "", s.bySource.judge ? `${s.bySource.judge} judge` : ""].filter(Boolean).join(", ");
|
|
140
|
+
console.log(`${s.name.padEnd(24)} ${head.padEnd(28)} ${s.count} scores (${from})`);
|
|
141
|
+
}
|
|
142
|
+
if (res.kinds.length)
|
|
143
|
+
console.log(`\nScore kinds: ${res.kinds.map((k) => `${k.name} (${k.dataType === "numeric" ? `${k.min ?? ""}–${k.max ?? ""}` : k.dataType === "boolean" ? "true/false" : k.categories.join("/")})`).join(", ")}`);
|
|
144
|
+
}
|
|
145
|
+
async function runQueue(args, config) {
|
|
146
|
+
const [verb, ...rest] = args;
|
|
147
|
+
if (verb === "list" || verb === undefined) {
|
|
148
|
+
const res = await call(config, "codiedev_traces_queue_list", "GET", "queues", {});
|
|
149
|
+
const live = res.queues.filter((q) => !q.archived);
|
|
150
|
+
if (!live.length)
|
|
151
|
+
console.log("No review queues yet. Make one in Codiedev → Traces → Review.");
|
|
152
|
+
for (const q of live)
|
|
153
|
+
console.log(`${q.name.padEnd(28)} ${String(q.pending).padStart(4)} to review ${String(q.done).padStart(4)} done ${q.scoreNames.join(", ")}`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (verb === "create")
|
|
157
|
+
return runQueueCreate(rest, config);
|
|
158
|
+
if (verb !== "add")
|
|
159
|
+
usage("codiedev traces queue list | queue create <name> [--scores a,b] | queue add <queue> --agent <name> [--failed] [--days n] [--run id ...]");
|
|
160
|
+
const p = parseArgs(rest);
|
|
161
|
+
const queue = p.positional[0];
|
|
162
|
+
if (!queue || (!p.flags["--agent"] && !p.runs.length))
|
|
163
|
+
usage("codiedev traces queue add <queue> --agent <name> [--failed] [--days n] [--run id ...]");
|
|
164
|
+
const res = await call(config, "codiedev_traces_queue_add", "POST", "queue", {
|
|
165
|
+
body: { queue, agent: p.flags["--agent"], failed: p.bools.has("--failed"), ...(p.flags["--days"] ? { days: Number(p.flags["--days"]) } : {}), ...(p.runs.length ? { runs: p.runs } : {}) },
|
|
166
|
+
});
|
|
167
|
+
console.log(`✓ Added ${res.added} ${res.added === 1 ? "run" : "runs"} to ${res.queue}${res.skipped ? ` (${res.skipped} already there or not yours)` : ""}. Review them in Codiedev → Traces → Review.`);
|
|
168
|
+
}
|
|
169
|
+
function printJudge(agent, judge) {
|
|
170
|
+
if (!judge || judge === "none") {
|
|
171
|
+
console.log(`Automatic scoring isn't set up for ${agent}.`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
console.log(`${agent}: automatic scoring ${judge.enabled ? "on" : "off"}`);
|
|
175
|
+
console.log(` scores ${judge.scoreNames.join(", ")} · ${Math.round(judge.sampleRate * 100)}% of runs, up to ${judge.dailyMaxRuns}/day · ${judge.model}`);
|
|
176
|
+
if (judge.judgeKey)
|
|
177
|
+
console.log(` billed to ${judge.judgeKey.name}${judge.judgeKey.monthlyBudgetUsd !== null ? ` ($${judge.judgeKey.monthlyBudgetUsd}/mo)` : ""} · ${judge.judgedToday} judged in 24h${judge.errorsToday ? `, ${judge.errorsToday} failed` : ""}`);
|
|
178
|
+
if (judge.lastError)
|
|
179
|
+
console.log(` last problem: ${judge.lastError}`);
|
|
180
|
+
}
|
|
181
|
+
async function runJudge(args, config) {
|
|
182
|
+
const p = parseArgs(args);
|
|
183
|
+
const agent = p.positional[0];
|
|
184
|
+
const help = "codiedev traces judge <agent> [--on | --off] [--scores a,b] [--instructions text] [--model id] [--sample percent] [--daily n] [--budget usd | --key cdvm_…]";
|
|
185
|
+
if (!agent)
|
|
186
|
+
usage(help);
|
|
187
|
+
const changing = p.bools.has("--on") || p.bools.has("--off") || Object.keys(p.flags).length > 0;
|
|
188
|
+
if (!changing) {
|
|
189
|
+
const res = await call(config, "codiedev_traces_judge", "GET", "judge", { query: { agent } });
|
|
190
|
+
printJudge(res.agent, res.judge);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const res = await call(config, "codiedev_traces_judge_set", "POST", "judge", {
|
|
194
|
+
body: {
|
|
195
|
+
agent,
|
|
196
|
+
...(p.bools.has("--on") ? { enabled: true } : p.bools.has("--off") ? { enabled: false } : {}),
|
|
197
|
+
...(p.flags["--scores"] ? { scores: p.flags["--scores"].split(",").map((s) => s.trim()).filter(Boolean) } : {}),
|
|
198
|
+
...(p.flags["--instructions"] ? { instructions: p.flags["--instructions"] } : {}),
|
|
199
|
+
...(p.flags["--model"] ? { model: p.flags["--model"] } : {}),
|
|
200
|
+
...(p.flags["--sample"] ? { sampleRate: Number(p.flags["--sample"]) / 100 } : {}),
|
|
201
|
+
...(p.flags["--daily"] ? { dailyMaxRuns: Number(p.flags["--daily"]) } : {}),
|
|
202
|
+
...(p.flags["--budget"] ? { newKeyBudgetUsd: Number(p.flags["--budget"]) } : {}),
|
|
203
|
+
...(p.flags["--key"] ? { pastedKey: p.flags["--key"] } : {}),
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
printJudge(res.agent, res.judge);
|
|
207
|
+
}
|
|
208
|
+
async function runImport(args, config) {
|
|
209
|
+
const p = parseArgs(args);
|
|
210
|
+
const [agent, file] = p.positional;
|
|
211
|
+
const dataset = p.flags["--dataset"];
|
|
212
|
+
if (!agent || !file || !dataset)
|
|
213
|
+
usage("codiedev traces import <agent> <file.csv|file.jsonl> --dataset <name>");
|
|
214
|
+
let text;
|
|
215
|
+
try {
|
|
216
|
+
text = fs.readFileSync(path.resolve(process.cwd(), file), "utf8");
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
console.error(`Couldn't read ${file}: ${err.message}`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
const res = await call(config, "codiedev_traces_import", "POST", "examples", { body: { agent, dataset, text } });
|
|
223
|
+
console.log(`✓ Imported ${res.added} ${res.added === 1 ? "example" : "examples"} into ${dataset} for ${agent}.`);
|
|
224
|
+
for (const e of res.errors.slice(0, 10))
|
|
225
|
+
console.log(` skipped${e.line ? ` line ${e.line}` : ""}: ${e.error}`);
|
|
226
|
+
}
|
|
227
|
+
async function runAddExample(args, config) {
|
|
228
|
+
const p = parseArgs(args);
|
|
229
|
+
const agent = p.positional[0];
|
|
230
|
+
const dataset = p.flags["--dataset"];
|
|
231
|
+
const input = p.flags["--input"];
|
|
232
|
+
if (!agent || !dataset || !input)
|
|
233
|
+
usage("codiedev traces add-example <agent> --dataset <name> --input <text> [--expected <text>] [--name <text>]");
|
|
234
|
+
await call(config, "codiedev_traces_add_example", "POST", "examples", { body: { agent, dataset, input, expected: p.flags["--expected"], name: p.flags["--name"] } });
|
|
235
|
+
console.log(`✓ Added an example to ${dataset} for ${agent}.`);
|
|
236
|
+
}
|
|
237
|
+
async function runVersions(args, config) {
|
|
238
|
+
const dataset = parseArgs(args).positional[0];
|
|
239
|
+
if (!dataset)
|
|
240
|
+
usage("codiedev traces versions <dataset>");
|
|
241
|
+
const res = await call(config, "codiedev_traces_versions", "GET", "versions", { query: { dataset } });
|
|
242
|
+
if (!res.versions.length)
|
|
243
|
+
console.log(`${dataset} has no saved versions. Save one with: codiedev traces save-version "${dataset}"`);
|
|
244
|
+
for (const v of res.versions)
|
|
245
|
+
console.log(`v${v.version} ${String(v.cases).padStart(4)} cases ${new Date(v.createdAt).toISOString().slice(0, 10)} +${v.added} −${v.removed} ${v.changed} edited${v.note ? ` ${v.note}` : ""}`);
|
|
246
|
+
if (res.unsaved)
|
|
247
|
+
console.log(`Unsaved since v${res.versions[0].version}: +${res.unsaved.added} −${res.unsaved.removed} ${res.unsaved.changed} edited`);
|
|
248
|
+
}
|
|
249
|
+
async function runSaveVersion(args, config) {
|
|
250
|
+
const p = parseArgs(args);
|
|
251
|
+
const dataset = p.positional[0];
|
|
252
|
+
if (!dataset)
|
|
253
|
+
usage("codiedev traces save-version <dataset> [--note text]");
|
|
254
|
+
const res = await call(config, "codiedev_traces_save_version", "POST", "versions", { body: { dataset, note: p.flags["--note"] } });
|
|
255
|
+
console.log(`✓ Saved ${dataset} as v${res.version} (${res.cases} cases). Run it later with --dataset "${dataset}" --version ${res.version}.`);
|
|
256
|
+
}
|
|
257
|
+
const usd = (n) => (n === 0 ? "$0" : n < 0.01 ? "<$0.01" : `$${n.toFixed(2)}`);
|
|
258
|
+
const json = (p, value) => { if (p.bools.has("--json")) {
|
|
259
|
+
process.stdout.write(JSON.stringify(value, null, 2) + "\n");
|
|
260
|
+
return true;
|
|
261
|
+
} return false; };
|
|
262
|
+
async function runOverview(args, config) {
|
|
263
|
+
const p = parseArgs(args);
|
|
264
|
+
const res = await call(config, "codiedev_traces_overview", "GET", "overview", { query: { days: p.flags["--days"] ?? "7" } });
|
|
265
|
+
if (json(p, res))
|
|
266
|
+
return;
|
|
267
|
+
if (!res.agents.length) {
|
|
268
|
+
console.log("No agents you can see yet. Connect one in Codiedev → Traces.");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
console.log(`Agents, last ${res.range}:`);
|
|
272
|
+
for (const a of res.agents)
|
|
273
|
+
console.log(` ${a.name.padEnd(28)} ${String(a.runs).padStart(6)} runs ${String(a.failedRuns).padStart(5)} failed ${usd(a.spend7dUsd).padStart(9)} 7d spend ${a.keepsPromptsDays ? `prompts ${a.keepsPromptsDays}d` : "metadata only"}${a.spike ? " spend spike" : ""}${a.budgetBlock ? " budget block" : ""}`);
|
|
274
|
+
if (res.issues.length) {
|
|
275
|
+
console.log("\nWhat's going wrong:");
|
|
276
|
+
for (const i of res.issues)
|
|
277
|
+
console.log(` ${i.label.padEnd(18)} ${String(i.runs).padStart(5)} runs ${usd(i.wastedUsd).padStart(9)} wasted ${i.agents.join(", ")}${i.exampleRun ? ` e.g. ${i.exampleRun}` : ""}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function runRuns(args, config) {
|
|
281
|
+
const p = parseArgs(args);
|
|
282
|
+
const agent = p.positional[0];
|
|
283
|
+
if (!agent)
|
|
284
|
+
usage("codiedev traces runs <agent> [--failed] [--flag errors|truncated|tool_loop|context_overflow|budget_block] [--model id] [--verdict pass|fail] [--days n] [--limit n] [--json]");
|
|
285
|
+
const res = await call(config, "codiedev_traces_runs", "GET", "runs", {
|
|
286
|
+
query: { agent, days: p.flags["--days"] ?? "7", ...(p.bools.has("--failed") ? { failed: "1" } : {}), ...(p.flags["--flag"] ? { flag: p.flags["--flag"] } : {}), ...(p.flags["--model"] ? { model: p.flags["--model"] } : {}), ...(p.flags["--verdict"] ? { verdict: p.flags["--verdict"] } : {}), limit: p.flags["--limit"] ?? "50" },
|
|
287
|
+
});
|
|
288
|
+
if (json(p, res))
|
|
289
|
+
return;
|
|
290
|
+
console.log(`${res.total} ${res.total === 1 ? "run" : "runs"} for ${res.agent}, last ${res.range}${res.total > res.runs.length ? ` (newest ${res.runs.length})` : ""}:`);
|
|
291
|
+
for (const r of res.runs)
|
|
292
|
+
console.log(` ${r.startedAt.slice(0, 16).replace("T", " ")} ${r.status === "failed" ? "FAIL" : " ok "} ${r.runId.padEnd(24)} ${String(r.steps).padStart(3)} calls ${usd(r.costUsd).padStart(8)} ${r.model ?? ""}${r.flags.length ? ` [${r.flags.join(", ")}]` : ""}${r.verdict ? ` marked ${r.verdict}` : ""}`);
|
|
293
|
+
if (res.runs.length)
|
|
294
|
+
console.log(`\nOpen one: codiedev traces run ${res.runs[0].runId} --agent "${res.agent}"`);
|
|
295
|
+
}
|
|
296
|
+
async function runRun(args, config) {
|
|
297
|
+
const p = parseArgs(args);
|
|
298
|
+
const run = p.positional[0];
|
|
299
|
+
if (!run)
|
|
300
|
+
usage("codiedev traces run <run> [--agent name] [--json]");
|
|
301
|
+
const res = await call(config, "codiedev_traces_run", "GET", "run", { query: { run, ...(p.flags["--agent"] ? { agent: p.flags["--agent"] } : {}) } });
|
|
302
|
+
if (json(p, res))
|
|
303
|
+
return;
|
|
304
|
+
const r = res.run;
|
|
305
|
+
console.log(`${r.title} · ${r.id}`);
|
|
306
|
+
console.log(`${new Date(r.startedAt).toISOString().slice(0, 16).replace("T", " ")} · ${r.status === "failed" ? "FAILED" : "ok"} · ${Math.round(r.durationMs / 100) / 10}s · ${usd(r.costUsd)}${r.flags.length ? ` · ${r.flags.join(", ")}` : ""}`);
|
|
307
|
+
if (r.diagnosis)
|
|
308
|
+
console.log(`\nWhat went wrong: ${r.diagnosis.title}\n ${r.diagnosis.why}\n\nFix (for your coding agent):\n${r.diagnosis.fix.split("\n").map((l) => ` ${l}`).join("\n")}`);
|
|
309
|
+
if (r.annotation)
|
|
310
|
+
console.log(`\nMarked ${r.annotation.verdict} by ${r.annotation.by}${r.annotation.reason ? `: ${r.annotation.reason}` : ""}`);
|
|
311
|
+
if (res.scores.length)
|
|
312
|
+
console.log(`\nScores: ${res.scores.map((s) => `${s.name} ${s.text}${s.step ? ` (step ${s.step})` : ""} by ${s.by ?? s.source}${s.comment ? ` "${s.comment}"` : ""}`).join("; ")}`);
|
|
313
|
+
console.log("\nSteps:");
|
|
314
|
+
for (const s of r.spans.filter((x) => x.kind !== "run")) {
|
|
315
|
+
console.log(` ${s.kind === "tool" ? "tool" : "call"} ${s.name} ${Math.round(s.durationMs)}ms${s.costUsd !== undefined ? ` ${usd(s.costUsd)}` : ""}${s.finishReason ? ` finish: ${s.finishReason}` : ""}${s.statusCode && s.statusCode >= 400 ? ` HTTP ${s.statusCode}` : ""}${s.flag ? ` [${s.flag}]` : ""}${s.note ? ` ${s.note}` : ""}`);
|
|
316
|
+
if (s.input)
|
|
317
|
+
console.log(` prompt: ${s.input.replace(/\s+/g, " ").slice(0, 300)}`);
|
|
318
|
+
if (s.output)
|
|
319
|
+
console.log(` reply: ${s.output.replace(/\s+/g, " ").slice(0, 300)}`);
|
|
320
|
+
}
|
|
321
|
+
if (!r.bodiesVisible)
|
|
322
|
+
console.log("\n(Prompts and replies are shown only to the agent's maintainers.)");
|
|
323
|
+
}
|
|
324
|
+
async function runScoreKinds(args, config) {
|
|
325
|
+
const [verb, ...rest] = args;
|
|
326
|
+
if (verb === "add") {
|
|
327
|
+
const p = parseArgs(rest);
|
|
328
|
+
const name = p.positional[0];
|
|
329
|
+
const type = p.flags["--type"];
|
|
330
|
+
if (!name || !type)
|
|
331
|
+
usage("codiedev traces score-kinds add <name> --type number|categories|boolean [--min n --max n] [--categories a,b,c] [--description text]");
|
|
332
|
+
const dataType = type === "number" || type === "numeric" ? "numeric" : type === "categories" || type === "categorical" ? "categorical" : type === "boolean" || type === "bool" ? "boolean" : type;
|
|
333
|
+
await call(config, "codiedev_traces_score_kind_add", "POST", "score-kinds", {
|
|
334
|
+
body: { name, type: dataType, ...(p.flags["--min"] ? { min: Number(p.flags["--min"]) } : {}), ...(p.flags["--max"] ? { max: Number(p.flags["--max"]) } : {}), ...(p.flags["--categories"] ? { categories: p.flags["--categories"].split(",").map((c) => c.trim()) } : {}), ...(p.flags["--description"] ? { description: p.flags["--description"] } : {}) },
|
|
335
|
+
});
|
|
336
|
+
console.log(`✓ Added score kind ${name}.`);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const p = parseArgs(verb === undefined || verb === "list" ? rest : args);
|
|
340
|
+
const res = await call(config, "codiedev_traces_score_kinds", "GET", "score-kinds", {});
|
|
341
|
+
if (json(p, res))
|
|
342
|
+
return;
|
|
343
|
+
const live = res.kinds.filter((k) => !k.archived);
|
|
344
|
+
if (!live.length)
|
|
345
|
+
console.log("No score kinds yet. Add one: codiedev traces score-kinds add helpfulness --type number --min 1 --max 5");
|
|
346
|
+
for (const k of live)
|
|
347
|
+
console.log(` ${k.name.padEnd(24)} ${k.dataType === "numeric" ? `number ${k.min ?? ""}–${k.max ?? ""}` : k.dataType === "boolean" ? "true / false" : k.categories.join(" / ")}${k.description ? ` ${k.description}` : ""}`);
|
|
348
|
+
}
|
|
349
|
+
async function runQueueCreate(args, config) {
|
|
350
|
+
const p = parseArgs(args);
|
|
351
|
+
const name = p.positional[0];
|
|
352
|
+
if (!name)
|
|
353
|
+
usage("codiedev traces queue create <name> [--scores a,b] [--description text]");
|
|
354
|
+
await call(config, "codiedev_traces_queue_create", "POST", "queues", { body: { name, scores: (p.flags["--scores"] ?? "").split(",").map((s) => s.trim()).filter(Boolean), ...(p.flags["--description"] ? { description: p.flags["--description"] } : {}) } });
|
|
355
|
+
console.log(`✓ Created review queue ${name}. Add runs: codiedev traces queue add "${name}" --agent <name> --failed`);
|
|
356
|
+
}
|
package/dist/configDir.d.ts
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The user's home, always absolute. $HOME can arrive relative or unexpanded ("~", ".", "") from a
|
|
3
|
+
* sandboxed shell or a wrapper; trusting it once wrote hook commands like ".codiedev/bin/…" into
|
|
4
|
+
* ~/.claude/settings.json, which only resolve when Claude Code starts in the home folder, so
|
|
5
|
+
* capture silently failed in every project. Fall back to the account's home from the OS.
|
|
6
|
+
*/
|
|
7
|
+
export declare function realHome(): string;
|
|
8
|
+
/** $HOME when it's a real absolute path, else the OS account's home, else resolved against cwd. */
|
|
9
|
+
export declare function pickHome(fromEnv: string | undefined, fromOs: string | undefined): string;
|
|
1
10
|
export declare function codiedevStateDir(): string;
|
package/dist/configDir.js
CHANGED
|
@@ -33,6 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.realHome = realHome;
|
|
37
|
+
exports.pickHome = pickHome;
|
|
36
38
|
exports.codiedevStateDir = codiedevStateDir;
|
|
37
39
|
// Single source of truth for where the CLI's config/state lives on disk.
|
|
38
40
|
//
|
|
@@ -46,9 +48,31 @@ exports.codiedevStateDir = codiedevStateDir;
|
|
|
46
48
|
// without creating a circular import.
|
|
47
49
|
const path = __importStar(require("path"));
|
|
48
50
|
const os = __importStar(require("os"));
|
|
51
|
+
/**
|
|
52
|
+
* The user's home, always absolute. $HOME can arrive relative or unexpanded ("~", ".", "") from a
|
|
53
|
+
* sandboxed shell or a wrapper; trusting it once wrote hook commands like ".codiedev/bin/…" into
|
|
54
|
+
* ~/.claude/settings.json, which only resolve when Claude Code starts in the home folder, so
|
|
55
|
+
* capture silently failed in every project. Fall back to the account's home from the OS.
|
|
56
|
+
*/
|
|
57
|
+
function realHome() {
|
|
58
|
+
let fromOs;
|
|
59
|
+
try {
|
|
60
|
+
fromOs = os.userInfo().homedir;
|
|
61
|
+
}
|
|
62
|
+
catch { /* no passwd entry */ }
|
|
63
|
+
return pickHome(os.homedir(), fromOs);
|
|
64
|
+
}
|
|
65
|
+
/** $HOME when it's a real absolute path, else the OS account's home, else resolved against cwd. */
|
|
66
|
+
function pickHome(fromEnv, fromOs) {
|
|
67
|
+
if (fromEnv && path.isAbsolute(fromEnv) && !fromEnv.split(path.sep).includes("~"))
|
|
68
|
+
return fromEnv;
|
|
69
|
+
if (fromOs && path.isAbsolute(fromOs))
|
|
70
|
+
return fromOs;
|
|
71
|
+
return path.resolve(fromEnv || ".");
|
|
72
|
+
}
|
|
49
73
|
const CONFIG_DIR = process.env.CODIEDEV_CONFIG_DIR?.trim()
|
|
50
74
|
? path.resolve(process.env.CODIEDEV_CONFIG_DIR.trim())
|
|
51
|
-
: path.join(
|
|
75
|
+
: path.join(realHome(), ".codiedev");
|
|
52
76
|
function codiedevStateDir() {
|
|
53
77
|
return CONFIG_DIR;
|
|
54
78
|
}
|
package/dist/connect.js
CHANGED
|
@@ -41,6 +41,7 @@ const crypto = __importStar(require("crypto"));
|
|
|
41
41
|
const os = __importStar(require("os"));
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
43
|
const utils_1 = require("./utils");
|
|
44
|
+
const cliSession_1 = require("./cliSession");
|
|
44
45
|
const connectFlow_1 = require("./connectFlow");
|
|
45
46
|
const version_1 = require("./version");
|
|
46
47
|
const upgradeNudge_1 = require("./upgradeNudge");
|
|
@@ -60,6 +61,16 @@ const BACKEND_URL = process.env.CODIEDEV_URL || "https://cli.codiedev.com";
|
|
|
60
61
|
* input in CI). Without a TTY there's no echo to suppress anyway, and
|
|
61
62
|
* raw mode would error.
|
|
62
63
|
*/
|
|
64
|
+
/** One visible line from the terminal (for yes/no questions). */
|
|
65
|
+
function prompt(question) {
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
68
|
+
rl.question(question, (answer) => {
|
|
69
|
+
rl.close();
|
|
70
|
+
resolve(answer);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
63
74
|
function promptHidden(question) {
|
|
64
75
|
return new Promise((resolve) => {
|
|
65
76
|
const stdin = process.stdin;
|
|
@@ -168,16 +179,47 @@ async function runConnect(args = []) {
|
|
|
168
179
|
// mint a fresh token from the portal — which is what bit Sonifi's first
|
|
169
180
|
// engineer when he interpreted "no visible token" as "the old one is gone".
|
|
170
181
|
const force = args.includes("--force") || args.includes("-f");
|
|
182
|
+
// `--token`: paste an API token instead of signing in (CI). Otherwise sign
|
|
183
|
+
// in with the browser: a loopback redirect on a desktop, a device code
|
|
184
|
+
// (approve on any device) over SSH or with no display, detected
|
|
185
|
+
// automatically. `--no-browser` / `--browser` force one or the other.
|
|
186
|
+
const pasteToken = args.includes("--token");
|
|
187
|
+
const useDevice = args.includes("--no-browser") || (!args.includes("--browser") && (0, cliSession_1.isHeadless)());
|
|
171
188
|
console.log("\nWelcome to Codiedev CLI\n");
|
|
172
189
|
await (0, upgradeNudge_1.maybePrintUpgradeNudge)();
|
|
173
190
|
console.log("This will connect your coding agent to your Codiedev workspace and install");
|
|
174
191
|
console.log("the session capture hook for org-wide session sharing.\n");
|
|
192
|
+
// A saved browser sign-in is reused only if it still works: refresh it
|
|
193
|
+
// first (a revoked one comes back signed out, with an empty token).
|
|
194
|
+
await (0, cliSession_1.ensureFreshToken)({ configPath: utils_1.CONFIG_PATH }).catch(() => undefined);
|
|
175
195
|
const saved = (0, utils_1.readConfig)();
|
|
176
|
-
const decision = (0, connectFlow_1.resolveConnectToken)({ savedConfig: saved, force });
|
|
196
|
+
const decision = (0, connectFlow_1.resolveConnectToken)({ savedConfig: saved, force: force || pasteToken });
|
|
177
197
|
let token;
|
|
198
|
+
let auth;
|
|
178
199
|
if (decision.source === "reused") {
|
|
179
200
|
token = decision.token;
|
|
180
|
-
|
|
201
|
+
auth = saved?.auth;
|
|
202
|
+
console.log(auth
|
|
203
|
+
? `Reusing your browser sign-in from ~/.codiedev/config.json (run with --force to sign in again).\n`
|
|
204
|
+
: `Reusing API token from ~/.codiedev/config.json (run with --force to sign in again).\n`);
|
|
205
|
+
}
|
|
206
|
+
else if (!pasteToken) {
|
|
207
|
+
// Replacing a browser sign-in: end the old one so the portal doesn't
|
|
208
|
+
// keep listing it.
|
|
209
|
+
if (saved?.auth)
|
|
210
|
+
await (0, cliSession_1.logout)({ configPath: utils_1.CONFIG_PATH }).catch(() => undefined);
|
|
211
|
+
const result = await (0, connectFlow_1.signInWithTokenFallback)({
|
|
212
|
+
signIn: () => useDevice ? (0, cliSession_1.deviceLogin)({ backendUrl: BACKEND_URL }) : (0, cliSession_1.browserLogin)({ backendUrl: BACKEND_URL }),
|
|
213
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
214
|
+
askYesNo: async (q) => /^y(es)?$/i.test((await prompt(q)).trim()),
|
|
215
|
+
askToken: promptHidden,
|
|
216
|
+
log: (line) => console.log(line),
|
|
217
|
+
error: (line) => console.error(line),
|
|
218
|
+
});
|
|
219
|
+
if (!result)
|
|
220
|
+
process.exit(1);
|
|
221
|
+
token = result.token;
|
|
222
|
+
auth = result.auth;
|
|
181
223
|
}
|
|
182
224
|
else {
|
|
183
225
|
// Hidden prompt — masks the token with `*` as it's typed/pasted so it
|
|
@@ -217,6 +259,7 @@ async function runConnect(args = []) {
|
|
|
217
259
|
deviceId: saved?.deviceId || crypto.randomUUID(),
|
|
218
260
|
deviceName: saved?.deviceName || os.hostname(),
|
|
219
261
|
captureIgnoredPaths: saved?.captureIgnoredPaths ?? [],
|
|
262
|
+
...(auth ? { auth } : {}),
|
|
220
263
|
};
|
|
221
264
|
try {
|
|
222
265
|
(0, utils_1.writeConfig)(config);
|
package/dist/connectFlow.d.ts
CHANGED
|
@@ -11,3 +11,24 @@ export type ConnectTokenResult = {
|
|
|
11
11
|
source: "prompt-required";
|
|
12
12
|
};
|
|
13
13
|
export declare function resolveConnectToken(input: ConnectTokenInput): ConnectTokenResult;
|
|
14
|
+
export interface SignInResult {
|
|
15
|
+
token: string;
|
|
16
|
+
auth?: import("./cliSession").BrowserAuth;
|
|
17
|
+
}
|
|
18
|
+
export interface SignInDeps {
|
|
19
|
+
/** Browser (loopback) or device-code sign-in; throws on failure. */
|
|
20
|
+
signIn: () => Promise<import("./cliSession").TokenSet>;
|
|
21
|
+
/** A person at a terminal who can answer prompts. */
|
|
22
|
+
interactive: boolean;
|
|
23
|
+
askYesNo: (question: string) => Promise<boolean>;
|
|
24
|
+
askToken: (question: string) => Promise<string>;
|
|
25
|
+
log: (line: string) => void;
|
|
26
|
+
error: (line: string) => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Browser sign-in, with the API-token paste as the way out: if the browser
|
|
30
|
+
* flow fails (blocked page, expired code, proxy) an interactive user is asked
|
|
31
|
+
* whether to paste a token instead, rather than being sent to rerun connect.
|
|
32
|
+
* Returns null when there's nothing to continue with (caller exits 1).
|
|
33
|
+
*/
|
|
34
|
+
export declare function signInWithTokenFallback(deps: SignInDeps): Promise<SignInResult | null>;
|
package/dist/connectFlow.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// generate a new one — leaving him thinking the old one had been lost.
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.resolveConnectToken = resolveConnectToken;
|
|
13
|
+
exports.signInWithTokenFallback = signInWithTokenFallback;
|
|
13
14
|
function resolveConnectToken(input) {
|
|
14
15
|
if (input.force)
|
|
15
16
|
return { source: "prompt-required" };
|
|
@@ -19,3 +20,43 @@ function resolveConnectToken(input) {
|
|
|
19
20
|
return { source: "prompt-required" };
|
|
20
21
|
return { source: "reused", token: input.savedConfig.token };
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Browser sign-in, with the API-token paste as the way out: if the browser
|
|
25
|
+
* flow fails (blocked page, expired code, proxy) an interactive user is asked
|
|
26
|
+
* whether to paste a token instead, rather than being sent to rerun connect.
|
|
27
|
+
* Returns null when there's nothing to continue with (caller exits 1).
|
|
28
|
+
*/
|
|
29
|
+
async function signInWithTokenFallback(deps) {
|
|
30
|
+
deps.log("Prefer an API token? Press Ctrl+C and run `codiedev connect --token`.\n");
|
|
31
|
+
try {
|
|
32
|
+
const s = await deps.signIn();
|
|
33
|
+
deps.log("Signed in. No API key is stored on this machine.\n");
|
|
34
|
+
return {
|
|
35
|
+
token: s.accessToken,
|
|
36
|
+
auth: {
|
|
37
|
+
kind: "browser",
|
|
38
|
+
clientId: s.clientId,
|
|
39
|
+
refreshToken: s.refreshToken,
|
|
40
|
+
accessTokenExpiresAt: s.expiresAt,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
deps.error(`\nBrowser sign-in didn't finish: ${err.message}`);
|
|
46
|
+
if (!deps.interactive) {
|
|
47
|
+
deps.error("To paste an API token instead, run `codiedev connect --token`.");
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (!(await deps.askYesNo("Paste an API token instead? [y/N] "))) {
|
|
51
|
+
deps.error("Run `codiedev connect` to try again, or `codiedev connect --token` to paste a token.");
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
deps.log("Create one at https://codiedev.com/portal/integrations/claude-code\n");
|
|
55
|
+
const token = (await deps.askToken("Enter your Codiedev API token: ")).trim();
|
|
56
|
+
if (!token) {
|
|
57
|
+
deps.error("Error: API token is required.");
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return { token };
|
|
61
|
+
}
|
|
62
|
+
}
|
package/dist/hook.js
CHANGED
|
@@ -60,6 +60,8 @@ const captureExclusions_1 = require("./captureExclusions");
|
|
|
60
60
|
const cursorHooks_1 = require("./cursorHooks");
|
|
61
61
|
const copilotHooks_1 = require("./copilotHooks");
|
|
62
62
|
const piHooks_1 = require("./piHooks");
|
|
63
|
+
const cliSession_1 = require("./cliSession");
|
|
64
|
+
const utils_2 = require("./utils");
|
|
63
65
|
// Pure builder for the discovery additionalContext. Returns null when there are
|
|
64
66
|
// no matches. The suggested `codiedev pull` command carries the EXACT top key
|
|
65
67
|
// (e.g. "pr-push.md") rather than a "<key>" placeholder — otherwise agents
|
|
@@ -440,6 +442,11 @@ function resolveMode(arg) {
|
|
|
440
442
|
return "claude-code";
|
|
441
443
|
}
|
|
442
444
|
async function main() {
|
|
445
|
+
// Browser sign-in: refresh the access token under the shared lock before
|
|
446
|
+
// anything reads config (cliSession.ts). A no-op for pasted API tokens and
|
|
447
|
+
// for tokens with time left; never throws, never prints (hook stdout is
|
|
448
|
+
// the agent's).
|
|
449
|
+
await (0, cliSession_1.ensureFreshToken)({ configPath: utils_2.CONFIG_PATH }).catch(() => undefined);
|
|
443
450
|
// Shared upload deps used by both "upload" and "upload-deferred" branches.
|
|
444
451
|
// Extracted to keep both branches in lockstep — any new dep added here
|
|
445
452
|
// automatically applies to the deferred path too.
|
package/dist/launcher.d.ts
CHANGED
|
@@ -8,13 +8,18 @@ export declare function launcherPath(): string;
|
|
|
8
8
|
/** True iff `cmd` invokes the stable launcher (from any state dir). */
|
|
9
9
|
export declare function usesLauncher(cmd: string | undefined): boolean;
|
|
10
10
|
export declare function readInstallRecord(): InstallRecord | null;
|
|
11
|
+
/**
|
|
12
|
+
* The hook script worth recording: absolute and present, and a global install over an npx cache
|
|
13
|
+
* copy (npm prunes ~/.npm/_npx, and hooks pointed there go quietly dead). Null = don't record.
|
|
14
|
+
*/
|
|
15
|
+
export declare function stableHookScript(hookScript: string, globalCandidates?: string[]): string | null;
|
|
11
16
|
/** Point the launcher at `hookScript`. Atomic, so a hook mid-launch never reads half a file. */
|
|
12
|
-
export declare function recordCurrentInstall(
|
|
17
|
+
export declare function recordCurrentInstall(requested: string): boolean;
|
|
13
18
|
/**
|
|
14
19
|
* Make sure the launcher exists and forwards to `hookScript`, returning the
|
|
15
20
|
* launcher path to bake into hook configs — or null if the state dir is not
|
|
16
21
|
* writable, in which case callers keep baking the direct path. Memoized:
|
|
17
22
|
* one connect writes ~20 hook entries.
|
|
18
23
|
*/
|
|
19
|
-
export declare function ensureLauncher(
|
|
24
|
+
export declare function ensureLauncher(requested: string): string | null;
|
|
20
25
|
export declare function resetLauncherCacheForTests(): void;
|