tines 0.0.265 → 0.0.267
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 +200 -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:
|
|
@@ -3462,6 +3466,9 @@ function repoDirFromUrl(url) {
|
|
|
3462
3466
|
}
|
|
3463
3467
|
var ARTIFACT_TYPES = ["file", "text", "link", "pr", "folder"];
|
|
3464
3468
|
var ARTIFACT_FILE_MAX_BYTES = 25 * 1024 * 1024;
|
|
3469
|
+
var ISSUE_CREATE_FILES_MAX_BYTES = 50 * 1024 * 1024;
|
|
3470
|
+
var ISSUE_CREATE_METADATA_MAX_BYTES = 256 * 1024;
|
|
3471
|
+
var ISSUE_CREATE_MULTIPART_MAX_BYTES = 51 * 1024 * 1024;
|
|
3465
3472
|
var ARTIFACT_TEXT_MAX_BYTES = 256 * 1024;
|
|
3466
3473
|
var ARTIFACT_FOLDER_MAX_BYTES = 50 * 1024 * 1024;
|
|
3467
3474
|
function canonicalGitHubRepoUrl(url) {
|
|
@@ -4035,6 +4042,21 @@ function createApiClient(options) {
|
|
|
4035
4042
|
removeIssueLabel: (issueId, labelRef) => request("DELETE", `/api/v1/issues/${issueId}/labels/${encodeURIComponent(labelRef)}`),
|
|
4036
4043
|
listProjectIssues: (projectId, filters = {}) => get(`/api/v1/projects/${projectId}/issues${query(filters)}`),
|
|
4037
4044
|
createIssue: (projectId, body) => request("POST", `/api/v1/projects/${projectId}/issues`, body),
|
|
4045
|
+
createIssueWithFiles: async (projectId, issue, files2) => {
|
|
4046
|
+
const form = new FormData();
|
|
4047
|
+
const metadata = {
|
|
4048
|
+
issue,
|
|
4049
|
+
attachments: files2.map((file, index) => ({
|
|
4050
|
+
part: `file-${index}`,
|
|
4051
|
+
name: file.name,
|
|
4052
|
+
filename: file.filename
|
|
4053
|
+
}))
|
|
4054
|
+
};
|
|
4055
|
+
form.append("metadata", JSON.stringify(metadata));
|
|
4056
|
+
files2.forEach((file, index) => form.append(`file-${index}`, file.file, file.filename));
|
|
4057
|
+
const res = await raw("POST", `/api/v1/projects/${projectId}/issues`, { body: form });
|
|
4058
|
+
return await res.json();
|
|
4059
|
+
},
|
|
4038
4060
|
getIssue: (id2) => get(`/api/v1/issues/${id2}`),
|
|
4039
4061
|
getIssueByNumber: (projectId, number) => get(`/api/v1/projects/${projectId}/issues/${number}`),
|
|
4040
4062
|
updateIssue: (id2, body) => request("PATCH", `/api/v1/issues/${id2}`, body),
|
|
@@ -5636,6 +5658,8 @@ function contextItemSummary(item) {
|
|
|
5636
5658
|
return `${item.repo_url}${item.repo_branch ? `#${item.repo_branch}` : ""}`;
|
|
5637
5659
|
case "artifact":
|
|
5638
5660
|
return item.artifact_type ?? "artifact";
|
|
5661
|
+
case "env":
|
|
5662
|
+
return item.secret ? `secret \xB7 set${item.hint ? ` \xB7 ${item.hint}` : ""}` : `= ${item.value ?? ""}`;
|
|
5639
5663
|
}
|
|
5640
5664
|
}
|
|
5641
5665
|
function recurrenceLabel(schedule2) {
|
|
@@ -6234,6 +6258,12 @@ async function resolveScopeFlags(api, opts) {
|
|
|
6234
6258
|
if (opts.label !== void 0) scope.label_id = await resolveLabelFlag(api, opts.label);
|
|
6235
6259
|
return scope;
|
|
6236
6260
|
}
|
|
6261
|
+
function readEnvValue(raw) {
|
|
6262
|
+
if (raw === "-") return readFileSync4(0, "utf8").replace(/\r?\n$/, "");
|
|
6263
|
+
if (raw.startsWith("@@")) return raw.slice(1);
|
|
6264
|
+
if (raw.startsWith("@")) return readFileSync4(raw.slice(1), "utf8").replace(/\r?\n$/, "");
|
|
6265
|
+
return raw;
|
|
6266
|
+
}
|
|
6237
6267
|
function printContextItem(item) {
|
|
6238
6268
|
console.log(`${item.kind} "${item.name}" [${item.id}] v${item.version}`);
|
|
6239
6269
|
if (item.description) console.log(item.description);
|
|
@@ -6256,6 +6286,15 @@ files (seeded at skills/${item.name}/):`);
|
|
|
6256
6286
|
console.log(` ${f.path} (${Buffer.byteLength(f.content, "utf8")} bytes)`);
|
|
6257
6287
|
}
|
|
6258
6288
|
if ((item.files ?? []).length === 0) console.log(" (none)");
|
|
6289
|
+
} else if (item.kind === "env") {
|
|
6290
|
+
if (item.secret) {
|
|
6291
|
+
console.log(`
|
|
6292
|
+
value: (secret, set)${item.hint ? ` hint: ${item.hint}` : ""}`);
|
|
6293
|
+
} else {
|
|
6294
|
+
console.log(`
|
|
6295
|
+
value: ${item.value ?? ""}`);
|
|
6296
|
+
if (item.hint) console.log(`hint: ${item.hint}`);
|
|
6297
|
+
}
|
|
6259
6298
|
} else {
|
|
6260
6299
|
console.log(`
|
|
6261
6300
|
url: ${item.repo_url}`);
|
|
@@ -6277,13 +6316,13 @@ function withScopeFlags(cmd) {
|
|
|
6277
6316
|
}
|
|
6278
6317
|
function register(program3) {
|
|
6279
6318
|
const context2 = program3.command("context").description(
|
|
6280
|
-
"Manage context items (prompts, skills, repo pointers) scoped to projects, states, and issues"
|
|
6319
|
+
"Manage context items (prompts, skills, repo pointers, environment variables) scoped to projects, states, and issues"
|
|
6281
6320
|
);
|
|
6282
6321
|
withList(
|
|
6283
6322
|
withScopeFlags(
|
|
6284
6323
|
context2.command("list").description(
|
|
6285
6324
|
"List context items (scope filters match every item whose scope includes the element)"
|
|
6286
|
-
).option("-k, --kind <kind>", "filter by kind: prompt, skill, or
|
|
6325
|
+
).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
6326
|
)
|
|
6288
6327
|
).action(
|
|
6289
6328
|
async (opts) => {
|
|
@@ -6327,21 +6366,27 @@ function register(program3) {
|
|
|
6327
6366
|
});
|
|
6328
6367
|
withCommon(
|
|
6329
6368
|
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
|
|
6369
|
+
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
6370
|
"-n, --name <name>",
|
|
6332
|
-
"item name (slug-like for skills; the dedup/override key)"
|
|
6371
|
+
"item name (slug-like for skills, the variable name for env; the dedup/override key)"
|
|
6333
6372
|
).option("-d, --description <text>", "one-liner shown in lists").option("--body <md>", "prompt body: inline Markdown or @file (escape a literal @ as @@)").option(
|
|
6334
6373
|
"--file <path>=@<local>",
|
|
6335
6374
|
"skill file: workspace path = local file (repeatable)",
|
|
6336
6375
|
collect,
|
|
6337
6376
|
[]
|
|
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)")
|
|
6377
|
+
).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(
|
|
6378
|
+
"--value <v>",
|
|
6379
|
+
"env: the value \u2014 inline, @file, or - for stdin (keeps a secret out of shell history)"
|
|
6380
|
+
).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
6381
|
)
|
|
6340
6382
|
).action(
|
|
6341
6383
|
async (opts) => {
|
|
6342
6384
|
if (opts.kind === "repo" && opts.repoUrl === void 0) {
|
|
6343
6385
|
die("--kind repo needs --repo-url <clone-url> (--url is the API base URL)");
|
|
6344
6386
|
}
|
|
6387
|
+
if (opts.kind === "env" && opts.value === void 0) {
|
|
6388
|
+
die("--kind env needs --value <value|@file|->");
|
|
6389
|
+
}
|
|
6345
6390
|
const api = client(opts);
|
|
6346
6391
|
const scope = await resolveScopeFlags(api, opts);
|
|
6347
6392
|
const body = {
|
|
@@ -6356,6 +6401,9 @@ function register(program3) {
|
|
|
6356
6401
|
if (opts.repoUrl !== void 0) body.repo_url = opts.repoUrl;
|
|
6357
6402
|
if (opts.branch !== void 0) body.repo_branch = opts.branch;
|
|
6358
6403
|
if (opts.dir !== void 0) body.repo_dir = opts.dir;
|
|
6404
|
+
if (opts.value !== void 0) body.value = readEnvValue(opts.value);
|
|
6405
|
+
if (opts.secret) body.secret = true;
|
|
6406
|
+
if (opts.hint !== void 0) body.hint = opts.hint;
|
|
6359
6407
|
const item = await api.createContextItem(body);
|
|
6360
6408
|
if (opts.json) return printJson(item);
|
|
6361
6409
|
console.log(`created ${item.kind} "${item.name}" (${item.id}) \u2014 scope: ${item.scope.label}`);
|
|
@@ -6363,7 +6411,7 @@ function register(program3) {
|
|
|
6363
6411
|
);
|
|
6364
6412
|
withCommon(
|
|
6365
6413
|
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(
|
|
6414
|
+
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
6415
|
"--unset <dimension>",
|
|
6368
6416
|
"drop a scope dimension: project, state, issue, or label (repeatable)",
|
|
6369
6417
|
collect,
|
|
@@ -6413,6 +6461,9 @@ function register(program3) {
|
|
|
6413
6461
|
if (opts.repoUrl !== void 0) body.repo_url = opts.repoUrl;
|
|
6414
6462
|
if (opts.branch !== void 0) body.repo_branch = opts.branch === "" ? null : opts.branch;
|
|
6415
6463
|
if (opts.dir !== void 0) body.repo_dir = opts.dir === "" ? null : opts.dir;
|
|
6464
|
+
if (opts.value !== void 0) body.value = readEnvValue(opts.value);
|
|
6465
|
+
if (opts.secret) body.secret = true;
|
|
6466
|
+
if (opts.hint !== void 0) body.hint = opts.hint === "" ? null : opts.hint;
|
|
6416
6467
|
if (Object.keys(body).length === 0) {
|
|
6417
6468
|
die(
|
|
6418
6469
|
"nothing to update: pass payload flags, --name/--description, scope flags, and/or --unset"
|
|
@@ -6459,7 +6510,7 @@ function register(program3) {
|
|
|
6459
6510
|
}
|
|
6460
6511
|
|
|
6461
6512
|
// src/commands/issues.ts
|
|
6462
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as
|
|
6513
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
6463
6514
|
import { basename, dirname as dirname2, join as join2 } from "node:path";
|
|
6464
6515
|
import { createInterface } from "node:readline/promises";
|
|
6465
6516
|
|
|
@@ -7171,7 +7222,7 @@ function readTextSource(source) {
|
|
|
7171
7222
|
if (source.kind === "text-stdin") return readBodyValue("-");
|
|
7172
7223
|
if (source.kind === "text-path") {
|
|
7173
7224
|
try {
|
|
7174
|
-
return
|
|
7225
|
+
return readFileSync5(source.path, "utf8");
|
|
7175
7226
|
} catch (err) {
|
|
7176
7227
|
die(`cannot read ${source.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7177
7228
|
}
|
|
@@ -7189,7 +7240,7 @@ function walkFolder(dir) {
|
|
|
7189
7240
|
files2.push({
|
|
7190
7241
|
path: nextRel,
|
|
7191
7242
|
contentType: sniffContentType(entry.name),
|
|
7192
|
-
bytes:
|
|
7243
|
+
bytes: readFileSync5(nextAbs)
|
|
7193
7244
|
});
|
|
7194
7245
|
}
|
|
7195
7246
|
}
|
|
@@ -7644,6 +7695,9 @@ function register2(program3) {
|
|
|
7644
7695
|
console.log(`
|
|
7645
7696
|
skills: ${context2.skills.map((s) => s.name).join(", ")}`);
|
|
7646
7697
|
}
|
|
7698
|
+
for (const e of context2.env ?? []) {
|
|
7699
|
+
console.log(`env: ${e.name}${e.secret ? " (secret)" : ""}`);
|
|
7700
|
+
}
|
|
7647
7701
|
for (const repo of context2.repos) {
|
|
7648
7702
|
console.log(
|
|
7649
7703
|
`repo: ${repo.name} ${repo.url}${repo.branch ? `#${repo.branch}` : ""} \u2192 ${repo.dir}/`
|
|
@@ -7827,10 +7881,10 @@ files (v${artifact.current_version.version}):`);
|
|
|
7827
7881
|
let bytes2;
|
|
7828
7882
|
if (fromPath === null) {
|
|
7829
7883
|
if (process.stdin.isTTY) die('"-" reads the file from stdin, but stdin is a terminal');
|
|
7830
|
-
bytes2 =
|
|
7884
|
+
bytes2 = readFileSync5(0);
|
|
7831
7885
|
} else {
|
|
7832
7886
|
try {
|
|
7833
|
-
bytes2 =
|
|
7887
|
+
bytes2 = readFileSync5(fromPath);
|
|
7834
7888
|
} catch (err) {
|
|
7835
7889
|
die(`cannot read ${fromPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7836
7890
|
}
|
|
@@ -8285,13 +8339,13 @@ function register4(program3) {
|
|
|
8285
8339
|
}
|
|
8286
8340
|
|
|
8287
8341
|
// src/commands/login.ts
|
|
8288
|
-
import { readFileSync as
|
|
8342
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
8289
8343
|
function maskKey(key) {
|
|
8290
8344
|
return `${key.slice(0, 14)}\u2026`;
|
|
8291
8345
|
}
|
|
8292
8346
|
function readKeyArg(value) {
|
|
8293
8347
|
if (value !== "-") return value;
|
|
8294
|
-
const key =
|
|
8348
|
+
const key = readFileSync6(0, "utf8").trim();
|
|
8295
8349
|
if (!key) die("no API key on stdin");
|
|
8296
8350
|
return key;
|
|
8297
8351
|
}
|
|
@@ -8731,7 +8785,7 @@ import {
|
|
|
8731
8785
|
createWriteStream,
|
|
8732
8786
|
existsSync as existsSync5,
|
|
8733
8787
|
mkdirSync as mkdirSync4,
|
|
8734
|
-
readFileSync as
|
|
8788
|
+
readFileSync as readFileSync10,
|
|
8735
8789
|
realpathSync,
|
|
8736
8790
|
rmSync as rmSync3,
|
|
8737
8791
|
statSync as statSync4,
|
|
@@ -8743,11 +8797,11 @@ import { dirname as dirname4, join as join6 } from "node:path";
|
|
|
8743
8797
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
8744
8798
|
|
|
8745
8799
|
// src/version.ts
|
|
8746
|
-
import { readFileSync as
|
|
8800
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
8747
8801
|
function cliVersion() {
|
|
8748
8802
|
try {
|
|
8749
8803
|
const manifest = new URL("../package.json", import.meta.url);
|
|
8750
|
-
return JSON.parse(
|
|
8804
|
+
return JSON.parse(readFileSync7(manifest, "utf8")).version ?? "0.0.0-unknown";
|
|
8751
8805
|
} catch {
|
|
8752
8806
|
return "0.0.0-unknown";
|
|
8753
8807
|
}
|
|
@@ -9761,7 +9815,7 @@ var EffortCapabilityRefresher = class {
|
|
|
9761
9815
|
|
|
9762
9816
|
// src/daemon/cli-refresh.ts
|
|
9763
9817
|
import { spawn as spawn2 } from "node:child_process";
|
|
9764
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as
|
|
9818
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync8 } from "node:fs";
|
|
9765
9819
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
9766
9820
|
|
|
9767
9821
|
// src/daemon/support.ts
|
|
@@ -9946,10 +10000,12 @@ var LogBatcher = class {
|
|
|
9946
10000
|
constructor(send, opts = {}) {
|
|
9947
10001
|
this.send = send;
|
|
9948
10002
|
this.opts = opts;
|
|
10003
|
+
this.redactor = new SecretRedactor(opts.redact ?? []);
|
|
9949
10004
|
}
|
|
9950
10005
|
send;
|
|
9951
10006
|
opts;
|
|
9952
10007
|
buffer = "";
|
|
10008
|
+
redactor;
|
|
9953
10009
|
timer = null;
|
|
9954
10010
|
sending = Promise.resolve();
|
|
9955
10011
|
seq = 0;
|
|
@@ -9957,6 +10013,7 @@ var LogBatcher = class {
|
|
|
9957
10013
|
pending = [];
|
|
9958
10014
|
append(text4) {
|
|
9959
10015
|
if (!text4) return;
|
|
10016
|
+
text4 = this.redactor.write(text4);
|
|
9960
10017
|
this.buffer += text4;
|
|
9961
10018
|
if (Buffer.byteLength(this.buffer, "utf8") >= (this.opts.maxBytes ?? 32 * 1024)) {
|
|
9962
10019
|
void this.flush();
|
|
@@ -9965,6 +10022,11 @@ var LogBatcher = class {
|
|
|
9965
10022
|
this.timer.unref?.();
|
|
9966
10023
|
}
|
|
9967
10024
|
}
|
|
10025
|
+
/** Release an incomplete final match only after the producer has ended. */
|
|
10026
|
+
finish() {
|
|
10027
|
+
this.buffer += this.redactor.end();
|
|
10028
|
+
return this.flush();
|
|
10029
|
+
}
|
|
9968
10030
|
/** Sends whatever is buffered now; resolves when every send so far settled. */
|
|
9969
10031
|
flush() {
|
|
9970
10032
|
if (this.timer) {
|
|
@@ -10077,15 +10139,68 @@ function pathWithin(path2, dir) {
|
|
|
10077
10139
|
function buildSpawnEnv(base, opts) {
|
|
10078
10140
|
const env = {
|
|
10079
10141
|
...base,
|
|
10142
|
+
...Object.fromEntries((opts.extra ?? []).map((e) => [e.name, e.value])),
|
|
10080
10143
|
TINES_API_KEY: opts.apiKey,
|
|
10081
10144
|
// Own canonicalization at the final child-process boundary too. This
|
|
10082
10145
|
// keeps the spawned CLI safe even if a future daemon call site passes
|
|
10083
10146
|
// the original --url value instead of its normalized local variable.
|
|
10084
10147
|
TINES_API_URL: opts.apiUrl.replace(/\/+$/, "")
|
|
10085
10148
|
};
|
|
10149
|
+
if (opts.extra?.some((e) => e.name === "PATH")) env.PATH = base.PATH;
|
|
10086
10150
|
if (opts.binDir) env.PATH = base.PATH ? `${opts.binDir}${delimiter}${base.PATH}` : opts.binDir;
|
|
10087
10151
|
return env;
|
|
10088
10152
|
}
|
|
10153
|
+
function secretForms(secrets) {
|
|
10154
|
+
return [
|
|
10155
|
+
...new Set(
|
|
10156
|
+
secrets.filter(Boolean).flatMap((secret) => [secret, JSON.stringify(secret).slice(1, -1)])
|
|
10157
|
+
)
|
|
10158
|
+
].sort((a, b) => b.length - a.length);
|
|
10159
|
+
}
|
|
10160
|
+
function secretPattern(forms) {
|
|
10161
|
+
return forms.length ? new RegExp(forms.map((form) => form.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g") : null;
|
|
10162
|
+
}
|
|
10163
|
+
function redactSecrets(text4, secrets) {
|
|
10164
|
+
const pattern = secretPattern(secretForms(secrets));
|
|
10165
|
+
return pattern ? text4.replace(pattern, "***") : text4;
|
|
10166
|
+
}
|
|
10167
|
+
var SecretRedactor = class {
|
|
10168
|
+
pending = "";
|
|
10169
|
+
forms;
|
|
10170
|
+
pattern;
|
|
10171
|
+
constructor(secrets) {
|
|
10172
|
+
this.forms = secretForms(secrets);
|
|
10173
|
+
this.pattern = secretPattern(this.forms);
|
|
10174
|
+
}
|
|
10175
|
+
write(chunk) {
|
|
10176
|
+
if (!this.pattern) return chunk;
|
|
10177
|
+
const text4 = this.pending + chunk;
|
|
10178
|
+
let cut = text4.length;
|
|
10179
|
+
for (const form of this.forms) {
|
|
10180
|
+
for (let size = Math.min(form.length - 1, text4.length); size > text4.length - cut; size--) {
|
|
10181
|
+
if (text4.endsWith(form.slice(0, size))) {
|
|
10182
|
+
cut = text4.length - size;
|
|
10183
|
+
break;
|
|
10184
|
+
}
|
|
10185
|
+
}
|
|
10186
|
+
}
|
|
10187
|
+
this.pattern.lastIndex = 0;
|
|
10188
|
+
for (let match; match = this.pattern.exec(text4); ) {
|
|
10189
|
+
if (match.index >= cut) break;
|
|
10190
|
+
if (this.pattern.lastIndex > cut) {
|
|
10191
|
+
cut = match.index;
|
|
10192
|
+
break;
|
|
10193
|
+
}
|
|
10194
|
+
}
|
|
10195
|
+
this.pending = text4.slice(cut);
|
|
10196
|
+
return text4.slice(0, cut).replace(this.pattern, "***");
|
|
10197
|
+
}
|
|
10198
|
+
end() {
|
|
10199
|
+
const text4 = this.pending;
|
|
10200
|
+
this.pending = "";
|
|
10201
|
+
return this.pattern ? text4.replace(this.pattern, "***") : text4;
|
|
10202
|
+
}
|
|
10203
|
+
};
|
|
10089
10204
|
|
|
10090
10205
|
// src/daemon/cli-refresh.ts
|
|
10091
10206
|
var PACKAGE = "tines";
|
|
@@ -10098,7 +10213,7 @@ function binDirOf(prefix) {
|
|
|
10098
10213
|
}
|
|
10099
10214
|
function installedVersion(prefix) {
|
|
10100
10215
|
try {
|
|
10101
|
-
const pkg =
|
|
10216
|
+
const pkg = readFileSync8(join4(prefix, "node_modules", PACKAGE, "package.json"), "utf8");
|
|
10102
10217
|
const version = JSON.parse(pkg).version;
|
|
10103
10218
|
return typeof version === "string" ? version : null;
|
|
10104
10219
|
} catch {
|
|
@@ -10178,7 +10293,7 @@ function message(err) {
|
|
|
10178
10293
|
import { arch, hostname, platform } from "node:os";
|
|
10179
10294
|
|
|
10180
10295
|
// src/daemon/store.ts
|
|
10181
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
10296
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
10182
10297
|
import { join as join5 } from "node:path";
|
|
10183
10298
|
function credentialsKey(url, name2) {
|
|
10184
10299
|
return `${url.replace(/\/+$/, "")}#${name2}`;
|
|
@@ -10238,10 +10353,10 @@ function saveDaemonDeclines(path2, runIds) {
|
|
|
10238
10353
|
}
|
|
10239
10354
|
function processStartTimeMs(pid) {
|
|
10240
10355
|
try {
|
|
10241
|
-
const stat2 =
|
|
10356
|
+
const stat2 = readFileSync9(`/proc/${pid}/stat`, "utf8");
|
|
10242
10357
|
const afterComm = stat2.slice(stat2.lastIndexOf(")") + 2).split(" ");
|
|
10243
10358
|
const startTicks = Number(afterComm[19]);
|
|
10244
|
-
const btimeLine =
|
|
10359
|
+
const btimeLine = readFileSync9("/proc/stat", "utf8").split("\n").find((line) => line.startsWith("btime "));
|
|
10245
10360
|
const btime = Number(btimeLine?.slice("btime ".length));
|
|
10246
10361
|
if (!Number.isFinite(startTicks) || !Number.isFinite(btime)) return null;
|
|
10247
10362
|
return btime * 1e3 + startTicks / 100 * 1e3;
|
|
@@ -10368,7 +10483,7 @@ async function uploadRawLog(run3) {
|
|
|
10368
10483
|
await new Promise((resolve2) => run3.rawSpool?.end(resolve2) ?? resolve2());
|
|
10369
10484
|
const size = statSync4(path2).size;
|
|
10370
10485
|
if (size > 0) {
|
|
10371
|
-
let body =
|
|
10486
|
+
let body = readFileSync10(path2);
|
|
10372
10487
|
if (body.byteLength > RUN_LOG_RAW_MAX_BYTES) {
|
|
10373
10488
|
const marker = Buffer.from(
|
|
10374
10489
|
`{"type":"tines_truncated","dropped_bytes":${body.byteLength - RUN_LOG_RAW_MAX_BYTES}}
|
|
@@ -10535,7 +10650,7 @@ async function runDaemon(opts) {
|
|
|
10535
10650
|
};
|
|
10536
10651
|
run3.batcher.append(`[usage] ${JSON.stringify(accounting)}
|
|
10537
10652
|
`);
|
|
10538
|
-
await run3.batcher.
|
|
10653
|
+
await run3.batcher.finish();
|
|
10539
10654
|
const ended = await client2.finishRun(run3.runId, {
|
|
10540
10655
|
status,
|
|
10541
10656
|
...run3.effortEvidence ? { effort_application: run3.effortEvidence } : {},
|
|
@@ -10636,6 +10751,7 @@ async function runDaemon(opts) {
|
|
|
10636
10751
|
const effortRefresher = new EffortCapabilityRefresher(opts.harness, DAEMON_VERSION);
|
|
10637
10752
|
let effortCapabilities = await effortRefresher.get();
|
|
10638
10753
|
const launch = async (assignment) => {
|
|
10754
|
+
const secretEnvValues = (assignment.env ?? []).filter((e) => e.secret).map((e) => e.value);
|
|
10639
10755
|
const runId = assignment.run.id;
|
|
10640
10756
|
if (table2.has(runId)) return;
|
|
10641
10757
|
const resume = assignment.resume && existsSync5(assignment.resume.workspace_path) ? assignment.resume : null;
|
|
@@ -10659,10 +10775,13 @@ async function runDaemon(opts) {
|
|
|
10659
10775
|
batcher: new LogBatcher(
|
|
10660
10776
|
(chunk, seq) => client2.appendRunLog(runId, { chunk, seq }).then(() => {
|
|
10661
10777
|
}),
|
|
10662
|
-
{
|
|
10778
|
+
{
|
|
10779
|
+
onError: (err) => log(`log append for run ${runId} failed: ${message2(err)}`),
|
|
10780
|
+
...secretEnvValues.length > 0 ? { redact: secretEnvValues } : {}
|
|
10781
|
+
}
|
|
10663
10782
|
)
|
|
10664
10783
|
};
|
|
10665
|
-
run3.flush = () => run3.batcher.
|
|
10784
|
+
run3.flush = () => run3.batcher.finish();
|
|
10666
10785
|
table2.track(run3);
|
|
10667
10786
|
if (assignment.effort) effortCapabilities = await effortRefresher.get(true);
|
|
10668
10787
|
const effortRejection = assignmentEffortRejection(assignment, effortCapabilities, opts.harness);
|
|
@@ -10760,8 +10879,15 @@ async function runDaemon(opts) {
|
|
|
10760
10879
|
const spawnEnv = buildSpawnEnv(process.env, {
|
|
10761
10880
|
binDir: cli.binDir,
|
|
10762
10881
|
apiKey: assignment.run_key,
|
|
10763
|
-
apiUrl: baseUrl
|
|
10882
|
+
apiUrl: baseUrl,
|
|
10883
|
+
...assignment.env ? { extra: assignment.env } : {}
|
|
10764
10884
|
});
|
|
10885
|
+
if (assignment.env?.length) {
|
|
10886
|
+
run3.batcher.append(
|
|
10887
|
+
`[env] ${assignment.env.length} environment variable(s) set: ${assignment.env.map((e) => `${e.name}${e.secret ? " (secret)" : ""}`).join(", ")}
|
|
10888
|
+
`
|
|
10889
|
+
);
|
|
10890
|
+
}
|
|
10765
10891
|
if (opts.harness === "codex") run3.codexHome = resolveCodexHome(spawnEnv, workspace);
|
|
10766
10892
|
const child = spawn3(invocation.file, invocation.args, {
|
|
10767
10893
|
cwd: workspace,
|
|
@@ -10800,14 +10926,20 @@ async function runDaemon(opts) {
|
|
|
10800
10926
|
run3.rawSpool = createWriteStream(spoolPath);
|
|
10801
10927
|
run3.rawUpload = (body) => client2.putRunLogRaw(runId, body);
|
|
10802
10928
|
const decoder = new StringDecoder2("utf8");
|
|
10929
|
+
const rawRedactor = new SecretRedactor(secretEnvValues);
|
|
10803
10930
|
run3.drain = () => {
|
|
10804
10931
|
const trailing = decoder.end();
|
|
10805
|
-
if (trailing)
|
|
10932
|
+
if (trailing) {
|
|
10933
|
+
run3.rawSpool?.write(rawRedactor.write(trailing));
|
|
10934
|
+
renderer.write(trailing);
|
|
10935
|
+
}
|
|
10936
|
+
run3.rawSpool?.write(rawRedactor.end());
|
|
10806
10937
|
renderer.finish();
|
|
10807
10938
|
};
|
|
10808
10939
|
child.stdout?.on("data", (data) => {
|
|
10809
|
-
|
|
10810
|
-
|
|
10940
|
+
const text4 = decoder.write(data);
|
|
10941
|
+
run3.rawSpool?.write(rawRedactor.write(text4));
|
|
10942
|
+
renderer.write(text4);
|
|
10811
10943
|
});
|
|
10812
10944
|
} else if (opts.harness === "codex") {
|
|
10813
10945
|
const renderer = new CodexStreamRenderer((line) => run3.batcher.append(line));
|
|
@@ -10820,11 +10952,26 @@ async function runDaemon(opts) {
|
|
|
10820
10952
|
};
|
|
10821
10953
|
child.stdout?.on("data", (data) => renderer.write(decoder.write(data)));
|
|
10822
10954
|
} else {
|
|
10823
|
-
|
|
10955
|
+
const decoder = new StringDecoder2("utf8");
|
|
10956
|
+
const redactor = new SecretRedactor(secretEnvValues);
|
|
10957
|
+
child.stdout?.on(
|
|
10958
|
+
"data",
|
|
10959
|
+
(data) => run3.batcher.append(redactor.write(decoder.write(data)))
|
|
10960
|
+
);
|
|
10961
|
+
run3.drain = () => {
|
|
10962
|
+
run3.batcher.append(redactor.write(decoder.end()) + redactor.end());
|
|
10963
|
+
};
|
|
10824
10964
|
}
|
|
10965
|
+
const stderrDecoder = new StringDecoder2("utf8");
|
|
10966
|
+
const stderrRedactor = new SecretRedactor(secretEnvValues);
|
|
10967
|
+
const drainStdout = run3.drain;
|
|
10968
|
+
run3.drain = () => {
|
|
10969
|
+
drainStdout?.();
|
|
10970
|
+
run3.batcher.append(stderrRedactor.write(stderrDecoder.end()) + stderrRedactor.end());
|
|
10971
|
+
};
|
|
10825
10972
|
child.stderr?.on("data", (data) => {
|
|
10826
|
-
const text4 =
|
|
10827
|
-
run3.batcher.append(text4);
|
|
10973
|
+
const text4 = stderrDecoder.write(data);
|
|
10974
|
+
run3.batcher.append(stderrRedactor.write(text4));
|
|
10828
10975
|
run3.limiter?.noteStderr(text4);
|
|
10829
10976
|
});
|
|
10830
10977
|
run3.timeout = setTimeout(() => {
|
|
@@ -10835,7 +10982,7 @@ async function runDaemon(opts) {
|
|
|
10835
10982
|
if (child.pid) setTimeout(() => killTree(child.pid, "SIGKILL"), 5e3).unref?.();
|
|
10836
10983
|
}, assignment.timeout_minutes * 6e4);
|
|
10837
10984
|
child.on("error", (err) => {
|
|
10838
|
-
const reason = `failed to launch harness: ${message2(err)}
|
|
10985
|
+
const reason = redactSecrets(`failed to launch harness: ${message2(err)}`, secretEnvValues);
|
|
10839
10986
|
if (assignment.effort)
|
|
10840
10987
|
run3.effortEvidence = {
|
|
10841
10988
|
status: "rejected",
|
|
@@ -10870,18 +11017,16 @@ async function runDaemon(opts) {
|
|
|
10870
11017
|
const limited = signal ? null : run3.limiter?.signal() ?? null;
|
|
10871
11018
|
const providerError = signal ? null : run3.limiter?.providerError() ?? null;
|
|
10872
11019
|
if (limited) {
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
void table2.finishAndCleanup(run3, "failed", `rate limited: ${limited.detail}`, {
|
|
11020
|
+
const detail = redactSecrets(limited.detail, secretEnvValues);
|
|
11021
|
+
log(`run ${runId}: harness rate limited (${detail}); reporting without a strike`);
|
|
11022
|
+
void table2.finishAndCleanup(run3, "failed", `rate limited: ${detail}`, {
|
|
10877
11023
|
judgment: "rate_limited",
|
|
10878
11024
|
...limited.resumeAt !== null ? { resume_at: limited.resumeAt } : {}
|
|
10879
11025
|
});
|
|
10880
11026
|
} else if (providerError) {
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
void table2.finishAndCleanup(run3, "failed", `provider error: ${providerError.detail}`, {
|
|
11027
|
+
const detail = redactSecrets(providerError.detail, secretEnvValues);
|
|
11028
|
+
log(`run ${runId}: transient provider error (${detail}); reporting without a strike`);
|
|
11029
|
+
void table2.finishAndCleanup(run3, "failed", `provider error: ${detail}`, {
|
|
10885
11030
|
judgment: "interrupted"
|
|
10886
11031
|
});
|
|
10887
11032
|
} else {
|
|
@@ -10894,7 +11039,11 @@ async function runDaemon(opts) {
|
|
|
10894
11039
|
}
|
|
10895
11040
|
});
|
|
10896
11041
|
} catch (err) {
|
|
10897
|
-
void table2.finishAndCleanup(
|
|
11042
|
+
void table2.finishAndCleanup(
|
|
11043
|
+
run3,
|
|
11044
|
+
"failed",
|
|
11045
|
+
redactSecrets(`workspace setup failed: ${message2(err)}`, secretEnvValues)
|
|
11046
|
+
);
|
|
10898
11047
|
}
|
|
10899
11048
|
};
|
|
10900
11049
|
const shutdown = async () => {
|
|
@@ -10934,6 +11083,8 @@ async function runDaemon(opts) {
|
|
|
10934
11083
|
},
|
|
10935
11084
|
...declinedAssignments.size > 0 ? { declined_assignments: [...declinedAssignments].slice(0, 100) } : {},
|
|
10936
11085
|
...effortCapabilities ? { effort_capabilities: effortCapabilities } : {},
|
|
11086
|
+
// Capability: this daemon merges assignment.env into the harness environment.
|
|
11087
|
+
env_delivery: 1,
|
|
10937
11088
|
// Stated on every poll while pending; absent otherwise, which
|
|
10938
11089
|
// the server reads as "not draining" — so a daemon that died
|
|
10939
11090
|
// mid-drain cannot pin its runner shut past its relaunch.
|
|
@@ -11112,7 +11263,7 @@ function harnessFlag(harness) {
|
|
|
11112
11263
|
|
|
11113
11264
|
// src/commands/runner-service.ts
|
|
11114
11265
|
import { execFile as execFile2 } from "node:child_process";
|
|
11115
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as
|
|
11266
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync11, rmSync as rmSync4, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
11116
11267
|
import { homedir as homedir3, hostname as hostname3, userInfo } from "node:os";
|
|
11117
11268
|
import { dirname as dirname6 } from "node:path";
|
|
11118
11269
|
import { promisify } from "node:util";
|
|
@@ -11359,7 +11510,7 @@ async function waitForStart(logPath, offset, name2) {
|
|
|
11359
11510
|
const deadline = Date.now() + STARTUP_TIMEOUT_MS;
|
|
11360
11511
|
while (Date.now() < deadline) {
|
|
11361
11512
|
if (existsSync7(logPath)) {
|
|
11362
|
-
const content =
|
|
11513
|
+
const content = readFileSync11(logPath, "utf8").slice(offset);
|
|
11363
11514
|
if (pattern.test(content)) return true;
|
|
11364
11515
|
}
|
|
11365
11516
|
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
@@ -12612,18 +12763,18 @@ function register10(program3) {
|
|
|
12612
12763
|
}
|
|
12613
12764
|
|
|
12614
12765
|
// src/commands/workflows.ts
|
|
12615
|
-
import { readFileSync as
|
|
12766
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
12616
12767
|
import { createInterface as createInterface5 } from "node:readline/promises";
|
|
12617
12768
|
|
|
12618
12769
|
// src/workflow-packages.ts
|
|
12619
12770
|
import { createInterface as createInterface4 } from "node:readline/promises";
|
|
12620
12771
|
import { createHash as createHash2 } from "node:crypto";
|
|
12621
|
-
import { readFileSync as
|
|
12772
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
12622
12773
|
var PACKAGE_STDIN = "-";
|
|
12623
12774
|
var MAX_LOCAL_JSON_BYTES = 32 * 1024 * 1024;
|
|
12624
12775
|
function readPackageSource(path2) {
|
|
12625
12776
|
try {
|
|
12626
|
-
const bytes2 =
|
|
12777
|
+
const bytes2 = readFileSync12(path2 === PACKAGE_STDIN ? 0 : path2);
|
|
12627
12778
|
if (bytes2.byteLength > MAX_LOCAL_JSON_BYTES)
|
|
12628
12779
|
throw new Error(`input exceeds ${MAX_LOCAL_JSON_BYTES} bytes`);
|
|
12629
12780
|
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes2);
|
|
@@ -13262,14 +13413,14 @@ function readJsonBody(inline, file) {
|
|
|
13262
13413
|
if (file !== void 0 && file !== "-") {
|
|
13263
13414
|
let raw;
|
|
13264
13415
|
try {
|
|
13265
|
-
raw =
|
|
13416
|
+
raw = readFileSync13(file, "utf8");
|
|
13266
13417
|
} catch (err) {
|
|
13267
13418
|
die(`cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13268
13419
|
}
|
|
13269
13420
|
return parseJsonObject(raw, file);
|
|
13270
13421
|
}
|
|
13271
13422
|
if (file === "-" || !process.stdin.isTTY) {
|
|
13272
|
-
const raw =
|
|
13423
|
+
const raw = readFileSync13(0, "utf8");
|
|
13273
13424
|
if (raw.trim() === "") {
|
|
13274
13425
|
if (file === "-") die("no JSON on stdin");
|
|
13275
13426
|
return void 0;
|