pilotswarm 0.5.48 → 0.5.50

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 (68) hide show
  1. package/package.json +2 -2
  2. package/ui/core/src/controller.js +19 -3
  3. package/ui/core/src/selectors.js +41 -4
  4. package/ui/react/src/web-app.js +124 -29
  5. package/web/dist/assets/{abnfDiagram-VRR7QNED-6rYy4Wj8.js → abnfDiagram-VRR7QNED-C_4vfIbv.js} +1 -1
  6. package/web/dist/assets/{arc-CrRZ1Za7.js → arc-DOWqX-WF.js} +1 -1
  7. package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-CPe2QuQL.js → architectureDiagram-ZJ3FMSHR-Cc38vIUp.js} +1 -1
  8. package/web/dist/assets/{blockDiagram-677ZJIJ3-DfT6vWOg.js → blockDiagram-677ZJIJ3-CJasxBgg.js} +1 -1
  9. package/web/dist/assets/{c4Diagram-LMCZKHZV-BO-VXBT8.js → c4Diagram-LMCZKHZV-CSsFEvwn.js} +1 -1
  10. package/web/dist/assets/channel-CJecP6sr.js +1 -0
  11. package/web/dist/assets/{chunk-2Q5K7J3B-nq8p0xwC.js → chunk-2Q5K7J3B-Cdcmo5W3.js} +1 -1
  12. package/web/dist/assets/{chunk-32BRIVSS-DstJjLIA.js → chunk-32BRIVSS-CmCFzR3z.js} +1 -1
  13. package/web/dist/assets/{chunk-5VM5RSS4-Bq0MPH_Q.js → chunk-5VM5RSS4-gZUtbZIc.js} +1 -1
  14. package/web/dist/assets/{chunk-EX3LRPZG-BPofXJWl.js → chunk-EX3LRPZG-nF9v3PEA.js} +1 -1
  15. package/web/dist/assets/{chunk-JWPE2WC7-BBr7v6mo.js → chunk-JWPE2WC7-BhwKbO0c.js} +1 -1
  16. package/web/dist/assets/{chunk-MOJQB5TN-DA7ca6Jr.js → chunk-MOJQB5TN-B8zfb4XG.js} +1 -1
  17. package/web/dist/assets/{chunk-RYQCIY6F-fdXZe4nw.js → chunk-RYQCIY6F-SM2kSUT1.js} +1 -1
  18. package/web/dist/assets/{chunk-V7JOEXUC-BSATEdk2.js → chunk-V7JOEXUC-C7lbdZCo.js} +1 -1
  19. package/web/dist/assets/{chunk-VR4S4FIN-RR27eQST.js → chunk-VR4S4FIN-BZR3_tbV.js} +1 -1
  20. package/web/dist/assets/{chunk-XXDRQBXY-Bh8VimxP.js → chunk-XXDRQBXY-DU5shflv.js} +1 -1
  21. package/web/dist/assets/classDiagram-OUVF2IWQ-BqWAphrT.js +1 -0
  22. package/web/dist/assets/classDiagram-v2-EOCWNBFH-BqWAphrT.js +1 -0
  23. package/web/dist/assets/{cose-bilkent-JH36ORCC-eP239MQJ.js → cose-bilkent-JH36ORCC-O3mfbu3h.js} +1 -1
  24. package/web/dist/assets/{cynefin-VYW2F7L2-Sb8l4Vm6.js → cynefin-VYW2F7L2-kJrtGP23.js} +1 -1
  25. package/web/dist/assets/{cynefinDiagram-TSTJHNR4-BrWMkhw9.js → cynefinDiagram-TSTJHNR4-DBJPD4ep.js} +1 -1
  26. package/web/dist/assets/{dagre-VKFMJZFB-DT5kNKEx.js → dagre-VKFMJZFB-Dcs8wwU7.js} +1 -1
  27. package/web/dist/assets/{diagram-FQU43EPY-Dy0Bxyod.js → diagram-FQU43EPY-DyfyZ0qG.js} +1 -1
  28. package/web/dist/assets/{diagram-G47NLZAW-R_yWa2-6.js → diagram-G47NLZAW-Dd2HPM5s.js} +1 -1
  29. package/web/dist/assets/{diagram-NH7WQ7WH-BV2cGmgX.js → diagram-NH7WQ7WH-DC03_BJp.js} +1 -1
  30. package/web/dist/assets/{diagram-OA4YK3LP-auayiDmq.js → diagram-OA4YK3LP-CfPUNCYI.js} +1 -1
  31. package/web/dist/assets/{diagram-WEI45ONY-CE7bHIYE.js → diagram-WEI45ONY-O-K9BqwQ.js} +1 -1
  32. package/web/dist/assets/{ebnfDiagram-CCIWWBDH-C3SMk6Uu.js → ebnfDiagram-CCIWWBDH-Pu3yFM4q.js} +1 -1
  33. package/web/dist/assets/{erDiagram-Q63AITRT-CnTs6X2D.js → erDiagram-Q63AITRT-9oh4uutz.js} +1 -1
  34. package/web/dist/assets/{flowDiagram-23GEKE2U-DobYm7mq.js → flowDiagram-23GEKE2U-D6sP81ck.js} +1 -1
  35. package/web/dist/assets/{ganttDiagram-NO4QXBWP-AkTQtAsR.js → ganttDiagram-NO4QXBWP-7aJ0dtGR.js} +1 -1
  36. package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-CpLWsqgx.js → gitGraphDiagram-IHSO6WYX-bLa8NZvk.js} +1 -1
  37. package/web/dist/assets/{index-BCzNnCl0.js → index-DT_gy82W.js} +2 -2
  38. package/web/dist/assets/{infoDiagram-FWYZ7A6U-ByzTSgj_.js → infoDiagram-FWYZ7A6U-Bx2QUCNC.js} +1 -1
  39. package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-CaL9Wi8i.js → ishikawaDiagram-FXEZZL3T-Z43QbfP4.js} +1 -1
  40. package/web/dist/assets/{journeyDiagram-5HDEW3XC-BillueZJ.js → journeyDiagram-5HDEW3XC-DDp4teSL.js} +1 -1
  41. package/web/dist/assets/{kanban-definition-HUTT4EX6-DI_IRVWQ.js → kanban-definition-HUTT4EX6-CoNysaii.js} +1 -1
  42. package/web/dist/assets/{linear-Cabaq-RD.js → linear-DiAWjM7P.js} +1 -1
  43. package/web/dist/assets/{mermaid.core-D8KnBjxP.js → mermaid.core-Pc4CQtPH.js} +4 -4
  44. package/web/dist/assets/{mindmap-definition-LN4V7U3C-Bl7FpPwk.js → mindmap-definition-LN4V7U3C-BXkQ4eOw.js} +1 -1
  45. package/web/dist/assets/{papaparse.min-CcjjKk0h.js → papaparse.min-CzTZ22jn.js} +1 -1
  46. package/web/dist/assets/{pegDiagram-2B236MQR-CmsG2owD.js → pegDiagram-2B236MQR-DLawXMJA.js} +1 -1
  47. package/web/dist/assets/{pieDiagram-ENE6RG2P-CLA_mcHl.js → pieDiagram-ENE6RG2P-Bqw6TCQf.js} +1 -1
  48. package/web/dist/assets/{pilotswarm-DMCBXi5A.js → pilotswarm-CJ6SEIPj.js} +46 -46
  49. package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-hT9bnnUG.js → quadrantDiagram-ABIIQ3AL-D4Nrodp1.js} +1 -1
  50. package/web/dist/assets/{railroadDiagram-RFXS5EU6-CNC0kFW6.js → railroadDiagram-RFXS5EU6-14ozmmna.js} +1 -1
  51. package/web/dist/assets/{requirementDiagram-TGXJPOKE-C-nSthhK.js → requirementDiagram-TGXJPOKE-g2jB_CR_.js} +1 -1
  52. package/web/dist/assets/{sankeyDiagram-HTMAVEWB-C5JdV5bI.js → sankeyDiagram-HTMAVEWB-UsWtjkg-.js} +1 -1
  53. package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-BMKBxKk9.js → sequenceDiagram-DBY2YBRQ-DOCKalZQ.js} +1 -1
  54. package/web/dist/assets/{sizeCapture-X5ZJPWSS-CbwNw-TR.js → sizeCapture-X5ZJPWSS-GOrSPHZX.js} +1 -1
  55. package/web/dist/assets/{stateDiagram-2N3HPSRC-ChvGrUa_.js → stateDiagram-2N3HPSRC-DcTBrPP0.js} +1 -1
  56. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DjyLnyGF.js +1 -0
  57. package/web/dist/assets/{swimlanes-5IMT3BWC-D9Uzkbi7.js → swimlanes-5IMT3BWC-DXlohBs3.js} +2 -2
  58. package/web/dist/assets/swimlanesDiagram-G3AALYLV-B8GZDsc2.js +8 -0
  59. package/web/dist/assets/{timeline-definition-FHXFAJF6-ctFEfgUp.js → timeline-definition-FHXFAJF6-CLAJChwQ.js} +1 -1
  60. package/web/dist/assets/{vennDiagram-L72KCM5P-DGrJhlyF.js → vennDiagram-L72KCM5P-DapjeRd3.js} +1 -1
  61. package/web/dist/assets/{wardleyDiagram-EHGQE667-BGqtZ8dQ.js → wardleyDiagram-EHGQE667-eYWaR7BL.js} +1 -1
  62. package/web/dist/assets/{xychartDiagram-FW5EYKEG-DAXXs9xC.js → xychartDiagram-FW5EYKEG-DcJXUeUI.js} +1 -1
  63. package/web/dist/index.html +2 -2
  64. package/web/dist/assets/channel-HtOTPdHg.js +0 -1
  65. package/web/dist/assets/classDiagram-OUVF2IWQ-C2P3dws0.js +0 -1
  66. package/web/dist/assets/classDiagram-v2-EOCWNBFH-C2P3dws0.js +0 -1
  67. package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DLbEwVA2.js +0 -1
  68. package/web/dist/assets/swimlanesDiagram-G3AALYLV-DMzhdO6y.js +0 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pilotswarm",
