@spencerboucher/switchyard 0.7.0 → 0.8.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 (60) hide show
  1. package/README.md +1 -1
  2. package/dist/bridge/switchyard-terminal.js +96 -1
  3. package/dist/switchyard.mjs +463 -126
  4. package/package.json +1 -1
  5. package/web-dist/assets/{AutomationView-BIW_pO0M.js → AutomationView-GCthHkym.js} +2 -2
  6. package/web-dist/assets/{AutomationView-BIW_pO0M.js.map → AutomationView-GCthHkym.js.map} +1 -1
  7. package/web-dist/assets/{Badge-BiDp7NKI.js → Badge-CEMI3YyI.js} +2 -2
  8. package/web-dist/assets/{Badge-BiDp7NKI.js.map → Badge-CEMI3YyI.js.map} +1 -1
  9. package/web-dist/assets/{InteractionCard-CWxyDv81.js → InteractionCard-DaLqafmA.js} +2 -2
  10. package/web-dist/assets/{InteractionCard-CWxyDv81.js.map → InteractionCard-DaLqafmA.js.map} +1 -1
  11. package/web-dist/assets/{Menu-BDt1bxwz.js → Menu-BcKiEc6X.js} +2 -2
  12. package/web-dist/assets/{Menu-BDt1bxwz.js.map → Menu-BcKiEc6X.js.map} +1 -1
  13. package/web-dist/assets/{SegmentedControl-D-2BCZzd.js → SegmentedControl-BZPzNU08.js} +2 -2
  14. package/web-dist/assets/{SegmentedControl-D-2BCZzd.js.map → SegmentedControl-BZPzNU08.js.map} +1 -1
  15. package/web-dist/assets/{Tabs-ByiPklAw.js → Tabs-COH5EMql.js} +2 -2
  16. package/web-dist/assets/{Tabs-ByiPklAw.js.map → Tabs-COH5EMql.js.map} +1 -1
  17. package/web-dist/assets/{TextArea-BYr88SnV.js → TextArea-CeMEPZn4.js} +2 -2
  18. package/web-dist/assets/{TextArea-BYr88SnV.js.map → TextArea-CeMEPZn4.js.map} +1 -1
  19. package/web-dist/assets/{attention-Bya8XHKI.js → attention-BAdea2kA.js} +2 -2
  20. package/web-dist/assets/{attention-Bya8XHKI.js.map → attention-BAdea2kA.js.map} +1 -1
  21. package/web-dist/assets/{automations-DiXKRcnE.js → automations-DSFUzB0w.js} +2 -2
  22. package/web-dist/assets/{automations-DiXKRcnE.js.map → automations-DSFUzB0w.js.map} +1 -1
  23. package/web-dist/assets/{board-BrLaJwLJ.js → board-eQ3H8yCp.js} +2 -2
  24. package/web-dist/assets/{board-BrLaJwLJ.js.map → board-eQ3H8yCp.js.map} +1 -1
  25. package/web-dist/assets/code-D7JmYMKe.js +13 -0
  26. package/web-dist/assets/code-D7JmYMKe.js.map +1 -0
  27. package/web-dist/assets/{describe-DnvH6_fl.js → describe-C1YimBXJ.js} +2 -2
  28. package/web-dist/assets/{describe-DnvH6_fl.js.map → describe-C1YimBXJ.js.map} +1 -1
  29. package/web-dist/assets/dist-B_RpNOcK.js +56 -0
  30. package/web-dist/assets/dist-B_RpNOcK.js.map +1 -0
  31. package/web-dist/assets/{enroll-DUDpbJoB.js → enroll-1OCy5r50.js} +2 -2
  32. package/web-dist/assets/{enroll-DUDpbJoB.js.map → enroll-1OCy5r50.js.map} +1 -1
  33. package/web-dist/assets/{index-vIzH1m8u.js → index-BhpEXLav.js} +4 -4
  34. package/web-dist/assets/{index-vIzH1m8u.js.map → index-BhpEXLav.js.map} +1 -1
  35. package/web-dist/assets/index-DUeOwDZ6.css +2 -0
  36. package/web-dist/assets/{logic-xb-QF00E.js → logic-CBvFOyIo.js} +2 -2
  37. package/web-dist/assets/{logic-xb-QF00E.js.map → logic-CBvFOyIo.js.map} +1 -1
  38. package/web-dist/assets/{more-BP5x45cs.js → more-RjVyxPaz.js} +2 -2
  39. package/web-dist/assets/{more-BP5x45cs.js.map → more-RjVyxPaz.js.map} +1 -1
  40. package/web-dist/assets/{now--XjJC37b.js → now-CDjeFSLD.js} +2 -2
  41. package/web-dist/assets/{now--XjJC37b.js.map → now-CDjeFSLD.js.map} +1 -1
  42. package/web-dist/assets/{settings-D8T1esWq.js → settings-tH4UuIfJ.js} +2 -2
  43. package/web-dist/assets/{settings-D8T1esWq.js.map → settings-tH4UuIfJ.js.map} +1 -1
  44. package/web-dist/assets/{shared-CZ7fcu_B.js → shared-CRX6KSBm.js} +2 -2
  45. package/web-dist/assets/{shared-CZ7fcu_B.js.map → shared-CRX6KSBm.js.map} +1 -1
  46. package/web-dist/assets/states-DXozulGZ.js +2 -0
  47. package/web-dist/assets/states-DXozulGZ.js.map +1 -0
  48. package/web-dist/assets/{terminal-login-BJxV78au.js → terminal-login-CW2rE50W.js} +2 -2
  49. package/web-dist/assets/{terminal-login-BJxV78au.js.map → terminal-login-CW2rE50W.js.map} +1 -1
  50. package/web-dist/assets/{useNow-jxdoOC_1.js → useNow-C_B7JgMM.js} +2 -2
  51. package/web-dist/assets/{useNow-jxdoOC_1.js.map → useNow-C_B7JgMM.js.map} +1 -1
  52. package/web-dist/index.html +4 -4
  53. package/web-dist/sw.js +1 -1
  54. package/web-dist/assets/code-DKLO8bzz.js +0 -11
  55. package/web-dist/assets/code-DKLO8bzz.js.map +0 -1
  56. package/web-dist/assets/dist-BE_V9dwJ.js +0 -56
  57. package/web-dist/assets/dist-BE_V9dwJ.js.map +0 -1
  58. package/web-dist/assets/index-DvRcMGEe.css +0 -2
  59. package/web-dist/assets/states--geW2Ntl.js +0 -2
  60. package/web-dist/assets/states--geW2Ntl.js.map +0 -1
