@tekmidian/pai 0.13.0 → 0.13.2

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.
@@ -6535,8 +6535,23 @@ async function dispatchAll(tasks, opts) {
6535
6535
  *
6536
6536
  * See Notes/docs/task-bus.md.
6537
6537
  */
6538
- /** Long enough to cover launching a terminal tab and waiting for it to settle. */
6539
- const DISPATCH_TIMEOUT_MS = 6e4;
6538
+ /**
6539
+ * How long AIBroker may spend on one dispatch, in seconds.
6540
+ *
6541
+ * A cold spawn measured ~10s on an idle machine, but boot time is not bounded:
6542
+ * under load a session can take considerably longer to start accepting input.
6543
+ */
6544
+ const DEFAULT_DISPATCH_TIMEOUT_SECS = 180;
6545
+ /**
6546
+ * Margin between AIBroker's own deadline and when we kill the process.
6547
+ *
6548
+ * These two timeouts must never disagree. If ours fired first we would kill a
6549
+ * dispatch mid-flight and report a transport failure that AIBroker cannot
6550
+ * reproduce from its own CLI — a phantom bug, in the other repo, with no trace
6551
+ * on either side. So we always pass our budget down via `--timeout` and give
6552
+ * it room to time out first and tell us why.
6553
+ */
6554
+ const KILL_MARGIN_MS = 15e3;
6540
6555
  const VALID_OUTCOMES = new Set([
6541
6556
  "delivered",
6542
6557
  "spawned",
@@ -6551,10 +6566,10 @@ const VALID_OUTCOMES = new Set([
6551
6566
  * and reasoning, so they are long, multi-line, and full of quotes and
6552
6567
  * backticks. argv would mangle them or hit length limits.
6553
6568
  */
6554
- function run(bin, args, stdin) {
6569
+ function run(bin, args, stdin, killAfterMs) {
6555
6570
  return new Promise((resolve, reject) => {
6556
6571
  execFile(bin, args, {
6557
- timeout: DISPATCH_TIMEOUT_MS,
6572
+ timeout: killAfterMs,
6558
6573
  maxBuffer: 1024 * 1024
6559
6574
  }, (error, stdout, stderr) => {
6560
6575
  if (error && !stdout.trim().startsWith("{")) {
@@ -6566,18 +6581,21 @@ function run(bin, args, stdin) {
6566
6581
  });
6567
6582
  }
6568
6583
  var AiBrokerTransport = class {
6569
- constructor(bin = "aibroker") {
6584
+ constructor(bin = "aibroker", timeoutSecs = DEFAULT_DISPATCH_TIMEOUT_SECS) {
6570
6585
  this.bin = bin;
6586
+ this.timeoutSecs = timeoutSecs;
6571
6587
  }
6572
6588
  async dispatch(project, message, opts) {
6573
6589
  const args = [
6574
6590
  "dispatch",
6575
6591
  project,
6576
6592
  "--stdin",
6577
- "--json"
6593
+ "--json",
6594
+ "--timeout",
6595
+ String(this.timeoutSecs)
6578
6596
  ];
6579
6597
  if (!opts.spawnIfAbsent) args.push("--no-spawn");
6580
- const stdout = await run(this.bin, args, message);
6598
+ const stdout = await run(this.bin, args, message, this.timeoutSecs * 1e3 + KILL_MARGIN_MS);
6581
6599
  const start = stdout.lastIndexOf("{");
6582
6600
  if (start === -1) throw new Error(`aibroker returned no JSON: ${stdout.trim().slice(0, 200)}`);
6583
6601
  let wire;
@@ -6605,14 +6623,14 @@ var AiBrokerTransport = class {
6605
6623
  * shipped for a long time without `dispatch`, and an older install would
6606
6624
  * otherwise fail once per task instead of degrading cleanly up front.
6607
6625
  */
6608
- async function detectAiBroker(bin = "aibroker") {
6626
+ async function detectAiBroker(bin = "aibroker", timeoutSecs = DEFAULT_DISPATCH_TIMEOUT_SECS) {
6609
6627
  try {
6610
6628
  return (await new Promise((resolve, reject) => {
6611
6629
  execFile(bin, ["help"], { timeout: 1e4 }, (error, stdout, stderr) => {
6612
6630
  if (error && !stdout) reject(error);
6613
6631
  else resolve(stdout + stderr);
6614
6632
  });
6615
- })).includes("dispatch") ? new AiBrokerTransport(bin) : null;
6633
+ })).includes("dispatch") ? new AiBrokerTransport(bin, timeoutSecs) : null;
6616
6634
  } catch {
6617
6635
  return null;
6618
6636
  }
@@ -6637,6 +6655,39 @@ function reportUnconfigured() {
6637
6655
  function todayIso() {
6638
6656
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6639
6657
  }
6658
+ /**
6659
+ * Prompt for a secret without echoing it.
6660
+ *
6661
+ * Never accept a token as a command-line argument: argv is visible in `ps` to
6662
+ * every user on the machine and lands in shell history verbatim.
6663
+ */
6664
+ function promptSecret(question) {
6665
+ return new Promise((resolve) => {
6666
+ const rl = createInterface({
6667
+ input: process.stdin,
6668
+ output: process.stdout,
6669
+ terminal: true
6670
+ });
6671
+ const out = process.stdout;
6672
+ const write = out.write.bind(out);
6673
+ rl._writeToOutput = (s) => {
6674
+ if (!out.muted) write(s);
6675
+ };
6676
+ write(question);
6677
+ out.muted = true;
6678
+ rl.question("", (answer) => {
6679
+ out.muted = false;
6680
+ write("\n");
6681
+ rl.close();
6682
+ resolve(answer.trim());
6683
+ });
6684
+ });
6685
+ }
6686
+ /** Show enough of a token to recognise it, never enough to use it. */
6687
+ function redact(token) {
6688
+ if (!token) return dim("(not set)");
6689
+ return token.length <= 8 ? "********" : `${token.slice(0, 4)}…${token.slice(-4)}`;
6690
+ }
6640
6691
  function renderOwner(task) {
6641
6692
  if (task.owner.project) {
6642
6693
  const via = task.owner.source === "label" ? "label" : "container";
@@ -6723,7 +6774,7 @@ function registerTaskCommands(taskCmd) {
6723
6774
  console.log(dim(" Nothing to dispatch."));
6724
6775
  return;
6725
6776
  }
6726
- const transport = opts.dryRun ? null : await detectAiBroker();
6777
+ const transport = opts.dryRun ? null : await detectAiBroker(void 0, config.tasks?.dispatchTimeoutSecs);
6727
6778
  if (!opts.dryRun && !transport) console.log(dim(" No aibroker CLI with `dispatch` found — reporting ownership only."));
6728
6779
  printResults(await dispatchAll(tasks, {
6729
6780
  transport,
@@ -6731,6 +6782,70 @@ function registerTaskCommands(taskCmd) {
6731
6782
  spawnIfAbsent: opts.spawn !== false
6732
6783
  }));
6733
6784
  });
6785
+ taskCmd.command("config").description("View or change task bus settings without running the full setup wizard").option("--token", "Prompt for the Todoist API token (input is hidden)").option("--from-env", "Adopt the token from TODOIST_API_KEY in the environment").option("--project <id>", "Tracker project ID that roots the bus (an ID, never a name)").option("--findings <id>", "Section ID for the findings inbox").option("--timeout <secs>", "Seconds a single dispatch may take", (v) => Number.parseInt(v, 10)).option("--auto-dispatch <bool>", "Hand tasks to owning sessions automatically (true/false)").option("--disable", "Turn the task bus off without discarding its settings").action(async (opts) => {
6786
+ const raw = readConfigRaw();
6787
+ const tasks = raw.tasks ?? {};
6788
+ tasks.providers ??= {};
6789
+ tasks.providers.todoist ??= {};
6790
+ const todoist = tasks.providers.todoist;
6791
+ if (!opts.token && !opts.fromEnv && !opts.project && !opts.findings && opts.timeout === void 0 && opts.autoDispatch === void 0 && !opts.disable) {
6792
+ console.log();
6793
+ console.log(` ${bold("Task bus")} ${tasks.enabled ? chalk.green("enabled") : dim("disabled")}`);
6794
+ console.log(` ${bold("Token")} ${redact(todoist.apiKey)}${!todoist.apiKey && process.env.TODOIST_API_KEY ? dim(" (TODOIST_API_KEY is set in this shell)") : ""}`);
6795
+ console.log(` ${bold("Root project")} ${todoist.rootProjectId ?? dim("(not set)")}`);
6796
+ console.log(` ${bold("Findings")} ${todoist.findingsSectionId ?? dim("(not set)")}`);
6797
+ console.log(` ${bold("Auto-dispatch")} ${tasks.autoDispatch ? chalk.green("on") : dim("off")}`);
6798
+ console.log(` ${bold("Timeout")} ${tasks.dispatchTimeoutSecs ?? dim("default (180s)")}`);
6799
+ console.log();
6800
+ console.log(dim(` ${CONFIG_FILE$2}`));
6801
+ console.log();
6802
+ return;
6803
+ }
6804
+ if (opts.token && opts.fromEnv) {
6805
+ console.error(chalk.yellow(" Use either --token or --from-env, not both."));
6806
+ process.exitCode = 1;
6807
+ return;
6808
+ }
6809
+ if (opts.fromEnv) {
6810
+ const envToken = process.env.TODOIST_API_KEY?.trim();
6811
+ if (!envToken) {
6812
+ console.error(chalk.yellow(" TODOIST_API_KEY is not set in this environment."));
6813
+ process.exitCode = 1;
6814
+ return;
6815
+ }
6816
+ todoist.apiKey = envToken;
6817
+ todoist.enabled = true;
6818
+ tasks.enabled = true;
6819
+ }
6820
+ if (opts.token) {
6821
+ const entered = await promptSecret(" Todoist API token (hidden): ");
6822
+ if (!entered) {
6823
+ console.error(chalk.yellow(" No token entered. Nothing changed."));
6824
+ process.exitCode = 1;
6825
+ return;
6826
+ }
6827
+ todoist.apiKey = entered;
6828
+ todoist.enabled = true;
6829
+ tasks.enabled = true;
6830
+ }
6831
+ if (opts.project) {
6832
+ todoist.rootProjectId = opts.project;
6833
+ tasks.enabled = true;
6834
+ }
6835
+ if (opts.findings) todoist.findingsSectionId = opts.findings;
6836
+ if (opts.timeout !== void 0) tasks.dispatchTimeoutSecs = opts.timeout;
6837
+ if (opts.autoDispatch !== void 0) tasks.autoDispatch = opts.autoDispatch === "true";
6838
+ if (opts.disable) tasks.enabled = false;
6839
+ raw.tasks = tasks;
6840
+ writeConfigRaw(raw);
6841
+ try {
6842
+ chmodSync(CONFIG_FILE$2, 384);
6843
+ } catch {
6844
+ console.log(chalk.yellow(` Could not restrict permissions on ${CONFIG_FILE$2} — check them by hand.`));
6845
+ }
6846
+ console.log(chalk.green(" Saved."));
6847
+ if (todoist.apiKey && !todoist.rootProjectId) console.log(dim(" No root project set yet — run `pai task config --project <id>`."));
6848
+ });
6734
6849
  taskCmd.command("done <id>").description("Mark a task complete on the tracker").action(async (id) => {
6735
6850
  const provider = buildProvider();
6736
6851
  if (!provider) return reportUnconfigured();
@@ -8900,4 +9015,4 @@ async function cmdPick(db, opts = {}) {
8900
9015
 
8901
9016
  //#endregion
8902
9017
  export { registerDaemonCommands as C, registerProjectsCommands as D, registerRegistryCommands as E, findMovedPath as O, registerBackupCommands as S, registerMemoryCommands as T, registerObservationCommands as _, cmdPauseAll as a, registerSetupCommand as b, cmdPause as c, registerKgCommands as d, registerTopicCommands as f, registerSkillCommands as g, registerUpdateCommand as h, cmdClearNames as i, resolveIdentifier as k, registerHelpCommand as l, registerNotifyCommands as m, cmdFind as n, cmdGoto as o, registerTaskCommands as p, cmdList as r, cmdEnd as s, cmdPick as t, registerDbCommands as u, registerZettelCommands as v, registerMcpCommands as w, registerRestoreCommands as x, registerObsidianCommands as y };
8903
- //# sourceMappingURL=pick-CS_TmOqX.mjs.map
9018
+ //# sourceMappingURL=pick-C5ziUh1b.mjs.map