3
- "version": "0.5.48",
3
+ "version": "0.5.50",
4
4
  "description": "PilotSwarm application package: terminal UI, browser portal + Web API server, and MCP server — one install, three bins.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -81,7 +81,7 @@
81
81
  "ink": "^6.8.0",
82
82
  "jose": "^6.2.2",
83
83
  "mermaid": "^11.16.0",
84
- "pilotswarm-sdk": "0.5.48",
84
+ "pilotswarm-sdk": "0.5.50",
85
85
  "react": "^19.2.4",
86
86
  "react-dom": "^19.2.4",
87
87
  "ws": "^8.18.2"
@@ -3455,6 +3455,12 @@ export class PilotSwarmUiController {
3455
3455
  });
3456
3456
  }
3457
3457
 
3458
+ /** Does the selected provider type authenticate as the worker itself? */
3459
+ _adminProviderTypeUsesWorkloadIdentity(typeId) {
3460
+ const types = selectAdminConsole(this.getState()).modelProviders?.providerTypes || [];
3461
+ return types.find((type) => type.id === typeId)?.usesWorkloadIdentity === true;
3462
+ }
3463
+
3458
3464
  cycleAdminProviderCreateType() {
3459
3465
  const providers = selectAdminConsole(this.getState()).modelProviders;
3460
3466
  const create = this.getState().admin?.modelProviders?.create;
@@ -3512,16 +3518,26 @@ export class PilotSwarmUiController {
3512
3518
  async saveAdminProviderCreate() {
3513
3519
  const create = this.getState().admin?.modelProviders?.create;
3514
3520
  if (!create?.editing || create.saving || create.stage !== "credential") return;
3521
+ // A workload-identity type has no key to type. The stage is still
3522
+ // reached — it is where the person confirms — but the draft is empty
3523
+ // by design, so demanding a credential would make the type
3524
+ // unusable from this screen.
3525
+ const workloadIdentity = this._adminProviderTypeUsesWorkloadIdentity(create.typeId);
3515
3526
  const credential = String(create.draft || "").trim();
3516
- if (!credential) {
3527
+ if (!credential && !workloadIdentity) {
3517
3528
  this.dispatch({ type: "admin/modelProviders/createFailed", error: "Enter a provider credential." });
3518
3529
  return;
3519
3530
  }
3520
3531
  const input = {
3521
3532
  name: create.name,
3522
3533
  type: create.typeId,
3523
- credentials: /github/i.test(create.typeId) ? { githubToken: credential } : { apiKey: credential },
3524
- shared: create.shared === true,
3534
+ credentials: workloadIdentity
3535
+ ? { kind: "workloadIdentity" }
3536
+ : /github/i.test(create.typeId) ? { githubToken: credential } : { apiKey: credential },
3537
+ // It runs on the worker's identity, which belongs to the
3538
+ // organization, so it can only be shared. The server refuses a
3539
+ // personal one; asking for it here would only earn an error.
3540
+ shared: workloadIdentity ? true : create.shared === true,
3525
3541
  };
3526
3542
  // Clear the credential from shared state before any network await.
3527
3543
  this.dispatch({ type: "admin/modelProviders/createSaving" });
@@ -39,6 +39,22 @@ import { isRecoverableTransportErrorText } from "./session-errors.js";
39
39
 
40
40
  export const ACTIVE_HIGHLIGHT_BACKGROUND = "activeHighlightBackground";
41
41
  export const ACTIVE_HIGHLIGHT_FOREGROUND = "activeHighlightForeground";
42
+
43
+ /**
44
+ * Provider types that authenticate as the worker rather than with a key.
45
+ *
46
+ * The same rule as `providerTypeUsesWorkloadIdentity` in the SDK, restated
47
+ * because the UI packages do not import from it. Both sides read the `type`
48
+ * field of the deployment's model-providers file, so they agree on the value
49
+ * even though they cannot share the function.
50
+ */
51
+ export function providerTypeUsesWorkloadIdentity(type) {
52
+ return type === "anthropic-wif";
53
+ }
54
+
55
+ /** What the add-provider form says where the key field would be. */
56
+ export const WORKLOAD_IDENTITY_CREDENTIAL_NOTE =
57
+ "Use the Workload Identity configured in the worker";
42
58
  const USER_CHAT_COLOR = "userChat";
43
59
  const USER_CHAT_LABEL_COLOR = "userChatLabel";
44
60
  // Speaker label for a message from another person in a shared session.
@@ -3939,11 +3955,19 @@ export function selectAdminConsole(state) {
3939
3955
  providerTypeMap.set(model.providerId, {
3940
3956
  id: model.providerId,
3941
3957
  label: model.providerType || model.providerId,
3958
+ // Carried on the type so that no screen has to know which type
3959
+ // names mean "no key": the add-provider form asks for a
3960
+ // credential unless this says the worker already holds one.
3961
+ usesWorkloadIdentity: providerTypeUsesWorkloadIdentity(model.providerType),
3942
3962
  });
3943
3963
  }
3944
3964
  const providerSummary = (provider) => ({
3945
3965
  name: provider.name,
3946
3966
  typeId: provider.typeId || provider.type || null,
3967
+ // There is no key on this one to rotate, so the screens that offer
3968
+ // "Update key" leave it off rather than offering an action the server
3969
+ // refuses.
3970
+ usesWorkloadIdentity: providerTypeMap.get(provider.typeId || provider.type)?.usesWorkloadIdentity === true,
3947
3971
  class: provider.class === "shared" ? "shared" : "personal",
3948
3972
  hasCredential: Boolean(provider.hasCredential),
3949
3973
  usableByMe: provider.usableByMe !== false,
@@ -4680,6 +4704,12 @@ export function selectAdminProviderCreateModal(state, maxWidth = 76) {
4680
4704
  ? "Add GitHub Copilot provider"
4681
4705
  : `Add ${create.typeId || "model"} provider`;
4682
4706
  const value = String(create.draft || "");
4707
+ // A workload-identity type has no key to ask for. The stage still exists
4708
+ // — it is where the person confirms what they are adding — but it prompts
4709
+ // instead of demanding, and nothing is typed into it.
4710
+ const typeRow = (admin.modelProviders?.models || [])
4711
+ .find((model) => model?.providerId === create.typeId);
4712
+ const workloadIdentity = providerTypeUsesWorkloadIdentity(typeRow?.providerType);
4683
4713
  return {
4684
4714
  type: "adminProviderCreate",
4685
4715
  title: updating
@@ -4687,10 +4717,12 @@ export function selectAdminProviderCreateModal(state, maxWidth = 76) {
4687
4717
  : credentialStage
4688
4718
  ? (shared ? "Add shared model provider" : ownTitle)
4689
4719
  : (shared ? "Name the shared provider" : ownTitle),
4690
- label: credentialStage ? "credential" : "provider name",
4691
- displayValue: credentialStage && value ? "•".repeat(value.length) : value,
4720
+ label: credentialStage ? (workloadIdentity ? "authentication" : "credential") : "provider name",
4721
+ displayValue: credentialStage && value && !workloadIdentity ? "•".repeat(value.length) : value,
4692
4722
  cursorIndex: Math.max(0, Math.min(Number(create.cursorIndex) || 0, value.length)),
4693
- placeholder: credentialStage ? "Paste token" : "my-ghcp",
4723
+ placeholder: credentialStage
4724
+ ? (workloadIdentity ? WORKLOAD_IDENTITY_CREDENTIAL_NOTE : "Paste token")
4725
+ : "my-ghcp",
4694
4726
  saving: Boolean(create.saving),
4695
4727
  error: create.error || null,
4696
4728
  idealWidth: Math.max(56, Math.min(maxWidth, 76)),
@@ -4698,6 +4730,9 @@ export function selectAdminProviderCreateModal(state, maxWidth = 76) {
4698
4730
  ? [
4699
4731
  { text: `Provider ${create.name}`, color: "white", bold: true },
4700
4732
  { text: `Type ${create.typeId}`, color: "cyan" },
4733
+ ...(workloadIdentity
4734
+ ? [{ text: WORKLOAD_IDENTITY_CREDENTIAL_NOTE, color: "gray" }]
4735
+ : []),
4701
4736
  { text: shared ? "Shared" : "Only you", color: "gray" },
4702
4737
  ]
4703
4738
  : [
@@ -4706,7 +4741,9 @@ export function selectAdminProviderCreateModal(state, maxWidth = 76) {
4706
4741
  { text: "Letters, numbers, dot, dash, and underscore only.", color: "gray" },
4707
4742
  ],
4708
4743
  helpLines: credentialStage
4709
- ? ["Type/paste credential", `Enter ${updating ? "update key" : "create provider"}`, "Esc cancel and clear"]
4744
+ ? workloadIdentity
4745
+ ? ["No key needed", "Enter create provider", "Esc cancel"]
4746
+ : ["Type/paste credential", `Enter ${updating ? "update key" : "create provider"}`, "Esc cancel and clear"]
4710
4747
  : ["Type provider name", "Tab next provider type", "Enter continue to credential", "Esc cancel"],
4711
4748
  };
4712
4749
  }
@@ -3949,7 +3949,7 @@ const SESSION_DETAIL_NONE = "—";
3949
3949
  */
3950
3950
  export function visibleWaitReason(session, statusLabel) {
3951
3951
  const reason = typeof session?.waitReason === "string" && session.waitReason.trim()
3952
- ? session.waitReason.trim()
3952
+ ? clampWaitReason(session.waitReason)
3953
3953
  : null;
3954
3954
  if (!reason) return null;
3955
3955
  const status = String(statusLabel || "");
@@ -3961,6 +3961,66 @@ export function visibleWaitReason(session, statusLabel) {
3961
3961
  return waiting ? reason : null;
3962
3962
  }
3963
3963
 
3964
+ /** Words kept before the sentence is clipped. A glance, not a document. */
3965
+ export const WAIT_REASON_MAX_WORDS = 10;
3966
+
3967
+ /**
3968
+ * A ceiling on the clipped line, because a word count is not a length.
3969
+ *
3970
+ * These instructions carry session UUIDs and artifact filenames, and one of
3971
+ * those is a single 40-character "word" — ten of them ran to 100 characters
3972
+ * and wrapped the box anyway. Whichever limit is reached first wins.
3973
+ */
3974
+ export const WAIT_REASON_MAX_CHARS = 72;
3975
+
3976
+ /**
3977
+ * One line's worth of a wait reason.
3978
+ *
3979
+ * `waitReason` is not one kind of text. `wait` asks the model for "why you're
3980
+ * waiting" and gets a sentence. `cron` asks for "what to do on each wake-up"
3981
+ * and gets an INSTRUCTION — the real wake-up prompt, which is replayed on
3982
+ * every fire and is routinely several paragraphs. Both land in the same column
3983
+ * and both used to be printed in full, so a cron session's detail box was its
3984
+ * entire next-turn plan under a one-word label.
3985
+ *
3986
+ * The stored value is left alone — for a cron it is load-bearing state, not
3987
+ * decoration. Only the display is cut, and newlines are flattened first so a
3988
+ * multi-paragraph prompt cannot smuggle its shape through the word count.
3989
+ */
3990
+ export function clampWaitReason(
3991
+ text,
3992
+ maxWords = WAIT_REASON_MAX_WORDS,
3993
+ maxChars = WAIT_REASON_MAX_CHARS,
3994
+ ) {
3995
+ const flat = String(text ?? "").replace(/\s+/gu, " ").trim();
3996
+ if (!flat) return "";
3997
+ const words = flat.split(" ");
3998
+ let kept = words.slice(0, maxWords).join(" ");
3999
+ // Then the length ceiling, cutting back to a word boundary so the line
4000
+ // does not end mid-UUID. A first word that is already over the limit is
4001
+ // cut where it stands — better a clipped token than a wrapped box.
4002
+ if (kept.length > maxChars) {
4003
+ const cut = kept.slice(0, maxChars);
4004
+ const boundary = cut.lastIndexOf(" ");
4005
+ kept = boundary > 0 ? cut.slice(0, boundary) : cut;
4006
+ }
4007
+ if (kept === flat) return flat;
4008
+ // Trailing punctuation on the last kept word reads as a full stop followed
4009
+ // by an ellipsis ("plan,…"), so it goes.
4010
+ return `${kept.replace(/[.,;:—-]+$/u, "")}…`;
4011
+ }
4012
+
4013
+ /**
4014
+ * What the detail box calls the sentence beside it.
4015
+ *
4016
+ * A cron session is not waiting *because of* this text — it is waiting for the
4017
+ * next tick, and the text is what it will then do. Naming it "Waiting" made
4018
+ * the wake-up instruction read as a stall reason.
4019
+ */
4020
+ export function waitReasonLabel(session) {
4021
+ return session?.cronActive === true ? "On wake" : "Waiting";
4022
+ }
4023
+
3964
4024
  function SessionDetailBox({ session, childCount = 0, pause = null, controller = null, collapsed = false, onToggle = null }) {
3965
4025
  // EVERY field renders on EVERY selection, empty ones as an em dash. The box
3966
4026
  // is a fixed grid of rows, so moving through the list cannot change its
@@ -4041,8 +4101,17 @@ function SessionDetailBox({ session, childCount = 0, pause = null, controller =
4041
4101
  const pauseBlock = (!pause && !waitReason) ? null : React.createElement("div", {
4042
4102
  className: `ps-session-detail-pause${pause ? " is-paused" : ""}`,
4043
4103
  },
4044
- React.createElement("span", { className: "ps-session-detail-label" }, pause ? "Paused" : "Waiting"),
4045
- React.createElement("span", { className: "ps-session-detail-why" },
4104
+ React.createElement("span", { className: "ps-session-detail-label" },
4105
+ pause ? "Paused" : waitReasonLabel(session)),
4106
+ React.createElement("span", {
4107
+ className: "ps-session-detail-why",
4108
+ // Clipped on screen, whole on hover: for a cron the full text is the
4109
+ // wake-up instruction, and someone reading the box is often trying to
4110
+ // find out exactly what the next tick will do.
4111
+ ...(!pause && typeof session?.waitReason === "string" && session.waitReason.trim()
4112
+ ? { title: session.waitReason.trim() }
4113
+ : {}),
4114
+ },
4046
4115
  // The sentence already says when it clears wherever it can; `clears`
4047
4116
  // only fills the gap where it cannot.
4048
4117
  pause ? pause.reason : waitReason,
@@ -10733,14 +10802,26 @@ function CreateProviderSheet({
10733
10802
  // Bounded and named here instead, where the rule can be said precisely.
10734
10803
  const badChars = trimmed !== "" && !/^[A-Za-z0-9._-]+$/.test(trimmed);
10735
10804
  const tooLong = trimmed.length > 64;
10805
+ // A workload-identity type carries no key: the worker authenticates as
10806
+ // itself. So the form asks for nothing to paste, and requiring a
10807
+ // credential would make the type impossible to add from this screen.
10808
+ const workloadIdentity = types.find((option) => option.id === type)?.usesWorkloadIdentity === true;
10809
+ // It runs on the worker's identity, which is the organization's, so it can
10810
+ // only be shared — a personal one would spend the cluster's account under
10811
+ // a name no administrator can cap. The server refuses it too.
10812
+ const sharedOnly = workloadIdentity;
10813
+ const effectiveShared = sharedOnly ? true : shared;
10736
10814
  const valid = Boolean(trimmed) && !taken && !badChars && !tooLong
10737
- && Boolean(type) && Boolean(credential.trim());
10815
+ && Boolean(type) && (workloadIdentity || Boolean(credential.trim()))
10816
+ && (!sharedOnly || isAdmin);
10738
10817
  const isGithub = /github/i.test(type);
10739
10818
  const submit = () => {
10740
10819
  if (!valid) return;
10741
10820
  const input = {
10742
- name: trimmed, type, shared,
10743
- credentials: isGithub ? { githubToken: credential.trim() } : { apiKey: credential.trim() },
10821
+ name: trimmed, type, shared: effectiveShared,
10822
+ credentials: workloadIdentity
10823
+ ? { kind: "workloadIdentity" }
10824
+ : isGithub ? { githubToken: credential.trim() } : { apiKey: credential.trim() },
10744
10825
  baseUrl: baseUrl.trim() || null,
10745
10826
  };
10746
10827
  setCredential("");
@@ -10753,7 +10834,7 @@ function CreateProviderSheet({
10753
10834
 
10754
10835
  return React.createElement(BudgetSheet, {
10755
10836
  title: updating ? `Update key for ${trimmed}` : (isGithub ? "Add GitHub Copilot provider" : "Add model provider"),
10756
- subtitle: shared ? "Shared" : "Personal",
10837
+ subtitle: effectiveShared ? "Shared" : "Personal",
10757
10838
  confirmLabel: updating ? "Update key" : "Add provider", busy, disabled: !valid, error, onCancel: cancel, onConfirm: submit,
10758
10839
  // The whole point of an update is what does NOT change. Saying so is
10759
10840
  // the difference between "replace the key" and "did I just make a
@@ -10789,33 +10870,45 @@ function CreateProviderSheet({
10789
10870
  }, types.length === 0
10790
10871
  ? React.createElement("option", { value: "" }, "No provider types are listed on this deployment")
10791
10872
  : types.map((option) => React.createElement("option", { key: option.id, value: option.id }, option.id)))),
10792
- React.createElement(SheetField, {
10793
- label: updating
10794
- ? (isGithub ? "New GitHub Copilot token" : "New API key")
10795
- : (isGithub ? "GitHub Copilot token" : "API key"),
10796
- hint: updating
10797
- ? "Replaces the current one. The existing key is never shown."
10798
- : "Paste the token value.",
10799
- },
10800
- React.createElement("input", {
10801
- className: "ps-budget-input", type: "password", autoComplete: "off",
10802
- "aria-label": updating
10803
- ? (isGithub ? "New GitHub Copilot token" : "New API key")
10804
- : (isGithub ? "GitHub Copilot token" : "API key"),
10805
- value: credential, placeholder: "Paste token",
10806
- onChange: (event) => { onDirty?.(); setCredential(event.target.value); },
10807
- // While updating this is the only editable field, and the Name input
10808
- // that carries the other Enter handler is disabled — so without this
10809
- // the sheet cannot be submitted from the keyboard at all.
10810
- onKeyDown: (event) => { if (event.key === "Enter" && valid) submit(); },
10811
- })),
10873
+ workloadIdentity
10874
+ ? React.createElement(SheetField, {
10875
+ label: "Authentication",
10876
+ hint: "Nothing is stored. The worker mints a short-lived token for each request.",
10877
+ },
10878
+ React.createElement("div", { className: "ps-budget-input is-static", role: "note" },
10879
+ "Use the Workload Identity configured in the worker"))
10880
+ : React.createElement(SheetField, {
10881
+ label: updating
10882
+ ? (isGithub ? "New GitHub Copilot token" : "New API key")
10883
+ : (isGithub ? "GitHub Copilot token" : "API key"),
10884
+ hint: updating
10885
+ ? "Replaces the current one. The existing key is never shown."
10886
+ : "Paste the token value.",
10887
+ },
10888
+ React.createElement("input", {
10889
+ className: "ps-budget-input", type: "password", autoComplete: "off",
10890
+ "aria-label": updating
10891
+ ? (isGithub ? "New GitHub Copilot token" : "New API key")
10892
+ : (isGithub ? "GitHub Copilot token" : "API key"),
10893
+ value: credential, placeholder: "Paste token",
10894
+ onChange: (event) => { onDirty?.(); setCredential(event.target.value); },
10895
+ // While updating this is the only editable field, and the Name input
10896
+ // that carries the other Enter handler is disabled — so without this
10897
+ // the sheet cannot be submitted from the keyboard at all.
10898
+ onKeyDown: (event) => { if (event.key === "Enter" && valid) submit(); },
10899
+ })),
10812
10900
  !updating ? React.createElement(SheetField, { label: "Base URL", hint: "Optional." },
10813
10901
  React.createElement("input", {
10814
10902
  className: "ps-budget-input", value: baseUrl, placeholder: "https://…",
10815
10903
  "aria-label": "Base URL",
10816
10904
  onChange: (event) => setBaseUrl(event.target.value),
10817
10905
  })) : null,
10818
- isAdmin && !updating ? React.createElement(SheetField, { label: "Access" },
10906
+ sharedOnly && !updating ? React.createElement(SheetField, {
10907
+ label: "Access",
10908
+ hint: "It runs on the worker's own identity, which belongs to the organization, so it cannot be personal.",
10909
+ },
10910
+ React.createElement("div", { className: "ps-budget-input is-static", role: "note" }, "Shared"))
10911
+ : isAdmin && !updating ? React.createElement(SheetField, { label: "Access" },
10819
10912
  React.createElement(Segmented, {
10820
10913
  value: shared ? "shared" : "mine", onChange: (value) => setShared(value === "shared"),
10821
10914
  label: "Access",
@@ -13385,7 +13478,9 @@ function AdminProviderRows({ providers, isAdmin, personal, busy, onSystemUse, on
13385
13478
  // drops the cluster-default flag, the allowance, any hold, the
13386
13479
  // system-use routing and the usage history, i.e. everything this
13387
13480
  // feature exists to preserve.
13388
- (personal || isAdmin) ? React.createElement("button", {
13481
+ // A workload-identity provider has no stored key: the worker
13482
+ // authenticates as itself, and the server refuses the update.
13483
+ (personal || isAdmin) && !provider.usesWorkloadIdentity ? React.createElement("button", {
13389
13484
  type: "button",
13390
13485
  className: "ps-mini-button",
13391
13486
  // Several providers means several identical "Update Key"
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-DA7ca6Jr.js";import{p as f}from"./chunk-JWPE2WC7-BBr7v6mo.js";import{_ as n,l as o}from"./mermaid.core-D8KnBjxP.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-Sb8l4Vm6.js";import"./pilotswarm-DMCBXi5A.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-B8zfb4XG.js";import{p as f}from"./chunk-JWPE2WC7-BhwKbO0c.js";import{_ as n,l as o}from"./mermaid.core-Pc4CQtPH.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-kJrtGP23.js";import"./pilotswarm-CJ6SEIPj.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
@@ -1 +1 @@
1
- import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-D8KnBjxP.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-Pc4CQtPH.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};