package/README.md CHANGED
@@ -118,7 +118,7 @@ switchyard up
118
118
  ```sh
119
119
  docker run -d --name switchyard -p 127.0.0.1:7400:7400 -v switchyard-data:/data \
120
120
  -e SWITCHYARD_PUBLIC_URL=https://switchyard.example.com \
121
- ghcr.io/justmytwospence/switchyard:0.7.0
121
+ ghcr.io/justmytwospence/switchyard:0.8.0
122
122
  # Single sign-on is required off localhost: see docs/auth.md
123
123
  ```
124
124
 
@@ -80,6 +80,29 @@ export default function switchyardTerminal(pi) {
80
80
  if (!socketPath)
81
81
  return;
82
82
  let ctx = null;
83
+ let completer = null;
84
+ // Work that needs a command context (reload, plugin web views) runs inside a hidden command.
85
+ const internal = new Map();
86
+ const runInternal = (fn) => {
87
+ const token = randomUUID();
88
+ internal.set(token, fn);
89
+ // Without expandPromptTemplates Pi sends the text to the model instead of running the command.
90
+ pi.sendUserMessage(`/switchyard-internal ${token}`, { expandPromptTemplates: true });
91
+ };
92
+ try {
93
+ pi.registerCommand?.("switchyard-internal", {
94
+ description: "Used by Switchyard's web app",
95
+ handler: async (args, c) => {
96
+ const key = String(args ?? "").trim();
97
+ const run = internal.get(key);
98
+ internal.delete(key);
99
+ await run?.(c);
100
+ },
101
+ });
102
+ }
103
+ catch {
104
+ // an older Pi: no web views or reload
105
+ }
83
106
  let remoteDialogs = false;
84
107
  let entryCount = 0;
85
108
  const pending = new Map();
@@ -377,6 +400,33 @@ export default function switchyardTerminal(pi) {
377
400
  const name = message.slice(1).split(/\s/)[0];
378
401
  const known = (pi.getCommands?.() ?? []).find((c) => c.name === name);
379
402
  if (known?.source === "extension") {
403
+ const args = message.slice(1 + name.length).trim();
404
+ const probe = { version: 1, name, args, probe: true };
405
+ try {
406
+ pi.events?.emit?.("switchyard.web-command.v1", probe);
407
+ }
408
+ catch {
409
+ // no web view
410
+ }
411
+ if (probe.accept === true) {
412
+ runInternal(async (c) => {
413
+ const req = { version: 1, name, args, probe: false, ctx: c };
414
+ try {
415
+ pi.events.emit("switchyard.web-command.v1", req);
416
+ const view = await Promise.race([
417
+ Promise.resolve(req.view),
418
+ new Promise((_, rej) => setTimeout(() => rej(new Error("timed out")), 15000)),
419
+ ]);
420
+ if (!view)
421
+ throw new Error(`/${name} returned no view`);
422
+ respond(id, type, { disposition: "view", view });
423
+ }
424
+ catch (e) {
425
+ respond(id, type, undefined, `/${name} failed: ${e?.message ?? e}`);
426
+ }
427
+ });
428
+ return;
429
+ }
380
430
  pi.sendUserMessage(message, { expandPromptTemplates: true });
381
431
  respond(id, type, { disposition: "handled" });
382
432
  return;
@@ -446,10 +496,13 @@ export default function switchyardTerminal(pi) {
446
496
  return;
447
497
  case "get_commands":
448
498
  respond(id, type, {
449
- commands: (pi.getCommands?.() ?? []).map((c) => ({
499
+ commands: (pi.getCommands?.() ?? [])
500
+ .filter((c) => c.name !== "switchyard-internal")
501
+ .map((c) => ({
450
502
  name: c.name,
451
503
  description: c.description ?? null,
452
504
  source: c.source,
505
+ sourceInfo: c.sourceInfo ?? null,
453
506
  })),
454
507
  });
455
508
  return;
@@ -498,6 +551,38 @@ export default function switchyardTerminal(pi) {
498
551
  }
499
552
  });
500
553
  return;
554
+ case "reload":
555
+ if (!ctx) {
556
+ respond(id, type, undefined, "Session is not ready");
557
+ return;
558
+ }
559
+ respond(id, type);
560
+ runInternal((c) => c.reload());
561
+ return;
562
+ case "complete": {
563
+ const text = String(cmd.text ?? "");
564
+ if (!completer?.getSuggestions || !/^\/\S+ /.test(text) || text.includes("\n")) {
565
+ respond(id, type, { items: [], prefix: "" });
566
+ return;
567
+ }
568
+ const ac = new AbortController();
569
+ const timer = setTimeout(() => ac.abort(), 2000);
570
+ Promise.resolve(completer.getSuggestions([text], 0, text.length, { signal: ac.signal }))
571
+ .then((s) => respond(id, type, {
572
+ items: (Array.isArray(s?.items) ? s.items : [])
573
+ .filter((i) => typeof i?.value === "string")
574
+ .slice(0, 50)
575
+ .map((i) => ({
576
+ value: i.value,
577
+ label: typeof i.label === "string" ? i.label : i.value,
578
+ description: typeof i.description === "string" ? i.description : null,
579
+ })),
580
+ prefix: typeof s?.prefix === "string" ? s.prefix : "",
581
+ }))
582
+ .catch(() => respond(id, type, { items: [], prefix: "" }))
583
+ .finally(() => clearTimeout(timer));
584
+ return;
585
+ }
501
586
  default:
502
587
  respond(id, type, undefined, `Unknown command: ${type}`);
503
588
  }
@@ -589,6 +674,16 @@ export default function switchyardTerminal(pi) {
589
674
  on("session_start", (event, c) => {
590
675
  ctx = c;
591
676
  patchUi(c);
677
+ completer = null;
678
+ try {
679
+ c.ui?.addAutocompleteProvider?.((current) => {
680
+ completer = current;
681
+ return current;
682
+ });
683
+ }
684
+ catch {
685
+ // RPC or an older Pi: no completions
686
+ }
592
687
  entryCount = c.sessionManager.getEntries().length;
593
688
  const sessionId = c.sessionManager.getSessionId();
594
689
  const sessionFile = c.sessionManager.getSessionFile() ?? null;