@edgehero/pi-dispatch-admin 0.1.2 → 0.1.3

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.mjs CHANGED
@@ -6591,6 +6591,51 @@ ${JSON.stringify(w)}` },
6591
6591
  return toolText(JSON.stringify(result));
6592
6592
  }
6593
6593
  });
6594
+ pi.registerTool({
6595
+ name: "dispatch_pause_edit",
6596
+ label: "pi-dispatch edit pause window",
6597
+ description: "Changes fields of an existing pause window (by array index from dispatch_pauses) and applies it live. Provide only the fields to change (scope/from/to/tz/days/dateFrom/dateTo); the rest keep their current value. The operator MUST approve a confirm dialog showing the before->after; refused with no interactive operator.",
6598
+ executionMode: "sequential",
6599
+ parameters: typebox_exports.Object({
6600
+ index: typebox_exports.Integer({ minimum: 0 }),
6601
+ scope: typebox_exports.Optional(typebox_exports.String()),
6602
+ from: typebox_exports.Optional(typebox_exports.String()),
6603
+ to: typebox_exports.Optional(typebox_exports.String()),
6604
+ tz: typebox_exports.Optional(typebox_exports.String()),
6605
+ days: typebox_exports.Optional(typebox_exports.Array(typebox_exports.String())),
6606
+ dateFrom: typebox_exports.Optional(typebox_exports.String()),
6607
+ dateTo: typebox_exports.Optional(typebox_exports.String())
6608
+ }),
6609
+ async execute(_id, params, _signal, _onUpdate, ctx) {
6610
+ const paths = resolvePaths(process.env);
6611
+ const p = readPauseWindows({ pauseWindowsPath: paths.pauseWindowsPath });
6612
+ const list = Array.isArray(p?.windows) ? p.windows : [];
6613
+ const cur = list[params.index];
6614
+ if (!cur) throw new Error(`no pause window at index ${params.index} (have ${list.length})`);
6615
+ const before = buildPauseWindow(cur);
6616
+ const merged = buildPauseWindow({
6617
+ scope: params.scope ?? cur.scope,
6618
+ from: params.from ?? cur.from,
6619
+ to: params.to ?? cur.to,
6620
+ tz: params.tz ?? cur.tz,
6621
+ days: params.days ?? cur.days,
6622
+ dateFrom: params.dateFrom ?? cur.dateFrom,
6623
+ dateTo: params.dateTo ?? cur.dateTo
6624
+ });
6625
+ const result = await confirmedWrite(
6626
+ ctx,
6627
+ { title: `Edit pause window #${params.index + 1}`, message: `pause window #${params.index + 1}:
6628
+ ${JSON.stringify(before)}
6629
+ \u2192 ${JSON.stringify(merged)}` },
6630
+ () => {
6631
+ const res = writePauseWindows({ pauseWindowsPath: paths.pauseWindowsPath, mutate: (l) => l.map((w, i) => i === params.index ? merged : w) });
6632
+ if (res.invalid) throw new Error(`rejected: ${res.invalid}`);
6633
+ return { applied: true, index: params.index, window: merged };
6634
+ }
6635
+ );
6636
+ return toolText(JSON.stringify(result));
6637
+ }
6638
+ });
6594
6639
  pi.registerTool({
6595
6640
  name: "dispatch_trigger_delete",
6596
6641
  label: "pi-dispatch delete trigger",
@@ -6814,9 +6859,10 @@ async function handleDashboardAction(result, paths, ctx) {
6814
6859
  }
6815
6860
  }
6816
6861
  async function managePausesViaDialogs(paths, ui, notify) {
6817
- const action = await ui.select("Pause windows", ["Add a pause window", "Delete a pause window"]);
6862
+ const action = await ui.select("Pause windows", ["Add a pause window", "Edit a pause window", "Delete a pause window"]);
6818
6863
  if (!action) return;
6819
6864
  if (action.startsWith("Add")) return addPauseWindowViaDialogs(paths, ui, notify);
6865
+ if (action.startsWith("Edit")) return editPauseWindowViaDialogs(paths, ui, notify);
6820
6866
  return deletePauseWindowViaDialogs(paths, ui, notify);
6821
6867
  }
6822
6868
  async function addPauseWindowViaDialogs(paths, ui, notify) {
@@ -6838,6 +6884,47 @@ async function addPauseWindowViaDialogs(paths, ui, notify) {
6838
6884
  const res = writePauseWindows({ pauseWindowsPath: paths.pauseWindowsPath, mutate: (list) => [...list, w] });
6839
6885
  notify?.(res.ok ? `pause window added (live) \u2014 ${w.scope} ${w.from}-${w.to}` : `add rejected: ${res.invalid}`, res.ok ? "info" : "error");
6840
6886
  }
6887
+ async function editPauseWindowViaDialogs(paths, ui, notify) {
6888
+ const p = readPauseWindows({ pauseWindowsPath: paths.pauseWindowsPath });
6889
+ const list = Array.isArray(p?.windows) ? p.windows : [];
6890
+ if (list.length === 0) {
6891
+ notify?.("no pause windows to edit", "info");
6892
+ return;
6893
+ }
6894
+ const labels = list.map((w, i) => `#${i + 1} ${w.scope} ${w.from}-${w.to} ${w.tz}${w.days ? ` [${w.days.join(",")}]` : ""}`);
6895
+ const picked = await ui.select("Edit which pause window", labels);
6896
+ if (!picked) return;
6897
+ const index = labels.indexOf(picked);
6898
+ if (index < 0) return;
6899
+ const cur = list[index];
6900
+ const ask = async (label, current) => ui.input(`${label} \u2014 blank keeps "${current}"`, current);
6901
+ const keep = (v, current) => v === void 0 || v.trim() === "" ? current : v;
6902
+ const scope = await ask("scope", cur.scope ?? "");
6903
+ if (scope === void 0) return;
6904
+ const from = await ask("from (HH:MM)", cur.from ?? "");
6905
+ if (from === void 0) return;
6906
+ const to = await ask("to (HH:MM)", cur.to ?? "");
6907
+ if (to === void 0) return;
6908
+ const tz = await ask("tz (IANA / UTC)", cur.tz ?? "UTC");
6909
+ if (tz === void 0) return;
6910
+ const days = await ask("days (space-separated)", (cur.days ?? []).join(" "));
6911
+ if (days === void 0) return;
6912
+ const dateFrom = await ask("dateFrom (YYYY-MM-DD)", cur.dateFrom ?? "");
6913
+ if (dateFrom === void 0) return;
6914
+ const dateTo = await ask("dateTo (YYYY-MM-DD)", cur.dateTo ?? "");
6915
+ if (dateTo === void 0) return;
6916
+ const merged = buildPauseWindow({
6917
+ scope: keep(scope, cur.scope),
6918
+ from: keep(from, cur.from),
6919
+ to: keep(to, cur.to),
6920
+ tz: keep(tz, cur.tz),
6921
+ days: keep(days, cur.days),
6922
+ dateFrom: keep(dateFrom, cur.dateFrom),
6923
+ dateTo: keep(dateTo, cur.dateTo)
6924
+ });
6925
+ const res = writePauseWindows({ pauseWindowsPath: paths.pauseWindowsPath, mutate: (l) => l.map((w, i) => i === index ? merged : w) });
6926
+ notify?.(res.ok ? `pause window #${index + 1} updated (live) \u2014 ${merged.scope} ${merged.from}-${merged.to}` : `edit rejected: ${res.invalid}`, res.ok ? "info" : "error");
6927
+ }
6841
6928
  async function deletePauseWindowViaDialogs(paths, ui, notify) {
6842
6929
  const p = readPauseWindows({ pauseWindowsPath: paths.pauseWindowsPath });
6843
6930
  const list = Array.isArray(p?.windows) ? p.windows : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgehero/pi-dispatch-admin",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Operator console (a pi extension) + skill for pi-dispatch: run the pi coding agent as a self-hosted service. A /dispatch TUI for the queue, spend caps, run history, editable triggers (cron/label/comment/PR), and scheduled pause windows — plus AI-operable, human-confirmed controls. Runs against a live pi-dispatch deployment.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -30,9 +30,10 @@ dialog before it takes effect**:
30
30
  - `dispatch_set` — change a limit/setting (e.g. `dailyCap`, `weeklyCap`, `maxTurns`, `model`). Omit `value`
31
31
  to unset.
32
32
  - `dispatch_trigger_add` / `dispatch_trigger_edit` / `dispatch_trigger_delete` — manage triggers.
33
- - `dispatch_pause_add` / `dispatch_pause_delete` — manage scheduled pause windows (per folder/repo "quiet
34
- hours": runs for a scope are deferred between certain times and auto-resume after; `dispatch_pauses` lists
35
- them). Deferring never drops a job and costs no budget.
33
+ - `dispatch_pause_add` / `dispatch_pause_edit` / `dispatch_pause_delete` — manage scheduled pause windows
34
+ (per folder/repo "quiet hours": runs for a scope are deferred between certain times and auto-resume after;
35
+ `dispatch_pauses` lists them with their index). `dispatch_pause_edit` is a partial change — pass the index
36
+ plus only the fields to alter. Deferring never drops a job and costs no budget.
36
37
 
37
38
  Use them like this:
38
39