tines 0.0.277 → 0.0.279

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 +247 -145
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3436,6 +3436,7 @@ var PROMPT_MAX_BYTES = 32 * 1024;
3436
3436
  var SKILL_MAX_FILES = 20;
3437
3437
  var SKILL_MAX_TOTAL_BYTES = 100 * 1024;
3438
3438
  var ENV_VALUE_MAX_BYTES = 16 * 1024;
3439
+ var SKILL_NAME_PATTERN = /^[a-z0-9-]+$/;
3439
3440
  var AGENT_GUIDELINES_NAME = "agent-guidelines";
3440
3441
  var JOURNAL_NAME = "journal";
3441
3442
  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:
@@ -3604,8 +3605,8 @@ function str(v) {
3604
3605
  function isoTime(v) {
3605
3606
  return typeof v === "number" && Number.isFinite(v) ? new Date(v).toISOString() : "an unknown time";
3606
3607
  }
3607
- function joinChanged(v, sep4) {
3608
- return Array.isArray(v) ? v.join(sep4) : "";
3608
+ function joinChanged(v, sep5) {
3609
+ return Array.isArray(v) ? v.join(sep5) : "";
3609
3610
  }
3610
3611
  function action(type) {
3611
3612
  return type.split(".")[1] ?? type;
@@ -5946,11 +5947,11 @@ function assertNewStatesHavePrompts(states, prompts) {
5946
5947
  }
5947
5948
  }
5948
5949
  function parseScheduleRef(ref) {
5949
- const sep4 = ref.indexOf("/");
5950
- if (sep4 < 1 || sep4 === ref.length - 1) {
5950
+ const sep5 = ref.indexOf("/");
5951
+ if (sep5 < 1 || sep5 === ref.length - 1) {
5951
5952
  throw new CliError(`schedule reference must look like <project>/<name>, got "${ref}"`);
5952
5953
  }
5953
- return { project: ref.slice(0, sep4), name: ref.slice(sep4 + 1) };
5954
+ return { project: ref.slice(0, sep5), name: ref.slice(sep5 + 1) };
5954
5955
  }
5955
5956
  function parseIssueRef(ref) {
5956
5957
  const match = ref.match(/^(.+)\/(\d+)$/);
@@ -5958,12 +5959,12 @@ function parseIssueRef(ref) {
5958
5959
  return { project: match[1], number: Number.parseInt(match[2], 10) };
5959
5960
  }
5960
5961
  function parseFileSpec(spec) {
5961
- const sep4 = spec.indexOf("=");
5962
- if (sep4 < 1 || sep4 === spec.length - 1) {
5962
+ const sep5 = spec.indexOf("=");
5963
+ if (sep5 < 1 || sep5 === spec.length - 1) {
5963
5964
  throw new CliError(`--file must look like <path>=@<local-file>, got "${spec}"`);
5964
5965
  }
5965
- const path2 = spec.slice(0, sep4);
5966
- const source = spec.slice(sep4 + 1);
5966
+ const path2 = spec.slice(0, sep5);
5967
+ const source = spec.slice(sep5 + 1);
5967
5968
  if (!source.startsWith("@")) {
5968
5969
  throw new CliError(
5969
5970
  `skill file content always comes from a local file: --file ${path2}=@<local-file>`
@@ -5977,10 +5978,10 @@ function parseFileSpec(spec) {
5977
5978
  }
5978
5979
  }
5979
5980
  function parseTargetSpec(spec) {
5980
- const sep4 = spec.lastIndexOf(":");
5981
- if (sep4 === -1) return { name: spec };
5982
- const name2 = spec.slice(0, sep4);
5983
- const tier = spec.slice(sep4 + 1);
5981
+ const sep5 = spec.lastIndexOf(":");
5982
+ if (sep5 === -1) return { name: spec };
5983
+ const name2 = spec.slice(0, sep5);
5984
+ const tier = spec.slice(sep5 + 1);
5984
5985
  if (!name2) throw new CliError(`target must look like <runner>[:tier], got "${spec}"`);
5985
5986
  if (!MODEL_TIERS.includes(tier)) {
5986
5987
  throw new CliError(`unknown tier "${tier}" in "${spec}" (tiers: ${MODEL_TIERS.join(", ")})`);
@@ -6224,12 +6225,12 @@ async function resolveLabelFlag(api, ref) {
6224
6225
  return match.id;
6225
6226
  }
6226
6227
  async function resolveStateFlag(api, ref) {
6227
- const sep4 = ref.indexOf("/");
6228
- if (sep4 < 1 || sep4 === ref.length - 1) {
6228
+ const sep5 = ref.indexOf("/");
6229
+ if (sep5 < 1 || sep5 === ref.length - 1) {
6229
6230
  die(`--state must look like <workflow>/<state>, got "${ref}"`);
6230
6231
  }
6231
- const workflow2 = await resolveWorkflow(api, ref.slice(0, sep4));
6232
- const stateRef2 = ref.slice(sep4 + 1);
6232
+ const workflow2 = await resolveWorkflow(api, ref.slice(0, sep5));
6233
+ const stateRef2 = ref.slice(sep5 + 1);
6233
6234
  const state = workflow2.states.find((s) => s.name === stateRef2) ?? workflow2.states.find((s) => s.id === stateRef2);
6234
6235
  if (!state) {
6235
6236
  die(
@@ -6281,7 +6282,7 @@ type: ${item.artifact_type ?? "artifact"}`);
6281
6282
  ${item.body}`);
6282
6283
  } else if (item.kind === "skill") {
6283
6284
  console.log(`
6284
- files (seeded at skills/${item.name}/):`);
6285
+ files (seeded at .agents/skills/${item.name}/):`);
6285
6286
  for (const f of item.files ?? []) {
6286
6287
  console.log(` ${f.path} (${Buffer.byteLength(f.content, "utf8")} bytes)`);
6287
6288
  }
@@ -6510,8 +6511,8 @@ function register(program3) {
6510
6511
  }
6511
6512
 
6512
6513
  // src/commands/issues.ts
6513
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
6514
- import { basename, dirname as dirname2, join as join2 } from "node:path";
6514
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync3 } from "node:fs";
6515
+ import { basename, dirname as dirname2, join as join3 } from "node:path";
6515
6516
  import { createInterface } from "node:readline/promises";
6516
6517
 
6517
6518
  // src/issue-transfer.ts
@@ -7142,6 +7143,97 @@ function buildRecurrence(opts) {
7142
7143
  }
7143
7144
  }
7144
7145
 
7146
+ // src/skills.ts
7147
+ import { randomUUID } from "node:crypto";
7148
+ import { existsSync as existsSync3, lstatSync, mkdirSync as mkdirSync2, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
7149
+ import { isAbsolute, join as join2, relative, resolve, sep } from "node:path";
7150
+ var SKILLS_DIRECTORY = ".agents/skills";
7151
+ function validateRelativePath(value, label) {
7152
+ if (!value || isAbsolute(value) || value.startsWith("/") || value.includes("\\")) {
7153
+ throw new Error(`${label} must be a relative path using forward slashes (got "${value}")`);
7154
+ }
7155
+ if (value.includes("=")) throw new Error(`${label} cannot contain "=" (got "${value}")`);
7156
+ const segments = value.split("/");
7157
+ if (segments.some((segment) => !segment || segment === "." || segment === "..")) {
7158
+ throw new Error(`${label} cannot contain empty, ".", or ".." segments (got "${value}")`);
7159
+ }
7160
+ return segments;
7161
+ }
7162
+ function isPrefix(left, right) {
7163
+ return left.length <= right.length && left.every((segment, index) => segment.toLowerCase() === right[index]?.toLowerCase());
7164
+ }
7165
+ function assertNoSkillRepoOverlap(repoDirs) {
7166
+ const skills = SKILLS_DIRECTORY.split("/");
7167
+ for (const dir of repoDirs) {
7168
+ const repo = validateRelativePath(dir, "Repository checkout directory");
7169
+ if (isPrefix(repo, skills) || isPrefix(skills, repo)) {
7170
+ throw new Error(
7171
+ `Repository checkout directory "${dir}" overlaps generated skill directory "${SKILLS_DIRECTORY}"`
7172
+ );
7173
+ }
7174
+ }
7175
+ }
7176
+ function rejectSymlink(path2, label) {
7177
+ if (!existsSync3(path2)) return;
7178
+ const stat2 = lstatSync(path2);
7179
+ if (stat2.isSymbolicLink()) throw new Error(`${label} must not be a symlink: ${path2}`);
7180
+ if (!stat2.isDirectory()) throw new Error(`${label} must be a directory: ${path2}`);
7181
+ }
7182
+ function materializeSkills(root, skills) {
7183
+ const seenSkills = /* @__PURE__ */ new Set();
7184
+ for (const skill of skills) {
7185
+ if (!SKILL_NAME_PATTERN.test(skill.name)) {
7186
+ throw new Error(`Invalid skill name "${skill.name}"; expected [a-z0-9-]+`);
7187
+ }
7188
+ if (seenSkills.has(skill.name)) throw new Error(`Duplicate skill name "${skill.name}"`);
7189
+ seenSkills.add(skill.name);
7190
+ const seenFiles = /* @__PURE__ */ new Set();
7191
+ for (const file of skill.files) {
7192
+ validateRelativePath(file.path, `Skill "${skill.name}" file path`);
7193
+ if (seenFiles.has(file.path)) {
7194
+ throw new Error(`Skill "${skill.name}" file path "${file.path}" is duplicated`);
7195
+ }
7196
+ seenFiles.add(file.path);
7197
+ }
7198
+ }
7199
+ const agentsDir = join2(root, ".agents");
7200
+ const destination = join2(agentsDir, "skills");
7201
+ rejectSymlink(agentsDir, "Agent metadata directory");
7202
+ rejectSymlink(destination, "Generated skill directory");
7203
+ mkdirSync2(agentsDir, { recursive: true });
7204
+ const suffix = randomUUID();
7205
+ const staged = join2(agentsDir, `.skills-stage-${suffix}`);
7206
+ const backup = join2(agentsDir, `.skills-backup-${suffix}`);
7207
+ try {
7208
+ mkdirSync2(staged);
7209
+ for (const skill of skills) {
7210
+ const skillDir = join2(staged, skill.name);
7211
+ mkdirSync2(skillDir, { recursive: true });
7212
+ for (const file of skill.files) {
7213
+ const target = resolve(skillDir, ...file.path.split("/"));
7214
+ const within = relative(skillDir, target);
7215
+ if (within === ".." || within.startsWith(`..${sep}`) || isAbsolute(within)) {
7216
+ throw new Error(`Skill "${skill.name}" file escapes its directory: ${file.path}`);
7217
+ }
7218
+ mkdirSync2(resolve(target, ".."), { recursive: true });
7219
+ writeFileSync2(target, file.content);
7220
+ }
7221
+ }
7222
+ if (existsSync3(destination)) renameSync2(destination, backup);
7223
+ try {
7224
+ renameSync2(staged, destination);
7225
+ } catch (error) {
7226
+ if (existsSync3(backup) && !existsSync3(destination)) renameSync2(backup, destination);
7227
+ throw error;
7228
+ }
7229
+ rmSync2(backup, { recursive: true, force: true });
7230
+ } finally {
7231
+ rmSync2(staged, { recursive: true, force: true });
7232
+ if (existsSync3(backup) && !existsSync3(destination)) renameSync2(backup, destination);
7233
+ else rmSync2(backup, { recursive: true, force: true });
7234
+ }
7235
+ }
7236
+
7145
7237
  // src/commands/issues.ts
7146
7238
  var systemTimezone = () => Intl.DateTimeFormat().resolvedOptions().timeZone;
7147
7239
  function printIssueLinks(links) {
@@ -7233,7 +7325,7 @@ function walkFolder(dir) {
7233
7325
  const files2 = [];
7234
7326
  const walk = (abs, rel) => {
7235
7327
  for (const entry of readdirSync(abs, { withFileTypes: true })) {
7236
- const nextAbs = join2(abs, entry.name);
7328
+ const nextAbs = join3(abs, entry.name);
7237
7329
  const nextRel = rel ? `${rel}/${entry.name}` : entry.name;
7238
7330
  if (entry.isDirectory()) walk(nextAbs, nextRel);
7239
7331
  else if (entry.isFile()) {
@@ -7693,7 +7785,7 @@ function register2(program3) {
7693
7785
  "Print an issue's effective context (the assembled bundle for its current state)"
7694
7786
  ).option(
7695
7787
  "--out <dir>",
7696
- "write the bundle to a directory: prompt.md, skills/<name>/\u2026, repos.json"
7788
+ "write the bundle to a directory: prompt.md, .agents/skills/<name>/\u2026, repos.json"
7697
7789
  ).option("--force", "allow --out into a non-empty directory")
7698
7790
  ).action(async (ref, opts) => {
7699
7791
  const api = client(opts);
@@ -7731,23 +7823,18 @@ skills: ${context2.skills.map((s) => s.name).join(", ")}`);
7731
7823
  `refusing to write: checkout-directory conflict${context2.conflicts.length === 1 ? "" : "s"} among the effective repos (${context2.conflicts.map((c) => `"${c.dir}": ${c.item_ids.join(", ")}`).join("; ")}); rename or re-dir the items first`
7732
7824
  );
7733
7825
  }
7734
- if (existsSync3(opts.out) && readdirSync(opts.out).length > 0 && !opts.force) {
7826
+ if (existsSync4(opts.out) && readdirSync(opts.out).length > 0 && !opts.force) {
7735
7827
  die(`refusing to write into non-empty directory ${opts.out} (pass --force to override)`);
7736
7828
  }
7737
- mkdirSync2(opts.out, { recursive: true });
7738
- writeFileSync2(
7739
- join2(opts.out, "prompt.md"),
7829
+ assertNoSkillRepoOverlap(context2.repos.map((repo) => repo.dir));
7830
+ mkdirSync3(opts.out, { recursive: true });
7831
+ materializeSkills(opts.out, context2.skills);
7832
+ writeFileSync3(
7833
+ join3(opts.out, "prompt.md"),
7740
7834
  context2.prompt.text ? `${context2.prompt.text}
7741
7835
  ` : ""
7742
7836
  );
7743
- for (const skill of context2.skills) {
7744
- for (const file of skill.files) {
7745
- const target = join2(opts.out, "skills", skill.name, file.path);
7746
- mkdirSync2(dirname2(target), { recursive: true });
7747
- writeFileSync2(target, file.content);
7748
- }
7749
- }
7750
- writeFileSync2(join2(opts.out, "repos.json"), `${JSON.stringify(context2.repos, null, 2)}
7837
+ writeFileSync3(join3(opts.out, "repos.json"), `${JSON.stringify(context2.repos, null, 2)}
7751
7838
  `);
7752
7839
  console.log(
7753
7840
  `wrote ${opts.out}/prompt.md, ${context2.skills.length} skill${context2.skills.length === 1 ? "" : "s"}, repos.json (${context2.repos.length} repo${context2.repos.length === 1 ? "" : "s"})`
@@ -8018,10 +8105,10 @@ files (v${artifact.current_version.version}):`);
8018
8105
  if (opts.out === void 0) {
8019
8106
  die(`artifact "${name2}" is a folder \u2014 pass --out <dir> to write its tree`);
8020
8107
  }
8021
- if (existsSync3(opts.out) && !statSync2(opts.out).isDirectory()) {
8108
+ if (existsSync4(opts.out) && !statSync2(opts.out).isDirectory()) {
8022
8109
  die(`--out for a folder must be a directory, and "${opts.out}" is a file`);
8023
8110
  }
8024
- if (existsSync3(opts.out) && readdirSync(opts.out).length > 0) {
8111
+ if (existsSync4(opts.out) && readdirSync(opts.out).length > 0) {
8025
8112
  die(
8026
8113
  `refusing to write folder artifact into non-empty directory "${opts.out}"; choose a new or empty directory`
8027
8114
  );
@@ -8033,9 +8120,9 @@ files (v${artifact.current_version.version}):`);
8033
8120
  version: opts.version,
8034
8121
  path: file.path
8035
8122
  });
8036
- const target2 = join2(opts.out, file.path);
8037
- mkdirSync2(dirname2(target2), { recursive: true });
8038
- writeFileSync2(target2, Buffer.from(content2.bytes));
8123
+ const target2 = join3(opts.out, file.path);
8124
+ mkdirSync3(dirname2(target2), { recursive: true });
8125
+ writeFileSync3(target2, Buffer.from(content2.bytes));
8039
8126
  total += content2.bytes.byteLength;
8040
8127
  }
8041
8128
  return console.log(
@@ -8046,10 +8133,10 @@ files (v${artifact.current_version.version}):`);
8046
8133
  const bytes2 = Buffer.from(content.bytes);
8047
8134
  if (opts.out !== void 0) {
8048
8135
  let target2 = opts.out;
8049
- if (existsSync3(target2) && statSync2(target2).isDirectory()) {
8050
- target2 = join2(target2, version.filename ?? name2);
8136
+ if (existsSync4(target2) && statSync2(target2).isDirectory()) {
8137
+ target2 = join3(target2, version.filename ?? name2);
8051
8138
  }
8052
- writeFileSync2(target2, bytes2);
8139
+ writeFileSync3(target2, bytes2);
8053
8140
  return console.log(`wrote ${target2} (${bytes2.byteLength} bytes, ${content.content_type})`);
8054
8141
  }
8055
8142
  if ((content.content_type ?? "").startsWith("text/")) {
@@ -8057,7 +8144,7 @@ files (v${artifact.current_version.version}):`);
8057
8144
  return;
8058
8145
  }
8059
8146
  const target = version.filename ?? name2;
8060
- writeFileSync2(target, bytes2);
8147
+ writeFileSync3(target, bytes2);
8061
8148
  console.log(`wrote ${target} (${bytes2.byteLength} bytes, ${content.content_type})`);
8062
8149
  }
8063
8150
  );
@@ -8794,17 +8881,17 @@ function register7(program3) {
8794
8881
  import { spawn as spawn3 } from "node:child_process";
8795
8882
  import {
8796
8883
  createWriteStream,
8797
- existsSync as existsSync5,
8798
- mkdirSync as mkdirSync4,
8884
+ existsSync as existsSync6,
8885
+ mkdirSync as mkdirSync5,
8799
8886
  readFileSync as readFileSync10,
8800
8887
  realpathSync,
8801
- rmSync as rmSync3,
8888
+ rmSync as rmSync4,
8802
8889
  statSync as statSync4,
8803
8890
  unlinkSync as unlinkSync2,
8804
- writeFileSync as writeFileSync3
8891
+ writeFileSync as writeFileSync4
8805
8892
  } from "node:fs";
8806
- import { randomUUID } from "node:crypto";
8807
- import { dirname as dirname4, join as join6 } from "node:path";
8893
+ import { randomUUID as randomUUID2 } from "node:crypto";
8894
+ import { dirname as dirname4, join as join7 } from "node:path";
8808
8895
  import { StringDecoder as StringDecoder2 } from "node:string_decoder";
8809
8896
 
8810
8897
  // src/version.ts
@@ -9175,7 +9262,7 @@ var CodexStreamRenderer = class {
9175
9262
 
9176
9263
  // src/daemon/codex-rollout.ts
9177
9264
  import { open, opendir, realpath, stat } from "node:fs/promises";
9178
- import { isAbsolute, join as join3, relative, resolve, sep } from "node:path";
9265
+ import { isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
9179
9266
  import { homedir as homedir2 } from "node:os";
9180
9267
  import { StringDecoder } from "node:string_decoder";
9181
9268
  var CODEX_ROLLOUT_MAX_BYTES = 128 * 1024 * 1024;
@@ -9301,7 +9388,7 @@ function reconcileCodexRollout(records, input2) {
9301
9388
  }
9302
9389
  function resolveCodexHome(env, workspace) {
9303
9390
  const configured = env.CODEX_HOME;
9304
- return configured ? resolve(workspace, configured) : join3(homedir2(), ".codex");
9391
+ return configured ? resolve2(workspace, configured) : join4(homedir2(), ".codex");
9305
9392
  }
9306
9393
  function dateDirectories(startedAt, endedAt) {
9307
9394
  const first = new Date(startedAt);
@@ -9313,7 +9400,7 @@ function dateDirectories(startedAt, endedAt) {
9313
9400
  const result = [];
9314
9401
  for (let date = first; date <= last; date = new Date(date.getTime() + 864e5)) {
9315
9402
  result.push(
9316
- join3(
9403
+ join4(
9317
9404
  String(date.getUTCFullYear()),
9318
9405
  String(date.getUTCMonth() + 1).padStart(2, "0"),
9319
9406
  String(date.getUTCDate()).padStart(2, "0")
@@ -9380,7 +9467,7 @@ async function collectCodexRequestContext(input2) {
9380
9467
  return unavailable("thread_id_missing");
9381
9468
  const dates = dateDirectories(input2.startedAt, input2.endedAt);
9382
9469
  if (!dates) return unavailable("limit_exceeded");
9383
- const sessions = join3(input2.codexHome, "sessions");
9470
+ const sessions = join4(input2.codexHome, "sessions");
9384
9471
  let root;
9385
9472
  try {
9386
9473
  root = await realpath(sessions);
@@ -9392,7 +9479,7 @@ async function collectCodexRequestContext(input2) {
9392
9479
  const deadline = Date.now() + CODEX_ROLLOUT_TIMEOUT_MS;
9393
9480
  try {
9394
9481
  for (const date of dates) {
9395
- const directory = join3(root, date);
9482
+ const directory = join4(root, date);
9396
9483
  let stream;
9397
9484
  try {
9398
9485
  stream = await opendir(directory);
@@ -9402,14 +9489,14 @@ async function collectCodexRequestContext(input2) {
9402
9489
  for await (const entry of stream) {
9403
9490
  if (++entries > 2e4 || Date.now() > deadline) return unavailable("limit_exceeded");
9404
9491
  if ((entry.isFile() || entry.isSymbolicLink()) && entry.name.startsWith("rollout-") && entry.name.endsWith(`-${input2.threadId}.jsonl`))
9405
- candidates.push(join3(directory, entry.name));
9492
+ candidates.push(join4(directory, entry.name));
9406
9493
  }
9407
9494
  }
9408
9495
  if (candidates.length === 0) return unavailable("rollout_missing");
9409
9496
  if (candidates.length !== 1) return unavailable("rollout_ambiguous");
9410
9497
  const candidate = await realpath(candidates[0]);
9411
- const inside = relative(root, candidate);
9412
- if (inside.startsWith(`..${sep}`) || inside === ".." || isAbsolute(inside))
9498
+ const inside = relative2(root, candidate);
9499
+ if (inside.startsWith(`..${sep2}`) || inside === ".." || isAbsolute2(inside))
9413
9500
  return unavailable("unsafe_path");
9414
9501
  const before = await stat(candidate);
9415
9502
  if (!before.isFile() || before.size > CODEX_ROLLOUT_MAX_BYTES)
@@ -9530,11 +9617,11 @@ var DURATION = /(\d+)\s*([dhms])/g;
9530
9617
  function parseResetTime(text4, now) {
9531
9618
  const raw = text4.trim();
9532
9619
  if (!raw) return null;
9533
- const relative2 = raw.match(/^in\s+(.+)$/i);
9534
- if (relative2) {
9620
+ const relative3 = raw.match(/^in\s+(.+)$/i);
9621
+ if (relative3) {
9535
9622
  let ms = 0;
9536
9623
  let matched = false;
9537
- for (const [, amount, unit] of relative2[1].matchAll(DURATION)) {
9624
+ for (const [, amount, unit] of relative3[1].matchAll(DURATION)) {
9538
9625
  const n = Number(amount);
9539
9626
  ms += n * { d: 864e5, h: 36e5, m: 6e4, s: 1e3 }[unit];
9540
9627
  matched = true;
@@ -9658,12 +9745,12 @@ function failure(harness, daemonVersion, reason) {
9658
9745
  };
9659
9746
  }
9660
9747
  function run(file, args) {
9661
- return new Promise((resolve2, reject) => {
9748
+ return new Promise((resolve3, reject) => {
9662
9749
  execFile(
9663
9750
  file,
9664
9751
  args,
9665
9752
  { timeout: DEADLINE_MS, maxBuffer: MAX_STDOUT },
9666
- (error, stdout) => error ? reject(error) : resolve2(stdout)
9753
+ (error, stdout) => error ? reject(error) : resolve3(stdout)
9667
9754
  );
9668
9755
  });
9669
9756
  }
@@ -9716,7 +9803,7 @@ async function discoverCodex(daemonVersion) {
9716
9803
  } catch (error) {
9717
9804
  return failure("codex", daemonVersion, error instanceof Error ? error.message : String(error));
9718
9805
  }
9719
- return new Promise((resolve2) => {
9806
+ return new Promise((resolve3) => {
9720
9807
  const child = spawn("codex", ["app-server"], { stdio: ["pipe", "pipe", "ignore"] });
9721
9808
  let buffer = "";
9722
9809
  let bytes2 = 0;
@@ -9728,7 +9815,7 @@ async function discoverCodex(daemonVersion) {
9728
9815
  settled = true;
9729
9816
  clearTimeout(timer);
9730
9817
  child.kill();
9731
- resolve2(report);
9818
+ resolve3(report);
9732
9819
  };
9733
9820
  const fail = (reason) => finish(failure("codex", daemonVersion, reason));
9734
9821
  const sendList = (cursor) => {
@@ -9826,11 +9913,11 @@ var EffortCapabilityRefresher = class {
9826
9913
 
9827
9914
  // src/daemon/cli-refresh.ts
9828
9915
  import { spawn as spawn2 } from "node:child_process";
9829
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync8 } from "node:fs";
9830
- import { dirname as dirname3, join as join4 } from "node:path";
9916
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8 } from "node:fs";
9917
+ import { dirname as dirname3, join as join5 } from "node:path";
9831
9918
 
9832
9919
  // src/daemon/support.ts
9833
- import { delimiter, sep as sep2 } from "node:path";
9920
+ import { delimiter, sep as sep3 } from "node:path";
9834
9921
  var HARNESS_KINDS = ["claude_code", "codex", "custom"];
9835
9922
  var KEEP_WORKSPACES_MODES = ["never", "failed", "always"];
9836
9923
  function keepWorkspace(mode, outcome) {
@@ -10144,7 +10231,7 @@ function pendingSelfUpdate(cli, running) {
10144
10231
  return isNewerVersion(cli.version, running) ? cli.version : null;
10145
10232
  }
10146
10233
  function pathWithin(path2, dir) {
10147
- const root = dir.endsWith(sep2) ? dir : dir + sep2;
10234
+ const root = dir.endsWith(sep3) ? dir : dir + sep3;
10148
10235
  return path2.startsWith(root);
10149
10236
  }
10150
10237
  function buildSpawnEnv(base, opts) {
@@ -10217,14 +10304,14 @@ var SecretRedactor = class {
10217
10304
  var PACKAGE = "tines";
10218
10305
  var NPM_ARGS = ["--min-release-age=0", "--no-audit", "--no-fund", "--loglevel=error"];
10219
10306
  function agentCliPrefix(configDir) {
10220
- return join4(configDir, "cli");
10307
+ return join5(configDir, "cli");
10221
10308
  }
10222
10309
  function binDirOf(prefix) {
10223
- return join4(prefix, "node_modules", ".bin");
10310
+ return join5(prefix, "node_modules", ".bin");
10224
10311
  }
10225
10312
  function installedVersion(prefix) {
10226
10313
  try {
10227
- const pkg = readFileSync8(join4(prefix, "node_modules", PACKAGE, "package.json"), "utf8");
10314
+ const pkg = readFileSync8(join5(prefix, "node_modules", PACKAGE, "package.json"), "utf8");
10228
10315
  const version = JSON.parse(pkg).version;
10229
10316
  return typeof version === "string" ? version : null;
10230
10317
  } catch {
@@ -10233,11 +10320,11 @@ function installedVersion(prefix) {
10233
10320
  }
10234
10321
  function lastGood(prefix) {
10235
10322
  const binDir = binDirOf(prefix);
10236
- if (!existsSync4(join4(binDir, PACKAGE))) return AMBIENT_CLI;
10323
+ if (!existsSync5(join5(binDir, PACKAGE))) return AMBIENT_CLI;
10237
10324
  return { binDir, version: installedVersion(prefix), source: "stale" };
10238
10325
  }
10239
10326
  function runNpm(file, args, cwd, timeoutMs) {
10240
- return new Promise((resolve2) => {
10327
+ return new Promise((resolve3) => {
10241
10328
  let output = "";
10242
10329
  let done = false;
10243
10330
  let timedOut = false;
@@ -10252,7 +10339,7 @@ function runNpm(file, args, cwd, timeoutMs) {
10252
10339
  if (done) return;
10253
10340
  done = true;
10254
10341
  clearTimeout(timer);
10255
- resolve2({ ...result, timedOut, output });
10342
+ resolve3({ ...result, timedOut, output });
10256
10343
  };
10257
10344
  child.stdout?.on("data", (data) => output += data.toString("utf8"));
10258
10345
  child.stderr?.on("data", (data) => output += data.toString("utf8"));
@@ -10270,7 +10357,7 @@ async function installAgentCli(opts) {
10270
10357
  const prefix = agentCliPrefix(opts.configDir);
10271
10358
  const timeoutMs = opts.timeoutMs ?? 6e4;
10272
10359
  try {
10273
- mkdirSync3(prefix, { recursive: true });
10360
+ mkdirSync4(prefix, { recursive: true });
10274
10361
  } catch (err) {
10275
10362
  opts.log(`agent CLI refresh: cannot create ${prefix} (${message(err)})`);
10276
10363
  return AMBIENT_CLI;
@@ -10278,8 +10365,8 @@ async function installAgentCli(opts) {
10278
10365
  const args = ["install", "--prefix", prefix, `${PACKAGE}@latest`, ...NPM_ARGS];
10279
10366
  let result = await runNpm("npm", args, prefix, timeoutMs);
10280
10367
  if (result.spawnError?.code === "ENOENT") {
10281
- const sibling = join4(dirname3(process.execPath), "npm");
10282
- if (existsSync4(sibling)) result = await runNpm(sibling, args, prefix, timeoutMs);
10368
+ const sibling = join5(dirname3(process.execPath), "npm");
10369
+ if (existsSync5(sibling)) result = await runNpm(sibling, args, prefix, timeoutMs);
10283
10370
  }
10284
10371
  if (result.code !== 0) {
10285
10372
  const fallback = lastGood(prefix);
@@ -10304,13 +10391,13 @@ function message(err) {
10304
10391
  import { arch, hostname, platform } from "node:os";
10305
10392
 
10306
10393
  // src/daemon/store.ts
10307
- import { readdirSync as readdirSync2, readFileSync as readFileSync9, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
10308
- import { join as join5 } from "node:path";
10394
+ import { readdirSync as readdirSync2, readFileSync as readFileSync9, rmSync as rmSync3, statSync as statSync3 } from "node:fs";
10395
+ import { join as join6 } from "node:path";
10309
10396
  function credentialsKey(url, name2) {
10310
10397
  return `${url.replace(/\/+$/, "")}#${name2}`;
10311
10398
  }
10312
10399
  function credentialsPath(dir) {
10313
- return join5(dir, "runners.json");
10400
+ return join6(dir, "runners.json");
10314
10401
  }
10315
10402
  function loadRunnerCredentials(dir, url, name2) {
10316
10403
  const all = readJsonFile(credentialsPath(dir));
@@ -10333,7 +10420,7 @@ function clearRunnerCredentials(dir, url, name2) {
10333
10420
  writeJsonFile(path2, all, { secret: true });
10334
10421
  }
10335
10422
  function daemonStatePath(dir, runnerId) {
10336
- return join5(dir, `daemon-state-${runnerId}.json`);
10423
+ return join6(dir, `daemon-state-${runnerId}.json`);
10337
10424
  }
10338
10425
  function loadDaemonState(path2) {
10339
10426
  const state = readJsonFile(path2);
@@ -10346,7 +10433,7 @@ function saveDaemonState(path2, runs) {
10346
10433
  writeJsonFile(path2, { runs });
10347
10434
  }
10348
10435
  function daemonDeclinesPath(dir, runnerId) {
10349
- return join5(dir, `daemon-declines-${runnerId}.json`);
10436
+ return join6(dir, `daemon-declines-${runnerId}.json`);
10350
10437
  }
10351
10438
  function loadDaemonDeclines(path2) {
10352
10439
  const state = readJsonFile(path2);
@@ -10376,10 +10463,10 @@ function processStartTimeMs(pid) {
10376
10463
  }
10377
10464
  }
10378
10465
  function workspacesDir(configDir) {
10379
- return join5(configDir, "workspaces");
10466
+ return join6(configDir, "workspaces");
10380
10467
  }
10381
10468
  function keptMarkerPath(workspace) {
10382
- return join5(workspace, "kept.json");
10469
+ return join6(workspace, "kept.json");
10383
10470
  }
10384
10471
  function writeKeptMarker(workspace, marker) {
10385
10472
  writeJsonFile(keptMarkerPath(workspace), marker);
@@ -10401,7 +10488,7 @@ function listKeptWorkspaces(configDir) {
10401
10488
  }
10402
10489
  const kept = [];
10403
10490
  for (const name2 of names) {
10404
- const path2 = join5(root, name2);
10491
+ const path2 = join6(root, name2);
10405
10492
  const marker = readKeptMarker(path2);
10406
10493
  if (marker) kept.push({ ...marker, path: path2 });
10407
10494
  }
@@ -10422,7 +10509,7 @@ function pruneKeptWorkspaces(configDir, opts) {
10422
10509
  const removed = [];
10423
10510
  for (const entry of doomed) {
10424
10511
  try {
10425
- rmSync2(entry.path, { recursive: true, force: true });
10512
+ rmSync3(entry.path, { recursive: true, force: true });
10426
10513
  removed.push(entry);
10427
10514
  } catch {
10428
10515
  }
@@ -10438,7 +10525,7 @@ function directorySizeBytes(path2) {
10438
10525
  return 0;
10439
10526
  }
10440
10527
  for (const entry of entries) {
10441
- const child = join5(path2, entry.name);
10528
+ const child = join6(path2, entry.name);
10442
10529
  if (entry.isDirectory()) total += directorySizeBytes(child);
10443
10530
  else if (entry.isFile()) {
10444
10531
  try {
@@ -10491,7 +10578,7 @@ async function uploadRawLog(run3) {
10491
10578
  if (!path2 || !run3.rawUpload) return;
10492
10579
  run3.rawSpoolPath = void 0;
10493
10580
  try {
10494
- await new Promise((resolve2) => run3.rawSpool?.end(resolve2) ?? resolve2());
10581
+ await new Promise((resolve3) => run3.rawSpool?.end(resolve3) ?? resolve3());
10495
10582
  const size = statSync4(path2).size;
10496
10583
  if (size > 0) {
10497
10584
  let body = readFileSync10(path2);
@@ -10516,7 +10603,7 @@ async function uploadRawLog(run3) {
10516
10603
  }
10517
10604
  }
10518
10605
  var log = (message3) => console.log(`[${(/* @__PURE__ */ new Date()).toISOString().slice(11, 19)}] ${message3}`);
10519
- var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
10606
+ var sleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
10520
10607
  function killTree(pid, signal) {
10521
10608
  try {
10522
10609
  process.kill(-pid, signal);
@@ -10536,10 +10623,10 @@ function pidAlive(pid) {
10536
10623
  }
10537
10624
  }
10538
10625
  async function runDaemon(opts) {
10539
- const instanceId = randomUUID();
10626
+ const instanceId = randomUUID2();
10540
10627
  const baseUrl = opts.url.replace(/\/+$/, "");
10541
- mkdirSync4(opts.configDir, { recursive: true });
10542
- mkdirSync4(workspacesDir(opts.configDir), { recursive: true, mode: 448 });
10628
+ mkdirSync5(opts.configDir, { recursive: true });
10629
+ mkdirSync5(workspacesDir(opts.configDir), { recursive: true, mode: 448 });
10543
10630
  const sweepKeptWorkspaces = () => {
10544
10631
  const removed = pruneKeptWorkspaces(opts.configDir, {
10545
10632
  maxAgeMs: opts.keepWorkspacesForHours * 36e5,
@@ -10554,10 +10641,10 @@ async function runDaemon(opts) {
10554
10641
  sweepKeptWorkspaces();
10555
10642
  const settleWorkspace = (workspace, keep, marker) => {
10556
10643
  if (!keep) {
10557
- rmSync3(workspace, { recursive: true, force: true });
10644
+ rmSync4(workspace, { recursive: true, force: true });
10558
10645
  return;
10559
10646
  }
10560
- if (!existsSync5(workspace)) return;
10647
+ if (!existsSync6(workspace)) return;
10561
10648
  try {
10562
10649
  writeKeptMarker(workspace, { ...marker, kept_at: (/* @__PURE__ */ new Date()).toISOString() });
10563
10650
  log(`run ${marker.run_id}: workspace kept at ${workspace}`);
@@ -10602,7 +10689,7 @@ async function runDaemon(opts) {
10602
10689
  const selfUpdate = opts.selfUpdate && opts.cliRefresh && managedInstall;
10603
10690
  if (opts.selfUpdate && opts.cliRefresh) {
10604
10691
  log(
10605
- managedInstall ? `self-update: on \u2014 this daemon (tines ${DAEMON_VERSION}) runs from ${prefix} and will restart once idle after a newer release is installed there` : `self-update: off \u2014 this daemon (tines ${DAEMON_VERSION}) runs from ${selfPath ?? process.argv[1] ?? "?"}, not ${prefix}; launch it from ${join6(prefix, "node_modules", ".bin", "tines")} to have refreshes apply on restart \u2014 \`tines runner install --name ${opts.name} --harness ${opts.harness.replaceAll("_", "-")}\` sets that up as a service (docs/runner-daemon.md)`
10692
+ managedInstall ? `self-update: on \u2014 this daemon (tines ${DAEMON_VERSION}) runs from ${prefix} and will restart once idle after a newer release is installed there` : `self-update: off \u2014 this daemon (tines ${DAEMON_VERSION}) runs from ${selfPath ?? process.argv[1] ?? "?"}, not ${prefix}; launch it from ${join7(prefix, "node_modules", ".bin", "tines")} to have refreshes apply on restart \u2014 \`tines runner install --name ${opts.name} --harness ${opts.harness.replaceAll("_", "-")}\` sets that up as a service (docs/runner-daemon.md)`
10606
10693
  );
10607
10694
  }
10608
10695
  let pendingUpdate = null;
@@ -10765,13 +10852,13 @@ async function runDaemon(opts) {
10765
10852
  const secretEnvValues = (assignment.env ?? []).filter((e) => e.secret).map((e) => e.value);
10766
10853
  const runId = assignment.run.id;
10767
10854
  if (table2.has(runId)) return;
10768
- const resume = assignment.resume && existsSync5(assignment.resume.workspace_path) ? assignment.resume : null;
10855
+ const resume = assignment.resume && existsSync6(assignment.resume.workspace_path) ? assignment.resume : null;
10769
10856
  if (assignment.resume && !resume) {
10770
10857
  log(
10771
10858
  `run ${runId}: resume workspace ${assignment.resume.workspace_path} is gone; launching fresh`
10772
10859
  );
10773
10860
  }
10774
- const workspace = resume ? resume.workspace_path : join6(workspacesDir(opts.configDir), runId);
10861
+ const workspace = resume ? resume.workspace_path : join7(workspacesDir(opts.configDir), runId);
10775
10862
  const issueLabel = assignment.run.issue_ref ? `${assignment.run.issue_ref.project_name}/${assignment.run.issue_ref.number}` : void 0;
10776
10863
  const run3 = {
10777
10864
  runId,
@@ -10813,25 +10900,40 @@ async function runDaemon(opts) {
10813
10900
  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`
10814
10901
  );
10815
10902
  try {
10903
+ const currentRepoDirs = assignment.bundle.repos.map((repo) => repo.dir);
10904
+ let retainedRepoDirs = [];
10905
+ if (resume) {
10906
+ const retainedPath = join7(workspace, "repos.json");
10907
+ let retained;
10908
+ try {
10909
+ retained = JSON.parse(readFileSync10(retainedPath, "utf8"));
10910
+ } catch (error) {
10911
+ throw new Error(
10912
+ `cannot safely refresh skills: retained ${retainedPath} is unreadable or invalid JSON (${message2(error)})`
10913
+ );
10914
+ }
10915
+ if (!Array.isArray(retained) || retained.some(
10916
+ (repo) => !repo || typeof repo !== "object" || typeof repo.dir !== "string"
10917
+ )) {
10918
+ throw new Error(
10919
+ `cannot safely refresh skills: retained ${retainedPath} must be an array of repositories with string dir fields`
10920
+ );
10921
+ }
10922
+ retainedRepoDirs = retained.map((repo) => repo.dir);
10923
+ }
10924
+ assertNoSkillRepoOverlap([...currentRepoDirs, ...retainedRepoDirs]);
10816
10925
  if (!resume) {
10817
- rmSync3(workspace, { recursive: true, force: true });
10818
- mkdirSync4(workspace, { recursive: true });
10926
+ rmSync4(workspace, { recursive: true, force: true });
10927
+ mkdirSync5(workspace, { recursive: true });
10819
10928
  } else {
10820
- rmSync3(join6(workspace, "kept.json"), { force: true });
10929
+ rmSync4(join7(workspace, "kept.json"), { force: true });
10821
10930
  }
10822
- writeFileSync3(join6(workspace, "prompt.md"), `${assignment.prompt}
10931
+ materializeSkills(workspace, assignment.bundle.skills);
10932
+ writeFileSync4(join7(workspace, "prompt.md"), `${assignment.prompt}
10823
10933
  `);
10824
10934
  if (!resume)
10825
- for (const skill of assignment.bundle.skills) {
10826
- for (const file of skill.files) {
10827
- const target = join6(workspace, "skills", skill.name, file.path);
10828
- mkdirSync4(dirname4(target), { recursive: true });
10829
- writeFileSync3(target, file.content);
10830
- }
10831
- }
10832
- if (!resume)
10833
- writeFileSync3(
10834
- join6(workspace, "repos.json"),
10935
+ writeFileSync4(
10936
+ join7(workspace, "repos.json"),
10835
10937
  `${JSON.stringify(assignment.bundle.repos, null, 2)}
10836
10938
  `
10837
10939
  );
@@ -10865,7 +10967,7 @@ async function runDaemon(opts) {
10865
10967
  );
10866
10968
  const harnessInput = {
10867
10969
  workspace,
10868
- promptFile: join6(workspace, "prompt.md"),
10970
+ promptFile: join7(workspace, "prompt.md"),
10869
10971
  prompt: assignment.prompt,
10870
10972
  model: assignment.run.model,
10871
10973
  effort: assignment.effort?.value ?? null,
@@ -10931,8 +11033,8 @@ async function runDaemon(opts) {
10931
11033
  (event) => limiter.noteStreamEvent(event)
10932
11034
  );
10933
11035
  run3.renderer = renderer;
10934
- const spoolPath = join6(opts.configDir, "rawlogs", `${runId}.ndjson`);
10935
- mkdirSync4(dirname4(spoolPath), { recursive: true });
11036
+ const spoolPath = join7(opts.configDir, "rawlogs", `${runId}.ndjson`);
11037
+ mkdirSync5(dirname4(spoolPath), { recursive: true });
10936
11038
  run3.rawSpoolPath = spoolPath;
10937
11039
  run3.rawSpool = createWriteStream(spoolPath);
10938
11040
  run3.rawUpload = (body) => client2.putRunLogRaw(runId, body);
@@ -11185,12 +11287,12 @@ function realpathOrNull(path2) {
11185
11287
  }
11186
11288
  }
11187
11289
  function runGit(args, cwd, batcher) {
11188
- return new Promise((resolve2) => {
11290
+ return new Promise((resolve3) => {
11189
11291
  const child = spawn3("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
11190
11292
  child.stdout?.on("data", (data) => batcher.append(data.toString("utf8")));
11191
11293
  child.stderr?.on("data", (data) => batcher.append(data.toString("utf8")));
11192
- child.on("error", () => resolve2(127));
11193
- child.on("exit", (code) => resolve2(code ?? 1));
11294
+ child.on("error", () => resolve3(127));
11295
+ child.on("exit", (code) => resolve3(code ?? 1));
11194
11296
  });
11195
11297
  }
11196
11298
 
@@ -11274,14 +11376,14 @@ function harnessFlag(harness) {
11274
11376
 
11275
11377
  // src/commands/runner-service.ts
11276
11378
  import { execFile as execFile2 } from "node:child_process";
11277
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync11, rmSync as rmSync4, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
11379
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync11, rmSync as rmSync5, statSync as statSync6, writeFileSync as writeFileSync5 } from "node:fs";
11278
11380
  import { homedir as homedir3, hostname as hostname3, userInfo } from "node:os";
11279
11381
  import { dirname as dirname6 } from "node:path";
11280
11382
  import { promisify } from "node:util";
11281
11383
 
11282
11384
  // src/daemon/service.ts
11283
- import { existsSync as existsSync6, statSync as statSync5 } from "node:fs";
11284
- import { delimiter as delimiter2, dirname as dirname5, join as join7, sep as sep3 } from "node:path";
11385
+ import { existsSync as existsSync7, statSync as statSync5 } from "node:fs";
11386
+ import { delimiter as delimiter2, dirname as dirname5, join as join8, sep as sep4 } from "node:path";
11285
11387
  function defaultServiceManager(platform2 = process.platform) {
11286
11388
  if (platform2 === "darwin") return "launchd";
11287
11389
  if (platform2 === "linux") return "systemd";
@@ -11292,13 +11394,13 @@ function serviceLabel(name2, kind) {
11292
11394
  }
11293
11395
  function unitPath(name2, kind, home) {
11294
11396
  const label = serviceLabel(name2, kind);
11295
- return kind === "launchd" ? join7(home, "Library", "LaunchAgents", `${label}.plist`) : join7(home, ".config", "systemd", "user", `${label}.service`);
11397
+ return kind === "launchd" ? join8(home, "Library", "LaunchAgents", `${label}.plist`) : join8(home, ".config", "systemd", "user", `${label}.service`);
11296
11398
  }
11297
11399
  function serviceLogPath(configDir, name2) {
11298
- return join7(configDir, "logs", `runner-${name2}.log`);
11400
+ return join8(configDir, "logs", `runner-${name2}.log`);
11299
11401
  }
11300
11402
  function managedBinary(prefix) {
11301
- return join7(prefix, "node_modules", ".bin", "tines");
11403
+ return join8(prefix, "node_modules", ".bin", "tines");
11302
11404
  }
11303
11405
  function daemonArgs(input2) {
11304
11406
  const args = [
@@ -11327,7 +11429,7 @@ function daemonArgs(input2) {
11327
11429
  function findOnPath(name2, envPath) {
11328
11430
  for (const dir of (envPath ?? "").split(delimiter2)) {
11329
11431
  if (!dir) continue;
11330
- const candidate = join7(dir, name2);
11432
+ const candidate = join8(dir, name2);
11331
11433
  try {
11332
11434
  if (statSync5(candidate).isFile()) return candidate;
11333
11435
  } catch {
@@ -11459,7 +11561,7 @@ function matchDaemonProcesses(psOutput, query2) {
11459
11561
  else if (w.startsWith("--name=")) name2 = w.slice("--name=".length);
11460
11562
  }
11461
11563
  if ((name2 ?? query2.hostname) !== query2.name) continue;
11462
- const root = query2.prefix.endsWith(sep3) ? query2.prefix : query2.prefix + sep3;
11564
+ const root = query2.prefix.endsWith(sep4) ? query2.prefix : query2.prefix + sep4;
11463
11565
  matches.push({ pid, args, managed: args.includes(root) });
11464
11566
  }
11465
11567
  return matches;
@@ -11469,7 +11571,7 @@ function daemonStartedLine(name2) {
11469
11571
  return new RegExp(`(reconnecting as runner|registered runner) "${escaped}"`);
11470
11572
  }
11471
11573
  function managedBinaryPresent(prefix) {
11472
- return existsSync6(managedBinary(prefix));
11574
+ return existsSync7(managedBinary(prefix));
11473
11575
  }
11474
11576
 
11475
11577
  // src/commands/runner-service.ts
@@ -11520,11 +11622,11 @@ async function waitForStart(logPath, offset, name2) {
11520
11622
  const pattern = daemonStartedLine(name2);
11521
11623
  const deadline = Date.now() + STARTUP_TIMEOUT_MS;
11522
11624
  while (Date.now() < deadline) {
11523
- if (existsSync7(logPath)) {
11625
+ if (existsSync8(logPath)) {
11524
11626
  const content = readFileSync11(logPath, "utf8").slice(offset);
11525
11627
  if (pattern.test(content)) return true;
11526
11628
  }
11527
- await new Promise((resolve2) => setTimeout(resolve2, 500));
11629
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
11528
11630
  }
11529
11631
  return false;
11530
11632
  }
@@ -11543,7 +11645,7 @@ function registerServiceCommands(runnerCmd) {
11543
11645
  const kind = resolveServiceManager(opts.serviceManager);
11544
11646
  const baseUrl = resolveUrl(opts).replace(/\/+$/, "");
11545
11647
  const configDir = defaultConfigDir();
11546
- mkdirSync5(configDir, { recursive: true });
11648
+ mkdirSync6(configDir, { recursive: true });
11547
11649
  const prefix = agentCliPrefix(configDir);
11548
11650
  const ps = await listProcesses();
11549
11651
  const running = ps ? matchDaemonProcesses(ps, {
@@ -11622,9 +11724,9 @@ stop it first \u2014 \`tines runners pause ${settings.name}\`, wait for 0 active
11622
11724
  logPath,
11623
11725
  env: process.env.TINES_CONFIG_DIR ? { TINES_CONFIG_DIR: process.env.TINES_CONFIG_DIR } : {}
11624
11726
  };
11625
- mkdirSync5(dirname6(target.unitPath), { recursive: true });
11626
- mkdirSync5(dirname6(logPath), { recursive: true });
11627
- writeFileSync4(target.unitPath, renderUnit(spec), { mode: 420 });
11727
+ mkdirSync6(dirname6(target.unitPath), { recursive: true });
11728
+ mkdirSync6(dirname6(logPath), { recursive: true });
11729
+ writeFileSync5(target.unitPath, renderUnit(spec), { mode: 420 });
11628
11730
  log2(`wrote ${target.unitPath}`);
11629
11731
  const offset = fileSize(logPath);
11630
11732
  await runServiceCommands(serviceCommands(target).load);
@@ -11656,7 +11758,7 @@ stop it first \u2014 \`tines runners pause ${settings.name}\`, wait for 0 active
11656
11758
  ).option("--service-manager <kind>", "launchd | systemd (default: the one this OS ships)").option("--json", "print JSON instead of a message").action(async (name2, opts) => {
11657
11759
  const kind = resolveServiceManager(opts.serviceManager);
11658
11760
  const target = serviceTarget(name2 ?? hostname3(), kind);
11659
- if (!existsSync7(target.unitPath)) {
11761
+ if (!existsSync8(target.unitPath)) {
11660
11762
  die(
11661
11763
  `"${target.name}" is not installed as a service (no ${target.unitPath}); run \`tines runner install --name ${target.name} \u2026\` first`
11662
11764
  );
@@ -11671,9 +11773,9 @@ stop it first \u2014 \`tines runners pause ${settings.name}\`, wait for 0 active
11671
11773
  ).option("--service-manager <kind>", "launchd | systemd (default: the one this OS ships)").option("--json", "print JSON instead of a message").action(async (name2, opts) => {
11672
11774
  const kind = resolveServiceManager(opts.serviceManager);
11673
11775
  const target = serviceTarget(name2 ?? hostname3(), kind);
11674
- const existed = existsSync7(target.unitPath);
11776
+ const existed = existsSync8(target.unitPath);
11675
11777
  await runServiceCommands(serviceCommands(target).unload);
11676
- rmSync4(target.unitPath, { force: true });
11778
+ rmSync5(target.unitPath, { force: true });
11677
11779
  if (opts.json)
11678
11780
  return printJson({ name: target.name, service_manager: kind, removed: existed });
11679
11781
  log2(
@@ -12752,12 +12854,12 @@ function register10(program3) {
12752
12854
  const api = client(opts);
12753
12855
  const overrides = {};
12754
12856
  for (const spec of opts.state) {
12755
- const sep4 = spec.lastIndexOf("=");
12756
- if (sep4 < 1 || sep4 === spec.length - 1) {
12857
+ const sep5 = spec.lastIndexOf("=");
12858
+ if (sep5 < 1 || sep5 === spec.length - 1) {
12757
12859
  die(`--state must look like <workflow>/<state>=<n>, got "${spec}"`);
12758
12860
  }
12759
- const limit = Number.parseInt(spec.slice(sep4 + 1), 10);
12760
- const { state } = await resolveStateFlag(api, spec.slice(0, sep4));
12861
+ const limit = Number.parseInt(spec.slice(sep5 + 1), 10);
12862
+ const { state } = await resolveStateFlag(api, spec.slice(0, sep5));
12761
12863
  overrides[state.id] = limit;
12762
12864
  }
12763
12865
  const settings = await api.updateSupervisorSettings({
@@ -13262,7 +13364,7 @@ function pinnedLookup(addresses) {
13262
13364
  }
13263
13365
  async function readHop(url, options) {
13264
13366
  const pinned = await pinnedAddresses(url, options.lookup);
13265
- return new Promise((resolve2, reject) => {
13367
+ return new Promise((resolve3, reject) => {
13266
13368
  const request = (url.protocol === "https:" ? httpsRequest : httpRequest)(
13267
13369
  url,
13268
13370
  {
@@ -13278,7 +13380,7 @@ async function readHop(url, options) {
13278
13380
  const location = response.headers.location;
13279
13381
  if (!location) return reject(new Error("publication redirect has no destination"));
13280
13382
  try {
13281
- return resolve2({ redirect: new URL(location, url) });
13383
+ return resolve3({ redirect: new URL(location, url) });
13282
13384
  } catch {
13283
13385
  return reject(new Error("publication redirect has an invalid destination"));
13284
13386
  }
@@ -13313,7 +13415,7 @@ async function readHop(url, options) {
13313
13415
  response.on("error", reject);
13314
13416
  response.on("end", () => {
13315
13417
  try {
13316
- resolve2({
13418
+ resolve3({
13317
13419
  body: new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
13318
13420
  Buffer.concat(chunks)
13319
13421
  )
@@ -13540,9 +13642,9 @@ async function resolveStateBases(states, library) {
13540
13642
  }
13541
13643
  }
13542
13644
  function resolveBasePair(lib, pair, stateName) {
13543
- const sep4 = pair.indexOf("/");
13544
- const workflowRef = pair.slice(0, sep4).trim();
13545
- const stateRef2 = pair.slice(sep4 + 1).trim();
13645
+ const sep5 = pair.indexOf("/");
13646
+ const workflowRef = pair.slice(0, sep5).trim();
13647
+ const stateRef2 = pair.slice(sep5 + 1).trim();
13546
13648
  const where = `state "${stateName}" inherits from "${pair}"`;
13547
13649
  if (!workflowRef || !stateRef2) {
13548
13650
  die(`${where}, which is not a <workflow>/<state> pair`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.277",
3
+ "version": "0.0.279",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",