tines 0.0.79 → 0.0.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +236 -142
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -126,6 +126,14 @@ function runDurationLabel(run, now = Date.now()) {
126
126
  const seconds = Math.max(0, Math.round(((run.ended_at ?? now) - run.started_at) / 1e3));
127
127
  return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m`;
128
128
  }
129
+ function runCostLabel(run) {
130
+ const usage = run.usage;
131
+ if (!usage) return null;
132
+ if (usage.cost_usd !== void 0) return `$${usage.cost_usd.toFixed(2)}`;
133
+ if (usage.cost_source === "none") return "unreported";
134
+ const tokens = (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0);
135
+ return tokens > 0 ? `${tokens.toLocaleString()} tok` : null;
136
+ }
129
137
  function utilizationLabel(quota2, activeRuns, stateName = (id) => id) {
130
138
  if (quota2.type === "global_cap") {
131
139
  return `${activeRuns.length}/${quota2.limit} global slot${quota2.limit === 1 ? "" : "s"} in use`;
@@ -143,7 +151,164 @@ function utilizationLabel(quota2, activeRuns, stateName = (id) => id) {
143
151
  if (!counts.has(stateId)) counts.set(stateId, { name: stateName(stateId), n: 0 });
144
152
  }
145
153
  if (counts.size === 0) return `no active runs (roster default ${quota2.default_limit} per state)`;
146
- return [...counts.entries()].map(([stateId, { name, n }]) => `${name} ${n}/${quota2.overrides[stateId] ?? quota2.default_limit}`).join(" \xB7 ");
154
+ return [...counts.entries()].map(([stateId, { name: name2, n }]) => `${name2} ${n}/${quota2.overrides[stateId] ?? quota2.default_limit}`).join(" \xB7 ");
155
+ }
156
+
157
+ // ../shared/src/events.ts
158
+ var text = (t) => ({ kind: "text", text: t });
159
+ var name = (t) => ({ kind: "name", text: str(t) });
160
+ var selfRef = () => ({ kind: "self-ref" });
161
+ function str(v) {
162
+ return v === null || v === void 0 ? "" : String(v);
163
+ }
164
+ function joinChanged(v, sep) {
165
+ return Array.isArray(v) ? v.join(sep) : "";
166
+ }
167
+ function action(type) {
168
+ return type.split(".")[1] ?? type;
169
+ }
170
+ function otherRef(p) {
171
+ return {
172
+ kind: "other-ref",
173
+ project_name: str(p.other_project_name),
174
+ number: Number(p.other_number)
175
+ };
176
+ }
177
+ function linkSegments(ev, p) {
178
+ const lead = ev.type === "issue.link_added" ? "marked" : "unmarked";
179
+ const duplicate = p.kind === "duplicate_of";
180
+ if (duplicate && p.role === "target") {
181
+ return [text(lead), otherRef(p), text("as a duplicate of"), selfRef()];
182
+ }
183
+ const middle = duplicate ? "as a duplicate of" : p.role === "target" ? "as blocked by" : "as blocking";
184
+ const segs = [text(lead), selfRef(), text(middle), otherRef(p)];
185
+ if (p.other_title) segs.push(text(`\u2014 ${str(p.other_title)}`));
186
+ return segs;
187
+ }
188
+ var DESCRIBERS = {
189
+ "issue.created": (_ev, p) => {
190
+ const segs = [text("created"), selfRef()];
191
+ if (p.title) segs.push(text(`\u2014 ${str(p.title)}`));
192
+ return segs;
193
+ },
194
+ "issue.updated": (_ev, p) => {
195
+ const changed = joinChanged(p.changed, " and ");
196
+ const segs = [text(changed ? `updated ${changed} of` : "updated"), selfRef()];
197
+ if (p.workflow_to_name) {
198
+ segs.push(text("from"), name(p.workflow_from_name), text("to"), name(p.workflow_to_name));
199
+ }
200
+ return segs;
201
+ },
202
+ "issue.transitioned": (_ev, p) => {
203
+ const segs = [text("moved"), selfRef()];
204
+ if (p.action) segs.push(text("via"), name(p.action));
205
+ else if (p.forced) segs.push(text("directly"));
206
+ segs.push({
207
+ kind: "state-transition",
208
+ from: str(p.from_state_name) || "?",
209
+ to: str(p.to_state_name) || "?"
210
+ });
211
+ return segs;
212
+ },
213
+ "issue.commented": () => [text("commented on"), selfRef()],
214
+ "issue.link_added": linkSegments,
215
+ "issue.link_removed": linkSegments,
216
+ "issue.parked": (_ev, p) => [
217
+ text("parked"),
218
+ selfRef(),
219
+ text(`after ${str(p.attempt_count)} strikes \u2014 needs attention`)
220
+ ],
221
+ "issue.resumed": () => [text("resumed"), selfRef(), text("(attempt count reset)")],
222
+ "project.created": (ev, p) => projectSegments(ev, p),
223
+ "project.updated": (ev, p) => projectSegments(ev, p),
224
+ "project.deleted": (ev, p) => projectSegments(ev, p),
225
+ "workflow.created": (ev, p) => [text(`${action(ev.type)} workflow`), name(p.name)],
226
+ "workflow.updated": (ev, p) => [text(`${action(ev.type)} workflow`), name(p.name)],
227
+ "workflow.deleted": (ev, p) => [text(`${action(ev.type)} workflow`), name(p.name)],
228
+ "api_key.created": (_ev, p) => [text("created API key"), name(p.name)],
229
+ "api_key.revoked": (_ev, p) => [text("revoked API key"), name(p.name)],
230
+ "scheduled_task.created": (ev, p) => [text(`${action(ev.type)} schedule`), name(p.name)],
231
+ "scheduled_task.updated": (ev, p) => [text(`${action(ev.type)} schedule`), name(p.name)],
232
+ "scheduled_task.deleted": (ev, p) => [text(`${action(ev.type)} schedule`), name(p.name)],
233
+ "scheduled_task.skipped": (_ev, p) => {
234
+ const blocking = Array.isArray(p.blocking) ? p.blocking.length : 0;
235
+ return [
236
+ text("skipped an occurrence of schedule"),
237
+ name(p.name),
238
+ text(`(${blocking} open instance${blocking === 1 ? "" : "s"})`)
239
+ ];
240
+ },
241
+ "context.created": (ev, p) => contextSegments(ev, p),
242
+ "context.updated": (ev, p) => contextSegments(ev, p),
243
+ "context.deleted": (ev, p) => contextSegments(ev, p),
244
+ "runner.registered": (ev, p) => [text(`${action(ev.type)} runner`), name(p.name)],
245
+ "runner.updated": (ev, p) => [text(`${action(ev.type)} runner`), name(p.name)],
246
+ "runner.removed": (ev, p) => [text(`${action(ev.type)} runner`), name(p.name)],
247
+ "runner.errored": (_ev, p) => [
248
+ text("saw runner"),
249
+ name(p.runner_name),
250
+ text(`fail to launch (${str(p.consecutive_failures)} consecutive): ${str(p.error)}`)
251
+ ],
252
+ "routing_rule.created": (ev, p) => [text(`${action(ev.type)} the ${str(p.scope_label)} routing rule`)],
253
+ "routing_rule.updated": (ev, p) => [text(`${action(ev.type)} the ${str(p.scope_label)} routing rule`)],
254
+ "routing_rule.deleted": (ev, p) => [text(`${action(ev.type)} the ${str(p.scope_label)} routing rule`)],
255
+ "settings.updated": (_ev, p) => [
256
+ text(`updated supervisor settings (${joinChanged(p.changed, ", ") || "no changes"})`)
257
+ ],
258
+ "agent_run.started": (_ev, p) => {
259
+ const segs = [text(`started a ${str(p.tier)} run`)];
260
+ if (p.model) segs.push(text(`(${str(p.model)})`));
261
+ segs.push(text("via"), name(p.runner_name), text("on"), selfRef());
262
+ return segs;
263
+ },
264
+ "agent_run.ended": (_ev, p) => {
265
+ const segs = [text(`run ${str(p.status).replaceAll("_", " ")}`)];
266
+ if (p.outcome) segs.push(text(`\u2014 ${str(p.outcome)}`));
267
+ segs.push(text("via"), name(p.runner_name), text("on"), selfRef());
268
+ return segs;
269
+ }
270
+ };
271
+ function projectSegments(ev, p) {
272
+ return [text(`${action(ev.type)} project`), name(p.name ?? ev.project_name)];
273
+ }
274
+ function contextSegments(ev, p) {
275
+ const segs = [text(`${action(ev.type)} ${str(p.kind)}`), name(p.name)];
276
+ if (ev.issue_ref) {
277
+ segs.push(text("on"), selfRef());
278
+ } else {
279
+ const label = p.scope?.label;
280
+ if (label) segs.push(text(`[${label}]`));
281
+ }
282
+ return segs;
283
+ }
284
+ function describeEvent(ev) {
285
+ const describe = DESCRIBERS[ev.type];
286
+ const segs = describe ? describe(ev, ev.payload ?? {}) : ev.issue_ref ? [text(ev.type), selfRef()] : [text(ev.type)];
287
+ return segs.filter((s) => s.kind === "text" || s.kind === "name" ? s.text !== "" : true);
288
+ }
289
+ function segmentText(ev, seg) {
290
+ switch (seg.kind) {
291
+ case "text":
292
+ return seg.text;
293
+ case "name":
294
+ return `"${seg.text}"`;
295
+ case "self-ref":
296
+ return ev.issue_ref ? `${ev.issue_ref.project_name}/#${ev.issue_ref.number}` : "this issue";
297
+ case "other-ref":
298
+ return `${seg.project_name}/#${seg.number}`;
299
+ case "state-transition":
300
+ return `${seg.from} \u2192 ${seg.to}`;
301
+ }
302
+ }
303
+ function eventSummary(ev) {
304
+ return describeEvent(ev).map((seg) => segmentText(ev, seg)).filter((s) => s !== "").join(" ");
305
+ }
306
+ function displayActor(ev) {
307
+ const p = ev.payload ?? {};
308
+ if (ev.type === "issue.created" && p.scheduled_task_name && !p.manual) {
309
+ return `${ev.actor.user_name} via schedule \u201C${str(p.scheduled_task_name)}\u201D`;
310
+ }
311
+ return actorLabel(ev.actor);
147
312
  }
