frizz 0.13.1 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/frizz.js +100 -31
  2. package/package.json +2 -2
package/dist/frizz.js CHANGED
@@ -180,10 +180,21 @@ var init_local_origin = __esm({
180
180
  }
181
181
  });
182
182
 
183
+ // packages/server/src/local-path.ts
184
+ function normalizeLocalPath(path, platform = process.platform) {
185
+ return platform === "win32" ? path.replace(/^\/([a-zA-Z]:[\\/])/, "$1") : path;
186
+ }
187
+ var init_local_path = __esm({
188
+ "packages/server/src/local-path.ts"() {
189
+ "use strict";
190
+ }
191
+ });
192
+
183
193
  // packages/server/src/local-image.ts
184
194
  import { readFileSync as readFileSync2, realpathSync, statSync } from "node:fs";
185
195
  import { extname, isAbsolute } from "node:path";
186
196
  function resolveLocalImage(rawPath) {
197
+ if (rawPath) rawPath = normalizeLocalPath(rawPath);
187
198
  if (!rawPath || !isAbsolute(rawPath)) return { status: 400 };
188
199
  const contentType = IMAGE_CONTENT_TYPE[extname(rawPath).toLowerCase()];
189
200
  if (!contentType) return { status: 400 };
@@ -204,6 +215,7 @@ var IMAGE_CONTENT_TYPE;
204
215
  var init_local_image = __esm({
205
216
  "packages/server/src/local-image.ts"() {
206
217
  "use strict";
218
+ init_local_path();
207
219
  IMAGE_CONTENT_TYPE = {
208
220
  ".png": "image/png",
209
221
  ".jpg": "image/jpeg",
@@ -12322,6 +12334,13 @@ var init_claim = __esm({
12322
12334
  }
12323
12335
  });
12324
12336
 
12337
+ // packages/shared/src/claude-editions.ts
12338
+ var init_claude_editions = __esm({
12339
+ "packages/shared/src/claude-editions.ts"() {
12340
+ "use strict";
12341
+ }
12342
+ });
12343
+
12325
12344
  // packages/shared/src/code-fences.ts
12326
12345
  var init_code_fences = __esm({
12327
12346
  "packages/shared/src/code-fences.ts"() {
@@ -12474,7 +12493,7 @@ function requireEffortOutsideAcp(profile, ctx) {
12474
12493
  function fallbackPort(base) {
12475
12494
  return base + 1e4;
12476
12495
  }
12477
- 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, AcpAgent, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, AskQuestionKind, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, 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, HUMAN_GAP_FLOOR_MS, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, 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_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
12496
+ 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, ClaudeModel, AcpAgent, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, AskQuestionKind, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, 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, UpgradeThreadModelInput, UpgradeThreadModelResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, HUMAN_GAP_FLOOR_MS, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, 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, ProjectRailCounts, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
12478
12497
  var init_src = __esm({
12479
12498
  "packages/shared/src/index.ts"() {
12480
12499
  "use strict";
@@ -12483,6 +12502,7 @@ var init_src = __esm({
12483
12502
  init_interactions();
12484
12503
  init_thread_slug();
12485
12504
  init_claim();
12505
+ init_claude_editions();
12486
12506
  init_code_fences();
12487
12507
  init_delta();
12488
12508
  init_drainable_worker();
@@ -12607,6 +12627,14 @@ var init_src = __esm({
12607
12627
  contextWindow: external_exports.number().int().positive().optional(),
12608
12628
  maxContextWindow: external_exports.number().int().positive().optional()
12609
12629
  });
12630
+ ClaudeModel = external_exports.object({
12631
+ alias: external_exports.string(),
12632
+ label: external_exports.string(),
12633
+ resolvedModel: external_exports.string().optional(),
12634
+ // The edition alone ("5.5") — the tail of `label`, which the picker sets dimmer than the family word.
12635
+ // Absent exactly when `resolvedModel` is.
12636
+ edition: external_exports.string().optional()
12637
+ });
12610
12638
  AcpAgent = external_exports.object({
12611
12639
  id: external_exports.string(),
12612
12640
  label: external_exports.string(),
@@ -12627,6 +12655,8 @@ var init_src = __esm({
12627
12655
  ThreadProfileOption = external_exports.object({
12628
12656
  model: external_exports.string().min(1),
12629
12657
  label: external_exports.string().min(1),
12658
+ // The edition at the end of `label` ("5.5" of "Opus 5.5"), for a Claude row the pinned runtime resolved.
12659
+ edition: external_exports.string().optional(),
12630
12660
  defaultEffort: external_exports.string().min(1),
12631
12661
  efforts: external_exports.array(external_exports.string().min(1)).min(1)
12632
12662
  });
@@ -13499,6 +13529,16 @@ var init_src = __esm({
13499
13529
  // values forward-compatible; absent when neither durable source knows → the UI renders no guess.
13500
13530
  model: external_exports.string().optional(),
13501
13531
  effort: external_exports.string().optional(),
13532
+ // The Claude EDITION this thread's worker last ran ("Opus 5"), read off the model id on its own
13533
+ // transcript. It can be older than the edition the picker's row names ("Opus 5.5"): a worker keeps the
13534
+ // runtime it was started on, and a new pin only reaches the workers started after it. Emitted only when
13535
+ // that id belongs to the family `model` names, so a pending model switch never shows the old family's.
13536
+ runningModelLabel: external_exports.string().optional(),
13537
+ // Present when the pinned runtime now resolves this thread's family to a NEWER edition than it runs.
13538
+ // `staged`: no live worker holds the old edition, so the next turn starts on `label` with nothing
13539
+ // further to do; otherwise the composer offers the upgrade, and the first message after the thread's
13540
+ // next compaction takes it on its own.
13541
+ modelUpgrade: external_exports.object({ label: external_exports.string(), staged: external_exports.boolean() }).optional(),
13502
13542
  // How full the session's context window is right now — the footer's fullness readout. BOTH halves
13503
13543
  // are provider-measured and the field is emitted ONLY when both are present, so a client never has
13504
13544
  // to decide what to do with half a fraction: absent ⇒ no reading, never a 0% dial. Codex reports
@@ -13631,13 +13671,13 @@ var init_src = __esm({
13631
13671
  * to, not furniture you sit beside. Hidden, the way back is a breadcrumb in the status bar, which
13632
13672
  * costs a click exactly when you meant to switch and nothing when you did not.
13633
13673
  *
13634
- * Machine-level, like the font: which chrome you want is a property of the person, not the repo.
13674
+ * Machine-level: which chrome you want is a property of the person, not the repo.
13635
13675
  */
13636
13676
  projectRail: external_exports.boolean(),
13637
- // UI type family. `mono` (default) is the mono-forward system; `sans` swaps prose/UI chrome to a
13638
- // sans stack while code / tool lines / the terminal stay mono. Optional so an old settings blob
13639
- // parses; defaultSettings pins "mono".
13640
- font: external_exports.enum(["mono", "sans"]).optional(),
13677
+ // There is no `font` key any more. The interface rendered in one of two type families as a machine
13678
+ // setting until 2026-09-19 (maintainer: "let's drop monospace as an option"); every surface is sans
13679
+ // now, and index.html pins `data-font="sans"` on <html> directly. Settings is a non-strict object,
13680
+ // so a stored `font` is stripped the moment an old blob parses — no migration.
13641
13681
  // Default action for a vetted non-image local path in agent markdown. Image clicks always use the
13642
13682
  // OS default viewer so screenshots retain their expected behavior.
13643
13683
  localFileOpener: LocalFileOpener.optional(),
@@ -13778,8 +13818,9 @@ var init_src = __esm({
13778
13818
  freshProcess: external_exports.boolean().optional(),
13779
13819
  // PREEMPT the operation the worker is running right now, so this message is read at once instead of
13780
13820
  // when that operation finishes. The operator's "Interrupt and send" verb, and opt-in for the same
13781
- // reason `freshProcess` is: it costs the in-flight tool call's result and the worker's in-memory
13782
- // sub-agents.
13821
+ // reason `freshProcess` is: it costs the in-flight tool call's result. (It no longer costs the
13822
+ // worker's sub-agents: since 2026-09-24 the broker's SDK query declares `perTaskStopAffordance`,
13823
+ // so the interrupt aborts only the turn and background agents run on.)
13783
13824
  //
13784
13825
  // It exists because delivery is ALREADY as fast as queueing can be. Measured over 14 days of this
13785
13826
  // project's own transcripts, Claude Code drains its queue at the first sampling boundary that
@@ -13986,11 +14027,15 @@ var init_src = __esm({
13986
14027
  SetThreadProfileInput = external_exports.object({
13987
14028
  slug: ThreadSlug,
13988
14029
  model: external_exports.string().trim().min(1).max(200),
13989
- effort: external_exports.string().trim().min(1).max(100)
14030
+ // Absent ONLY for an ACP thread, whose model slug (`acp:<agent>@<model>`) carries no effort axis; the
14031
+ // router refuses a Claude/Codex profile without one rather than storing "".
14032
+ effort: external_exports.string().trim().min(1).max(100).optional()
13990
14033
  }).strict();
13991
14034
  SetThreadProfileResult = external_exports.object({
13992
14035
  effect: external_exports.enum(["applied", "next-resume"])
13993
14036
  });
14037
+ UpgradeThreadModelInput = external_exports.object({ slug: ThreadSlug, sessionId: external_exports.string().min(1) }).strict();
14038
+ UpgradeThreadModelResult = external_exports.object({ effect: external_exports.literal("next-turn"), label: external_exports.string() });
13994
14039
  DISPATCH_TASK_BANNER = [
13995
14040
  "===============================================================",
13996
14041
  "====================== YOUR TASK ========================",
@@ -14566,6 +14611,10 @@ ${DISPATCH_TASK_BANNER}
14566
14611
  /** An operator's uploaded icon, rather than one the scan found. Drives what the menu offers. */
14567
14612
  iconIsCustom: external_exports.boolean().optional()
14568
14613
  });
14614
+ ProjectRailCounts = external_exports.object({
14615
+ queued: external_exports.number().int().nonnegative(),
14616
+ running: external_exports.number().int().nonnegative()
14617
+ });
14569
14618
  PROJECT_ICON_MAX_BASE64_CHARS = 4 * 1024 * 1024;
14570
14619
  DirectoryPickResult = external_exports.discriminatedUnion("kind", [
14571
14620
  external_exports.object({ kind: external_exports.literal("picked"), project: ProjectCard }),
@@ -22224,12 +22273,12 @@ var qrcode_default = qrcode;
22224
22273
  var stringToBytes = qrcode.stringToBytes;
22225
22274
 
22226
22275
  // packages/server/src/qr.ts
22227
- var LIGHT = "\x1B[38;5;15m";
22228
22276
  var LIGHT_BG = "\x1B[48;5;15m";
22229
22277
  var DARK = "\x1B[38;5;0m";
22230
22278
  var DARK_BG = "\x1B[48;5;0m";
22231
22279
  var RESET = "\x1B[0m";
22232
22280
  var UPPER_HALF = "\u2580";
22281
+ var LOWER_HALF = "\u2584";
22233
22282
  var QUIET_ZONE = 4;
22234
22283
  function encode(value, errorCorrection) {
22235
22284
  const code = qrcode_default(0, errorCorrection);
@@ -22260,7 +22309,8 @@ function renderQrLines(value, options2 = {}) {
22260
22309
  line += top && bottom ? "#" : top ? "^" : bottom ? "v" : " ";
22261
22310
  continue;
22262
22311
  }
22263
- line += `${top ? DARK : LIGHT}${bottom ? DARK_BG : LIGHT_BG}${UPPER_HALF}`;
22312
+ if (top === bottom) line += `${top ? DARK_BG : LIGHT_BG} `;
22313
+ else line += `${DARK}${LIGHT_BG}${top ? UPPER_HALF : LOWER_HALF}`;
22264
22314
  }
22265
22315
  lines.push(options2.plain ? line : `${line}${RESET}`);
22266
22316
  }
@@ -22285,12 +22335,29 @@ var BASE_STYLE = `
22285
22335
  :root{
22286
22336
  color-scheme:dark;
22287
22337
  --bg:#0d0e10;--panel:#131519;--panel-2:#181b20;
22288
- --border:#26282d;--border-strong:#33363c;
22338
+ --border:#26282d;--border-strong:#33363c;--control-border:#26282d;--control-strong:#33363c;
22289
22339
  --fg:#e6e7e9;--muted:#8b8f96;--accent:#e8b923;--danger:#fca5a5;
22340
+ --focus-border:color-mix(in srgb,var(--accent) 55%,transparent);--focus-ring:color-mix(in srgb,var(--accent) 22%,transparent);
22290
22341
  --sans:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
22291
22342
  --mono:ui-monospace,"JetBrains Mono","SF Mono","Cascadia Mono",Menlo,Consolas,monospace;
22292
22343
  --ease:cubic-bezier(0,0,.2,1);
22293
22344
  }
22345
+ :root[data-theme=light]{
22346
+ color-scheme:light;
22347
+ --bg:#f7f7f7;--panel:#fff;--panel-2:#fafafa;
22348
+ --border:#dedede;--border-strong:#cecece;--control-border:#858585;--control-strong:#7d7d7d;
22349
+ --fg:#292929;--muted:#606060;--accent:#416896;--danger:#cf222e;
22350
+ --focus-border:#416896;--focus-ring:#416896;
22351
+ }
22352
+ @media(prefers-color-scheme:light){
22353
+ :root:not([data-theme]){
22354
+ color-scheme:light;
22355
+ --bg:#f7f7f7;--panel:#fff;--panel-2:#fafafa;
22356
+ --border:#dedede;--border-strong:#cecece;--control-border:#858585;--control-strong:#7d7d7d;
22357
+ --fg:#292929;--muted:#606060;--accent:#416896;--danger:#cf222e;
22358
+ --focus-border:#416896;--focus-ring:#416896;
22359
+ }
22360
+ }
22294
22361
  html,body{background:var(--bg)}
22295
22362
  body{
22296
22363
  margin:0;min-height:100vh;min-height:100dvh;
@@ -22298,7 +22365,6 @@ body{
22298
22365
  color:var(--fg);font-family:var(--sans);font-size:12.5px;line-height:1.55;
22299
22366
  -webkit-font-smoothing:antialiased;
22300
22367
  }
22301
- html[data-font=mono] body{font-family:var(--mono)}
22302
22368
  main{width:100%;max-width:28rem;padding:18px;border:1px solid var(--border-strong);border-radius:12px;background:var(--panel-2)}
22303
22369
  h1{margin:0;display:flex;align-items:baseline;gap:7px;font-size:14px;font-weight:600;letter-spacing:-.01em;line-height:1.4}
22304
22370
  p{margin:0;text-wrap:pretty}
@@ -22312,13 +22378,13 @@ var RECOVERY_STYLE = `
22312
22378
  /* ErrorBoundary's ACTION_CLASS, one size up for a page that is alone in a viewport. */
22313
22379
  .btn{
22314
22380
  appearance:none;display:inline-flex;align-items:center;
22315
- border:1px solid var(--border);border-radius:6px;background:transparent;
22381
+ border:1px solid var(--control-border);border-radius:6px;background:transparent;
22316
22382
  padding:5px 10px;font:inherit;font-size:11.5px;color:color-mix(in srgb,var(--fg) 90%,transparent);
22317
22383
  cursor:pointer;text-decoration:none;
22318
22384
  transition:background-color 120ms var(--ease),border-color 120ms var(--ease),color 120ms var(--ease);
22319
22385
  }
22320
- .btn:hover{background:var(--panel);border-color:var(--border-strong);color:var(--fg)}
22321
- .btn:focus-visible{outline:none;border-color:color-mix(in srgb,var(--accent) 55%,transparent);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 22%,transparent)}
22386
+ .btn:hover{background:var(--panel);border-color:var(--control-strong);color:var(--fg)}
22387
+ .btn:focus-visible{outline:none;border-color:var(--focus-border);box-shadow:0 0 0 2px var(--focus-ring)}
22322
22388
  .btn:disabled{opacity:.5;pointer-events:none}
22323
22389
  /* min-height holds the slot open while it is LIVE, so a changing status line never nudges the card;
22324
22390
  :empty collapses it when there is nothing live to say, so a halted card has no trailing gutter. */
@@ -22338,8 +22404,7 @@ var RECOVERY_STYLE = `
22338
22404
  Alignment is an INK problem. A baseline-aligned flex item with no text of its own contributes its
22339
22405
  BORDER-BOX BOTTOM as the baseline, so the dot's ink centre lands 3px above it while the cap band's
22340
22406
  centre sits 0.5cap above it. \`cap\` is the resolved font's own cap height, so the correction holds
22341
- in BOTH board fonts and at any size, with nothing to re-measure when the type scale moves or the
22342
- font setting flips. The em spelling is the fallback for a browser without the unit (.35em ~= 0.5 x a
22407
+ at any size, with nothing to re-measure when the type scale moves. The em spelling is the fallback for a browser without the unit (.35em ~= 0.5 x a
22343
22408
  0.7em cap) and is overwritten wherever \`cap\` parses. */
22344
22409
  .dot{
22345
22410
  width:6px;height:6px;flex:0 0 auto;border-radius:9999px;background:var(--accent);
@@ -22370,7 +22435,7 @@ var RECOVERY_STYLE = `
22370
22435
  .probe[data-live=yes]{animation:none;background:none;color:var(--muted)}
22371
22436
  }
22372
22437
  `.trim();
22373
- var FONT_SCRIPT = `try{document.documentElement.dataset.font=localStorage.getItem("frizz-font")==="mono"?"mono":"sans"}catch{}`;
22438
+ var THEME_SCRIPT = `var p;try{p=localStorage.getItem("frizz-theme")}catch{}var d=false;try{d=matchMedia&&matchMedia("(prefers-color-scheme: dark)").matches}catch{}var t=p==="dark"||p==="light"?p:d?"dark":"light";document.documentElement.dataset.theme=t;document.documentElement.style.colorScheme=t;document.querySelector('meta[name="theme-color"]').content=t==="dark"?"#0d0e10":"#f7f7f7"`;
22374
22439
  var RECOVERY_SCRIPT = `
22375
22440
  (function(){
22376
22441
  var target=document.body.dataset.target||"/"
@@ -22469,8 +22534,8 @@ var RECOVERY_SCRIPT = `
22469
22534
  })()
22470
22535
  `.trim();
22471
22536
  function documentShell(options2) {
22472
- return `<!doctype html><html lang="en" data-font="sans"><head><meta charset="utf-8"><title>${options2.title}</title><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="dark"><meta name="theme-color" content="#0d0e10"><style>${options2.style ? `${BASE_STYLE}
22473
- ${options2.style}` : BASE_STYLE}</style>` + (options2.font ? `<script>${FONT_SCRIPT}</script>` : "") + `</head><body${options2.bodyAttributes ? ` ${options2.bodyAttributes}` : ""}><main>${options2.body}</main>` + (options2.script ? `<script>${options2.script}</script>` : "") + `</body></html>`;
22537
+ return `<!doctype html><html lang="en" data-font="sans"><head><meta charset="utf-8"><title>${options2.title}</title><meta name="viewport" content="width=device-width,initial-scale=1">` + (options2.appearance ? `<meta name="color-scheme" content="dark light"><meta name="theme-color" content="#0d0e10">` : `<meta name="color-scheme" content="dark light"><meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0d0e10"><meta name="theme-color" media="(prefers-color-scheme: light)" content="#f7f7f7">`) + `<style>${options2.style ? `${BASE_STYLE}
22538
+ ${options2.style}` : BASE_STYLE}</style>` + (options2.appearance ? `<script>${THEME_SCRIPT}</script>` : "") + `</head><body${options2.bodyAttributes ? ` ${options2.bodyAttributes}` : ""}><main>${options2.body}</main>` + (options2.script ? `<script>${options2.script}</script>` : "") + `</body></html>`;
22474
22539
  }
22475
22540
  var RECOVERY_COPY = {
22476
22541
  starting: {
@@ -22491,7 +22556,7 @@ function recoveryPage(url, variant = "starting") {
22491
22556
  return documentShell({
22492
22557
  title: copy.title,
22493
22558
  style: RECOVERY_STYLE,
22494
- font: true,
22559
+ appearance: true,
22495
22560
  bodyAttributes: `data-target="${target2}"`,
22496
22561
  body: `<h1><span class="dot" id="dot" aria-hidden="true"></span><span id="heading">${copy.heading}</span></h1><p class="detail" id="detail">${copy.detail}</p><pre class="log" id="log" hidden></pre><p class="note">${THREADS_NOTE}</p><div class="actions"><a class="btn" href="${target2}">Try again</a><button class="btn" type="button" id="restart">Restart Frizz</button></div><p class="probe" id="probe" aria-live="polite"></p>`,
22497
22562
  script: RECOVERY_SCRIPT
@@ -22508,7 +22573,7 @@ function unlistedHostPage(name) {
22508
22573
  const shown = escapeHtml(name);
22509
22574
  return documentShell({
22510
22575
  title: "Not served by this name",
22511
- font: true,
22576
+ appearance: true,
22512
22577
  body: `<h1>Not served by this name</h1><p class="detail">Frizz does not answer to <code>${shown}</code>. A name a browser has to resolve is how DNS rebinding makes another site's page count as this board, so an exposed board accepts only its own hostname and the names its operator lists.</p><p class="note">Open the board by its IP address, or relaunch with <code>--allowed-host ${shown}</code> (or <code>FRIZZ_ALLOWED_HOSTS=${shown}</code>) to accept this one.</p>`
22513
22578
  });
22514
22579
  }
@@ -23203,6 +23268,7 @@ function createAccessPane(options2) {
23203
23268
  import { homedir as homedir5 } from "node:os";
23204
23269
 
23205
23270
  // src/cloud.ts
23271
+ init_frizz_paths();
23206
23272
  init_src();
23207
23273
  import { spawn as spawn2 } from "node:child_process";
23208
23274
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3, rmSync } from "node:fs";
@@ -23216,8 +23282,8 @@ init_src();
23216
23282
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
23217
23283
  import { homedir as homedir3 } from "node:os";
23218
23284
  import { dirname as dirname3, join as join4 } from "node:path";
23219
- function claimIdentityPath(home = homedir3()) {
23220
- return join4(frizzPaths({ home }).state, "identity.key");
23285
+ function claimIdentityPath(home = homedir3(), env) {
23286
+ return join4(frizzPaths({ home, env }).state, "identity.key");
23221
23287
  }
23222
23288
  async function loadOrCreateClaimIdentity(home = homedir3()) {
23223
23289
  const path = claimIdentityPath(home);
@@ -23762,7 +23828,7 @@ async function reconcileCloudConfig(config, port, home = homedir4(), onNotice, o
23762
23828
  return establishCloudConfig(label, port, home, origin, github);
23763
23829
  }
23764
23830
  function cloudConfigPath(home = homedir4()) {
23765
- return join5(home, ".frizz", "cloud.json");
23831
+ return join5(frizzPaths({ home }).data, "cloud.json");
23766
23832
  }
23767
23833
  function readCloudConfig(home = homedir4()) {
23768
23834
  try {
@@ -23785,7 +23851,7 @@ function readCloudConfig(home = homedir4()) {
23785
23851
  }
23786
23852
  }
23787
23853
  function tunnelTokenPath(home = homedir4()) {
23788
- return join5(home, ".frizz", "tunnel-token");
23854
+ return join5(frizzPaths({ home }).state, "tunnel-token");
23789
23855
  }
23790
23856
  function readTunnelToken(home = homedir4()) {
23791
23857
  try {
@@ -24844,11 +24910,14 @@ function parseCliArgs(argv) {
24844
24910
  function boardAddress(url) {
24845
24911
  return url.includes("?") || url.endsWith("/") ? url : `${url}/`;
24846
24912
  }
24913
+ var SANDBOX_DROPPED_XDG_ROOTS = ["XDG_DATA_HOME", "XDG_STATE_HOME", "XDG_CACHE_HOME"];
24847
24914
  function prepareSandbox(env = process.env, realHome = homedir10()) {
24848
24915
  const home = mkdtempSync(join13(tmpdir2(), "frizz-sandbox-"));
24849
- shareCredentials(realHome, home);
24916
+ const inherited = { ...env };
24850
24917
  env.HOME = home;
24851
24918
  env.USERPROFILE = home;
24919
+ for (const name of SANDBOX_DROPPED_XDG_ROOTS) delete env[name];
24920
+ shareCredentials(realHome, home, inherited, env);
24852
24921
  const project = join13(home, "sandbox");
24853
24922
  mkdirSync10(project, { recursive: true });
24854
24923
  writeFileSync10(join13(project, "README.md"), "# frizz sandbox\n\nA throwaway project; everything here is deleted when the sandbox exits.\n");
@@ -24860,15 +24929,15 @@ function prepareSandbox(env = process.env, realHome = homedir10()) {
24860
24929
  process.chdir(project);
24861
24930
  return { home, project };
24862
24931
  }
24863
- function shareCredentials(realHome, home) {
24932
+ function shareCredentials(realHome, home, realEnv, sandboxEnv) {
24864
24933
  const link = (from, to) => {
24865
24934
  try {
24866
24935
  symlinkSync2(from, to);
24867
24936
  } catch {
24868
24937
  }
24869
24938
  };
24870
- const realKey = claimIdentityPath(realHome);
24871
- const sandboxKey = claimIdentityPath(home);
24939
+ const realKey = claimIdentityPath(realHome, realEnv);
24940
+ const sandboxKey = claimIdentityPath(home, sandboxEnv);
24872
24941
  mkdirSync10(dirname10(realKey), { recursive: true });
24873
24942
  mkdirSync10(dirname10(sandboxKey), { recursive: true });
24874
24943
  link(realKey, sandboxKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frizz",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "private": false,
5
5
  "description": "A local web UI for running many coding agents at once",
6
6
  "keywords": [
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "frizzServer": {
67
67
  "package": "frizz-server",
68
- "version": "0.13.8",
68
+ "version": "0.15.0",
69
69
  "protocol": 1,
70
70
  "dataEpoch": 1
71
71
  }