frizz 0.12.11 → 0.12.13
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/codex-app-server-daemon.js +8 -1
- package/dist/dev-child.js +639 -293
- package/dist/frizz.js +499 -82
- package/package.json +1 -1
- package/runtime/cc-worker/bin/frizz-mcp.mjs +19 -14
- package/runtime/cc-worker/hooks/bash-background.d.mts +1 -1
- package/runtime/cc-worker/hooks/bash-background.mjs +17 -6
- package/runtime/cc-worker/hooks/deny-ask.mjs +5 -4
- package/runtime/cc-worker/hooks/deny-plan.mjs +3 -3
- package/runtime/cc-worker/hooks/session-seed.mjs +1 -1
- package/runtime/cc-worker/skills/gh/SKILL.md +3 -3
- package/web-dist/assets/{TerminalPane-DHvHCziB.js → TerminalPane-Cv0c_SNS.js} +1 -1
- package/web-dist/assets/{abnfDiagram-VRR7QNED-DIzM_IN8.js → abnfDiagram-VRR7QNED-B-wED8ht.js} +1 -1
- package/web-dist/assets/architecture-TIHT7OUA-nsH-QfGr.js +1 -0
- package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-Bm_8vd5v.js → architectureDiagram-ZJ3FMSHR-kHm_O2sR.js} +1 -1
- package/web-dist/assets/{blockDiagram-677ZJIJ3-BXUW_N2o.js → blockDiagram-677ZJIJ3-CdazUaKU.js} +1 -1
- package/web-dist/assets/{c4Diagram-LMCZKHZV-C7rfqxUO.js → c4Diagram-LMCZKHZV-rauLKZbJ.js} +1 -1
- package/web-dist/assets/channel-fvwjeUt3.js +1 -0
- package/web-dist/assets/{chunk-32BRIVSS-DbfUERLa.js → chunk-32BRIVSS-DjOqyEDX.js} +1 -1
- package/web-dist/assets/{chunk-52WLFC77-BT_3wKSP.js → chunk-52WLFC77-f1SThegZ.js} +1 -1
- package/web-dist/assets/{chunk-C7G6YPKG-JqApnOVt.js → chunk-C7G6YPKG-BfQsesjz.js} +1 -1
- package/web-dist/assets/{chunk-EX3LRPZG-Cj6hlA4t.js → chunk-EX3LRPZG-BIzZW7OQ.js} +1 -1
- package/web-dist/assets/{chunk-FWX5IMBZ-BGthhuW_.js → chunk-FWX5IMBZ-CATINZFC.js} +2 -2
- package/web-dist/assets/{chunk-HOUHSVGY-uyLpm_YS.js → chunk-HOUHSVGY-DbMBRCCE.js} +1 -1
- package/web-dist/assets/{chunk-ICXQ74PX-BlwUMZAk.js → chunk-ICXQ74PX-COcoSWx4.js} +1 -1
- package/web-dist/assets/{chunk-MOJQB5TN-MXbNNk-P.js → chunk-MOJQB5TN-CVnI-o_4.js} +1 -1
- package/web-dist/assets/{chunk-OGEWGWER-Da4My1Rf.js → chunk-OGEWGWER-U0vdZqB6.js} +1 -1
- package/web-dist/assets/{chunk-PUDLZKDR-C-PgvO__.js → chunk-PUDLZKDR-Bukb7rem.js} +1 -1
- package/web-dist/assets/{chunk-Q4XR5HBZ-BxZFe7gr.js → chunk-Q4XR5HBZ-Bl019PHd.js} +1 -1
- package/web-dist/assets/{chunk-V7JOEXUC-DgERuXm_.js → chunk-V7JOEXUC-YJR0vzI6.js} +1 -1
- package/web-dist/assets/{chunk-VAUOI2AC-T5OoQE-_.js → chunk-VAUOI2AC-CK6NU9kj.js} +1 -1
- package/web-dist/assets/{chunk-VR4S4FIN--HFvJUG1.js → chunk-VR4S4FIN-B3LBv3C3.js} +1 -1
- package/web-dist/assets/{chunk-WYO6CB5R-DlAvy41i.js → chunk-WYO6CB5R-DjRPyRuS.js} +1 -1
- package/web-dist/assets/{chunk-ZGVPDNZ5-BdDg7X_H.js → chunk-ZGVPDNZ5-BUJCayKP.js} +1 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-BRZQ_xnN.js +1 -0
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-BRZQ_xnN.js +1 -0
- package/web-dist/assets/{cynefin-VYW2F7L2-BhjNO28R.js → cynefin-VYW2F7L2-CE3ce51v.js} +1 -1
- package/web-dist/assets/{cynefinDiagram-TSTJHNR4-B6VA5gCd.js → cynefinDiagram-TSTJHNR4-DSXSPZQV.js} +1 -1
- package/web-dist/assets/{dagre-VKFMJZFB-6MXoEc6K.js → dagre-VKFMJZFB-VwBQdjum.js} +1 -1
- package/web-dist/assets/{diagram-FQU43EPY-D2RSPoK4.js → diagram-FQU43EPY-7Tf5_WDu.js} +1 -1
- package/web-dist/assets/{diagram-G47NLZAW-BOSbtzOf.js → diagram-G47NLZAW-mu4aclWA.js} +1 -1
- package/web-dist/assets/{diagram-NH7WQ7WH-Dt_u21nP.js → diagram-NH7WQ7WH-B3kAVNwv.js} +1 -1
- package/web-dist/assets/{diagram-OA4YK3LP-CjuWW-7r.js → diagram-OA4YK3LP-DAaV-2ou.js} +1 -1
- package/web-dist/assets/{diagram-WEI45ONY-rANIKJ8N.js → diagram-WEI45ONY-DijPfgly.js} +1 -1
- package/web-dist/assets/{ebnfDiagram-CCIWWBDH-Bf3kfaKZ.js → ebnfDiagram-CCIWWBDH-B1o42Wp-.js} +1 -1
- package/web-dist/assets/{erDiagram-Q63AITRT-6vBp0ITn.js → erDiagram-Q63AITRT-Dqej9Wi3.js} +1 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-B8xeGcXU.js +1 -0
- package/web-dist/assets/flowDiagram-23GEKE2U-Cnyfcz6Z.js +1 -0
- package/web-dist/assets/{ganttDiagram-NO4QXBWP-C2qN2PMS.js → ganttDiagram-NO4QXBWP-UoyrLPUT.js} +1 -1
- package/web-dist/assets/{gitGraph-TEB2WS4Q-CRASqc9n.js → gitGraph-TEB2WS4Q-CwjIwQ3T.js} +1 -1
- package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-gsXSPrH2.js → gitGraphDiagram-IHSO6WYX-BxU758_g.js} +1 -1
- package/web-dist/assets/index-CT6k_A5y.css +1 -0
- package/web-dist/assets/index-k8HCkyvh.js +319 -0
- package/web-dist/assets/{info-DKCQHKI2-rDkFlFOd.js → info-DKCQHKI2-CWtSI6aK.js} +1 -1
- package/web-dist/assets/{infoDiagram-FWYZ7A6U-03ql8Tqr.js → infoDiagram-FWYZ7A6U-n5xRfy_d.js} +1 -1
- package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-CfDSAVNp.js → ishikawaDiagram-FXEZZL3T-DU1LTQY-.js} +1 -1
- package/web-dist/assets/{journeyDiagram-5HDEW3XC-RWaA6AvH.js → journeyDiagram-5HDEW3XC-BzZWHoPv.js} +1 -1
- package/web-dist/assets/{kanban-definition-HUTT4EX6-C89qresM.js → kanban-definition-HUTT4EX6-CeneyR7c.js} +1 -1
- package/web-dist/assets/{line-CZtSsvHu.js → line-DTfkLWnw.js} +1 -1
- package/web-dist/assets/{mermaid-parser.core-DKNnvxt0.js → mermaid-parser.core-CHzoJ5LT.js} +3 -3
- package/web-dist/assets/{mermaid.core-DX8cFeBF.js → mermaid.core-Cl84DLN2.js} +3 -3
- package/web-dist/assets/{mindmap-definition-LN4V7U3C-DW8ClyAX.js → mindmap-definition-LN4V7U3C-C39sBNqB.js} +1 -1
- package/web-dist/assets/{packet-7NZHBO7P-CUOv3apI.js → packet-7NZHBO7P-Bj73rlQn.js} +1 -1
- package/web-dist/assets/{pegDiagram-2B236MQR-CFyxKEBL.js → pegDiagram-2B236MQR-BEEM1vSv.js} +1 -1
- package/web-dist/assets/{pie-RZYD4A2V-DyXXRie2.js → pie-RZYD4A2V-CJ-fEfdy.js} +1 -1
- package/web-dist/assets/{pieDiagram-ENE6RG2P-yUw7HA8l.js → pieDiagram-ENE6RG2P-Dcqj3oyh.js} +1 -1
- package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-Mc7aea_i.js → quadrantDiagram-ABIIQ3AL-D5cljQ3D.js} +1 -1
- package/web-dist/assets/{radar-I7S5WNFK-BKj0BsQy.js → radar-I7S5WNFK-G8JqqGPf.js} +1 -1
- package/web-dist/assets/{railroad-3IZDKUUU-DUK_kJIY.js → railroad-3IZDKUUU-CaGNL0iN.js} +1 -1
- package/web-dist/assets/railroad-abnf-AHOZXSZD-DWZmdy-r.js +1 -0
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-C-XoPuo_.js +1 -0
- package/web-dist/assets/railroad-peg-LSFZ7HO6-BGpxqg46.js +1 -0
- package/web-dist/assets/{railroadDiagram-RFXS5EU6-By0Xgb_U.js → railroadDiagram-RFXS5EU6-BZGZXMY4.js} +1 -1
- package/web-dist/assets/{requirementDiagram-TGXJPOKE-CgTycsRY.js → requirementDiagram-TGXJPOKE-Dj5yn_9e.js} +1 -1
- package/web-dist/assets/{sankeyDiagram-HTMAVEWB-mJ_-Grkn.js → sankeyDiagram-HTMAVEWB-7iJ_NDNf.js} +1 -1
- package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-Dn6YyeNv.js → sequenceDiagram-DBY2YBRQ-Bl54CknA.js} +1 -1
- package/web-dist/assets/{stateDiagram-2N3HPSRC-BBws1E-e.js → stateDiagram-2N3HPSRC-DuvjeNgJ.js} +1 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-BR-vrMgH.js +1 -0
- package/web-dist/assets/{swimlanes-5IMT3BWC-Jab-07o9.js → swimlanes-5IMT3BWC-B7-Jj4Sk.js} +1 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-C1HvMf2k.js +8 -0
- package/web-dist/assets/{timeline-definition-FHXFAJF6-Cq6RznHz.js → timeline-definition-FHXFAJF6-jsey2pEG.js} +1 -1
- package/web-dist/assets/{treeView-QDETBFTQ-KSelJnyL.js → treeView-QDETBFTQ-DhPmOzHr.js} +1 -1
- package/web-dist/assets/{treemap-6X3UGDF4-B-iiUurZ.js → treemap-6X3UGDF4-Dw2IaXqK.js} +1 -1
- package/web-dist/assets/{vennDiagram-L72KCM5P-B8t4NeQy.js → vennDiagram-L72KCM5P-CXKZkBab.js} +1 -1
- package/web-dist/assets/{wardley-OPB4EBWU-CtkAPZLQ.js → wardley-OPB4EBWU-CTCdDwyE.js} +1 -1
- package/web-dist/assets/{wardleyDiagram-EHGQE667-DC-ms_sD.js → wardleyDiagram-EHGQE667-B1w0xIqB.js} +1 -1
- package/web-dist/assets/{xychartDiagram-FW5EYKEG-DW6fcgjF.js → xychartDiagram-FW5EYKEG-gULk-uIl.js} +1 -1
- package/web-dist/index.html +2 -2
- package/web-dist/assets/architecture-TIHT7OUA-DBvKOf0j.js +0 -1
- package/web-dist/assets/channel-CbdXYswc.js +0 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-BVXwECjS.js +0 -1
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-BVXwECjS.js +0 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-STTT-geQ.js +0 -1
- package/web-dist/assets/flowDiagram-23GEKE2U-CC7DF1Fz.js +0 -1
- package/web-dist/assets/index-DimQCLe8.js +0 -319
- package/web-dist/assets/index-XhfdAXcw.css +0 -1
- package/web-dist/assets/railroad-abnf-AHOZXSZD-BDImJcFw.js +0 -1
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-BR2XIYqq.js +0 -1
- package/web-dist/assets/railroad-peg-LSFZ7HO6-DRCwBbJY.js +0 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-B2jOFVGG.js +0 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-DhYK_6fH.js +0 -8
package/dist/dev-child.js
CHANGED
|
@@ -13456,8 +13456,8 @@ function parkExpiredWakeMessage(status) {
|
|
|
13456
13456
|
...status,
|
|
13457
13457
|
"",
|
|
13458
13458
|
"Re-park if they are genuinely still going \u2014 there is no limit on that, and a long job is not a",
|
|
13459
|
-
"failure. If something is finished, read its result. If nothing is left, end in ```done or
|
|
13460
|
-
"
|
|
13459
|
+
"failure. If something is finished, read its result. If nothing is left, end in ```done or register",
|
|
13460
|
+
"a question with `mcp__frizz__ask`."
|
|
13461
13461
|
].join("\n");
|
|
13462
13462
|
}
|
|
13463
13463
|
function parkFinishedWakeMessage(status, several) {
|
|
@@ -13469,7 +13469,7 @@ function parkFinishedWakeMessage(status, several) {
|
|
|
13469
13469
|
"READ ITS OUTPUT AND CARRY ON. This is not a broken fence and there is nothing to fix: the wait",
|
|
13470
13470
|
"you declared simply ended. Do NOT relaunch the same work \u2014 its result is already on disk.",
|
|
13471
13471
|
"",
|
|
13472
|
-
"Then park on whatever comes next, or end in ```done or a
|
|
13472
|
+
"Then park on whatever comes next, or end in ```done or a registered question (`mcp__frizz__ask`) if nothing is left."
|
|
13473
13473
|
].join("\n");
|
|
13474
13474
|
}
|
|
13475
13475
|
function prWatchExpiredWakeMessage(ref) {
|
|
@@ -13483,6 +13483,12 @@ function ownWatchExpiredWakeMessage(kind, target) {
|
|
|
13483
13483
|
|
|
13484
13484
|
If you are still waiting on it, register it again with \`mcp__frizz__watch\` and a fresh \`for:\`. If you are not, and it was the only thing you were waiting on, end in a proper terminal state instead of parking on it again.`;
|
|
13485
13485
|
}
|
|
13486
|
+
function questionFencesLive(spawnedAt) {
|
|
13487
|
+
if (spawnedAt === void 0 || spawnedAt === null) return true;
|
|
13488
|
+
const at = typeof spawnedAt === "number" ? spawnedAt : Date.parse(spawnedAt);
|
|
13489
|
+
if (!Number.isFinite(at)) return true;
|
|
13490
|
+
return at < Date.parse(QUESTION_FENCE_RETIRED_AT);
|
|
13491
|
+
}
|
|
13486
13492
|
function askedQuestionDepth(q) {
|
|
13487
13493
|
let deepest = 1;
|
|
13488
13494
|
for (const option of q.options ?? []) {
|
|
@@ -13679,7 +13685,7 @@ function parseGithubWakeSteer(text2) {
|
|
|
13679
13685
|
function frizzRoute(path3) {
|
|
13680
13686
|
return `${FRIZZ_ROUTE_PREFIX}${path3.startsWith("/") ? path3 : `/${path3}`}`;
|
|
13681
13687
|
}
|
|
13682
|
-
var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, AWAITING_KEY_RE, AWAITING_YAML_KEYS, AWAITING_SEQUENCE_KEYS, AWAITING_HINT_MAX, AWAITING_HINT_VALUE_MAX, AWAITING_TITLE_MAX, AWAITING_ITEM_KINDS, AWAITING_DURATION_RE, DURATION_UNIT_MS, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, INTERRUPT_MARKERS, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, DISMISSED_ANSWER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
|
|
13688
|
+
var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, AWAITING_KEY_RE, AWAITING_YAML_KEYS, AWAITING_SEQUENCE_KEYS, AWAITING_HINT_MAX, AWAITING_HINT_VALUE_MAX, AWAITING_TITLE_MAX, AWAITING_ITEM_KINDS, AWAITING_DURATION_RE, DURATION_UNIT_MS, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, INTERRUPT_MARKERS, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, QUESTION_FENCE_RETIRED_AT, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, DISMISSED_ANSWER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
|
|
13683
13689
|
var init_src = __esm({
|
|
13684
13690
|
"packages/shared/src/index.ts"() {
|
|
13685
13691
|
"use strict";
|
|
@@ -13997,7 +14003,7 @@ var init_src = __esm({
|
|
|
13997
14003
|
RETIRED_AWAITING_REPLACEMENT = {
|
|
13998
14004
|
"watch": "`shells: [<the id your runtime gave you>]` (or `agents: [<id>]`) \u2014 the same id, in the current sequence",
|
|
13999
14005
|
"pr-watch": "register the PR with `mcp__frizz__watch_pr`, then name it `prs: [owner/repo#123]`",
|
|
14000
|
-
"human": "there is no human gate any more \u2014 if you need a person,
|
|
14006
|
+
"human": "there is no human gate any more \u2014 if you need a person, register a question with `mcp__frizz__ask` instead of parking",
|
|
14001
14007
|
"ci": "CI is not a wait of its own: register the PR with `mcp__frizz__watch_pr` and you are woken when its checks settle",
|
|
14002
14008
|
"session": "there is no cross-session wait \u2014 name the sub-agent you dispatched with `agents: [<id>]`",
|
|
14003
14009
|
// THE 2026-08-24 CUTOVER. The frontmatter is YAML now, and YAML has no repeated keys — so the four
|
|
@@ -14057,7 +14063,7 @@ var init_src = __esm({
|
|
|
14057
14063
|
INTERRUPT_MARKERS = ["[Request interrupted by user]", "[Request interrupted by user for tool use]"];
|
|
14058
14064
|
ALLDONE_SENTINEL = "ALLDONE";
|
|
14059
14065
|
OPT_OUT_NOTE = "To stop these, sign off with a ```done fence \u2014 but ONLY when the work is genuinely finished: it files this thread away, and nothing but new work from the human reopens it.";
|
|
14060
|
-
OVER_QUESTION_NOTE = "Your
|
|
14066
|
+
OVER_QUESTION_NOTE = "Your registered question is still unanswered, and a Goal armed at rest means the operator is not waiting to answer it: decide it yourself, say in one line which way you went and what would reverse it, and carry on. Do NOT re-ask it.";
|
|
14061
14067
|
TIMER_PROMPT_MAX = SNOOZE_PROMPT_MAX;
|
|
14062
14068
|
TIMER_MAX_DELAY_SECONDS = 30 * 24 * 60 * 60;
|
|
14063
14069
|
TIMER_MAX_ARMED = 64;
|
|
@@ -14089,14 +14095,16 @@ var init_src = __esm({
|
|
|
14089
14095
|
"the write-up.",
|
|
14090
14096
|
"",
|
|
14091
14097
|
"**DECIDE RATHER THAN ASK.** Stop only for a decision that is genuinely the human's AND that blocks you",
|
|
14092
|
-
"right now:
|
|
14098
|
+
"right now: register that one with `mcp__frizz__ask`. Every other open choice INSIDE the task \u2014 a name, a",
|
|
14093
14099
|
"default, a reversible design call \u2014 is yours to make: decide it, say in one line which way you went and",
|
|
14094
14100
|
"what would reverse it, and carry on. A choice that would ENLARGE the task is not one of those: an",
|
|
14095
14101
|
"unanswered question is not permission to go build the answer.",
|
|
14096
14102
|
"",
|
|
14097
|
-
"Otherwise,
|
|
14103
|
+
"Otherwise, sign off \u2014 a registration, or a fence at the END of your next message:",
|
|
14098
14104
|
"",
|
|
14099
|
-
"-
|
|
14105
|
+
"- `mcp__frizz__ask` \u2014 you need the human. NOT a fence: the ```question fence is retired, and a fence",
|
|
14106
|
+
" with a question in its body is plain prose. Register it (options with one-line trade-offs, the",
|
|
14107
|
+
" recommended one first), then rest normally \u2014 an open registered question is the sign-off.",
|
|
14100
14108
|
"- `` ```done `` \u2014 genuinely FINISHED. A DISMISSAL: the card is filed away and nobody looks again, so",
|
|
14101
14109
|
" if anything is still owed, it is not done. Body: 1-3 sentences, then bullets, each opening with a",
|
|
14102
14110
|
" **bolded verb phrase**.",
|
|
@@ -14311,6 +14319,7 @@ var init_src = __esm({
|
|
|
14311
14319
|
watches: external_exports.array(OwnWatchView)
|
|
14312
14320
|
}).strict();
|
|
14313
14321
|
OWN_WATCH_MAX_ARMED = 24;
|
|
14322
|
+
QUESTION_FENCE_RETIRED_AT = "2026-09-11T17:30:00Z";
|
|
14314
14323
|
AskQuestionKind = external_exports.enum(["question", "multi"]);
|
|
14315
14324
|
ASK_MAX_DEPTH = 3;
|
|
14316
14325
|
AskedOptionSchema = external_exports.lazy(() => external_exports.object({
|
|
@@ -17821,7 +17830,7 @@ function rpcRequestIdJson(value) {
|
|
|
17821
17830
|
}
|
|
17822
17831
|
function canonicalJson(value) {
|
|
17823
17832
|
const visiting = /* @__PURE__ */ new WeakSet();
|
|
17824
|
-
const
|
|
17833
|
+
const normalize2 = (candidate) => {
|
|
17825
17834
|
if (candidate === null || typeof candidate === "string" || typeof candidate === "boolean") return candidate;
|
|
17826
17835
|
if (typeof candidate === "number") {
|
|
17827
17836
|
if (!Number.isFinite(candidate)) throw new InteractionStoreError("invalid-response", "provider JSON contains a non-finite number");
|
|
@@ -17833,14 +17842,14 @@ function canonicalJson(value) {
|
|
|
17833
17842
|
if (visiting.has(candidate)) throw new InteractionStoreError("invalid-response", "provider JSON contains a cycle");
|
|
17834
17843
|
visiting.add(candidate);
|
|
17835
17844
|
try {
|
|
17836
|
-
if (Array.isArray(candidate)) return candidate.map((item) => item === void 0 ? null :
|
|
17845
|
+
if (Array.isArray(candidate)) return candidate.map((item) => item === void 0 ? null : normalize2(item));
|
|
17837
17846
|
const object = candidate;
|
|
17838
|
-
return Object.fromEntries(Object.keys(object).sort().filter((key) => object[key] !== void 0).map((key) => [key,
|
|
17847
|
+
return Object.fromEntries(Object.keys(object).sort().filter((key) => object[key] !== void 0).map((key) => [key, normalize2(object[key])]));
|
|
17839
17848
|
} finally {
|
|
17840
17849
|
visiting.delete(candidate);
|
|
17841
17850
|
}
|
|
17842
17851
|
};
|
|
17843
|
-
return JSON.stringify(
|
|
17852
|
+
return JSON.stringify(normalize2(value));
|
|
17844
17853
|
}
|
|
17845
17854
|
function boundedProviderJson(value) {
|
|
17846
17855
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -22181,6 +22190,35 @@ var init_detached_daemons = __esm({
|
|
|
22181
22190
|
}
|
|
22182
22191
|
});
|
|
22183
22192
|
|
|
22193
|
+
// packages/server/src/backend/daemon-tree.ts
|
|
22194
|
+
import { spawnSync } from "node:child_process";
|
|
22195
|
+
function endDaemonTree(pid, signal, deps = {}) {
|
|
22196
|
+
const kill = deps.kill ?? defaultKill;
|
|
22197
|
+
if ((deps.platform ?? process.platform) !== "win32") {
|
|
22198
|
+
try {
|
|
22199
|
+
kill(pid, signal);
|
|
22200
|
+
} catch {
|
|
22201
|
+
}
|
|
22202
|
+
return;
|
|
22203
|
+
}
|
|
22204
|
+
const killed = (deps.spawnSync ?? spawnSync)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
22205
|
+
if (killed.error || killed.status !== 0) {
|
|
22206
|
+
try {
|
|
22207
|
+
kill(pid, signal);
|
|
22208
|
+
} catch {
|
|
22209
|
+
}
|
|
22210
|
+
}
|
|
22211
|
+
}
|
|
22212
|
+
var defaultKill;
|
|
22213
|
+
var init_daemon_tree = __esm({
|
|
22214
|
+
"packages/server/src/backend/daemon-tree.ts"() {
|
|
22215
|
+
"use strict";
|
|
22216
|
+
defaultKill = (pid, signal) => {
|
|
22217
|
+
process.kill(pid, signal);
|
|
22218
|
+
};
|
|
22219
|
+
}
|
|
22220
|
+
});
|
|
22221
|
+
|
|
22184
22222
|
// packages/server/src/backend/ipc-path.ts
|
|
22185
22223
|
import { join as join15 } from "node:path";
|
|
22186
22224
|
function frizzIpcPath(name) {
|
|
@@ -22370,7 +22408,10 @@ function forkBroker(options) {
|
|
|
22370
22408
|
cwd: options.cwd,
|
|
22371
22409
|
env: { ...process.env, FRIZZ_CLAUDE_BROKER: JSON.stringify(config) },
|
|
22372
22410
|
detached: true,
|
|
22373
|
-
stdio: "ignore"
|
|
22411
|
+
stdio: "ignore",
|
|
22412
|
+
// A detached process owns no console on Windows; keep the daemon from ever being handed a
|
|
22413
|
+
// visible one (Windows audit 2026-09-11, finding 4 — the Codex daemon's spawn tells the story).
|
|
22414
|
+
windowsHide: true
|
|
22374
22415
|
});
|
|
22375
22416
|
child.unref();
|
|
22376
22417
|
const deadline = Date.now() + (options.timeoutMs ?? 3e4);
|
|
@@ -22421,7 +22462,7 @@ async function adoptOrForkBroker(options) {
|
|
|
22421
22462
|
if (existing) return { record: existing, reattached: true };
|
|
22422
22463
|
return { record: await forkBroker(options), reattached: false };
|
|
22423
22464
|
}
|
|
22424
|
-
function killBroker(stateDir, sessionId, retireReason) {
|
|
22465
|
+
function killBroker(stateDir, sessionId, retireReason, deps = {}) {
|
|
22425
22466
|
const recordPath2 = claudeBrokerRecordPath(stateDir, sessionId);
|
|
22426
22467
|
const record = liveBrokerRecord(recordPath2);
|
|
22427
22468
|
if (retireReason) {
|
|
@@ -22429,10 +22470,7 @@ function killBroker(stateDir, sessionId, retireReason) {
|
|
|
22429
22470
|
} else takeBrokerRetirement(stateDir, sessionId);
|
|
22430
22471
|
if (record) {
|
|
22431
22472
|
rememberBrokerIdentity(recordPath2, record);
|
|
22432
|
-
|
|
22433
|
-
process.kill(record.daemonPid, "SIGTERM");
|
|
22434
|
-
} catch {
|
|
22435
|
-
}
|
|
22473
|
+
endDaemonTree(record.daemonPid, "SIGTERM", deps);
|
|
22436
22474
|
}
|
|
22437
22475
|
try {
|
|
22438
22476
|
unlinkSync(recordPath2);
|
|
@@ -22446,6 +22484,7 @@ var init_claude_broker_host = __esm({
|
|
|
22446
22484
|
"use strict";
|
|
22447
22485
|
init_detached_daemons();
|
|
22448
22486
|
init_claude_broker_diagnostics();
|
|
22487
|
+
init_daemon_tree();
|
|
22449
22488
|
init_ipc_path();
|
|
22450
22489
|
WINDOWS_SHIM_TARGET = /^\s*"%dp0%\\(.+?)"/mu;
|
|
22451
22490
|
}
|
|
@@ -23401,10 +23440,30 @@ var init_tail_cache = __esm({
|
|
|
23401
23440
|
import { statSync as statSync8, openSync as openSync10, readSync as readSync4, closeSync as closeSync9, readdirSync as readdirSync5, realpathSync as realpathSync5, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8, readFileSync as readFileSync12, existsSync as existsSync5 } from "node:fs";
|
|
23402
23441
|
import { execFile as execFile2 } from "node:child_process";
|
|
23403
23442
|
import { promisify as promisify2 } from "node:util";
|
|
23404
|
-
import { basename as basename4, join as join18 } from "node:path";
|
|
23443
|
+
import { basename as basename4, join as join18, win32 } from "node:path";
|
|
23405
23444
|
import { homedir as homedir7 } from "node:os";
|
|
23406
|
-
async function probeShellsAlive(outputFiles) {
|
|
23445
|
+
async function probeShellsAlive(outputFiles, opts = {}) {
|
|
23446
|
+
const platform3 = opts.platform ?? process.platform;
|
|
23447
|
+
const exec = opts.exec ?? execFileAsync;
|
|
23407
23448
|
const verdicts = /* @__PURE__ */ new Map();
|
|
23449
|
+
if (platform3 === "win32") {
|
|
23450
|
+
const present = outputFiles.filter((file) => {
|
|
23451
|
+
if (existsSync5(file)) return true;
|
|
23452
|
+
verdicts.set(file, void 0);
|
|
23453
|
+
return false;
|
|
23454
|
+
});
|
|
23455
|
+
if (present.length === 0) return verdicts;
|
|
23456
|
+
const [shell, args] = windowsShellHolderCommand(present, opts.env ?? process.env);
|
|
23457
|
+
let report;
|
|
23458
|
+
try {
|
|
23459
|
+
report = (await exec(shell, args, { encoding: "utf8", timeout: 8e3, windowsHide: true })).stdout;
|
|
23460
|
+
} catch {
|
|
23461
|
+
for (const file of present) verdicts.set(file, void 0);
|
|
23462
|
+
return verdicts;
|
|
23463
|
+
}
|
|
23464
|
+
for (const [file, alive] of parseWindowsShellHolderReport(String(report), present)) verdicts.set(file, alive);
|
|
23465
|
+
return verdicts;
|
|
23466
|
+
}
|
|
23408
23467
|
const live = [];
|
|
23409
23468
|
for (const file of outputFiles) {
|
|
23410
23469
|
if (!existsSync5(file)) {
|
|
@@ -23420,7 +23479,7 @@ async function probeShellsAlive(outputFiles) {
|
|
|
23420
23479
|
if (live.length === 0) return verdicts;
|
|
23421
23480
|
let stdout = "";
|
|
23422
23481
|
try {
|
|
23423
|
-
stdout = (await
|
|
23482
|
+
stdout = (await exec("lsof", ["-F", "pn", "--", ...live.map((f) => f.real)], { encoding: "utf8", timeout: 8e3 })).stdout;
|
|
23424
23483
|
} catch (err) {
|
|
23425
23484
|
const e = err;
|
|
23426
23485
|
const ran = typeof e.code === "number" && !e.killed;
|
|
@@ -23435,6 +23494,25 @@ async function probeShellsAlive(outputFiles) {
|
|
|
23435
23494
|
for (const f of live) verdicts.set(f.requested, held.has(f.real));
|
|
23436
23495
|
return verdicts;
|
|
23437
23496
|
}
|
|
23497
|
+
function windowsShellHolderCommand(paths, env = process.env) {
|
|
23498
|
+
const shell = win32.join(env.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
23499
|
+
const list = paths.map((p) => `'${p.replace(/'/g, "''")}'`).join(",");
|
|
23500
|
+
const script = `foreach($p in @(${list})){try{$f=[System.IO.File]::Open($p,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None);$f.Close();'free'}catch [System.IO.IOException]{if((($_.Exception.HResult) -band 0xFFFF) -in 32,33){'held'}else{'unknown'}}catch{'unknown'}}`;
|
|
23501
|
+
return [shell, ["-NoProfile", "-NonInteractive", "-NoLogo", "-Command", script]];
|
|
23502
|
+
}
|
|
23503
|
+
function parseWindowsShellHolderReport(report, paths) {
|
|
23504
|
+
const verdicts = /* @__PURE__ */ new Map();
|
|
23505
|
+
const lines = report.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
23506
|
+
if (lines.length !== paths.length) {
|
|
23507
|
+
for (const file of paths) verdicts.set(file, void 0);
|
|
23508
|
+
return verdicts;
|
|
23509
|
+
}
|
|
23510
|
+
paths.forEach((file, i) => {
|
|
23511
|
+
const word = lines[i];
|
|
23512
|
+
verdicts.set(file, word === "held" ? true : word === "free" ? false : void 0);
|
|
23513
|
+
});
|
|
23514
|
+
return verdicts;
|
|
23515
|
+
}
|
|
23438
23516
|
function activityMinute(at) {
|
|
23439
23517
|
if (!at) return "";
|
|
23440
23518
|
const ms = Date.parse(at);
|
|
@@ -25518,18 +25596,20 @@ ${detail2}
|
|
|
25518
25596
|
}, delay2);
|
|
25519
25597
|
nudgeTimer.unref?.();
|
|
25520
25598
|
}
|
|
25521
|
-
function
|
|
25599
|
+
function currentRowFor(state) {
|
|
25522
25600
|
const current = deps.storage.getSession(state.slug);
|
|
25523
|
-
return
|
|
25524
|
-
|
|
25525
|
-
|
|
25601
|
+
return current && current.session_id === state.sessionId && (current.runtime_generation ?? 0) === state.runtimeGeneration ? current : void 0;
|
|
25602
|
+
}
|
|
25603
|
+
function registeredStateIsCurrent(state) {
|
|
25604
|
+
return currentRowFor(state) !== void 0;
|
|
25526
25605
|
}
|
|
25527
25606
|
return {
|
|
25528
25607
|
get(slug) {
|
|
25529
25608
|
const registered = states.get(slug);
|
|
25530
|
-
const
|
|
25609
|
+
const row = registered ? currentRowFor(registered) : void 0;
|
|
25610
|
+
const s = registered ? row ? registered : void 0 : foreignStates.get(slug);
|
|
25531
25611
|
if (!s) return void 0;
|
|
25532
|
-
const pendingQuestion = s.lastAssistantHasQuestion;
|
|
25612
|
+
const pendingQuestion = s.lastAssistantHasQuestion && questionFencesLive(row?.spawned_at);
|
|
25533
25613
|
const nowMs = now();
|
|
25534
25614
|
return { turn: s.turn, permPrompt: s.permPrompt, permPolicy: s.permPolicy, permDenies: s.permDenies, model: s.model, effort: s.effort, profileAt: s.profileAt, profileRevision: s.profileRevision, permissionMode: s.permissionMode, permissionModeAt: s.permissionModeAt, permissionModeRevision: s.permissionModeRevision, lastActivityAt: s.lastActivityAt, lastAssistantAt: s.lastAssistantAt, lastAssistant: s.lastAssistant, aiTitle: s.aiTitle, customTitle: s.customTitle, customTitleRevision: s.customTitleRevision, subAgents: subAgentViews(s, nowMs), droppedReports: [...s.queuedReports.values()], bgShells: [...bgShellViews(s), ...codexBgShellViews(s)], retiredShells: retiredShellViews(s), pendingAsk: s.pendingAsk, pendingQuestion, lastAssistantAllDone: s.lastAssistantAllDone, lastUserAt: s.lastUserAt, lastUserText: s.lastUserText, firstUserText: s.firstUserText, lastFence: s.lastFence, noTranscript: s.noTranscript, authFault: s.authFault, apiFault: s.apiFault, providerError: s.providerError, limitFault: s.limitFault, contextTokens: s.contextTokens, contextWindow: s.contextWindow, lastCompactionAt: s.lastCompactionAt };
|
|
25535
25615
|
},
|
|
@@ -26371,6 +26451,101 @@ var init_codex = __esm({
|
|
|
26371
26451
|
}
|
|
26372
26452
|
});
|
|
26373
26453
|
|
|
26454
|
+
// packages/server/src/backend/codex-executable.ts
|
|
26455
|
+
import { accessSync as accessSync2, constants as fsConstants2, readFileSync as readFileSync14 } from "node:fs";
|
|
26456
|
+
import { posix, win32 as win322 } from "node:path";
|
|
26457
|
+
function defaultAccess(path3, mode) {
|
|
26458
|
+
try {
|
|
26459
|
+
accessSync2(path3, mode === "executable" ? fsConstants2.X_OK : fsConstants2.F_OK);
|
|
26460
|
+
return true;
|
|
26461
|
+
} catch {
|
|
26462
|
+
return false;
|
|
26463
|
+
}
|
|
26464
|
+
}
|
|
26465
|
+
function defaultReadFile(path3) {
|
|
26466
|
+
try {
|
|
26467
|
+
return readFileSync14(path3, "utf8");
|
|
26468
|
+
} catch {
|
|
26469
|
+
return void 0;
|
|
26470
|
+
}
|
|
26471
|
+
}
|
|
26472
|
+
function searchPath2(env) {
|
|
26473
|
+
const direct = env.PATH ?? env.Path ?? env.path;
|
|
26474
|
+
if (direct !== void 0) return direct;
|
|
26475
|
+
for (const [key, value] of Object.entries(env)) if (key.toLowerCase() === "path") return value ?? "";
|
|
26476
|
+
return "";
|
|
26477
|
+
}
|
|
26478
|
+
function vendoredNativeBeside(launcherJs, arch, access) {
|
|
26479
|
+
const triple = WINDOWS_TRIPLE[arch];
|
|
26480
|
+
if (!triple) return void 0;
|
|
26481
|
+
const { dirname: dirname24, join: join46 } = win322;
|
|
26482
|
+
const pkgRoot = dirname24(dirname24(launcherJs));
|
|
26483
|
+
const platformPkg = `codex-win32-${arch}`;
|
|
26484
|
+
const vendorRoots = [
|
|
26485
|
+
join46(pkgRoot, "node_modules", "@openai", platformPkg, "vendor"),
|
|
26486
|
+
// nested under the package
|
|
26487
|
+
join46(dirname24(pkgRoot), platformPkg, "vendor"),
|
|
26488
|
+
// hoisted beside it: <prefix>\node_modules\@openai\<pkg>
|
|
26489
|
+
join46(pkgRoot, "vendor")
|
|
26490
|
+
// the pre-platform-package layout, binaries inside @openai/codex itself
|
|
26491
|
+
];
|
|
26492
|
+
for (const root of vendorRoots) {
|
|
26493
|
+
const exe = join46(root, triple, "bin", "codex.exe");
|
|
26494
|
+
if (access(exe, "exists")) return exe;
|
|
26495
|
+
}
|
|
26496
|
+
return void 0;
|
|
26497
|
+
}
|
|
26498
|
+
function resolveCodexExecutable(bin, deps = {}) {
|
|
26499
|
+
const env = deps.env ?? process.env;
|
|
26500
|
+
const platform3 = deps.platform ?? process.platform;
|
|
26501
|
+
const arch = deps.arch ?? process.arch;
|
|
26502
|
+
const execPath = deps.execPath ?? process.execPath;
|
|
26503
|
+
const access = deps.access ?? defaultAccess;
|
|
26504
|
+
const readFile2 = deps.readFile ?? defaultReadFile;
|
|
26505
|
+
const windows = platform3 === "win32";
|
|
26506
|
+
const path3 = windows ? win322 : posix;
|
|
26507
|
+
const candidate = bin && bin.length > 0 ? bin : "codex";
|
|
26508
|
+
if (path3.isAbsolute(candidate) || /[\\/]/u.test(candidate)) return { file: candidate, args: [] };
|
|
26509
|
+
for (const dir of searchPath2(env).split(path3.delimiter)) {
|
|
26510
|
+
if (!dir) continue;
|
|
26511
|
+
if (!windows) {
|
|
26512
|
+
const full2 = path3.join(dir, candidate);
|
|
26513
|
+
if (access(full2, "executable")) return { file: full2, args: [] };
|
|
26514
|
+
continue;
|
|
26515
|
+
}
|
|
26516
|
+
const exe = path3.join(dir, `${candidate}.exe`);
|
|
26517
|
+
if (access(exe, "exists")) return { file: exe, args: [] };
|
|
26518
|
+
const body = readFile2(path3.join(dir, `${candidate}.cmd`));
|
|
26519
|
+
const target = body === void 0 ? void 0 : WINDOWS_SHIM_TARGET2.exec(body)?.[1];
|
|
26520
|
+
if (!target) continue;
|
|
26521
|
+
const full = path3.join(dir, target);
|
|
26522
|
+
if (!access(full, "exists")) continue;
|
|
26523
|
+
if (!/\.(?:js|mjs|cjs)$/iu.test(full)) return { file: full, args: [] };
|
|
26524
|
+
const native = vendoredNativeBeside(full, arch, access);
|
|
26525
|
+
if (native) return { file: native, args: [] };
|
|
26526
|
+
return { file: execPath, args: [full] };
|
|
26527
|
+
}
|
|
26528
|
+
throw new CodexExecutableNotFoundError(candidate);
|
|
26529
|
+
}
|
|
26530
|
+
var CodexExecutableNotFoundError, WINDOWS_SHIM_TARGET2, WINDOWS_TRIPLE;
|
|
26531
|
+
var init_codex_executable = __esm({
|
|
26532
|
+
"packages/server/src/backend/codex-executable.ts"() {
|
|
26533
|
+
"use strict";
|
|
26534
|
+
CodexExecutableNotFoundError = class extends Error {
|
|
26535
|
+
code = "ENOENT";
|
|
26536
|
+
constructor(candidate) {
|
|
26537
|
+
super(`could not resolve '${candidate}' to an executable on PATH`);
|
|
26538
|
+
this.name = "CodexExecutableNotFoundError";
|
|
26539
|
+
}
|
|
26540
|
+
};
|
|
26541
|
+
WINDOWS_SHIM_TARGET2 = /"%dp0%\\([^"]+)"\s+%\*/u;
|
|
26542
|
+
WINDOWS_TRIPLE = {
|
|
26543
|
+
x64: "x86_64-pc-windows-msvc",
|
|
26544
|
+
arm64: "aarch64-pc-windows-msvc"
|
|
26545
|
+
};
|
|
26546
|
+
}
|
|
26547
|
+
});
|
|
26548
|
+
|
|
26374
26549
|
// packages/server/src/backend/worker-env.ts
|
|
26375
26550
|
function isFrizzInternalEnvKey(key) {
|
|
26376
26551
|
return key.startsWith(FRIZZ_INTERNAL_PREFIX);
|
|
@@ -27739,14 +27914,14 @@ var require_receiver = __commonJS({
|
|
|
27739
27914
|
* @return {(Error|RangeError)} The error
|
|
27740
27915
|
* @private
|
|
27741
27916
|
*/
|
|
27742
|
-
createError(ErrorCtor, message, prefix, statusCode,
|
|
27917
|
+
createError(ErrorCtor, message, prefix, statusCode, errorCode5) {
|
|
27743
27918
|
this._loop = false;
|
|
27744
27919
|
this._errored = true;
|
|
27745
27920
|
const err = new ErrorCtor(
|
|
27746
27921
|
prefix ? `Invalid WebSocket frame: ${message}` : message
|
|
27747
27922
|
);
|
|
27748
27923
|
Error.captureStackTrace(err, this.createError);
|
|
27749
|
-
err.code =
|
|
27924
|
+
err.code = errorCode5;
|
|
27750
27925
|
err[kStatusCode] = statusCode;
|
|
27751
27926
|
return err;
|
|
27752
27927
|
}
|
|
@@ -30210,7 +30385,7 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
30210
30385
|
import { connect } from "node:net";
|
|
30211
30386
|
import { Agent as HttpAgent } from "node:http";
|
|
30212
30387
|
import { createHash as createHash7, randomUUID as randomUUID9 } from "node:crypto";
|
|
30213
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync11, readFileSync as
|
|
30388
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync15, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
|
|
30214
30389
|
import { join as join20 } from "node:path";
|
|
30215
30390
|
import { PassThrough, Writable } from "node:stream";
|
|
30216
30391
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -30234,7 +30409,7 @@ function pidAlive2(pid) {
|
|
|
30234
30409
|
}
|
|
30235
30410
|
function readNativeRecord(stateDir, projectId) {
|
|
30236
30411
|
try {
|
|
30237
|
-
const value = JSON.parse(
|
|
30412
|
+
const value = JSON.parse(readFileSync15(nativeRecordPath(stateDir, projectId), "utf8"));
|
|
30238
30413
|
if (typeof value.listenerPid !== "number" || typeof value.socketPath !== "string" || typeof value.generation !== "string") return null;
|
|
30239
30414
|
return {
|
|
30240
30415
|
projectId,
|
|
@@ -30404,7 +30579,10 @@ async function startListener(options) {
|
|
|
30404
30579
|
cwd: options.cwd,
|
|
30405
30580
|
env: options.env,
|
|
30406
30581
|
detached: true,
|
|
30407
|
-
stdio: "ignore"
|
|
30582
|
+
stdio: "ignore",
|
|
30583
|
+
// No console window for codex.exe on Windows (Windows audit 2026-09-11, finding 4; the story is on
|
|
30584
|
+
// the daemon's spawn in codex-app-server-daemon.ts).
|
|
30585
|
+
windowsHide: true
|
|
30408
30586
|
});
|
|
30409
30587
|
child.unref();
|
|
30410
30588
|
const record = {
|
|
@@ -30476,7 +30654,7 @@ var init_codex_app_server_native = __esm({
|
|
|
30476
30654
|
};
|
|
30477
30655
|
}
|
|
30478
30656
|
log.error("codex", `codex app-server native listener unavailable (${error.message}); falling back to an in-process app-server \u2014 turns will NOT survive a frizz restart`);
|
|
30479
|
-
const child = spawn2(options.codexBin, codexAppServerArgv(["--stdio"], options.frizzMcp), { cwd: options.cwd, env: options.env, stdio: ["pipe", "pipe", "pipe"] });
|
|
30657
|
+
const child = spawn2(options.codexBin, codexAppServerArgv(["--stdio"], options.frizzMcp), { cwd: options.cwd, env: options.env, stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
30480
30658
|
return { process: child, generation: randomUUID9(), reattached: false, daemonPid: process.pid, droppedWhileDetached: 0, authAccountId: options.authAccountId };
|
|
30481
30659
|
}
|
|
30482
30660
|
};
|
|
@@ -30484,10 +30662,10 @@ var init_codex_app_server_native = __esm({
|
|
|
30484
30662
|
});
|
|
30485
30663
|
|
|
30486
30664
|
// packages/server/src/backend/codex-app-server-host.ts
|
|
30487
|
-
import { spawn as spawn3
|
|
30665
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
30488
30666
|
import { createConnection } from "node:net";
|
|
30489
30667
|
import { createHash as createHash8, randomUUID as randomUUID10 } from "node:crypto";
|
|
30490
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync12, readFileSync as
|
|
30668
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync12, readFileSync as readFileSync16, unlinkSync as unlinkSync3 } from "node:fs";
|
|
30491
30669
|
import { join as join21 } from "node:path";
|
|
30492
30670
|
import { PassThrough as PassThrough2, Writable as Writable2 } from "node:stream";
|
|
30493
30671
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
@@ -30511,7 +30689,7 @@ function pidAlive3(pid) {
|
|
|
30511
30689
|
}
|
|
30512
30690
|
function readDaemonRecord(stateDir, projectId) {
|
|
30513
30691
|
try {
|
|
30514
|
-
const value = JSON.parse(
|
|
30692
|
+
const value = JSON.parse(readFileSync16(recordPath(stateDir, projectId), "utf8"));
|
|
30515
30693
|
if (typeof value.daemonPid !== "number" || typeof value.socketPath !== "string" || typeof value.generation !== "string") return null;
|
|
30516
30694
|
return {
|
|
30517
30695
|
projectId,
|
|
@@ -30528,7 +30706,7 @@ function readDaemonRecord(stateDir, projectId) {
|
|
|
30528
30706
|
}
|
|
30529
30707
|
function readDaemonExitBreadcrumb(stateDir, projectId) {
|
|
30530
30708
|
try {
|
|
30531
|
-
const value = JSON.parse(
|
|
30709
|
+
const value = JSON.parse(readFileSync16(`${recordPath(stateDir, projectId)}.exit`, "utf8"));
|
|
30532
30710
|
if (typeof value.generation !== "string" || typeof value.reason !== "string") return null;
|
|
30533
30711
|
return {
|
|
30534
30712
|
generation: value.generation,
|
|
@@ -30551,22 +30729,6 @@ function liveDaemonRecord(stateDir, projectId) {
|
|
|
30551
30729
|
}
|
|
30552
30730
|
return null;
|
|
30553
30731
|
}
|
|
30554
|
-
function endDaemonTree(pid, signal) {
|
|
30555
|
-
if (process.platform !== "win32") {
|
|
30556
|
-
try {
|
|
30557
|
-
process.kill(pid, signal);
|
|
30558
|
-
} catch {
|
|
30559
|
-
}
|
|
30560
|
-
return;
|
|
30561
|
-
}
|
|
30562
|
-
const killed = spawnSync("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
30563
|
-
if (killed.error || killed.status !== 0) {
|
|
30564
|
-
try {
|
|
30565
|
-
process.kill(pid, signal);
|
|
30566
|
-
} catch {
|
|
30567
|
-
}
|
|
30568
|
-
}
|
|
30569
|
-
}
|
|
30570
30732
|
function killCodexAppServerDaemon(stateDir, projectId) {
|
|
30571
30733
|
const record = liveDaemonRecord(stateDir, projectId);
|
|
30572
30734
|
if (!record) return;
|
|
@@ -30627,7 +30789,11 @@ function forkDaemon(options) {
|
|
|
30627
30789
|
// the payload and is applied by the daemon, keeping the audited env allowlist authoritative.
|
|
30628
30790
|
env: { ...process.env, FRIZZ_CODEX_APP_SERVER_DAEMON: payload },
|
|
30629
30791
|
detached: true,
|
|
30630
|
-
stdio: "ignore"
|
|
30792
|
+
stdio: "ignore",
|
|
30793
|
+
// Belt to the daemon's own braces: a detached process owns no console on Windows, so anything
|
|
30794
|
+
// console-subsystem it starts without CREATE_NO_WINDOW gets a fresh visible one (Windows audit
|
|
30795
|
+
// 2026-09-11, finding 4). The daemon hides its app-server; this keeps the daemon itself hidden.
|
|
30796
|
+
windowsHide: true
|
|
30631
30797
|
});
|
|
30632
30798
|
child.unref();
|
|
30633
30799
|
const deadline = Date.now() + (options.timeoutMs ?? 3e4);
|
|
@@ -30732,7 +30898,11 @@ function inProcessCodexAppServer(options) {
|
|
|
30732
30898
|
const child = spawn3(options.codexBin, codexAppServerArgv(["--stdio"], options.frizzMcp), {
|
|
30733
30899
|
cwd: options.cwd,
|
|
30734
30900
|
env: options.env,
|
|
30735
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
30901
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
30902
|
+
// No console window for codex.exe (Windows audit 2026-09-11, finding 4). The server child has a
|
|
30903
|
+
// console of its own so this site was not the visible-window bug, but a codex.exe that inherits
|
|
30904
|
+
// it dies with it, and the shape test in daemon-tree.test.ts holds every provider spawn to it.
|
|
30905
|
+
windowsHide: true
|
|
30736
30906
|
});
|
|
30737
30907
|
const kill = child.kill.bind(child);
|
|
30738
30908
|
child.kill = ((signal) => {
|
|
@@ -30759,6 +30929,7 @@ var init_codex_app_server_host = __esm({
|
|
|
30759
30929
|
"packages/server/src/backend/codex-app-server-host.ts"() {
|
|
30760
30930
|
"use strict";
|
|
30761
30931
|
init_detached_daemons();
|
|
30932
|
+
init_daemon_tree();
|
|
30762
30933
|
init_codex_app_server_native();
|
|
30763
30934
|
init_ipc_path();
|
|
30764
30935
|
init_codex_mcp();
|
|
@@ -31206,7 +31377,7 @@ var init_claude_quota = __esm({
|
|
|
31206
31377
|
// packages/server/src/backend/auth-status.ts
|
|
31207
31378
|
import { join as join23 } from "node:path";
|
|
31208
31379
|
import { homedir as homedir10, platform as platform2 } from "node:os";
|
|
31209
|
-
import { readFileSync as
|
|
31380
|
+
import { readFileSync as readFileSync17, statSync as statSync10 } from "node:fs";
|
|
31210
31381
|
import { execFile as execFile4 } from "node:child_process";
|
|
31211
31382
|
import { promisify as promisify4 } from "node:util";
|
|
31212
31383
|
function claudeConfigDir2() {
|
|
@@ -31216,7 +31387,7 @@ function claudeConfigDir2() {
|
|
|
31216
31387
|
function claudeFileState(configDir) {
|
|
31217
31388
|
let raw2;
|
|
31218
31389
|
try {
|
|
31219
|
-
raw2 =
|
|
31390
|
+
raw2 = readFileSync17(join23(configDir, ".credentials.json"), "utf8");
|
|
31220
31391
|
} catch (err) {
|
|
31221
31392
|
return err.code === "ENOENT" ? "absent" : "error";
|
|
31222
31393
|
}
|
|
@@ -31249,7 +31420,7 @@ function readCodexAuthState(codexHome = defaultCodexHome()) {
|
|
|
31249
31420
|
if (process.env.OPENAI_API_KEY || process.env.CODEX_API_KEY || process.env.CODEX_ACCESS_TOKEN) return "authed";
|
|
31250
31421
|
let raw2;
|
|
31251
31422
|
try {
|
|
31252
|
-
raw2 =
|
|
31423
|
+
raw2 = readFileSync17(join23(codexHome, "auth.json"), "utf8");
|
|
31253
31424
|
} catch (err) {
|
|
31254
31425
|
return err.code === "ENOENT" ? "signed-out" : "unknown";
|
|
31255
31426
|
}
|
|
@@ -31269,7 +31440,7 @@ function readCodexAuthState(codexHome = defaultCodexHome()) {
|
|
|
31269
31440
|
function readCodexAccountId(codexHome = defaultCodexHome()) {
|
|
31270
31441
|
if (process.env.OPENAI_API_KEY || process.env.CODEX_API_KEY || process.env.CODEX_ACCESS_TOKEN) return void 0;
|
|
31271
31442
|
try {
|
|
31272
|
-
const doc = JSON.parse(
|
|
31443
|
+
const doc = JSON.parse(readFileSync17(join23(codexHome, "auth.json"), "utf8"));
|
|
31273
31444
|
if (!doc || typeof doc !== "object") return void 0;
|
|
31274
31445
|
const tokens = doc.tokens;
|
|
31275
31446
|
if (!tokens || typeof tokens !== "object") return void 0;
|
|
@@ -31279,9 +31450,10 @@ function readCodexAccountId(codexHome = defaultCodexHome()) {
|
|
|
31279
31450
|
return void 0;
|
|
31280
31451
|
}
|
|
31281
31452
|
}
|
|
31282
|
-
async function readCodexBinaryState(codexBin = "codex", exec = execFileAsync3) {
|
|
31453
|
+
async function readCodexBinaryState(codexBin = "codex", exec = execFileAsync3, resolve12 = resolveCodexExecutable) {
|
|
31283
31454
|
try {
|
|
31284
|
-
|
|
31455
|
+
const codex = resolve12(codexBin);
|
|
31456
|
+
await exec(codex.file, [...codex.args, "--version"], { timeout: 5e3 });
|
|
31285
31457
|
return "present";
|
|
31286
31458
|
} catch (err) {
|
|
31287
31459
|
return err.code === "ENOENT" ? "missing" : "unknown";
|
|
@@ -31340,7 +31512,7 @@ function readClaudeAccountEmail(path3 = claudeAccountFile()) {
|
|
|
31340
31512
|
if (memo3 && memo3.path === path3 && memo3.mtimeMs === stat2.mtimeMs && memo3.size === stat2.size) return memo3.email;
|
|
31341
31513
|
let email;
|
|
31342
31514
|
try {
|
|
31343
|
-
const doc = JSON.parse(
|
|
31515
|
+
const doc = JSON.parse(readFileSync17(path3, "utf8"));
|
|
31344
31516
|
email = asEmail(doc?.oauthAccount?.emailAddress);
|
|
31345
31517
|
} catch {
|
|
31346
31518
|
email = void 0;
|
|
@@ -31351,7 +31523,7 @@ function readClaudeAccountEmail(path3 = claudeAccountFile()) {
|
|
|
31351
31523
|
function readCodexAccountEmail(codexHome = defaultCodexHome()) {
|
|
31352
31524
|
let doc;
|
|
31353
31525
|
try {
|
|
31354
|
-
doc = JSON.parse(
|
|
31526
|
+
doc = JSON.parse(readFileSync17(join23(codexHome, "auth.json"), "utf8"));
|
|
31355
31527
|
} catch {
|
|
31356
31528
|
return void 0;
|
|
31357
31529
|
}
|
|
@@ -31406,6 +31578,7 @@ var init_auth_status = __esm({
|
|
|
31406
31578
|
init_claude_quota();
|
|
31407
31579
|
init_codex();
|
|
31408
31580
|
init_claude_broker_host();
|
|
31581
|
+
init_codex_executable();
|
|
31409
31582
|
execFileAsync3 = promisify4(execFile4);
|
|
31410
31583
|
ProviderAuthRequiredError = class extends Error {
|
|
31411
31584
|
backend;
|
|
@@ -31746,7 +31919,7 @@ function fileChangeDisplays(changes) {
|
|
|
31746
31919
|
}
|
|
31747
31920
|
function canonicalJson2(value) {
|
|
31748
31921
|
const visiting = /* @__PURE__ */ new WeakSet();
|
|
31749
|
-
const
|
|
31922
|
+
const normalize2 = (candidate) => {
|
|
31750
31923
|
if (candidate === null || typeof candidate === "string" || typeof candidate === "boolean") return candidate;
|
|
31751
31924
|
if (typeof candidate === "number") {
|
|
31752
31925
|
if (!Number.isFinite(candidate)) throw new Error("non-finite JSON number");
|
|
@@ -31756,14 +31929,14 @@ function canonicalJson2(value) {
|
|
|
31756
31929
|
if (visiting.has(candidate)) throw new Error("cyclic JSON value");
|
|
31757
31930
|
visiting.add(candidate);
|
|
31758
31931
|
try {
|
|
31759
|
-
if (Array.isArray(candidate)) return candidate.map((item) => item === void 0 ? null :
|
|
31932
|
+
if (Array.isArray(candidate)) return candidate.map((item) => item === void 0 ? null : normalize2(item));
|
|
31760
31933
|
const object = candidate;
|
|
31761
|
-
return Object.fromEntries(Object.keys(object).sort().filter((key) => object[key] !== void 0).map((key) => [key,
|
|
31934
|
+
return Object.fromEntries(Object.keys(object).sort().filter((key) => object[key] !== void 0).map((key) => [key, normalize2(object[key])]));
|
|
31762
31935
|
} finally {
|
|
31763
31936
|
visiting.delete(candidate);
|
|
31764
31937
|
}
|
|
31765
31938
|
};
|
|
31766
|
-
return JSON.stringify(
|
|
31939
|
+
return JSON.stringify(normalize2(value));
|
|
31767
31940
|
}
|
|
31768
31941
|
function logicalRequestId(method, parts) {
|
|
31769
31942
|
const digest = createHash10("sha256").update(JSON.stringify([method, ...parts])).digest("hex");
|
|
@@ -34655,9 +34828,12 @@ function queryCodexRateLimits(codexHome = defaultCodexHome(), codexBin = "codex"
|
|
|
34655
34828
|
return new Promise((resolve12) => {
|
|
34656
34829
|
let child;
|
|
34657
34830
|
try {
|
|
34658
|
-
|
|
34831
|
+
const codex = resolveCodexExecutable(codexBin);
|
|
34832
|
+
child = spawn4(codex.file, [...codex.args, "app-server"], {
|
|
34659
34833
|
stdio: ["pipe", "pipe", "ignore"],
|
|
34660
|
-
env: { ...process.env, CODEX_HOME: codexHome }
|
|
34834
|
+
env: { ...process.env, CODEX_HOME: codexHome },
|
|
34835
|
+
// No console window for codex.exe (Windows audit 2026-09-11, finding 4).
|
|
34836
|
+
windowsHide: true
|
|
34661
34837
|
});
|
|
34662
34838
|
} catch {
|
|
34663
34839
|
resolve12(void 0);
|
|
@@ -34737,6 +34913,7 @@ var init_codex_quota = __esm({
|
|
|
34737
34913
|
"packages/server/src/backend/codex-quota.ts"() {
|
|
34738
34914
|
"use strict";
|
|
34739
34915
|
init_codex();
|
|
34916
|
+
init_codex_executable();
|
|
34740
34917
|
init_codex_app_server();
|
|
34741
34918
|
descByName2 = (a, b) => a < b ? 1 : a > b ? -1 : 0;
|
|
34742
34919
|
TAIL_BYTES = 512 * 1024;
|
|
@@ -34816,7 +34993,7 @@ var init_quota = __esm({
|
|
|
34816
34993
|
|
|
34817
34994
|
// packages/server/src/local-file.ts
|
|
34818
34995
|
import { spawn as spawn5 } from "node:child_process";
|
|
34819
|
-
import { readFileSync as
|
|
34996
|
+
import { existsSync as existsSync8, readFileSync as readFileSync18, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
|
|
34820
34997
|
import { homedir as homedir11 } from "node:os";
|
|
34821
34998
|
import { isAbsolute as isAbsolute4, join as join25, resolve as resolve5, sep as sep2 } from "node:path";
|
|
34822
34999
|
function isUnder(real, root) {
|
|
@@ -34848,7 +35025,7 @@ function resolveLocalFile(rawPath, roots) {
|
|
|
34848
35025
|
function resolveOpenableFile(raw2, projectDir, roots, home = homedir11()) {
|
|
34849
35026
|
const trimmed = raw2.trim().replace(/:\d+(?::\d+)?$/, "");
|
|
34850
35027
|
if (!trimmed) return null;
|
|
34851
|
-
const abs = trimmed === "~" ? home : trimmed
|
|
35028
|
+
const abs = trimmed === "~" ? home : /^~[\/\\]/.test(trimmed) ? join25(home, trimmed.slice(2)) : isAbsolute4(trimmed) ? trimmed : resolve5(projectDir, trimmed);
|
|
34852
35029
|
try {
|
|
34853
35030
|
return resolveLocalFile(abs, roots);
|
|
34854
35031
|
} catch {
|
|
@@ -34857,7 +35034,7 @@ function resolveOpenableFile(raw2, projectDir, roots, home = homedir11()) {
|
|
|
34857
35034
|
}
|
|
34858
35035
|
function readLocalTextFile(rawPath, roots) {
|
|
34859
35036
|
const path3 = resolveLocalFile(rawPath, roots);
|
|
34860
|
-
const bytes =
|
|
35037
|
+
const bytes = readFileSync18(path3);
|
|
34861
35038
|
if (bytes.subarray(0, 8192).includes(0)) throw new Error("Local file is not a text file");
|
|
34862
35039
|
if (bytes.length <= MARKDOWN_READ_LIMIT) return { path: path3, text: bytes.toString("utf8"), truncated: false };
|
|
34863
35040
|
const head = bytes.subarray(0, MARKDOWN_READ_LIMIT);
|
|
@@ -34868,7 +35045,7 @@ function readLocalMarkdown(rawPath, roots) {
|
|
|
34868
35045
|
if (!MARKDOWN_FILE_EXT.test(rawPath.trim())) throw new Error("Local file is not a Markdown file");
|
|
34869
35046
|
const path3 = resolveLocalFile(rawPath, roots);
|
|
34870
35047
|
if (!MARKDOWN_FILE_EXT.test(path3)) throw new Error("Local file is not a Markdown file");
|
|
34871
|
-
const bytes =
|
|
35048
|
+
const bytes = readFileSync18(path3);
|
|
34872
35049
|
if (bytes.length <= MARKDOWN_READ_LIMIT) return { path: path3, markdown: bytes.toString("utf8"), truncated: false };
|
|
34873
35050
|
const head = bytes.subarray(0, MARKDOWN_READ_LIMIT);
|
|
34874
35051
|
const lastBreak = head.lastIndexOf(10);
|
|
@@ -34884,20 +35061,61 @@ function resolveWatchableLocalFile(rawPath, roots) {
|
|
|
34884
35061
|
function defaultSpawn(command, args, options) {
|
|
34885
35062
|
return spawn5(command, [...args], options);
|
|
34886
35063
|
}
|
|
34887
|
-
function
|
|
35064
|
+
async function awaitOpenerStart(child, command) {
|
|
35065
|
+
child.unref();
|
|
35066
|
+
await new Promise((resolve12, reject) => {
|
|
35067
|
+
child.once("error", (error) => reject(new Error(
|
|
35068
|
+
error.code === "ENOENT" ? `${command} is not installed or not on PATH` : `could not start ${command}: ${error.message}`
|
|
35069
|
+
)));
|
|
35070
|
+
child.once("spawn", resolve12);
|
|
35071
|
+
});
|
|
35072
|
+
}
|
|
35073
|
+
function localFileOpenCommand(path3, selected, options = {}) {
|
|
35074
|
+
const platform3 = options.platform ?? process.platform;
|
|
35075
|
+
if (platform3 === "darwin") {
|
|
35076
|
+
return selected === "cursor" ? { command: "open", args: ["-a", "Cursor", path3] } : selected === "vscode" ? { command: "open", args: ["-a", "Visual Studio Code", path3] } : selected === "finder" ? { command: "open", args: ["-R", path3] } : { command: "open", args: [path3] };
|
|
35077
|
+
}
|
|
35078
|
+
if (platform3 === "win32") {
|
|
35079
|
+
if (selected === "system") return { command: "explorer.exe", args: [path3] };
|
|
35080
|
+
if (selected === "finder") return { command: "explorer.exe", args: [`/select,${path3}`] };
|
|
35081
|
+
const editor = WINDOWS_EDITOR_EXE[selected];
|
|
35082
|
+
const env = options.env ?? process.env;
|
|
35083
|
+
const exists = options.exists ?? existsSync8;
|
|
35084
|
+
const localAppData = env.LOCALAPPDATA;
|
|
35085
|
+
if (localAppData) {
|
|
35086
|
+
const exe = join25(localAppData, ...editor.exe);
|
|
35087
|
+
if (exists(exe)) return { command: exe, args: [path3] };
|
|
35088
|
+
}
|
|
35089
|
+
if (/["%\r\n]/u.test(path3)) throw new Error(`cannot hand ${path3} to the ${editor.shim} shim through cmd.exe`);
|
|
35090
|
+
return { command: "cmd.exe", args: ["/d", "/s", "/c", `"${editor.shim} "${path3}""`], verbatim: true };
|
|
35091
|
+
}
|
|
35092
|
+
return selected === "cursor" ? { command: "cursor", args: [path3] } : selected === "vscode" ? { command: "code", args: [path3] } : { command: "xdg-open", args: [path3] };
|
|
35093
|
+
}
|
|
35094
|
+
async function openLocalFile(rawPath, opener, roots, options = {}) {
|
|
34888
35095
|
const path3 = resolveLocalFile(rawPath, roots);
|
|
34889
35096
|
const selected = options.forceSystem ? "system" : opener;
|
|
34890
35097
|
if (selected === "copy") return { action: "copy", path: path3 };
|
|
34891
|
-
const spec =
|
|
34892
|
-
(options.spawn ?? defaultSpawn)(spec
|
|
35098
|
+
const spec = localFileOpenCommand(path3, selected, options);
|
|
35099
|
+
const child = (options.spawn ?? defaultSpawn)(spec.command, spec.args, {
|
|
35100
|
+
detached: true,
|
|
35101
|
+
stdio: "ignore",
|
|
35102
|
+
shell: false,
|
|
35103
|
+
windowsHide: true,
|
|
35104
|
+
...spec.verbatim ? { windowsVerbatimArguments: true } : {}
|
|
35105
|
+
});
|
|
35106
|
+
await awaitOpenerStart(child, spec.command);
|
|
34893
35107
|
return { action: "opened", path: path3 };
|
|
34894
35108
|
}
|
|
34895
|
-
var MARKDOWN_FILE_EXT, MARKDOWN_READ_LIMIT;
|
|
35109
|
+
var MARKDOWN_FILE_EXT, MARKDOWN_READ_LIMIT, WINDOWS_EDITOR_EXE;
|
|
34896
35110
|
var init_local_file = __esm({
|
|
34897
35111
|
"packages/server/src/local-file.ts"() {
|
|
34898
35112
|
"use strict";
|
|
34899
35113
|
MARKDOWN_FILE_EXT = /\.(?:md|mdx|markdown)$/i;
|
|
34900
35114
|
MARKDOWN_READ_LIMIT = 1024 * 1024;
|
|
35115
|
+
WINDOWS_EDITOR_EXE = {
|
|
35116
|
+
cursor: { exe: ["Programs", "cursor", "Cursor.exe"], shim: "cursor" },
|
|
35117
|
+
vscode: { exe: ["Programs", "Microsoft VS Code", "Code.exe"], shim: "code" }
|
|
35118
|
+
};
|
|
34901
35119
|
}
|
|
34902
35120
|
});
|
|
34903
35121
|
|
|
@@ -35068,12 +35286,12 @@ var init_awaiting = __esm({
|
|
|
35068
35286
|
});
|
|
35069
35287
|
|
|
35070
35288
|
// packages/server/src/backend/project-mcp-servers.ts
|
|
35071
|
-
import { readFileSync as
|
|
35289
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
35072
35290
|
import { homedir as homedir12 } from "node:os";
|
|
35073
35291
|
import { join as join27 } from "node:path";
|
|
35074
35292
|
function readJsonObject(path3) {
|
|
35075
35293
|
try {
|
|
35076
|
-
const parsed = JSON.parse(
|
|
35294
|
+
const parsed = JSON.parse(readFileSync19(path3, "utf8"));
|
|
35077
35295
|
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
35078
35296
|
} catch {
|
|
35079
35297
|
return void 0;
|
|
@@ -35198,17 +35416,17 @@ var init_project_mcp_servers = __esm({
|
|
|
35198
35416
|
});
|
|
35199
35417
|
|
|
35200
35418
|
// packages/server/src/worker-plugin-dir.ts
|
|
35201
|
-
import { existsSync as
|
|
35419
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
35202
35420
|
import { dirname as dirname10, join as join28 } from "node:path";
|
|
35203
35421
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
35204
35422
|
function resolveWorkerPluginDir(moduleUrl = import.meta.url, env = process.env) {
|
|
35205
35423
|
const override = env.FRIZZ_WORKER_PLUGIN_DIR;
|
|
35206
|
-
if (override &&
|
|
35424
|
+
if (override && existsSync9(join28(override, ".claude-plugin", "plugin.json")))
|
|
35207
35425
|
return override;
|
|
35208
35426
|
let current = dirname10(fileURLToPath3(moduleUrl));
|
|
35209
35427
|
for (; ; ) {
|
|
35210
35428
|
const candidate = join28(current, "cc-worker");
|
|
35211
|
-
if (
|
|
35429
|
+
if (existsSync9(join28(candidate, ".claude-plugin", "plugin.json"))) return candidate;
|
|
35212
35430
|
const parent = dirname10(current);
|
|
35213
35431
|
if (parent === current) return void 0;
|
|
35214
35432
|
current = parent;
|
|
@@ -35325,9 +35543,11 @@ nothing. Frizz bumps you for one, twice, then gives up \u2014 a turn wasted each
|
|
|
35325
35543
|
\`mcp__frizz__ask\` and \`mcp__frizz__watch\` each record a ROW, and frizz reads all three as a sign-off
|
|
35326
35544
|
\u2014 it will not bump you for a missing fence after any of them. A fence is a sentence with the lifetime
|
|
35327
35545
|
of the message carrying it, so it has to be rewritten at every rest and is wrong the moment anything
|
|
35328
|
-
changes; a row survives your turn ending, a compaction and a frizz restart. **The
|
|
35329
|
-
work**, they are still the right shape for the prose the human reads
|
|
35330
|
-
but where a verb exists for what you are saying, call it.
|
|
35546
|
+
changes; a row survives your turn ending, a compaction and a frizz restart. **The \`done\` and
|
|
35547
|
+
\`awaiting\` fences below still work**, they are still the right shape for the prose the human reads \u2014
|
|
35548
|
+
but where a verb exists for what you are saying, call it. **A QUESTION HAS NO FENCE ANY MORE** (retired
|
|
35549
|
+
2026-09-11): the only way to ask is \`mcp__frizz__ask\`, and a question written into a fence's body is
|
|
35550
|
+
plain prose \u2014 no card, no answer, and no sign-off.
|
|
35331
35551
|
|
|
35332
35552
|
**AN OPEN QUESTION IS A STANDING SIGN-OFF \u2014 REST NORMALLY.** Any unanswered question counts, not only
|
|
35333
35553
|
one you registered this turn, and frizz does not bump you for a missing fence while one is open. When
|
|
@@ -35341,8 +35561,9 @@ without it. Name what is still running in the prose (frizz lists every live shel
|
|
|
35341
35561
|
watcher under the prompt box whether or not a fence names it). A question you no longer need answered
|
|
35342
35562
|
is one you \`unask\`; only then can a park take.
|
|
35343
35563
|
|
|
35344
|
-
Use at most ONE fenced signal block, at the very END
|
|
35345
|
-
|
|
35564
|
+
Use at most ONE fenced signal block, at the very END (a question's PLACEMENT marker is not a signal
|
|
35565
|
+
block \u2014 see Questions for the human). The fence language is the state; the body is the card the human
|
|
35566
|
+
reads. An open registered question and real permission prompts are higher-priority asks
|
|
35346
35567
|
than \`done\` or \`awaiting\`.
|
|
35347
35568
|
|
|
35348
35569
|
**NAME THE PULL REQUEST, EVERY TIME YOU REST** \u2014 its number in EVERY resting message, not just the
|
|
@@ -35393,7 +35614,8 @@ exactly ONE of them.
|
|
|
35393
35614
|
- **A RECOMMENDATION IS NOT A CONCLUSION, AND AN UNSENT DRAFT IS NOT A DELIVERABLE.** When the
|
|
35394
35615
|
verdict is that SOMEONE SHOULD NOW DO SOMETHING \u2014 merge it, decline it, post this comment, pick
|
|
35395
35616
|
one of these designs, press the button you are not allowed to press \u2014 that someone is the human,
|
|
35396
|
-
so
|
|
35617
|
+
so register the question with \`mcp__frizz__ask\`, your recommendation as option A, and rest on
|
|
35618
|
+
it. Same for anything you WROTE
|
|
35397
35619
|
but did not SEND (a drafted comment, reply, issue body or release note): \`done\` files that draft
|
|
35398
35620
|
away with the thread.
|
|
35399
35621
|
- Follow-up work you DISCOVERED blocks \`done\` just as hard as work you were assigned, even when it
|
|
@@ -35479,8 +35701,9 @@ exactly ONE of them.
|
|
|
35479
35701
|
dimmed Snoozed band; any one dead, unknown or not yours \u21D2 you are BUMPED immediately and told which.
|
|
35480
35702
|
**A FENCE THAT NAMES NOTHING IS NOT A PARK** \u2014 \`for:\` describes a wait, it is not one \u2014 so
|
|
35481
35703
|
"waiting on the checks" plus a duration just sits in the queue: REGISTER the PR and name it, and
|
|
35482
|
-
if nothing is actually running, end with \`\`\`done or
|
|
35483
|
-
- **WAITING ON A PERSON IS A
|
|
35704
|
+
if nothing is actually running, end with \`\`\`done or register a question with \`mcp__frizz__ask\`.
|
|
35705
|
+
- **WAITING ON A PERSON IS A REGISTERED QUESTION** \u2014 \`mcp__frizz__ask\`. There is no human gate, no
|
|
35706
|
+
prose park, and no question fence.
|
|
35484
35707
|
- **CI, RELEASES, DEPLOYS AND MERGE PROGRESSION ARE AUTOMATABLE \u2014 never \` \`\`\`awaiting \` them
|
|
35485
35708
|
BLINDLY.** For a pull request, \`mcp__frizz__watch_pr\`. For anything else stay ACTIVE: dispatch a
|
|
35486
35709
|
sub-agent to own the wait (its return re-invokes you), or set a timer and name it here.
|
|
@@ -35491,13 +35714,14 @@ exactly ONE of them.
|
|
|
35491
35714
|
is still running, and emit a FRESH fence naming it. If it turns out to be automatable, arm the
|
|
35492
35715
|
active wait instead and do not fence.
|
|
35493
35716
|
|
|
35494
|
-
-
|
|
35717
|
+
- A REGISTERED QUESTION \u2014 \`mcp__frizz__ask\`, not a fence: you need the human's input, and the open
|
|
35718
|
+
row IS the handback; see **Questions for the human**.
|
|
35495
35719
|
|
|
35496
35720
|
### When the work is finished but the thread found more
|
|
35497
35721
|
|
|
35498
35722
|
Your mandate is complete and you turned up something else \u2014 a bug you were not sent for, a follow-up
|
|
35499
35723
|
the change implies. \`done\` is barred (the finding dies with the card), \`awaiting\` is barred (nothing
|
|
35500
|
-
is running), and
|
|
35724
|
+
is running), and asking only looks barred. That deadlock is not real, and stretching \`done\` is not
|
|
35501
35725
|
how you break it. Take the first exit that fits:
|
|
35502
35726
|
|
|
35503
35727
|
1. **DO IT \u2014 the default, and it covers far more than "small".** Anything inside your change's own
|
|
@@ -35506,10 +35730,10 @@ how you break it. Take the first exit that fits:
|
|
|
35506
35730
|
is more than one sitting, dispatch an in-session
|
|
35507
35731
|
SUB-AGENT: its result comes BACK to you, so the follow-up lands on YOUR card, in your diff, under one
|
|
35508
35732
|
review. Then \`done\` is honest with nothing left over.
|
|
35509
|
-
2. **ASK** \u2014 the human should choose whether or how it happens; "nothing was
|
|
35510
|
-
objection. This is the exit for work you cannot bound or verify, or that is
|
|
35511
|
-
call \u2014 and it is where a separable effort goes, as an OPTION they pick, rather
|
|
35512
|
-
create for them.
|
|
35733
|
+
2. **ASK** \u2014 \`mcp__frizz__ask\`: the human should choose whether or how it happens; "nothing was
|
|
35734
|
+
pending" is not an objection. This is the exit for work you cannot bound or verify, or that is
|
|
35735
|
+
genuinely the human's call \u2014 and it is where a separable effort goes, as an OPTION they pick, rather
|
|
35736
|
+
than a card you create for them.
|
|
35513
35737
|
3. **HAND IT OFF TO ITS OWN CARD \u2014 the LAST resort, not the tidy one.** \`mcp__frizz__spawn_thread\` puts
|
|
35514
35738
|
a separable effort on the board as its own thread; put the returned \`[title](/thread/<slug>)\` link
|
|
35515
35739
|
in the body. **A spawned thread is FIRE-AND-FORGET: it reports to the HUMAN, never to you, so
|
|
@@ -35577,88 +35801,101 @@ The API call is the unit of cost, not the tokens you type: each message re-reads
|
|
|
35577
35801
|
do not rest on a step you could take now.`;
|
|
35578
35802
|
QUESTIONS = `## Questions for the human
|
|
35579
35803
|
|
|
35580
|
-
You run under a dashboard, not a live chat, so
|
|
35581
|
-
|
|
35582
|
-
|
|
35804
|
+
You run under a dashboard, not a live chat, so a question is a ROW the human still owes an answer to,
|
|
35805
|
+
never a sentence in a message: \`mcp__frizz__ask\` registers it, and the dashboard renders it as an
|
|
35806
|
+
answerable card. **THERE IS NO \`question\` FENCE ANY MORE.** A fence with a question written in its body
|
|
35807
|
+
is plain prose \u2014 it draws no card, nothing can answer it, and it is not a sign-off. (Retired 2026-09-11:
|
|
35808
|
+
the fence was taught beside the tool for two weeks, workers went on writing it on most days, and nothing
|
|
35809
|
+
tracks a fence's answer \u2014 a fence is bytes in a message; the row is the only lifecycle.)
|
|
35583
35810
|
|
|
35584
|
-
|
|
35585
|
-
|
|
35586
|
-
|
|
35587
|
-
|
|
35588
|
-
|
|
35589
|
-
|
|
35590
|
-
|
|
35591
|
-
|
|
35592
|
-
|
|
35593
|
-
question
|
|
35594
|
-
|
|
35595
|
-
|
|
35596
|
-
|
|
35597
|
-
|
|
35598
|
-
|
|
35599
|
-
|
|
35600
|
-
|
|
35601
|
-
|
|
35602
|
-
|
|
35603
|
-
|
|
35811
|
+
\`ask\` takes \`questions\` \u2014 several at once, each with \`question\`, \`kind\` (\`question\` = pick one,
|
|
35812
|
+
\`multi\` = pick several, no options at all = free text), an optional \`danger\`, and \`options\` each
|
|
35813
|
+
carrying a \`label\`, a \`description\` (ONE LINE by default \u2014 name the trade-off and stop; earn more
|
|
35814
|
+
than that and spend it on a shape the human can SCAN, a short list, a table, a code block, the diff an
|
|
35815
|
+
option would produce, the message that would be posted, and never a run of one-sentence paragraphs;
|
|
35816
|
+
it renders inside the option, so the human sees it BEFORE choosing), an optional \`recommended\`, and
|
|
35817
|
+
optional \`followUps\`: questions that become live ONLY if that option is taken, so you can ask "and if
|
|
35818
|
+
so, which?" without asking it of somebody who said no. Written out, one registration reads:
|
|
35819
|
+
|
|
35820
|
+
question: Should the settings store use SQLite or a JSON file?
|
|
35821
|
+
kind: question
|
|
35822
|
+
options:
|
|
35823
|
+
- label: SQLite
|
|
35824
|
+
description: transactional, matches how sessions are already stored
|
|
35825
|
+
recommended: true
|
|
35826
|
+
- label: JSON file
|
|
35827
|
+
description: zero deps, human-editable, racy under concurrent writes
|
|
35828
|
+
|
|
35829
|
+
Each question must stand alone: the specific question on ONE line, options each with a one-line
|
|
35830
|
+
trade-off, and enough context to answer cold. The card letters the options A, B, C in the order you
|
|
35831
|
+
give them, so put the one you would take FIRST and mark it \`recommended\`; mark exactly one. Several
|
|
35832
|
+
independent questions are several entries of ONE \`ask\` call, never one bundled question. A bare
|
|
35833
|
+
"which approach?" with no options is a broken handoff.
|
|
35834
|
+
|
|
35835
|
+
NO "I" AND NO "you" ANYWHERE IN A QUESTION \u2014 the question, its options, or any interactive prompt.
|
|
35836
|
+
Clicking an option is the HUMAN speaking, so first and second person flip between writer and reader: in
|
|
35837
|
+
"Leave it \u2014 you run the command yourself" / "I run it now", neither line says which actor is which.
|
|
35838
|
+
Write the whole question actor-explicit \u2014 each option an instruction the human hands back ("Reinstall
|
|
35839
|
+
the hooks now") or one that names its actor outright ("the worker retries nightly", "left for the
|
|
35604
35840
|
maintainer's own terminal"):
|
|
35605
35841
|
|
|
35606
|
-
|
|
35607
|
-
|
|
35842
|
+
question: A reply for issue #482 explaining the workaround is drafted. Post it from the maintainer's GitHub account, or leave it a draft?
|
|
35843
|
+
kind: question
|
|
35844
|
+
options:
|
|
35845
|
+
- label: Post it
|
|
35846
|
+
description: the reporter is blocked and the workaround is verified
|
|
35847
|
+
recommended: true
|
|
35848
|
+
- label: Leave it in the handoff
|
|
35849
|
+
description: for edits before anything is posted
|
|
35608
35850
|
|
|
35609
|
-
|
|
35610
|
-
- B. Leave it in the handoff, for edits before anything is posted
|
|
35611
|
-
\`\`\`
|
|
35612
|
-
|
|
35613
|
-
Write the block in the human's OWN vocabulary: they have their original prompt and nothing else \u2014 not
|
|
35851
|
+
Write the question in the human's OWN vocabulary: they have their original prompt and nothing else \u2014 not
|
|
35614
35852
|
your plan, your notes, or the names you settled on while working. A name you coined mid-effort (a
|
|
35615
35853
|
phase, lane, tier, step or section number, "the C path", "the second variant") means nothing to them, so
|
|
35616
35854
|
translate it into what the thing does. Minimize code identifiers by default: lead with the behavior, and
|
|
35617
35855
|
spend a file, symbol or flag where it genuinely reads clearest or the human already uses it. Every input
|
|
35618
35856
|
the choice needs \u2014 what happens today, each option's user-visible consequence, any number that matters \u2014
|
|
35619
|
-
belongs INSIDE the
|
|
35620
|
-
|
|
35621
|
-
A GO/NO-GO gate is not a special
|
|
35622
|
-
decline, each a real choice the human can click). Tag
|
|
35623
|
-
|
|
35624
|
-
|
|
35625
|
-
|
|
35626
|
-
**
|
|
35627
|
-
|
|
35628
|
-
|
|
35629
|
-
|
|
35630
|
-
|
|
35631
|
-
|
|
35632
|
-
|
|
35633
|
-
|
|
35634
|
-
option would produce, the message that would be posted, and never a run of one-sentence paragraphs;
|
|
35635
|
-
it renders inside the option, so the human sees it BEFORE choosing), an optional
|
|
35636
|
-
\`recommended\`, and optional \`followUps\`: questions that become live ONLY if that option is taken,
|
|
35637
|
-
so you can ask "and if so, which?" without asking it of somebody who said no. **REGISTERING A QUESTION DOES NOT END YOUR TURN** \u2014
|
|
35638
|
-
it carries no timeout and waits on a person, so keep doing everything that does not depend on the
|
|
35639
|
-
answer. The answer comes back as its own wake, restating what was asked. Withdraw one you no longer
|
|
35640
|
-
need with \`mcp__frizz__unask\`, above all when you work the answer out yourself. **ON A THREAD
|
|
35641
|
-
CARRYING A GOAL IT REFUSES** \u2014 a standing "keep going, decide for yourself" is autonomous mode, and the
|
|
35642
|
-
refusal hands that instruction back at the moment it matters. Decide, and say which way you went. A
|
|
35643
|
-
genuinely human-owned call still belongs in the prose of your FINAL MESSAGE; autonomous does not mean
|
|
35644
|
-
nobody reads.
|
|
35857
|
+
belongs INSIDE the question and its options; "as discussed above" points at something they cannot see.
|
|
35858
|
+
|
|
35859
|
+
A GO/NO-GO gate is not a special kind \u2014 it is an ordinary \`question\` with two options (the go and the
|
|
35860
|
+
decline, each a real choice the human can click). Tag it only to change how it renders: \`danger\` for
|
|
35861
|
+
the genuinely irreversible (force-merge, deletion, history rewrite, prod rollback), \`kind: multi\` for
|
|
35862
|
+
select-several triage.
|
|
35863
|
+
|
|
35864
|
+
**REGISTERING A QUESTION DOES NOT END YOUR TURN** \u2014 it carries no timeout and waits on a person, so
|
|
35865
|
+
keep doing everything that does not depend on the answer, and register the question the moment you
|
|
35866
|
+
find it rather than saving it for the end. The answer comes back as its own wake, restating what was
|
|
35867
|
+
asked. Withdraw one you no longer need with \`mcp__frizz__unask\`, above all when you work the answer
|
|
35868
|
+
out yourself. **ON A THREAD CARRYING A GOAL IT REFUSES** \u2014 a standing "keep going, decide for yourself"
|
|
35869
|
+
is autonomous mode, and the refusal hands that instruction back at the moment it matters. Decide, and
|
|
35870
|
+
say which way you went. A genuinely human-owned call still belongs in the prose of your FINAL MESSAGE;
|
|
35871
|
+
autonomous does not mean nobody reads.
|
|
35645
35872
|
|
|
35646
35873
|
**EVERY OPEN QUESTION DRAWS ITS OWN CARD AT ITS REST \u2014 never write it into the handoff.** The
|
|
35647
|
-
registered card renders whether you mention it or not, and
|
|
35648
|
-
|
|
35649
|
-
|
|
35650
|
-
|
|
35651
|
-
|
|
35874
|
+
registered card renders whether you mention it or not, and prose that restates a registered question
|
|
35875
|
+
draws NOTHING: one question, one card, and answering the registered one is what settles the row and
|
|
35876
|
+
un-gates \`done\`. So the handoff's job is the reasoning around the ask, not the ask itself.
|
|
35877
|
+
|
|
35878
|
+
**TO PLACE A QUESTION INSIDE YOUR PROSE, WRITE AN EMPTY FENCE NAMING ITS ID** \u2014 the id \`ask\` returned
|
|
35879
|
+
(\`mcp__frizz__activity\` lists them). At the point your write-up has earned the ask \u2014 after the
|
|
35880
|
+
paragraph that sets it up, before the one that says what happens either way:
|
|
35881
|
+
|
|
35882
|
+
\`\`\`question qst_ab12cd34
|
|
35883
|
+
\`\`\`
|
|
35884
|
+
|
|
35885
|
+
Frizz draws the registered card THERE instead of at the tail. Nothing goes in the body: the card comes
|
|
35886
|
+
from the ROW, so nothing about the question is ever guessed from prose, and a marker naming no open
|
|
35887
|
+
question of yours draws nothing. One marker per question. Placement is OPTIONAL \u2014 a question with no
|
|
35888
|
+
marker renders at the tail of the rest, and a handoff that writes no marker loses nothing. Every answer
|
|
35889
|
+
of the rest is sent together, with ONE Send button at the tail, wherever the cards sit. (Placement was
|
|
35890
|
+
retired 2026-08-30 and restored 2026-09-11 with the free-form fence's retirement: the marker references
|
|
35891
|
+
a row, which is exactly what a fence with a body never did.)
|
|
35652
35892
|
|
|
35653
35893
|
A question you no longer want answered is not one you leave out of the write-up \u2014 it is one you
|
|
35654
35894
|
\`unask\`, which is the difference between deciding something yourself and quietly hoping nobody
|
|
35655
35895
|
answers. Questions asked in one \`ask\` call render together and send as one batch.
|
|
35656
35896
|
|
|
35657
|
-
|
|
35658
|
-
|
|
35659
|
-
A \` \`\`\`question \` block IS the handback \u2014 do not also emit a \`done\`/\`awaiting\` fence, and the same
|
|
35660
|
-
holds while a REGISTERED question stands open. Answers arrive as your next user message, possibly as
|
|
35661
|
-
terse as "1: A, 2: B".`;
|
|
35897
|
+
**AN OPEN REGISTERED QUESTION IS THE HANDBACK** \u2014 emit no \`done\`/\`awaiting\` fence beside it, and
|
|
35898
|
+
write no question fence at all. The answer arrives as your next user message, as its own wake.`;
|
|
35662
35899
|
STOP_CRITERION = `## The stop criterion
|
|
35663
35900
|
|
|
35664
35901
|
**COMING TO REST IS A STOP, and it needs the same justification as a question.** The far more common
|
|
@@ -35706,14 +35943,14 @@ Stop only when a wrong guess would be BOTH costly AND hard to undo: destructive
|
|
|
35706
35943
|
security posture with real exposure; product/UX direction that is genuinely the human's taste to set;
|
|
35707
35944
|
scope so vague that acting means inventing substantial new code with no way to check it. Everything
|
|
35708
35945
|
else \u2014 mechanical work, clear bugs, refactors, defaults you can justify, architecture you can reverse \u2014
|
|
35709
|
-
you finish. When you do stop,
|
|
35710
|
-
all the work that does NOT depend on the answer first.`;
|
|
35946
|
+
you finish. When you do stop, register it with \`mcp__frizz__ask\` with your recommendation marked, and
|
|
35947
|
+
do all the work that does NOT depend on the answer first.`;
|
|
35711
35948
|
TRIVIAL_PROMPTS = `## Trivial and conversational prompts
|
|
35712
35949
|
|
|
35713
35950
|
Some dispatches never deserved a work effort \u2014 a greeting, a one-line question, a joke, a test ping.
|
|
35714
35951
|
Resolve them with ZERO ceremony: answer inline in one message, and close with a \` \`\`\`done \` fence whose
|
|
35715
35952
|
body is one line ("Answered inline \u2014 conversational prompt, nothing to ship."). If the answer genuinely
|
|
35716
|
-
needs a reply,
|
|
35953
|
+
needs a reply, register it with \`mcp__frizz__ask\` instead. Do not manufacture scope, restate the "task", or ask
|
|
35717
35954
|
clarifying questions to seem busy.`;
|
|
35718
35955
|
THREAD_NAME = `## Name your thread once you know what the work is
|
|
35719
35956
|
|
|
@@ -36093,7 +36330,7 @@ Dispatches share a vocabulary for the deliverable and quality bar, not for fleet
|
|
|
36093
36330
|
first that works? Cover and synthesize every relevant prong inline unless delegation was
|
|
36094
36331
|
explicitly requested. Close with a \` \`\`\`done \` fence listing the completed research/evidence \u2014
|
|
36095
36332
|
the report IS this thread's deliverable, unlike a bug/issue investigation headed for a fix, which
|
|
36096
|
-
bare-rests; use \`
|
|
36333
|
+
bare-rests; use \`mcp__frizz__ask\` for a human call.
|
|
36097
36334
|
- **Audit thread** \u2014 adversarially verify correctness, safety, or compatibility of something that
|
|
36098
36335
|
exists. Exercise proportionate cases and lenses until dry; re-check load-bearing verdicts and cite
|
|
36099
36336
|
evidence. Thorough coverage is required, but the audit label alone does not authorize fan-out.
|
|
@@ -36374,11 +36611,10 @@ import { homedir as homedir13 } from "node:os";
|
|
|
36374
36611
|
function clip(text2, max) {
|
|
36375
36612
|
return text2.length > max ? `${text2.slice(0, max - 1)}\u2026` : text2;
|
|
36376
36613
|
}
|
|
36377
|
-
function tildePath(path3) {
|
|
36378
|
-
const home = homedir13();
|
|
36614
|
+
function tildePath(path3, home = homedir13()) {
|
|
36379
36615
|
if (!path3.startsWith(home)) return path3;
|
|
36380
36616
|
const rest = path3.slice(home.length);
|
|
36381
|
-
return rest === "" ? "~" : rest
|
|
36617
|
+
return rest === "" ? "~" : /^[\\/]/.test(rest) ? `~${rest}` : path3;
|
|
36382
36618
|
}
|
|
36383
36619
|
function inputPreview(input, shown) {
|
|
36384
36620
|
if (!input) return "";
|
|
@@ -36612,8 +36848,8 @@ var init_claude_permission_interactions = __esm({
|
|
|
36612
36848
|
HAS_UNSAFE_TEXT = new RegExp(UNSAFE_TEXT_SOURCE, "u");
|
|
36613
36849
|
CLAUDE_NOTES_ONLY = "(notes only)";
|
|
36614
36850
|
CLAUDE_QUESTION_DECLINED = "The operator did not answer this question from the frizz dashboard. Do not ask it again with this tool \u2014 decide with the information you already have, or raise it in your final message.";
|
|
36615
|
-
CLAUDE_ASK_DENY_MESSAGE = "Frizz could not render this question as a card.
|
|
36616
|
-
CLAUDE_ASK_WITHDRAWN_MESSAGE = "This question was withdrawn before anyone answered it \u2014 most likely because the operator sent a message instead. Read their next message and follow it. Do not ask this again with this tool; if you still need the decision,
|
|
36851
|
+
CLAUDE_ASK_DENY_MESSAGE = "Frizz could not render this question as a card. Register it with `mcp__frizz__ask` instead \u2014 context, the question, options with a one-line trade-off each, the recommended one first \u2014 never as a ```question fence, which is retired. The operator answers it from the queue and the reply arrives as your next user message.";
|
|
36852
|
+
CLAUDE_ASK_WITHDRAWN_MESSAGE = "This question was withdrawn before anyone answered it \u2014 most likely because the operator sent a message instead. Read their next message and follow it. Do not ask this again with this tool; if you still need the decision, register it with `mcp__frizz__ask`.";
|
|
36617
36853
|
}
|
|
36618
36854
|
});
|
|
36619
36855
|
|
|
@@ -37025,7 +37261,7 @@ var init_claude_agent_broker_bridge = __esm({
|
|
|
37025
37261
|
});
|
|
37026
37262
|
|
|
37027
37263
|
// packages/server/src/dispatch.ts
|
|
37028
|
-
import { closeSync as closeSync12, constants, existsSync as
|
|
37264
|
+
import { closeSync as closeSync12, constants, existsSync as existsSync10, fstatSync as fstatSync2, lstatSync as lstatSync3, openSync as openSync13, readFileSync as readFileSync20, realpathSync as realpathSync8, statSync as statSync13, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, rmSync as rmSync9 } from "node:fs";
|
|
37029
37265
|
import { basename as basename6, join as join29, dirname as dirname11 } from "node:path";
|
|
37030
37266
|
import { createHash as createHash11, randomUUID as randomUUID13 } from "node:crypto";
|
|
37031
37267
|
function fallbackTitle(prompt) {
|
|
@@ -37045,7 +37281,7 @@ function fallbackTitle(prompt) {
|
|
|
37045
37281
|
}
|
|
37046
37282
|
function resolveSlug(frizzDir, base, taken) {
|
|
37047
37283
|
base = ThreadSlug.parse(base);
|
|
37048
|
-
const isTaken = (slug) =>
|
|
37284
|
+
const isTaken = (slug) => existsSync10(join29(frizzDir, `${slug}.md`)) || (taken?.(slug) ?? false);
|
|
37049
37285
|
if (!isTaken(base)) return base;
|
|
37050
37286
|
for (let n = 2; ; n++) {
|
|
37051
37287
|
const suffix = `-${n}`;
|
|
@@ -37078,7 +37314,7 @@ function resolveLegacyThreadFile(projectDir, value) {
|
|
|
37078
37314
|
const fd = openSync13(path3, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
37079
37315
|
try {
|
|
37080
37316
|
openedBefore = fstatSync2(fd);
|
|
37081
|
-
contents =
|
|
37317
|
+
contents = readFileSync20(fd);
|
|
37082
37318
|
openedAfter = fstatSync2(fd);
|
|
37083
37319
|
} finally {
|
|
37084
37320
|
closeSync12(fd);
|
|
@@ -37142,7 +37378,7 @@ function monitorScriptsDir() {
|
|
|
37142
37378
|
const plugin = workerPluginDir();
|
|
37143
37379
|
if (!plugin) return void 0;
|
|
37144
37380
|
const dir = join29(plugin, "skills", "gh", "scripts");
|
|
37145
|
-
return
|
|
37381
|
+
return existsSync10(join29(dir, "ci-watch.mjs")) ? dir : void 0;
|
|
37146
37382
|
}
|
|
37147
37383
|
function codexScratchpadHookConfig(hookScript, sessionId) {
|
|
37148
37384
|
if (!hookScript || !sessionId) return {};
|
|
@@ -37201,7 +37437,7 @@ function frizzConfigBlock(projectDir) {
|
|
|
37201
37437
|
try {
|
|
37202
37438
|
const st = statSync13(path3);
|
|
37203
37439
|
if (!st.isFile() || st.size > FRIZZ_MD_MAX_BYTES) return "";
|
|
37204
|
-
body =
|
|
37440
|
+
body = readFileSync20(path3, "utf8").trim();
|
|
37205
37441
|
} catch {
|
|
37206
37442
|
return "";
|
|
37207
37443
|
}
|
|
@@ -37241,7 +37477,7 @@ function resolveFrizzMcp(target, moduleUrl = import.meta.url, env = process.env,
|
|
|
37241
37477
|
const pluginDir = resolveWorkerPluginDir(moduleUrl, env);
|
|
37242
37478
|
if (!pluginDir) return void 0;
|
|
37243
37479
|
const scriptPath = join29(pluginDir, "bin", FRIZZ_MCP.script);
|
|
37244
|
-
if (!
|
|
37480
|
+
if (!existsSync10(scriptPath)) return void 0;
|
|
37245
37481
|
return {
|
|
37246
37482
|
scriptPath,
|
|
37247
37483
|
stateDir,
|
|
@@ -38089,20 +38325,20 @@ function resolveSessionProfile(row, tele) {
|
|
|
38089
38325
|
function resolveSessionPermission(row, tele) {
|
|
38090
38326
|
const saved = PermissionMode.safeParse(row.permission_mode);
|
|
38091
38327
|
const pending = PermissionMode.safeParse(row.permission_pending);
|
|
38092
|
-
const
|
|
38093
|
-
if (saved.success && pending.success &&
|
|
38328
|
+
const normalize2 = (mode) => effectivePermissionMode(row.backend === "codex" ? "codex" : "claude", mode);
|
|
38329
|
+
if (saved.success && pending.success && normalize2(saved.data) === normalize2(pending.data)) return normalize2(saved.data);
|
|
38094
38330
|
if (row.backend === "codex" && saved.success) {
|
|
38095
38331
|
const observedAt = tele?.permissionModeAt ? Date.parse(tele.permissionModeAt) : NaN;
|
|
38096
38332
|
const spawnedAt = Date.parse(row.spawned_at);
|
|
38097
38333
|
if (tele?.permissionMode && Number.isFinite(observedAt) && Number.isFinite(spawnedAt) && observedAt >= spawnedAt) {
|
|
38098
38334
|
const setAt = row.permission_set_at ? Date.parse(row.permission_set_at) : NaN;
|
|
38099
|
-
if (Number.isFinite(setAt) && setAt > observedAt) return
|
|
38100
|
-
return
|
|
38335
|
+
if (Number.isFinite(setAt) && setAt > observedAt) return normalize2(saved.data);
|
|
38336
|
+
return normalize2(tele.permissionMode);
|
|
38101
38337
|
}
|
|
38102
|
-
return
|
|
38338
|
+
return normalize2(saved.data);
|
|
38103
38339
|
}
|
|
38104
|
-
if (saved.success) return
|
|
38105
|
-
return tele?.permissionMode ?
|
|
38340
|
+
if (saved.success) return normalize2(saved.data);
|
|
38341
|
+
return tele?.permissionMode ? normalize2(tele.permissionMode) : void 0;
|
|
38106
38342
|
}
|
|
38107
38343
|
function resolvePendingPermission(row) {
|
|
38108
38344
|
const parsed = PermissionMode.safeParse(row.permission_pending);
|
|
@@ -40264,8 +40500,8 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
|
|
|
40264
40500
|
"in a sentence breaks the YAML, which is why `reason:` is gone.",
|
|
40265
40501
|
"",
|
|
40266
40502
|
"`mcp__frizz__activity` lists everything you have running, and prints it back as a ready-to-paste",
|
|
40267
|
-
"fence. If you are not waiting on anything, you are not awaiting \u2014 end with ```done, or
|
|
40268
|
-
"
|
|
40503
|
+
"fence. If you are not waiting on anything, you are not awaiting \u2014 end with ```done, or register",
|
|
40504
|
+
"a question with `mcp__frizz__ask`."
|
|
40269
40505
|
].join("\n") : nameless ? [
|
|
40270
40506
|
`${PARK_CORRECTION_NAMES_LEAD}nothing to wait on, so it is not a park \u2014 your thread is still`,
|
|
40271
40507
|
"in the queue, and nothing will wake you.",
|
|
@@ -40281,15 +40517,15 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
|
|
|
40281
40517
|
"Use `mcp__frizz__activity` to read it all back as a ready-to-paste fence.",
|
|
40282
40518
|
"",
|
|
40283
40519
|
"AND IF YOU ARE NOT WAITING ON ANYTHING, you are not awaiting \u2014 you are done. End with ```done,",
|
|
40284
|
-
"or
|
|
40520
|
+
"or register a question with `mcp__frizz__ask` if you need the human."
|
|
40285
40521
|
].join("\n") : expired ? parkExpiredWakeMessage(status) : allFinished ? parkFinishedWakeMessage(status, dead.length !== 1) : [
|
|
40286
40522
|
`${PARK_CORRECTION_NAMES_LEAD}${dead.length === 1 ? "something that is" : "things that are"} not running, so it is not a park and your thread stayed in the queue.`,
|
|
40287
40523
|
"",
|
|
40288
40524
|
...status,
|
|
40289
40525
|
"",
|
|
40290
40526
|
"Use `mcp__frizz__activity` to read back what you actually have out, with the exact id each line",
|
|
40291
|
-
"needs, then re-fence naming only those \u2014 or end in ```done or a
|
|
40292
|
-
"left to wait for."
|
|
40527
|
+
"needs, then re-fence naming only those \u2014 or end in ```done or a registered question",
|
|
40528
|
+
"(`mcp__frizz__ask`) if there is nothing left to wait for."
|
|
40293
40529
|
].join("\n");
|
|
40294
40530
|
const cause = retired.length > 0 ? "retired" : nameless ? "nameless" : expired ? "expired" : "dead";
|
|
40295
40531
|
const ops = cause === "expired" ? [] : liveOpsLines({
|
|
@@ -41431,10 +41667,31 @@ import { randomBytes } from "node:crypto";
|
|
|
41431
41667
|
import pty from "node-pty";
|
|
41432
41668
|
function createLoginUtility(deps) {
|
|
41433
41669
|
const spawnPty = deps.spawnPty ?? pty.spawn;
|
|
41670
|
+
const env = deps.env ?? process.env;
|
|
41434
41671
|
const lifetimeMs = deps.lifetimeMs ?? ATTEMPT_LIFETIME_MS;
|
|
41435
41672
|
const attempts = /* @__PURE__ */ new Map();
|
|
41436
41673
|
function loginArgv(backend) {
|
|
41437
|
-
|
|
41674
|
+
if (backend === "codex") {
|
|
41675
|
+
const codex = resolveCodexExecutable(deps.codexBin, { env });
|
|
41676
|
+
return { file: codex.file, args: [...codex.args, "login"] };
|
|
41677
|
+
}
|
|
41678
|
+
return { file: resolveClaudeExecutableAbsolute(deps.claudeBin, env), args: ["auth", "login"] };
|
|
41679
|
+
}
|
|
41680
|
+
function failedAttempt(id, backend, cause) {
|
|
41681
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
41682
|
+
const error = `Could not start the ${backend === "codex" ? "Codex" : "Claude"} sign-in: ${reason}`;
|
|
41683
|
+
return {
|
|
41684
|
+
id,
|
|
41685
|
+
backend,
|
|
41686
|
+
timer: setTimeout(() => teardown(id), lifetimeMs),
|
|
41687
|
+
buffer: `${error}\r
|
|
41688
|
+
`,
|
|
41689
|
+
bufferBytes: Buffer.byteLength(error) + 2,
|
|
41690
|
+
exited: true,
|
|
41691
|
+
error,
|
|
41692
|
+
dataListeners: /* @__PURE__ */ new Set(),
|
|
41693
|
+
exitListeners: /* @__PURE__ */ new Set()
|
|
41694
|
+
};
|
|
41438
41695
|
}
|
|
41439
41696
|
function teardown(id) {
|
|
41440
41697
|
const attempt = attempts.get(id);
|
|
@@ -41452,7 +41709,7 @@ function createLoginUtility(deps) {
|
|
|
41452
41709
|
}
|
|
41453
41710
|
attempt.exitListeners.clear();
|
|
41454
41711
|
try {
|
|
41455
|
-
attempt.term
|
|
41712
|
+
attempt.term?.kill();
|
|
41456
41713
|
} catch {
|
|
41457
41714
|
}
|
|
41458
41715
|
}
|
|
@@ -41464,14 +41721,22 @@ function createLoginUtility(deps) {
|
|
|
41464
41721
|
teardown(attempt2.id);
|
|
41465
41722
|
}
|
|
41466
41723
|
const id = `login-${randomBytes(8).toString("hex")}`;
|
|
41467
|
-
|
|
41468
|
-
|
|
41469
|
-
|
|
41470
|
-
|
|
41471
|
-
|
|
41472
|
-
|
|
41473
|
-
|
|
41474
|
-
|
|
41724
|
+
let term;
|
|
41725
|
+
try {
|
|
41726
|
+
const { file, args } = loginArgv(backend);
|
|
41727
|
+
term = spawnPty(file, args, {
|
|
41728
|
+
name: process.platform === "win32" ? "xterm-color" : "xterm-256color",
|
|
41729
|
+
cwd: deps.cwd,
|
|
41730
|
+
env,
|
|
41731
|
+
cols: 120,
|
|
41732
|
+
rows: 30
|
|
41733
|
+
});
|
|
41734
|
+
} catch (cause) {
|
|
41735
|
+
const failed = failedAttempt(id, backend, cause);
|
|
41736
|
+
failed.timer.unref?.();
|
|
41737
|
+
attempts.set(id, failed);
|
|
41738
|
+
return { attemptId: id };
|
|
41739
|
+
}
|
|
41475
41740
|
const attempt = {
|
|
41476
41741
|
id,
|
|
41477
41742
|
backend,
|
|
@@ -41523,7 +41788,7 @@ function createLoginUtility(deps) {
|
|
|
41523
41788
|
write: (data) => {
|
|
41524
41789
|
if (!attempt.exited) {
|
|
41525
41790
|
try {
|
|
41526
|
-
attempt.term
|
|
41791
|
+
attempt.term?.write(data);
|
|
41527
41792
|
} catch {
|
|
41528
41793
|
}
|
|
41529
41794
|
}
|
|
@@ -41531,7 +41796,7 @@ function createLoginUtility(deps) {
|
|
|
41531
41796
|
resize: (cols, rows) => {
|
|
41532
41797
|
if (!attempt.exited) {
|
|
41533
41798
|
try {
|
|
41534
|
-
attempt.term
|
|
41799
|
+
attempt.term?.resize(cols, rows);
|
|
41535
41800
|
} catch {
|
|
41536
41801
|
}
|
|
41537
41802
|
}
|
|
@@ -41552,7 +41817,7 @@ function createLoginUtility(deps) {
|
|
|
41552
41817
|
status(attemptId) {
|
|
41553
41818
|
const attempt = attempts.get(attemptId);
|
|
41554
41819
|
if (!attempt) return { state: "exited" };
|
|
41555
|
-
return { state: attempt.exited ? "exited" : "running", backend: attempt.backend };
|
|
41820
|
+
return { state: attempt.exited ? "exited" : "running", backend: attempt.backend, ...attempt.error ? { error: attempt.error } : {} };
|
|
41556
41821
|
},
|
|
41557
41822
|
cancel(attemptId) {
|
|
41558
41823
|
teardown(attemptId);
|
|
@@ -41566,6 +41831,8 @@ var ATTEMPT_LIFETIME_MS, REPLAY_CAP_BYTES;
|
|
|
41566
41831
|
var init_login_utility = __esm({
|
|
41567
41832
|
"packages/server/src/login-utility.ts"() {
|
|
41568
41833
|
"use strict";
|
|
41834
|
+
init_claude_broker_host();
|
|
41835
|
+
init_codex_executable();
|
|
41569
41836
|
ATTEMPT_LIFETIME_MS = 10 * 60 * 1e3;
|
|
41570
41837
|
REPLAY_CAP_BYTES = 256 * 1024;
|
|
41571
41838
|
}
|
|
@@ -41677,7 +41944,7 @@ var init_stale_socket_sweep = __esm({
|
|
|
41677
41944
|
|
|
41678
41945
|
// packages/server/src/orphan-reaper.ts
|
|
41679
41946
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
41680
|
-
import { readFileSync as
|
|
41947
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
41681
41948
|
import { dirname as dirname13 } from "node:path";
|
|
41682
41949
|
function firstTokenBasename(command) {
|
|
41683
41950
|
const first = command.trimStart().split(/\s+/, 1)[0] ?? "";
|
|
@@ -41833,7 +42100,7 @@ ${chunk}`;
|
|
|
41833
42100
|
}
|
|
41834
42101
|
return rows;
|
|
41835
42102
|
}
|
|
41836
|
-
function reapSubtrees(seed, rows, protectedPids, live, kill =
|
|
42103
|
+
function reapSubtrees(seed, rows, protectedPids, live, kill = defaultKill2) {
|
|
41837
42104
|
const keep = (row) => protectedPids.has(row.pid) || isSessionRoot(row.command) || isTmuxServer(row.command) || row.slug !== null && live.has(row.slug);
|
|
41838
42105
|
const order = subtreeKillOrder(seed, rows, keep);
|
|
41839
42106
|
for (const pid of order) kill(pid);
|
|
@@ -41885,8 +42152,9 @@ function sweepOrphansOnce(deps = {}) {
|
|
|
41885
42152
|
let rows;
|
|
41886
42153
|
try {
|
|
41887
42154
|
rows = enumerateProcs(exec, { platform: deps.platform, readEnv: deps.readEnv });
|
|
41888
|
-
} catch {
|
|
41889
|
-
|
|
42155
|
+
} catch (error) {
|
|
42156
|
+
const code = error?.code;
|
|
42157
|
+
return { reaped: 0, deadSlugs: [], liveSlugs: [], ...code === "ENOENT" ? { unavailable: "`ps` is not on PATH" } : {} };
|
|
41890
42158
|
}
|
|
41891
42159
|
const protectedPids = selfAndAncestors(rows, selfPid);
|
|
41892
42160
|
const { reap, liveSlugs } = decideOrphans(rows, { minAgeMs, protectedPids });
|
|
@@ -41910,14 +42178,25 @@ function sweepStaleBrokerSockets() {
|
|
|
41910
42178
|
}
|
|
41911
42179
|
}
|
|
41912
42180
|
function startOrphanReaper(deps = {}) {
|
|
42181
|
+
if ((deps.platform ?? process.platform) === "win32") {
|
|
42182
|
+
deps.log?.(UNAVAILABLE_ON_WINDOWS);
|
|
42183
|
+
return () => {
|
|
42184
|
+
};
|
|
42185
|
+
}
|
|
41913
42186
|
const intervalMs = deps.intervalMs ?? ORPHAN_REAP_INTERVAL_MS;
|
|
42187
|
+
const unavailable = (result) => {
|
|
42188
|
+
if (!result.unavailable) return false;
|
|
42189
|
+
deps.log?.(`orphan-reaper: unavailable on this machine \u2014 ${result.unavailable}; background processes a stopped thread leaves behind are not collected here`);
|
|
42190
|
+
return true;
|
|
42191
|
+
};
|
|
41914
42192
|
try {
|
|
41915
|
-
sweepOrphansOnce(deps)
|
|
42193
|
+
if (unavailable(sweepOrphansOnce(deps))) return () => {
|
|
42194
|
+
};
|
|
41916
42195
|
} catch {
|
|
41917
42196
|
}
|
|
41918
42197
|
const timer = setInterval(() => {
|
|
41919
42198
|
try {
|
|
41920
|
-
sweepOrphansOnce(deps);
|
|
42199
|
+
if (unavailable(sweepOrphansOnce(deps))) clearInterval(timer);
|
|
41921
42200
|
} catch {
|
|
41922
42201
|
}
|
|
41923
42202
|
}, intervalMs);
|
|
@@ -41932,7 +42211,7 @@ function startOrphanReaper(deps = {}) {
|
|
|
41932
42211
|
clearInterval(socketTimer);
|
|
41933
42212
|
};
|
|
41934
42213
|
}
|
|
41935
|
-
var ORPHAN_REAP_INTERVAL_MS, ORPHAN_MIN_AGE_MS, SLUG_RE, defaultExec, defaultReadEnv,
|
|
42214
|
+
var ORPHAN_REAP_INTERVAL_MS, ORPHAN_MIN_AGE_MS, SLUG_RE, defaultExec, defaultReadEnv, defaultKill2, STALE_SOCKET_SWEEP_INTERVAL_MS, STALE_SOCKET_SWEEP_DELAY_MS, CLAUDE_BROKER_SOCKET_PREFIX, UNAVAILABLE_ON_WINDOWS;
|
|
41936
42215
|
var init_orphan_reaper = __esm({
|
|
41937
42216
|
"packages/server/src/orphan-reaper.ts"() {
|
|
41938
42217
|
"use strict";
|
|
@@ -41944,12 +42223,12 @@ var init_orphan_reaper = __esm({
|
|
|
41944
42223
|
defaultExec = (file, args) => execFileSync4(file, args, { encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 1e4 });
|
|
41945
42224
|
defaultReadEnv = (pid) => {
|
|
41946
42225
|
try {
|
|
41947
|
-
return
|
|
42226
|
+
return readFileSync21(`/proc/${pid}/environ`, "utf8");
|
|
41948
42227
|
} catch {
|
|
41949
42228
|
return null;
|
|
41950
42229
|
}
|
|
41951
42230
|
};
|
|
41952
|
-
|
|
42231
|
+
defaultKill2 = (pid) => {
|
|
41953
42232
|
try {
|
|
41954
42233
|
process.kill(pid, "SIGKILL");
|
|
41955
42234
|
} catch {
|
|
@@ -41958,6 +42237,7 @@ var init_orphan_reaper = __esm({
|
|
|
41958
42237
|
STALE_SOCKET_SWEEP_INTERVAL_MS = 10 * 6e4;
|
|
41959
42238
|
STALE_SOCKET_SWEEP_DELAY_MS = 3e4;
|
|
41960
42239
|
CLAUDE_BROKER_SOCKET_PREFIX = "frizz-claude-";
|
|
42240
|
+
UNAVAILABLE_ON_WINDOWS = "orphan-reaper: unavailable on Windows \u2014 a worker's FRIZZ_THREAD is read from its process environment, which Windows does not expose; background processes a stopped thread leaves behind are not collected here, and a live thread's runaway work is not reported";
|
|
41961
42241
|
}
|
|
41962
42242
|
});
|
|
41963
42243
|
|
|
@@ -45299,7 +45579,7 @@ var init_server = __esm({
|
|
|
45299
45579
|
});
|
|
45300
45580
|
|
|
45301
45581
|
// packages/server/src/repair.ts
|
|
45302
|
-
import { existsSync as
|
|
45582
|
+
import { existsSync as existsSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync11 } from "node:fs";
|
|
45303
45583
|
import { basename as basename7, dirname as dirname14, resolve as resolve8 } from "node:path";
|
|
45304
45584
|
function quoteValue(v) {
|
|
45305
45585
|
if (/^[\w./#:+-]+$/.test(v)) return v;
|
|
@@ -45314,8 +45594,8 @@ function repairThreadFile(frizzDir, file) {
|
|
|
45314
45594
|
const abs = resolve8(root, file);
|
|
45315
45595
|
if (dirname14(abs) !== root) throw new RepairError(`refusing to repair "${file}": not directly under .frizz/`);
|
|
45316
45596
|
if (!abs.endsWith(".md")) throw new RepairError(`refusing to repair "${file}": not a .md thread file`);
|
|
45317
|
-
if (!
|
|
45318
|
-
const content =
|
|
45597
|
+
if (!existsSync12(abs)) throw new RepairError(`no thread file to repair: ${basename7(abs)}`);
|
|
45598
|
+
const content = readFileSync22(abs, "utf8");
|
|
45319
45599
|
const firstLine2 = content.replace(/^/, "").split(/\r?\n/).find((l) => l.trim() !== "");
|
|
45320
45600
|
if (firstLine2?.trim() === "---") {
|
|
45321
45601
|
throw new RepairError(`${basename7(abs)} already opens with a "---" block \u2014 repair only heals a MISSING frontmatter block`);
|
|
@@ -45506,9 +45786,9 @@ var init_repo_files = __esm({
|
|
|
45506
45786
|
});
|
|
45507
45787
|
|
|
45508
45788
|
// cc-worker/hooks/bash-background.mjs
|
|
45509
|
-
import { readFileSync as
|
|
45789
|
+
import { readFileSync as readFileSync23, realpathSync as realpathSync10 } from "node:fs";
|
|
45510
45790
|
import { basename as basename8 } from "node:path";
|
|
45511
|
-
import { pathToFileURL } from "node:url";
|
|
45791
|
+
import { fileURLToPath as fileURLToPath4, pathToFileURL } from "node:url";
|
|
45512
45792
|
function emit(obj) {
|
|
45513
45793
|
process.stdout.write(JSON.stringify(obj));
|
|
45514
45794
|
process.exit(0);
|
|
@@ -45644,8 +45924,14 @@ function evaluateBashBackgroundHook(input, env = process.env) {
|
|
|
45644
45924
|
}
|
|
45645
45925
|
};
|
|
45646
45926
|
}
|
|
45647
|
-
function isDirectHookExecution(argv1, moduleUrl) {
|
|
45648
|
-
|
|
45927
|
+
function isDirectHookExecution(argv1, moduleUrl, realpath = realpathSync10) {
|
|
45928
|
+
if (typeof argv1 !== "string" || basename8(argv1) !== "bash-background.mjs") return false;
|
|
45929
|
+
if (pathToFileURL(argv1).href === moduleUrl) return true;
|
|
45930
|
+
try {
|
|
45931
|
+
return realpath(argv1) === realpath(fileURLToPath4(moduleUrl));
|
|
45932
|
+
} catch {
|
|
45933
|
+
return false;
|
|
45934
|
+
}
|
|
45649
45935
|
}
|
|
45650
45936
|
var LOCAL_SHELL_SCRIPT;
|
|
45651
45937
|
var init_bash_background = __esm({
|
|
@@ -45655,7 +45941,7 @@ var init_bash_background = __esm({
|
|
|
45655
45941
|
if (isDirectHookExecution(process.argv[1], import.meta.url)) {
|
|
45656
45942
|
try {
|
|
45657
45943
|
const env = process.argv.includes("--frizz-thread") ? { ...process.env, FRIZZ_THREAD: process.env.FRIZZ_THREAD || "codex-worker" } : process.env;
|
|
45658
|
-
emit(evaluateBashBackgroundHook(JSON.parse(
|
|
45944
|
+
emit(evaluateBashBackgroundHook(JSON.parse(readFileSync23(0, "utf8")), env));
|
|
45659
45945
|
} catch {
|
|
45660
45946
|
emit({});
|
|
45661
45947
|
}
|
|
@@ -45664,7 +45950,7 @@ var init_bash_background = __esm({
|
|
|
45664
45950
|
});
|
|
45665
45951
|
|
|
45666
45952
|
// packages/server/src/transcript.ts
|
|
45667
|
-
import { closeSync as closeSync13, existsSync as
|
|
45953
|
+
import { closeSync as closeSync13, existsSync as existsSync13, fstatSync as fstatSync3, mkdirSync as mkdirSync15, openSync as openSync14, readdirSync as readdirSync9, readFileSync as readFileSync24, readSync as readSync7, renameSync as renameSync9, statSync as statSync15, unlinkSync as unlinkSync5, writeFileSync as writeFileSync12 } from "node:fs";
|
|
45668
45954
|
import { createHash as createHash13 } from "node:crypto";
|
|
45669
45955
|
import { StringDecoder as StringDecoder4 } from "node:string_decoder";
|
|
45670
45956
|
import { join as join35 } from "node:path";
|
|
@@ -46286,7 +46572,7 @@ function persistBase64Image(mediaType, data, idKey) {
|
|
|
46286
46572
|
const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
|
|
46287
46573
|
const path3 = join35(SCREENSHOT_CACHE_DIR, `${name}.${ext}`);
|
|
46288
46574
|
try {
|
|
46289
|
-
if (
|
|
46575
|
+
if (existsSync13(path3)) return path3;
|
|
46290
46576
|
if (data.length > SCREENSHOT_MAX_BASE64) return void 0;
|
|
46291
46577
|
const buf = Buffer.from(data, "base64");
|
|
46292
46578
|
if (buf.length === 0 || !looksLikeImage(buf, ext)) return void 0;
|
|
@@ -46307,10 +46593,10 @@ function persistSentFile(srcPath, idKey) {
|
|
|
46307
46593
|
try {
|
|
46308
46594
|
const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
|
|
46309
46595
|
const dest = join35(SCREENSHOT_CACHE_DIR, `${name}.${outExt}`);
|
|
46310
|
-
if (
|
|
46596
|
+
if (existsSync13(dest)) return dest;
|
|
46311
46597
|
const size = statSync15(srcPath).size;
|
|
46312
46598
|
if (size === 0 || size > SENT_IMAGE_MAX_BYTES) return void 0;
|
|
46313
|
-
const buf =
|
|
46599
|
+
const buf = readFileSync24(srcPath);
|
|
46314
46600
|
if (!looksLikeImage(buf, outExt)) return void 0;
|
|
46315
46601
|
mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
|
|
46316
46602
|
const tmp = join35(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
|
|
@@ -46751,7 +47037,7 @@ function readTranscript(project, sessionId) {
|
|
|
46751
47037
|
}
|
|
46752
47038
|
function verifyIncrementalParse(path3, identityPrefix, incremental) {
|
|
46753
47039
|
try {
|
|
46754
|
-
const fresh = parseTranscript(
|
|
47040
|
+
const fresh = parseTranscript(readFileSync24(path3, "utf8"), identityPrefix);
|
|
46755
47041
|
const a = JSON.stringify(incremental);
|
|
46756
47042
|
const b = JSON.stringify(fresh);
|
|
46757
47043
|
if (a !== b) {
|
|
@@ -47807,7 +48093,7 @@ function sourceForThread(project, storage, slug, backendFor) {
|
|
|
47807
48093
|
}
|
|
47808
48094
|
if (!FOREIGN_SESSION_ID_RE.test(slug)) return void 0;
|
|
47809
48095
|
const claudePath = resolveTranscriptPath(project, slug);
|
|
47810
|
-
if (
|
|
48096
|
+
if (existsSync13(claudePath)) {
|
|
47811
48097
|
return { slug, sessionId: slug, nativeId: slug, backend: "claude", runtimeGeneration: 0, path: claudePath };
|
|
47812
48098
|
}
|
|
47813
48099
|
const rollout = (backendFor?.("codex") ?? defaultCodexBackend()).transcriptPath(slug);
|
|
@@ -48073,7 +48359,7 @@ function readEarlierThreadTranscriptPage(project, storage, slug, cursor, backend
|
|
|
48073
48359
|
}
|
|
48074
48360
|
function readCodexTranscriptFile(absPath, nativeId = absPath) {
|
|
48075
48361
|
try {
|
|
48076
|
-
return parseCodexTranscript(
|
|
48362
|
+
return parseCodexTranscript(readFileSync24(absPath, "utf8"), `codex:${nativeId}`);
|
|
48077
48363
|
} catch {
|
|
48078
48364
|
return [];
|
|
48079
48365
|
}
|
|
@@ -48171,7 +48457,7 @@ function projectTranscriptPageAgentLifecycles(page, lookup, peerLookup) {
|
|
|
48171
48457
|
function readTranscriptFile(absPath) {
|
|
48172
48458
|
try {
|
|
48173
48459
|
const pathKey = createHash13("sha256").update(absPath).digest("base64url").slice(0, 16);
|
|
48174
|
-
return parseTranscript(
|
|
48460
|
+
return parseTranscript(readFileSync24(absPath, "utf8"), `claude-file:${pathKey}`);
|
|
48175
48461
|
} catch {
|
|
48176
48462
|
return [];
|
|
48177
48463
|
}
|
|
@@ -48260,15 +48546,30 @@ function validateExternalUrl(raw2) {
|
|
|
48260
48546
|
}
|
|
48261
48547
|
return { ok: true, url: parsed.toString() };
|
|
48262
48548
|
}
|
|
48263
|
-
function
|
|
48549
|
+
function externalUrlOpenCommand(url, platform3 = process.platform) {
|
|
48550
|
+
if (platform3 === "darwin") return { command: "open", args: [url] };
|
|
48551
|
+
if (platform3 === "win32") return { command: "rundll32.exe", args: ["url.dll,FileProtocolHandler", url] };
|
|
48552
|
+
return { command: "xdg-open", args: [url] };
|
|
48553
|
+
}
|
|
48554
|
+
async function openExternalUrl(raw2, options = {}) {
|
|
48264
48555
|
const v = validateExternalUrl(raw2);
|
|
48265
48556
|
if (!v.ok) throw new Error(v.reason);
|
|
48266
|
-
const
|
|
48267
|
-
spawn6(
|
|
48557
|
+
const spec = externalUrlOpenCommand(v.url, options.platform);
|
|
48558
|
+
const child = (options.spawn ?? spawn6)(spec.command, [...spec.args], { detached: true, stdio: "ignore", windowsHide: true });
|
|
48559
|
+
try {
|
|
48560
|
+
await awaitOpenerStart(child, spec.command);
|
|
48561
|
+
return { opened: true };
|
|
48562
|
+
} catch (error) {
|
|
48563
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48564
|
+
(options.onError ?? ((line) => log.warn("open-external", line)))(`could not open ${v.url}: ${message}`);
|
|
48565
|
+
return { opened: false, error: message };
|
|
48566
|
+
}
|
|
48268
48567
|
}
|
|
48269
48568
|
var init_open_external = __esm({
|
|
48270
48569
|
"packages/server/src/open-external.ts"() {
|
|
48271
48570
|
"use strict";
|
|
48571
|
+
init_local_file();
|
|
48572
|
+
init_logging();
|
|
48272
48573
|
}
|
|
48273
48574
|
});
|
|
48274
48575
|
|
|
@@ -48597,7 +48898,14 @@ var init_account_actions = __esm({
|
|
|
48597
48898
|
function shellQuote(value) {
|
|
48598
48899
|
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
48599
48900
|
}
|
|
48600
|
-
function
|
|
48901
|
+
function powershellQuote(value) {
|
|
48902
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
48903
|
+
}
|
|
48904
|
+
function providerResumeCommand(backend, projectDir, sessionId, platform3 = process.platform) {
|
|
48905
|
+
if (platform3 === "win32") {
|
|
48906
|
+
const resume2 = backend === "codex" ? `& codex resume ${powershellQuote(sessionId)} --dangerously-bypass-approvals-and-sandbox` : `& claude --resume ${powershellQuote(sessionId)} --dangerously-skip-permissions`;
|
|
48907
|
+
return `Set-Location -LiteralPath ${powershellQuote(projectDir)}; ${resume2}`;
|
|
48908
|
+
}
|
|
48601
48909
|
const resume = backend === "codex" ? `codex resume ${shellQuote(sessionId)} --dangerously-bypass-approvals-and-sandbox` : `claude --resume ${shellQuote(sessionId)} --dangerously-skip-permissions`;
|
|
48602
48910
|
return `cd ${shellQuote(projectDir)} && ${resume}`;
|
|
48603
48911
|
}
|
|
@@ -48945,11 +49253,11 @@ var init_directory_picker = __esm({
|
|
|
48945
49253
|
});
|
|
48946
49254
|
|
|
48947
49255
|
// packages/server/src/router.ts
|
|
48948
|
-
import { readFileSync as
|
|
49256
|
+
import { readFileSync as readFileSync25, statSync as statSync17 } from "node:fs";
|
|
48949
49257
|
import { join as join36, resolve as resolve9 } from "node:path";
|
|
48950
49258
|
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
48951
49259
|
import { basename as basename9, dirname as dirname15 } from "node:path";
|
|
48952
|
-
import { existsSync as
|
|
49260
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync16, rmSync as rmSync10, writeFileSync as writeFileSync13 } from "node:fs";
|
|
48953
49261
|
import { homedir as homedir17 } from "node:os";
|
|
48954
49262
|
function validateGithubDispatchProfile(input) {
|
|
48955
49263
|
validateThreadProfile(input.backend, input.model, input.effort);
|
|
@@ -49159,7 +49467,7 @@ function storeProjectIcon(id, name, bytes) {
|
|
|
49159
49467
|
function setProjectIconFromFile(id, file) {
|
|
49160
49468
|
let bytes;
|
|
49161
49469
|
try {
|
|
49162
|
-
bytes =
|
|
49470
|
+
bytes = readFileSync25(file);
|
|
49163
49471
|
} catch {
|
|
49164
49472
|
throw new Error("That file could not be read.");
|
|
49165
49473
|
}
|
|
@@ -50779,7 +51087,8 @@ Say which way you went and why in your write-up, so the human can course-correct
|
|
|
50779
51087
|
openExternal: mutation({
|
|
50780
51088
|
input: external_exports.object({ url: external_exports.string() }),
|
|
50781
51089
|
handler: async ({ input }) => {
|
|
50782
|
-
openExternalUrl(input.url);
|
|
51090
|
+
const result = await openExternalUrl(input.url);
|
|
51091
|
+
if (!result.opened) throw new Error(result.error);
|
|
50783
51092
|
}
|
|
50784
51093
|
}),
|
|
50785
51094
|
// A local file can be opened only after its canonical real path is contained by the openable roots
|
|
@@ -51187,7 +51496,7 @@ Say which way you went and why in your write-up, so the human can course-correct
|
|
|
51187
51496
|
}
|
|
51188
51497
|
const updated = clearProjectIcon(input.id);
|
|
51189
51498
|
if (!updated) throw new Error("No such project.");
|
|
51190
|
-
return projectCard(updated, !
|
|
51499
|
+
return projectCard(updated, !existsSync14(updated.path));
|
|
51191
51500
|
}
|
|
51192
51501
|
}),
|
|
51193
51502
|
/**
|
|
@@ -51593,7 +51902,7 @@ var init_compression = __esm({
|
|
|
51593
51902
|
});
|
|
51594
51903
|
|
|
51595
51904
|
// packages/server/src/local-image.ts
|
|
51596
|
-
import { readFileSync as
|
|
51905
|
+
import { readFileSync as readFileSync26, realpathSync as realpathSync11, statSync as statSync18 } from "node:fs";
|
|
51597
51906
|
import { extname as extname2, isAbsolute as isAbsolute6 } from "node:path";
|
|
51598
51907
|
function resolveLocalImage(rawPath) {
|
|
51599
51908
|
if (!rawPath || !isAbsolute6(rawPath)) return { status: 400 };
|
|
@@ -51601,13 +51910,13 @@ function resolveLocalImage(rawPath) {
|
|
|
51601
51910
|
if (!contentType) return { status: 400 };
|
|
51602
51911
|
let real;
|
|
51603
51912
|
try {
|
|
51604
|
-
real =
|
|
51913
|
+
real = realpathSync11(rawPath);
|
|
51605
51914
|
} catch {
|
|
51606
51915
|
return { status: 404 };
|
|
51607
51916
|
}
|
|
51608
51917
|
try {
|
|
51609
51918
|
if (!statSync18(real).isFile()) return { status: 404 };
|
|
51610
|
-
return { status: 200, contentType, body:
|
|
51919
|
+
return { status: 200, contentType, body: readFileSync26(real) };
|
|
51611
51920
|
} catch {
|
|
51612
51921
|
return { status: 404 };
|
|
51613
51922
|
}
|
|
@@ -51627,7 +51936,7 @@ var init_local_image = __esm({
|
|
|
51627
51936
|
});
|
|
51628
51937
|
|
|
51629
51938
|
// packages/server/src/project-icon.ts
|
|
51630
|
-
import { readFileSync as
|
|
51939
|
+
import { readFileSync as readFileSync27, readdirSync as readdirSync10, realpathSync as realpathSync12, statSync as statSync19 } from "node:fs";
|
|
51631
51940
|
import { homedir as homedir18 } from "node:os";
|
|
51632
51941
|
import { basename as basename10, dirname as dirname16, extname as extname3, join as join37, relative as relative2, sep as sep3 } from "node:path";
|
|
51633
51942
|
function normalizeStem(stem) {
|
|
@@ -51689,7 +51998,7 @@ function iconDirectories(root) {
|
|
|
51689
51998
|
function manifestIcons(manifestPath) {
|
|
51690
51999
|
let parsed;
|
|
51691
52000
|
try {
|
|
51692
|
-
parsed = JSON.parse(
|
|
52001
|
+
parsed = JSON.parse(readFileSync27(manifestPath, "utf8"));
|
|
51693
52002
|
} catch {
|
|
51694
52003
|
return [];
|
|
51695
52004
|
}
|
|
@@ -51776,21 +52085,21 @@ function resolveProjectIconResponse(id, options = {}) {
|
|
|
51776
52085
|
if (!contentType) return { status: 404 };
|
|
51777
52086
|
let real;
|
|
51778
52087
|
try {
|
|
51779
|
-
real =
|
|
52088
|
+
real = realpathSync12(path3);
|
|
51780
52089
|
if (!statSync19(real).isFile()) return { status: 404 };
|
|
51781
52090
|
} catch {
|
|
51782
52091
|
return { status: 404 };
|
|
51783
52092
|
}
|
|
51784
52093
|
const roots = [entry.path, projectStateDir(id, home)].map((root) => {
|
|
51785
52094
|
try {
|
|
51786
|
-
return
|
|
52095
|
+
return realpathSync12(root);
|
|
51787
52096
|
} catch {
|
|
51788
52097
|
return root;
|
|
51789
52098
|
}
|
|
51790
52099
|
});
|
|
51791
52100
|
if (!roots.some((root) => real === root || real.startsWith(root + sep3))) return { status: 404 };
|
|
51792
52101
|
try {
|
|
51793
|
-
return { status: 200, contentType, body:
|
|
52102
|
+
return { status: 200, contentType, body: readFileSync27(real) };
|
|
51794
52103
|
} catch {
|
|
51795
52104
|
return { status: 404 };
|
|
51796
52105
|
}
|
|
@@ -51902,7 +52211,7 @@ var init_project_icon = __esm({
|
|
|
51902
52211
|
});
|
|
51903
52212
|
|
|
51904
52213
|
// packages/server/src/local-visualization.ts
|
|
51905
|
-
import { readdirSync as readdirSync11, readFileSync as
|
|
52214
|
+
import { readdirSync as readdirSync11, readFileSync as readFileSync28, realpathSync as realpathSync13, statSync as statSync20 } from "node:fs";
|
|
51906
52215
|
import { join as join38, sep as sep4 } from "node:path";
|
|
51907
52216
|
function children(path3, pattern) {
|
|
51908
52217
|
try {
|
|
@@ -51919,8 +52228,8 @@ function resolveFragment(projectDir, sessionId, file) {
|
|
|
51919
52228
|
let projectReal;
|
|
51920
52229
|
let baseReal;
|
|
51921
52230
|
try {
|
|
51922
|
-
projectReal =
|
|
51923
|
-
baseReal =
|
|
52231
|
+
projectReal = realpathSync13(projectDir);
|
|
52232
|
+
baseReal = realpathSync13(base);
|
|
51924
52233
|
} catch {
|
|
51925
52234
|
return null;
|
|
51926
52235
|
}
|
|
@@ -51933,7 +52242,7 @@ function resolveFragment(projectDir, sessionId, file) {
|
|
|
51933
52242
|
const sessionRoot = join38(baseReal, year, month, day, sessionId);
|
|
51934
52243
|
const candidate = join38(monthDir, day, sessionId, file);
|
|
51935
52244
|
try {
|
|
51936
|
-
const real =
|
|
52245
|
+
const real = realpathSync13(candidate);
|
|
51937
52246
|
if (isUnder2(real, sessionRoot) && statSync20(real).isFile()) return real;
|
|
51938
52247
|
} catch {
|
|
51939
52248
|
}
|
|
@@ -52017,7 +52326,7 @@ function resolveLocalVisualization(projectDir, sessionId, file) {
|
|
|
52017
52326
|
try {
|
|
52018
52327
|
const size = statSync20(path3).size;
|
|
52019
52328
|
if (size > MAX_FRAGMENT_BYTES) return { status: 413 };
|
|
52020
|
-
fragment =
|
|
52329
|
+
fragment = readFileSync28(path3, "utf8");
|
|
52021
52330
|
} catch {
|
|
52022
52331
|
return { status: 404 };
|
|
52023
52332
|
}
|
|
@@ -53539,7 +53848,7 @@ var init_local_file_watch = __esm({
|
|
|
53539
53848
|
});
|
|
53540
53849
|
|
|
53541
53850
|
// packages/server/src/boot-progress.ts
|
|
53542
|
-
import { mkdirSync as mkdirSync18, readFileSync as
|
|
53851
|
+
import { mkdirSync as mkdirSync18, readFileSync as readFileSync29, renameSync as renameSync10, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
|
|
53543
53852
|
import { basename as basename12, dirname as dirname18, join as join40 } from "node:path";
|
|
53544
53853
|
function bootProgressPath(stateDir) {
|
|
53545
53854
|
return join40(stateDir, BOOT_PROGRESS_NAME);
|
|
@@ -53588,12 +53897,12 @@ var init_boot_progress = __esm({
|
|
|
53588
53897
|
});
|
|
53589
53898
|
|
|
53590
53899
|
// packages/server/src/runtime-lease.ts
|
|
53591
|
-
import { existsSync as
|
|
53900
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync19, readdirSync as readdirSync12, readFileSync as readFileSync30, unlinkSync as unlinkSync6, writeFileSync as writeFileSync16 } from "node:fs";
|
|
53592
53901
|
import { dirname as dirname19, join as join41 } from "node:path";
|
|
53593
53902
|
function runtimeVersionDir(bin) {
|
|
53594
53903
|
let dir = dirname19(bin);
|
|
53595
53904
|
for (let step = 0; step < MAX_ASCENT; step++) {
|
|
53596
|
-
if (
|
|
53905
|
+
if (existsSync15(join41(dir, PROVISIONED_MARKER))) return dir;
|
|
53597
53906
|
const parent = dirname19(dir);
|
|
53598
53907
|
if (parent === dir) return void 0;
|
|
53599
53908
|
dir = parent;
|
|
@@ -53642,7 +53951,7 @@ function liveRuntimeLeases(versionDir) {
|
|
|
53642
53951
|
const path3 = join41(dir, name);
|
|
53643
53952
|
let lease;
|
|
53644
53953
|
try {
|
|
53645
|
-
lease = JSON.parse(
|
|
53954
|
+
lease = JSON.parse(readFileSync30(path3, "utf8"));
|
|
53646
53955
|
} catch {
|
|
53647
53956
|
continue;
|
|
53648
53957
|
}
|
|
@@ -53673,10 +53982,10 @@ import { createHash as createHash15 } from "node:crypto";
|
|
|
53673
53982
|
import {
|
|
53674
53983
|
chmodSync,
|
|
53675
53984
|
closeSync as closeSync16,
|
|
53676
|
-
existsSync as
|
|
53985
|
+
existsSync as existsSync16,
|
|
53677
53986
|
mkdirSync as mkdirSync20,
|
|
53678
53987
|
openSync as openSync17,
|
|
53679
|
-
readFileSync as
|
|
53988
|
+
readFileSync as readFileSync31,
|
|
53680
53989
|
readdirSync as readdirSync13,
|
|
53681
53990
|
renameSync as renameSync11,
|
|
53682
53991
|
rmSync as rmSync12,
|
|
@@ -53711,13 +54020,13 @@ function provisionedBinary(backend, root, label) {
|
|
|
53711
54020
|
const dir = join42(root, backend, label);
|
|
53712
54021
|
let marker;
|
|
53713
54022
|
try {
|
|
53714
|
-
marker = JSON.parse(
|
|
54023
|
+
marker = JSON.parse(readFileSync31(join42(dir, MARKER), "utf8"));
|
|
53715
54024
|
} catch {
|
|
53716
54025
|
return void 0;
|
|
53717
54026
|
}
|
|
53718
54027
|
if (typeof marker.binary !== "string" || marker.label !== label) return void 0;
|
|
53719
54028
|
const bin = join42(dir, marker.binary);
|
|
53720
|
-
return
|
|
54029
|
+
return existsSync16(bin) ? bin : void 0;
|
|
53721
54030
|
}
|
|
53722
54031
|
function octal(buffer, offset, length) {
|
|
53723
54032
|
const text2 = buffer.toString("latin1", offset, offset + length).replace(/\0.*$/su, "").trim();
|
|
@@ -53739,7 +54048,7 @@ function safeEntryPath(dest, name) {
|
|
|
53739
54048
|
function locateBinary(dir, backend, binary) {
|
|
53740
54049
|
if (backend === "claude") {
|
|
53741
54050
|
const direct = join42(dir, binary);
|
|
53742
|
-
return
|
|
54051
|
+
return existsSync16(direct) ? direct : void 0;
|
|
53743
54052
|
}
|
|
53744
54053
|
const vendor = join42(dir, "vendor");
|
|
53745
54054
|
let triples;
|
|
@@ -53750,7 +54059,7 @@ function locateBinary(dir, backend, binary) {
|
|
|
53750
54059
|
}
|
|
53751
54060
|
for (const triple of triples) {
|
|
53752
54061
|
const candidate = join42(vendor, triple, "bin", binary);
|
|
53753
|
-
if (
|
|
54062
|
+
if (existsSync16(candidate)) return candidate;
|
|
53754
54063
|
}
|
|
53755
54064
|
return void 0;
|
|
53756
54065
|
}
|
|
@@ -53816,8 +54125,17 @@ async function provisionRuntime(backend, options) {
|
|
|
53816
54125
|
};
|
|
53817
54126
|
writeFileSync17(join42(partial, MARKER), `${JSON.stringify(marker, null, 2)}
|
|
53818
54127
|
`, "utf8");
|
|
54128
|
+
const rename2 = options.rename ?? renameSync11;
|
|
53819
54129
|
try {
|
|
53820
|
-
|
|
54130
|
+
for (let attempt = 1; ; attempt++) {
|
|
54131
|
+
try {
|
|
54132
|
+
rename2(partial, finalDir);
|
|
54133
|
+
break;
|
|
54134
|
+
} catch (err) {
|
|
54135
|
+
if (attempt >= RENAME_ATTEMPTS || !TRANSIENT_HANDLE_CODES.has(errorCode4(err) ?? "") || existsSync16(finalDir)) throw err;
|
|
54136
|
+
await new Promise((resolve12) => setTimeout(resolve12, options.retryDelayMs ?? RENAME_RETRY_DELAY_MS));
|
|
54137
|
+
}
|
|
54138
|
+
}
|
|
53821
54139
|
} catch (err) {
|
|
53822
54140
|
const theirs = provisionedBinary(backend, root, coordinates.label);
|
|
53823
54141
|
if (!theirs) throw err;
|
|
@@ -53831,7 +54149,8 @@ async function provisionRuntime(backend, options) {
|
|
|
53831
54149
|
throw err;
|
|
53832
54150
|
}
|
|
53833
54151
|
}
|
|
53834
|
-
function sweepRuntimes(backend, root, keepLabel, now = Date.now()) {
|
|
54152
|
+
function sweepRuntimes(backend, root, keepLabel, now = Date.now(), deps = {}) {
|
|
54153
|
+
const rm2 = deps.rm ?? defaultSweepRm;
|
|
53835
54154
|
const backendDir = join42(root, backend);
|
|
53836
54155
|
let entries2;
|
|
53837
54156
|
try {
|
|
@@ -53856,8 +54175,14 @@ function sweepRuntimes(backend, root, keepLabel, now = Date.now()) {
|
|
|
53856
54175
|
continue;
|
|
53857
54176
|
}
|
|
53858
54177
|
}
|
|
53859
|
-
|
|
53860
|
-
|
|
54178
|
+
try {
|
|
54179
|
+
rm2(full);
|
|
54180
|
+
sweep.removed.push(full);
|
|
54181
|
+
} catch (err) {
|
|
54182
|
+
const code = errorCode4(err);
|
|
54183
|
+
const reason = code && TRANSIENT_HANDLE_CODES.has(code) ? `in use (${code})` : `could not remove: ${err instanceof Error ? err.message : String(err)}`;
|
|
54184
|
+
sweep.kept.push({ dir: full, leases: [], reason });
|
|
54185
|
+
}
|
|
53861
54186
|
}
|
|
53862
54187
|
return sweep;
|
|
53863
54188
|
}
|
|
@@ -53876,27 +54201,34 @@ async function resolveOne(backend, explicit, options) {
|
|
|
53876
54201
|
return { bin: bare, source: "path", version: "unknown", note };
|
|
53877
54202
|
}
|
|
53878
54203
|
const root = options.root ?? runtimesRoot(env);
|
|
54204
|
+
let provisioned;
|
|
53879
54205
|
try {
|
|
53880
|
-
|
|
54206
|
+
provisioned = await provisionRuntime(backend, {
|
|
53881
54207
|
root,
|
|
53882
54208
|
coordinates,
|
|
53883
54209
|
fetch: options.fetch,
|
|
53884
54210
|
registry: options.registry,
|
|
54211
|
+
rename: options.rename,
|
|
53885
54212
|
onProgress: (message) => options.onProgress?.(backend, message)
|
|
53886
54213
|
});
|
|
53887
|
-
if (provisioned.fetched) log3("info", `runtimes: provisioned ${backend} ${provisioned.label} at ${provisioned.bin}`);
|
|
53888
|
-
leaseRuntime(provisioned.bin, "server");
|
|
53889
|
-
const sweep = sweepRuntimes(backend, root, coordinates.label);
|
|
53890
|
-
for (const retired of sweep.removed) log3("info", `runtimes: retired ${retired}`);
|
|
53891
|
-
for (const { dir, leases } of sweep.kept) {
|
|
53892
|
-
log3("info", `runtimes: kept ${dir} \u2014 still used by ${leases.map((lease) => `${lease.role} pid ${lease.pid}`).join(", ")}`);
|
|
53893
|
-
}
|
|
53894
|
-
return { bin: provisioned.bin, source: "provisioned", version: provisioned.label };
|
|
53895
54214
|
} catch (err) {
|
|
53896
54215
|
const note = err instanceof Error ? err.message : String(err);
|
|
53897
54216
|
log3("warn", `runtimes: could not provision ${backend} ${coordinates.label} (${note}); using ${bare} from PATH \u2014 the version Frizz was built against is not what will run`);
|
|
53898
54217
|
return { bin: bare, source: "path", version: "unknown", note };
|
|
53899
54218
|
}
|
|
54219
|
+
if (provisioned.fetched) log3("info", `runtimes: provisioned ${backend} ${provisioned.label} at ${provisioned.bin}`);
|
|
54220
|
+
leaseRuntime(provisioned.bin, "server");
|
|
54221
|
+
try {
|
|
54222
|
+
const sweep = sweepRuntimes(backend, root, coordinates.label, Date.now(), { rm: options.rm });
|
|
54223
|
+
for (const retired of sweep.removed) log3("info", `runtimes: retired ${retired}`);
|
|
54224
|
+
for (const { dir, leases, reason } of sweep.kept) {
|
|
54225
|
+
const why = reason ?? `still used by ${leases.map((lease) => `${lease.role} pid ${lease.pid}`).join(", ")}`;
|
|
54226
|
+
log3("info", `runtimes: kept ${dir} \u2014 ${why}`);
|
|
54227
|
+
}
|
|
54228
|
+
} catch (err) {
|
|
54229
|
+
log3("warn", `runtimes: sweep of superseded ${backend} versions failed (${err instanceof Error ? err.message : String(err)}); the pin is unaffected`);
|
|
54230
|
+
}
|
|
54231
|
+
return { bin: provisioned.bin, source: "provisioned", version: provisioned.label };
|
|
53900
54232
|
}
|
|
53901
54233
|
async function resolveRuntimes(options = {}) {
|
|
53902
54234
|
const [claude, codex] = await Promise.all([
|
|
@@ -53915,7 +54247,7 @@ function describeRuntime(backend, runtime) {
|
|
|
53915
54247
|
return `${backend}: ${runtime.bin} from PATH${runtime.note ? ` \u2014 ${runtime.note}` : ""}`;
|
|
53916
54248
|
}
|
|
53917
54249
|
}
|
|
53918
|
-
var CLAUDE_AGENT_SDK_VERSION, CLAUDE_CODE_VERSION, CODEX_VERSION, RUNTIMES_MODE_ENV, RUNTIMES_DIR_ENV, CLAUDE_BIN_ENV, CODEX_BIN_ENV, REGISTRY, MARKER, BLOCK, TarExtractor;
|
|
54250
|
+
var CLAUDE_AGENT_SDK_VERSION, CLAUDE_CODE_VERSION, CODEX_VERSION, RUNTIMES_MODE_ENV, RUNTIMES_DIR_ENV, CLAUDE_BIN_ENV, CODEX_BIN_ENV, REGISTRY, MARKER, BLOCK, TarExtractor, RENAME_ATTEMPTS, RENAME_RETRY_DELAY_MS, TRANSIENT_HANDLE_CODES, errorCode4, defaultSweepRm;
|
|
53919
54251
|
var init_runtimes = __esm({
|
|
53920
54252
|
"packages/server/src/runtimes.ts"() {
|
|
53921
54253
|
"use strict";
|
|
@@ -54060,6 +54392,11 @@ var init_runtimes = __esm({
|
|
|
54060
54392
|
}
|
|
54061
54393
|
}
|
|
54062
54394
|
};
|
|
54395
|
+
RENAME_ATTEMPTS = 5;
|
|
54396
|
+
RENAME_RETRY_DELAY_MS = 200;
|
|
54397
|
+
TRANSIENT_HANDLE_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
54398
|
+
errorCode4 = (err) => err?.code;
|
|
54399
|
+
defaultSweepRm = (path3) => rmSync12(path3, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
54063
54400
|
}
|
|
54064
54401
|
});
|
|
54065
54402
|
|
|
@@ -54592,7 +54929,7 @@ var init_legacy_project_db = __esm({
|
|
|
54592
54929
|
});
|
|
54593
54930
|
|
|
54594
54931
|
// packages/server/src/frizz-db.ts
|
|
54595
|
-
import { existsSync as
|
|
54932
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync21, readdirSync as readdirSync14, renameSync as renameSync12, statSync as statSync23 } from "node:fs";
|
|
54596
54933
|
import { basename as basename13, dirname as dirname21, join as join43 } from "node:path";
|
|
54597
54934
|
function frizzDatabaseLocation(options) {
|
|
54598
54935
|
if (options.stateDir !== void 0) {
|
|
@@ -54677,7 +55014,7 @@ function openFrizzDatabase(options = {}) {
|
|
|
54677
55014
|
};
|
|
54678
55015
|
}
|
|
54679
55016
|
function projectIdsWithFile(projectsDir, file) {
|
|
54680
|
-
if (!
|
|
55017
|
+
if (!existsSync17(projectsDir)) return [];
|
|
54681
55018
|
return readdirSync14(projectsDir).filter((name) => {
|
|
54682
55019
|
try {
|
|
54683
55020
|
return statSync23(join43(projectsDir, name, file)).isFile();
|
|
@@ -54690,7 +55027,7 @@ function legacyProjectIds(projectsDir) {
|
|
|
54690
55027
|
return projectIdsWithFile(projectsDir, "ui.db");
|
|
54691
55028
|
}
|
|
54692
55029
|
function retiredLegacyProjectIds(projectsDir) {
|
|
54693
|
-
return projectIdsWithFile(projectsDir, "ui.db.imported").filter((id) => !
|
|
55030
|
+
return projectIdsWithFile(projectsDir, "ui.db.imported").filter((id) => !existsSync17(join43(projectsDir, id, "ui.db")));
|
|
54694
55031
|
}
|
|
54695
55032
|
function tableColumns(db, schema, table) {
|
|
54696
55033
|
return db.prepare(`PRAGMA ${schema}.table_info(${table})`).all().map((c) => c.name);
|
|
@@ -54895,11 +55232,11 @@ __export(index_exports, {
|
|
|
54895
55232
|
unknownProjectPage: () => unknownProjectPage
|
|
54896
55233
|
});
|
|
54897
55234
|
import { createServer } from "node:http";
|
|
54898
|
-
import { readFileSync as
|
|
54899
|
-
import { dirname as dirname23, join as join44, resolve as resolve11, extname as extname4,
|
|
55235
|
+
import { readFileSync as readFileSync32, existsSync as existsSync18, statSync as statSync24 } from "node:fs";
|
|
55236
|
+
import { dirname as dirname23, join as join44, resolve as resolve11, extname as extname4, posix as posix2, sep as sep6 } from "node:path";
|
|
54900
55237
|
function readServerAddressHolder(path3) {
|
|
54901
55238
|
try {
|
|
54902
|
-
const value = JSON.parse(
|
|
55239
|
+
const value = JSON.parse(readFileSync32(path3, "utf8"));
|
|
54903
55240
|
if (typeof value?.pid !== "number") return void 0;
|
|
54904
55241
|
return { pid: value.pid, port: typeof value.port === "number" ? value.port : -1 };
|
|
54905
55242
|
} catch {
|
|
@@ -55023,10 +55360,10 @@ function staticIsFresh(req, etag, mtimeMs) {
|
|
|
55023
55360
|
return Number.isFinite(since) && Math.floor(mtimeMs / 1e3) <= Math.floor(since / 1e3);
|
|
55024
55361
|
}
|
|
55025
55362
|
function serveStatic(distDir, req, res) {
|
|
55026
|
-
const rel =
|
|
55363
|
+
const rel = posix2.normalize((req.url ?? "/").split("?")[0].replace(/\\/g, "/")).replace(/^(\.\.[/\\])+/, "");
|
|
55027
55364
|
let file = join44(distDir, rel === "/" ? "index.html" : rel);
|
|
55028
|
-
if (!file.startsWith(distDir)) file = join44(distDir, "index.html");
|
|
55029
|
-
if (!
|
|
55365
|
+
if (!file.startsWith(distDir + sep6)) file = join44(distDir, "index.html");
|
|
55366
|
+
if (!existsSync18(file)) file = join44(distDir, "index.html");
|
|
55030
55367
|
try {
|
|
55031
55368
|
const stats = statSync24(file);
|
|
55032
55369
|
const cacheControl = file.startsWith(join44(distDir, "assets") + sep6) ? STATIC_CACHE_IMMUTABLE : STATIC_CACHE_REVALIDATE;
|
|
@@ -55045,7 +55382,7 @@ function serveStatic(distDir, req, res) {
|
|
|
55045
55382
|
res.end();
|
|
55046
55383
|
return;
|
|
55047
55384
|
}
|
|
55048
|
-
let body =
|
|
55385
|
+
let body = readFileSync32(file);
|
|
55049
55386
|
const accept = req.headers["accept-encoding"];
|
|
55050
55387
|
const encoding = negotiateEncoding(Array.isArray(accept) ? accept.join(",") : accept);
|
|
55051
55388
|
if (encoding && shouldCompress(new Headers({ "content-type": headers["content-type"] }), body.byteLength)) {
|
|
@@ -55521,7 +55858,7 @@ async function startServer(opts = {}) {
|
|
|
55521
55858
|
if (vite) {
|
|
55522
55859
|
vite.middlewares(req, res, () => {
|
|
55523
55860
|
try {
|
|
55524
|
-
const html =
|
|
55861
|
+
const html = readFileSync32(join44(webRoot, "index.html"), "utf8");
|
|
55525
55862
|
void vite.transformIndexHtml(url, html).then((out) => {
|
|
55526
55863
|
res.writeHead(200, { "content-type": "text/html" });
|
|
55527
55864
|
res.end(out);
|
|
@@ -55533,7 +55870,7 @@ async function startServer(opts = {}) {
|
|
|
55533
55870
|
});
|
|
55534
55871
|
return;
|
|
55535
55872
|
}
|
|
55536
|
-
if (
|
|
55873
|
+
if (existsSync18(distDir)) {
|
|
55537
55874
|
serveStatic(distDir, req, res);
|
|
55538
55875
|
return;
|
|
55539
55876
|
}
|
|
@@ -55754,7 +56091,7 @@ init_project();
|
|
|
55754
56091
|
init_project_launch();
|
|
55755
56092
|
init_shutdown();
|
|
55756
56093
|
init_logging();
|
|
55757
|
-
import { existsSync as
|
|
56094
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
55758
56095
|
import { join as join45 } from "node:path";
|
|
55759
56096
|
process.on("uncaughtException", (error) => {
|
|
55760
56097
|
log.error("dev-child", `uncaught exception: ${error instanceof Error ? error.stack ?? error.message : error}`);
|
|
@@ -55777,6 +56114,15 @@ function notifySupervisor(message) {
|
|
|
55777
56114
|
} catch {
|
|
55778
56115
|
}
|
|
55779
56116
|
}
|
|
56117
|
+
var shutdown;
|
|
56118
|
+
process.once("disconnect", () => {
|
|
56119
|
+
if (shutdown) {
|
|
56120
|
+
void shutdown();
|
|
56121
|
+
return;
|
|
56122
|
+
}
|
|
56123
|
+
log.info("dev-child", "supervisor disconnected before the server was up; exiting");
|
|
56124
|
+
process.exit(0);
|
|
56125
|
+
});
|
|
55780
56126
|
try {
|
|
55781
56127
|
const target = projectLaunchTargetFromEnvironment(process.env);
|
|
55782
56128
|
const launchOwnerToken = projectLaunchOwnerTokenFromEnvironment(process.env);
|
|
@@ -55793,9 +56139,9 @@ try {
|
|
|
55793
56139
|
["FRIZZ_SCRIPTS_DIR", process.env.FRIZZ_SCRIPTS_DIR, "index.mjs"],
|
|
55794
56140
|
["FRIZZ_WORKER_PLUGIN_DIR", process.env.FRIZZ_WORKER_PLUGIN_DIR, ".claude-plugin/plugin.json"]
|
|
55795
56141
|
];
|
|
55796
|
-
if (!
|
|
56142
|
+
if (!existsSync19(stableWebDist)) throw new Error("stable artifact launch is missing its verified web directory");
|
|
55797
56143
|
for (const [name, directory, requiredFile] of required) {
|
|
55798
|
-
if (!directory || !
|
|
56144
|
+
if (!directory || !existsSync19(join45(directory, requiredFile)))
|
|
55799
56145
|
throw new Error(`stable artifact launch is missing verified ${name}`);
|
|
55800
56146
|
}
|
|
55801
56147
|
}
|
|
@@ -55816,7 +56162,7 @@ try {
|
|
|
55816
56162
|
bootId: server.ctx.bootId
|
|
55817
56163
|
});
|
|
55818
56164
|
let shuttingDown = false;
|
|
55819
|
-
const
|
|
56165
|
+
const stop = async () => {
|
|
55820
56166
|
if (shuttingDown) return;
|
|
55821
56167
|
shuttingDown = true;
|
|
55822
56168
|
const force = setTimeout(() => process.exit(1), 15e3);
|
|
@@ -55839,9 +56185,9 @@ try {
|
|
|
55839
56185
|
process.exit(1);
|
|
55840
56186
|
}
|
|
55841
56187
|
};
|
|
55842
|
-
|
|
55843
|
-
process.on("
|
|
55844
|
-
process.
|
|
56188
|
+
shutdown = stop;
|
|
56189
|
+
process.on("SIGINT", () => void stop());
|
|
56190
|
+
process.on("SIGTERM", () => void stop());
|
|
55845
56191
|
} catch (err) {
|
|
55846
56192
|
log.error("dev-child", `failed to start: ${err instanceof Error ? err.stack ?? err.message : err}`);
|
|
55847
56193
|
process.exit(1);
|