tines 0.0.265 → 0.0.266
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +182 -49
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3368,6 +3368,9 @@ function useColor() {
|
|
|
3368
3368
|
// ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/index.js
|
|
3369
3369
|
var program = new Command();
|
|
3370
3370
|
|
|
3371
|
+
// src/commands/context.ts
|
|
3372
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
3373
|
+
|
|
3371
3374
|
// src/body-value.ts
|
|
3372
3375
|
import { readFileSync } from "node:fs";
|
|
3373
3376
|
var processStdin = {
|
|
@@ -3432,6 +3435,7 @@ var LABEL_NAME_MAX = 50;
|
|
|
3432
3435
|
var PROMPT_MAX_BYTES = 32 * 1024;
|
|
3433
3436
|
var SKILL_MAX_FILES = 20;
|
|
3434
3437
|
var SKILL_MAX_TOTAL_BYTES = 100 * 1024;
|
|
3438
|
+
var ENV_VALUE_MAX_BYTES = 16 * 1024;
|
|
3435
3439
|
var AGENT_GUIDELINES_NAME = "agent-guidelines";
|
|
3436
3440
|
var JOURNAL_NAME = "journal";
|
|
3437
3441
|
var AGENT_GUIDELINES_BODY = `You are an agent working on a Tines issue over its HTTP API / CLI. Beyond doing the work, leave the workspace smarter than you found it. Four places to write, chosen by who should inherit what you learned:
|
|
@@ -5636,6 +5640,8 @@ function contextItemSummary(item) {
|
|
|
5636
5640
|
return `${item.repo_url}${item.repo_branch ? `#${item.repo_branch}` : ""}`;
|
|
5637
5641
|
case "artifact":
|
|
5638
5642
|
return item.artifact_type ?? "artifact";
|
|
5643
|
+
case "env":
|
|
5644
|
+
return item.secret ? `secret \xB7 set${item.hint ? ` \xB7 ${item.hint}` : ""}` : `= ${item.value ?? ""}`;
|
|
5639
5645
|
}
|
|
5640
5646
|
}
|
|
5641
5647
|
function recurrenceLabel(schedule2) {
|
|
@@ -6234,6 +6240,12 @@ async function resolveScopeFlags(api, opts) {
|
|
|
6234
6240
|
if (opts.label !== void 0) scope.label_id = await resolveLabelFlag(api, opts.label);
|
|
6235
6241
|
return scope;
|
|
6236
6242
|
}
|
|
6243
|
+
function readEnvValue(raw) {
|
|
6244
|
+
if (raw === "-") return readFileSync4(0, "utf8").replace(/\r?\n$/, "");
|
|
6245
|
+
if (raw.startsWith("@@")) return raw.slice(1);
|
|
6246
|
+
if (raw.startsWith("@")) return readFileSync4(raw.slice(1), "utf8").replace(/\r?\n$/, "");
|
|
6247
|
+
return raw;
|
|
6248
|
+
}
|
|
6237
6249
|
function printContextItem(item) {
|
|
6238
6250
|
console.log(`${item.kind} "${item.name}" [${item.id}] v${item.version}`);
|
|
6239
6251
|
if (item.description) console.log(item.description);
|
|
@@ -6256,6 +6268,15 @@ files (seeded at skills/${item.name}/):`);
|
|
|
6256
6268
|
console.log(` ${f.path} (${Buffer.byteLength(f.content, "utf8")} bytes)`);
|
|
6257
6269
|
}
|
|
6258
6270
|
if ((item.files ?? []).length === 0) console.log(" (none)");
|
|
6271
|
+
} else if (item.kind === "env") {
|
|
6272
|
+
if (item.secret) {
|
|
6273
|
+
console.log(`
|
|
6274
|
+
value: (secret, set)${item.hint ? ` hint: ${item.hint}` : ""}`);
|
|
6275
|
+
} else {
|
|
6276
|
+
console.log(`
|
|
6277
|
+
value: ${item.value ?? ""}`);
|
|
6278
|
+
if (item.hint) console.log(`hint: ${item.hint}`);
|
|
6279
|
+
}
|
|
6259
6280
|
} else {
|
|
6260
6281
|
console.log(`
|
|
6261
6282
|
url: ${item.repo_url}`);
|
|
@@ -6277,13 +6298,13 @@ function withScopeFlags(cmd) {
|
|
|
6277
6298
|
}
|
|
6278
6299
|
function register(program3) {
|
|
6279
6300
|
const context2 = program3.command("context").description(
|
|
6280
|
-
"Manage context items (prompts, skills, repo pointers) scoped to projects, states, and issues"
|
|
6301
|
+
"Manage context items (prompts, skills, repo pointers, environment variables) scoped to projects, states, and issues"
|
|
6281
6302
|
);
|
|
6282
6303
|
withList(
|
|
6283
6304
|
withScopeFlags(
|
|
6284
6305
|
context2.command("list").description(
|
|
6285
6306
|
"List context items (scope filters match every item whose scope includes the element)"
|
|
6286
|
-
).option("-k, --kind <kind>", "filter by kind: prompt, skill, or
|
|
6307
|
+
).option("-k, --kind <kind>", "filter by kind: prompt, skill, repo, artifact, or env").option("--exact", "only items whose scope sets exactly the given dimensions").option("-q, --search <text>", "search names and descriptions")
|
|
6287
6308
|
)
|
|
6288
6309
|
).action(
|
|
6289
6310
|
async (opts) => {
|
|
@@ -6327,21 +6348,27 @@ function register(program3) {
|
|
|
6327
6348
|
});
|
|
6328
6349
|
withCommon(
|
|
6329
6350
|
withScopeFlags(
|
|
6330
|
-
context2.command("create").description("Create a context item scoped to a project, state, and/or issue").requiredOption("-k, --kind <kind>", "prompt, skill, or
|
|
6351
|
+
context2.command("create").description("Create a context item scoped to a project, state, and/or issue").requiredOption("-k, --kind <kind>", "prompt, skill, repo, or env").requiredOption(
|
|
6331
6352
|
"-n, --name <name>",
|
|
6332
|
-
"item name (slug-like for skills; the dedup/override key)"
|
|
6353
|
+
"item name (slug-like for skills, the variable name for env; the dedup/override key)"
|
|
6333
6354
|
).option("-d, --description <text>", "one-liner shown in lists").option("--body <md>", "prompt body: inline Markdown or @file (escape a literal @ as @@)").option(
|
|
6334
6355
|
"--file <path>=@<local>",
|
|
6335
6356
|
"skill file: workspace path = local file (repeatable)",
|
|
6336
6357
|
collect,
|
|
6337
6358
|
[]
|
|
6338
|
-
).option("--repo-url <url>", "repo: clone URL").option("--branch <branch>", "repo: branch to check out").option("--dir <dir>", "repo: checkout directory (defaults to the URL's basename)")
|
|
6359
|
+
).option("--repo-url <url>", "repo: clone URL").option("--branch <branch>", "repo: branch to check out").option("--dir <dir>", "repo: checkout directory (defaults to the URL's basename)").option(
|
|
6360
|
+
"--value <v>",
|
|
6361
|
+
"env: the value \u2014 inline, @file, or - for stdin (keeps a secret out of shell history)"
|
|
6362
|
+
).option("--secret", "env: encrypt the value at rest; it is write-only afterwards").option("--hint <text>", 'env: a safe display hint for a secret (e.g. "github_pat_\u2026abcd")')
|
|
6339
6363
|
)
|
|
6340
6364
|
).action(
|
|
6341
6365
|
async (opts) => {
|
|
6342
6366
|
if (opts.kind === "repo" && opts.repoUrl === void 0) {
|
|
6343
6367
|
die("--kind repo needs --repo-url <clone-url> (--url is the API base URL)");
|
|
6344
6368
|
}
|
|
6369
|
+
if (opts.kind === "env" && opts.value === void 0) {
|
|
6370
|
+
die("--kind env needs --value <value|@file|->");
|
|
6371
|
+
}
|
|
6345
6372
|
const api = client(opts);
|
|
6346
6373
|
const scope = await resolveScopeFlags(api, opts);
|
|
6347
6374
|
const body = {
|
|
@@ -6356,6 +6383,9 @@ function register(program3) {
|
|
|
6356
6383
|
if (opts.repoUrl !== void 0) body.repo_url = opts.repoUrl;
|
|
6357
6384
|
if (opts.branch !== void 0) body.repo_branch = opts.branch;
|
|
6358
6385
|
if (opts.dir !== void 0) body.repo_dir = opts.dir;
|
|
6386
|
+
if (opts.value !== void 0) body.value = readEnvValue(opts.value);
|
|
6387
|
+
if (opts.secret) body.secret = true;
|
|
6388
|
+
if (opts.hint !== void 0) body.hint = opts.hint;
|
|
6359
6389
|
const item = await api.createContextItem(body);
|
|
6360
6390
|
if (opts.json) return printJson(item);
|
|
6361
6391
|
console.log(`created ${item.kind} "${item.name}" (${item.id}) \u2014 scope: ${item.scope.label}`);
|
|
@@ -6363,7 +6393,7 @@ function register(program3) {
|
|
|
6363
6393
|
);
|
|
6364
6394
|
withCommon(
|
|
6365
6395
|
withScopeFlags(
|
|
6366
|
-
context2.command("edit <id>").description("Edit a context item: payload, name, description, or scope").option("-n, --name <name>", "rename the item").option("-d, --description <text>", "set the description").option("--body <md>", "prompt body: inline Markdown or @file (escape a literal @ as @@)").option("--file <path>=@<local>", "add or replace a skill file (repeatable)", collect, []).option("--remove-file <path>", "remove a skill file (repeatable)", collect, []).option("--repo-url <url>", "repo: clone URL").option("--branch <branch>", "repo: branch (empty string clears it)").option("--dir <dir>", "repo: checkout directory (empty string restores the URL default)").option(
|
|
6396
|
+
context2.command("edit <id>").description("Edit a context item: payload, name, description, or scope").option("-n, --name <name>", "rename the item").option("-d, --description <text>", "set the description").option("--body <md>", "prompt body: inline Markdown or @file (escape a literal @ as @@)").option("--file <path>=@<local>", "add or replace a skill file (repeatable)", collect, []).option("--remove-file <path>", "remove a skill file (repeatable)", collect, []).option("--repo-url <url>", "repo: clone URL").option("--branch <branch>", "repo: branch (empty string clears it)").option("--dir <dir>", "repo: checkout directory (empty string restores the URL default)").option("--value <v>", "env: replace the value (inline, @file, or - for stdin)").option("--secret", "env: encrypt the stored value (irreversible; delete to make public)").option("--hint <text>", "env: display hint for a secret (empty string clears it)").option(
|
|
6367
6397
|
"--unset <dimension>",
|
|
6368
6398
|
"drop a scope dimension: project, state, issue, or label (repeatable)",
|
|
6369
6399
|
collect,
|
|
@@ -6413,6 +6443,9 @@ function register(program3) {
|
|
|
6413
6443
|
if (opts.repoUrl !== void 0) body.repo_url = opts.repoUrl;
|
|
6414
6444
|
if (opts.branch !== void 0) body.repo_branch = opts.branch === "" ? null : opts.branch;
|
|
6415
6445
|
if (opts.dir !== void 0) body.repo_dir = opts.dir === "" ? null : opts.dir;
|
|
6446
|
+
if (opts.value !== void 0) body.value = readEnvValue(opts.value);
|
|
6447
|
+
if (opts.secret) body.secret = true;
|
|
6448
|
+
if (opts.hint !== void 0) body.hint = opts.hint === "" ? null : opts.hint;
|
|
6416
6449
|
if (Object.keys(body).length === 0) {
|
|
6417
6450
|
die(
|
|
6418
6451
|
"nothing to update: pass payload flags, --name/--description, scope flags, and/or --unset"
|
|
@@ -6459,7 +6492,7 @@ function register(program3) {
|
|
|
6459
6492
|
}
|
|
6460
6493
|
|
|
6461
6494
|
// src/commands/issues.ts
|
|
6462
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as
|
|
6495
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
6463
6496
|
import { basename, dirname as dirname2, join as join2 } from "node:path";
|
|
6464
6497
|
import { createInterface } from "node:readline/promises";
|
|
6465
6498
|
|
|
@@ -7171,7 +7204,7 @@ function readTextSource(source) {
|
|
|
7171
7204
|
if (source.kind === "text-stdin") return readBodyValue("-");
|
|
7172
7205
|
if (source.kind === "text-path") {
|
|
7173
7206
|
try {
|
|
7174
|
-
return
|
|
7207
|
+
return readFileSync5(source.path, "utf8");
|
|
7175
7208
|
} catch (err) {
|
|
7176
7209
|
die(`cannot read ${source.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7177
7210
|
}
|
|
@@ -7189,7 +7222,7 @@ function walkFolder(dir) {
|
|
|
7189
7222
|
files2.push({
|
|
7190
7223
|
path: nextRel,
|
|
7191
7224
|
contentType: sniffContentType(entry.name),
|
|
7192
|
-
bytes:
|
|
7225
|
+
bytes: readFileSync5(nextAbs)
|
|
7193
7226
|
});
|
|
7194
7227
|
}
|
|
7195
7228
|
}
|
|
@@ -7644,6 +7677,9 @@ function register2(program3) {
|
|
|
7644
7677
|
console.log(`
|
|
7645
7678
|
skills: ${context2.skills.map((s) => s.name).join(", ")}`);
|
|
7646
7679
|
}
|
|
7680
|
+
for (const e of context2.env ?? []) {
|
|
7681
|
+
console.log(`env: ${e.name}${e.secret ? " (secret)" : ""}`);
|
|
7682
|
+
}
|
|
7647
7683
|
for (const repo of context2.repos) {
|
|
7648
7684
|
console.log(
|
|
7649
7685
|
`repo: ${repo.name} ${repo.url}${repo.branch ? `#${repo.branch}` : ""} \u2192 ${repo.dir}/`
|
|
@@ -7827,10 +7863,10 @@ files (v${artifact.current_version.version}):`);
|
|
|
7827
7863
|
let bytes2;
|
|
7828
7864
|
if (fromPath === null) {
|
|
7829
7865
|
if (process.stdin.isTTY) die('"-" reads the file from stdin, but stdin is a terminal');
|
|
7830
|
-
bytes2 =
|
|
7866
|
+
bytes2 = readFileSync5(0);
|
|
7831
7867
|
} else {
|
|
7832
7868
|
try {
|
|
7833
|
-
bytes2 =
|
|
7869
|
+
bytes2 = readFileSync5(fromPath);
|
|
7834
7870
|
} catch (err) {
|
|
7835
7871
|
die(`cannot read ${fromPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7836
7872
|
}
|
|
@@ -8285,13 +8321,13 @@ function register4(program3) {
|
|
|
8285
8321
|
}
|
|
8286
8322
|
|
|
8287
8323
|
// src/commands/login.ts
|
|
8288
|
-
import { readFileSync as
|
|
8324
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
8289
8325
|
function maskKey(key) {
|
|
8290
8326
|
return `${key.slice(0, 14)}\u2026`;
|
|
8291
8327
|
}
|
|
8292
8328
|
function readKeyArg(value) {
|
|
8293
8329
|
if (value !== "-") return value;
|
|
8294
|
-
const key =
|
|
8330
|
+
const key = readFileSync6(0, "utf8").trim();
|
|
8295
8331
|
if (!key) die("no API key on stdin");
|
|
8296
8332
|
return key;
|
|
8297
8333
|
}
|
|
@@ -8731,7 +8767,7 @@ import {
|
|
|
8731
8767
|
createWriteStream,
|
|
8732
8768
|
existsSync as existsSync5,
|
|
8733
8769
|
mkdirSync as mkdirSync4,
|
|
8734
|
-
readFileSync as
|
|
8770
|
+
readFileSync as readFileSync10,
|
|
8735
8771
|
realpathSync,
|
|
8736
8772
|
rmSync as rmSync3,
|
|
8737
8773
|
statSync as statSync4,
|
|
@@ -8743,11 +8779,11 @@ import { dirname as dirname4, join as join6 } from "node:path";
|
|
|
8743
8779
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
8744
8780
|
|
|
8745
8781
|
// src/version.ts
|
|
8746
|
-
import { readFileSync as
|
|
8782
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
8747
8783
|
function cliVersion() {
|
|
8748
8784
|
try {
|
|
8749
8785
|
const manifest = new URL("../package.json", import.meta.url);
|
|
8750
|
-
return JSON.parse(
|
|
8786
|
+
return JSON.parse(readFileSync7(manifest, "utf8")).version ?? "0.0.0-unknown";
|
|
8751
8787
|
} catch {
|
|
8752
8788
|
return "0.0.0-unknown";
|
|
8753
8789
|
}
|
|
@@ -9761,7 +9797,7 @@ var EffortCapabilityRefresher = class {
|
|
|
9761
9797
|
|
|
9762
9798
|
// src/daemon/cli-refresh.ts
|
|
9763
9799
|
import { spawn as spawn2 } from "node:child_process";
|
|
9764
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as
|
|
9800
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync8 } from "node:fs";
|
|
9765
9801
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
9766
9802
|
|
|
9767
9803
|
// src/daemon/support.ts
|
|
@@ -9946,10 +9982,12 @@ var LogBatcher = class {
|
|
|
9946
9982
|
constructor(send, opts = {}) {
|
|
9947
9983
|
this.send = send;
|
|
9948
9984
|
this.opts = opts;
|
|
9985
|
+
this.redactor = new SecretRedactor(opts.redact ?? []);
|
|
9949
9986
|
}
|
|
9950
9987
|
send;
|
|
9951
9988
|
opts;
|
|
9952
9989
|
buffer = "";
|
|
9990
|
+
redactor;
|
|
9953
9991
|
timer = null;
|
|
9954
9992
|
sending = Promise.resolve();
|
|
9955
9993
|
seq = 0;
|
|
@@ -9957,6 +9995,7 @@ var LogBatcher = class {
|
|
|
9957
9995
|
pending = [];
|
|
9958
9996
|
append(text4) {
|
|
9959
9997
|
if (!text4) return;
|
|
9998
|
+
text4 = this.redactor.write(text4);
|
|
9960
9999
|
this.buffer += text4;
|
|
9961
10000
|
if (Buffer.byteLength(this.buffer, "utf8") >= (this.opts.maxBytes ?? 32 * 1024)) {
|
|
9962
10001
|
void this.flush();
|
|
@@ -9965,6 +10004,11 @@ var LogBatcher = class {
|
|
|
9965
10004
|
this.timer.unref?.();
|
|
9966
10005
|
}
|
|
9967
10006
|
}
|
|
10007
|
+
/** Release an incomplete final match only after the producer has ended. */
|
|
10008
|
+
finish() {
|
|
10009
|
+
this.buffer += this.redactor.end();
|
|
10010
|
+
return this.flush();
|
|
10011
|
+
}
|
|
9968
10012
|
/** Sends whatever is buffered now; resolves when every send so far settled. */
|
|
9969
10013
|
flush() {
|
|
9970
10014
|
if (this.timer) {
|
|
@@ -10077,15 +10121,68 @@ function pathWithin(path2, dir) {
|
|
|
10077
10121
|
function buildSpawnEnv(base, opts) {
|
|
10078
10122
|
const env = {
|
|
10079
10123
|
...base,
|
|
10124
|
+
...Object.fromEntries((opts.extra ?? []).map((e) => [e.name, e.value])),
|
|
10080
10125
|
TINES_API_KEY: opts.apiKey,
|
|
10081
10126
|
// Own canonicalization at the final child-process boundary too. This
|
|
10082
10127
|
// keeps the spawned CLI safe even if a future daemon call site passes
|
|
10083
10128
|
// the original --url value instead of its normalized local variable.
|
|
10084
10129
|
TINES_API_URL: opts.apiUrl.replace(/\/+$/, "")
|
|
10085
10130
|
};
|
|
10131
|
+
if (opts.extra?.some((e) => e.name === "PATH")) env.PATH = base.PATH;
|
|
10086
10132
|
if (opts.binDir) env.PATH = base.PATH ? `${opts.binDir}${delimiter}${base.PATH}` : opts.binDir;
|
|
10087
10133
|
return env;
|
|
10088
10134
|
}
|
|
10135
|
+
function secretForms(secrets) {
|
|
10136
|
+
return [
|
|
10137
|
+
...new Set(
|
|
10138
|
+
secrets.filter(Boolean).flatMap((secret) => [secret, JSON.stringify(secret).slice(1, -1)])
|
|
10139
|
+
)
|
|
10140
|
+
].sort((a, b) => b.length - a.length);
|
|
10141
|
+
}
|
|
10142
|
+
function secretPattern(forms) {
|
|
10143
|
+
return forms.length ? new RegExp(forms.map((form) => form.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g") : null;
|
|
10144
|
+
}
|
|
10145
|
+
function redactSecrets(text4, secrets) {
|
|
10146
|
+
const pattern = secretPattern(secretForms(secrets));
|
|
10147
|
+
return pattern ? text4.replace(pattern, "***") : text4;
|
|
10148
|
+
}
|
|
10149
|
+
var SecretRedactor = class {
|
|
10150
|
+
pending = "";
|
|
10151
|
+
forms;
|
|
10152
|
+
pattern;
|
|
10153
|
+
constructor(secrets) {
|
|
10154
|
+
this.forms = secretForms(secrets);
|
|
10155
|
+
this.pattern = secretPattern(this.forms);
|
|
10156
|
+
}
|
|
10157
|
+
write(chunk) {
|
|
10158
|
+
if (!this.pattern) return chunk;
|
|
10159
|
+
const text4 = this.pending + chunk;
|
|
10160
|
+
let cut = text4.length;
|
|
10161
|
+
for (const form of this.forms) {
|
|
10162
|
+
for (let size = Math.min(form.length - 1, text4.length); size > text4.length - cut; size--) {
|
|
10163
|
+
if (text4.endsWith(form.slice(0, size))) {
|
|
10164
|
+
cut = text4.length - size;
|
|
10165
|
+
break;
|
|
10166
|
+
}
|
|
10167
|
+
}
|
|
10168
|
+
}
|
|
10169
|
+
this.pattern.lastIndex = 0;
|
|
10170
|
+
for (let match; match = this.pattern.exec(text4); ) {
|
|
10171
|
+
if (match.index >= cut) break;
|
|
10172
|
+
if (this.pattern.lastIndex > cut) {
|
|
10173
|
+
cut = match.index;
|
|
10174
|
+
break;
|
|
10175
|
+
}
|
|
10176
|
+
}
|
|
10177
|
+
this.pending = text4.slice(cut);
|
|
10178
|
+
return text4.slice(0, cut).replace(this.pattern, "***");
|
|
10179
|
+
}
|
|
10180
|
+
end() {
|
|
10181
|
+
const text4 = this.pending;
|
|
10182
|
+
this.pending = "";
|
|
10183
|
+
return this.pattern ? text4.replace(this.pattern, "***") : text4;
|
|
10184
|
+
}
|
|
10185
|
+
};
|
|
10089
10186
|
|
|
10090
10187
|
// src/daemon/cli-refresh.ts
|
|
10091
10188
|
var PACKAGE = "tines";
|
|
@@ -10098,7 +10195,7 @@ function binDirOf(prefix) {
|
|
|
10098
10195
|
}
|
|
10099
10196
|
function installedVersion(prefix) {
|
|
10100
10197
|
try {
|
|
10101
|
-
const pkg =
|
|
10198
|
+
const pkg = readFileSync8(join4(prefix, "node_modules", PACKAGE, "package.json"), "utf8");
|
|
10102
10199
|
const version = JSON.parse(pkg).version;
|
|
10103
10200
|
return typeof version === "string" ? version : null;
|
|
10104
10201
|
} catch {
|
|
@@ -10178,7 +10275,7 @@ function message(err) {
|
|
|
10178
10275
|
import { arch, hostname, platform } from "node:os";
|
|
10179
10276
|
|
|
10180
10277
|
// src/daemon/store.ts
|
|
10181
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
10278
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
10182
10279
|
import { join as join5 } from "node:path";
|
|
10183
10280
|
function credentialsKey(url, name2) {
|
|
10184
10281
|
return `${url.replace(/\/+$/, "")}#${name2}`;
|
|
@@ -10238,10 +10335,10 @@ function saveDaemonDeclines(path2, runIds) {
|
|
|
10238
10335
|
}
|
|
10239
10336
|
function processStartTimeMs(pid) {
|
|
10240
10337
|
try {
|
|
10241
|
-
const stat2 =
|
|
10338
|
+
const stat2 = readFileSync9(`/proc/${pid}/stat`, "utf8");
|
|
10242
10339
|
const afterComm = stat2.slice(stat2.lastIndexOf(")") + 2).split(" ");
|
|
10243
10340
|
const startTicks = Number(afterComm[19]);
|
|
10244
|
-
const btimeLine =
|
|
10341
|
+
const btimeLine = readFileSync9("/proc/stat", "utf8").split("\n").find((line) => line.startsWith("btime "));
|
|
10245
10342
|
const btime = Number(btimeLine?.slice("btime ".length));
|
|
10246
10343
|
if (!Number.isFinite(startTicks) || !Number.isFinite(btime)) return null;
|
|
10247
10344
|
return btime * 1e3 + startTicks / 100 * 1e3;
|
|
@@ -10368,7 +10465,7 @@ async function uploadRawLog(run3) {
|
|
|
10368
10465
|
await new Promise((resolve2) => run3.rawSpool?.end(resolve2) ?? resolve2());
|
|
10369
10466
|
const size = statSync4(path2).size;
|
|
10370
10467
|
if (size > 0) {
|
|
10371
|
-
let body =
|
|
10468
|
+
let body = readFileSync10(path2);
|
|
10372
10469
|
if (body.byteLength > RUN_LOG_RAW_MAX_BYTES) {
|
|
10373
10470
|
const marker = Buffer.from(
|
|
10374
10471
|
`{"type":"tines_truncated","dropped_bytes":${body.byteLength - RUN_LOG_RAW_MAX_BYTES}}
|
|
@@ -10535,7 +10632,7 @@ async function runDaemon(opts) {
|
|
|
10535
10632
|
};
|
|
10536
10633
|
run3.batcher.append(`[usage] ${JSON.stringify(accounting)}
|
|
10537
10634
|
`);
|
|
10538
|
-
await run3.batcher.
|
|
10635
|
+
await run3.batcher.finish();
|
|
10539
10636
|
const ended = await client2.finishRun(run3.runId, {
|
|
10540
10637
|
status,
|
|
10541
10638
|
...run3.effortEvidence ? { effort_application: run3.effortEvidence } : {},
|
|
@@ -10636,6 +10733,7 @@ async function runDaemon(opts) {
|
|
|
10636
10733
|
const effortRefresher = new EffortCapabilityRefresher(opts.harness, DAEMON_VERSION);
|
|
10637
10734
|
let effortCapabilities = await effortRefresher.get();
|
|
10638
10735
|
const launch = async (assignment) => {
|
|
10736
|
+
const secretEnvValues = (assignment.env ?? []).filter((e) => e.secret).map((e) => e.value);
|
|
10639
10737
|
const runId = assignment.run.id;
|
|
10640
10738
|
if (table2.has(runId)) return;
|
|
10641
10739
|
const resume = assignment.resume && existsSync5(assignment.resume.workspace_path) ? assignment.resume : null;
|
|
@@ -10659,10 +10757,13 @@ async function runDaemon(opts) {
|
|
|
10659
10757
|
batcher: new LogBatcher(
|
|
10660
10758
|
(chunk, seq) => client2.appendRunLog(runId, { chunk, seq }).then(() => {
|
|
10661
10759
|
}),
|
|
10662
|
-
{
|
|
10760
|
+
{
|
|
10761
|
+
onError: (err) => log(`log append for run ${runId} failed: ${message2(err)}`),
|
|
10762
|
+
...secretEnvValues.length > 0 ? { redact: secretEnvValues } : {}
|
|
10763
|
+
}
|
|
10663
10764
|
)
|
|
10664
10765
|
};
|
|
10665
|
-
run3.flush = () => run3.batcher.
|
|
10766
|
+
run3.flush = () => run3.batcher.finish();
|
|
10666
10767
|
table2.track(run3);
|
|
10667
10768
|
if (assignment.effort) effortCapabilities = await effortRefresher.get(true);
|
|
10668
10769
|
const effortRejection = assignmentEffortRejection(assignment, effortCapabilities, opts.harness);
|
|
@@ -10760,8 +10861,15 @@ async function runDaemon(opts) {
|
|
|
10760
10861
|
const spawnEnv = buildSpawnEnv(process.env, {
|
|
10761
10862
|
binDir: cli.binDir,
|
|
10762
10863
|
apiKey: assignment.run_key,
|
|
10763
|
-
apiUrl: baseUrl
|
|
10864
|
+
apiUrl: baseUrl,
|
|
10865
|
+
...assignment.env ? { extra: assignment.env } : {}
|
|
10764
10866
|
});
|
|
10867
|
+
if (assignment.env?.length) {
|
|
10868
|
+
run3.batcher.append(
|
|
10869
|
+
`[env] ${assignment.env.length} environment variable(s) set: ${assignment.env.map((e) => `${e.name}${e.secret ? " (secret)" : ""}`).join(", ")}
|
|
10870
|
+
`
|
|
10871
|
+
);
|
|
10872
|
+
}
|
|
10765
10873
|
if (opts.harness === "codex") run3.codexHome = resolveCodexHome(spawnEnv, workspace);
|
|
10766
10874
|
const child = spawn3(invocation.file, invocation.args, {
|
|
10767
10875
|
cwd: workspace,
|
|
@@ -10800,14 +10908,20 @@ async function runDaemon(opts) {
|
|
|
10800
10908
|
run3.rawSpool = createWriteStream(spoolPath);
|
|
10801
10909
|
run3.rawUpload = (body) => client2.putRunLogRaw(runId, body);
|
|
10802
10910
|
const decoder = new StringDecoder2("utf8");
|
|
10911
|
+
const rawRedactor = new SecretRedactor(secretEnvValues);
|
|
10803
10912
|
run3.drain = () => {
|
|
10804
10913
|
const trailing = decoder.end();
|
|
10805
|
-
if (trailing)
|
|
10914
|
+
if (trailing) {
|
|
10915
|
+
run3.rawSpool?.write(rawRedactor.write(trailing));
|
|
10916
|
+
renderer.write(trailing);
|
|
10917
|
+
}
|
|
10918
|
+
run3.rawSpool?.write(rawRedactor.end());
|
|
10806
10919
|
renderer.finish();
|
|
10807
10920
|
};
|
|
10808
10921
|
child.stdout?.on("data", (data) => {
|
|
10809
|
-
|
|
10810
|
-
|
|
10922
|
+
const text4 = decoder.write(data);
|
|
10923
|
+
run3.rawSpool?.write(rawRedactor.write(text4));
|
|
10924
|
+
renderer.write(text4);
|
|
10811
10925
|
});
|
|
10812
10926
|
} else if (opts.harness === "codex") {
|
|
10813
10927
|
const renderer = new CodexStreamRenderer((line) => run3.batcher.append(line));
|
|
@@ -10820,11 +10934,26 @@ async function runDaemon(opts) {
|
|
|
10820
10934
|
};
|
|
10821
10935
|
child.stdout?.on("data", (data) => renderer.write(decoder.write(data)));
|
|
10822
10936
|
} else {
|
|
10823
|
-
|
|
10937
|
+
const decoder = new StringDecoder2("utf8");
|
|
10938
|
+
const redactor = new SecretRedactor(secretEnvValues);
|
|
10939
|
+
child.stdout?.on(
|
|
10940
|
+
"data",
|
|
10941
|
+
(data) => run3.batcher.append(redactor.write(decoder.write(data)))
|
|
10942
|
+
);
|
|
10943
|
+
run3.drain = () => {
|
|
10944
|
+
run3.batcher.append(redactor.write(decoder.end()) + redactor.end());
|
|
10945
|
+
};
|
|
10824
10946
|
}
|
|
10947
|
+
const stderrDecoder = new StringDecoder2("utf8");
|
|
10948
|
+
const stderrRedactor = new SecretRedactor(secretEnvValues);
|
|
10949
|
+
const drainStdout = run3.drain;
|
|
10950
|
+
run3.drain = () => {
|
|
10951
|
+
drainStdout?.();
|
|
10952
|
+
run3.batcher.append(stderrRedactor.write(stderrDecoder.end()) + stderrRedactor.end());
|
|
10953
|
+
};
|
|
10825
10954
|
child.stderr?.on("data", (data) => {
|
|
10826
|
-
const text4 =
|
|
10827
|
-
run3.batcher.append(text4);
|
|
10955
|
+
const text4 = stderrDecoder.write(data);
|
|
10956
|
+
run3.batcher.append(stderrRedactor.write(text4));
|
|
10828
10957
|
run3.limiter?.noteStderr(text4);
|
|
10829
10958
|
});
|
|
10830
10959
|
run3.timeout = setTimeout(() => {
|
|
@@ -10835,7 +10964,7 @@ async function runDaemon(opts) {
|
|
|
10835
10964
|
if (child.pid) setTimeout(() => killTree(child.pid, "SIGKILL"), 5e3).unref?.();
|
|
10836
10965
|
}, assignment.timeout_minutes * 6e4);
|
|
10837
10966
|
child.on("error", (err) => {
|
|
10838
|
-
const reason = `failed to launch harness: ${message2(err)}
|
|
10967
|
+
const reason = redactSecrets(`failed to launch harness: ${message2(err)}`, secretEnvValues);
|
|
10839
10968
|
if (assignment.effort)
|
|
10840
10969
|
run3.effortEvidence = {
|
|
10841
10970
|
status: "rejected",
|
|
@@ -10870,18 +10999,16 @@ async function runDaemon(opts) {
|
|
|
10870
10999
|
const limited = signal ? null : run3.limiter?.signal() ?? null;
|
|
10871
11000
|
const providerError = signal ? null : run3.limiter?.providerError() ?? null;
|
|
10872
11001
|
if (limited) {
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
void table2.finishAndCleanup(run3, "failed", `rate limited: ${limited.detail}`, {
|
|
11002
|
+
const detail = redactSecrets(limited.detail, secretEnvValues);
|
|
11003
|
+
log(`run ${runId}: harness rate limited (${detail}); reporting without a strike`);
|
|
11004
|
+
void table2.finishAndCleanup(run3, "failed", `rate limited: ${detail}`, {
|
|
10877
11005
|
judgment: "rate_limited",
|
|
10878
11006
|
...limited.resumeAt !== null ? { resume_at: limited.resumeAt } : {}
|
|
10879
11007
|
});
|
|
10880
11008
|
} else if (providerError) {
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
void table2.finishAndCleanup(run3, "failed", `provider error: ${providerError.detail}`, {
|
|
11009
|
+
const detail = redactSecrets(providerError.detail, secretEnvValues);
|
|
11010
|
+
log(`run ${runId}: transient provider error (${detail}); reporting without a strike`);
|
|
11011
|
+
void table2.finishAndCleanup(run3, "failed", `provider error: ${detail}`, {
|
|
10885
11012
|
judgment: "interrupted"
|
|
10886
11013
|
});
|
|
10887
11014
|
} else {
|
|
@@ -10894,7 +11021,11 @@ async function runDaemon(opts) {
|
|
|
10894
11021
|
}
|
|
10895
11022
|
});
|
|
10896
11023
|
} catch (err) {
|
|
10897
|
-
void table2.finishAndCleanup(
|
|
11024
|
+
void table2.finishAndCleanup(
|
|
11025
|
+
run3,
|
|
11026
|
+
"failed",
|
|
11027
|
+
redactSecrets(`workspace setup failed: ${message2(err)}`, secretEnvValues)
|
|
11028
|
+
);
|
|
10898
11029
|
}
|
|
10899
11030
|
};
|
|
10900
11031
|
const shutdown = async () => {
|
|
@@ -10934,6 +11065,8 @@ async function runDaemon(opts) {
|
|
|
10934
11065
|
},
|
|
10935
11066
|
...declinedAssignments.size > 0 ? { declined_assignments: [...declinedAssignments].slice(0, 100) } : {},
|
|
10936
11067
|
...effortCapabilities ? { effort_capabilities: effortCapabilities } : {},
|
|
11068
|
+
// Capability: this daemon merges assignment.env into the harness environment.
|
|
11069
|
+
env_delivery: 1,
|
|
10937
11070
|
// Stated on every poll while pending; absent otherwise, which
|
|
10938
11071
|
// the server reads as "not draining" — so a daemon that died
|
|
10939
11072
|
// mid-drain cannot pin its runner shut past its relaunch.
|
|
@@ -11112,7 +11245,7 @@ function harnessFlag(harness) {
|
|
|
11112
11245
|
|
|
11113
11246
|
// src/commands/runner-service.ts
|
|
11114
11247
|
import { execFile as execFile2 } from "node:child_process";
|
|
11115
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as
|
|
11248
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync11, rmSync as rmSync4, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
11116
11249
|
import { homedir as homedir3, hostname as hostname3, userInfo } from "node:os";
|
|
11117
11250
|
import { dirname as dirname6 } from "node:path";
|
|
11118
11251
|
import { promisify } from "node:util";
|
|
@@ -11359,7 +11492,7 @@ async function waitForStart(logPath, offset, name2) {
|
|
|
11359
11492
|
const deadline = Date.now() + STARTUP_TIMEOUT_MS;
|
|
11360
11493
|
while (Date.now() < deadline) {
|
|
11361
11494
|
if (existsSync7(logPath)) {
|
|
11362
|
-
const content =
|
|
11495
|
+
const content = readFileSync11(logPath, "utf8").slice(offset);
|
|
11363
11496
|
if (pattern.test(content)) return true;
|
|
11364
11497
|
}
|
|
11365
11498
|
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
@@ -12612,18 +12745,18 @@ function register10(program3) {
|
|
|
12612
12745
|
}
|
|
12613
12746
|
|
|
12614
12747
|
// src/commands/workflows.ts
|
|
12615
|
-
import { readFileSync as
|
|
12748
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
12616
12749
|
import { createInterface as createInterface5 } from "node:readline/promises";
|
|
12617
12750
|
|
|
12618
12751
|
// src/workflow-packages.ts
|
|
12619
12752
|
import { createInterface as createInterface4 } from "node:readline/promises";
|
|
12620
12753
|
import { createHash as createHash2 } from "node:crypto";
|
|
12621
|
-
import { readFileSync as
|
|
12754
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
12622
12755
|
var PACKAGE_STDIN = "-";
|
|
12623
12756
|
var MAX_LOCAL_JSON_BYTES = 32 * 1024 * 1024;
|
|
12624
12757
|
function readPackageSource(path2) {
|
|
12625
12758
|
try {
|
|
12626
|
-
const bytes2 =
|
|
12759
|
+
const bytes2 = readFileSync12(path2 === PACKAGE_STDIN ? 0 : path2);
|
|
12627
12760
|
if (bytes2.byteLength > MAX_LOCAL_JSON_BYTES)
|
|
12628
12761
|
throw new Error(`input exceeds ${MAX_LOCAL_JSON_BYTES} bytes`);
|
|
12629
12762
|
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes2);
|
|
@@ -13262,14 +13395,14 @@ function readJsonBody(inline, file) {
|
|
|
13262
13395
|
if (file !== void 0 && file !== "-") {
|
|
13263
13396
|
let raw;
|
|
13264
13397
|
try {
|
|
13265
|
-
raw =
|
|
13398
|
+
raw = readFileSync13(file, "utf8");
|
|
13266
13399
|
} catch (err) {
|
|
13267
13400
|
die(`cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13268
13401
|
}
|
|
13269
13402
|
return parseJsonObject(raw, file);
|
|
13270
13403
|
}
|
|
13271
13404
|
if (file === "-" || !process.stdin.isTTY) {
|
|
13272
|
-
const raw =
|
|
13405
|
+
const raw = readFileSync13(0, "utf8");
|
|
13273
13406
|
if (raw.trim() === "") {
|
|
13274
13407
|
if (file === "-") die("no JSON on stdin");
|
|
13275
13408
|
return void 0;
|