@task0/cli 0.10.0 → 0.12.0

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/main.js +315 -40
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1909,11 +1909,11 @@ async function request(method, pathname, body) {
1909
1909
  }
1910
1910
  }
1911
1911
  var api = {
1912
- get: (path32) => request("GET", path32),
1913
- post: (path32, body) => request("POST", path32, body ?? {}),
1914
- put: (path32, body) => request("PUT", path32, body ?? {}),
1915
- patch: (path32, body) => request("PATCH", path32, body ?? {}),
1916
- del: (path32) => request("DELETE", path32)
1912
+ get: (path33) => request("GET", path33),
1913
+ post: (path33, body) => request("POST", path33, body ?? {}),
1914
+ put: (path33, body) => request("PUT", path33, body ?? {}),
1915
+ patch: (path33, body) => request("PATCH", path33, body ?? {}),
1916
+ del: (path33) => request("DELETE", path33)
1917
1917
  };
1918
1918
 
1919
1919
  // src/commands/task/triage.ts
@@ -4763,6 +4763,9 @@ function emitAgentRunStatus(runId, status, opts = {}) {
4763
4763
  phase: opts.phase ?? null,
4764
4764
  exit_code: opts.exitCode ?? null,
4765
4765
  error: opts.error ?? null,
4766
+ agent_session_id: opts.agentSessionId ?? null,
4767
+ agent_project: opts.agentProject ?? null,
4768
+ metadata: opts.metadata ?? null,
4766
4769
  ts: (/* @__PURE__ */ new Date()).toISOString()
4767
4770
  });
4768
4771
  if (status === "completed" || status === "failed" || status === "killed") {
@@ -4872,6 +4875,28 @@ function checkTmuxAvailable() {
4872
4875
  return { ok: false, reason: `tmux not available: ${err instanceof Error ? err.message : String(err)}` };
4873
4876
  }
4874
4877
  }
