tines 0.0.169 → 0.0.171
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 +4 -0
- package/dist/index.js +148 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,10 @@ built for humans and their agents: work moves through workflows, every action is
|
|
|
5
5
|
record, and a supervisor hands eligible issues to agents. This package talks to the same
|
|
6
6
|
HTTP API the web app uses.
|
|
7
7
|
|
|
8
|
+
Awaiting-session continuation policy fields are staged and default off. Runtime continuation is
|
|
9
|
+
unavailable until support for the runner's provider is released; configuring thresholds does not
|
|
10
|
+
enable continuation.
|
|
11
|
+
|
|
8
12
|
```sh
|
|
9
13
|
npm install -g tines # or, one-shot with no install:
|
|
10
14
|
npx -y tines --help
|
package/dist/index.js
CHANGED
|
@@ -3474,6 +3474,10 @@ function parsePrSpec(spec) {
|
|
|
3474
3474
|
}
|
|
3475
3475
|
var MODEL_TIERS = ["smartest", "balanced", "cheapest"];
|
|
3476
3476
|
var RUNNER_ONLINE_WINDOW_MS = 2 * 60 * 1e3;
|
|
3477
|
+
var DEFAULT_RESUME_WINDOW_HOURS = 48;
|
|
3478
|
+
var DEFAULT_RESUME_MAX_TURNS = 25;
|
|
3479
|
+
var DEFAULT_RESUME_MAX_TOKENS = 1e5;
|
|
3480
|
+
var DEFAULT_RESUME_MAX_COST_USD = 2;
|
|
3477
3481
|
var RUNNER_OFFLINE_FAIL_MS = 5 * 60 * 1e3;
|
|
3478
3482
|
var LAUNCH_STALL_MS = 5 * 60 * 1e3;
|
|
3479
3483
|
var RUN_KEY_SLACK_MS = 10 * 60 * 1e3;
|
|
@@ -4436,7 +4440,7 @@ function runRow(run) {
|
|
|
4436
4440
|
run.issue_ref ? issueRef(run.issue_ref) : run.issue_id,
|
|
4437
4441
|
run.runner_name,
|
|
4438
4442
|
`${run.tier}${run.model ? ` (${run.model})` : ""}`,
|
|
4439
|
-
run.status
|
|
4443
|
+
`${run.status}${run.resumed_from_run_id ? ` \xB7 resumed run ${run.resumed_from_run_id}` : ""}`,
|
|
4440
4444
|
runDurationLabel(run),
|
|
4441
4445
|
runCostLabel(run) ?? "\u2014",
|
|
4442
4446
|
timestamp(run.created_at)
|
|
@@ -4555,7 +4559,15 @@ function parseTargetSpec(spec) {
|
|
|
4555
4559
|
}
|
|
4556
4560
|
|
|
4557
4561
|
// src/config.ts
|
|
4558
|
-
import {
|
|
4562
|
+
import {
|
|
4563
|
+
existsSync,
|
|
4564
|
+
mkdirSync,
|
|
4565
|
+
readFileSync as readFileSync3,
|
|
4566
|
+
renameSync,
|
|
4567
|
+
rmSync,
|
|
4568
|
+
unlinkSync,
|
|
4569
|
+
writeFileSync
|
|
4570
|
+
} from "node:fs";
|
|
4559
4571
|
import { homedir } from "node:os";
|
|
4560
4572
|
import { dirname, join } from "node:path";
|
|
4561
4573
|
function defaultConfigDir() {
|
|
@@ -4571,8 +4583,15 @@ function readJsonFile(path2) {
|
|
|
4571
4583
|
}
|
|
4572
4584
|
function writeJsonFile(path2, value, { secret = false } = {}) {
|
|
4573
4585
|
mkdirSync(dirname(path2), { recursive: true });
|
|
4574
|
-
|
|
4586
|
+
const tmp = `${path2}.tmp-${process.pid}`;
|
|
4587
|
+
try {
|
|
4588
|
+
writeFileSync(tmp, `${JSON.stringify(value, null, 2)}
|
|
4575
4589
|
`, secret ? { mode: 384 } : {});
|
|
4590
|
+
renameSync(tmp, path2);
|
|
4591
|
+
} catch (e) {
|
|
4592
|
+
rmSync(tmp, { force: true });
|
|
4593
|
+
throw e;
|
|
4594
|
+
}
|
|
4576
4595
|
}
|
|
4577
4596
|
function configPath(dir = defaultConfigDir()) {
|
|
4578
4597
|
return join(dir, "config.json");
|
|
@@ -6931,7 +6950,7 @@ import {
|
|
|
6931
6950
|
mkdirSync as mkdirSync4,
|
|
6932
6951
|
readFileSync as readFileSync9,
|
|
6933
6952
|
realpathSync,
|
|
6934
|
-
rmSync as
|
|
6953
|
+
rmSync as rmSync3,
|
|
6935
6954
|
statSync as statSync4,
|
|
6936
6955
|
unlinkSync as unlinkSync2,
|
|
6937
6956
|
writeFileSync as writeFileSync3
|
|
@@ -7432,7 +7451,7 @@ function buildHarnessInvocation(spec, input) {
|
|
|
7432
7451
|
file: "sh",
|
|
7433
7452
|
args: [
|
|
7434
7453
|
"-c",
|
|
7435
|
-
`claude -p --output-format stream-json --verbose${input.model ? ` --model ${shellQuote(input.model)}` : ""} < ${shellQuote(input.promptFile)}`
|
|
7454
|
+
`claude -p${input.resumeSessionId ? ` --resume ${shellQuote(input.resumeSessionId)}` : ""} --output-format stream-json --verbose${input.model ? ` --model ${shellQuote(input.model)}` : ""} < ${shellQuote(input.promptFile)}`
|
|
7436
7455
|
]
|
|
7437
7456
|
};
|
|
7438
7457
|
case "codex":
|
|
@@ -7470,7 +7489,10 @@ function formatLaunchBanner(invocation, input, meta) {
|
|
|
7470
7489
|
`model=${input.model ?? "(fixed)"}`,
|
|
7471
7490
|
`timeout=${meta.timeoutMinutes}m`,
|
|
7472
7491
|
`cli=${meta.cliVersion}`,
|
|
7473
|
-
`workspace=${input.workspace}
|
|
7492
|
+
`workspace=${input.workspace}`,
|
|
7493
|
+
// The one line that says a send-back reused a session rather than
|
|
7494
|
+
// re-exploring the repository from zero.
|
|
7495
|
+
...input.resumeSessionId ? [`resumed=${meta.resumedFromRunId ?? input.resumeSessionId}`] : []
|
|
7474
7496
|
];
|
|
7475
7497
|
return `$ ${formatLaunchCommand(invocation)}
|
|
7476
7498
|
# tines runner: ${fields.join(" ")}
|
|
@@ -7534,7 +7556,7 @@ var RunTable = class {
|
|
|
7534
7556
|
cleanup(run, outcome = "failed") {
|
|
7535
7557
|
this.runs.delete(run.runId);
|
|
7536
7558
|
this.effects.persist();
|
|
7537
|
-
this.effects.release(run, { keep: this.keep(outcome), outcome });
|
|
7559
|
+
this.effects.release(run, { keep: this.keep(outcome, run), outcome });
|
|
7538
7560
|
}
|
|
7539
7561
|
/**
|
|
7540
7562
|
* Ends a run: flush logs, finish-report, clean up. On a run someone
|
|
@@ -7555,11 +7577,15 @@ var RunTable = class {
|
|
|
7555
7577
|
run.settled = true;
|
|
7556
7578
|
run.endNote ??= error;
|
|
7557
7579
|
run.drain?.();
|
|
7558
|
-
if (this.keep(status)) this.effects.noteKept?.(run);
|
|
7580
|
+
if (this.keep(status, run)) this.effects.noteKept?.(run);
|
|
7559
7581
|
await run.flush?.();
|
|
7582
|
+
const keptBeforeFinish = this.keep(status, run);
|
|
7560
7583
|
try {
|
|
7561
7584
|
await this.effects.finish(run, status, error, judgment);
|
|
7562
7585
|
this.effects.log(`run ${run.runId} finished: ${status}${error ? ` (${error})` : ""}`);
|
|
7586
|
+
if (!keptBeforeFinish && this.keep(status, run)) {
|
|
7587
|
+
this.effects.log(`run ${run.runId} workspace kept for resume at ${run.workspace}`);
|
|
7588
|
+
}
|
|
7563
7589
|
} catch (err) {
|
|
7564
7590
|
this.effects.log(
|
|
7565
7591
|
`finish report for run ${run.runId} not accepted: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -7814,7 +7840,7 @@ function message(err) {
|
|
|
7814
7840
|
}
|
|
7815
7841
|
|
|
7816
7842
|
// src/daemon/store.ts
|
|
7817
|
-
import { readdirSync as readdirSync2, readFileSync as readFileSync8, rmSync, statSync as statSync3 } from "node:fs";
|
|
7843
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync8, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
7818
7844
|
import { join as join4 } from "node:path";
|
|
7819
7845
|
function credentialsKey(url, name2) {
|
|
7820
7846
|
return `${url.replace(/\/+$/, "")}#${name2}`;
|
|
@@ -7915,7 +7941,7 @@ function pruneKeptWorkspaces(configDir, opts) {
|
|
|
7915
7941
|
const removed = [];
|
|
7916
7942
|
for (const entry of doomed) {
|
|
7917
7943
|
try {
|
|
7918
|
-
|
|
7944
|
+
rmSync2(entry.path, { recursive: true, force: true });
|
|
7919
7945
|
removed.push(entry);
|
|
7920
7946
|
} catch {
|
|
7921
7947
|
}
|
|
@@ -8013,7 +8039,7 @@ async function runDaemon(opts) {
|
|
|
8013
8039
|
sweepKeptWorkspaces();
|
|
8014
8040
|
const settleWorkspace = (workspace, keep, marker) => {
|
|
8015
8041
|
if (!keep) {
|
|
8016
|
-
|
|
8042
|
+
rmSync3(workspace, { recursive: true, force: true });
|
|
8017
8043
|
return;
|
|
8018
8044
|
}
|
|
8019
8045
|
if (!existsSync5(workspace)) return;
|
|
@@ -8087,13 +8113,19 @@ async function runDaemon(opts) {
|
|
|
8087
8113
|
{
|
|
8088
8114
|
finish: async (run, status, error, judgment) => {
|
|
8089
8115
|
const summary = run.renderer?.summary();
|
|
8090
|
-
await client2.finishRun(run.runId, {
|
|
8116
|
+
const ended = await client2.finishRun(run.runId, {
|
|
8091
8117
|
status,
|
|
8092
8118
|
...error ? { error } : {},
|
|
8093
8119
|
...judgment,
|
|
8094
8120
|
usage: summary?.usage ?? { cost_source: "none" },
|
|
8095
|
-
...summary?.providerSessionId ? { provider_session_id: summary.providerSessionId } : {}
|
|
8121
|
+
...summary?.providerSessionId ? { provider_session_id: summary.providerSessionId } : {},
|
|
8122
|
+
...summary?.numTurns !== void 0 ? { turn_count: summary.numTurns } : {},
|
|
8123
|
+
// The whole conversation's turns, which for a resumed run is
|
|
8124
|
+
// more than this run's own: the size guard reads this one.
|
|
8125
|
+
...summary?.numTurns !== void 0 ? { conversation_turn_count: run.priorTurns + summary.numTurns } : {},
|
|
8126
|
+
workspace_path: run.workspace
|
|
8096
8127
|
});
|
|
8128
|
+
if (ended?.resume_expires_at) run.keepForResume = true;
|
|
8097
8129
|
},
|
|
8098
8130
|
release: (run, { keep, outcome }) => {
|
|
8099
8131
|
if (run.timeout) clearTimeout(run.timeout);
|
|
@@ -8122,7 +8154,11 @@ async function runDaemon(opts) {
|
|
|
8122
8154
|
},
|
|
8123
8155
|
log
|
|
8124
8156
|
},
|
|
8125
|
-
{
|
|
8157
|
+
{
|
|
8158
|
+
// A workspace the server retained for a resume is kept whatever the
|
|
8159
|
+
// --keep-workspaces mode says; pruning still bounds it by age/count.
|
|
8160
|
+
keep: (outcome, run) => run.keepForResume || keepWorkspace(opts.keepWorkspaces, outcome)
|
|
8161
|
+
}
|
|
8126
8162
|
);
|
|
8127
8163
|
for (const orphan of loadDaemonState(statePath)) {
|
|
8128
8164
|
if (pidAlive(orphan.pid)) {
|
|
@@ -8168,7 +8204,13 @@ async function runDaemon(opts) {
|
|
|
8168
8204
|
const launch = async (assignment) => {
|
|
8169
8205
|
const runId = assignment.run.id;
|
|
8170
8206
|
if (table2.has(runId)) return;
|
|
8171
|
-
const
|
|
8207
|
+
const resume = assignment.resume && existsSync5(assignment.resume.workspace_path) ? assignment.resume : null;
|
|
8208
|
+
if (assignment.resume && !resume) {
|
|
8209
|
+
log(
|
|
8210
|
+
`run ${runId}: resume workspace ${assignment.resume.workspace_path} is gone; launching fresh`
|
|
8211
|
+
);
|
|
8212
|
+
}
|
|
8213
|
+
const workspace = resume ? resume.workspace_path : join5(workspacesDir(opts.configDir), runId);
|
|
8172
8214
|
const issueLabel = assignment.run.issue_ref ? `${assignment.run.issue_ref.project_name}/${assignment.run.issue_ref.number}` : void 0;
|
|
8173
8215
|
const run = {
|
|
8174
8216
|
runId,
|
|
@@ -8178,6 +8220,8 @@ async function runDaemon(opts) {
|
|
|
8178
8220
|
timedOut: false,
|
|
8179
8221
|
settled: false,
|
|
8180
8222
|
keyFingerprint: assignment.run_key.slice(0, 14),
|
|
8223
|
+
priorTurns: resume?.prior_turn_count ?? 0,
|
|
8224
|
+
keepForResume: false,
|
|
8181
8225
|
batcher: new LogBatcher(
|
|
8182
8226
|
(chunk, seq) => client2.appendRunLog(runId, { chunk, seq }).then(() => {
|
|
8183
8227
|
}),
|
|
@@ -8187,26 +8231,32 @@ async function runDaemon(opts) {
|
|
|
8187
8231
|
run.flush = () => run.batcher.flush();
|
|
8188
8232
|
table2.track(run);
|
|
8189
8233
|
log(
|
|
8190
|
-
`run ${runId} assigned (issue ${issueLabel ?? assignment.run.issue_id}); materializing workspace`
|
|
8234
|
+
resume ? `run ${runId} assigned (issue ${issueLabel ?? assignment.run.issue_id}); resuming run ${resume.previous_run_id} in ${workspace}` : `run ${runId} assigned (issue ${issueLabel ?? assignment.run.issue_id}); materializing workspace`
|
|
8191
8235
|
);
|
|
8192
8236
|
try {
|
|
8193
|
-
|
|
8194
|
-
|
|
8237
|
+
if (!resume) {
|
|
8238
|
+
rmSync3(workspace, { recursive: true, force: true });
|
|
8239
|
+
mkdirSync4(workspace, { recursive: true });
|
|
8240
|
+
} else {
|
|
8241
|
+
rmSync3(join5(workspace, "kept.json"), { force: true });
|
|
8242
|
+
}
|
|
8195
8243
|
writeFileSync3(join5(workspace, "prompt.md"), `${assignment.prompt}
|
|
8196
8244
|
`);
|
|
8197
|
-
|
|
8198
|
-
for (const
|
|
8199
|
-
const
|
|
8200
|
-
|
|
8201
|
-
|
|
8245
|
+
if (!resume)
|
|
8246
|
+
for (const skill of assignment.bundle.skills) {
|
|
8247
|
+
for (const file of skill.files) {
|
|
8248
|
+
const target = join5(workspace, "skills", skill.name, file.path);
|
|
8249
|
+
mkdirSync4(dirname4(target), { recursive: true });
|
|
8250
|
+
writeFileSync3(target, file.content);
|
|
8251
|
+
}
|
|
8202
8252
|
}
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8253
|
+
if (!resume)
|
|
8254
|
+
writeFileSync3(
|
|
8255
|
+
join5(workspace, "repos.json"),
|
|
8256
|
+
`${JSON.stringify(assignment.bundle.repos, null, 2)}
|
|
8207
8257
|
`
|
|
8208
|
-
|
|
8209
|
-
for (const repo of assignment.bundle.repos) {
|
|
8258
|
+
);
|
|
8259
|
+
for (const repo of resume ? [] : assignment.bundle.repos) {
|
|
8210
8260
|
if (run.settled) return table2.cleanup(run);
|
|
8211
8261
|
const args = [
|
|
8212
8262
|
"clone",
|
|
@@ -8238,7 +8288,8 @@ async function runDaemon(opts) {
|
|
|
8238
8288
|
workspace,
|
|
8239
8289
|
promptFile: join5(workspace, "prompt.md"),
|
|
8240
8290
|
prompt: assignment.prompt,
|
|
8241
|
-
model: assignment.run.model
|
|
8291
|
+
model: assignment.run.model,
|
|
8292
|
+
...resume ? { resumeSessionId: resume.provider_session_id } : {}
|
|
8242
8293
|
};
|
|
8243
8294
|
const invocation = buildHarnessInvocation(
|
|
8244
8295
|
{ harness: opts.harness, command: opts.command },
|
|
@@ -8248,7 +8299,8 @@ async function runDaemon(opts) {
|
|
|
8248
8299
|
formatLaunchBanner(invocation, harnessInput, {
|
|
8249
8300
|
harness: opts.harness,
|
|
8250
8301
|
timeoutMinutes: assignment.timeout_minutes,
|
|
8251
|
-
cliVersion: DAEMON_VERSION
|
|
8302
|
+
cliVersion: DAEMON_VERSION,
|
|
8303
|
+
...resume ? { resumedFromRunId: resume.previous_run_id } : {}
|
|
8252
8304
|
})
|
|
8253
8305
|
);
|
|
8254
8306
|
const child = spawn2(invocation.file, invocation.args, {
|
|
@@ -8461,6 +8513,29 @@ function runGit(args, cwd, batcher) {
|
|
|
8461
8513
|
}
|
|
8462
8514
|
|
|
8463
8515
|
// src/commands/runners.ts
|
|
8516
|
+
function parseBoolean(value) {
|
|
8517
|
+
if (value === "true") return true;
|
|
8518
|
+
if (value === "false") return false;
|
|
8519
|
+
throw new InvalidArgumentError("must be true or false");
|
|
8520
|
+
}
|
|
8521
|
+
function boundedInteger(min, max) {
|
|
8522
|
+
return (value) => {
|
|
8523
|
+
if (!/^\d+$/.test(value))
|
|
8524
|
+
throw new InvalidArgumentError(`must be an integer between ${min} and ${max}`);
|
|
8525
|
+
const parsed = Number(value);
|
|
8526
|
+
if (!Number.isSafeInteger(parsed) || parsed < min || parsed > max) {
|
|
8527
|
+
throw new InvalidArgumentError(`must be an integer between ${min} and ${max}`);
|
|
8528
|
+
}
|
|
8529
|
+
return parsed;
|
|
8530
|
+
};
|
|
8531
|
+
}
|
|
8532
|
+
function resumeCost(value) {
|
|
8533
|
+
const parsed = Number(value);
|
|
8534
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 1e3) {
|
|
8535
|
+
throw new InvalidArgumentError("must be greater than 0 and at most 1000");
|
|
8536
|
+
}
|
|
8537
|
+
return parsed;
|
|
8538
|
+
}
|
|
8464
8539
|
function printTierTable(runner) {
|
|
8465
8540
|
if (!runner.tier_models && !runner.tiers) {
|
|
8466
8541
|
console.log("tiers: don't apply to this runner (fixed configuration)");
|
|
@@ -8527,6 +8602,12 @@ function register8(program3) {
|
|
|
8527
8602
|
if (runner.type !== "local") {
|
|
8528
8603
|
console.log(`api key: ${runner.has_api_key ? "set (write-only)" : "missing"}`);
|
|
8529
8604
|
}
|
|
8605
|
+
console.log(
|
|
8606
|
+
`resume awaiting sessions: ${runner.resume_enabled ?? false ? "enabled" : "disabled"} window: ${runner.resume_window_hours ?? DEFAULT_RESUME_WINDOW_HOURS}h (staged; runtime continuation unavailable)`
|
|
8607
|
+
);
|
|
8608
|
+
console.log(
|
|
8609
|
+
`resume limits: ${runner.resume_max_turns ?? DEFAULT_RESUME_MAX_TURNS} local turns ${(runner.resume_max_tokens ?? DEFAULT_RESUME_MAX_TOKENS).toLocaleString()} managed tokens $${runner.resume_max_cost_usd ?? DEFAULT_RESUME_MAX_COST_USD} managed cost`
|
|
8610
|
+
);
|
|
8530
8611
|
if (runner.budget) {
|
|
8531
8612
|
const b = runner.budget;
|
|
8532
8613
|
const parts = [];
|
|
@@ -8540,6 +8621,39 @@ function register8(program3) {
|
|
|
8540
8621
|
printTierTable(runner);
|
|
8541
8622
|
}
|
|
8542
8623
|
);
|
|
8624
|
+
withCommon(
|
|
8625
|
+
runners.command("edit <name>").description("Edit staged awaiting-session continuation policy (runtime unavailable)").option(
|
|
8626
|
+
"--resume-enabled <true|false>",
|
|
8627
|
+
"stage opt-in policy (enabling is unavailable until provider support ships)",
|
|
8628
|
+
parseBoolean
|
|
8629
|
+
).option("--resume-window-hours <n>", "continuation window (1-168)", boundedInteger(1, 168)).option(
|
|
8630
|
+
"--resume-max-turns <n>",
|
|
8631
|
+
"local conversation turn limit (1-1000)",
|
|
8632
|
+
boundedInteger(1, 1e3)
|
|
8633
|
+
).option(
|
|
8634
|
+
"--resume-max-tokens <n>",
|
|
8635
|
+
"managed conversation token limit (1-10000000)",
|
|
8636
|
+
boundedInteger(1, 1e7)
|
|
8637
|
+
).option("--resume-max-cost-usd <n>", "managed conversation cost limit (0-1000]", resumeCost)
|
|
8638
|
+
).action(
|
|
8639
|
+
async (ref, opts) => {
|
|
8640
|
+
const patch = {
|
|
8641
|
+
...opts.resumeEnabled !== void 0 ? { resume_enabled: opts.resumeEnabled } : {},
|
|
8642
|
+
...opts.resumeWindowHours !== void 0 ? { resume_window_hours: opts.resumeWindowHours } : {},
|
|
8643
|
+
...opts.resumeMaxTurns !== void 0 ? { resume_max_turns: opts.resumeMaxTurns } : {},
|
|
8644
|
+
...opts.resumeMaxTokens !== void 0 ? { resume_max_tokens: opts.resumeMaxTokens } : {},
|
|
8645
|
+
...opts.resumeMaxCostUsd !== void 0 ? { resume_max_cost_usd: opts.resumeMaxCostUsd } : {}
|
|
8646
|
+
};
|
|
8647
|
+
if (Object.keys(patch).length === 0) die("provide at least one resume setting");
|
|
8648
|
+
const api = client(opts);
|
|
8649
|
+
const runner = await resolveRunner(api, ref);
|
|
8650
|
+
const updated = await api.updateRunner(runner.id, patch);
|
|
8651
|
+
if (opts.json) return printJson(updated);
|
|
8652
|
+
console.log(
|
|
8653
|
+
`updated runner "${updated.name}"; awaiting-session resume is ${updated.resume_enabled ? "enabled" : "disabled"}`
|
|
8654
|
+
);
|
|
8655
|
+
}
|
|
8656
|
+
);
|
|
8543
8657
|
withCommon(
|
|
8544
8658
|
runners.command("tiers <name>").description("Show or edit a runner's tier\u2192model mapping").option("--default <tier>", "set the default tier (used by targets without an explicit tier)").option("--set <tier=model...>", "override a tier with an exact model id (repeatable)").option("--unset <tier...>", "drop an override, falling back to the built-in (repeatable)")
|
|
8545
8659
|
).action(
|
|
@@ -8836,7 +8950,9 @@ function register8(program3) {
|
|
|
8836
8950
|
const api = client(opts);
|
|
8837
8951
|
const run = await api.getRun(id);
|
|
8838
8952
|
if (opts.json) return printJson(run);
|
|
8839
|
-
console.log(
|
|
8953
|
+
console.log(
|
|
8954
|
+
`${run.id} ${run.status} on ${run.runner_name}${run.resumed_from_run_id ? ` \xB7 resumed run ${run.resumed_from_run_id}` : ""}`
|
|
8955
|
+
);
|
|
8840
8956
|
if (run.issue_ref) console.log(`issue: ${issueRef(run.issue_ref)} \u2014 ${run.issue_ref.title}`);
|
|
8841
8957
|
console.log(`tier: ${run.tier} model: ${run.model ?? "(n/a)"}`);
|
|
8842
8958
|
console.log(
|