@huynle/hub 0.10.0 → 0.10.1
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/CHANGELOG.md +24 -0
- package/README.md +3 -1
- package/package.json +1 -1
- package/packages/claude-code/package.json +1 -1
- package/packages/cli/dist/hub-cli.js +9 -7
- package/packages/cli/package.json +1 -1
- package/packages/codex/package.json +1 -1
- package/packages/node/package.json +1 -1
- package/packages/preset/package.json +1 -1
- package/packages/protocol/package.json +1 -1
- package/packages/protocol/src/index.ts +13 -1
- package/packages/runtime-openshell/package.json +1 -1
- package/packages/runtime-sdk/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/packages/spoke-sdk/package.json +1 -1
- package/src/central/http.ts +33 -4
- package/src/spoke/spoke.ts +19 -0
- package/web/dist/assets/{ArtifactPage-Bz992ZX6.js → ArtifactPage-CsPjam8D.js} +1 -1
- package/web/dist/assets/{ArtifactPane-BJZNixJC.js → ArtifactPane-B9weO8L6.js} +1 -1
- package/web/dist/assets/{ArtifactWorkbench-Dti3VP07.js → ArtifactWorkbench-DzKhMs3l.js} +1 -1
- package/web/dist/assets/{Diagram-B4HdUxw0.js → Diagram-CuRIffka.js} +1 -1
- package/web/dist/assets/{Library-CpzD2HDW.js → Library-rv5DGtrm.js} +2 -2
- package/web/dist/assets/{ProfilesDestination-DlQeogea.js → ProfilesDestination-DUuQ9jmd.js} +1 -1
- package/web/dist/assets/{TeamsPage-4xdWCaI-.js → TeamsPage-D_17MkvN.js} +1 -1
- package/web/dist/assets/{Watchtower-CpQdIRns.js → Watchtower-DlrNOJ7X.js} +2 -2
- package/web/dist/assets/{index-BS6lA6fN.js → index-bX8B6NLo.js} +6 -6
- package/web/dist/assets/{index-BC0O1gfR.css → index-rW6LKQj1.css} +1 -1
- package/web/dist/assets/{live-CVUFIdUX.js → live-BGLA-peO.js} +1 -1
- package/web/dist/assets/{renderer2d-IaJiloPi.js → renderer2d-DyANQ1WS.js} +1 -1
- package/web/dist/assets/{renderer3d-ZiD1uKGW.js → renderer3d-vYYKFAJq.js} +1 -1
- package/web/dist/assets/{safe-area-C7IAlkgY.js → safe-area-B2Bk__FK.js} +1 -1
- package/web/dist/index.html +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
Clicking the composer's Model chip changes the session's model. No migration is needed. Machines need the new
|
|
6
|
+
plugin (and an OpenCode restart) for the chip to become clickable; with older plugins it stays display only.
|
|
7
|
+
|
|
8
|
+
### Console
|
|
9
|
+
- **The composer's Model chip changes the session's model** (user decision, 2026-11-10: "yes, it should change the
|
|
10
|
+
session model"). Clicking (or Enter on) "Model: …" opens the same picker as the Voice model (search over the
|
|
11
|
+
session machine's catalog, the variant step, arrows, Enter and 1–9); a pick sets the **session's own model**, so its
|
|
12
|
+
next turn and every later one run on it, whether the prompt comes from the hub console, the OpenCode TUI or a
|
|
13
|
+
Voice relay. The chip then shows it with source "chosen" (the picker marks it). The Voice chip and its picker are
|
|
14
|
+
unchanged, and a Voice pick still never changes the session's model.
|
|
15
|
+
- Mechanism: OpenCode 2.x's `session.switchModel`, which stores the model on the session (the same call the TUI
|
|
16
|
+
makes when you pick a model there); the runner uses it for every turn of that session. Last writer wins: a model
|
|
17
|
+
you then pick in the TUI's own model dialog replaces it at the TUI's next prompt. OpenCode has no way to go back
|
|
18
|
+
to "no model chosen", so the picker has no Default entry: pick the default model itself.
|
|
19
|
+
- New console route `PUT /api/sessions/:id/model {model: {providerID, modelID, variant?}}` → `{model}` (the
|
|
20
|
+
session's model now). The same access as sending a prompt: the session's owner only (foreign or unknown
|
|
21
|
+
sessions 404, session/app tokens and grant-holders 403, as for every console route). 400 for a malformed body or
|
|
22
|
+
a model or variant the session's catalog lacks, 409 `unsupported` when its spoke is older. Audited as
|
|
23
|
+
`session.model`.
|
|
24
|
+
- Protocol (additive): spoke op `setModel {sessionID, model}` → `{model: SessionModel}`, announced as the new spoke
|
|
25
|
+
feature `setModel`. Older spokes (and hubs that list no features) keep the chip display only.
|
|
26
|
+
|
|
3
27
|
## 0.10.0
|
|
4
28
|
|
|
5
29
|
One release for everything since 0.9.2: the production-testing fixes, the voice update (Voice front on by default,
|
package/README.md
CHANGED
|
@@ -419,6 +419,7 @@ conversation mode and utterances (403 `not_exposed` for sessions not shared with
|
|
|
419
419
|
| `POST /api/sessions/:id/rename` | `{title}` → `{title}`: renames the session in OpenCode (`session.update`), through the process that owns it, else any other process that reports it. The title is trimmed and must be 1–200 characters (400 otherwise). Works with Voice off; the new title shows in every console at once (the spoke reports it before answering) |
|
|
420
420
|
| `POST /api/sessions/:id/fork {messageID}` · `GET …/revert/preview?messageID=` · `POST …/revert {messageID, files?}` · `POST …/revert/restore` · `POST …/revert/commit` | fork before one of the session's user messages (201 `{sessionID, text, attachments}`), or stage, undo and commit a revert from it (409 `busy` while it runs); console only, audited; needs a spoke announcing `fork`/`revert` (409 `unsupported` otherwise). Contract: [docs/session-view.md](docs/session-view.md#fork-and-revert-a-message) |
|
|
421
421
|
| `POST /api/sessions/:id/stop` | interrupts running descendants deepest first, then the root; idempotent. Also releases the hub's turn lock for the tree: a turn request still waiting answers `{outcome: "interrupted"}` and the next turn can start (`/interrupt` does the same) |
|
|
422
|
+
| `PUT /api/sessions/:id/model {model: {providerID, modelID, variant?}}` | (feature `setModel`; console only, same access as a prompt: the session's owner) sets the session's own model: its next turn and every later one run on it, whoever prompts (the console, the TUI, a Voice relay). The spoke calls OpenCode's `session.switchModel`, which stores it on the session. Answers `{model}` (the summary's `model`, source `chosen`). 400 malformed or not in the session's catalog (`GET …/models`), 409 `unsupported` from an older spoke. Audited `session.model`. The Voice model (`…/model-prefs`) is separate |
|
|
422
423
|
| `GET /api/sessions/:id/summary` | context usage, the model the session runs on (`model`: chosen, else its latest reply's, else the default; with `source`), todos, MCP status (at most 100 servers), branch and working-tree change stats (each part optional). `changes` lists at most 500 files, most-changed first, with `totalFiles` and `truncated`; its `additions`/`deletions` cover every file |
|
|
423
424
|
| `GET /api/sessions/:id/context-cards` · `POST …/context-cards/:provider/refresh` | the session's [context cards](docs/context-cards.md) (`{cards, providers, enabled, configured}`), and running one provider now (`{force?}` → `{outcome: "generated", card}` · `{outcome: "skipped", reason}` · `{outcome: "failed"}`; 404 unknown provider; 429 with `Retry-After`: one per card per 30 s, 10 per user per minute). Live: SSE `{type: "contextCard", sessionID, provider, primary?, generatedAt}` (never the text) |
|
|
424
425
|
| `GET /api/context-cards/primary?sessions=a,b` | the primary context cards of up to 200 sessions (`{cards: {sessionID: card}}`): the session list's second line |
|
|
@@ -1469,7 +1470,8 @@ the Docker image, npm package and binaries ship the build). One store fed by the
|
|
|
1469
1470
|
recording cancels it; each session keeps its own unsent draft), Voice mode (hands-free, see below; a toggle that
|
|
1470
1471
|
turns Voice on first when needed; one session at a time, so turning it on elsewhere turns the first one off; it
|
|
1471
1472
|
keeps running while you look at other sessions, which show a "Voice: <session>" pill with End) with a
|
|
1472
|
-
microphone picker,
|
|
1473
|
+
microphone picker, the Voice model menu (the voice front's model) and the Model chip (the session's own model; where
|
|
1474
|
+
the machine's plugin serves `setModel` it opens the same menu and sets the model every later turn runs on). The right rail (a bottom sheet on phones) has
|
|
1473
1475
|
cards that collapse from their header (remembered per device): **Environment** (machine with
|
|
1474
1476
|
online/offline/Connecting, project folder with the full path as tooltip, branch, and where it is open:
|
|
1475
1477
|
TUI, Herdr or background), Context, **Connectors · N connected** (MCP servers) and Changes with a diff
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Hub: a harness-agnostic central hub your AI agent harnesses connect to (OpenCode first). Follow, steer and stop sessions from your phone or browser, with notifications, optional hands-free Voice and a self-hostable server.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Huy Le",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub-claude-code",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "The Claude Code hooks spoke: per-user daemon, hook bridge, plugin dir, `hub claude` wrapper and daemon-backed MCP server (internal; shipped inside @huynle/hub, not published on its own).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -7,7 +7,7 @@ import { createInterface } from "readline";
|
|
|
7
7
|
// packages/cli/package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@huynle/hub-cli",
|
|
10
|
-
version: "0.10.
|
|
10
|
+
version: "0.10.1",
|
|
11
11
|
private: true,
|
|
12
12
|
description: "The hub CLI (`hub` / `hubctl`) for agents and scripts (internal; bundled into @huynle/hub, not published on its own).",
|
|
13
13
|
license: "MIT",
|
|
@@ -4421,7 +4421,7 @@ var IMPLIED_OPENCODE_FEATURES = [
|
|
|
4421
4421
|
"expose",
|
|
4422
4422
|
"create"
|
|
4423
4423
|
];
|
|
4424
|
-
var OPTIONAL_SPOKE_FEATURES = [...IMPLIED_OPENCODE_FEATURES, "autoName", "sessionTokens", "deliver", "hubPrompt", "permissionMessage", "actTokens", "launch", "fork", "revert", "dismissError", "timelineMedia", PREVIEW_BUNDLE_FEATURE, "voiceFront", "conciergeFront", "inboxCarried"];
|
|
4424
|
+
var OPTIONAL_SPOKE_FEATURES = [...IMPLIED_OPENCODE_FEATURES, "autoName", "sessionTokens", "deliver", "hubPrompt", "permissionMessage", "actTokens", "launch", "fork", "revert", "dismissError", "timelineMedia", PREVIEW_BUNDLE_FEATURE, "voiceFront", "conciergeFront", "inboxCarried", "setModel"];
|
|
4425
4425
|
var CORE_OPS = ["turn", "steer", "interrupt", "stop", "permission", "form"];
|
|
4426
4426
|
var KNOWN_SPOKE_FEATURES = [...CORE_OPS, ...OPTIONAL_SPOKE_FEATURES, "turnEvents", PROFILE_APPLY_FEATURE, SESSION_CONTEXT_FEATURE];
|
|
4427
4427
|
var SPOKE_FEATURES = [...OPTIONAL_SPOKE_FEATURES, PROFILE_APPLY_FEATURE, SESSION_CONTEXT_FEATURE];
|
|
@@ -4620,6 +4620,8 @@ function isServerRequest(v) {
|
|
|
4620
4620
|
return nonEmpty(v.sessionID);
|
|
4621
4621
|
case "setModelPrefs":
|
|
4622
4622
|
return nonEmpty(v.sessionID) && (v.voice === undefined || v.voice === null || isModelSelection(v.voice)) && (v.worker === undefined || v.worker === null || isModelSelection(v.worker)) && (v.voice !== undefined || v.worker !== undefined);
|
|
4623
|
+
case "setModel":
|
|
4624
|
+
return nonEmpty(v.sessionID) && isModelSelection(v.model);
|
|
4623
4625
|
case "timeline":
|
|
4624
4626
|
return nonEmpty(v.sessionID) && (v.limit === undefined || Number.isInteger(v.limit) && Number(v.limit) >= 1 && Number(v.limit) <= 200) && (v.cursor === undefined || str2(v.cursor) && v.cursor.length <= 4096) && (v.order === undefined || v.order === "asc" || v.order === "desc") && (v.before === undefined || nonEmpty(v.before) && v.before.length <= 4096) && (v.cursor === undefined || v.before === undefined && v.order !== "desc");
|
|
4625
4627
|
case "timelinePart":
|
|
@@ -9422,7 +9424,7 @@ import { arch as osArch, platform as osPlatform } from "node:os";
|
|
|
9422
9424
|
// packages/node/package.json
|
|
9423
9425
|
var package_default2 = {
|
|
9424
9426
|
name: "@huynle/hub-node",
|
|
9425
|
-
version: "0.10.
|
|
9427
|
+
version: "0.10.1",
|
|
9426
9428
|
private: true,
|
|
9427
9429
|
description: "`hub node`: the per-machine daemon that prepares, collects and cleans up launch workspaces (internal; shipped inside @huynle/hub, not published on its own).",
|
|
9428
9430
|
license: "MIT",
|
|
@@ -10877,7 +10879,7 @@ import path14 from "node:path";
|
|
|
10877
10879
|
// packages/runtime-openshell/package.json
|
|
10878
10880
|
var package_default3 = {
|
|
10879
10881
|
name: "@huynle/hub-runtime-openshell",
|
|
10880
|
-
version: "0.10.
|
|
10882
|
+
version: "0.10.1",
|
|
10881
10883
|
private: true,
|
|
10882
10884
|
description: "The `openshell` runtime of `hub node`: launches in NVIDIA OpenShell sandboxes, driven through the openshell CLI (internal; shipped inside @huynle/hub, not published on its own).",
|
|
10883
10885
|
license: "MIT",
|
|
@@ -14875,7 +14877,7 @@ import path23 from "node:path";
|
|
|
14875
14877
|
// packages/claude-code/package.json
|
|
14876
14878
|
var package_default4 = {
|
|
14877
14879
|
name: "@huynle/hub-claude-code",
|
|
14878
|
-
version: "0.10.
|
|
14880
|
+
version: "0.10.1",
|
|
14879
14881
|
private: true,
|
|
14880
14882
|
description: "The Claude Code hooks spoke: per-user daemon, hook bridge, plugin dir, `hub claude` wrapper and daemon-backed MCP server (internal; shipped inside @huynle/hub, not published on its own).",
|
|
14881
14883
|
license: "MIT",
|
|
@@ -15164,7 +15166,7 @@ import path26 from "node:path";
|
|
|
15164
15166
|
// packages/preset/package.json
|
|
15165
15167
|
var package_default5 = {
|
|
15166
15168
|
name: "@huynle/hub-preset",
|
|
15167
|
-
version: "0.10.
|
|
15169
|
+
version: "0.10.1",
|
|
15168
15170
|
private: true,
|
|
15169
15171
|
description: "The preset spoke: drives a harness without a spoke of its own through a terminal pane (tmux), described by a data-only preset; official presets in presets/ (internal; shipped inside @huynle/hub, not published on its own).",
|
|
15170
15172
|
license: "MIT",
|
|
@@ -15689,7 +15691,7 @@ import { openSync as openSync6, closeSync as closeSync6, readFileSync as readFil
|
|
|
15689
15691
|
// packages/codex/package.json
|
|
15690
15692
|
var package_default6 = {
|
|
15691
15693
|
name: "@huynle/hub-codex",
|
|
15692
|
-
version: "0.10.
|
|
15694
|
+
version: "0.10.1",
|
|
15693
15695
|
private: true,
|
|
15694
15696
|
description: "The Codex app-server spoke: attaches to Codex's app-server (its own over stdio, and the shared daemon's socket when present), and the `hub codex` wrapper (internal; shipped inside @huynle/hub, not published on its own).",
|
|
15695
15697
|
license: "MIT",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub-codex",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "The Codex app-server spoke: attaches to Codex's app-server (its own over stdio, and the shared daemon's socket when present), and the `hub codex` wrapper (internal; shipped inside @huynle/hub, not published on its own).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub-node",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "`hub node`: the per-machine daemon that prepares, collects and cleans up launch workspaces (internal; shipped inside @huynle/hub, not published on its own).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub-preset",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "The preset spoke: drives a harness without a spoke of its own through a terminal pane (tmux), described by a data-only preset; official presets in presets/ (internal; shipped inside @huynle/hub, not published on its own).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -480,8 +480,10 @@ export const IMPLIED_OPENCODE_FEATURES = [
|
|
|
480
480
|
* "inboxCarried": its session.inbox.delivered marks a user item the hub carried (`carried: true`), so every other
|
|
481
481
|
* user item is a prompt typed in the terminal. Spokes of harnesses that report the hub's prompts as origin "hub"
|
|
482
482
|
* need not announce it.
|
|
483
|
+
* "setModel": it sets the session's own model (setModel op) the way the harness keeps it for every later turn of
|
|
484
|
+
* that session (OpenCode: session.switchModel, stored on the session), whoever prompts it next.
|
|
483
485
|
*/
|
|
484
|
-
const OPTIONAL_SPOKE_FEATURES = [...IMPLIED_OPENCODE_FEATURES, "autoName", "sessionTokens", "deliver", "hubPrompt", "permissionMessage", "actTokens", "launch", "fork", "revert", "dismissError", "timelineMedia", PREVIEW_BUNDLE_FEATURE, "voiceFront", "conciergeFront", "inboxCarried"] as const
|
|
486
|
+
const OPTIONAL_SPOKE_FEATURES = [...IMPLIED_OPENCODE_FEATURES, "autoName", "sessionTokens", "deliver", "hubPrompt", "permissionMessage", "actTokens", "launch", "fork", "revert", "dismissError", "timelineMedia", PREVIEW_BUNDLE_FEATURE, "voiceFront", "conciergeFront", "inboxCarried", "setModel"] as const
|
|
485
487
|
|
|
486
488
|
/**
|
|
487
489
|
* The core ops. An OpenCode spoke serves them all without announcing them; a spoke of another harness announces
|
|
@@ -700,6 +702,12 @@ export type ServerRequest =
|
|
|
700
702
|
| { readonly op: "modelPrefs"; readonly sessionID: string }
|
|
701
703
|
/** `worker` is obsolete: older hubs send it, current spokes ignore it. */
|
|
702
704
|
| { readonly op: "setModelPrefs"; readonly sessionID: string; readonly voice?: ModelSelection | null; readonly worker?: ModelSelection | null }
|
|
705
|
+
/**
|
|
706
|
+
* (feature "setModel") Set the session's own model: every later turn of the session runs on it, whoever prompts it
|
|
707
|
+
* (the console, the terminal, a voice relay). One of the session's catalog models (models op); no variant: the
|
|
708
|
+
* model's default. The Voice model (setModelPrefs) is separate and never changes it.
|
|
709
|
+
*/
|
|
710
|
+
| { readonly op: "setModel"; readonly sessionID: string; readonly model: ModelSelection }
|
|
703
711
|
| { readonly op: "timeline"; readonly sessionID: string; readonly limit?: number; readonly cursor?: string; readonly order?: "asc" | "desc"; readonly before?: string }
|
|
704
712
|
| { readonly op: "timelinePart"; readonly sessionID: string; readonly messageID: string; readonly partID: string }
|
|
705
713
|
/** One image attached to a user message (its position in the message's files). */
|
|
@@ -920,6 +928,8 @@ export interface ResultFor {
|
|
|
920
928
|
models: { readonly models: readonly AvailableModel[] }
|
|
921
929
|
modelPrefs: { readonly prefs: SessionModelPrefs; readonly defaults: SessionModelPrefs }
|
|
922
930
|
setModelPrefs: { readonly prefs: SessionModelPrefs; readonly defaults: SessionModelPrefs }
|
|
931
|
+
/** `model`: the session's model now (source "chosen"). */
|
|
932
|
+
setModel: { readonly model: SessionModel }
|
|
923
933
|
timeline: TimelinePage
|
|
924
934
|
timelinePart: { readonly detail: unknown }
|
|
925
935
|
/** No `file` when the message has no image at that index; `tooLarge` when its bytes would not fit in a frame. */
|
|
@@ -1268,6 +1278,8 @@ function isServerRequest(v: unknown): v is ServerRequest {
|
|
|
1268
1278
|
(v.worker === undefined || v.worker === null || isModelSelection(v.worker)) &&
|
|
1269
1279
|
(v.voice !== undefined || v.worker !== undefined)
|
|
1270
1280
|
)
|
|
1281
|
+
case "setModel":
|
|
1282
|
+
return nonEmpty(v.sessionID) && isModelSelection(v.model)
|
|
1271
1283
|
case "timeline":
|
|
1272
1284
|
return (
|
|
1273
1285
|
nonEmpty(v.sessionID) &&
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub-runtime-openshell",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "The `openshell` runtime of `hub node`: launches in NVIDIA OpenShell sandboxes, driven through the openshell CLI (internal; shipped inside @huynle/hub, not published on its own).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub-spoke-sdk",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Harness-neutral spoke building blocks: hub connection, enrollment, tokens, idempotent deliver, prompt references, HarnessSpoke (internal; shipped inside @huynle/hub, not published on its own).",
|
|
6
6
|
"license": "MIT",
|
package/src/central/http.ts
CHANGED
|
@@ -453,7 +453,7 @@ class HttpError extends Error {
|
|
|
453
453
|
/** A core op the session's spoke does not serve (another harness's spoke): 409 code unsupported, like plugin_outdated. */
|
|
454
454
|
/** (AF3) The same, as an /api/agent error. */
|
|
455
455
|
const unsupportedAgentError = (feature: string, harness: string) => new AgentError("unsupported", unsupportedMessage({ feature, harness }), { extra: { feature, harness } })
|
|
456
|
-
const unsupportedError = (lacking: { readonly feature: Unsupported["feature"] | "fork" | "revert"; readonly harness: string }) => new HttpError(409, unsupportedMessage(lacking), "unsupported")
|
|
456
|
+
const unsupportedError = (lacking: { readonly feature: Unsupported["feature"] | "fork" | "revert" | "setModel"; readonly harness: string }) => new HttpError(409, unsupportedMessage(lacking), "unsupported")
|
|
457
457
|
|
|
458
458
|
const error = (status: number, message: string, code?: string, headers?: Record<string, string>) =>
|
|
459
459
|
Response.json({ error: message, ...(code ? { code } : {}) }, { status, ...(headers ? { headers } : {}) })
|
|
@@ -2196,11 +2196,14 @@ export function startCentral(options: CentralOptions): Central {
|
|
|
2196
2196
|
const change = modelPrefsInput(await jsonBody(request))
|
|
2197
2197
|
// Only an obsolete Worker model (an older console): nothing changes.
|
|
2198
2198
|
if (!("voice" in change)) return getModelPrefs(params.session!, user.id)
|
|
2199
|
-
await checkModelSelections(ownerOf(params.session!), params.session!, change)
|
|
2199
|
+
await checkModelSelections(ownerOf(params.session!), params.session!, [change.voice])
|
|
2200
2200
|
const reply = await spokes.request(ownerOf(params.session!), { op: "setModelPrefs", sessionID: params.session!, ...change })
|
|
2201
2201
|
const prefs = await store.setModelPrefs(user.id, params.session!, change)
|
|
2202
2202
|
return Response.json({ prefs, defaults: voiceOnly(reply.defaults) }, { headers: NO_STORE })
|
|
2203
2203
|
}),
|
|
2204
|
+
// The session's own model (feature "setModel"): every later turn runs on it, whoever prompts. Same access as a
|
|
2205
|
+
// prompt (the session's owner); the Voice model above is separate.
|
|
2206
|
+
route("PUT", "/api/sessions/:session/model", "session", async ({ request, params, user }) => setSessionModel(request, params.session!, user.id)),
|
|
2204
2207
|
route("GET", "/api/sessions/:session/timeline", "session", async ({ params, url }) => {
|
|
2205
2208
|
const limitRaw = url.searchParams.get("limit")
|
|
2206
2209
|
const limit = limitRaw === null ? undefined : Number(limitRaw)
|
|
@@ -2668,6 +2671,20 @@ export function startCentral(options: CentralOptions): Central {
|
|
|
2668
2671
|
})
|
|
2669
2672
|
}
|
|
2670
2673
|
|
|
2674
|
+
/**
|
|
2675
|
+
* PUT /api/sessions/:id/model {model}: 400 for a malformed body or a model (variant) the session's catalog lacks,
|
|
2676
|
+
* 409 unsupported when its spoke did not announce setModel (an older plugin: the console's chip stays display only).
|
|
2677
|
+
*/
|
|
2678
|
+
async function setSessionModel(request: Request, sessionID: string, userID: string): Promise<Response> {
|
|
2679
|
+
const model = sessionModelInput(await jsonBody(request))
|
|
2680
|
+
const connID = ownerOf(sessionID)
|
|
2681
|
+
if (!registry.supports(connID, "setModel")) throw unsupportedError({ feature: "setModel", harness: registry.harnessOf(connID) ?? "unknown" })
|
|
2682
|
+
await checkModelSelections(connID, sessionID, [model])
|
|
2683
|
+
const result = await spokes.request(connID, { op: "setModel", sessionID, model })
|
|
2684
|
+
await store.audit({ actor: userID, action: "session.model", target: sessionID, detail: `${model.providerID}/${model.modelID}${model.variant ? `#${model.variant}` : ""}` })
|
|
2685
|
+
return Response.json({ model: result.model }, { headers: NO_STORE })
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2671
2688
|
/** A session's Voice model preference, re-sent to its spoke (which answers with its defaults). */
|
|
2672
2689
|
async function getModelPrefs(sessionID: string, userID: string): Promise<Response> {
|
|
2673
2690
|
const prefs = store.modelPrefs(userID, sessionID)
|
|
@@ -2706,8 +2723,8 @@ export function startCentral(options: CentralOptions): Central {
|
|
|
2706
2723
|
* Every selection must be in the session's catalog (provider, model and variant), so a typo cannot leave
|
|
2707
2724
|
* the session failing every turn. A catalog that cannot be fetched is a 502, never an accepted selection.
|
|
2708
2725
|
*/
|
|
2709
|
-
async function checkModelSelections(connID: string, sessionID: string,
|
|
2710
|
-
const selections =
|
|
2726
|
+
async function checkModelSelections(connID: string, sessionID: string, picked: ReadonlyArray<ModelSelection | null | undefined>): Promise<void> {
|
|
2727
|
+
const selections = picked.filter((m): m is ModelSelection => !!m)
|
|
2711
2728
|
if (selections.length === 0) return
|
|
2712
2729
|
let models: readonly AvailableModel[]
|
|
2713
2730
|
try {
|
|
@@ -3572,6 +3589,18 @@ function modelPrefsInput(value: unknown): { voice?: ModelSelection | null } {
|
|
|
3572
3589
|
return "voice" in out ? { voice: out.voice! } : {}
|
|
3573
3590
|
}
|
|
3574
3591
|
|
|
3592
|
+
/** The body of PUT /api/sessions/:id/model: {model: {providerID, modelID, variant?}}; other keys inside `model` are dropped. */
|
|
3593
|
+
function sessionModelInput(value: unknown): ModelSelection {
|
|
3594
|
+
const invalid = () => new HttpError(400, "Expected {model: {providerID, modelID, variant?}}")
|
|
3595
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw invalid()
|
|
3596
|
+
const record = value as Record<string, unknown>
|
|
3597
|
+
if (Object.keys(record).some((key) => key !== "model")) throw invalid()
|
|
3598
|
+
const m = record.model as Record<string, unknown> | null | undefined
|
|
3599
|
+
if (typeof m !== "object" || m === null || Array.isArray(m)) throw invalid()
|
|
3600
|
+
if (typeof m.providerID !== "string" || !MODEL_ID.test(m.providerID) || typeof m.modelID !== "string" || !MODEL_ID.test(m.modelID) || (m.variant !== undefined && (typeof m.variant !== "string" || !MODEL_ID.test(m.variant)))) throw invalid()
|
|
3601
|
+
return { providerID: m.providerID, modelID: m.modelID, ...(typeof m.variant === "string" && m.variant !== "default" ? { variant: m.variant } : {}) }
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3575
3604
|
function param(raw: string): string {
|
|
3576
3605
|
try {
|
|
3577
3606
|
return decodeURIComponent(raw)
|
package/src/spoke/spoke.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
type SessionTokensRequest,
|
|
29
29
|
type ActTokensRequest,
|
|
30
30
|
type CreateRequest,
|
|
31
|
+
type ModelSelection,
|
|
31
32
|
type SessionModelPrefs,
|
|
32
33
|
type SpokeRequest,
|
|
33
34
|
type TerminalInfo,
|
|
@@ -1419,6 +1420,8 @@ export class Spoke {
|
|
|
1419
1420
|
else this.modelPrefs.delete(req.sessionID)
|
|
1420
1421
|
return this.modelPrefsResult(req.sessionID)
|
|
1421
1422
|
}
|
|
1423
|
+
case "setModel":
|
|
1424
|
+
return this.setSessionModel(req.sessionID, req.model)
|
|
1422
1425
|
case "timeline": {
|
|
1423
1426
|
// A first page reads OpenCode (it shows the newest state); following pages reuse those rows briefly. A
|
|
1424
1427
|
// staged revert hides its message and everything after it (OpenCode keeps them until it is committed).
|
|
@@ -1940,6 +1943,22 @@ export class Spoke {
|
|
|
1940
1943
|
await this.host.switchModel(sessionID, model)
|
|
1941
1944
|
}
|
|
1942
1945
|
|
|
1946
|
+
/**
|
|
1947
|
+
* (feature "setModel") The session's own model, as the console picked it: OpenCode's session.switchModel stores it
|
|
1948
|
+
* on the session, so every later turn runs on it whoever prompts (the console, the TUI, a voice relay). Only a
|
|
1949
|
+
* model (and variant) of this OpenCode's catalog; no variant: the model's default.
|
|
1950
|
+
*/
|
|
1951
|
+
private async setSessionModel(sessionID: string, model: ModelSelection): Promise<ResultFor["setModel"]> {
|
|
1952
|
+
const catalog = (await this.host.listModels?.()) ?? []
|
|
1953
|
+
const known = catalog.find((value: any) => value?.providerID === model.providerID && (typeof value.id === "string" && value.id ? value.id : value.modelID) === model.modelID) as any
|
|
1954
|
+
const variants: string[] = Array.isArray(known?.variants) ? known.variants.flatMap((v: any) => (typeof v?.id === "string" ? [v.id] : [])) : []
|
|
1955
|
+
const variant = model.variant && model.variant !== "default" ? model.variant : undefined
|
|
1956
|
+
if (!known || (variant && !variants.includes(variant))) throw new Error(`Unknown model ${model.providerID}/${model.modelID}${variant ? `#${variant}` : ""} for this session`)
|
|
1957
|
+
const ref: ModelRef = { providerID: model.providerID, id: model.modelID, ...(variant ? { variant } : {}) }
|
|
1958
|
+
await this.switchModel(sessionID, ref)
|
|
1959
|
+
return { model: sessionModel({ chosen: ref, models: catalog })! }
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1943
1962
|
/** Enables or disables voice for a session this process reported (mode "session" only). */
|
|
1944
1963
|
private expose(sessionID: string, on: boolean): ResultFor["expose"] {
|
|
1945
1964
|
if (!this.tracker.has(sessionID)) throw new Error("Unknown session")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,I as t,S as n,i as r,k as i,r as a}from"./ui-CFB4wQfF.js";import{o}from"./artifacts-But87Jfo.js";import{it as s,rt as c}from"./index-
|
|
1
|
+
import{B as e,I as t,S as n,i as r,k as i,r as a}from"./ui-CFB4wQfF.js";import{o}from"./artifacts-But87Jfo.js";import{it as s,rt as c}from"./index-bX8B6NLo.js";import{t as l}from"./ArtifactWorkbench-DzKhMs3l.js";var u=i(`<div class="feature-page av-page">`),d=i(`<a class="icon-btn av-back"href=/artifacts aria-label="All artifacts"title="All artifacts">`);function f(i){return(()=>{var f=u();return n(f,e(l,{get id(){return i.id},layout:`page`,get lead(){return[e(t,{get when(){return c()},get children(){return e(r,{icon:`menu`,label:`Open sidebar`,onClick:s})}}),(()=>{var t=d();return n(t,e(a,{name:`back`})),t})()]},get actions(){return e(r,{class:`av-wide`,icon:`split`,label:`Open in pane`,onClick:()=>o(i.id)})}})),f})()}export{f as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,G as t,S as n,T as r,k as i,r as a}from"./ui-CFB4wQfF.js";import{t as o}from"./ArtifactWorkbench-
|
|
1
|
+
import{B as e,G as t,S as n,T as r,k as i,r as a}from"./ui-CFB4wQfF.js";import{t as o}from"./ArtifactWorkbench-DzKhMs3l.js";var s=i(`<a class=icon-btn aria-label="Full page"title="Open the artifact's full page">`);function c(i){return e(o,{get id(){return i.artifactID},layout:`pane`,onArtifact:e=>i.onTitle?.(e.title||`Untitled`),get actions(){return(()=>{var o=s();return n(o,e(a,{name:`expand`})),t(()=>r(o,`href`,`/artifacts/${encodeURIComponent(i.artifactID)}`)),o})()},get trailing(){return i.paneTools}})}export{c as ArtifactPane};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{$ as e,A as t,B as n,C as r,E as i,F as a,G as o,I as s,J as c,N as l,O as u,P as d,Q as f,R as p,S as m,T as h,U as g,W as _,_ as v,a as y,b,et as x,g as S,i as C,k as w,r as T,tt as E,u as D,x as O,y as k}from"./ui-CFB4wQfF.js";import{A,B as j,H as M,M as N,R as P,U as F,V as I,W as ee}from"./toast-CXTwTvDw.js";import{n as L,o as R}from"./artifacts-But87Jfo.js";import{B as te,F as z,G as ne,H as B,I as re,J as V,K as ie,L as ae,M as oe,N as se,P as ce,Q as le,R as ue,U as de,V as fe,W as pe,X as me,Y as H,Z as he,at as ge,et as _e,j as ve,q as ye,st as be,z as xe}from"./index-
|
|
1
|
+
import{$ as e,A as t,B as n,C as r,E as i,F as a,G as o,I as s,J as c,N as l,O as u,P as d,Q as f,R as p,S as m,T as h,U as g,W as _,_ as v,a as y,b,et as x,g as S,i as C,k as w,r as T,tt as E,u as D,x as O,y as k}from"./ui-CFB4wQfF.js";import{A,B as j,H as M,M as N,R as P,U as F,V as I,W as ee}from"./toast-CXTwTvDw.js";import{n as L,o as R}from"./artifacts-But87Jfo.js";import{B as te,F as z,G as ne,H as B,I as re,J as V,K as ie,L as ae,M as oe,N as se,P as ce,Q as le,R as ue,U as de,V as fe,W as pe,X as me,Y as H,Z as he,at as ge,et as _e,j as ve,q as ye,st as be,z as xe}from"./index-bX8B6NLo.js";import{c as Se,i as Ce,n as we,r as Te,t as Ee}from"./source-C-De8L3s.js";var De=`ai-actions`,Oe=16,ke=[`ask`,`propose-edit`,`run-action`],Ae=[`queued`,`running`,`succeeded`,`failed`,`cancelled`],je=[`starting`,`waiting`,`offline`,`ready`,`ended`,`failed`],Me=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/,U=e=>typeof e==`object`&&!!e&&!Array.isArray(e),W=e=>typeof e==`string`&&e.length>0,Ne=e=>typeof e==`number`&&Number.isFinite(e),Pe=(e,t)=>typeof t==`string`&&e.includes(t),Fe=e=>e.replace(/\/+$/,``).split(`/`).pop()||e;function Ie(e){if(U(e)&&Pe(je,e.status)){if(e.kind===`session`&&W(e.address))return e;if(e.kind===`launch`&&W(e.machine)&&W(e.repo))return{harness:``,mode:`checkout`,...e}}}function Le(e){if(!U(e)||!Pe([`pending`,`applied`,`conflict`],e.state)||!Array.isArray(e.files))return;let t=e.files.filter(e=>U(e)&&W(e.mediaType)&&Ne(e.bytes)&&W(e.hash));return{state:e.state,files:t,...Array.isArray(e.keep)?{keep:e.keep.filter(W)}:{},...W(e.entry)?{entry:e.entry}:{},...W(e.appliedRevisionID)?{appliedRevisionID:e.appliedRevisionID}:{}}}function Re(e){if(!U(e)||!W(e.id)||!W(e.artifactID)||!W(e.baseRevisionID)||!Ne(e.revision)||!Pe(Ae,e.state)||!Pe(ke,e.kind)||typeof e.instruction!=`string`||!W(e.by)||!Ne(e.createdAt)||!Ne(e.updatedAt))return;let t=Ie(e.handler);if(!t)return;let n=Le(e.proposal),{proposal:r,...i}=e;return{...i,handler:t,...n?{proposal:n}:{},...typeof e.answer==`string`?{answer:e.answer}:{},...typeof e.error==`string`?{error:e.error}:{}}}function ze(e){let t=U(e)?e:{};return{items:Array.isArray(t.items)?t.items.flatMap(e=>Re(e)??[]):[],...W(t.cursor)?{cursor:t.cursor}:{}}}function Be(e){let t=e?.[De];return t?t.split(`,`).map(e=>e.trim()).filter(N).slice(0,Oe):[]}var Ve=e=>e.state===`queued`||e.state===`running`;function G(e,t){return e.kind===`session`?e.address.startsWith(`@`)?e.address:t?.(e.sessionID??e.address)??(e.sessionID?t?.(e.address):void 0)??e.address:`New ${e.harness||`agent`} session on ${e.machine} · ${Fe(e.repo)}`}function He(e,t){let n=(e,t)=>({tone:e,active:!1,text:t});if(e.state===`failed`)return n(`failed`,`Failed: ${e.error||`no reason given`}`);if(e.state===`cancelled`)return n(`cancelled`,`Cancelled`);if(e.state===`succeeded`){if(e.kind!==`propose-edit`)return n(`done`,`Answered`);let t=e.proposal;return t?t.state===`applied`?n(`done`,`Edit accepted`):t.state===`conflict`?n(`failed`,`Not accepted: the artifact changed after this edit was proposed. Request it again on the latest revision.`):n(`done`,`Proposed an edit: review it below`):n(`done`,`Finished without a proposal`)}let r=e.handler,i=(e,t)=>({tone:e,active:!0,text:t});switch(r.status){case`offline`:return r.kind===`session`?i(`offline`,`Waiting: ${G(r,t)} is offline. The request is queued and is delivered when it reconnects.`):i(`offline`,`Waiting: ${r.machine} is offline. The request is queued until it's back.`);case`starting`:return i(`waiting`,r.kind===`session`?`Sending to ${G(r,t)}…`:`Starting a session on ${r.machine}…`);case`waiting`:return i(`waiting`,r.kind===`launch`?`Waiting for a session on ${r.machine}${r.reason?`: ${r.reason}`:``}`:`Waiting for ${G(r,t)}`);case`ready`:return e.state===`running`?i(`running`,`Working: ${G(r,t)} has the request.`):i(`waiting`,`Request delivered to ${G(r,t)}; waiting for it to pick it up.`);default:return i(`waiting`,`The handler ${r.status===`ended`?`ended`:`failed`}; the job finishes shortly.`)}}function Ue(e){return e===`user`?`You`:e.startsWith(`app:`)?`App ${e.slice(4)}`:`Session ${e}`}function We(e,t){let n=t.get(e);return n===void 0?`revision ${e.slice(-6)}`:`revision ${n}`}function Ge(e){let t=[],n=[];for(let r of e.sessions){if(r.archived)continue;let e=r.name?.replace(/^@+/,``)?`@${r.name.replace(/^@+/,``)}`:void 0,i=r.title||r.id,a=[r.machineName,r.harness].filter(Boolean).join(` · `),o=`${r.online?`●`:`○`} ${e?`${e} — ${i} · ${a}`:`${i} · ${a}`}${r.online?``:` (offline: queued until it reconnects)`}`,s={key:`session:${r.id}`,kind:`session`,label:o,display:e??i,online:r.online,handler:{session:r.id},search:[e,r.title,r.id,r.machineName,r.harness].filter(Boolean).join(` `).toLowerCase()};e?t.push({...s,sortName:e.toLowerCase()}):n.push(s)}t.sort((e,t)=>e.sortName<t.sortName?-1:+(e.sortName>t.sortName));let r=[...t.map(({sortName:e,...t})=>t),...n];for(let t of e.bindings){let n=e.machines.find(e=>e.id===t.machineID),i=n?.online??!1,a=`New session in ${t.label||Fe(t.repo)} on ${n?.name??t.machineID}`;r.push({key:`launch:${t.id}`,kind:`launch`,label:`${a}${i?``:` (machine offline: waits for it)`}`,display:a,online:i,handler:{launch:{machine:t.machineID,repo:t.repo}},search:[a,t.repo,t.machineID].join(` `).toLowerCase()})}return r}function Ke(e,t,n){let r=t.trim().toLowerCase();return r?e.filter(e=>e.key===n||e.search.includes(r)):[...e]}function qe(e){let t=new Map;for(let n of e)n.kind===`session`&&`session`in n.handler&&n.handler.session&&t.set(n.handler.session,n.display);return t}function Je(e){return e.kind===`session`?{session:e.sessionID??e.address}:{launch:{machine:e.machine,repo:e.repo,...e.harness?{harness:e.harness}:{},...e.model?{model:e.model}:{},...e.agent?{agent:e.agent}:{},mode:e.mode}}}function Ye(e){let t=e=>({ok:!1,problem:e});if(!e.handler)return t(`Choose who handles this request.`);let n=e.instruction.trim();if(e.kind===`run-action`){let r=e.actions??[];if(r.length===0)return t(`This artifact declares no AI actions.`);if(!e.action||!r.includes(e.action))return t(`Choose one of the artifact's actions.`);n||=`Run the ${e.action} action.`}if(!n)return t(e.kind===`ask`?`Write a question.`:`Describe the edit.`);if(n.length>L.aiInstructionChars)return t(`Keep it under ${L.aiInstructionChars.toLocaleString(`en-US`)} characters.`);if(Me.test(n))return t(`Use plain text (no control characters).`);let r={kind:e.kind,baseRevisionID:e.baseRevisionID,instruction:n,handler:e.handler};return e.kind===`run-action`?{ok:!0,request:{kind:`run-action`,action:e.action,baseRevisionID:e.baseRevisionID,instruction:n,handler:e.handler}}:{ok:!0,request:e.kind===`propose-edit`?{...r,apply:`manual`}:r}}var Xe=4e6,Ze=3,Qe=e=>{let t=e.replace(/\r\n?/g,`
|
|
2
2
|
`).split(`
|
|
3
3
|
`);return t.length>1&&t[t.length-1]===``&&t.pop(),e===``?[]:t};function $e(e,t){let n=Qe(e),r=Qe(t),i=[{type:`meta`,text:`Too large to compare line by line.`}];if(n.length+r.length>1e4)return i;let a=0;for(;a<n.length&&a<r.length&&n[a]===r[a];)a++;let o=n.length,s=r.length;for(;o>a&&s>a&&n[o-1]===r[s-1];)o--,s--;let c=o-a,l=s-a;if((c+1)*(l+1)>Xe)return i;let u=l+1,d=new Uint32Array((c+1)*u);for(let e=c-1;e>=0;e--)for(let t=l-1;t>=0;t--)d[e*u+t]=n[a+e]===r[a+t]?d[(e+1)*u+t+1]+1:Math.max(d[(e+1)*u+t],d[e*u+t+1]);let f=[];for(let e=0;e<a;e++)f.push({type:`context`,text:n[e],oldNo:e+1,newNo:e+1});let p=0,m=0,h=[],g=[],_=()=>{f.push(...h,...g),h=[],g=[]};for(;p<c||m<l;)p<c&&m<l&&n[a+p]===r[a+m]?(_(),f.push({type:`context`,text:n[a+p],oldNo:a+p+1,newNo:a+m+1}),p++,m++):m<l&&(p===c||d[p*u+m+1]>=d[(p+1)*u+m])?(g.push({type:`add`,text:r[a+m],newNo:a+m+1}),m++):(h.push({type:`del`,text:n[a+p],oldNo:a+p+1}),p++);_();for(let e=0;e<n.length-o;e++)f.push({type:`context`,text:n[o+e],oldNo:o+e+1,newNo:s+e+1});let v=new Uint8Array(f.length);f.forEach((e,t)=>{if(e.type!==`context`)for(let e=Math.max(0,t-Ze);e<=Math.min(f.length-1,t+Ze);e++)v[e]=1});let y=[],b=0;for(;b<f.length;){if(!v[b]){b++;continue}let e=b;for(;e<f.length&&v[e];)e++;let t=f.slice(b,e),n=t.find(e=>e.oldNo!==void 0)?.oldNo??0,r=t.find(e=>e.newNo!==void 0)?.newNo??0,i=t.filter(e=>e.type!==`add`).length,a=t.filter(e=>e.type!==`del`).length;y.push({type:`hunk`,text:`@@ -${n},${i} +${r},${a} @@`});for(let e of t)y.push(e);b=e}return y}function et(e,t){if(!(t.files.some(e=>e.path!==void 0)||t.keep.length>0||e.some(e=>e.path!==void 0))){let n=e[0];return t.files.map(e=>({change:n?n.hash===e.hash?`unchanged`:`changed`:`added`,mediaType:e.mediaType,bytes:e.bytes,...n?{baseBytes:n.bytes}:{}}))}let n=new Map(e.map(e=>[e.path??``,e])),r=[],i=new Set;for(let e of t.files){let t=e.path??``;i.add(t);let a=n.get(t);r.push({path:t,change:a?a.hash===e.hash?`unchanged`:`changed`:`added`,mediaType:e.mediaType,bytes:e.bytes,...a?{baseBytes:a.bytes}:{}})}for(let e of t.keep){if(i.has(e))continue;i.add(e);let t=n.get(e);r.push({path:e,change:`kept`,mediaType:t?.mediaType??``,bytes:t?.bytes??0,...t?{baseBytes:t.bytes}:{}})}for(let t of e){let e=t.path??``;i.has(e)||r.push({path:e,change:`removed`,mediaType:t.mediaType,bytes:0,baseBytes:t.bytes})}return r}function K(e){let t=U(e)||e instanceof Error?e:{},n=Ne(t.status)?t.status:0,r=typeof t.code==`string`?t.code:``,i=typeof t.message==`string`?t.message:String(e),a=U(t.details)?t.details:{};if(n===422||r===`unsupported`){let e=typeof a.feature==`string`?a.feature:``;return e===`artifactActions`?{kind:`unsupported`,text:`This artifact declares no AI actions.`}:!e||e===`artifacts`?{kind:`unsupported`,text:`AI requests aren't supported on this hub.`}:{kind:`unsupported`,text:`Not supported on this hub (${e}): ${i}`}}return n===404||r===`not_found`?{kind:`unavailable`,text:`AI requests aren't available for this artifact (it doesn't exist, or you can't see it).`}:n===401||n===403||r===`forbidden`?{kind:`forbidden`,text:`Not allowed: ${i}`}:n===409||r===`conflict`?{kind:`conflict`,text:i}:n===429||r===`rate_limited`?{kind:`rate_limited`,text:`Too many AI requests are running (on this artifact or for you). Wait for one to finish.`}:n===503||r===`offline`?{kind:`offline`,text:`Offline: ${i}`}:n===400||r===`invalid`?{kind:`invalid`,text:i}:{kind:`error`,text:i}}function tt(e){return e.proposal?.state===`conflict`?`stale-base`:`job-changed`}function nt(e){return Math.min(1e4,Math.round(1500*1.5**Math.max(0,e)))}var rt={set(e,t){let n=setTimeout(e,t);return()=>clearTimeout(n)}},it=()=>globalThis.crypto?.randomUUID?.()??`${Date.now()}-${Math.random().toString(36).slice(2)}`,at=e=>!(e&&typeof e==`object`&&typeof e.status==`number`);function ot(e){let{source:t,artifactID:n,onState:r}=e,i=e.timers??rt,a=e.key??it,o={status:`loading`,actions:[],revisionNumbers:new Map,jobs:[],more:!1,loadingMore:!1,handlers:[],sending:!1,pending:{},notices:{},reviews:{}},s,c,l=0,u=!1,d,f=e=>{u||(o={...o,...e},r(o))},p=e=>o.jobs.find(t=>t.id===e),m=e=>f({jobs:o.jobs.some(t=>t.id===e.id)?o.jobs.map(t=>t.id===e.id?e:t):[e,...o.jobs]}),h=(e,t)=>{let{[e]:n,...r}=o.notices;f({notices:t?{...r,[e]:t}:r})},g=(e,t)=>{let{[e]:n,...r}=o.pending;f({pending:t?{...r,[e]:t}:r})};async function _(){let[e,r]=await Promise.all([t.artifact(n),t.revisionNumbers(n).catch(()=>o.revisionNumbers)]);f({artifact:e,actions:Be(e.metadata),revisionNumbers:r})}function v(){c?.(),c=void 0,u||!o.jobs.some(Ve)?l=0:c=i.set(()=>{c=void 0,x()},nt(l))}async function y(){f({status:`loading`});try{let[,e]=await Promise.all([_(),t.jobs(n)]);s=e.cursor,f({status:`ready`,jobs:e.items,more:!!e.cursor,error:void 0})}catch(e){f({status:`error`,error:K(e)});return}v(),await b()}async function b(){if(!u&&o.artifact)try{f({handlers:await t.handlers(o.artifact),handlersError:void 0})}catch(e){f({handlersError:K(e).text})}}async function x(){if(!(u||o.status!==`ready`)){try{let e=await t.jobs(n),r=new Map(o.jobs.map(e=>[e.id,e])),i=new Set(e.items.map(e=>e.id)),a=e.items.some(e=>r.get(e.id)?.revision!==e.revision),s=e.items.some(e=>!Ve(e)&&r.get(e.id)&&Ve(r.get(e.id)));f({jobs:[...e.items,...o.jobs.filter(e=>!i.has(e.id))]}),l=a?0:l+1,s&&await _().catch(()=>void 0)}catch{l++}v()}}async function S(){if(s&&!o.loadingMore){f({loadingMore:!0});try{let e=await t.jobs(n,s);s=e.cursor;let r=new Set(o.jobs.map(e=>e.id));f({jobs:[...o.jobs,...e.items.filter(e=>!r.has(e.id))],more:!!e.cursor,loadingMore:!1})}catch(e){f({loadingMore:!1,sendError:K(e)})}}}async function C(e){let r=JSON.stringify(e),i=d?.body===r?d.key:a();try{let r=await t.create(n,e,i);return d=void 0,m(r),f({sending:!1,sendError:void 0}),l=0,v(),r}catch(e){d=at(e)?{body:r,key:i}:void 0;let t=K(e);f({sending:!1,sendError:t}),t.kind===`conflict`&&await _().catch(()=>void 0);return}}async function w(e){return await T(e)!==void 0}async function T(e){if(o.sending||!o.artifact)return;if(e.baseRevisionID!==void 0&&e.baseRevisionID!==o.artifact.latestRevisionID){f({sendError:{kind:`conflict`,text:`The artifact changed since that view opened: the request was not sent.`}});return}let t=o.handlers.find(t=>t.key===e.handlerKey)?.handler,n=Ye({kind:e.kind,instruction:e.instruction,baseRevisionID:o.artifact.latestRevisionID,...t?{handler:t}:{},...e.action?{action:e.action}:{},actions:o.actions});if(!n.ok)f({sendError:{kind:`invalid`,text:n.problem}});else return f({sending:!0,sendError:void 0}),C(n.request)}async function E(e){try{let r=await t.job(n,e);return m(r),r}catch{return}}async function D(e){let r=p(e);if(!r||o.pending[e]||!Ve(r))return!1;g(e,`cancel`);try{return m(await t.cancel(n,e,r.revision)),h(e,void 0),!0}catch(t){let n=K(t),r=n.kind===`conflict`?await E(e):void 0;return h(e,{kind:n.kind===`conflict`?`conflict`:`error`,text:r&&!Ve(r)?`Not cancelled: it already ${r.state===`succeeded`?`finished`:r.state}.`:n.text}),!1}finally{g(e,void 0),v()}}async function O(e){let r=p(e);if(!r||o.pending[e]||r.kind!==`propose-edit`||r.state!==`succeeded`||r.proposal?.state!==`pending`)return!1;g(e,`apply`);try{let i=await t.apply(n,e,r.revision);m(i.job),await _().catch(()=>void 0);let a=i.revisionNumber??o.revisionNumbers.get(i.revisionID);return h(e,{kind:`info`,text:a===void 0?`Accepted as a new revision.`:`Accepted as revision ${a}.`}),!0}catch(t){let n=K(t);if(n.kind!==`conflict`)return h(e,{kind:`error`,text:n.text}),!1;let[r]=await Promise.all([E(e),_().catch(()=>void 0)]);if(r&&tt(r)===`stale-base`){let t=o.artifact?We(o.artifact.latestRevisionID,o.revisionNumbers):`a newer revision`;h(e,{kind:`conflict`,stale:!0,text:`Not accepted: the artifact changed since this edit was proposed (it is at ${t} now). Nothing was changed. Request the edit again on the latest revision.`})}else h(e,{kind:`conflict`,stale:!1,text:`This request changed since you read it. Review it again, then Accept.`});return!1}finally{g(e,void 0)}}async function k(e){let t=p(e);if(!t||o.pending[e]||o.sending||t.kind!==`propose-edit`)return!1;g(e,`rerequest`),f({sending:!0,sendError:void 0});try{await _();let n=!!await C({kind:`propose-edit`,baseRevisionID:o.artifact.latestRevisionID,instruction:t.instruction,handler:Je(t.handler),apply:`manual`});return n&&h(e,{kind:`info`,text:`Requested again on the latest revision.`}),n}catch(e){return f({sending:!1,sendError:K(e)}),!1}finally{g(e,void 0)}}async function A(e){let r=p(e);if(r?.proposal){f({reviews:{...o.reviews,[e]:{status:`loading`}}});try{let[i,a]=await Promise.all([t.proposal(n,e),t.revisionFiles(n,r.baseRevisionID)]),s=et(a,{files:i.files,keep:i.keep}),c=a.length<=1&&i.files.every(e=>e.path===void 0),l=i.files.map(e=>{let t=c?a[0]:a.find(t=>t.path===e.path),n=e.path===void 0?{}:{path:e.path};return e.omitted===`binary`?{...n,note:`Binary file: not compared.`}:e.omitted===`budget`||e.text===void 0?{...n,note:`Too large to show here.`}:t&&t.text===void 0?{...n,note:t.omitted===`binary`?`Replaces a binary file.`:`The current file is too large to compare.`}:t&&t.hash===e.hash?{...n,note:`Unchanged.`}:{...n,lines:$e(t?.text??``,e.text)}});f({reviews:{...o.reviews,[e]:{status:`ready`,changes:s,diffs:l,truncated:i.truncated}}})}catch(t){f({reviews:{...o.reviews,[e]:{status:`error`,text:K(t).text}}})}}}return{state:()=>o,load:y,refresh:x,more:S,request:w,requestJob:T,cancel:D,accept:O,rerequest:k,review:A,reloadHandlers:b,dispose(){c?.(),c=void 0,u=!0}}}var st={markdown:`Markdown`,code:`Code`,image:`Image`,table:`Table`,chart:`Chart`,dashboard:`Dashboard`,html:`HTML`,app:`App`,file:`File`};function ct(e){let t=[[]];for(let n of e)n.text.split(`
|
|
4
4
|
`).forEach((e,r)=>{r>0&&t.push([]),e&&t[t.length-1].push(n.kind?{text:e,kind:n.kind}:{text:e})});return t.length>1&&t[t.length-1].length===0&&t.pop(),t}var lt={ts:`TypeScript`,tsx:`TypeScript (TSX)`,js:`JavaScript`,jsx:`JavaScript (JSX)`,python:`Python`,go:`Go`,rust:`Rust`,bash:`Shell`,json:`JSON`,css:`CSS`,html:`HTML`,markdown:`Markdown`,yaml:`YAML`,toml:`TOML`,sql:`SQL`,java:`Java`,c:`C`,cpp:`C++`,ruby:`Ruby`,swift:`Swift`,kotlin:`Kotlin`,diff:`Diff`,csv:`CSV`};function ut(e,t){return{name:e?.path?.split(`/`).pop()||`Untitled`,language:t?lt[t]??t.toUpperCase():`Plain text`}}function dt(e,t){let n=[{label:`Type`,value:st[e.kind]??e.kind},{label:`Visibility`,value:e.visibility===`workspace`?`Workspace members`:`Private (only you)`}];e.workspaceID&&n.push({label:`Workspace`,value:t.workspaceName??e.workspaceID}),n.push({label:`Created by`,value:e.createdBySessionID?re({kind:`session`,sessionID:e.createdBySessionID}):`You`}),n.push({label:`Created`,value:t.when(e.createdAt)},{label:`Updated`,value:t.when(e.updatedAt)}),e.state===`archived`&&n.push({label:`State`,value:`Archived (read only)`}),e.tags?.length&&n.push({label:`Tags`,value:e.tags.join(`, `)});let r=Be(e.metadata);r.length&&n.push({label:`AI actions`,value:r.join(`, `)});for(let[t,r]of Object.entries(e.metadata??{}))t!==`ai-actions`&&n.push({label:t,value:r});return n}var ft={ask:`ask`,edit:`propose-edit`,action:`run-action`};function pt(e,t){let n=/^\/([a-z]+)(?:\s+([\s\S]*))?$/.exec(e),r=n?ft[n[1]]:void 0;if(!n||!r)return{text:e};let i=n[2]??``;if(r!==`run-action`)return{kind:r,text:i};let a=/^(\S+)\s*([\s\S]*)$/.exec(i);return!a||!t.includes(a[1])?{text:e}:{kind:r,action:a[1],text:a[2]}}var mt=`hub.artifactAiHandler.v1`;function ht(e,t){try{e?.setItem(mt,t)}catch{}}function gt(e,t){let n=null;try{n=e?.getItem(`hub.artifactAiHandler.v1`)??null}catch{return}return n?t.find(e=>e.key===n):void 0}function _t(e){return[...e].reverse()}var vt=`hub.artifactAiPanel.v1`,q={min:280,max:640,default:380,share:.6};function yt(e,t){if(!Number.isFinite(e))return q.default;let n=Math.max(q.min,Math.min(q.max,Math.floor(t*q.share)));return Math.min(n,Math.max(q.min,Math.round(e)))}function bt(e){try{let t=JSON.parse(e?.getItem(`hub.artifactAiPanel.v1`)??`null`);if(!t||typeof t!=`object`)return{};let{open:n,width:r}=t;return{...typeof n==`boolean`?{open:n}:{},...typeof r==`number`&&Number.isFinite(r)?{width:r}:{}}}catch{return{}}}function xt(e,t){try{e?.setItem(vt,JSON.stringify(t))}catch{}}var St=w(`<input type=search class=aai-picker-search aria-label="Search handlers"placeholder=Find… title="Find a handler by @name, title or session ID">`),Ct=w(`<optgroup label="Your sessions">`),wt=w(`<optgroup label="New session in a workspace repo">`),Tt=w(`<span><select aria-label="Handled by"title="Who handles this request"><option value>`),Et=w(`<option>`),Dt=w(`<button type=button class=aai-recent>Use `);function Ot(){try{return window.localStorage}catch{return}}function kt(e){let[t,r]=c(``),i=()=>e.value?void 0:gt(Ot(),e.handlers),a=_(()=>Ke(e.handlers,t(),e.value)),u=()=>a().filter(e=>e.kind===`session`),d=()=>a().filter(e=>e.kind===`launch`),f=()=>!!t().trim()&&!a().some(t=>t.key!==e.value);return(()=>{var a=Tt(),c=a.firstChild,p=c.firstChild;return m(a,n(s,{get when(){return e.handlers.length>1},get children(){var n=St();return n.$$input=e=>r(e.currentTarget.value),n.addEventListener(`focus`,()=>e.onPick?.()),o(()=>n.value=t()),n}}),c),c.addEventListener(`change`,t=>e.onChange(t.currentTarget.value)),c.addEventListener(`focus`,()=>e.onPick?.()),m(p,()=>f()?`No handler matches the search`:`Choose a session…`),m(c,n(s,{get when(){return u().length},get children(){var t=Ct();return m(t,n(l,{get each(){return u()},children:t=>(()=>{var n=Et();return m(n,()=>t.label),o(()=>n.selected=t.key===e.value),o(()=>n.value=t.key),n})()})),t}}),null),m(c,n(s,{get when(){return d().length},get children(){var t=wt();return m(t,n(l,{get each(){return d()},children:t=>(()=>{var n=Et();return m(n,()=>t.label),o(()=>n.selected=t.key===e.value),o(()=>n.value=t.key),n})()})),t}}),null),m(a,n(s,{get when(){return i()},children:t=>(()=>{var n=Dt();return n.firstChild,n.$$click=()=>e.onChange(t().key),m(n,()=>t().display,null),o(e=>{var r=`Last used on this device: ${t().label}`,i=`Use ${t().display} (last used)`;return r!==e.e&&h(n,`title`,e.e=r),i!==e.t&&h(n,`aria-label`,e.t=i),e},{e:void 0,t:void 0}),n})()}),null),o(()=>b(a,`aai-picker ${e.class??``}`)),o(()=>c.value=e.value),a})()}O([`input`,`click`]);var At=`/api/agent/artifacts`,jt=50,Mt=262144,Nt=e=>e.startsWith(`text/`)||e===`application/json`||e===`image/svg+xml`,J=e=>typeof e==`object`&&!!e&&!Array.isArray(e),Pt=e=>new P(502,`The hub answered an unexpected ${e}`);function Ft(e){let t=J(e)&&J(e.artifact)?e.artifact:e;if(!J(t)||typeof t.id!=`string`||typeof t.latestRevisionID!=`string`||typeof t.revision!=`number`)throw Pt(`artifact`);return{id:t.id,title:typeof t.title==`string`?t.title:``,kind:typeof t.kind==`string`?t.kind:`file`,state:typeof t.state==`string`?t.state:`active`,latestRevisionID:t.latestRevisionID,revision:t.revision,...typeof t.workspaceID==`string`?{workspaceID:t.workspaceID}:{},...J(t.metadata)?{metadata:t.metadata}:{}}}var It=e=>{let t=Re(J(e)?e.job:void 0);if(!t)throw Pt(`job`);return t},Lt=async(e,t,n={})=>(await j(e,{method:`POST`,json:t,headers:n})).json();async function Rt(){try{let e=await F(`/api/agent/who`),t=J(e)&&Array.isArray(e.sessions)?e.sessions:[];return new Map(t.flatMap(e=>J(e)&&typeof e.sessionID==`string`&&typeof e.name==`string`&&e.name?[[e.sessionID,e.name]]:[]))}catch{return new Map}}function zt(e){let t=e=>`${At}/${I(e)}/jobs`,n=(e,n,r=``)=>`${t(e)}/${I(n)}${r}`;return{artifact:async e=>Ft(await F(`${At}/${I(e)}`)),async revisionNumbers(e){let t=await F(`${At}/${I(e)}/revisions?limit=200`),n=J(t)&&Array.isArray(t.items)?t.items:[];return new Map(n.flatMap(e=>J(e)&&typeof e.id==`string`&&typeof e.number==`number`?[[e.id,e.number]]:[]))},jobs:async(e,n)=>ze(await F(`${t(e)}?limit=${jt}${n?`&cursor=${I(n)}`:``}`)),job:async(e,t)=>It(await F(n(e,t))),create:async(e,n,r)=>It(await Lt(t(e),n,{"idempotency-key":r})),cancel:async(e,t,r)=>It(await Lt(n(e,t,`/cancel`),{expectedRevision:r})),async apply(e,t,r){let i=await Lt(n(e,t,`/apply`),{expectedRevision:r}),a=J(i)&&J(i.revision)?i.revision:{};if(typeof a.id!=`string`)throw Pt(`revision`);return{job:It(i),revisionID:a.id,...typeof a.number==`number`?{revisionNumber:a.number}:{}}},proposal:async(e,t)=>{let r=await F(n(e,t,`/proposal`));if(!J(r)||!Array.isArray(r.files))throw Pt(`proposal`);return{...r,keep:Array.isArray(r.keep)?r.keep:[],truncated:r.truncated===!0}},async revisionFiles(e,t){let n=await F(`${At}/${I(e)}/revisions/${I(t)}`),r=J(n)&&J(n.revision)&&Array.isArray(n.revision.blobs)?n.revision.blobs:[],i=Mt,a=[];for(let n of r){if(!J(n)||typeof n.hash!=`string`||typeof n.mediaType!=`string`||typeof n.bytes!=`number`)continue;let r={...typeof n.path==`string`?{path:n.path}:{},hash:n.hash,mediaType:n.mediaType,bytes:n.bytes};Nt(n.mediaType)?n.bytes>i?a.push({...r,omitted:`budget`}):(i-=n.bytes,a.push({...r,text:await(await j(`${At}/${I(e)}/revisions/${I(t)}/blobs/${I(n.hash)}`)).text()})):a.push({...r,omitted:`binary`})}return a},async handlers(t){let[n,r]=await Promise.all([(async()=>{if(!t.workspaceID)return[];try{let e=await F(`/api/agent/workspaces/${I(t.workspaceID)}/bindings`);return(J(e)&&Array.isArray(e.bindings)?e.bindings:[]).flatMap(e=>J(e)&&typeof e.id==`string`&&typeof e.machineID==`string`&&typeof e.repo==`string`?[{id:e.id,machineID:e.machineID,repo:e.repo,...typeof e.label==`string`?{label:e.label}:{}}]:[])}catch{return[]}})(),Rt()]);return Ge({sessions:e.sessions().map(e=>r.get(e.id)?{...e,name:r.get(e.id)}:e),bindings:n,machines:e.machines()})}}}var Bt=w(`<span>an unknown revision`),Vt=w(`<button type=button class=aai-link>`),Ht=w(`<span>`),Ut=w(`<strong class=aai-older>You are viewing an older revision.`),Wt=w(`<p class=aai-sub>Requests use the latest revision (<!>).`),Gt=w(`<p class=aai-note role=status>Loading AI requests…`),Kt=w(`<button type=button class="btn small aai-older-btn">Load older requests`),qt=w(`<ol class=aai-jobs aria-label="AI request history">`),Jt=w(`<p class=aai-note>Could not list handlers: `),Yt=w(`<p class=aai-note role=status>`),Xt=w(`<div class=aai-confirm role=group aria-label="Confirm action"><p>Run <strong></strong> on <!>, handled by <strong></strong>? It gets a read-only copy of this revision; its answer shows here and nothing is changed.</p><div class=aai-actions><button type=button class="btn primary small">Confirm and run</button><button type=button class="btn small">Back`),Zt=w(`<select class=aai-action aria-label="Declared action"><option value>Choose an action…`),Qt=w(`<form class=aai-composer aria-label="New AI request"><div class=aai-box><div class=aai-box-top><div class=aai-seg role=radiogroup aria-label="Request type"></div></div><textarea class=aai-input rows=2></textarea><div class=aai-box-foot><span class=aai-hint>Ctrl+Enter</span><button type=submit class=aai-send><span class=aai-send-text>`),$t=w(`<section class=aai><header class=aai-head><span class=aai-mark aria-hidden=true></span><div class=aai-head-text><h2 class=aai-title>AI requests</h2></div></header><div class=aai-scroll>`),en=w(`<button type=button class="btn small">Retry`),tn=w(`<p class=aai-note> `),nn=w(`<kbd>/action `),rn=w(`<div class=aai-empty><p><strong>Ask about this artifact, or describe an edit.</strong></p><p>The session you choose gets a read-only copy. Nothing changes until you accept an edit.</p><p class=aai-tips>Tip: start with <kbd>/ask</kbd>, <kbd>/edit</kbd>.`),an=w(`<p class="aai-note err"role=alert>`),on=w(`<label class=aai-seg-item><input type=radio>`),sn=w(`<option>`),cn=w(`<span class=aai-dot aria-hidden=true>`),ln=w(`<div class=aai-answer>`),un=w(`<p class="aai-note warn"role=status>The artifact changed since this edit was proposed: accepting it will be refused. Request it again on the latest revision.`),dn=w(`<button type=button class="btn primary small">`),fn=w(`<button type=button class="btn small">Request again on the latest revision`),pn=w(`<button type=button class="btn small">`),mn=w(`<button type=button class="btn small">Continue in side chat`),hn=w(`<li class=aai-job><div class=aai-q><div class=aai-q-meta><span class=aai-badge></span><span class=aai-meta> · </span></div><p class=aai-instruction></p></div><div class=aai-a><p class="aai-meta aai-prov">On <!> · handled by </p><p role=status aria-live=polite></p><div class=aai-actions>`),gn=w(`<div class=aai-proposal>`),_n=w(`<button type=button class="btn small">Review proposed edit (<!> file<!>)`),vn=w(`<p class=aai-note role=status>Loading the proposal…`),yn=w(`<p class="aai-note err">Could not load the proposal: `),bn=w(`<ul class=aai-files aria-label="Proposed files">`),xn=w(`<p class=aai-note>Some files are too large to show here.`),Sn=w(`<li><span class=aai-file-change></span> <code>`),Cn=w(`<p class=aai-meta><code>`),wn=w(`<table class=diff><tbody>`),Tn=w(`<div class=aai-diff>`),En=w(`<p class=aai-note>`),Dn=w(`<tr><td class=ln></td><td class=ln></td><td class=code-line><span class=sign>`),On=w(`<p>`),kn={ask:`Ask`,"propose-edit":`Edit`,"run-action":`Action`},An={ask:`Ask a question (answer only)`,"propose-edit":`Propose an edit you review before it applies`,"run-action":`Run one of the artifact's declared actions`},jn=e=>e.kind===`ask`?`Question`:e.kind===`propose-edit`?`Edit`:`Action: ${e.action??`?`}`;function Mn(){return zt({sessions:()=>{let e=new Map;for(let t of ge.machines??[])for(let n of t.processes)for(let t of n.sessions)e.set(t.id,n.harness||`opencode`);return be().filter(e=>!e.hidden&&!e.archived).map(t=>({id:t.id,title:t.title,machineName:t.machineName,online:t.machineOnline,...e.get(t.id)?{harness:e.get(t.id)}:{}}))},machines:()=>(ge.machines??[]).map(e=>({id:e.id,name:e.name,online:!!e.online}))})}function Nn(i){let[u,d]=c(),f=!i.controller,v=i.controller?.ai??ot({source:i.source??Mn(),artifactID:i.artifactID,onState:d});E(()=>f&&void v.load()),x(()=>f&&v.dispose());let y=()=>(f?u():i.controller.state())??v.state(),b=()=>y().revisionNumbers,S=()=>y().artifact?.latestRevisionID,[C,w]=c(`ask`),[D,O]=c(``),[k,A]=c(``),[j,M]=c(``),[N,P]=c(!1),F,I,ee,L=_(()=>y().handlers.find(e=>e.key===k())),R=()=>C()===`ask`?`Ask`:C()===`propose-edit`?`Propose edit`:j()?`Run ${j()}…`:`Run action…`,te=_(()=>_t(y().jobs));g(e(()=>[y().jobs.length,i.tailCount??0],()=>queueMicrotask(()=>ee&&(ee.scrollTop=ee.scrollHeight))));async function z(){let e=k(),t=await v.request({kind:C(),instruction:D(),...e?{handlerKey:e}:{},...C()===`run-action`&&j()?{action:j()}:{}});P(!1),t&&(O(``),ht(Ot(),e))}function ne(e){e?.preventDefault(),!y().sending&&(C()===`run-action`&&j()&&k()?(P(!0),queueMicrotask(()=>F?.focus())):z())}function B(e){let t=pt(e,y().actions);t.kind&&/\s$/.test(e)?(w(t.kind),t.action&&M(t.action),P(!1),O(t.text),I&&(I.value=t.text)):O(e)}async function re(e){if(await v.accept(e.id)){let e=S();e&&i.onRevised?.(e)}}let V=(e,t=``)=>n(s,{when:e,get fallback(){return(()=>{var e=Bt(),n=e.firstChild;return m(e,t,n),e})()},children:e=>n(s,{get when(){return i.onShowRevision},get fallback(){return(()=>{var n=Ht();return m(n,()=>t+We(e(),b())),n})()},get children(){var n=Vt();return n.$$click=()=>i.onShowRevision?.(e()),m(n,()=>t+We(e(),b())),n}})});return(()=>{var e=$t(),c=e.firstChild,u=c.firstChild,d=u.nextSibling,f=d.firstChild,g=c.nextSibling;m(u,n(T,{name:`spark`})),m(d,n(s,{get when(){return y().status===`ready`},get children(){var e=Wt(),t=e.firstChild.nextSibling;return t.nextSibling,m(e,()=>V(S()),t),m(e,n(s,{get when(){return r(()=>!!(i.revisionID&&S()))()&&i.revisionID!==S()},get children(){return[` `,Ut()]}}),null),e}}),null),m(c,()=>i.headerActions,null);var _=ee;return typeof _==`function`?t(_,g):ee=g,m(g,n(p,{get children(){return[n(a,{get when(){return y().status===`loading`},get children(){return Gt()}}),n(a,{get when(){return r(()=>y().status===`error`)()&&y().error},children:e=>(()=>{var t=tn(),r=t.firstChild;return m(t,()=>e().text,r),m(t,n(s,{get when(){return e().kind===`error`||e().kind===`offline`},get children(){var e=en();return e.$$click=()=>void v.load(),e}}),null),o(n=>{var r=e().kind===`error`,i=e().kind===`error`?`alert`:`status`;return r!==n.e&&t.classList.toggle(`err`,n.e=r),i!==n.t&&h(t,`role`,n.t=i),n},{e:void 0,t:void 0}),t})()}),n(a,{get when(){return y().status===`ready`},get children(){return[n(s,{get when(){return y().more},get children(){var e=Kt();return e.$$click=()=>void v.more(),o(t=>{var n=y().loadingMore,r=y().loadingMore;return n!==t.e&&(e.disabled=t.e=n),r!==t.t&&h(e,`aria-busy`,t.t=r),t},{e:void 0,t:void 0}),e}}),n(s,{get when(){return y().jobs.length},get fallback(){return(()=>{var e=rn(),t=e.firstChild.nextSibling.nextSibling,r=t.firstChild.nextSibling.nextSibling.nextSibling.nextSibling;return m(t,n(s,{get when(){return y().actions.length},get children(){return[` `,`or `,(()=>{var e=nn();return e.firstChild,m(e,()=>y().actions[0],null),e})()]}}),r),e})()},get children(){var e=qt();return m(e,n(l,{get each(){return te()},children:e=>n(Pn,{job:e,get state(){return y()},revisionRef:V,onCancel:()=>void v.cancel(e.id),onReview:()=>void v.review(e.id),onAccept:()=>void re(e),onRerequest:()=>void v.rerequest(e.id),get onContinue(){return i.onContinueInSideChat}})})),e}})]}})]}}),null),m(g,()=>i.threadTail,null),m(e,n(s,{get when(){return y().status===`ready`},get children(){var e=Qt(),a=e.firstChild,c=a.firstChild,u=c.firstChild,d=c.nextSibling,f=d.nextSibling,p=f.firstChild,g=p.nextSibling,_=g.firstChild;e.addEventListener(`submit`,ne),m(e,n(s,{get when(){return y().sendError},children:e=>(()=>{var t=an();return m(t,()=>e().text),t})()}),a),m(e,n(s,{get when(){return y().handlersError},get children(){var e=Jt();return e.firstChild,m(e,()=>y().handlersError,null),e}}),a),m(e,n(s,{get when(){return r(()=>!!L())()&&!L().online},get children(){var e=Yt();return m(e,()=>L().kind===`session`?`That session is offline: the request is queued and delivered when it reconnects.`:`That machine is offline: the new session starts when it's back.`),e}}),a),m(e,n(s,{get when(){return N()},get children(){var e=Xt(),n=e.firstChild,r=n.firstChild.nextSibling,i=r.nextSibling.nextSibling,a=i.nextSibling.nextSibling,s=n.nextSibling.firstChild,c=s.nextSibling;e.$$keydown=e=>{e.key===`Escape`&&(e.preventDefault(),P(!1),I?.focus())},m(r,j),m(n,()=>We(S()??``,b()),i),m(a,()=>L()?.display),s.$$click=()=>void z();var l=F;return typeof l==`function`?t(l,s):F=s,c.$$click=()=>{P(!1),I?.focus()},o(()=>s.disabled=y().sending),e}}),a),m(u,n(l,{each:[`ask`,`propose-edit`,`run-action`],children:e=>{let t=()=>e===`run-action`&&y().actions.length===0;return(()=>{var n=on(),r=n.firstChild;return r.addEventListener(`change`,()=>{w(e),P(!1)}),r.value=e,m(n,()=>kn[e],null),o(a=>{var o=C()===e,s=!!t(),c=t()?`This artifact declares no AI actions`:An[e],l=`aai-mode-${i.artifactID}`,u=t();return o!==a.e&&n.classList.toggle(`on`,a.e=o),s!==a.t&&n.classList.toggle(`disabled`,a.t=s),c!==a.a&&h(n,`title`,a.a=c),l!==a.o&&h(r,`name`,a.o=l),u!==a.i&&(r.disabled=a.i=u),a},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0}),o(()=>r.checked=C()===e),n})()}})),m(c,n(s,{get when(){return C()===`run-action`},get children(){var e=Zt();return e.firstChild,e.addEventListener(`change`,e=>(M(e.currentTarget.value),P(!1))),m(e,n(l,{get each(){return y().actions},children:e=>(()=>{var t=sn();return t.value=e,m(t,e),t})()}),null),o(()=>e.value=j()),e}}),null),d.$$keydown=e=>{e.key===`Enter`&&(e.metaKey||e.ctrlKey)&&ne(e)},d.$$input=e=>B(e.currentTarget.value);var x=I;return typeof x==`function`?t(x,d):I=d,m(f,n(kt,{get handlers(){return y().handlers},get value(){return k()},onPick:()=>void v.reloadHandlers(),onChange:e=>(A(e),P(!1))}),p),m(g,n(T,{name:`send`}),_),m(_,(()=>{var e=r(()=>!!y().sending);return()=>e()?`Sending…`:R()})()),o(e=>{var t=C()===`ask`?`Question`:C()===`propose-edit`?`Describe the edit`:`Notes for the action (optional)`,n=C()===`ask`?`Ask about this artifact…`:C()===`propose-edit`?`Describe the edit, e.g. Add a rollback section`:`Notes for the action (optional)`,r=y().sending||N(),i=y().sending,a=y().sending?`Sending…`:R(),o=`${R()} (Ctrl+Enter)`;return t!==e.e&&h(d,`aria-label`,e.e=t),n!==e.t&&h(d,`placeholder`,e.t=n),r!==e.a&&(g.disabled=e.a=r),i!==e.o&&h(g,`aria-busy`,e.o=i),a!==e.i&&h(g,`aria-label`,e.i=a),o!==e.n&&h(g,`title`,e.n=o),e},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0,n:void 0}),o(()=>d.value=D()),e}}),null),o(t=>{var n=`aai-h-${i.artifactID}`,r=`aai-h-${i.artifactID}`;return n!==t.e&&h(e,`aria-labelledby`,t.e=n),r!==t.t&&h(f,`id`,t.t=r),t},{e:void 0,t:void 0}),e})()}function Pn(e){let t=()=>e.job,i=_(()=>qe(e.state.handlers)),c=e=>i().get(e)??be().find(t=>t.id===e)?.title,u=()=>He(t(),c),d=()=>e.state.pending[t().id],f=()=>e.state.notices[t().id],g=()=>e.state.reviews[t().id],v=()=>e.state.artifact?.latestRevisionID,y=()=>t().kind===`propose-edit`&&t().state===`succeeded`&&t().proposal?.state===`pending`,x=()=>y()&&v()!==void 0&&t().baseRevisionID!==v(),S=()=>t().kind===`propose-edit`&&(t().proposal?.state===`conflict`||x()||f()?.stale);return(()=>{var i=hn(),_=i.firstChild,v=_.firstChild,C=v.firstChild,w=C.nextSibling,T=w.firstChild,E=v.nextSibling,O=_.nextSibling,A=O.firstChild,j=A.firstChild.nextSibling;j.nextSibling;var M=A.nextSibling,N=M.nextSibling;return m(C,()=>jn(t())),m(w,()=>Ue(t().by),T),m(w,()=>D(t().createdAt),null),m(E,()=>t().instruction),m(A,()=>e.revisionRef(t().baseRevisionID),j),m(A,()=>G(t().handler,c),null),m(A,n(s,{get when(){return t().proposal?.appliedRevisionID},children:t=>[` · became `,r(()=>e.revisionRef(t()))]}),null),m(M,n(s,{get when(){return u().active},get children(){return cn()}}),null),m(M,()=>u().text,null),m(O,n(s,{get when(){return t().answer},get children(){var n=ln();return m(n,()=>t().answer),o(()=>h(n,`aria-label`,`Answer from ${G(t().handler,c)} on ${We(t().baseRevisionID,e.state.revisionNumbers)}`)),n}}),N),m(O,n(s,{get when(){return r(()=>!!(t().proposal&&t().proposal.state!==`applied`))()&&t().proposal},children:t=>(()=>{var i=gn();return m(i,n(s,{get when(){return g()},get fallback(){return(()=>{var n=_n(),i=n.firstChild.nextSibling,a=i.nextSibling.nextSibling;return a.nextSibling,k(n,`click`,e.onReview,!0),m(n,()=>t().files.length,i),m(n,()=>t().files.length===1?``:`s`,a),m(n,(()=>{var e=r(()=>!!t().keep?.length);return()=>e()?`, ${t().keep.length} kept`:``})(),a),n})()},children:e=>n(p,{get children(){return[n(a,{get when(){return e().status===`loading`},get children(){return vn()}}),n(a,{get when(){return r(()=>e().status===`error`)()&&e()},children:e=>(()=>{var t=yn();return t.firstChild,m(t,()=>e().text,null),t})()}),n(a,{get when(){return r(()=>e().status===`ready`)()&&e()},children:e=>{let t=()=>e();return[n(s,{get when(){return t().changes.some(e=>e.path!==void 0)},get children(){var e=bn();return m(e,n(l,{get each(){return t().changes},children:e=>(()=>{var t=Sn(),n=t.firstChild,r=n.nextSibling.nextSibling;return m(n,()=>e.change),m(r,()=>e.path),o(()=>b(t,`aai-file ${e.change}`)),t})()})),e}}),n(l,{get each(){return t().diffs},children:e=>(()=>{var t=Tn();return m(t,n(s,{get when(){return e.path},get children(){var t=Cn(),n=t.firstChild;return m(n,()=>e.path),t}}),null),m(t,n(s,{get when(){return e.lines},get fallback(){return(()=>{var t=En();return m(t,()=>e.note),t})()},get children(){var t=wn(),i=t.firstChild;return m(i,n(l,{get each(){return e.lines},children:e=>(()=>{var t=Dn(),n=t.firstChild,i=n.nextSibling,a=i.nextSibling,s=a.firstChild;return m(n,()=>e.oldNo??``),m(i,()=>e.newNo??``),m(s,(()=>{var t=r(()=>e.type===`add`);return()=>t()?`+`:e.type===`del`?`−`:` `})()),m(a,()=>e.text,null),o(()=>b(t,e.type)),t})()})),o(()=>h(t,`aria-label`,`Changes${e.path?` to ${e.path}`:``}`)),t}}),null),t})()}),n(s,{get when(){return t().truncated},get children(){return xn()}})]}})]}})})),i})()}),N),m(O,n(s,{get when(){return r(()=>!!x())()&&!f()},get children(){return un()}}),N),m(O,n(s,{get when(){return f()},children:e=>(()=>{var t=On();return m(t,()=>e().text),o(n=>{var r=`aai-note ${e().kind===`info`?`ok`:e().kind===`conflict`?`warn`:`err`}`,i=e().kind===`info`?`status`:`alert`;return r!==n.e&&b(t,n.e=r),i!==n.t&&h(t,`role`,n.t=i),n},{e:void 0,t:void 0}),t})()}),N),m(N,n(s,{get when(){return y()},get children(){var t=dn();return k(t,`click`,e.onAccept,!0),m(t,()=>d()===`apply`?`Accepting…`:`Accept as new revision`),o(e=>{var n=!!d(),r=d()===`apply`;return n!==e.e&&(t.disabled=e.e=n),r!==e.t&&h(t,`aria-busy`,e.t=r),e},{e:void 0,t:void 0}),t}}),null),m(N,n(s,{get when(){return S()},get children(){var t=fn();return k(t,`click`,e.onRerequest,!0),o(()=>t.disabled=!!d()||e.state.sending),t}}),null),m(N,n(s,{get when(){return Ve(t())},get children(){var t=pn();return k(t,`click`,e.onCancel,!0),m(t,()=>d()===`cancel`?`Cancelling…`:`Cancel`),o(e=>{var n=!!d(),r=d()===`cancel`;return n!==e.e&&(t.disabled=e.e=n),r!==e.t&&h(t,`aria-busy`,e.t=r),e},{e:void 0,t:void 0}),t}}),null),m(N,n(s,{get when(){return r(()=>!!e.onContinue)()&&t().handler.sessionID},get children(){var n=mn();return n.$$click=()=>e.onContinue?.(t()),n}}),null),o(e=>{var n=t().id,r=t().state,a=`${jn(t())}: ${t().instruction}`,o=`aai-status ${u().tone}`;return n!==e.e&&h(i,`data-job`,e.e=n),r!==e.t&&h(i,`data-state`,e.t=r),a!==e.a&&h(i,`aria-label`,e.a=a),o!==e.o&&b(M,e.o=o),e},{e:void 0,t:void 0,a:void 0,o:void 0}),i})()}O([`click`,`keydown`,`input`]);var Y={rows:1e3,columns:50,cellChars:2e3};function Fn(e){let t=e.length>Y.columns,n=e.slice(0,Y.columns).map(e=>i(e)),r=[];function i(e){return typeof e==`string`&&e.length>Y.cellChars?(t=!0,e.slice(0,Y.cellChars)):e}return{add(e,a){return r.length>=Y.rows?(t=!0,!1):(r.push(n.map((t,n)=>i(n<e.length?e[n]:a))),!0)},done:()=>({columns:n,rows:r,truncated:t})}}function In(e){let t=[],n=[],r=``,i=!1,a=0,o=()=>{n.push(r),r=``,(n.length!==1||n[0]!==``)&&t.push(n),n=[]};for(;a<e.length&&t.length<=Y.rows+1;){let t=e[a];i?t===`"`?e[a+1]===`"`?(r+=`"`,a++):i=!1:r+=t:t===`"`&&r===``?i=!0:t===`,`?(n.push(r),r=``):t===`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{A as e,B as t,C as n,F as r,G as i,I as a,J as o,R as s,S as c,T as l,U as u,W as d,et as f,k as p,tt as m,x as h}from"./ui-CFB4wQfF.js";import{C as g,S as _,T as v,c as y,s as b,v as x,w as S}from"./MediaImage-Ch3ryIdR.js";import{$ as C,A as w,M as T}from"./index-
|
|
1
|
+
import{A as e,B as t,C as n,F as r,G as i,I as a,J as o,R as s,S as c,T as l,U as u,W as d,et as f,k as p,tt as m,x as h}from"./ui-CFB4wQfF.js";import{C as g,S as _,T as v,c as y,s as b,v as x,w as S}from"./MediaImage-Ch3ryIdR.js";import{$ as C,A as w,M as T}from"./index-bX8B6NLo.js";import E from"./Lightbox-BlbivCkd.js";var D={images:!1,links:!1,linkRel:`noopener noreferrer`,fontSize:14};function O(e){let t=e.timeoutMs??1e4,n=Math.max(1,e.concurrency??2),r=Math.max(1,e.cacheSize??64),i=e.setTimer??((e,t)=>setTimeout(e,t)),a=e.clearTimer??(e=>clearTimeout(e)),o=e.failureTtlMs??6e4,s=e.now??Date.now,c=new Map,l=new Map,u=[],d=[],f=1,p=(e,t,n)=>{for(c.delete(e),c.set(e,{result:t,expires:n});c.size>r;)c.delete(c.keys().next().value)},m=e=>{let t=c.get(e);if(t?.expires!==void 0&&s()>=t.expires)c.delete(e);else return t?.result},h=(e,t,{cacheable:n,replace:r})=>{let i=e.job;i&&(a(i.timer),e.job=void 0,r&&(e.worker.onmessage=null,e.worker.onerror=null,e.worker.terminate(),d.splice(d.indexOf(e),1)),n&&p(i.key,t,n===`briefly`?s()+o:void 0),l.delete(i.key),i.resolve(t),b())},_=(e,t)=>{if(typeof t.svg!=`string`)return{status:`error`,message:typeof t.error==`string`&&t.error?t.error:`the renderer returned no SVG`};let n=g(t.svg,e.palette);return v(n)?{status:`unsafe`}:{status:`ok`,svg:n}},y=()=>{let t={worker:e.spawn()};return t.worker.onmessage=e=>{let n=e.data,r=t.job;r&&n&&n.id===r.id&&h(t,_(r,n),{cacheable:!0,replace:!1})},t.worker.onerror=e=>h(t,{status:`error`,message:e?.message||`the diagram worker failed`},{cacheable:!1,replace:!0}),d.push(t),t};function b(){for(;u.length;){let e=d.find(e=>!e.job)??(d.length<n?y():void 0);if(!e)return;let r=u.shift(),a=f++,o=r.palette.font,s={...r,id:a,timer:void 0};e.job=s,s.timer=i(()=>h(e,{status:`error`,message:`timed out after ${t/1e3} s`},{cacheable:`briefly`,replace:!0}),t),e.worker.postMessage({id:a,source:r.source,options:o?{...D,fontFamily:o}:{...D}})}}return{render(e,t,n){let r=x(e,t),i=m(r);if(i)return p(r,i,c.get(r).expires),Promise.resolve(i);let a=l.get(r);if(a)return a;let o=new Promise(t=>u.push({key:r,source:e,palette:n,resolve:t}));return l.set(r,o),b(),o},cached(e,t){return m(x(e,t))}}}var k=p(`<p class=diagram-note>`),A=p(`<div class="code-block diagram-frame"><div class=code-head><span class=code-lang></span><button type=button class=code-btn>Diagram</button><button type=button class=code-btn>Source</button><button type=button class=code-btn>`),j=p(`<div class=diagram-block>`),M=p(`<button type=button class=code-btn>Download SVG`),N=p(`<pre class=code><code>`),P=p(`<p class=diagram-loading>Drawing diagram…`),F=p(`<pre class="code diagram-pending"><code>`),I=p(`<button type=button class=diagram-open aria-label="Open the diagram full screen"title="Open full screen"><img alt=Diagram>`),L,R=()=>L??=O({spawn:()=>new Worker(new URL(`/assets/mermaid.worker-WDVRAh32.js`,``+import.meta.url),{type:`module`})}),z=matchMedia(`(prefers-color-scheme: dark)`),[B,V]=o(z.matches);z.addEventListener(`change`,e=>V(e.matches));var H=()=>C()===`system`?B()?`dark`:`light`:C(),U={bg:`--side`,fg:`--text`,muted:`--muted`,line:`--muted`,surface:`--raise`,"surface-alt":`--raise-2`,border:`--line-2`,accent:`--accent`,font:`--sans`,"font-mono":`--mono`};function W(){let e=getComputedStyle(document.documentElement),t={};for(let[n,r]of Object.entries(U)){let i=e.getPropertyValue(r).trim();i&&(t[n]=i)}return t}function G(e){let t=URL.createObjectURL(new Blob([e],{type:`image/svg+xml`})),n=document.createElement(`a`);n.href=t,n.download=`diagram.svg`,n.click(),setTimeout(()=>URL.revokeObjectURL(t),1e4)}function K(e){return[t(w,{get lang(){return e.lang},get text(){return e.text}}),(()=>{var t=k();return c(t,()=>e.note),t})()]}function q(p){let[h,g]=o(`diagram`),[v,x]=o(!1),[C,w]=o(),[D,O]=o(!1),[k,L]=o(!1),z=d(()=>p.text),B=d(()=>S(z())),V=d(()=>(H(),W())),U,q;f(()=>clearTimeout(q)),m(()=>{let e=new IntersectionObserver(e=>x(e.some(e=>e.isIntersecting)),{rootMargin:`200px 0px`});e.observe(U),f(()=>e.disconnect())}),u(()=>{if(B()||!v())return;let e=z(),t=V(),n=JSON.stringify(t),r=R().cached(e,n);if(r)return void w(r);let i=!0;f(()=>i=!1),R().render(e,n,t).then(e=>i&&w(e))});let J=()=>{let e=C();return e?.status===`ok`?e.svg:void 0},Y=y(),X;return m(()=>{if(!Y)return;let e=async()=>{let e=V(),t=R().cached(p.text,JSON.stringify(e))??await R().render(p.text,JSON.stringify(e),e);if(w(t),t.status!==`ok`)throw Error(`Couldn't draw this diagram`);return _(t.svg)},t=Y.register({kind:`diagram`,name:`Diagram`,mime:`image/svg+xml`,position:()=>b(U),available:()=>!B()&&(!C()||!!J()),load:async()=>J()?_(J()):e()});X=()=>Y.open(t.id),f(t.dispose)}),(()=>{var o=j(),u=U;return typeof u==`function`?e(u,o):U=o,c(o,t(s,{get children(){return[t(r,{get when(){return B()},get children(){return t(K,{get lang(){return p.lang},get text(){return p.text},note:`Diagram too large to draw`})}}),t(r,{get when(){return n(()=>C()?.status===`error`)()&&C()},children:e=>t(K,{get lang(){return p.lang},get text(){return p.text},get note(){return`Couldn't draw this diagram: ${e().message}`}})}),t(r,{get when(){return C()?.status===`unsafe`},get children(){return t(K,{get lang(){return p.lang},get text(){return p.text},note:`Diagram not shown: the drawing had active content in it.`})}}),t(r,{when:!0,get children(){return[(()=>{var e=A(),n=e.firstChild,r=n.firstChild,o=r.nextSibling,s=o.nextSibling,u=s.nextSibling;return c(r,()=>p.lang||`mermaid`),o.$$click=()=>g(`diagram`),s.$$click=()=>g(`source`),u.$$click=async()=>{await T(p.text)&&(L(!0),clearTimeout(q),q=setTimeout(()=>L(!1),1500))},c(u,()=>k()?`Copied`:`Copy`),c(n,t(a,{get when(){return J()},children:e=>(()=>{var t=M();return t.$$click=()=>G(e()),t})()}),null),c(e,t(a,{get when(){return h()===`diagram`},get fallback(){return(()=>{var e=N(),t=e.firstChild;return c(t,()=>p.text),i(()=>l(e,`data-lang`,p.lang||void 0)),e})()},get children(){return t(a,{get when(){return J()},get fallback(){return[P(),(()=>{var e=F(),t=e.firstChild;return c(t,()=>p.text),i(()=>l(e,`data-lang`,p.lang||void 0)),e})()]},children:e=>(()=>{var t=I(),n=t.firstChild;return t.$$click=()=>X?X():O(!0),i(()=>l(n,`src`,_(e()))),t})()})}}),null),i(e=>{var t=h()===`diagram`,n=h()===`source`,r=k()?`Copied`:`Copy diagram source`;return t!==e.e&&l(o,`aria-pressed`,e.e=t),n!==e.t&&l(s,`aria-pressed`,e.t=n),r!==e.a&&l(u,`aria-label`,e.a=r),e},{e:void 0,t:void 0,a:void 0}),e})(),t(E,{get open(){return n(()=>!!D())()&&!!J()},get items(){return[{key:`diagram`,name:`Diagram`,mime:`image/svg+xml`,src:_(J()??``)}]},index:0,onClose:()=>O(!1)})]}})]}})),o})()}h([`click`]);export{q as default};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ArtifactPage-
|
|
2
|
-
import{B as e,C as t,F as n,G as r,I as i,J as a,N as o,R as s,S as c,T as l,U as u,W as d,Z as f,k as p,tt as m,u as h,x as g,y as _}from"./ui-CFB4wQfF.js";import{o as v,s as y}from"./toast-CXTwTvDw.js";import{o as b,t as x}from"./artifacts-But87Jfo.js";import{t as S}from"./preload-helper-BLdHbfdN.js";import{nt as C,rt as w,t as T,tt as E}from"./index-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ArtifactPage-CsPjam8D.js","assets/ui-CFB4wQfF.js","assets/artifacts-But87Jfo.js","assets/toast-CXTwTvDw.js","assets/pane-layout-BW0RdX5a.js","assets/index-bX8B6NLo.js","assets/plural-Bspvoqfq.js","assets/MediaImage-Ch3ryIdR.js","assets/preload-helper-BLdHbfdN.js","assets/index-rW6LKQj1.css","assets/ArtifactWorkbench-DzKhMs3l.js","assets/source-C-De8L3s.js","assets/ArtifactWorkbench-9eTj1CeW.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{B as e,C as t,F as n,G as r,I as i,J as a,N as o,R as s,S as c,T as l,U as u,W as d,Z as f,k as p,tt as m,u as h,x as g,y as _}from"./ui-CFB4wQfF.js";import{o as v,s as y}from"./toast-CXTwTvDw.js";import{o as b,t as x}from"./artifacts-But87Jfo.js";import{t as S}from"./preload-helper-BLdHbfdN.js";import{nt as C,rt as w,t as T,tt as E}from"./index-bX8B6NLo.js";import{a as D,c as O,i as k,n as A,o as j,r as M,s as N,t as P}from"./source-C-De8L3s.js";var F=p(`<p class="lib-intro muted">Everything your sessions made, across workspaces.`),I=p(`<div class=lib-filters><label><span>Workspace</span><select aria-label=Workspace><option value>All workspaces</option><option value=none>No workspace</option></select></label><label><span>Type</span><select aria-label=Type><option value>All types</option></select></label><label><span>Show</span><select aria-label=Show><option value>Artifacts</option><option value=preview>Previews`),L=p(`<p class=muted role=status>Loading artifacts…`),R=p(`<option>`),z=p(`<p class="lib-note err"role=alert> <button type=button class="btn small">Retry`),B=p(`<p class=lib-note>`),V=p(`<ul class=lib-list aria-label=Artifacts>`),H=p(`<p class="lib-note err"role=alert>`),U=p(`<button type=button class="btn lib-more">Load more`),W=p(`<button type=button class="btn small lib-keep"title="Keep it as a normal artifact (previews expire)">Keep`),G=p(`<p class=lib-desc>`),K=p(`<li class=lib-row><a class=lib-title></a><button type=button class="btn small lib-pane">Open in pane</button><span class=lib-meta><span class=lib-kind></span><span></span><span>`),q=f(()=>S(()=>import(`./ArtifactPage-CsPjam8D.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12]))),J={markdown:`Markdown`,code:`Code`,image:`Image`,table:`Table`,chart:`Chart`,dashboard:`Dashboard`,html:`HTML`,app:`App`,file:`File`};function Y(t){return e(i,{get when(){return t.rest[0]},keyed:!0,get fallback(){return e(Z,{})},children:t=>e(q,{id:t})})}var X=(e,t)=>e.workspace===t.workspace&&e.type===t.type&&e.origin===t.origin;function Z(){let f=d(()=>N(v().search),void 0,{equals:X}),[p,h]=a({status:`loading`}),g=k(P,h);u(()=>g.load(f()));let[_,b]=a(new Map);m(()=>void A().then(e=>b(new Map(e.map(e=>[e.id,e.title])))));let S=e=>y(`/artifacts${j({...f(),...e})}`),[C,w]=a(),E=async e=>w(await g.keep(e)),M=d(()=>{let e=new Set(_().keys()),t=p();if(t.status===`ready`)for(let n of t.items)n.workspaceID&&e.add(n.workspaceID);let n=f().workspace;return n&&n!==`none`&&e.add(n),[...e].map(e=>({id:e,label:O(e,_())}))}),W=()=>{let e=p();return e.status===`error`&&e.error.kind!==`error`};return e(T,{title:`Artifacts`,get children(){return[F(),e(i,{get when(){return!W()},get children(){var t=I(),n=t.firstChild,i=n.firstChild.nextSibling,a=i.firstChild,s=a.nextSibling,l=n.nextSibling,u=l.firstChild.nextSibling,d=u.firstChild,p=l.nextSibling.firstChild.nextSibling,m=p.firstChild,h=m.nextSibling;return i.addEventListener(`change`,e=>S({workspace:e.currentTarget.value})),c(i,e(o,{get each(){return M()},children:e=>(()=>{var t=R();return c(t,()=>e.label),r(()=>t.selected=f().workspace===e.id),r(()=>t.value=e.id),t})()}),null),u.addEventListener(`change`,e=>S({type:e.currentTarget.value})),c(u,e(o,{get each(){return[...x]},children:e=>(()=>{var t=R();return t.value=e,c(t,()=>J[e]),r(()=>t.selected=f().type===e),t})()}),null),p.addEventListener(`change`,e=>S({origin:e.currentTarget.value===`preview`?`preview`:void 0})),r(e=>{var t=f().workspace===``,n=f().workspace===`none`,r=f().type===``,i=!f().origin,o=f().origin===`preview`;return t!==e.e&&(a.selected=e.e=t),n!==e.t&&(s.selected=e.t=n),r!==e.a&&(d.selected=e.a=r),i!==e.o&&(m.selected=e.o=i),o!==e.i&&(h.selected=e.i=o),e},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0}),t}}),e(s,{get children(){return[e(n,{get when(){return p().status===`loading`},get children(){return L()}}),e(n,{get when(){return t(()=>p().status===`error`)()&&p()},children:t=>{let n=()=>t().error;return e(i,{get when(){return n().kind===`error`},get fallback(){return(()=>{var e=B();return c(e,()=>n().text),e})()},get children(){var e=z(),t=e.firstChild,r=t.nextSibling;return c(e,()=>n().text,t),r.$$click=()=>g.reload(),e}})}}),e(n,{get when(){return t(()=>p().status===`ready`)()&&p()},children:t=>{let n=()=>t();return[e(i,{get when(){return n().items.length},get fallback(){return(()=>{var e=B();return c(e,()=>D(f(),n().more)),e})()},get children(){var t=V();return c(t,e(o,{get each(){return n().items},children:t=>e($,{item:t,get names(){return _()},onKeep:E})})),t}}),e(i,{get when(){return C()},get children(){var e=H();return c(e,C),e}}),e(i,{get when(){return n().moreError},get children(){var e=H();return c(e,()=>n().moreError),e}}),e(i,{get when(){return n().more},get children(){var e=U();return e.$$click=()=>g.more(),r(t=>{var r=n().loadingMore,i=n().loadingMore;return r!==t.e&&(e.disabled=t.e=r),i!==t.t&&l(e,`aria-busy`,t.t=i),t},{e:void 0,t:void 0}),e}})]}})]}})]}})}function Q(e,t){return e.workspaceID?O(e.workspaceID,t):`Private`}function $(t){let n=()=>t.item;return(()=>{var a=K(),o=a.firstChild,s=o.nextSibling,u=s.nextSibling,d=u.firstChild,f=d.nextSibling,p=f.nextSibling;return _(a,`dragend`,E),a.addEventListener(`dragstart`,e=>C(e,{target:{artifactID:n().id},label:n().title||`Untitled`})),c(o,()=>n().title||`Untitled`),s.$$click=()=>b(n().id),c(a,e(i,{get when(){return n().origin===`preview`},get children(){var e=W();return e.$$click=()=>t.onKeep(n()),r(()=>l(e,`aria-label`,`Keep ${n().title||`Untitled`} as an artifact`)),e}}),u),c(d,()=>J[n().kind]),c(f,()=>Q(n(),t.names)),c(p,()=>h(n().updatedAt)),c(a,e(i,{get when(){return n().description},get children(){var e=G();return c(e,()=>n().description),e}}),null),r(e=>{var t=n().id,r=!w(),i=M(n().id),c=`Open ${n().title||`Untitled`} in a pane`;return t!==e.e&&l(a,`data-artifact`,e.e=t),r!==e.t&&l(a,`draggable`,e.t=r),i!==e.a&&l(o,`href`,e.a=i),c!==e.o&&l(s,`aria-label`,e.o=c),e},{e:void 0,t:void 0,a:void 0,o:void 0}),a})()}g([`click`]);export{Y as default};
|