4878
+ function injectNodeBin(scriptContent) {
4879
+ const nodeBin = process.execPath;
4880
+ const nodeDir = path25.dirname(nodeBin);
4881
+ const home = process.env.HOME ?? "";
4882
+ const extraPath = [
4883
+ nodeDir,
4884
+ home ? `${home}/.local/share/mise/shims` : "",
4885
+ home ? `${home}/.local/bin` : "",
4886
+ home ? `${home}/bin` : "",
4887
+ home ? `${home}/.cargo/bin` : ""
4888
+ ].filter(Boolean).join(":");
4889
+ const injected = [
4890
+ `NODE_BIN=${JSON.stringify(nodeBin)}`,
4891
+ `export PATH=${JSON.stringify(extraPath)}:"$PATH"`,
4892
+ ""
4893
+ ].join("\n");
4894
+ const shebangMatch = scriptContent.match(/^(#![^\n]*\n)/);
4895
+ if (shebangMatch) {
4896
+ return shebangMatch[1] + injected + scriptContent.slice(shebangMatch[1].length);
4897
+ }
4898
+ return injected + scriptContent;
4899
+ }
4875
4900
  function isSessionAlive2(sessionName) {
4876
4901
  try {
4877
4902
  execFileSync(getTmuxBin(), ["has-session", "-t", sessionName], {
@@ -4911,7 +4936,17 @@ function deriveStatus(parsed) {
4911
4936
  else if (raw === "error" || raw === "failed") status = "failed";
4912
4937
  const phase = typeof parsed.phase === "string" ? parsed.phase : null;
4913
4938
  const error2 = typeof parsed.error === "string" && parsed.error ? parsed.error : null;
4914
- return { status, phase, error: error2 };
4939
+ const agentSessionId = typeof parsed.agentSessionId === "string" && parsed.agentSessionId ? parsed.agentSessionId : null;
4940
+ const agentProject = typeof parsed.agentProject === "string" && parsed.agentProject ? parsed.agentProject : null;
4941
+ let metadata = null;
4942
+ if (parsed.metadata && typeof parsed.metadata === "object") {
4943
+ const out = {};
4944
+ for (const [k, v] of Object.entries(parsed.metadata)) {
4945
+ if (typeof k === "string" && typeof v === "string") out[k] = v;
4946
+ }
4947
+ if (Object.keys(out).length > 0) metadata = out;
4948
+ }
4949
+ return { status, phase, error: error2, agentSessionId, agentProject, metadata };
4915
4950
  }
4916
4951
  function launchAgentRun(params) {
4917
4952
  const tmuxCheck = checkTmuxAvailable();
@@ -4926,9 +4961,11 @@ function launchAgentRun(params) {
4926
4961
  }
4927
4962
  const runDir = ensureAgentRunDir(params.runId);
4928
4963
  const scriptPath = path25.join(runDir, "script.sh");
4929
- fs27.writeFileSync(scriptPath, params.scriptContent, { mode: 493 });
4964
+ const scriptWithNode = injectNodeBin(params.scriptContent);
4965
+ fs27.writeFileSync(scriptPath, scriptWithNode, { mode: 493 });
4930
4966
  if (params.promptContent !== void 0) {
4931
- fs27.writeFileSync(path25.join(runDir, "prompt.txt"), params.promptContent, "utf-8");
4967
+ const expanded = params.promptContent.replace(/__TASK0_AGENT_RUN_DIR__/g, runDir);
4968
+ fs27.writeFileSync(path25.join(runDir, "prompt.txt"), expanded, "utf-8");
4932
4969
  }
4933
4970
  for (const [name, content] of Object.entries(params.auxFiles ?? {})) {
4934
4971
  if (name.includes("/") || name.includes("\\") || name === ".." || name === ".") {
@@ -4970,7 +5007,20 @@ function launchAgentRun(params) {
4970
5007
  return;
4971
5008
  }
4972
5009
  if (!isSessionAlive2(cur.sessionName)) {
4973
- finishRun(params.runId, "killed", { error: "tmux session disappeared" });
5010
+ const final = readStatusFile(params.runId);
5011
+ const derived = final ? deriveStatus(final.parsed) : null;
5012
+ if (derived && (derived.status === "completed" || derived.status === "failed")) {
5013
+ emitAgentRunStatus(params.runId, derived.status, {
5014
+ phase: derived.phase,
5015
+ error: derived.error,
5016
+ agentSessionId: derived.agentSessionId,
5017
+ agentProject: derived.agentProject,
5018
+ metadata: derived.metadata
5019
+ });
5020
+ finishRun(params.runId, derived.status, { phase: derived.phase, error: derived.error });
5021
+ } else {
5022
+ finishRun(params.runId, "killed", { error: "tmux session disappeared" });
5023
+ }
4974
5024
  clearInterval(sessionProbe);
4975
5025
  }
4976
5026
  }, SESSION_PROBE_MS);
@@ -4983,10 +5033,16 @@ function pollOne(runId) {
4983
5033
  if (!current) return;
4984
5034
  if (current.raw === entry.lastStatusJson) return;
4985
5035
  entry.lastStatusJson = current.raw;
4986
- const { status, phase, error: error2 } = deriveStatus(current.parsed);
4987
- emitAgentRunStatus(runId, status, { phase, error: error2 });
4988
- if (status === "completed" || status === "failed") {
4989
- finishRun(runId, status, { phase, error: error2 });
5036
+ const derived = deriveStatus(current.parsed);
5037
+ emitAgentRunStatus(runId, derived.status, {
5038
+ phase: derived.phase,
5039
+ error: derived.error,
5040
+ agentSessionId: derived.agentSessionId,
5041
+ agentProject: derived.agentProject,
5042
+ metadata: derived.metadata
5043
+ });
5044
+ if (derived.status === "completed" || derived.status === "failed") {
5045
+ finishRun(runId, derived.status, { phase: derived.phase, error: derived.error });
4990
5046
  }
4991
5047
  }
4992
5048
  function finishRun(runId, status, opts = {}) {
@@ -6093,6 +6149,55 @@ function run2(cmd, args) {
6093
6149
  const res = spawnSync6(cmd, args, { encoding: "utf-8" });
6094
6150
  return { code: res.status ?? -1, stdout: res.stdout ?? "", stderr: res.stderr ?? "" };
6095
6151
  }
6152
+ function sleep2(ms) {
6153
+ return new Promise((resolve) => setTimeout(resolve, ms));
6154
+ }
6155
+ function launchdServicePid(scope) {
6156
+ const res = run2("launchctl", ["print", serviceTarget(scope)]);
6157
+ if (res.code !== 0) return null;
6158
+ const m = res.stdout.match(/\bpid\s*=\s*(\d+)/);
6159
+ if (!m) return null;
6160
+ const pid = parseInt(m[1], 10);
6161
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
6162
+ }
6163
+ function pidAlive(pid) {
6164
+ try {
6165
+ process.kill(pid, 0);
6166
+ return true;
6167
+ } catch (err) {
6168
+ return err.code === "EPERM";
6169
+ }
6170
+ }
6171
+ function findOrphanDaemonPids(mainPath, excludePid) {
6172
+ const res = run2("ps", ["-Ewwo", "pid=,command="]);
6173
+ if (res.code !== 0) return [];
6174
+ const expectedHome = process.env.TASK0_HOME ?? "";
6175
+ const pids = [];
6176
+ for (const line of res.stdout.split("\n")) {
6177
+ const m = line.match(/^\s*(\d+)\s+(.*)$/);
6178
+ if (!m) continue;
6179
+ const pid = parseInt(m[1], 10);
6180
+ if (!Number.isFinite(pid) || pid <= 0) continue;
6181
+ if (excludePid !== null && pid === excludePid) continue;
6182
+ if (pid === process.pid) continue;
6183
+ const cmd = m[2];
6184
+ if (!cmd.includes(mainPath)) continue;
6185
+ if (!/\bdaemon\s+run\b/.test(cmd)) continue;
6186
+ const homeMatch = cmd.match(/\bTASK0_HOME=([^\s]*)/);
6187
+ const actualHome = homeMatch ? homeMatch[1] : "";
6188
+ if (actualHome !== expectedHome) continue;
6189
+ pids.push(pid);
6190
+ }
6191
+ return pids;
6192
+ }
6193
+ async function waitForPidExit(pid, timeoutMs) {
6194
+ const start = Date.now();
6195
+ while (Date.now() - start < timeoutMs) {
6196
+ if (!pidAlive(pid)) return true;
6197
+ await sleep2(100);
6198
+ }
6199
+ return false;
6200
+ }
6096
6201
  function createLaunchdManager(scope) {
6097
6202
  const file = unitPath(scope);
6098
6203
  const logs = logPaths();
@@ -6141,19 +6246,65 @@ function createLaunchdManager(scope) {
6141
6246
  }
6142
6247
  }
6143
6248
  async function start() {
6144
- const res = run2("launchctl", ["kickstart", "-k", serviceTarget(scope)]);
6145
- if (res.code !== 0) {
6146
- const load = run2("launchctl", ["load", "-w", file]);
6147
- if (load.code !== 0) {
6148
- throw new Error(`launchctl start failed: ${res.stderr || load.stderr}`);
6249
+ if (!fs30.existsSync(file)) {
6250
+ throw new Error(
6251
+ `service unit ${file} is missing \u2014 run \`task0 daemon register\` first to install it`
6252
+ );
6253
+ }
6254
+ const kick = run2("launchctl", ["kickstart", "-k", serviceTarget(scope)]);
6255
+ if (kick.code !== 0) {
6256
+ const bootstrap = run2("launchctl", ["bootstrap", domainTarget(scope), file]);
6257
+ if (bootstrap.code !== 0) {
6258
+ const legacy = run2("launchctl", ["load", "-w", file]);
6259
+ if (legacy.code !== 0) {
6260
+ throw new Error(
6261
+ `launchctl start failed: ${kick.stderr || bootstrap.stderr || legacy.stderr}`
6262
+ );
6263
+ }
6149
6264
  }
6150
6265
  }
6266
+ for (let i = 0; i < 30; i += 1) {
6267
+ if (launchdServicePid(scope) != null) return;
6268
+ await sleep2(100);
6269
+ }
6270
+ throw new Error(
6271
+ `daemon did not start within 3s \u2014 check logs at ${logs.err}`
6272
+ );
6151
6273
  }
6152
6274
  async function stop() {
6153
- const res = run2("launchctl", ["kill", "SIGTERM", serviceTarget(scope)]);
6154
- if (res.code !== 0) {
6275
+ const launchdPid = launchdServicePid(scope);
6276
+ const bootout = run2("launchctl", ["bootout", serviceTarget(scope)]);
6277
+ if (bootout.code !== 0) {
6155
6278
  run2("launchctl", ["unload", file]);
6156
6279
  }
6280
+ if (launchdPid !== null) {
6281
+ const exited = await waitForPidExit(launchdPid, 5e3);
6282
+ if (!exited) {
6283
+ try {
6284
+ process.kill(launchdPid, "SIGKILL");
6285
+ } catch {
6286
+ }
6287
+ }
6288
+ }
6289
+ const inv = resolveTask0Invocation();
6290
+ const orphans = findOrphanDaemonPids(inv.main, launchdPid);
6291
+ for (const pid of orphans) {
6292
+ try {
6293
+ process.kill(pid, "SIGTERM");
6294
+ } catch {
6295
+ }
6296
+ }
6297
+ if (orphans.length > 0) {
6298
+ await sleep2(500);
6299
+ for (const pid of orphans) {
6300
+ if (pidAlive(pid)) {
6301
+ try {
6302
+ process.kill(pid, "SIGKILL");
6303
+ } catch {
6304
+ }
6305
+ }
6306
+ }
6307
+ }
6157
6308
  }
6158
6309
  async function status() {
6159
6310
  if (!fs30.existsSync(file)) return "absent";
@@ -6327,15 +6478,119 @@ function readCliVersion() {
6327
6478
  return cached2;
6328
6479
  }
6329
6480
 
6481
+ // src/core/project-watcher.ts
6482
+ init_node();
6483
+ import fs32 from "fs";
6484
+ import path31 from "path";
6485
+ var ProjectWatcher = class {
6486
+ perProject = /* @__PURE__ */ new Map();
6487
+ debounceTimers = /* @__PURE__ */ new Map();
6488
+ debounceMs;
6489
+ onChange;
6490
+ constructor(opts) {
6491
+ this.onChange = opts.onChange;
6492
+ this.debounceMs = opts.debounceMs ?? 300;
6493
+ }
6494
+ /**
6495
+ * Reconcile watchers against the desired project list. Idempotent — call it
6496
+ * whenever the daemon's project set changes (project_add / project_remove /
6497
+ * project_set_enabled RPCs, or on reconnect with the latest config).
6498
+ */
6499
+ setProjects(projects) {
6500
+ const wanted = /* @__PURE__ */ new Map();
6501
+ for (const p of projects) {
6502
+ if (p.enabled) wanted.set(p.name, p);
6503
+ }
6504
+ for (const [name, entry] of this.perProject) {
6505
+ const target = wanted.get(name);
6506
+ if (!target || path31.resolve(target.path) !== entry.rootPath) {
6507
+ this.stopProject(name);
6508
+ }
6509
+ }
6510
+ for (const [name, proj] of wanted) {
6511
+ if (!this.perProject.has(name)) {
6512
+ this.startProject(proj);
6513
+ }
6514
+ }
6515
+ }
6516
+ close() {
6517
+ for (const name of [...this.perProject.keys()]) this.stopProject(name);
6518
+ for (const [, t] of this.debounceTimers) clearTimeout(t);
6519
+ this.debounceTimers.clear();
6520
+ }
6521
+ startProject(proj) {
6522
+ const rootPath = path31.resolve(proj.path);
6523
+ const projectYml = path31.join(rootPath, "task0.yml");
6524
+ const cfg = readYaml(projectYml);
6525
+ if (!cfg || typeof cfg.tasks_dir !== "string" || cfg.tasks_dir.length === 0) {
6526
+ return;
6527
+ }
6528
+ const tasksDir = path31.resolve(rootPath, cfg.tasks_dir);
6529
+ const watchers = [];
6530
+ try {
6531
+ const w = fs32.watch(tasksDir, { recursive: true }, () => this.notify(proj.name));
6532
+ w.on("error", () => {
6533
+ });
6534
+ watchers.push(w);
6535
+ } catch {
6536
+ }
6537
+ try {
6538
+ const parentDir = path31.dirname(tasksDir);
6539
+ const tasksDirName = path31.basename(tasksDir);
6540
+ const tarName = tasksDirName + ".tar";
6541
+ const w = fs32.watch(parentDir, (_event, filename) => {
6542
+ if (!filename) return;
6543
+ if (filename === tasksDirName || filename === tarName) {
6544
+ this.notify(proj.name);
6545
+ }
6546
+ });
6547
+ w.on("error", () => {
6548
+ });
6549
+ watchers.push(w);
6550
+ } catch {
6551
+ }
6552
+ this.perProject.set(proj.name, { watchers, rootPath });
6553
+ }
6554
+ stopProject(name) {
6555
+ const entry = this.perProject.get(name);
6556
+ if (!entry) return;
6557
+ for (const w of entry.watchers) {
6558
+ try {
6559
+ w.close();
6560
+ } catch {
6561
+ }
6562
+ }
6563
+ this.perProject.delete(name);
6564
+ const timer = this.debounceTimers.get(name);
6565
+ if (timer) {
6566
+ clearTimeout(timer);
6567
+ this.debounceTimers.delete(name);
6568
+ }
6569
+ }
6570
+ notify(projectName) {
6571
+ const prev = this.debounceTimers.get(projectName);
6572
+ if (prev) clearTimeout(prev);
6573
+ const t = setTimeout(() => {
6574
+ this.debounceTimers.delete(projectName);
6575
+ try {
6576
+ this.onChange(projectName);
6577
+ } catch (err) {
6578
+ console.error(`project watcher onChange threw for ${projectName}:`, err);
6579
+ }
6580
+ }, this.debounceMs);
6581
+ this.debounceTimers.set(projectName, t);
6582
+ }
6583
+ };
6584
+
6330
6585
  // src/commands/daemon.ts
6331
- async function dispatchRpc(ws, id, method, params) {
6586
+ async function dispatchRpc(ws, id, method, params, notifyManifestChanged) {
6332
6587
  const handler = rpcHandlers[method];
6333
6588
  if (!handler) {
6334
6589
  sendRpc(ws, { type: "rpc_error", id, error: { code: "unknown_method", message: `unknown method: ${method}` } });
6335
6590
  return;
6336
6591
  }
6337
6592
  try {
6338
- const ctx = { notifyManifestChanged: () => pushManifest(ws) };
6593
+ const ctx = { notifyManifestChanged };
6339
6594
  const result = await handler(params ?? {}, ctx);
6340
6595
  sendRpc(ws, { type: "rpc_response", id, result });
6341
6596
  } catch (error2) {
@@ -6382,6 +6637,9 @@ function buildManifest() {
6382
6637
  }
6383
6638
  return { type: "manifest", projects, tasks, scan_errors: scanErrors };
6384
6639
  }
6640
+ function currentWatchedProjects() {
6641
+ return loadConfig().sources.filter((source2) => source2.type === "project").map((source2) => ({ name: source2.name, path: source2.path, enabled: source2.enabled }));
6642
+ }
6385
6643
  function pushManifest(ws) {
6386
6644
  if (ws.readyState !== ws.OPEN) return;
6387
6645
  const manifest = buildManifest();
@@ -6599,11 +6857,21 @@ daemonCmd.command("run").description("Run the daemon WebSocket loop in foregroun
6599
6857
  let reconnectDelay = 1e3;
6600
6858
  let shouldRun = true;
6601
6859
  let activeWs = null;
6860
+ let activeWatcher = null;
6602
6861
  function connect() {
6603
6862
  const ws = new WebSocket(wsUrl, {
6604
6863
  headers: { authorization: `Bearer ${identity.token}` }
6605
6864
  });
6606
6865
  activeWs = ws;
6866
+ const watcher = new ProjectWatcher({
6867
+ onChange: () => pushManifest(ws),
6868
+ debounceMs: 300
6869
+ });
6870
+ activeWatcher = watcher;
6871
+ const notifyManifestChanged = () => {
6872
+ pushManifest(ws);
6873
+ watcher.setProjects(currentWatchedProjects());
6874
+ };
6607
6875
  ws.on("open", () => {
6608
6876
  reconnectDelay = 1e3;
6609
6877
  console.log(chalk19.green(`[${(/* @__PURE__ */ new Date()).toISOString()}] connected`));
@@ -6629,6 +6897,7 @@ daemonCmd.command("run").description("Run the daemon WebSocket loop in foregroun
6629
6897
  const manifest = buildManifest();
6630
6898
  ws.send(JSON.stringify(manifest));
6631
6899
  console.log(chalk19.dim(`pushed manifest: ${manifest.projects.length} project(s)`));
6900
+ watcher.setProjects(currentWatchedProjects());
6632
6901
  const sink = {
6633
6902
  send: (frame) => {
6634
6903
  if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(frame));
@@ -6651,7 +6920,7 @@ daemonCmd.command("run").description("Run the daemon WebSocket loop in foregroun
6651
6920
  } else if (msg.type === "error") {
6652
6921
  console.error(chalk19.yellow(`server error: ${msg.message}`));
6653
6922
  } else if (msg.type === "rpc_request") {
6654
- void dispatchRpc(ws, msg.id, msg.method, msg.params);
6923
+ void dispatchRpc(ws, msg.id, msg.method, msg.params, notifyManifestChanged);
6655
6924
  } else if (msg.type === "agent_run_resume_request") {
6656
6925
  const sent = replayAfterRanges(msg.ranges);
6657
6926
  if (sent > 0) {
@@ -6662,6 +6931,8 @@ daemonCmd.command("run").description("Run the daemon WebSocket loop in foregroun
6662
6931
  ws.on("close", (code, reason) => {
6663
6932
  activeWs = null;
6664
6933
  bindAgentRunFrameSink(null);
6934
+ watcher.close();
6935
+ if (activeWatcher === watcher) activeWatcher = null;
6665
6936
  const reasonText = reason.toString("utf-8") || "no reason";
6666
6937
  console.log(chalk19.yellow(`[${(/* @__PURE__ */ new Date()).toISOString()}] disconnected (code=${code}, ${reasonText})`));
6667
6938
  if (code === 4001) {
@@ -6679,6 +6950,10 @@ daemonCmd.command("run").description("Run the daemon WebSocket loop in foregroun
6679
6950
  }
6680
6951
  const shutdown = () => {
6681
6952
  shouldRun = false;
6953
+ try {
6954
+ activeWatcher?.close();
6955
+ } catch {
6956
+ }
6682
6957
  try {
6683
6958
  activeWs?.close(1e3, "shutdown");
6684
6959
  } catch {
@@ -6807,8 +7082,8 @@ function fail7(message, code = 1) {
6807
7082
  console.error(chalk20.red(message));
6808
7083
  process.exit(code);
6809
7084
  }
6810
- async function callServer(path32, init = {}) {
6811
- const url = `${serverBase2()}${path32}`;
7085
+ async function callServer(path33, init = {}) {
7086
+ const url = `${serverBase2()}${path33}`;
6812
7087
  let auth;
6813
7088
  try {
6814
7089
  auth = adminAuthHeader();
@@ -7313,8 +7588,8 @@ automation.command("runs <id>").description("List recent runs for an automation"
7313
7588
 
7314
7589
  // src/commands/profile.ts
7315
7590
  init_node();
7316
- import fs32 from "fs";
7317
- import path31 from "path";
7591
+ import fs33 from "fs";
7592
+ import path32 from "path";
7318
7593
  import { Command as Command23 } from "commander";
7319
7594
  import chalk23 from "chalk";
7320
7595
  import yaml11 from "js-yaml";
@@ -7327,19 +7602,19 @@ function fail9(msg) {
7327
7602
  process.exit(1);
7328
7603
  }
7329
7604
  function readDaemonAt(dir) {
7330
- const file = path31.join(dir, "daemon.json");
7331
- if (!fs32.existsSync(file)) return null;
7605
+ const file = path32.join(dir, "daemon.json");
7606
+ if (!fs33.existsSync(file)) return null;
7332
7607
  try {
7333
- return JSON.parse(fs32.readFileSync(file, "utf-8"));
7608
+ return JSON.parse(fs33.readFileSync(file, "utf-8"));
7334
7609
  } catch {
7335
7610
  return null;
7336
7611
  }
7337
7612
  }
7338
7613
  function readProfileApiUrl(name) {
7339
- const file = path31.join(profileDir(name), "config.yml");
7340
- if (!fs32.existsSync(file)) return void 0;
7614
+ const file = path32.join(profileDir(name), "config.yml");
7615
+ if (!fs33.existsSync(file)) return void 0;
7341
7616
  try {
7342
- const data = yaml11.load(fs32.readFileSync(file, "utf-8"));
7617
+ const data = yaml11.load(fs33.readFileSync(file, "utf-8"));
7343
7618
  return data?.api_url;
7344
7619
  } catch {
7345
7620
  return void 0;
@@ -7368,10 +7643,10 @@ profile2.command("add <name>").description("Create a new profile directory (use
7368
7643
  fail9(`"${DEFAULT_PROFILE_NAME}" is reserved; it always exists.`);
7369
7644
  }
7370
7645
  const dir = profileDir(name);
7371
- if (fs32.existsSync(dir)) {
7646
+ if (fs33.existsSync(dir)) {
7372
7647
  fail9(`Profile "${name}" already exists at ${dir}.`);
7373
7648
  }
7374
- fs32.mkdirSync(dir, { recursive: true });
7649
+ fs33.mkdirSync(dir, { recursive: true });
7375
7650
  const prev = process.env.TASK0_PROFILE;
7376
7651
  process.env.TASK0_PROFILE = name;
7377
7652
  try {
@@ -7395,14 +7670,14 @@ profile2.command("remove <name>").description('Delete a profile directory (refus
7395
7670
  fail9(`"${DEFAULT_PROFILE_NAME}" cannot be removed.`);
7396
7671
  }
7397
7672
  const dir = profileDir(name);
7398
- if (!fs32.existsSync(dir)) {
7673
+ if (!fs33.existsSync(dir)) {
7399
7674
  fail9(`Profile "${name}" not found.`);
7400
7675
  }
7401
7676
  const current = currentProfileName();
7402
7677
  if (current === name && !opts.force) {
7403
7678
  fail9(`Profile "${name}" is current. Re-run with --force to remove it.`);
7404
7679
  }
7405
- fs32.rmSync(dir, { recursive: true, force: true });
7680
+ fs33.rmSync(dir, { recursive: true, force: true });
7406
7681
  if (current === name) {
7407
7682
  writeCurrentProfile(null);
7408
7683
  console.log(chalk23.dim('Current profile reset to "default".'));
@@ -7413,7 +7688,7 @@ profile2.command("use <name>").description("Set the current profile").action((na
7413
7688
  if (!isValidProfileName(name)) {
7414
7689
  fail9(`Invalid profile name "${name}".`);
7415
7690
  }
7416
- if (!fs32.existsSync(profileDir(name))) {
7691
+ if (!fs33.existsSync(profileDir(name))) {
7417
7692
  const names = listProfileNames();
7418
7693
  fail9(`Profile "${name}" not found. Available: ${names.join(", ")}.`);
7419
7694
  }
@@ -7448,7 +7723,7 @@ profile2.command("show [name]").description("Show a profile's configuration and
7448
7723
  fail9(`Invalid profile name "${target}".`);
7449
7724
  }
7450
7725
  const dir = profileDir(target);
7451
- if (!fs32.existsSync(dir)) {
7726
+ if (!fs33.existsSync(dir)) {
7452
7727
  fail9(`Profile "${target}" not found.`);
7453
7728
  }
7454
7729
  const current = currentProfileName();
@@ -7458,7 +7733,7 @@ profile2.command("show [name]").description("Show a profile's configuration and
7458
7733
  const effective = envOverride ?? apiUrl;
7459
7734
  console.log(`${chalk23.bold(target)}${current === target ? chalk23.green(" (current)") : ""}`);
7460
7735
  console.log(` dir: ${dir}`);
7461
- console.log(` config: ${path31.join(dir, "config.yml")}`);
7736
+ console.log(` config: ${path32.join(dir, "config.yml")}`);
7462
7737
  if (envOverride && apiUrl && envOverride !== apiUrl) {
7463
7738
  console.log(` api_url: ${envOverride} ${chalk23.dim("(TASK0_API_URL env, overrides profile)")}`);
7464
7739
  console.log(` ${chalk23.dim(`profile: ${apiUrl}`)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@task0/cli",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "task0 — task-centric agent workflow CLI",
5
5
  "homepage": "https://github.com/cy0-labs/task0#readme",
6
6
  "repository": {