148
313
 
149
314
  // ../shared/src/client.ts
@@ -206,7 +371,7 @@ function createApiClient(options) {
206
371
  }
207
372
  return res;
208
373
  }
209
- const artifactPath = (issueId, name, suffix = "") => `/api/v1/issues/${issueId}/artifacts/${encodeURIComponent(name)}${suffix}`;
374
+ const artifactPath = (issueId, name2, suffix = "") => `/api/v1/issues/${issueId}/artifacts/${encodeURIComponent(name2)}${suffix}`;
210
375
  return {
211
376
  getTime: () => get("/api/time"),
212
377
  // Projects
@@ -266,14 +431,14 @@ function createApiClient(options) {
266
431
  getIssuePrompt: (issueId) => get(`/api/v1/issues/${issueId}/prompt`),
267
432
  // Issue artifacts (name-addressed under the issue)
268
433
  listArtifacts: (issueId) => get(`/api/v1/issues/${issueId}/artifacts`),
269
- getArtifact: (issueId, name) => get(artifactPath(issueId, name)),
434
+ getArtifact: (issueId, name2) => get(artifactPath(issueId, name2)),
270
435
  /** JSON upsert for text/link/pr: creates the artifact or appends a version. */
271
- putArtifact: (issueId, name, body) => request("PUT", artifactPath(issueId, name), body),
436
+ putArtifact: (issueId, name2, body) => request("PUT", artifactPath(issueId, name2), body),
272
437
  /** Raw-body upload for `file`: creates the artifact or appends a version. */
273
- uploadArtifactFile: async (issueId, name, bytes, opts) => {
438
+ uploadArtifactFile: async (issueId, name2, bytes, opts) => {
274
439
  const res = await raw(
275
440
  "PUT",
276
- artifactPath(issueId, name, `/file?filename=${encodeURIComponent(opts.filename)}`),
441
+ artifactPath(issueId, name2, `/file?filename=${encodeURIComponent(opts.filename)}`),
277
442
  {
278
443
  body: bytes,
279
444
  headers: { "content-type": opts.contentType }
@@ -285,23 +450,23 @@ function createApiClient(options) {
285
450
  * Multipart snapshot upload for `folder`: every file of the new version
286
451
  * in one request (path as the part filename, MIME as the part type).
287
452
  */
288
- uploadArtifactFolder: async (issueId, name, files) => {
453
+ uploadArtifactFolder: async (issueId, name2, files) => {
289
454
  const form = new FormData();
290
455
  for (const file of files) {
291
456
  form.append("file", new Blob([file.bytes], { type: file.contentType }), file.path);
292
457
  }
293
- const res = await raw("PUT", artifactPath(issueId, name, "/folder"), { body: form });
458
+ const res = await raw("PUT", artifactPath(issueId, name2, "/folder"), { body: form });
294
459
  return await res.json();
295
460
  },
296
461
  /** Bless the current content as fresh: appends a reaffirming version. */
297
- reaffirmArtifact: (issueId, name) => request("POST", artifactPath(issueId, name, "/reaffirm")),
462
+ reaffirmArtifact: (issueId, name2) => request("POST", artifactPath(issueId, name2, "/reaffirm")),
298
463
  /** Bytes of a version (default: current; `path` selects a folder entry). */
299
- getArtifactContent: async (issueId, name, opts = {}) => {
464
+ getArtifactContent: async (issueId, name2, opts = {}) => {
300
465
  const params = new URLSearchParams();
301
466
  if (opts.version !== void 0) params.set("version", String(opts.version));
302
467
  if (opts.path !== void 0) params.set("path", opts.path);
303
468
  const query2 = params.toString() ? `?${params.toString()}` : "";
304
- const res = await raw("GET", artifactPath(issueId, name, `/content${query2}`));
469
+ const res = await raw("GET", artifactPath(issueId, name2, `/content${query2}`));
305
470
  const disposition = res.headers.get("content-disposition") ?? "";
306
471
  const filenameMatch = disposition.match(/filename="((?:[^"\\]|\\.)*)"/);
307
472
  return {
@@ -310,7 +475,7 @@ function createApiClient(options) {
310
475
  filename: filenameMatch ? filenameMatch[1].replaceAll('\\"', '"') : null
311
476
  };
312
477
  },
313
- deleteArtifact: (issueId, name) => request("DELETE", artifactPath(issueId, name)),
478
+ deleteArtifact: (issueId, name2) => request("DELETE", artifactPath(issueId, name2)),
314
479
  // Events
315
480
  listEvents: (filters = {}) => get(`/api/v1/events${query(filters)}`),
316
481
  // Runners
@@ -405,30 +570,30 @@ function writeJsonFile(path, value, { secret = false } = {}) {
405
570
  writeFileSync(path, `${JSON.stringify(value, null, 2)}
406
571
  `, secret ? { mode: 384 } : {});
407
572
  }
408
- function credentialsKey(url, name) {
409
- return `${url.replace(/\/+$/, "")}#${name}`;
573
+ function credentialsKey(url, name2) {
574
+ return `${url.replace(/\/+$/, "")}#${name2}`;
410
575
  }
411
576
  function credentialsPath(dir) {
412
577
  return join(dir, "runners.json");
413
578
  }
414
- function loadRunnerCredentials(dir, url, name) {
579
+ function loadRunnerCredentials(dir, url, name2) {
415
580
  const all = readJsonFile(credentialsPath(dir));
416
- const entry = all?.[credentialsKey(url, name)];
581
+ const entry = all?.[credentialsKey(url, name2)];
417
582
  return entry && typeof entry.runner_id === "string" && typeof entry.token === "string" ? entry : null;
418
583
  }
419
- function saveRunnerCredentials(dir, url, name, creds) {
584
+ function saveRunnerCredentials(dir, url, name2, creds) {
420
585
  const path = credentialsPath(dir);
421
586
  const all = readJsonFile(path) ?? {};
422
- all[credentialsKey(url, name)] = creds;
587
+ all[credentialsKey(url, name2)] = creds;
423
588
  writeJsonFile(path, all, { secret: true });
424
589
  }
425
- function hasRunnerCredentials(dir, url, name) {
426
- return loadRunnerCredentials(dir, url, name) !== null;
590
+ function hasRunnerCredentials(dir, url, name2) {
591
+ return loadRunnerCredentials(dir, url, name2) !== null;
427
592
  }
428
- function clearRunnerCredentials(dir, url, name) {
593
+ function clearRunnerCredentials(dir, url, name2) {
429
594
  const path = credentialsPath(dir);
430
595
  const all = readJsonFile(path) ?? {};
431
- delete all[credentialsKey(url, name)];
596
+ delete all[credentialsKey(url, name2)];
432
597
  writeJsonFile(path, all, { secret: true });
433
598
  }
434
599
  function daemonStatePath(dir, runnerId) {
@@ -563,9 +728,9 @@ var LogBatcher = class {
563
728
  buffer = "";
564
729
  timer = null;
565
730
  sending = Promise.resolve();
566
- append(text) {
567
- if (!text) return;
568
- this.buffer += text;
731
+ append(text2) {
732
+ if (!text2) return;
733
+ this.buffer += text2;
569
734
  if (Buffer.byteLength(this.buffer, "utf8") >= (this.opts.maxBytes ?? 8 * 1024)) {
570
735
  void this.flush();
571
736
  } else if (!this.timer) {
@@ -1057,13 +1222,13 @@ function parseScheduleRef(ref) {
1057
1222
  return { project: ref.slice(0, sep), name: ref.slice(sep + 1) };
1058
1223
  }
1059
1224
  async function resolveSchedule(api, ref) {
1060
- const { project, name } = parseScheduleRef(ref);
1225
+ const { project, name: name2 } = parseScheduleRef(ref);
1061
1226
  const proj = await resolveProject(api, project);
1062
1227
  const { items } = await api.listProjectSchedules(proj.id, { limit: 100 });
1063
- const found = items.find((s) => s.name === name) ?? items.find((s) => s.id === name);
1228
+ const found = items.find((s) => s.name === name2) ?? items.find((s) => s.id === name2);
1064
1229
  if (!found) {
1065
1230
  die(
1066
- `no schedule "${name}" in project "${proj.name}" (have: ${items.map((s) => s.name).join(", ") || "none"})`
1231
+ `no schedule "${name2}" in project "${proj.name}" (have: ${items.map((s) => s.name).join(", ") || "none"})`
1067
1232
  );
1068
1233
  }
1069
1234
  return found;
@@ -1307,69 +1472,6 @@ function printWorkflowDetail(wf) {
1307
1472
  for (const w of wf.warnings ?? []) console.log(`
1308
1473
  warning: ${w}`);
1309
1474
  }
1310
- function eventSummary(ev) {
1311
- const p = ev.payload;
1312
- const issue = ev.issue_ref ? `${ev.issue_ref.project_name}/#${ev.issue_ref.number}` : null;
1313
- switch (ev.type) {
1314
- case "issue.created":
1315
- return `created ${issue}: ${p.title}${p.scheduled_task_name ? ` (via schedule "${p.scheduled_task_name}")` : ""}`;
1316
- case "issue.updated":
1317
- return `updated ${issue} (${p.changed?.join(", ")})`;
1318
- case "issue.transitioned":
1319
- return `${p.action ? `"${p.action}" on` : "moved"} ${issue}: ${p.from_state_name} \u2192 ${p.to_state_name}`;
1320
- case "issue.commented":
1321
- return `commented on ${issue}`;
1322
- case "project.created":
1323
- case "project.updated":
1324
- case "project.deleted":
1325
- return `${ev.type.split(".")[1]} project "${p.name ?? ev.project_name}"`;
1326
- case "workflow.created":
1327
- case "workflow.updated":
1328
- case "workflow.deleted":
1329
- return `${ev.type.split(".")[1]} workflow "${p.name}"`;
1330
- case "api_key.created":
1331
- return `created API key "${p.name}"`;
1332
- case "api_key.revoked":
1333
- return `revoked API key "${p.name}"`;
1334
- case "scheduled_task.created":
1335
- case "scheduled_task.updated":
1336
- case "scheduled_task.deleted":
1337
- return `${ev.type.split(".")[1]} schedule "${p.name}"`;
1338
- case "context.created":
1339
- case "context.updated":
1340
- case "context.deleted": {
1341
- const scope = p.scope;
1342
- const verb = ev.type.split(".")[1];
1343
- return `${verb} ${p.kind} "${p.name}"${scope?.label ? ` [${scope.label}]` : ""}`;
1344
- }
1345
- case "scheduled_task.skipped": {
1346
- const blocking = Array.isArray(p.blocking) ? p.blocking.length : 0;
1347
- return `skipped schedule "${p.name}" (${blocking} open instance${blocking === 1 ? "" : "s"})`;
1348
- }
1349
- case "runner.registered":
1350
- case "runner.updated":
1351
- case "runner.removed":
1352
- return `${ev.type.split(".")[1]} runner "${p.name}"`;
1353
- case "runner.errored":
1354
- return `runner "${p.runner_name}" failed to launch (${p.consecutive_failures} consecutive): ${p.error}`;
1355
- case "agent_run.started":
1356
- return `run started on ${issue} via ${p.runner_name} (${p.tier}${p.model ? ` \u2192 ${p.model}` : ""})`;
1357
- case "agent_run.ended":
1358
- return `run ${p.status} on ${issue} via ${p.runner_name}${p.outcome ? ` \u2014 ${p.outcome}` : ""}`;
1359
- case "issue.parked":
1360
- return `parked ${issue} after ${p.attempt_count} strikes \u2014 needs attention`;
1361
- case "issue.resumed":
1362
- return `resumed ${issue} (attempt count reset)`;
1363
- case "routing_rule.created":
1364
- case "routing_rule.updated":
1365
- case "routing_rule.deleted":
1366
- return `${ev.type.split(".")[1]} the ${p.scope_label} routing rule`;
1367
- case "settings.updated":
1368
- return `updated supervisor settings (${p.changed?.join(", ") || "no changes"})`;
1369
- default:
1370
- return ev.type;
1371
- }
1372
- }
1373
1475
  function cliVersion() {
1374
1476
  try {
1375
1477
  const manifest = new URL("../package.json", import.meta.url);
@@ -1401,7 +1503,7 @@ withList(projects.command("list").description("List projects")).action(async (op
1401
1503
  withCommon(
1402
1504
  projects.command("create <name>").description("Create a project (with its initial context prompt)").option("-d, --description <text>", "project description").option("-w, --default-workflow <id-or-name>", "default workflow for new issues").option("--prompt <md>", "initial conventions prompt, stitched into every issue's agent prompt: inline Markdown or @file").option("--no-prompt", "create without an initial prompt")
1403
1505
  ).action(
1404
- async (name, opts) => {
1506
+ async (name2, opts) => {
1405
1507
  if (opts.prompt === void 0 || opts.prompt === true) {
1406
1508
  die(
1407
1509
  'every issue in a project inherits its context \u2014 give the project an initial prompt:\n --prompt "<markdown>" house conventions, inline or @file\n --no-prompt create without one (add later: tines context create -k prompt -n conventions -p <name> --body \u2026)'
@@ -1410,7 +1512,7 @@ withCommon(
1410
1512
  const api = client(opts);
1411
1513
  const workflowId = opts.defaultWorkflow ? (await resolveWorkflow(api, opts.defaultWorkflow)).id : void 0;
1412
1514
  const project = await api.createProject({
1413
- name,
1515
+ name: name2,
1414
1516
  description: opts.description,
1415
1517
  default_workflow_id: workflowId,
1416
1518
  initial_prompt: typeof opts.prompt === "string" ? readBodyValue(opts.prompt) : void 0
@@ -1647,13 +1749,13 @@ withCommon(
1647
1749
  );
1648
1750
  withCommon(
1649
1751
  issues.command("move <ref> <action>").description('Take a transition on an issue by its action name (e.g. "approve")')
1650
- ).action(async (ref, action, opts) => {
1752
+ ).action(async (ref, action2, opts) => {
1651
1753
  const api = client(opts);
1652
1754
  const issue = await resolveIssue(api, ref);
1653
- const moved = await api.transitionIssue(issue.id, { action });
1755
+ const moved = await api.transitionIssue(issue.id, { action: action2 });
1654
1756
  if (opts.json) return printJson(moved);
1655
1757
  console.log(
1656
- `${moved.project_name}/#${moved.number}: ${issue.state.name} \u2192 ${moved.state.name} ("${action}")`
1758
+ `${moved.project_name}/#${moved.number}: ${issue.state.name} \u2192 ${moved.state.name} ("${action2}")`
1657
1759
  );
1658
1760
  });
1659
1761
  withCommon(
@@ -1863,10 +1965,10 @@ withCommon(artifactsCmd.command("list <ref>").description("List the artifacts at
1863
1965
  );
1864
1966
  withCommon(
1865
1967
  artifactsCmd.command("show <ref> <name>").description("Show an artifact with its full version history")
1866
- ).action(async (ref, name, opts) => {
1968
+ ).action(async (ref, name2, opts) => {
1867
1969
  const api = client(opts);
1868
1970
  const issue = await resolveIssue(api, ref);
1869
- const artifact = await api.getArtifact(issue.id, name);
1971
+ const artifact = await api.getArtifact(issue.id, name2);
1870
1972
  if (opts.json) return printJson(artifact);
1871
1973
  console.log(`${artifact.artifact_type} artifact "${artifact.name}" on ${issue.project_name}/${issue.number}`);
1872
1974
  if (artifact.description) console.log(artifact.description);
@@ -1898,7 +2000,7 @@ withCommon(
1898
2000
  // --url is the link payload here; the API base comes from TINES_API_URL.
1899
2001
  { baseUrlFlag: false }
1900
2002
  ).action(
1901
- async (ref, name, opts) => {
2003
+ async (ref, name2, opts) => {
1902
2004
  const api = client({ apiKey: opts.apiKey, json: opts.json });
1903
2005
  const sources = [opts.file, opts.folder, opts.text, opts.url, opts.pr].filter((v) => v !== void 0);
1904
2006
  if (sources.length !== 1) {
@@ -1914,9 +2016,9 @@ withCommon(
1914
2016
  }
1915
2017
  const files = walkFolder(opts.folder);
1916
2018
  if (files.length === 0) die(`${opts.folder} contains no files to snapshot`);
1917
- artifact = await api.uploadArtifactFolder(issue.id, name, files);
2019
+ artifact = await api.uploadArtifactFolder(issue.id, name2, files);
1918
2020
  if (opts.description !== void 0) {
1919
- artifact = await api.putArtifact(issue.id, name, { description: opts.description });
2021
+ artifact = await api.putArtifact(issue.id, name2, { description: opts.description });
1920
2022
  }
1921
2023
  } else if (opts.file !== void 0) {
1922
2024
  let bytes;
@@ -1925,15 +2027,15 @@ withCommon(
1925
2027
  } catch (err) {
1926
2028
  die(`cannot read ${opts.file}: ${err instanceof Error ? err.message : String(err)}`);
1927
2029
  }
1928
- artifact = await api.uploadArtifactFile(issue.id, name, bytes, {
2030
+ artifact = await api.uploadArtifactFile(issue.id, name2, bytes, {
1929
2031
  filename: opts.filename ?? basename(opts.file),
1930
2032
  contentType: opts.contentType ?? sniffContentType(opts.file)
1931
2033
  });
1932
2034
  if (opts.description !== void 0) {
1933
- artifact = await api.putArtifact(issue.id, name, { description: opts.description });
2035
+ artifact = await api.putArtifact(issue.id, name2, { description: opts.description });
1934
2036
  }
1935
2037
  } else if (opts.text !== void 0) {
1936
- artifact = await api.putArtifact(issue.id, name, {
2038
+ artifact = await api.putArtifact(issue.id, name2, {
1937
2039
  type: "text",
1938
2040
  content: readBodyValue(opts.text),
1939
2041
  ...opts.filename !== void 0 ? { filename: opts.filename } : {},
@@ -1941,7 +2043,7 @@ withCommon(
1941
2043
  ...opts.description !== void 0 ? { description: opts.description } : {}
1942
2044
  });
1943
2045
  } else if (opts.url !== void 0) {
1944
- artifact = await api.putArtifact(issue.id, name, {
2046
+ artifact = await api.putArtifact(issue.id, name2, {
1945
2047
  type: "link",
1946
2048
  url: opts.url,
1947
2049
  ...opts.title !== void 0 ? { title: opts.title } : {},
@@ -1952,7 +2054,7 @@ withCommon(
1952
2054
  if (!parsed) {
1953
2055
  die(`--pr takes owner/repo#N or a GitHub PR URL, got "${opts.pr}"`);
1954
2056
  }
1955
- artifact = await api.putArtifact(issue.id, name, {
2057
+ artifact = await api.putArtifact(issue.id, name2, {
1956
2058
  type: "pr",
1957
2059
  pr_repo_url: parsed.repo_url,
1958
2060
  pr_number: parsed.number,
@@ -1967,10 +2069,10 @@ withCommon(
1967
2069
  );
1968
2070
  withCommon(
1969
2071
  artifactsCmd.command("reaffirm <ref> <name>").description("Bless the current content as fresh (appends a version reusing the same payload)")
1970
- ).action(async (ref, name, opts) => {
2072
+ ).action(async (ref, name2, opts) => {
1971
2073
  const api = client(opts);
1972
2074
  const issue = await resolveIssue(api, ref);
1973
- const artifact = await api.reaffirmArtifact(issue.id, name);
2075
+ const artifact = await api.reaffirmArtifact(issue.id, name2);
1974
2076
  if (opts.json) return printJson(artifact);
1975
2077
  console.log(
1976
2078
  `reaffirmed "${artifact.name}" on ${issue.project_name}/${issue.number}: v${artifact.current_version.version} reaffirms v${artifact.current_version.reaffirmed_from} \u2014 fresh as of now`
@@ -1979,14 +2081,14 @@ withCommon(
1979
2081
  withCommon(
1980
2082
  artifactsCmd.command("get <ref> <name>").description("Fetch content (current version by default); a link/pr prints its URL").option("--version <n>", "fetch a specific version from the history", (v) => Number.parseInt(v, 10)).option("--out <path>", "write to this file, or into this directory (keeps the stored filename)")
1981
2083
  ).action(
1982
- async (ref, name, opts) => {
2084
+ async (ref, name2, opts) => {
1983
2085
  const api = client(opts);
1984
2086
  const issue = await resolveIssue(api, ref);
1985
- const artifact = await api.getArtifact(issue.id, name);
2087
+ const artifact = await api.getArtifact(issue.id, name2);
1986
2088
  const version = opts.version === void 0 ? artifact.current_version : artifact.versions.find((v) => v.version === opts.version);
1987
2089
  if (!version) {
1988
2090
  die(
1989
- `artifact "${name}" has no version ${opts.version} (history: v1\u2013v${artifact.current_version.version})`
2091
+ `artifact "${name2}" has no version ${opts.version} (history: v1\u2013v${artifact.current_version.version})`
1990
2092
  );
1991
2093
  }
1992
2094
  if (artifact.artifact_type === "link" || artifact.artifact_type === "pr") {
@@ -1996,7 +2098,7 @@ withCommon(
1996
2098
  }
1997
2099
  if (artifact.artifact_type === "folder") {
1998
2100
  if (opts.out === void 0) {
1999
- die(`artifact "${name}" is a folder \u2014 pass --out <dir> to write its tree`);
2101
+ die(`artifact "${name2}" is a folder \u2014 pass --out <dir> to write its tree`);
2000
2102
  }
2001
2103
  if (existsSync2(opts.out) && !statSync(opts.out).isDirectory()) {
2002
2104
  die(`--out for a folder must be a directory, and "${opts.out}" is a file`);
@@ -2004,7 +2106,7 @@ withCommon(
2004
2106
  const files = version.files ?? [];
2005
2107
  let total = 0;
2006
2108
  for (const file of files) {
2007
- const content2 = await api.getArtifactContent(issue.id, name, {
2109
+ const content2 = await api.getArtifactContent(issue.id, name2, {
2008
2110
  version: opts.version,
2009
2111
  path: file.path
2010
2112
  });
@@ -2014,15 +2116,15 @@ withCommon(
2014
2116
  total += content2.bytes.byteLength;
2015
2117
  }
2016
2118
  return console.log(
2017
- `wrote ${files.length} file${files.length === 1 ? "" : "s"} (${total} bytes) from "${name}" v${version.version} into ${opts.out}/`
2119
+ `wrote ${files.length} file${files.length === 1 ? "" : "s"} (${total} bytes) from "${name2}" v${version.version} into ${opts.out}/`
2018
2120
  );
2019
2121
  }
2020
- const content = await api.getArtifactContent(issue.id, name, { version: opts.version });
2122
+ const content = await api.getArtifactContent(issue.id, name2, { version: opts.version });
2021
2123
  const bytes = Buffer.from(content.bytes);
2022
2124
  if (opts.out !== void 0) {
2023
2125
  let target2 = opts.out;
2024
2126
  if (existsSync2(target2) && statSync(target2).isDirectory()) {
2025
- target2 = join3(target2, version.filename ?? name);
2127
+ target2 = join3(target2, version.filename ?? name2);
2026
2128
  }
2027
2129
  writeFileSync3(target2, bytes);
2028
2130
  return console.log(`wrote ${target2} (${bytes.byteLength} bytes, ${content.content_type})`);
@@ -2031,20 +2133,20 @@ withCommon(
2031
2133
  process.stdout.write(bytes);
2032
2134
  return;
2033
2135
  }
2034
- const target = version.filename ?? name;
2136
+ const target = version.filename ?? name2;
2035
2137
  writeFileSync3(target, bytes);
2036
2138
  console.log(`wrote ${target} (${bytes.byteLength} bytes, ${content.content_type})`);
2037
2139
  }
2038
2140
  );
2039
2141
  withCommon(
2040
2142
  artifactsCmd.command("delete <ref> <name>").description("Delete an artifact \u2014 every version and its stored files (history is not recoverable)")
2041
- ).action(async (ref, name, opts) => {
2143
+ ).action(async (ref, name2, opts) => {
2042
2144
  const api = client(opts);
2043
2145
  const issue = await resolveIssue(api, ref);
2044
- const artifact = await api.getArtifact(issue.id, name);
2045
- await api.deleteArtifact(issue.id, name);
2146
+ const artifact = await api.getArtifact(issue.id, name2);
2147
+ await api.deleteArtifact(issue.id, name2);
2046
2148
  console.log(
2047
- `deleted ${artifact.artifact_type} artifact "${name}" from ${issue.project_name}/${issue.number} (${artifact.version_count} version${artifact.version_count === 1 ? "" : "s"})`
2149
+ `deleted ${artifact.artifact_type} artifact "${name2}" from ${issue.project_name}/${issue.number} (${artifact.version_count} version${artifact.version_count === 1 ? "" : "s"})`
2048
2150
  );
2049
2151
  });
2050
2152
  withCommon(
@@ -2059,8 +2161,8 @@ withCommon(
2059
2161
  return console.log(`unpinned ${updated2.project_name}/#${updated2.number} \u2014 routing rules apply again`);
2060
2162
  }
2061
2163
  if (runnerSpec === void 0) die("pass <runner>[:tier] to pin, or --clear to unpin");
2062
- const { name, tier } = parseTargetSpec(runnerSpec);
2063
- const runner = await resolveRunner(api, name);
2164
+ const { name: name2, tier } = parseTargetSpec(runnerSpec);
2165
+ const runner = await resolveRunner(api, name2);
2064
2166
  const updated = await api.updateIssue(issue.id, {
2065
2167
  pinned_runner_id: runner.id,
2066
2168
  pinned_tier: tier ?? null
@@ -2357,12 +2459,12 @@ withCommon(
2357
2459
  ).action(
2358
2460
  async (ref, markdown, opts, command) => {
2359
2461
  if (helpGuard(command, markdown)) return;
2360
- const text = readBodyValue(markdown);
2462
+ const text2 = readBodyValue(markdown);
2361
2463
  const api = client(opts);
2362
2464
  const { scope, note, item } = await resolveJournal(api, ref, opts.state);
2363
2465
  printNote(note);
2364
2466
  if (item) {
2365
- const updated = await api.appendContextItem(item.id, { text });
2467
+ const updated = await api.appendContextItem(item.id, { text: text2 });
2366
2468
  if (opts.json) return printJson(updated);
2367
2469
  return console.log(`appended to the ${scope.label} journal (now v${updated.version})`);
2368
2470
  }
@@ -2372,7 +2474,7 @@ withCommon(
2372
2474
  name: JOURNAL_NAME,
2373
2475
  project_id: scope.project_id ?? void 0,
2374
2476
  workflow_state_id: scope.workflow_state_id ?? void 0,
2375
- body: text.trim()
2477
+ body: text2.trim()
2376
2478
  });
2377
2479
  if (opts.json) return printJson(created);
2378
2480
  console.log(`started the ${scope.label} journal (${created.id})`);
@@ -2380,7 +2482,7 @@ withCommon(
2380
2482
  if (!(err instanceof ApiError) || err.code !== "duplicate_context_name") throw err;
2381
2483
  const { item: fresh } = await resolveJournal(api, ref, opts.state);
2382
2484
  if (!fresh) throw err;
2383
- const updated = await api.appendContextItem(fresh.id, { text });
2485
+ const updated = await api.appendContextItem(fresh.id, { text: text2 });
2384
2486
  if (opts.json) return printJson(updated);
2385
2487
  console.log(`appended to the ${scope.label} journal (now v${updated.version})`);
2386
2488
  }
@@ -2561,13 +2663,13 @@ async function resolveRunner(api, ref) {
2561
2663
  function parseTargetSpec(spec) {
2562
2664
  const sep = spec.lastIndexOf(":");
2563
2665
  if (sep === -1) return { name: spec };
2564
- const name = spec.slice(0, sep);
2666
+ const name2 = spec.slice(0, sep);
2565
2667
  const tier = spec.slice(sep + 1);
2566
- if (!name) die(`target must look like <runner>[:tier], got "${spec}"`);
2668
+ if (!name2) die(`target must look like <runner>[:tier], got "${spec}"`);
2567
2669
  if (!MODEL_TIERS.includes(tier)) {
2568
2670
  die(`unknown tier "${tier}" in "${spec}" (tiers: ${MODEL_TIERS.join(", ")})`);
2569
2671
  }
2570
- return { name, tier };
2672
+ return { name: name2, tier };
2571
2673
  }
2572
2674
  function runnerStatusLabel(runner) {
2573
2675
  if (runner.status === "paused") return "paused";
@@ -2805,14 +2907,6 @@ withCommon(
2805
2907
  }
2806
2908
  );
2807
2909
  var runsCmd = program.command("runs").description("Agent runs: attempts at issues by runners");
2808
- function runCostLabel(run) {
2809
- const usage = run.usage;
2810
- if (!usage) return "\u2014";
2811
- if (usage.cost_usd !== void 0) return `$${usage.cost_usd.toFixed(2)}`;
2812
- if (usage.cost_source === "none") return "unreported";
2813
- const tokens = (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0);
2814
- return tokens > 0 ? `${tokens.toLocaleString()} tok` : "\u2014";
2815
- }
2816
2910
  function runRow(run) {
2817
2911
  return [
2818
2912
  run.id,
@@ -2821,7 +2915,7 @@ function runRow(run) {
2821
2915
  `${run.tier}${run.model ? ` (${run.model})` : ""}`,
2822
2916
  run.status,
2823
2917
  runDurationLabel(run),
2824
- runCostLabel(run),
2918
+ runCostLabel(run) ?? "\u2014",
2825
2919
  timestamp(run.created_at)
2826
2920
  ];
2827
2921
  }
@@ -2920,8 +3014,8 @@ withCommon(
2920
3014
  const scope = await resolveRoutingScope(api, opts);
2921
3015
  const targets = [];
2922
3016
  for (const spec of targetSpecs) {
2923
- const { name, tier } = parseTargetSpec(spec);
2924
- const runner = await resolveRunner(api, name);
3017
+ const { name: name2, tier } = parseTargetSpec(spec);
3018
+ const runner = await resolveRunner(api, name2);
2925
3019
  targets.push(tier ? { runner_id: runner.id, tier } : { runner_id: runner.id });
2926
3020
  }
2927
3021
  const { items } = await api.listRoutingRules();
@@ -3063,7 +3157,7 @@ withList(
3063
3157
  if (items.length === 0) return console.log("no events");
3064
3158
  table([
3065
3159
  ["WHEN", "ACTOR", "EVENT"],
3066
- ...items.map((ev) => [timestamp(ev.created_at), actorLabel(ev.actor), eventSummary(ev)])
3160
+ ...items.map((ev) => [timestamp(ev.created_at), displayActor(ev), eventSummary(ev)])
3067
3161
  ]);
3068
3162
  });
3069
3163
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.79",
3
+ "version": "0.0.81",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",