antechamber 0.2.2 → 0.2.4
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 +18 -0
- package/README.md +25 -10
- package/bin/opencode-antechamber +4 -6
- package/dist/cli.js +8 -0
- package/dist/commands/context.d.ts +4 -0
- package/dist/commands/context.js +19 -0
- package/dist/commands/doctor.js +8 -3
- package/dist/commands/help.d.ts +1 -1
- package/dist/commands/help.js +2 -0
- package/dist/commands/serve.js +14 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +34 -5
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +7 -0
- package/dist/route-services.d.ts +2 -0
- package/dist/routes-v1.js +18 -4
- package/dist/self-update.d.ts +32 -0
- package/dist/self-update.js +188 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +2 -0
- package/extensions/omo-dag/package.json +1 -1
- package/extensions/omo-dag/panel/main.js +39 -39
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.4] - 2026-10-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- feat(cli): `antechamber setup --auto-update` points OpenChamber at a launcher that installs the latest release in the background on every start; it is now the recommended install path
|
|
15
|
+
- feat(cli): `antechamber upgrade` installs the latest release with the package manager that owns the global install, so OpenChamber 2.3.2's Update button now upgrades antechamber
|
|
16
|
+
- feat(api): `POST /global/upgrade` upgrades antechamber itself instead of returning `UnsupportedError`
|
|
17
|
+
- feat(config): `ANTECHAMBER_AUTO_UPDATE=1` enables the post-start background update
|
|
18
|
+
|
|
19
|
+
## [0.2.3] - 2026-10-15
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- OmO Graph: panning no longer gets stuck following the mouse after the button
|
|
24
|
+
is released outside the panel (or the pointer is cancelled, capture is lost
|
|
25
|
+
or the window loses focus); a move without the primary button held now ends
|
|
26
|
+
the drag.
|
|
27
|
+
|
|
10
28
|
## [0.2.2] - 2026-10-15
|
|
11
29
|
|
|
12
30
|
### Fixed
|
package/README.md
CHANGED
|
@@ -73,13 +73,23 @@ Needs Node.js 22+ and OmO's `omo` command on PATH (`bun add -g omo-ai`).
|
|
|
73
73
|
npm i -g antechamber
|
|
74
74
|
# or: bun add -g antechamber
|
|
75
75
|
antechamber doctor
|
|
76
|
-
antechamber setup
|
|
76
|
+
antechamber setup --auto-update
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
`setup` updates OpenChamber's `opencodeBinary`, preserves all other settings,
|
|
80
80
|
and backs up an existing settings file before changing it. Restart OpenChamber
|
|
81
81
|
after setup. Preview changes with `antechamber setup --dry-run`.
|
|
82
82
|
|
|
83
|
+
`--auto-update` keeps antechamber current without any action from you. It
|
|
84
|
+
writes a small launcher, `~/.config/antechamber/opencode-antechamber`, and points
|
|
85
|
+
OpenChamber at it. Each time OpenChamber starts antechamber, the launcher
|
|
86
|
+
checks npm for a newer release once the server is up and installs it with the
|
|
87
|
+
package manager that owns the global install. The new version runs from the
|
|
88
|
+
next start; startup never waits on the network, and an offline start just
|
|
89
|
+
skips the check. It needs a global npm or bun install, so it is refused for
|
|
90
|
+
source checkouts and `npx`. Drop the flag (plain `antechamber setup`) to point
|
|
91
|
+
OpenChamber back at the `antechamber` bin and stop auto-updates.
|
|
92
|
+
|
|
83
93
|
Optionally, add the bundled [OmO Graph extension](#openchamber-extension-omo-graph):
|
|
84
94
|
`setup` ends by printing its folder
|
|
85
95
|
(`$(npm root -g)/antechamber/extensions/omo-dag`). In OpenChamber, open
|
|
@@ -131,28 +141,33 @@ antechamber serve --hostname 127.0.0.1 --port 4096
|
|
|
131
141
|
| Command | Purpose / options |
|
|
132
142
|
|---|---|
|
|
133
143
|
| `antechamber serve` | OpenCode-compatible server; `--hostname`/`--host`, `--port`, `--directory`. Bare `antechamber` also starts the server. |
|
|
134
|
-
| `antechamber setup` | Configure OpenChamber; `--settings <file>`, `--profile <dir>`, `--binary <path>`, `--dry-run`, `--systemd [unit]`, `--pi-binary <path>`, `--restart`. |
|
|
144
|
+
| `antechamber setup` | Configure OpenChamber; `--settings <file>`, `--profile <dir>`, `--binary <path>`, `--dry-run`, `--systemd [unit]`, `--pi-binary <path>`, `--restart`, `--auto-update`. |
|
|
135
145
|
| `antechamber doctor` | Read-only Node, backend, agent/state directories, teardown, OpenChamber and auth checks; `--json`, `--settings <file>`, `--profile <dir>`. Exits 1 on any FAIL; warnings do not fail. |
|
|
146
|
+
| `antechamber upgrade` | Install the latest release over a global npm or bun install, with the package manager that owns it. Refused for source checkouts and `npx`. |
|
|
136
147
|
| `antechamber version` | Package version plus OpenCode API contract version. `--version`/`-v` deliberately print exactly `opencode v2.0.20` for OpenChamber. |
|
|
137
148
|
| `antechamber help` | Usage and options; also `--help`/`-h`. |
|
|
138
149
|
|
|
139
150
|
## Upgrade, uninstall and rollback
|
|
140
151
|
|
|
152
|
+
With `setup --auto-update` there is nothing to do: each start installs the
|
|
153
|
+
latest release for the next one. Otherwise:
|
|
154
|
+
|
|
141
155
|
```bash
|
|
142
|
-
|
|
143
|
-
# or: bun add -g antechamber
|
|
156
|
+
antechamber upgrade
|
|
157
|
+
# equivalent: npm update -g antechamber (or: bun add -g antechamber)
|
|
144
158
|
```
|
|
145
159
|
|
|
146
160
|
Restart OpenChamber, or `systemctl --user restart openchamber.service` for a
|
|
147
161
|
managed service. Run `antechamber doctor` again. If the executable moved,
|
|
148
162
|
rerun `antechamber setup`.
|
|
149
163
|
|
|
150
|
-
OpenChamber 2.3.2
|
|
151
|
-
OpenCode API 2.0.20, the minimum OpenChamber accepts
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
OpenChamber 2.3.2 shows an "OpenCode update" notice because antechamber
|
|
165
|
+
reports OpenCode API 2.0.20, the minimum OpenChamber accepts. The notice names
|
|
166
|
+
an OpenCode version, but its **Update** button runs `<opencodeBinary> upgrade`,
|
|
167
|
+
which upgrades antechamber itself (a no-op when it is already current). Click
|
|
168
|
+
**Reload OpenCode** afterwards to restart onto the new version. OpenChamber
|
|
169
|
+
1.x shows no notice; its `POST /global/upgrade` call upgrades antechamber the
|
|
170
|
+
same way.
|
|
156
171
|
|
|
157
172
|
To roll back wiring, copy the backup printed by setup over the settings file:
|
|
158
173
|
|
package/bin/opencode-antechamber
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
# OpenChamber launcher: point `settings.opencodeBinary` (or $OPENCODE_BINARY)
|
|
3
3
|
# at this file. OpenChamber runs `<binary> serve --hostname H --port P` and
|
|
4
|
-
# `<binary> --version
|
|
4
|
+
# `<binary> --version`, and OpenChamber 2.x's Update action runs `<binary> upgrade`;
|
|
5
|
+
# anything else is rejected.
|
|
5
6
|
# Requires a build (`npm run build`) so that dist/cli.js exists.
|
|
6
7
|
set -eu
|
|
7
8
|
SCRIPT_PATH=$0
|
|
@@ -22,10 +23,7 @@ fi
|
|
|
22
23
|
case "${1:-}" in
|
|
23
24
|
serve) shift; exec node "$CLI" serve "$@" ;;
|
|
24
25
|
--version|-v) exec node "$CLI" --version ;;
|
|
25
|
-
upgrade)
|
|
26
|
-
echo "opencode-antechamber: OpenChamber cannot upgrade antechamber; run 'npm update -g antechamber' (or 'bun add -g antechamber') instead" >&2
|
|
27
|
-
exit 1
|
|
28
|
-
;;
|
|
26
|
+
upgrade) exec node "$CLI" upgrade ;;
|
|
29
27
|
esac
|
|
30
|
-
echo "opencode-antechamber: unsupported command '${1:-}' (only 'serve'
|
|
28
|
+
echo "opencode-antechamber: unsupported command '${1:-}' (only 'serve', '--version' and 'upgrade' are implemented)" >&2
|
|
31
29
|
exit 1
|
package/dist/cli.js
CHANGED
|
@@ -26,6 +26,7 @@ try {
|
|
|
26
26
|
systemd: { type: "boolean" },
|
|
27
27
|
"pi-binary": { type: "string" },
|
|
28
28
|
restart: { type: "boolean" },
|
|
29
|
+
"auto-update": { type: "boolean" },
|
|
29
30
|
json: { type: "boolean" },
|
|
30
31
|
},
|
|
31
32
|
});
|
|
@@ -49,8 +50,15 @@ try {
|
|
|
49
50
|
...(values.systemd ? { systemd: positionals[1] ?? "openchamber.service" } : {}),
|
|
50
51
|
dryRun: values["dry-run"] ?? false,
|
|
51
52
|
restart: values.restart ?? false,
|
|
53
|
+
autoUpdate: values["auto-update"] ?? false,
|
|
52
54
|
});
|
|
53
55
|
}
|
|
56
|
+
else if (command === "upgrade") {
|
|
57
|
+
if (positionals.length > 1)
|
|
58
|
+
throw new Error("Unexpected upgrade argument");
|
|
59
|
+
const { upgrade } = await import("./commands/upgrade.js");
|
|
60
|
+
await upgrade();
|
|
61
|
+
}
|
|
54
62
|
else if (command === "doctor") {
|
|
55
63
|
const { doctor, formatDoctor } = await import("./commands/doctor.js");
|
|
56
64
|
const report = doctor({
|
|
@@ -22,4 +22,8 @@ export interface SettingsOptions {
|
|
|
22
22
|
export declare function settingsPath(options: SettingsOptions, ctx: CommandContext): string;
|
|
23
23
|
export declare function readSettings(path: string, ctx: CommandContext): Record<string, unknown>;
|
|
24
24
|
export declare function shellQuote(value: string): string;
|
|
25
|
+
/** OpenChamber launcher written by `setup --auto-update`, kept beside the session registry. */
|
|
26
|
+
export declare function autoUpdateLauncherPath(ctx: CommandContext): string;
|
|
27
|
+
export declare function autoUpdateLauncherText(binary: string): string;
|
|
28
|
+
export declare function autoUpdateLauncherTarget(text: string): string | undefined;
|
|
25
29
|
export declare function isPackageBinary(binary: string, ctx: CommandContext): boolean;
|
package/dist/commands/context.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { delimiter, isAbsolute, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { defaultStateDir, envSetting } from "../config.js";
|
|
6
7
|
export function createContext(overrides = {}) {
|
|
7
8
|
const env = overrides.env ?? process.env;
|
|
8
9
|
const cwd = overrides.cwd ?? process.cwd();
|
|
@@ -80,6 +81,24 @@ export function readSettings(path, ctx) {
|
|
|
80
81
|
export function shellQuote(value) {
|
|
81
82
|
return /^[\w./:@=-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
|
|
82
83
|
}
|
|
84
|
+
/** OpenChamber launcher written by `setup --auto-update`, kept beside the session registry. */
|
|
85
|
+
export function autoUpdateLauncherPath(ctx) {
|
|
86
|
+
return resolve(ctx.cwd, envSetting("STATE_DIR", ctx.env) ?? defaultStateDir(ctx.home, ctx.fs.existsSync), "opencode-antechamber");
|
|
87
|
+
}
|
|
88
|
+
export function autoUpdateLauncherText(binary) {
|
|
89
|
+
return [
|
|
90
|
+
"#!/bin/sh",
|
|
91
|
+
"# Written by `antechamber setup --auto-update`; rerun `antechamber setup` without it to stop auto-updates.",
|
|
92
|
+
"ANTECHAMBER_AUTO_UPDATE=1",
|
|
93
|
+
"export ANTECHAMBER_AUTO_UPDATE",
|
|
94
|
+
`exec '${binary.replaceAll("'", "'\\''")}' "$@"`,
|
|
95
|
+
"",
|
|
96
|
+
].join("\n");
|
|
97
|
+
}
|
|
98
|
+
export function autoUpdateLauncherTarget(text) {
|
|
99
|
+
const match = /^exec '((?:[^']|'\\'')*)' "\$@"$/m.exec(text);
|
|
100
|
+
return match?.[1]?.replaceAll("'\\''", "'");
|
|
101
|
+
}
|
|
83
102
|
export function isPackageBinary(binary, ctx) {
|
|
84
103
|
const resolved = resolveBinary(binary, ctx);
|
|
85
104
|
if (resolved === undefined)
|
package/dist/commands/doctor.js
CHANGED
|
@@ -3,7 +3,7 @@ import { piAgentDir } from "../agent-dir.js";
|
|
|
3
3
|
import { detectAgentName } from "../agent-name.js";
|
|
4
4
|
import { agentBinary, defaultStateDir, envSetting, isPlainPi } from "../config.js";
|
|
5
5
|
import { findPidfdPython } from "../pidfd.js";
|
|
6
|
-
import { createContext, isPackageBinary, readSettings, resolveBinary, settingsPath, } from "./context.js";
|
|
6
|
+
import { autoUpdateLauncherPath, autoUpdateLauncherTarget, createContext, isPackageBinary, readSettings, resolveBinary, settingsPath, } from "./context.js";
|
|
7
7
|
/** Checks capabilities with access/stat only, never creating state or config files. */
|
|
8
8
|
export function doctor(options = {}, ctx = createContext()) {
|
|
9
9
|
const checks = [];
|
|
@@ -75,8 +75,13 @@ export function doctor(options = {}, ctx = createContext()) {
|
|
|
75
75
|
else {
|
|
76
76
|
try {
|
|
77
77
|
const configured = readSettings(settings, ctx).opencodeBinary;
|
|
78
|
-
const
|
|
79
|
-
|
|
78
|
+
const launcher = autoUpdateLauncherPath(ctx);
|
|
79
|
+
const launcherTarget = configured === launcher && ctx.fs.existsSync(launcher)
|
|
80
|
+
? autoUpdateLauncherTarget(ctx.fs.readFileSync(launcher, "utf8"))
|
|
81
|
+
: undefined;
|
|
82
|
+
const viaLauncher = launcherTarget !== undefined && isPackageBinary(launcherTarget, ctx);
|
|
83
|
+
const matches = viaLauncher || (typeof configured === "string" && isPackageBinary(configured, ctx));
|
|
84
|
+
add("openchamber-settings", matches ? "PASS" : "WARN", `${settings}: opencodeBinary=${JSON.stringify(configured ?? null)}${viaLauncher ? ` (auto-update launcher for ${launcherTarget})` : matches ? "" : "; does not point at this antechamber installation"}`);
|
|
80
85
|
}
|
|
81
86
|
catch (error) {
|
|
82
87
|
add("openchamber-settings", "WARN", error instanceof Error ? error.message : String(error));
|
package/dist/commands/help.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const help = "Usage: antechamber [command] [options]\n\nCommands:\n serve Run the OpenCode-compatible HTTP/SSE server (also the default)\n setup Point OpenChamber at this installation; back up changed files\n doctor Read-only installation and backend checks (exit 1 on FAIL)\n version Print the npm package and OpenCode API versions\n help Show this help (--help, -h)\n\nServe: --port 4096 --host 127.0.0.1 (or --hostname) --directory <path>\n --insecure-no-auth\nSetup: --settings <file> | --profile <dir>\n --binary <path> --dry-run --systemd [unit] --pi-binary <path> --restart\nDoctor: --settings <file> | --profile <dir> --json\nProbe: --version, -v prints the OpenCode compatibility version for OpenChamber";
|
|
1
|
+
export declare const help = "Usage: antechamber [command] [options]\n\nCommands:\n serve Run the OpenCode-compatible HTTP/SSE server (also the default)\n setup Point OpenChamber at this installation; back up changed files\n doctor Read-only installation and backend checks (exit 1 on FAIL)\n upgrade Install the latest release over a global npm or bun install\n version Print the npm package and OpenCode API versions\n help Show this help (--help, -h)\n\nServe: --port 4096 --host 127.0.0.1 (or --hostname) --directory <path>\n --insecure-no-auth\nSetup: --settings <file> | --profile <dir>\n --binary <path> --dry-run --systemd [unit] --pi-binary <path> --restart\n --auto-update (launch through a wrapper that upgrades on each start)\nDoctor: --settings <file> | --profile <dir> --json\nProbe: --version, -v prints the OpenCode compatibility version for OpenChamber";
|
package/dist/commands/help.js
CHANGED
|
@@ -4,6 +4,7 @@ Commands:
|
|
|
4
4
|
serve Run the OpenCode-compatible HTTP/SSE server (also the default)
|
|
5
5
|
setup Point OpenChamber at this installation; back up changed files
|
|
6
6
|
doctor Read-only installation and backend checks (exit 1 on FAIL)
|
|
7
|
+
upgrade Install the latest release over a global npm or bun install
|
|
7
8
|
version Print the npm package and OpenCode API versions
|
|
8
9
|
help Show this help (--help, -h)
|
|
9
10
|
|
|
@@ -11,5 +12,6 @@ Serve: --port 4096 --host 127.0.0.1 (or --hostname) --directory <path>
|
|
|
11
12
|
--insecure-no-auth
|
|
12
13
|
Setup: --settings <file> | --profile <dir>
|
|
13
14
|
--binary <path> --dry-run --systemd [unit] --pi-binary <path> --restart
|
|
15
|
+
--auto-update (launch through a wrapper that upgrades on each start)
|
|
14
16
|
Doctor: --settings <file> | --profile <dir> --json
|
|
15
17
|
Probe: --version, -v prints the OpenCode compatibility version for OpenChamber`;
|
package/dist/commands/serve.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { envSetting } from "../config.js";
|
|
2
|
+
import { selfUpdate } from "../self-update.js";
|
|
2
3
|
import { AdapterServer, isLoopbackHost, serverPassword } from "../server.js";
|
|
3
4
|
export async function serve(options) {
|
|
4
5
|
const { port, host, directory, insecureNoAuth, compatible } = options;
|
|
@@ -26,4 +27,17 @@ export async function serve(options) {
|
|
|
26
27
|
console.log(`antechamber listening on ${server.url}`);
|
|
27
28
|
console.log(`default directory: ${directory}`);
|
|
28
29
|
}
|
|
30
|
+
// After the listening line: OpenChamber's startup timeout must not wait on the registry.
|
|
31
|
+
if (envSetting("AUTO_UPDATE") === "1")
|
|
32
|
+
void autoUpdate();
|
|
33
|
+
}
|
|
34
|
+
async function autoUpdate() {
|
|
35
|
+
try {
|
|
36
|
+
const result = await selfUpdate();
|
|
37
|
+
if (result.updated)
|
|
38
|
+
console.error(`antechamber: auto-update installed ${result.to} (running ${result.from}); it starts next time`);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error(`antechamber: auto-update skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
42
|
+
}
|
|
29
43
|
}
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ interface SetupOptions extends SettingsOptions {
|
|
|
5
5
|
readonly systemd?: string;
|
|
6
6
|
readonly piBinary?: string;
|
|
7
7
|
readonly restart?: boolean;
|
|
8
|
+
readonly autoUpdate?: boolean;
|
|
8
9
|
/** User-unit root, overridable without touching the real systemd configuration. */
|
|
9
10
|
readonly systemdDir?: string;
|
|
10
11
|
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { installMethod, npmBinaryFor } from "../self-update.js";
|
|
4
|
+
import { autoUpdateLauncherPath, autoUpdateLauncherText, createContext, isPackageBinary, readSettings, resolveBinary, settingsPath, shellQuote, } from "./context.js";
|
|
4
5
|
function systemdValue(value) {
|
|
5
6
|
if (/[\r\n\0]/.test(value))
|
|
6
7
|
throw new Error("systemd environment values cannot contain newlines or NUL");
|
|
@@ -40,8 +41,24 @@ export function setup(options, ctx = createContext()) {
|
|
|
40
41
|
if (binary === ctx.cliPath && !ctx.fs.readFileSync(binary, "utf8").startsWith("#!/usr/bin/env node\n")) {
|
|
41
42
|
throw new Error(`Package CLI ${binary} is missing its node shebang; run npm run build`);
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
let launcher;
|
|
45
|
+
let launcherText;
|
|
46
|
+
let oldLauncher;
|
|
47
|
+
if (options.autoUpdate) {
|
|
48
|
+
installMethod({
|
|
49
|
+
packageRoot: resolve(dirname(ctx.cliPath), ".."),
|
|
50
|
+
env: ctx.env,
|
|
51
|
+
home: ctx.home,
|
|
52
|
+
realpath: (path) => ctx.fs.realpathSync(path),
|
|
53
|
+
}, () => ctx.exec(npmBinaryFor(), ["root", "-g"]));
|
|
54
|
+
launcher = autoUpdateLauncherPath(ctx);
|
|
55
|
+
launcherText = autoUpdateLauncherText(binary);
|
|
56
|
+
if (ctx.fs.existsSync(launcher))
|
|
57
|
+
oldLauncher = ctx.fs.readFileSync(launcher, "utf8");
|
|
58
|
+
}
|
|
59
|
+
const configured = launcher ?? binary;
|
|
60
|
+
const updated = `${JSON.stringify({ ...data, opencodeBinary: configured }, null, 2)}\n`;
|
|
61
|
+
const changed = data.opencodeBinary !== configured;
|
|
45
62
|
let dropIn;
|
|
46
63
|
let dropInText;
|
|
47
64
|
let oldDropIn;
|
|
@@ -102,10 +119,22 @@ export function setup(options, ctx = createContext()) {
|
|
|
102
119
|
report(`Made ${binary} executable`);
|
|
103
120
|
}
|
|
104
121
|
}
|
|
122
|
+
if (launcher !== undefined && launcherText !== undefined) {
|
|
123
|
+
if (oldLauncher === launcherText && (ctx.fs.statSync(launcher).mode & 0o111) !== 0)
|
|
124
|
+
report(`Already configured: ${launcher}`);
|
|
125
|
+
else if (options.dryRun)
|
|
126
|
+
report(`Would write auto-update launcher ${launcher} for ${binary}`);
|
|
127
|
+
else {
|
|
128
|
+
ctx.fs.mkdirSync(dirname(launcher), { recursive: true });
|
|
129
|
+
writeAtomically(launcher, launcherText, ctx);
|
|
130
|
+
ctx.fs.chmodSync(launcher, 0o755);
|
|
131
|
+
report(`Wrote auto-update launcher ${launcher} for ${binary}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
105
134
|
if (changed) {
|
|
106
135
|
report(`${options.dryRun ? "Would update" : "Updating"} ${settings}`);
|
|
107
136
|
report(`- opencodeBinary: ${JSON.stringify(data.opencodeBinary ?? null)}`);
|
|
108
|
-
report(`+ opencodeBinary: ${JSON.stringify(
|
|
137
|
+
report(`+ opencodeBinary: ${JSON.stringify(configured)}`);
|
|
109
138
|
if (existing)
|
|
110
139
|
backup(settings);
|
|
111
140
|
if (!options.dryRun) {
|
|
@@ -115,7 +144,7 @@ export function setup(options, ctx = createContext()) {
|
|
|
115
144
|
}
|
|
116
145
|
}
|
|
117
146
|
else
|
|
118
|
-
report(`Already configured: ${settings} points at ${
|
|
147
|
+
report(`Already configured: ${settings} points at ${configured}`);
|
|
119
148
|
if (dropIn !== undefined && dropInText !== undefined) {
|
|
120
149
|
if (oldDropIn === dropInText)
|
|
121
150
|
report(`Already configured: ${dropIn}`);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { selfUpdate } from "../self-update.js";
|
|
2
|
+
export async function upgrade(update = selfUpdate, output = console.log) {
|
|
3
|
+
const result = await update();
|
|
4
|
+
output(result.updated
|
|
5
|
+
? `Updated antechamber ${result.from} -> ${result.to} with ${result.method}; restart OpenChamber to use it.`
|
|
6
|
+
: `antechamber ${result.from} is already the latest release.`);
|
|
7
|
+
}
|
package/dist/route-services.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Catalog } from "./catalog.js";
|
|
|
5
5
|
import type { ModelSelection } from "./thinking-levels.js";
|
|
6
6
|
import type { OCEvent, OCMessageWithParts, OCPromptPart } from "./types.js";
|
|
7
7
|
import type { TodoUpdated } from "./todo-bridge.js";
|
|
8
|
+
import type { SelfUpdateResult } from "./self-update.js";
|
|
8
9
|
export interface RouteServices {
|
|
9
10
|
readonly sessions: SessionManager;
|
|
10
11
|
readonly defaultDirectory: string;
|
|
@@ -12,6 +13,7 @@ export interface RouteServices {
|
|
|
12
13
|
readonly body: (req: IncomingMessage) => Promise<unknown>;
|
|
13
14
|
readonly catalog: () => Promise<Catalog>;
|
|
14
15
|
readonly broadcast: (directory: string, event: OCEvent | TodoUpdated) => void;
|
|
16
|
+
readonly selfUpdate: () => Promise<SelfUpdateResult>;
|
|
15
17
|
}
|
|
16
18
|
export declare const OPENCODE_COMPAT_VERSION: string | null;
|
|
17
19
|
export declare function numberParam(value: string | null): number | undefined;
|
package/dist/routes-v1.js
CHANGED
|
@@ -6,6 +6,7 @@ import { miscRoute } from "./misc-routes.js";
|
|
|
6
6
|
import { getAgentName } from "./agent-name.js";
|
|
7
7
|
import { nativePromptInput, parsePromptParts } from "./prompt-parts.js";
|
|
8
8
|
import { RequestBodyError } from "./http.js";
|
|
9
|
+
import { SelfUpdateUnsupportedError } from "./self-update.js";
|
|
9
10
|
import { announceSessionError, compactSession, getCommands, unsupportedShare } from "./session-commands.js";
|
|
10
11
|
import { agentDescriptors, modelRefFromBody, numberParam, OPENCODE_COMPAT_VERSION, promptAsync, runSessionShell, runSessionSlash, sessionDirectory, sessionMetadata, } from "./route-services.js";
|
|
11
12
|
/**
|
|
@@ -19,13 +20,26 @@ function applyArchivedFilter(sessions, archived) {
|
|
|
19
20
|
return sessions.filter((s) => s.time.archived === undefined);
|
|
20
21
|
return sessions;
|
|
21
22
|
}
|
|
22
|
-
function handleV1GlobalRoute(res, path, method) {
|
|
23
|
+
async function handleV1GlobalRoute(services, res, path, method) {
|
|
23
24
|
if (method === "GET" && path === "/global/health") {
|
|
24
25
|
json(res, 200, OPENCODE_COMPAT_VERSION === null ? { healthy: true } : { healthy: true, version: OPENCODE_COMPAT_VERSION });
|
|
25
26
|
return true;
|
|
26
27
|
}
|
|
27
|
-
if (
|
|
28
|
-
|
|
28
|
+
if (method === "POST" && path === "/global/upgrade") {
|
|
29
|
+
// The requested OpenCode `target` does not apply: this upgrades antechamber itself.
|
|
30
|
+
try {
|
|
31
|
+
const result = await services.selfUpdate();
|
|
32
|
+
json(res, 200, { success: true, version: result.to, updated: result.updated });
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (!(error instanceof SelfUpdateUnsupportedError))
|
|
36
|
+
throw error;
|
|
37
|
+
json(res, 409, errorBody("UnsupportedError", error.message));
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
if (method === "GET" && path === "/global/upgrade") {
|
|
42
|
+
json(res, 409, errorBody("UnsupportedError", "Upgrade antechamber with POST /global/upgrade."));
|
|
29
43
|
return true;
|
|
30
44
|
}
|
|
31
45
|
return false;
|
|
@@ -47,7 +61,7 @@ function parseV1Prompt(body, defaultModel) {
|
|
|
47
61
|
return { text, model, parts, delivery: body.delivery ?? "steer" };
|
|
48
62
|
}
|
|
49
63
|
export async function handleV1(services, req, res, url, path, method, directory) {
|
|
50
|
-
if (handleV1GlobalRoute(res, path, method))
|
|
64
|
+
if (await handleV1GlobalRoute(services, res, path, method))
|
|
51
65
|
return;
|
|
52
66
|
// --- sessions ---
|
|
53
67
|
const misc = await miscRoute(method, path, url, directory);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type InstallMethod = "npm" | "bun";
|
|
2
|
+
export declare class SelfUpdateUnsupportedError extends Error {
|
|
3
|
+
readonly name = "SelfUpdateUnsupportedError";
|
|
4
|
+
}
|
|
5
|
+
export interface InstallLocation {
|
|
6
|
+
readonly packageRoot: string;
|
|
7
|
+
readonly env: NodeJS.ProcessEnv;
|
|
8
|
+
readonly home: string;
|
|
9
|
+
readonly realpath: (path: string) => string;
|
|
10
|
+
}
|
|
11
|
+
export interface SelfUpdateDeps extends InstallLocation {
|
|
12
|
+
readonly npmBinary: string;
|
|
13
|
+
readonly bunBinary: string;
|
|
14
|
+
readonly npmRoot: () => Promise<string>;
|
|
15
|
+
readonly currentVersion: string;
|
|
16
|
+
readonly latestVersion: () => Promise<string>;
|
|
17
|
+
readonly install: (binary: string, args: readonly string[]) => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export interface SelfUpdateResult {
|
|
20
|
+
readonly updated: boolean;
|
|
21
|
+
readonly from: string;
|
|
22
|
+
readonly to: string;
|
|
23
|
+
readonly method: InstallMethod;
|
|
24
|
+
}
|
|
25
|
+
export declare function installMethod(location: InstallLocation, npmRoot: () => string): InstallMethod;
|
|
26
|
+
export declare function isNewerVersion(a: string, b: string): boolean;
|
|
27
|
+
export declare function fetchLatestVersion(env?: NodeJS.ProcessEnv): Promise<string>;
|
|
28
|
+
/** Prefers the npm shipped beside the running node: service PATHs often lack the user's tool directories. */
|
|
29
|
+
export declare function npmBinaryFor(execPath?: string): string;
|
|
30
|
+
export declare function defaultSelfUpdateDeps(env?: NodeJS.ProcessEnv, home?: string): SelfUpdateDeps;
|
|
31
|
+
/** Installs the latest release when it is newer; concurrent callers in this process share one attempt. */
|
|
32
|
+
export declare function selfUpdate(deps?: SelfUpdateDeps): Promise<SelfUpdateResult>;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/** Upgrades a global npm or bun install of antechamber to the latest published release. */
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { basename, dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { ADAPTER_VERSION } from "./version.js";
|
|
8
|
+
export class SelfUpdateUnsupportedError extends Error {
|
|
9
|
+
name = "SelfUpdateUnsupportedError";
|
|
10
|
+
}
|
|
11
|
+
const INSTALL_TIMEOUT_MS = 5 * 60_000;
|
|
12
|
+
const VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;
|
|
13
|
+
function tryRealpath(path, location) {
|
|
14
|
+
try {
|
|
15
|
+
return location.realpath(path);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function bunInstallDir(location) {
|
|
22
|
+
return location.env.BUN_INSTALL ?? join(location.home, ".bun");
|
|
23
|
+
}
|
|
24
|
+
function locate(location) {
|
|
25
|
+
const root = tryRealpath(location.packageRoot, location) ?? location.packageRoot;
|
|
26
|
+
if (basename(root) !== "antechamber" || basename(dirname(root)) !== "node_modules")
|
|
27
|
+
throw notGlobal(root);
|
|
28
|
+
const bunPackage = join(bunInstallDir(location), "install", "global", "node_modules", "antechamber");
|
|
29
|
+
return { root, bun: tryRealpath(bunPackage, location) === root };
|
|
30
|
+
}
|
|
31
|
+
function notGlobal(root) {
|
|
32
|
+
return new SelfUpdateUnsupportedError(`${root} is not a global npm or bun install of antechamber; update it the way it was installed (source checkout: git pull && npm ci && npm run build)`);
|
|
33
|
+
}
|
|
34
|
+
function npmOwns(location, root, npmGlobal) {
|
|
35
|
+
const global = npmGlobal.trim();
|
|
36
|
+
if (global !== "" && tryRealpath(join(global, "antechamber"), location) === root)
|
|
37
|
+
return "npm";
|
|
38
|
+
throw notGlobal(root);
|
|
39
|
+
}
|
|
40
|
+
function npmUnavailable(error) {
|
|
41
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
42
|
+
return new SelfUpdateUnsupportedError(`cannot run npm root -g to confirm a global npm install (${reason})`);
|
|
43
|
+
}
|
|
44
|
+
export function installMethod(location, npmRoot) {
|
|
45
|
+
const { root, bun } = locate(location);
|
|
46
|
+
if (bun)
|
|
47
|
+
return "bun";
|
|
48
|
+
let global;
|
|
49
|
+
try {
|
|
50
|
+
global = npmRoot();
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw npmUnavailable(error);
|
|
54
|
+
}
|
|
55
|
+
return npmOwns(location, root, global);
|
|
56
|
+
}
|
|
57
|
+
async function installMethodAsync(deps) {
|
|
58
|
+
const { root, bun } = locate(deps);
|
|
59
|
+
if (bun)
|
|
60
|
+
return "bun";
|
|
61
|
+
let global;
|
|
62
|
+
try {
|
|
63
|
+
global = await deps.npmRoot();
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw npmUnavailable(error);
|
|
67
|
+
}
|
|
68
|
+
return npmOwns(deps, root, global);
|
|
69
|
+
}
|
|
70
|
+
export function isNewerVersion(a, b) {
|
|
71
|
+
const left = VERSION_PATTERN.exec(a);
|
|
72
|
+
const right = VERSION_PATTERN.exec(b);
|
|
73
|
+
if (left === null || right === null)
|
|
74
|
+
return false;
|
|
75
|
+
for (let index = 1; index <= 3; index += 1) {
|
|
76
|
+
const diff = Number(left[index]) - Number(right[index]);
|
|
77
|
+
if (diff !== 0)
|
|
78
|
+
return diff > 0;
|
|
79
|
+
}
|
|
80
|
+
if (left[4] === right[4])
|
|
81
|
+
return false;
|
|
82
|
+
if (left[4] === undefined)
|
|
83
|
+
return true;
|
|
84
|
+
if (right[4] === undefined)
|
|
85
|
+
return false;
|
|
86
|
+
const leftParts = left[4].split(".");
|
|
87
|
+
const rightParts = right[4].split(".");
|
|
88
|
+
for (let index = 0; index < Math.max(leftParts.length, rightParts.length); index += 1) {
|
|
89
|
+
const l = leftParts[index];
|
|
90
|
+
const r = rightParts[index];
|
|
91
|
+
if (l === undefined)
|
|
92
|
+
return false;
|
|
93
|
+
if (r === undefined)
|
|
94
|
+
return true;
|
|
95
|
+
if (l === r)
|
|
96
|
+
continue;
|
|
97
|
+
const ln = /^\d+$/.test(l) ? Number(l) : undefined;
|
|
98
|
+
const rn = /^\d+$/.test(r) ? Number(r) : undefined;
|
|
99
|
+
if (ln !== undefined && rn !== undefined)
|
|
100
|
+
return ln > rn;
|
|
101
|
+
if (ln !== undefined)
|
|
102
|
+
return false;
|
|
103
|
+
if (rn !== undefined)
|
|
104
|
+
return true;
|
|
105
|
+
return l > r;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
export async function fetchLatestVersion(env = process.env) {
|
|
110
|
+
const url = new URL(`${(env.npm_config_registry ?? "https://registry.npmjs.org").replace(/\/+$/, "")}/antechamber/latest`);
|
|
111
|
+
// Registry URLs can embed a token; fetch rejects them and its error would echo the URL.
|
|
112
|
+
url.username = "";
|
|
113
|
+
url.password = "";
|
|
114
|
+
let response;
|
|
115
|
+
try {
|
|
116
|
+
response = await fetch(url, { headers: { Accept: "application/json" }, signal: AbortSignal.timeout(10_000) });
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
throw new Error(`cannot reach the npm registry at ${url.host}`);
|
|
120
|
+
}
|
|
121
|
+
if (!response.ok)
|
|
122
|
+
throw new Error(`npm registry at ${url.host} answered ${response.status} for antechamber`);
|
|
123
|
+
const payload = await response.json().catch(() => undefined);
|
|
124
|
+
const version = typeof payload === "object" && payload !== null && "version" in payload ? String(payload.version) : "";
|
|
125
|
+
if (!VERSION_PATTERN.test(version))
|
|
126
|
+
throw new Error("npm registry returned no valid antechamber version");
|
|
127
|
+
return version;
|
|
128
|
+
}
|
|
129
|
+
function run(binary, args, env, timeout) {
|
|
130
|
+
return new Promise((resolve, reject) => {
|
|
131
|
+
const child = execFile(binary, [...args], { env, timeout, maxBuffer: 4 * 1024 * 1024 }, (error, stdout) => {
|
|
132
|
+
// Installer output can contain registry credentials, so it is never echoed.
|
|
133
|
+
if (error)
|
|
134
|
+
reject(new Error(`${basename(binary)} ${args.join(" ")} failed; run it in a terminal for details`));
|
|
135
|
+
else
|
|
136
|
+
resolve(stdout);
|
|
137
|
+
});
|
|
138
|
+
// Package managers must see EOF instead of waiting for interactive input.
|
|
139
|
+
child.stdin?.end();
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/** Prefers the npm shipped beside the running node: service PATHs often lack the user's tool directories. */
|
|
143
|
+
export function npmBinaryFor(execPath = process.execPath) {
|
|
144
|
+
const sibling = join(dirname(execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
145
|
+
return existsSync(sibling) ? sibling : "npm";
|
|
146
|
+
}
|
|
147
|
+
function bunBinaryFor(env, home) {
|
|
148
|
+
const installed = join(bunInstallDir({ env, home }), "bin", "bun");
|
|
149
|
+
return existsSync(installed) ? installed : "bun";
|
|
150
|
+
}
|
|
151
|
+
export function defaultSelfUpdateDeps(env = process.env, home = homedir()) {
|
|
152
|
+
const npmBinary = npmBinaryFor();
|
|
153
|
+
return {
|
|
154
|
+
// src/self-update.ts (tsx) and dist/self-update.js both sit one level below the package root.
|
|
155
|
+
packageRoot: fileURLToPath(new URL("..", import.meta.url)),
|
|
156
|
+
env,
|
|
157
|
+
home,
|
|
158
|
+
realpath: realpathSync,
|
|
159
|
+
npmBinary,
|
|
160
|
+
bunBinary: bunBinaryFor(env, home),
|
|
161
|
+
npmRoot: () => run(npmBinary, ["root", "-g"], env, 10_000),
|
|
162
|
+
currentVersion: ADAPTER_VERSION,
|
|
163
|
+
latestVersion: () => fetchLatestVersion(env),
|
|
164
|
+
install: async (binary, args) => {
|
|
165
|
+
await run(binary, args, env, INSTALL_TIMEOUT_MS);
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
let inFlight;
|
|
170
|
+
/** Installs the latest release when it is newer; concurrent callers in this process share one attempt. */
|
|
171
|
+
export function selfUpdate(deps = defaultSelfUpdateDeps()) {
|
|
172
|
+
inFlight ??= (async () => {
|
|
173
|
+
const method = await installMethodAsync(deps);
|
|
174
|
+
const latest = await deps.latestVersion();
|
|
175
|
+
const from = deps.currentVersion;
|
|
176
|
+
if (!isNewerVersion(latest, from))
|
|
177
|
+
return { updated: false, from, to: from, method };
|
|
178
|
+
const spec = `antechamber@${latest}`;
|
|
179
|
+
if (method === "npm")
|
|
180
|
+
await deps.install(deps.npmBinary, ["install", "-g", spec]);
|
|
181
|
+
else
|
|
182
|
+
await deps.install(deps.bunBinary, ["add", "-g", spec]);
|
|
183
|
+
return { updated: true, from, to: latest, method };
|
|
184
|
+
})().finally(() => {
|
|
185
|
+
inFlight = undefined;
|
|
186
|
+
});
|
|
187
|
+
return inFlight;
|
|
188
|
+
}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/** HTTP/SSE composition root for the OpenCode API backed by pi. */
|
|
2
|
+
import { type SelfUpdateResult } from "./self-update.js";
|
|
2
3
|
export { isLoopbackHost, serverPassword } from "./http.js";
|
|
3
4
|
interface AdapterServerOptions {
|
|
4
5
|
port: number;
|
|
5
6
|
host: string;
|
|
6
7
|
/** Fallback directory when requests carry no ?directory= param. */
|
|
7
8
|
defaultDirectory: string;
|
|
9
|
+
/** Replaces the npm self-update behind POST /global/upgrade (tests). */
|
|
10
|
+
selfUpdate?: () => Promise<SelfUpdateResult>;
|
|
8
11
|
}
|
|
9
12
|
export declare class AdapterServer {
|
|
10
13
|
private options;
|
package/dist/server.js
CHANGED
|
@@ -8,6 +8,7 @@ import { Http } from "./http.js";
|
|
|
8
8
|
import { SSE } from "./sse.js";
|
|
9
9
|
import { handleV1 } from "./routes-v1.js";
|
|
10
10
|
import { handleV2 } from "./routes-v2.js";
|
|
11
|
+
import { selfUpdate } from "./self-update.js";
|
|
11
12
|
export { isLoopbackHost, serverPassword } from "./http.js";
|
|
12
13
|
export class AdapterServer {
|
|
13
14
|
options;
|
|
@@ -203,6 +204,7 @@ export class AdapterServer {
|
|
|
203
204
|
body: (request) => this.http.body(request),
|
|
204
205
|
catalog: () => this.catalog(),
|
|
205
206
|
broadcast: (dir, event) => this.broadcast(dir, event),
|
|
207
|
+
selfUpdate: this.options.selfUpdate ?? (() => selfUpdate()),
|
|
206
208
|
};
|
|
207
209
|
if (path === "/api" || path.startsWith("/api/")) {
|
|
208
210
|
await handleV2(services, req, res, url, path, method, directory);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{var
|
|
1
|
+
"use strict";(()=>{var x="openchamber.sdk";var ze=`
|
|
2
2
|
:root {
|
|
3
3
|
--oc-scrollbar-thumb: color-mix(in srgb, var(--oc-muted, currentColor) 40%, transparent);
|
|
4
4
|
--oc-scrollbar-thumb-hover: color-mix(in srgb, var(--oc-muted, currentColor) 65%, transparent);
|
|
@@ -28,54 +28,54 @@
|
|
|
28
28
|
* { scrollbar-color: auto; }
|
|
29
29
|
::-webkit-scrollbar-thumb, ::-webkit-scrollbar-thumb:hover { background: CanvasText; }
|
|
30
30
|
}
|
|
31
|
-
`;var Tt=["file","directory","other","missing"],ze=e=>!!(e&&"sessionId"in e),Ke=e=>!!(e&&"sent"in e&&!("sessionId"in e));var Se=500,Te=32e3,_t=16e3,yt=128,At=200,wt=2e3,vt=16e3,Mt=80,Ot=200,Lt=16e3;var Ct=2e3;var Ve=2e4,_e=1024,ye=2e6;var Ae=64e3,we=8e3,je=4e3;var qe=9e4;var Rt=999,ve=500,Gt=["HOST_UNAVAILABLE","HOST_TIMEOUT","HOST_REJECTED","DISCONNECTED","DISABLED","BAD_PATH","NO_INTEGRATION","NO_SERVICE","SERVICE_FAILED","NO_SESSION","SESSION_BUSY","NOT_GRANTED","NO_DIRECTORY","NOT_FOUND","FILE_TOO_LARGE","DENIED","NO_MODEL","MODEL_FAILED"],It=["stopped","starting","ready","failed"],ro=new Set(Gt),$t=e=>ro.has(e),Nt=e=>e&&$t(e)?e:"HOST_REJECTED",re=e=>e===void 0?!1:e===null||e===!0||e===!1||String(e)===e?!0:Number(e)===e?Number.isFinite(e):Array.isArray(e)?e.every(re):Object(e)===e?Object.values(e).every(re):!1,Ut=e=>re(e)&&JSON.stringify(e).length<=Lt,St=e=>e?.trim().slice(0,Ot)??"",se=e=>{let t=e.id.trim().slice(0,yt),r=e.title.trim().slice(0,At),a=e.url.trim().slice(0,wt),p=e.text?.trim().slice(0,vt),l=e.author?.trim().slice(0,Mt),s=e.kind==="pull"?"pull":"issue",c={providerId:e.providerId.trim(),id:t,title:r||t,url:a,kind:s};if(p&&(c.text=p),l&&(c.author=l),s==="pull"){let h=St(e.branches?.head),A=St(e.branches?.base);h&&A&&(c.branches={head:h,base:A})}return Ut(e.data)&&(c.data=e.data),c},Ye=e=>{let t=se(e);return e.projectId&&(t.projectId=e.projectId),e.navigation&&(t.navigation=e.navigation),e.worktree&&(t.worktree=e.worktree),t},Je=e=>{let t={text:e.text.trim().slice(0,_t)};return e.send&&(t.send=!0),t},We=e=>e===null||!Number.isFinite(e)?null:Math.min(Rt,Math.max(0,Math.round(e)));var ie=e=>e.length>0&&e.length<=_e&&!e.includes("\0")&&!e.includes("\\");var Me=e=>!e.startsWith("/")||e.includes("\0")||e.includes("\\")||e.includes("://")||e.length>Ct?!1:!e.split("/").some(r=>r==="."||r===".."),so=new Set(It),Qe=e=>!!(e&&"status"in e&&so.has(String(e.status))&&!("body"in e)),Oe=e=>!!(e&&"status"in e&&"body"in e&&Number.isInteger(e.status)),Ze=e=>!!(e&&"content"in e&&String(e.content)===e.content),et=e=>!!(e&&"written"in e&&e.written===!0),tt=e=>!!(e&&"entries"in e&&Array.isArray(e.entries)),io=new Set(Tt),ot=e=>!!(e&&"kind"in e&&"size"in e&&io.has(String(e.kind))&&Number.isFinite(e.size)),nt=e=>!!(e&&"text"in e&&String(e.text)===e.text&&!("status"in e)),ao=new Set(["workspace","ready","directory","session","connection","settings","session-lifecycle","item","resolve","action"]),co=e=>Object(e)===e?e:null,Fe=e=>String(e)===e&&e.length>0,lo=e=>{if(!Fe(e.id))return null;if(e.ok===!0){let t={channel:k,v:1,type:"result",id:e.id,ok:!0};return Object(e.payload)===e.payload&&(t.payload=e.payload),t}return e.ok===!1&&Fe(e.error)?{channel:k,v:1,type:"result",id:e.id,ok:!1,error:e.error,code:Nt(Fe(e.code)?e.code:void 0)}:null},rt=e=>{let t=co(e);return!t||t.channel!==k||t.v!==1?null:t.type==="result"?lo(t):!ao.has(String(t.type))||Object(t.payload)!==t.payload?null:t};var u=class extends Error{code;constructor(t,r){super(r),this.name="HostRequestError",this.code=t}},Dt=()=>Promise.reject(new u("BAD_PATH",'Request path must start with "/" and stay on the declared origin.')),Le=()=>Promise.reject(new u("BAD_PATH",`File path must be 1 to ${_e} characters without NUL or backslash.`)),S=e=>(e.value+=1,`oc-${e.value}`),st=(e={})=>{let t=e.target??("window"in globalThis?window:null);if(!t)throw new u("HOST_UNAVAILABLE","No window. connectHost runs in a browser frame.");let r=e.acceptSource??(o=>o===t.parent),a=e.requestTimeoutMs??Ve,p=new Set,l=new Set,s=new Set,c=new Set,h=new Set,A=new Set,i=new Set,g=null,f=null,x=new Map,I=new Map,W=!1,E={value:0},T=null,q=null,xt=o=>o?{sessionId:o.id,phase:o.busy?"started":"completed"}:null,Q=o=>{t.parent.postMessage(o,"*")},N=(o,n)=>{for(let m of o)try{m(n)}catch(w){console.error(w)}},bt=o=>{if(!(o instanceof MessageEvent)||!r(o.source))return;let n=rt(o.data);if(!n)return;if(n.type==="workspace"){let w=I.get(n.payload.subscriptionId);w&&N([w],n.payload.snapshot);return}if(n.type==="ready"){T=n.payload,q=xt(n.payload.session),N(p,n.payload),N(l,n.payload.directory),N(s,n.payload.session),q&&N(c,q),N(h,n.payload.connection),N(A,n.payload.settings),N(i,n.payload.item);return}if(n.type==="directory"){T&&(T={...T,directory:n.payload.directory}),N(l,n.payload.directory);return}if(n.type==="session"){T&&(T={...T,session:n.payload.session}),n.payload.session?q?.sessionId!==n.payload.session.id&&(q=xt(n.payload.session)):q=null,N(s,n.payload.session);return}if(n.type==="session-lifecycle"){q=n.payload,N(c,n.payload);return}if(n.type==="connection"){T&&(T={...T,connection:n.payload.connection}),N(h,n.payload.connection);return}if(n.type==="settings"){T&&(T={...T,settings:n.payload.settings}),N(A,n.payload.settings);return}if(n.type==="item"){T&&(T={...T,item:n.payload.item}),N(i,n.payload.item);return}if(n.type==="action"){let w=G=>{W||Q({channel:k,v:1,type:"action-result",id:n.id,payload:G})},F=f;if(!F){w({ok:!1,error:"This extension does not handle background actions."});return}Promise.resolve().then(()=>F(n.payload)).then(()=>w({ok:!0}),G=>{let Xe=(G instanceof Error?G.message:String(G)).trim();w({ok:!1,error:(Xe||"Action failed.").slice(0,ve)})});return}if(n.type==="resolve"){let w=G=>{Q({channel:k,v:1,type:"resolve-result",id:n.id,payload:G})},F=g;if(!F){w({error:"This extension does not resolve commands."});return}Promise.resolve().then(()=>F(n.payload)).then(G=>w({item:G?se(G):null}),G=>{let Xe=(G instanceof Error?G.message:String(G)).trim();w({error:(Xe||"Command failed.").slice(0,ve)})});return}let m=x.get(n.id);if(m){if(clearTimeout(m.timer),x.delete(n.id),n.ok){m.resolve(n.payload);return}m.reject(new u(n.code,n.error))}};t.addEventListener("message",bt),Q({channel:k,v:1,type:"hello"});let H=(o,n=a)=>W||t.parent===t?Promise.reject(new u("HOST_UNAVAILABLE","No host frame. This page is not in an iframe.")):new Promise((m,w)=>{let F=setTimeout(()=>{x.delete(o.id),w(new u("HOST_TIMEOUT","Host did not answer in time."))},n);x.set(o.id,{resolve:m,reject:w,timer:F}),Q(o)}),P=o=>H(o).then(()=>{}),Z={channel:k,v:1},ee=(o,n=1024)=>{if(!o.trim()||o.length>n)throw new u("HOST_REJECTED",`Identity must contain 1 to ${n} characters.`)},He=async o=>{o.kind!=="projects"&&ee(o.projectId);let n=await H({...Z,type:"workspace-read",id:S(E),payload:o});if(!n||!("kind"in n)||!("state"in n)||n.kind!==o.kind)throw new u("HOST_REJECTED","Host did not return workspace data.");return n},Pe=async(o,n)=>{o.kind!=="projects"&&ee(o.projectId);let m=S(E);I.set(m,n);try{await P({...Z,type:"workspace-subscribe",id:S(E),payload:{subscriptionId:m,query:o}})}catch(w){throw I.delete(m),W||Q({...Z,type:"workspace-unsubscribe",id:S(E),payload:{subscriptionId:m}}),w}return()=>{!I.delete(m)||W||Q({...Z,type:"workspace-unsubscribe",id:S(E),payload:{subscriptionId:m}})}},be=async o=>{if("key"in o&&(o.key.length===0||o.key.length>128))throw new u("HOST_REJECTED","Storage key must contain 1 to 128 characters.");if(o.op==="set"&&!re(o.value))throw new u("HOST_REJECTED","Storage values must be JSON.");if(o.op==="set"&&new TextEncoder().encode(JSON.stringify(o.value)).length>65536)throw new u("HOST_REJECTED","Storage value exceeds 64 KiB.");let n=await H({...Z,type:"storage",id:S(E),payload:o});if(!n||!("storage"in n)||n.op!==o.op)throw new u("HOST_REJECTED","Host did not return storage data.");return n};return{onAction:o=>(f=o,()=>{f===o&&(f=null)}),listProjects:async()=>{let o=await He({kind:"projects"});if(o.kind!=="projects")throw new u("HOST_REJECTED","Expected projects.");return o},listWorktrees:async o=>{let n=await He({kind:"worktrees",projectId:o});if(n.kind!=="worktrees")throw new u("HOST_REJECTED","Expected worktrees.");return n},listSessions:async o=>{let n=await He({kind:"sessions",projectId:o});if(n.kind!=="sessions")throw new u("HOST_REJECTED","Expected sessions.");return n},onProjects:o=>Pe({kind:"projects"},n=>{n.kind==="projects"&&o(n)}),onWorktrees:(o,n)=>Pe({kind:"worktrees",projectId:o},m=>{m.kind==="worktrees"&&n(m)}),onSessions:(o,n)=>Pe({kind:"sessions",projectId:o},m=>{m.kind==="sessions"&&n(m)}),openSession:async o=>{ee(o),await P({...Z,type:"open-session",id:S(E),payload:{sessionId:o}})},storage:{get:async o=>{let n=await be({op:"get",key:o});return n.op==="get"&&n.found?n.value:void 0},set:async(o,n)=>{await be({op:"set",key:o,value:n})},delete:async o=>{await be({op:"delete",key:o})},keys:async()=>{let o=await be({op:"keys"});if(o.op!=="keys")throw new u("HOST_REJECTED","Expected storage keys.");return o.keys}},onReady:o=>(p.add(o),T&&o(T),()=>{p.delete(o)}),onDirectory:o=>(l.add(o),T&&o(T.directory),()=>{l.delete(o)}),onSession:o=>(s.add(o),T&&o(T.session),()=>{s.delete(o)}),onSessionLifecycle:o=>(c.add(o),q&&o(q),()=>{c.delete(o)}),onConnection:o=>(h.add(o),T&&o(T.connection),()=>{h.delete(o)}),onSettings:o=>(A.add(o),T&&o(T.settings),()=>{A.delete(o)}),onItem:o=>(i.add(o),T&&o(T.item),()=>{i.delete(o)}),onResolve:o=>(g=o,()=>{g===o&&(g=null)}),toast:o=>{let n=o.message.trim();return!n||n.length>Se?Promise.reject(new u("HOST_REJECTED",`Toast message must contain 1 to ${Se} characters.`)):o.copy&&o.copy!==!0&&(!o.copy.text.length||o.copy.text.length>Te)?Promise.reject(new u("HOST_REJECTED",`Toast copy text must contain 1 to ${Te} characters.`)):P({channel:k,v:1,type:"toast",id:S(E),payload:{...o,message:n}})},openUrl:o=>P({channel:k,v:1,type:"open-url",id:S(E),payload:{url:o}}),openSurface:o=>P({channel:k,v:1,type:"open-surface",id:S(E),payload:{surfaceId:o}}),writeClipboard:o=>P({channel:k,v:1,type:"clipboard-write",id:S(E),payload:{text:o}}),compose:o=>P({channel:k,v:1,type:"compose",id:S(E),payload:o}),attach:o=>P({channel:k,v:1,type:"attach",id:S(E),payload:se(o)}),startSession:async o=>{o.projectId!==void 0&&ee(o.projectId);let n=o.worktree;n&&n!==!0&&(n.kind==="existing"?ee(n.directory):(n.name!==void 0&&ee(n.name,200),n.baseBranch!==void 0&&ee(n.baseBranch,200)));let m=await H({channel:k,v:1,type:"start-session",id:S(E),payload:Ye(o)},e.requestTimeoutMs??18e4);if(!ze(m))throw new u("HOST_REJECTED","Host did not return a session.");return m},prompt:o=>H({channel:k,v:1,type:"prompt",id:S(E),payload:Je(o)}).then(n=>{if(!Ke(n))throw new u("HOST_REJECTED","Host did not return a prompt result.");return n}),sessionLink:o=>P({channel:k,v:1,type:"session-link",id:S(E),payload:se(o)}),close:()=>P({channel:k,v:1,type:"close",id:S(E)}),oauthStart:()=>P({channel:k,v:1,type:"oauth-start",id:S(E)}),oauthDisconnect:()=>P({channel:k,v:1,type:"oauth-disconnect",id:S(E)}),request:o=>(Me(o.path)?H({channel:k,v:1,type:"request",id:S(E),payload:o}):Dt()).then(n=>{if(!Oe(n))throw new u("HOST_REJECTED","Host request result was empty.");return n}),serviceRequest:o=>(Me(o.path)?H({channel:k,v:1,type:"service-request",id:S(E),payload:o}):Dt()).then(n=>{if(!Oe(n))throw new u("HOST_REJECTED","Host service request result was empty.");return n}),serviceStatus:()=>H({channel:k,v:1,type:"service-status",id:S(E)}).then(o=>{if(!Qe(o))throw new u("HOST_REJECTED","Host did not return service status.");return o}),readFile:o=>(ie(o)?H({channel:k,v:1,type:"file-read",id:S(E),payload:{path:o}}):Le()).then(n=>{if(!Ze(n))throw new u("HOST_REJECTED","Host did not return file content.");return n}),writeFile:(o,n)=>ie(o)?n.length>ye?Promise.reject(new u("FILE_TOO_LARGE",`Content is over ${ye} characters.`)):H({channel:k,v:1,type:"file-write",id:S(E),payload:{path:o,content:n}}).then(m=>{if(!et(m))throw new u("HOST_REJECTED","Host did not confirm the write.");return m}):Le(),listDir:o=>(ie(o)?H({channel:k,v:1,type:"file-list",id:S(E),payload:{path:o}}):Le()).then(n=>{if(!tt(n))throw new u("HOST_REJECTED","Host did not return directory entries.");return n}),stat:o=>(ie(o)?H({channel:k,v:1,type:"file-stat",id:S(E),payload:{path:o}}):Le()).then(n=>{if(!ot(n))throw new u("HOST_REJECTED","Host did not return file status.");return n}),generate:o=>{let n=o.prompt.trim(),m=o.system?.trim();if(n.length===0||n.length>Ae)return Promise.reject(new u("HOST_REJECTED",`Prompt must be 1 to ${Ae} characters.`));if(m!==void 0&&(m.length===0||m.length>we))return Promise.reject(new u("HOST_REJECTED",`System prompt must be 1 to ${we} characters.`));let w=o.maxOutputTokens===void 0?void 0:Math.min(je,Math.max(1,Math.floor(o.maxOutputTokens)));if(w!==void 0&&!Number.isFinite(w))return Promise.reject(new u("HOST_REJECTED","maxOutputTokens must be a number."));let F={prompt:n};return m!==void 0&&(F.system=m),w!==void 0&&(F.maxOutputTokens=w),H({channel:k,v:1,type:"generate",id:S(E),payload:F},e.requestTimeoutMs??qe).then(G=>{if(!nt(G))throw new u("HOST_REJECTED","Host did not return generated text.");return G})},setBadge:o=>P({channel:k,v:1,type:"badge",id:S(E),payload:{count:We(o)}}),dispose:()=>{for(let o of I.keys())Q({...Z,type:"workspace-unsubscribe",id:S(E),payload:{subscriptionId:o}});I.clear(),W=!0,g=null,f=null,t.removeEventListener("message",bt);for(let o of x.values())clearTimeout(o.timer),o.reject(new u("HOST_UNAVAILABLE","Host client was disposed."));x.clear(),p.clear(),l.clear(),s.clear(),c.clear(),h.clear(),A.clear(),i.clear()}}};var po=[["--oc-bg","background"],["--oc-elevated","elevated"],["--oc-fg","foreground"],["--oc-muted","muted"],["--oc-subtle","subtle"],["--oc-border","border"],["--oc-hover","hover"],["--oc-selection","selection"],["--oc-focus","focus"],["--oc-primary","primary"],["--oc-muted-surface","mutedSurface"],["--oc-elevated-fg","elevatedForeground"],["--oc-active","active"],["--oc-selection-fg","selectionForeground"],["--oc-primary-fg","primaryForeground"],["--oc-primary-text","primaryText"],["--oc-success-text","successText"],["--oc-warning-text","warningText"],["--oc-error-text","errorText"],["--oc-info-text","infoText"],["--oc-success","success"],["--oc-warning","warning"],["--oc-error","error"],["--oc-info","info"],["--oc-font","font"],["--oc-mono","mono"],["--oc-radius","radius"],["--surface-background","background"],["--surface-elevated","elevated"],["--surface-foreground","foreground"],["--surface-muted-foreground","muted"],["--surface-subtle","subtle"],["--interactive-border","border"],["--interactive-hover","hover"],["--interactive-selection","selection"],["--interactive-focus-ring","focus"],["--primary","primary"],["--surface-muted","mutedSurface"],["--surface-elevated-foreground","elevatedForeground"],["--interactive-active","active"],["--interactive-selection-foreground","selectionForeground"],["--primary-foreground","primaryForeground"],["--primary-text","primaryText"],["--success-text","successText"],["--warning-text","warningText"],["--error-text","errorText"],["--info-text","infoText"],["--status-success","success"],["--status-warning","warning"],["--status-error","error"],["--status-info","info"],["--font-sans","font"],["--font-mono","mono"],["--radius","radius"]],Xt=(e,t)=>{t.style.colorScheme=e.mode;for(let[r,a]of po)t.style.setProperty(r,e.tokens[a]);t.style.setProperty("font-family",e.tokens.font),t.style.setProperty("font-size","0.875rem"),t.style.setProperty("line-height","1.45"),t.style.setProperty("color",e.tokens.foreground)},it=(e,t)=>{Xt(e.theme,t),t.dataset&&(t.dataset.ocSurface=e.surface,t.dataset.ocTheme=e.theme.mode)};var uo={"surface-background":"bg","surface-elevated":"elevated","surface-elevated-foreground":"elevated-fg","surface-foreground":"fg","surface-muted-foreground":"muted","surface-muted":"muted-surface","surface-subtle":"subtle","interactive-border":"border","interactive-hover":"hover","interactive-active":"active","interactive-selection":"selection","interactive-selection-foreground":"selection-fg","interactive-focus-ring":"focus",primary:"primary","primary-foreground":"primary-fg","primary-text":"primary-text","success-text":"success-text","warning-text":"warning-text","error-text":"error-text","info-text":"info-text","status-success":"success","status-warning":"warning","status-error":"error","status-info":"info","font-sans":"font","font-mono":"mono",radius:"radius"},y=(e,t)=>`var(--${e}, var(--oc-${uo[e]}, ${t}))`,te=y("surface-background","transparent"),Ce=y("surface-elevated","transparent"),pe=y("surface-elevated-foreground","inherit"),ue=y("surface-foreground","inherit"),v=y("surface-muted-foreground","gray"),fo=y("surface-muted","transparent"),B=y("interactive-border","currentColor"),X=y("interactive-hover","transparent"),oe=y("interactive-active","transparent"),at=y("interactive-selection","transparent"),ct=y("interactive-selection-foreground","inherit"),zt=y("interactive-focus-ring","currentColor"),z=y("primary","currentColor"),dt=y("primary-text","inherit"),lt=y("error-text","inherit"),mo=y("font-sans","inherit"),pt=y("font-mono","monospace"),Bt=y("radius","9px"),O=(e,t,r="transparent")=>`color-mix(in srgb, ${e} ${t}%, ${r})`,Ft=`box-shadow: 0 0 0 2px ${zt};`,Re=e=>{let t=y(`status-${e}`,"currentColor");return`
|
|
31
|
+
`;var yt=["file","directory","other","missing"],Ve=e=>!!(e&&"sessionId"in e),je=e=>!!(e&&"sent"in e&&!("sessionId"in e));var Te=500,_e=32e3,At=16e3,wt=128,vt=200,Mt=2e3,Ot=16e3,Lt=80,Ct=200,Rt=16e3;var Gt=2e3;var Ye=2e4,ye=1024,Ae=2e6;var we=64e3,ve=8e3,qe=4e3;var Je=9e4;var It=999,Me=500,$t=["HOST_UNAVAILABLE","HOST_TIMEOUT","HOST_REJECTED","DISCONNECTED","DISABLED","BAD_PATH","NO_INTEGRATION","NO_SERVICE","SERVICE_FAILED","NO_SESSION","SESSION_BUSY","NOT_GRANTED","NO_DIRECTORY","NOT_FOUND","FILE_TOO_LARGE","DENIED","NO_MODEL","MODEL_FAILED"],Nt=["stopped","starting","ready","failed"],io=new Set($t),Ut=e=>io.has(e),Dt=e=>e&&Ut(e)?e:"HOST_REJECTED",ne=e=>e===void 0?!1:e===null||e===!0||e===!1||String(e)===e?!0:Number(e)===e?Number.isFinite(e):Array.isArray(e)?e.every(ne):Object(e)===e?Object.values(e).every(ne):!1,Ht=e=>ne(e)&&JSON.stringify(e).length<=Rt,_t=e=>e?.trim().slice(0,Ct)??"",re=e=>{let t=e.id.trim().slice(0,wt),r=e.title.trim().slice(0,vt),a=e.url.trim().slice(0,Mt),l=e.text?.trim().slice(0,Ot),p=e.author?.trim().slice(0,Lt),s=e.kind==="pull"?"pull":"issue",c={providerId:e.providerId.trim(),id:t,title:r||t,url:a,kind:s};if(l&&(c.text=l),p&&(c.author=p),s==="pull"){let h=_t(e.branches?.head),A=_t(e.branches?.base);h&&A&&(c.branches={head:h,base:A})}return Ht(e.data)&&(c.data=e.data),c},We=e=>{let t=re(e);return e.projectId&&(t.projectId=e.projectId),e.navigation&&(t.navigation=e.navigation),e.worktree&&(t.worktree=e.worktree),t},Qe=e=>{let t={text:e.text.trim().slice(0,At)};return e.send&&(t.send=!0),t},Ze=e=>e===null||!Number.isFinite(e)?null:Math.min(It,Math.max(0,Math.round(e)));var se=e=>e.length>0&&e.length<=ye&&!e.includes("\0")&&!e.includes("\\");var Oe=e=>!e.startsWith("/")||e.includes("\0")||e.includes("\\")||e.includes("://")||e.length>Gt?!1:!e.split("/").some(r=>r==="."||r===".."),ao=new Set(Nt),et=e=>!!(e&&"status"in e&&ao.has(String(e.status))&&!("body"in e)),Le=e=>!!(e&&"status"in e&&"body"in e&&Number.isInteger(e.status)),tt=e=>!!(e&&"content"in e&&String(e.content)===e.content),ot=e=>!!(e&&"written"in e&&e.written===!0),nt=e=>!!(e&&"entries"in e&&Array.isArray(e.entries)),co=new Set(yt),rt=e=>!!(e&&"kind"in e&&"size"in e&&co.has(String(e.kind))&&Number.isFinite(e.size)),st=e=>!!(e&&"text"in e&&String(e.text)===e.text&&!("status"in e)),lo=new Set(["workspace","ready","directory","session","connection","settings","session-lifecycle","item","resolve","action"]),po=e=>Object(e)===e?e:null,Ke=e=>String(e)===e&&e.length>0,uo=e=>{if(!Ke(e.id))return null;if(e.ok===!0){let t={channel:x,v:1,type:"result",id:e.id,ok:!0};return Object(e.payload)===e.payload&&(t.payload=e.payload),t}return e.ok===!1&&Ke(e.error)?{channel:x,v:1,type:"result",id:e.id,ok:!1,error:e.error,code:Dt(Ke(e.code)?e.code:void 0)}:null},it=e=>{let t=po(e);return!t||t.channel!==x||t.v!==1?null:t.type==="result"?uo(t):!lo.has(String(t.type))||Object(t.payload)!==t.payload?null:t};var u=class extends Error{code;constructor(t,r){super(r),this.name="HostRequestError",this.code=t}},Pt=()=>Promise.reject(new u("BAD_PATH",'Request path must start with "/" and stay on the declared origin.')),Ce=()=>Promise.reject(new u("BAD_PATH",`File path must be 1 to ${ye} characters without NUL or backslash.`)),S=e=>(e.value+=1,`oc-${e.value}`),at=(e={})=>{let t=e.target??("window"in globalThis?window:null);if(!t)throw new u("HOST_UNAVAILABLE","No window. connectHost runs in a browser frame.");let r=e.acceptSource??(o=>o===t.parent),a=e.requestTimeoutMs??Ye,l=new Set,p=new Set,s=new Set,c=new Set,h=new Set,A=new Set,i=new Set,g=null,f=null,k=new Map,I=new Map,J=!1,E={value:0},T=null,j=null,St=o=>o?{sessionId:o.id,phase:o.busy?"started":"completed"}:null,W=o=>{t.parent.postMessage(o,"*")},N=(o,n)=>{for(let m of o)try{m(n)}catch(w){console.error(w)}},Tt=o=>{if(!(o instanceof MessageEvent)||!r(o.source))return;let n=it(o.data);if(!n)return;if(n.type==="workspace"){let w=I.get(n.payload.subscriptionId);w&&N([w],n.payload.snapshot);return}if(n.type==="ready"){T=n.payload,j=St(n.payload.session),N(l,n.payload),N(p,n.payload.directory),N(s,n.payload.session),j&&N(c,j),N(h,n.payload.connection),N(A,n.payload.settings),N(i,n.payload.item);return}if(n.type==="directory"){T&&(T={...T,directory:n.payload.directory}),N(p,n.payload.directory);return}if(n.type==="session"){T&&(T={...T,session:n.payload.session}),n.payload.session?j?.sessionId!==n.payload.session.id&&(j=St(n.payload.session)):j=null,N(s,n.payload.session);return}if(n.type==="session-lifecycle"){j=n.payload,N(c,n.payload);return}if(n.type==="connection"){T&&(T={...T,connection:n.payload.connection}),N(h,n.payload.connection);return}if(n.type==="settings"){T&&(T={...T,settings:n.payload.settings}),N(A,n.payload.settings);return}if(n.type==="item"){T&&(T={...T,item:n.payload.item}),N(i,n.payload.item);return}if(n.type==="action"){let w=G=>{J||W({channel:x,v:1,type:"action-result",id:n.id,payload:G})},F=f;if(!F){w({ok:!1,error:"This extension does not handle background actions."});return}Promise.resolve().then(()=>F(n.payload)).then(()=>w({ok:!0}),G=>{let Fe=(G instanceof Error?G.message:String(G)).trim();w({ok:!1,error:(Fe||"Action failed.").slice(0,Me)})});return}if(n.type==="resolve"){let w=G=>{W({channel:x,v:1,type:"resolve-result",id:n.id,payload:G})},F=g;if(!F){w({error:"This extension does not resolve commands."});return}Promise.resolve().then(()=>F(n.payload)).then(G=>w({item:G?re(G):null}),G=>{let Fe=(G instanceof Error?G.message:String(G)).trim();w({error:(Fe||"Command failed.").slice(0,Me)})});return}let m=k.get(n.id);if(m){if(clearTimeout(m.timer),k.delete(n.id),n.ok){m.resolve(n.payload);return}m.reject(new u(n.code,n.error))}};t.addEventListener("message",Tt),W({channel:x,v:1,type:"hello"});let H=(o,n=a)=>J||t.parent===t?Promise.reject(new u("HOST_UNAVAILABLE","No host frame. This page is not in an iframe.")):new Promise((m,w)=>{let F=setTimeout(()=>{k.delete(o.id),w(new u("HOST_TIMEOUT","Host did not answer in time."))},n);k.set(o.id,{resolve:m,reject:w,timer:F}),W(o)}),P=o=>H(o).then(()=>{}),Q={channel:x,v:1},Z=(o,n=1024)=>{if(!o.trim()||o.length>n)throw new u("HOST_REJECTED",`Identity must contain 1 to ${n} characters.`)},Xe=async o=>{o.kind!=="projects"&&Z(o.projectId);let n=await H({...Q,type:"workspace-read",id:S(E),payload:o});if(!n||!("kind"in n)||!("state"in n)||n.kind!==o.kind)throw new u("HOST_REJECTED","Host did not return workspace data.");return n},Be=async(o,n)=>{o.kind!=="projects"&&Z(o.projectId);let m=S(E);I.set(m,n);try{await P({...Q,type:"workspace-subscribe",id:S(E),payload:{subscriptionId:m,query:o}})}catch(w){throw I.delete(m),J||W({...Q,type:"workspace-unsubscribe",id:S(E),payload:{subscriptionId:m}}),w}return()=>{!I.delete(m)||J||W({...Q,type:"workspace-unsubscribe",id:S(E),payload:{subscriptionId:m}})}},Se=async o=>{if("key"in o&&(o.key.length===0||o.key.length>128))throw new u("HOST_REJECTED","Storage key must contain 1 to 128 characters.");if(o.op==="set"&&!ne(o.value))throw new u("HOST_REJECTED","Storage values must be JSON.");if(o.op==="set"&&new TextEncoder().encode(JSON.stringify(o.value)).length>65536)throw new u("HOST_REJECTED","Storage value exceeds 64 KiB.");let n=await H({...Q,type:"storage",id:S(E),payload:o});if(!n||!("storage"in n)||n.op!==o.op)throw new u("HOST_REJECTED","Host did not return storage data.");return n};return{onAction:o=>(f=o,()=>{f===o&&(f=null)}),listProjects:async()=>{let o=await Xe({kind:"projects"});if(o.kind!=="projects")throw new u("HOST_REJECTED","Expected projects.");return o},listWorktrees:async o=>{let n=await Xe({kind:"worktrees",projectId:o});if(n.kind!=="worktrees")throw new u("HOST_REJECTED","Expected worktrees.");return n},listSessions:async o=>{let n=await Xe({kind:"sessions",projectId:o});if(n.kind!=="sessions")throw new u("HOST_REJECTED","Expected sessions.");return n},onProjects:o=>Be({kind:"projects"},n=>{n.kind==="projects"&&o(n)}),onWorktrees:(o,n)=>Be({kind:"worktrees",projectId:o},m=>{m.kind==="worktrees"&&n(m)}),onSessions:(o,n)=>Be({kind:"sessions",projectId:o},m=>{m.kind==="sessions"&&n(m)}),openSession:async o=>{Z(o),await P({...Q,type:"open-session",id:S(E),payload:{sessionId:o}})},storage:{get:async o=>{let n=await Se({op:"get",key:o});return n.op==="get"&&n.found?n.value:void 0},set:async(o,n)=>{await Se({op:"set",key:o,value:n})},delete:async o=>{await Se({op:"delete",key:o})},keys:async()=>{let o=await Se({op:"keys"});if(o.op!=="keys")throw new u("HOST_REJECTED","Expected storage keys.");return o.keys}},onReady:o=>(l.add(o),T&&o(T),()=>{l.delete(o)}),onDirectory:o=>(p.add(o),T&&o(T.directory),()=>{p.delete(o)}),onSession:o=>(s.add(o),T&&o(T.session),()=>{s.delete(o)}),onSessionLifecycle:o=>(c.add(o),j&&o(j),()=>{c.delete(o)}),onConnection:o=>(h.add(o),T&&o(T.connection),()=>{h.delete(o)}),onSettings:o=>(A.add(o),T&&o(T.settings),()=>{A.delete(o)}),onItem:o=>(i.add(o),T&&o(T.item),()=>{i.delete(o)}),onResolve:o=>(g=o,()=>{g===o&&(g=null)}),toast:o=>{let n=o.message.trim();return!n||n.length>Te?Promise.reject(new u("HOST_REJECTED",`Toast message must contain 1 to ${Te} characters.`)):o.copy&&o.copy!==!0&&(!o.copy.text.length||o.copy.text.length>_e)?Promise.reject(new u("HOST_REJECTED",`Toast copy text must contain 1 to ${_e} characters.`)):P({channel:x,v:1,type:"toast",id:S(E),payload:{...o,message:n}})},openUrl:o=>P({channel:x,v:1,type:"open-url",id:S(E),payload:{url:o}}),openSurface:o=>P({channel:x,v:1,type:"open-surface",id:S(E),payload:{surfaceId:o}}),writeClipboard:o=>P({channel:x,v:1,type:"clipboard-write",id:S(E),payload:{text:o}}),compose:o=>P({channel:x,v:1,type:"compose",id:S(E),payload:o}),attach:o=>P({channel:x,v:1,type:"attach",id:S(E),payload:re(o)}),startSession:async o=>{o.projectId!==void 0&&Z(o.projectId);let n=o.worktree;n&&n!==!0&&(n.kind==="existing"?Z(n.directory):(n.name!==void 0&&Z(n.name,200),n.baseBranch!==void 0&&Z(n.baseBranch,200)));let m=await H({channel:x,v:1,type:"start-session",id:S(E),payload:We(o)},e.requestTimeoutMs??18e4);if(!Ve(m))throw new u("HOST_REJECTED","Host did not return a session.");return m},prompt:o=>H({channel:x,v:1,type:"prompt",id:S(E),payload:Qe(o)}).then(n=>{if(!je(n))throw new u("HOST_REJECTED","Host did not return a prompt result.");return n}),sessionLink:o=>P({channel:x,v:1,type:"session-link",id:S(E),payload:re(o)}),close:()=>P({channel:x,v:1,type:"close",id:S(E)}),oauthStart:()=>P({channel:x,v:1,type:"oauth-start",id:S(E)}),oauthDisconnect:()=>P({channel:x,v:1,type:"oauth-disconnect",id:S(E)}),request:o=>(Oe(o.path)?H({channel:x,v:1,type:"request",id:S(E),payload:o}):Pt()).then(n=>{if(!Le(n))throw new u("HOST_REJECTED","Host request result was empty.");return n}),serviceRequest:o=>(Oe(o.path)?H({channel:x,v:1,type:"service-request",id:S(E),payload:o}):Pt()).then(n=>{if(!Le(n))throw new u("HOST_REJECTED","Host service request result was empty.");return n}),serviceStatus:()=>H({channel:x,v:1,type:"service-status",id:S(E)}).then(o=>{if(!et(o))throw new u("HOST_REJECTED","Host did not return service status.");return o}),readFile:o=>(se(o)?H({channel:x,v:1,type:"file-read",id:S(E),payload:{path:o}}):Ce()).then(n=>{if(!tt(n))throw new u("HOST_REJECTED","Host did not return file content.");return n}),writeFile:(o,n)=>se(o)?n.length>Ae?Promise.reject(new u("FILE_TOO_LARGE",`Content is over ${Ae} characters.`)):H({channel:x,v:1,type:"file-write",id:S(E),payload:{path:o,content:n}}).then(m=>{if(!ot(m))throw new u("HOST_REJECTED","Host did not confirm the write.");return m}):Ce(),listDir:o=>(se(o)?H({channel:x,v:1,type:"file-list",id:S(E),payload:{path:o}}):Ce()).then(n=>{if(!nt(n))throw new u("HOST_REJECTED","Host did not return directory entries.");return n}),stat:o=>(se(o)?H({channel:x,v:1,type:"file-stat",id:S(E),payload:{path:o}}):Ce()).then(n=>{if(!rt(n))throw new u("HOST_REJECTED","Host did not return file status.");return n}),generate:o=>{let n=o.prompt.trim(),m=o.system?.trim();if(n.length===0||n.length>we)return Promise.reject(new u("HOST_REJECTED",`Prompt must be 1 to ${we} characters.`));if(m!==void 0&&(m.length===0||m.length>ve))return Promise.reject(new u("HOST_REJECTED",`System prompt must be 1 to ${ve} characters.`));let w=o.maxOutputTokens===void 0?void 0:Math.min(qe,Math.max(1,Math.floor(o.maxOutputTokens)));if(w!==void 0&&!Number.isFinite(w))return Promise.reject(new u("HOST_REJECTED","maxOutputTokens must be a number."));let F={prompt:n};return m!==void 0&&(F.system=m),w!==void 0&&(F.maxOutputTokens=w),H({channel:x,v:1,type:"generate",id:S(E),payload:F},e.requestTimeoutMs??Je).then(G=>{if(!st(G))throw new u("HOST_REJECTED","Host did not return generated text.");return G})},setBadge:o=>P({channel:x,v:1,type:"badge",id:S(E),payload:{count:Ze(o)}}),dispose:()=>{for(let o of I.keys())W({...Q,type:"workspace-unsubscribe",id:S(E),payload:{subscriptionId:o}});I.clear(),J=!0,g=null,f=null,t.removeEventListener("message",Tt);for(let o of k.values())clearTimeout(o.timer),o.reject(new u("HOST_UNAVAILABLE","Host client was disposed."));k.clear(),l.clear(),p.clear(),s.clear(),c.clear(),h.clear(),A.clear(),i.clear()}}};var fo=[["--oc-bg","background"],["--oc-elevated","elevated"],["--oc-fg","foreground"],["--oc-muted","muted"],["--oc-subtle","subtle"],["--oc-border","border"],["--oc-hover","hover"],["--oc-selection","selection"],["--oc-focus","focus"],["--oc-primary","primary"],["--oc-muted-surface","mutedSurface"],["--oc-elevated-fg","elevatedForeground"],["--oc-active","active"],["--oc-selection-fg","selectionForeground"],["--oc-primary-fg","primaryForeground"],["--oc-primary-text","primaryText"],["--oc-success-text","successText"],["--oc-warning-text","warningText"],["--oc-error-text","errorText"],["--oc-info-text","infoText"],["--oc-success","success"],["--oc-warning","warning"],["--oc-error","error"],["--oc-info","info"],["--oc-font","font"],["--oc-mono","mono"],["--oc-radius","radius"],["--surface-background","background"],["--surface-elevated","elevated"],["--surface-foreground","foreground"],["--surface-muted-foreground","muted"],["--surface-subtle","subtle"],["--interactive-border","border"],["--interactive-hover","hover"],["--interactive-selection","selection"],["--interactive-focus-ring","focus"],["--primary","primary"],["--surface-muted","mutedSurface"],["--surface-elevated-foreground","elevatedForeground"],["--interactive-active","active"],["--interactive-selection-foreground","selectionForeground"],["--primary-foreground","primaryForeground"],["--primary-text","primaryText"],["--success-text","successText"],["--warning-text","warningText"],["--error-text","errorText"],["--info-text","infoText"],["--status-success","success"],["--status-warning","warning"],["--status-error","error"],["--status-info","info"],["--font-sans","font"],["--font-mono","mono"],["--radius","radius"]],Ft=(e,t)=>{t.style.colorScheme=e.mode;for(let[r,a]of fo)t.style.setProperty(r,e.tokens[a]);t.style.setProperty("font-family",e.tokens.font),t.style.setProperty("font-size","0.875rem"),t.style.setProperty("line-height","1.45"),t.style.setProperty("color",e.tokens.foreground)},ct=(e,t)=>{Ft(e.theme,t),t.dataset&&(t.dataset.ocSurface=e.surface,t.dataset.ocTheme=e.theme.mode)};var mo={"surface-background":"bg","surface-elevated":"elevated","surface-elevated-foreground":"elevated-fg","surface-foreground":"fg","surface-muted-foreground":"muted","surface-muted":"muted-surface","surface-subtle":"subtle","interactive-border":"border","interactive-hover":"hover","interactive-active":"active","interactive-selection":"selection","interactive-selection-foreground":"selection-fg","interactive-focus-ring":"focus",primary:"primary","primary-foreground":"primary-fg","primary-text":"primary-text","success-text":"success-text","warning-text":"warning-text","error-text":"error-text","info-text":"info-text","status-success":"success","status-warning":"warning","status-error":"error","status-info":"info","font-sans":"font","font-mono":"mono",radius:"radius"},y=(e,t)=>`var(--${e}, var(--oc-${mo[e]}, ${t}))`,ee=y("surface-background","transparent"),Re=y("surface-elevated","transparent"),le=y("surface-elevated-foreground","inherit"),pe=y("surface-foreground","inherit"),v=y("surface-muted-foreground","gray"),ho=y("surface-muted","transparent"),B=y("interactive-border","currentColor"),X=y("interactive-hover","transparent"),te=y("interactive-active","transparent"),dt=y("interactive-selection","transparent"),lt=y("interactive-selection-foreground","inherit"),Vt=y("interactive-focus-ring","currentColor"),z=y("primary","currentColor"),pt=y("primary-text","inherit"),ut=y("error-text","inherit"),go=y("font-sans","inherit"),ft=y("font-mono","monospace"),zt=y("radius","9px"),O=(e,t,r="transparent")=>`color-mix(in srgb, ${e} ${t}%, ${r})`,Kt=`box-shadow: 0 0 0 2px ${Vt};`,Ge=e=>{let t=y(`status-${e}`,"currentColor");return`
|
|
32
32
|
.oc-sdk[data-tone="${e}"], .oc-sdk [data-tone="${e}"] { --oc-sdk-tone: ${t}; --oc-sdk-tone-text: ${y(`${e}-text`,"inherit")}; }`},$=`
|
|
33
|
-
${
|
|
34
|
-
.oc-sdk { box-sizing: border-box; color: ${
|
|
33
|
+
${ze}
|
|
34
|
+
.oc-sdk { box-sizing: border-box; color: ${pe}; font-family: ${go}; font-size: 0.875rem; line-height: 1.45; }
|
|
35
35
|
.oc-sdk *, .oc-sdk *::before, .oc-sdk *::after { box-sizing: border-box; }
|
|
36
36
|
/* :where() keeps the reset at zero specificity so every primitive class below overrides it. */
|
|
37
37
|
:where(.oc-sdk) :where(button, input, textarea), :where(button.oc-sdk, input.oc-sdk, textarea.oc-sdk) { font: inherit; color: inherit; margin: 0; }
|
|
38
38
|
:where(.oc-sdk) :where(button), :where(button.oc-sdk) { cursor: pointer; background: none; border: 0; padding: 0; }
|
|
39
39
|
.oc-sdk button:disabled, button.oc-sdk:disabled, .oc-sdk[aria-disabled="true"], .oc-sdk [aria-disabled="true"] { opacity: .5; pointer-events: none; }
|
|
40
|
-
.oc-sdk :focus-visible { outline: none; ${
|
|
41
|
-
.oc-sdk-mono { font-family: ${
|
|
40
|
+
.oc-sdk :focus-visible { outline: none; ${Kt} }
|
|
41
|
+
.oc-sdk-mono { font-family: ${ft}; }
|
|
42
42
|
.oc-sdk-muted { color: ${v}; }
|
|
43
|
-
${
|
|
44
|
-
.oc-sdk[data-tone="primary"], .oc-sdk [data-tone="primary"] { --oc-sdk-tone: ${z}; --oc-sdk-tone-text: ${
|
|
43
|
+
${Ge("success")}${Ge("warning")}${Ge("error")}${Ge("info")}
|
|
44
|
+
.oc-sdk[data-tone="primary"], .oc-sdk [data-tone="primary"] { --oc-sdk-tone: ${z}; --oc-sdk-tone-text: ${pt}; }
|
|
45
45
|
|
|
46
|
-
.oc-sdk-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 36px; padding: 0 14px; border: 1px solid transparent; border-radius: ${
|
|
46
|
+
.oc-sdk-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 36px; padding: 0 14px; border: 1px solid transparent; border-radius: ${zt}; font-size: 0.875rem; font-weight: 500; line-height: 1; white-space: nowrap; transition: background 150ms ease-out, color 150ms ease-out; }
|
|
47
47
|
.oc-sdk-btn[data-size="sm"] { height: 32px; padding: 0 10px; font-size: 0.8125rem; }
|
|
48
48
|
.oc-sdk-btn[data-size="xs"] { height: 24px; padding: 0 8px; font-size: 0.75rem; border-radius: 6px; }
|
|
49
|
-
.oc-sdk-btn[data-variant="default"] { color: ${
|
|
50
|
-
.oc-sdk-btn[data-variant="default"]:hover { background: ${O(z,16,
|
|
51
|
-
.oc-sdk-btn[data-variant="default"]:active { background: ${O(z,22,
|
|
52
|
-
.oc-sdk-btn[data-variant="secondary"] { background: ${
|
|
49
|
+
.oc-sdk-btn[data-variant="default"] { color: ${pt}; background: ${O(z,10,ee)}; border-color: ${O(z,12)}; }
|
|
50
|
+
.oc-sdk-btn[data-variant="default"]:hover { background: ${O(z,16,ee)}; }
|
|
51
|
+
.oc-sdk-btn[data-variant="default"]:active { background: ${O(z,22,ee)}; }
|
|
52
|
+
.oc-sdk-btn[data-variant="secondary"] { background: ${ho}; color: var(--oc-fg); }
|
|
53
53
|
.oc-sdk-btn[data-variant="secondary"]:hover { background-image: linear-gradient(${X}, ${X}); }
|
|
54
|
-
.oc-sdk-btn[data-variant="secondary"]:active { background-image: linear-gradient(${
|
|
55
|
-
.oc-sdk-btn[data-variant="outline"] { background: ${
|
|
54
|
+
.oc-sdk-btn[data-variant="secondary"]:active { background-image: linear-gradient(${te}, ${te}); }
|
|
55
|
+
.oc-sdk-btn[data-variant="outline"] { background: ${Re}; color: ${le}; border-color: ${B}; }
|
|
56
56
|
.oc-sdk-btn[data-variant="outline"]:hover { background-image: linear-gradient(${X}, ${X}); }
|
|
57
|
-
.oc-sdk-btn[data-variant="outline"]:active { background-image: linear-gradient(${
|
|
57
|
+
.oc-sdk-btn[data-variant="outline"]:active { background-image: linear-gradient(${te}, ${te}); }
|
|
58
58
|
.oc-sdk-btn[data-variant="ghost"] { background: transparent; }
|
|
59
59
|
.oc-sdk-btn[data-variant="ghost"]:hover { background: ${X}; }
|
|
60
|
-
.oc-sdk-btn[data-variant="ghost"]:active { background: ${
|
|
61
|
-
.oc-sdk-btn[data-variant="destructive"] { --oc-sdk-tone: ${y("status-error","red")}; color: ${
|
|
62
|
-
.oc-sdk-btn[data-variant="destructive"]:hover { background: ${O("var(--oc-sdk-tone)",9,
|
|
63
|
-
.oc-sdk-btn[data-variant="destructive"]:active { background: ${O("var(--oc-sdk-tone)",11,
|
|
60
|
+
.oc-sdk-btn[data-variant="ghost"]:active { background: ${te}; }
|
|
61
|
+
.oc-sdk-btn[data-variant="destructive"] { --oc-sdk-tone: ${y("status-error","red")}; color: ${ut}; background: ${O("var(--oc-sdk-tone)",7,ee)}; border-color: ${O("var(--oc-sdk-tone)",12)}; }
|
|
62
|
+
.oc-sdk-btn[data-variant="destructive"]:hover { background: ${O("var(--oc-sdk-tone)",9,ee)}; }
|
|
63
|
+
.oc-sdk-btn[data-variant="destructive"]:active { background: ${O("var(--oc-sdk-tone)",11,ee)}; }
|
|
64
64
|
.oc-sdk-btn[data-loading="true"] { opacity: .5; pointer-events: none; }
|
|
65
65
|
.oc-sdk-btn > .oc-sdk-spinner-ring { width: 14px; height: 14px; }
|
|
66
66
|
|
|
67
67
|
.oc-sdk-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
68
68
|
.oc-sdk-field-label { font-size: 0.8125rem; font-weight: 500; }
|
|
69
69
|
.oc-sdk-field-note { font-size: 0.75rem; color: ${v}; }
|
|
70
|
-
.oc-sdk-field[data-invalid="true"] .oc-sdk-field-note { color: ${
|
|
71
|
-
.oc-sdk-input { display: block; width: 100%; min-width: 0; height: 36px; padding: 0 12px; border: 0; border-radius: ${
|
|
70
|
+
.oc-sdk-field[data-invalid="true"] .oc-sdk-field-note { color: ${ut}; }
|
|
71
|
+
.oc-sdk-input { display: block; width: 100%; min-width: 0; height: 36px; padding: 0 12px; border: 0; border-radius: ${zt}; background: ${Re}; color: ${le}; font-size: 0.875rem; line-height: 1.45; appearance: none; box-shadow: inset 0 0 0 1px ${O(B,60)}; transition: background 150ms ease-out, box-shadow 150ms ease-out; }
|
|
72
72
|
textarea.oc-sdk-input { height: auto; padding: 8px 12px; resize: vertical; }
|
|
73
73
|
.oc-sdk-input::placeholder { color: ${v}; }
|
|
74
74
|
.oc-sdk-input:hover:not(:focus) { background-image: linear-gradient(${X}, ${X}); }
|
|
75
|
-
.oc-sdk-input:focus, .oc-sdk-input:focus-visible { box-shadow: inset 0 0 0 2px ${
|
|
75
|
+
.oc-sdk-input:focus, .oc-sdk-input:focus-visible { box-shadow: inset 0 0 0 2px ${Vt}; }
|
|
76
76
|
.oc-sdk-field[data-invalid="true"] .oc-sdk-input { box-shadow: inset 0 0 0 1px ${y("status-error","red")}; }
|
|
77
77
|
.oc-sdk-field[data-invalid="true"] .oc-sdk-input:focus { box-shadow: inset 0 0 0 2px ${y("status-error","red")}; }
|
|
78
|
-
.oc-sdk-input[data-mono="true"] { font-family: ${
|
|
78
|
+
.oc-sdk-input[data-mono="true"] { font-family: ${ft}; }
|
|
79
79
|
|
|
80
80
|
.oc-sdk-search { position: relative; min-width: 0; }
|
|
81
81
|
.oc-sdk-search .oc-sdk-input { padding-left: 34px; padding-right: 34px; }
|
|
@@ -83,22 +83,22 @@ textarea.oc-sdk-input { height: auto; padding: 8px 12px; resize: vertical; }
|
|
|
83
83
|
.oc-sdk-search[data-active="true"] .oc-sdk-search-icon { color: ${z}; }
|
|
84
84
|
.oc-sdk-search-clear { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); display: none; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 6px; color: ${v}; }
|
|
85
85
|
.oc-sdk-search[data-active="true"] .oc-sdk-search-clear { display: inline-flex; }
|
|
86
|
-
.oc-sdk-search-clear:hover { background: ${X}; color: ${
|
|
86
|
+
.oc-sdk-search-clear:hover { background: ${X}; color: ${pe}; }
|
|
87
87
|
|
|
88
88
|
.oc-sdk-select { position: relative; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
89
|
-
.oc-sdk-trigger { display: inline-flex; align-items: center; gap: 6px; width: 100%; min-width: 0; height: 32px; padding: 0 8px 0 10px; border: 1px solid ${B}; border-radius: 6px; background: ${
|
|
89
|
+
.oc-sdk-trigger { display: inline-flex; align-items: center; gap: 6px; width: 100%; min-width: 0; height: 32px; padding: 0 8px 0 10px; border: 1px solid ${B}; border-radius: 6px; background: ${Re}; color: ${le}; font-size: 0.8125rem; text-align: left; transition: background 150ms ease-out; }
|
|
90
90
|
.oc-sdk-trigger:hover { background-image: linear-gradient(${X}, ${X}); }
|
|
91
|
-
.oc-sdk-trigger[aria-expanded="true"] { background-image: linear-gradient(${
|
|
91
|
+
.oc-sdk-trigger[aria-expanded="true"] { background-image: linear-gradient(${te}, ${te}); }
|
|
92
92
|
.oc-sdk-trigger-value { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
93
93
|
.oc-sdk-trigger-value[data-empty="true"] { color: ${v}; }
|
|
94
94
|
.oc-sdk-trigger-chevron { flex: 0 0 auto; color: ${v}; }
|
|
95
|
-
.oc-sdk-popup { --surface-foreground: ${
|
|
95
|
+
.oc-sdk-popup { --surface-foreground: ${le}; position: fixed; z-index: 50; display: flex; flex-direction: column; gap: 2px; min-width: 160px; max-width: calc(100vw - 16px); max-height: min(320px, calc(100vh - 16px)); overflow: auto; padding: 4px; border: 1px solid ${O(B,60)}; border-radius: 12px; background: ${Re}; color: ${le}; box-shadow: 0 8px 24px ${O(pe,12)}; }
|
|
96
96
|
.oc-sdk-popup-search { flex: 0 0 auto; padding: 2px 2px 4px; }
|
|
97
97
|
.oc-sdk-popup-search .oc-sdk-input { height: 32px; font-size: 0.8125rem; }
|
|
98
98
|
.oc-sdk-option { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border-radius: 8px; font-size: 0.8125rem; text-align: left; }
|
|
99
99
|
.oc-sdk-option[data-active="true"] { background: ${X}; }
|
|
100
|
-
.oc-sdk-option[aria-selected="true"] { background: ${
|
|
101
|
-
.oc-sdk-option[data-destructive="true"] { color: ${
|
|
100
|
+
.oc-sdk-option[aria-selected="true"] { background: ${dt}; color: ${lt}; }
|
|
101
|
+
.oc-sdk-option[data-destructive="true"] { color: ${ut}; }
|
|
102
102
|
.oc-sdk-option[data-destructive="true"][data-active="true"] { background: ${O(y("status-error","red"),10)}; }
|
|
103
103
|
.oc-sdk-option-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
104
104
|
.oc-sdk-option-hint { flex: 0 0 auto; font-size: 0.75rem; color: ${v}; }
|
|
@@ -111,20 +111,20 @@ textarea.oc-sdk-input { height: auto; padding: 8px 12px; resize: vertical; }
|
|
|
111
111
|
.oc-sdk-check-box > svg { display: none; }
|
|
112
112
|
.oc-sdk-check[aria-checked="true"] .oc-sdk-check-box > svg { display: block; }
|
|
113
113
|
.oc-sdk-check-thumb { flex: 0 0 auto; position: relative; width: 36px; height: 20px; border-radius: 9999px; background: ${B}; transition: background 150ms ease-out; }
|
|
114
|
-
.oc-sdk-check-thumb::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 9999px; background: ${
|
|
114
|
+
.oc-sdk-check-thumb::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 9999px; background: ${ee}; transition: transform 150ms ease-out; }
|
|
115
115
|
.oc-sdk-check[aria-checked="true"] .oc-sdk-check-thumb { background: ${z}; }
|
|
116
116
|
.oc-sdk-check[aria-checked="true"] .oc-sdk-check-thumb::after { transform: translateX(16px); }
|
|
117
117
|
.oc-sdk-check:focus-visible { box-shadow: none; }
|
|
118
|
-
.oc-sdk-check:focus-visible .oc-sdk-check-box, .oc-sdk-check:focus-visible .oc-sdk-check-thumb { ${
|
|
118
|
+
.oc-sdk-check:focus-visible .oc-sdk-check-box, .oc-sdk-check:focus-visible .oc-sdk-check-thumb { ${Kt} }
|
|
119
119
|
.oc-sdk-check-text { display: flex; flex-direction: column; min-width: 0; }
|
|
120
120
|
.oc-sdk-check-label { font-size: 0.875rem; }
|
|
121
121
|
.oc-sdk-check-desc { font-size: 0.75rem; color: ${v}; }
|
|
122
122
|
|
|
123
123
|
.oc-sdk-tabs { display: inline-flex; gap: 2px; padding: 2px; border-radius: 10px; max-width: 100%; overflow: auto; }
|
|
124
|
-
.oc-sdk-tabs[data-track="true"] { background: ${O(
|
|
124
|
+
.oc-sdk-tabs[data-track="true"] { background: ${O(pe,4)}; }
|
|
125
125
|
.oc-sdk-tab { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; border: 1px solid transparent; border-radius: 8px; font-size: 0.8125rem; font-weight: 500; color: ${v}; white-space: nowrap; transition: color 150ms ease-out, background 150ms ease-out; }
|
|
126
|
-
.oc-sdk-tab:hover { color: ${
|
|
127
|
-
.oc-sdk-tab[aria-selected="true"] { color: ${
|
|
126
|
+
.oc-sdk-tab:hover { color: ${pe}; }
|
|
127
|
+
.oc-sdk-tab[aria-selected="true"] { color: ${lt}; background: ${dt}; border-color: ${B}; }
|
|
128
128
|
.oc-sdk-tab-count { font-size: 0.75rem; font-variant-numeric: tabular-nums; color: ${v}; }
|
|
129
129
|
|
|
130
130
|
.oc-sdk-badge { display: inline-flex; align-items: center; padding: 1px 6px; border-radius: 9999px; font-size: 11px; font-weight: 500; line-height: 16px; white-space: nowrap; background: ${X}; color: ${v}; }
|
|
@@ -133,8 +133,8 @@ textarea.oc-sdk-input { height: auto; padding: 8px 12px; resize: vertical; }
|
|
|
133
133
|
.oc-sdk-list { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
|
|
134
134
|
.oc-sdk-row { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border-radius: 6px; text-align: left; transition: background 120ms ease-out; }
|
|
135
135
|
.oc-sdk-row:hover, .oc-sdk-row[data-active="true"] { background: ${X}; }
|
|
136
|
-
.oc-sdk-row[aria-selected="true"] { background: ${
|
|
137
|
-
.oc-sdk-row-lead { flex: 0 0 auto; width: 64px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ${
|
|
136
|
+
.oc-sdk-row[aria-selected="true"] { background: ${dt}; color: ${lt}; }
|
|
137
|
+
.oc-sdk-row-lead { flex: 0 0 auto; width: 64px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ${ft}; font-size: 0.75rem; color: ${v}; }
|
|
138
138
|
.oc-sdk-row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
|
|
139
139
|
.oc-sdk-row-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
140
140
|
.oc-sdk-row-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.75rem; color: ${v}; }
|
|
@@ -171,6 +171,6 @@ textarea.oc-sdk-input { height: auto; padding: 8px 12px; resize: vertical; }
|
|
|
171
171
|
.oc-sdk-menu { position: relative; display: inline-flex; }
|
|
172
172
|
|
|
173
173
|
.oc-sdk-text { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
174
|
-
.oc-sdk-text a { color: ${
|
|
174
|
+
.oc-sdk-text a { color: ${pt}; text-decoration: underline; text-underline-offset: 2px; }
|
|
175
175
|
.oc-sdk-text img { display: block; max-width: 100%; margin: 8px 0; border-radius: 8px; border: 1px solid ${O(B,60)}; }
|
|
176
|
-
`;var xo=e=>24+e*308;function jt(e){let t=new Map,r=new Map;for(let i of e.nodes)r.set(i.column,[...r.get(i.column)??[],i]);let a=[...r.keys()].sort((i,g)=>i-g),p=new Map(a.map(i=>[i,xo(i)])),l=new Map,s=0;for(let i of a)(r.get(i)??[]).slice().sort((f,x)=>(l.get(f.parentId??"")??-1)-(l.get(x.parentId??"")??-1)||(f.createdAt??0)-(x.createdAt??0)||f.id.localeCompare(x.id)).forEach((f,x)=>{let I=58+x*94;t.set(f.id,{x:p.get(i)??0,y:I}),l.set(f.id,l.size),s=Math.max(s,I+80+24)});let c=t.get(e.rootId),h=e.nodes.filter(i=>i.parentId===e.rootId).map(i=>t.get(i.id)).filter(i=>i!==void 0);if(c&&h.length>0&&(r.get(0)?.length??0)===1){let i=Math.min(...h.map(f=>f.y)),g=Math.max(...h.map(f=>f.y));c.y=Math.round((i+g)/2)}let A=24+(Math.max(0,...a)+1)*308-76+24;return{boxes:t,edges:bo(e.edges,t),columnX:p,width:A,height:Math.max(s,162)}}function Vt(e,t){if(t<=1)return 80/2;let r=Math.min(12,80*.44/(t-1));return 80/2+(e-(t-1)/2)*r}function bo(e,t){let r=e.filter(s=>t.has(s.from)&&t.has(s.to)),a=s=>(t.get(s)?.y??0)+80/2,p=new Map,l=new Map;for(let s of r)p.set(s.from,[...p.get(s.from)??[],s]),l.set(s.to,[...l.get(s.to)??[],s]);for(let s of p.values())s.sort((c,h)=>a(c.to)-a(h.to));for(let s of l.values())s.sort((c,h)=>a(c.from)-a(h.from));return r.map(s=>{let c=t.get(s.from),h=t.get(s.to),A=p.get(s.from)??[],i=l.get(s.to)??[],g=c.x+232,f=c.y+Vt(A.indexOf(s),A.length),x=h.x,I=h.y+Vt(i.indexOf(s),i.length),W=x>g?Math.max(32,(x-g)*.5):90,E=`M ${g} ${f} C ${g+W} ${f}, ${x-W} ${I}, ${x} ${I}`;return{...s,d:E,sx:g,sy:f,tx:x,ty:I}})}function ft(e,t,r){let a=new Set,p=[t];for(;p.length>0;){let l=p.pop();for(let s of e){let c=r==="down"?s.from===l?s.to:void 0:s.to===l?s.from:void 0;c!==void 0&&!a.has(c)&&c!==t&&(a.add(c),p.push(c))}}return a}function qt(e){let t=Math.max(0,Math.round(e/1e3));if(t<60)return`${t}s`;let r=Math.floor(t/60);return r<60?`${r}m ${String(t%60).padStart(2,"0")}s`:`${Math.floor(r/60)}h ${String(r%60).padStart(2,"0")}m`}function mt(e){if(e)return e.includes("/")?e.slice(e.indexOf("/")+1):e}function Yt(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(e>=1e4?0:1)}k`:String(e)}var So={running:"running",queued:"queued",completed:"completed",failed:"failed",cancelled:"cancelled",idle:"idle"};function fe(e,t){if(e.kind==="root")return e.rawStatus;let r=e.rawStatus===e.state?So[e.state]:e.rawStatus,a=e.startedAt;return a===void 0?r:e.state==="running"?`${r} \xB7 ${qt(t-a)}`:e.endedAt!==void 0?`${r} \xB7 ${qt(e.endedAt-a)}`:r}var To=new Set(["running","queued","completed","failed","cancelled","idle"]),he=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),J=e=>typeof e=="number"&&Number.isFinite(e),me=e=>e===void 0||typeof e=="string",ce=e=>e===void 0||J(e),_o=e=>typeof e=="string"&&To.has(e);function yo(e){return he(e)?typeof e.id=="string"&&(e.kind==="root"||e.kind==="task")&&typeof e.sessionId=="string"&&typeof e.title=="string"&&_o(e.state)&&typeof e.rawStatus=="string"&&me(e.agent)&&me(e.model)&&ce(e.createdAt)&&ce(e.startedAt)&&ce(e.endedAt)&&me(e.summary)&&me(e.error)&&ce(e.tokens)&&ce(e.costUsd)&&ce(e.toolCalls)&&me(e.parentId)&&J(e.column)&&J(e.wave):!1}function Ao(e){return he(e)&&typeof e.id=="string"&&typeof e.from=="string"&&typeof e.to=="string"&&(e.kind==="spawn"||e.kind==="uses")}function wo(e){return he(e)&&J(e.column)&&J(e.total)&&J(e.done)&&J(e.running)}function vo(e){return he(e)&&J(e.omittedNodes)&&Number.isInteger(e.omittedNodes)&&e.omittedNodes>=0}function Mo(e){return he(e)&&(e.status==="ok"||e.status==="unknown-session")&&typeof e.rootId=="string"&&typeof e.focusId=="string"&&typeof e.title=="string"&&Array.isArray(e.nodes)&&e.nodes.every(yo)&&Array.isArray(e.edges)&&e.edges.every(Ao)&&Array.isArray(e.columns)&&e.columns.every(wo)&&J(e.models)&&(e.truncated===void 0||vo(e.truncated))}function Oo(e){let t=JSON.parse(e);if(!Mo(t))throw new Error("service answered with an unexpected graph shape");return t}var Ue=class{constructor(t){this.deps=t}deps;session=void 0;generation=0;inFlight=!1;pending=!1;signature="";setSession(t){t!==this.session&&(this.session=t,this.invalidate())}invalidate(){this.generation++,this.signature="",this.refresh()}async refresh(){let t=this.session;if(t===void 0)return;if(t===null){this.signature="",this.deps.show({kind:"no-session"});return}if(this.deps.isHidden()){this.deps.schedule(5e3,()=>{this.refresh()});return}if(this.inFlight){this.pending=!0;return}this.inFlight=!0;let r=this.generation;try{let a=await this.deps.fetch(t);if(r!==this.generation)return;if(a.status!==200)throw new Error(`service answered HTTP ${a.status}: ${a.body.slice(0,200)}`);let p=Oo(a.body);p.status!=="ok"||p.nodes.length<=1?(this.signature="",this.deps.show({kind:"empty"})):a.body!==this.signature&&(this.signature=a.body,this.deps.show({kind:"graph",graph:p}));let l=p.nodes.some(s=>s.state==="running"||s.state==="queued");this.deps.schedule(l?1500:5e3,()=>{this.refresh()})}catch(a){if(r!==this.generation)return;this.signature="",this.deps.show({kind:"error",error:a}),this.deps.schedule(1e4,()=>{this.refresh()})}finally{this.inFlight=!1,(this.pending||r!==this.generation)&&(this.pending=!1,this.refresh())}}};var Jt="http://www.w3.org/2000/svg";document.documentElement.style.setProperty("--node-card-width",`${232}px`);document.documentElement.style.setProperty("--node-card-height",`${80}px`);var j=st(),C=e=>document.querySelector(e),d={app:C("#app"),title:C("#title"),titleDot:C("#title-dot"),meta:C("#meta"),viewport:C("#viewport"),world:C("#world"),edges:C("#edges"),bands:C("#bands"),nodes:C("#nodes"),empty:C("#empty"),inspector:C("#inspector"),settings:C("#settings")},Ee=null,to=!1,M,R,Wt="",V,ne={},ht,_={x:0,y:0,k:1},de=new Map;function L(e,t="",r=""){let a=document.createElement(e);return t&&(a.className=t),r&&(a.textContent=r),a}function gt(e,t=""){d.empty.replaceChildren(L("strong","",e),...t?[L("p","",t)]:[]),d.empty.hidden=!1,d.world.hidden=!0,d.inspector.hidden=!0}function oo(e){return e instanceof u?e.code==="NO_SERVICE"?["Local service not allowed yet","Approve OmO Graph in Settings \u2192 Extensions so it can read the OmO task store."]:e.code==="SERVICE_FAILED"?["The local service did not start","Check Settings \u2192 Extensions; the next refresh retries it."]:e.code==="DISABLED"?["OmO Graph is paused","Resume it in Settings \u2192 Extensions."]:[`Request failed (${e.code})`,e.message]:["Request failed",e instanceof Error?e.message:String(e)]}function le(){d.world.style.transform=`translate(${_.x}px, ${_.y}px) scale(${_.k})`,d.app.style.setProperty("--zoom",String(_.k))}function De(){if(!R)return;let e=d.viewport.clientWidth,t=d.viewport.clientHeight,r=Math.min(1,Math.max(.62,Math.min((e-16)/R.width,(t-16)/R.height)));_.k=r,_.x=Math.round(R.width*r<=e?(e-R.width*r)/2:8),_.y=Math.round(Math.max(8,(t-R.height*r)/2)),le()}function no(e){let t=e?R?.boxes.get(e):void 0;if(!t)return De();_.k=Math.max(_.k,.8),_.x=Math.round(d.viewport.clientWidth/2-(t.x+232/2)*_.k),_.y=Math.round(d.viewport.clientHeight/2-(t.y+80/2)*_.k),le()}function ke(e,t=d.viewport.clientWidth/2,r=d.viewport.clientHeight/2){let a=Math.min(2,Math.max(.2,_.k*e));_.x=t-(t-_.x)*a/_.k,_.y=r-(r-_.y)*a/_.k,_.k=a,le()}var K,Et=!1;d.viewport.addEventListener("pointerdown",e=>{e.button===0&&(K={x:e.clientX,y:e.clientY,vx:_.x,vy:_.y,moved:!1})});window.addEventListener("pointermove",e=>{if(!K)return;let t=e.clientX-K.x,r=e.clientY-K.y;!K.moved&&Math.hypot(t,r)<4||(K.moved||(K.moved=!0,d.viewport.setPointerCapture?.(e.pointerId),d.app.classList.add("is-panning")),_.x=K.vx+t,_.y=K.vy+r,le())});window.addEventListener("pointerup",()=>{K?.moved&&(Et=!0,setTimeout(()=>{Et=!1},0)),K=void 0,d.app.classList.remove("is-panning")});d.viewport.addEventListener("wheel",e=>{e.preventDefault();let t=d.viewport.getBoundingClientRect();e.ctrlKey||e.metaKey||Math.abs(e.deltaY)>Math.abs(e.deltaX)*2&&!e.shiftKey?ke(Math.exp(-e.deltaY*(e.ctrlKey?.01:.0015)),e.clientX-t.left,e.clientY-t.top):(_.x-=e.deltaX||(e.shiftKey?e.deltaY:0),_.y-=e.shiftKey?0:e.deltaY,le())},{passive:!1});C("#btn-fit").addEventListener("click",De);C("#btn-locate").addEventListener("click",()=>{let e=M?.nodes.find(t=>t.kind==="task"&&t.state==="running");no(M?.focusId!==M?.rootId?M?.focusId:e?.id??M?.rootId)});C("#btn-in").addEventListener("click",()=>ke(1.2));C("#btn-out").addEventListener("click",()=>ke(1/1.2));window.addEventListener("keydown",e=>{e.target instanceof HTMLInputElement||(e.key==="0"?De():e.key==="+"||e.key==="="?ke(1.2):e.key==="-"&&ke(1/1.2))});function ge(e){if(V=e,!M)return;let t=M.edges,r=new Set(e?t.filter(s=>s.to===e).map(s=>s.from):[]),a=new Set(e?t.filter(s=>s.from===e).map(s=>s.to):[]),p=e?ft(t,e,"up"):new Set,l=e?ft(t,e,"down"):new Set;d.app.classList.toggle("has-hover",e!==void 0);for(let[s,c]of de)c.classList.toggle("is-hover",s===e),c.classList.toggle("is-input",r.has(s)),c.classList.toggle("is-output",a.has(s)),c.classList.toggle("is-reach",p.has(s)||l.has(s));for(let s of d.edges.querySelectorAll("path[data-edge]")){let c=s.dataset.from??"",h=s.dataset.to??"";s.classList.toggle("is-in",e!==void 0&&h===e),s.classList.toggle("is-out",e!==void 0&&c===e),s.classList.toggle("is-reach",e!==void 0&&(p.has(c)&&(p.has(h)||h===e)||l.has(h)&&(l.has(c)||c===e)))}Lo()}function Lo(){let e=M?.nodes.find(A=>A.id===(V??(M?.focusId!==M?.rootId?M?.focusId:void 0)));if(!e||!M){d.inspector.hidden=!0;return}let t=M.edges.filter(A=>A.to===e.id).length,r=M.edges.filter(A=>A.from===e.id).length,a=[fe(e,Date.now())];e.agent&&a.push(e.agent);let p=mt(e.model);p&&a.push(p),e.toolCalls!==void 0&&a.push(`${e.toolCalls} tools`),e.tokens!==void 0&&a.push(`${Yt(e.tokens)} tok`),e.costUsd!==void 0&&a.push(`$${e.costUsd.toFixed(2)}`);let l=L("div","i-head");l.append(L("span",`dot s-${e.state}`),L("strong","",e.title));let s=L("div","i-flow");s.append(L("span","i-in",`\u2190 ${t} input${t===1?"":"s"}`),L("span","i-out",`${r} output${r===1?"":"s"} \u2192`));let c=[l,L("div","i-facts",a.join(" \xB7 ")),s],h=e.error??e.summary;h&&c.push(L("p",e.error?"i-text is-error":"i-text",h)),c.push(L("div","i-hint",e.kind==="root"?"Click to open the orchestrating session":"Click to open this sub-agent's session")),d.inspector.replaceChildren(...c),d.inspector.hidden=!1}function Co(e){let t=de.get(e.id);return t||(t=L("button","node"),t.type="button",t.dataset.id=e.id,t.append(L("div","n-head"),L("div","n-status"),L("div","n-meta"),L("div","n-sub")),t.querySelector(".n-head")?.append(L("span","dot"),L("span","n-title")),t.addEventListener("pointerenter",()=>ge(e.id)),t.addEventListener("pointerleave",()=>{V===e.id&&ge(void 0)}),t.addEventListener("focus",()=>ge(e.id)),t.addEventListener("blur",()=>{V===e.id&&ge(void 0)}),t.addEventListener("click",()=>{Et||$o(e.id)}),de.set(e.id,t),d.nodes.append(t)),t}function Ro(e,t,r){e.className=`node k-${t.kind} s-${t.state}${M?.focusId===t.id&&t.kind==="task"?" is-current":""}`,V===t.id&&e.classList.add("is-hover"),e.setAttribute("aria-label",`${t.title}, ${fe(t,r)}. Open session.`);let a=(p,l)=>{let s=e.querySelector(p);s&&s.textContent!==l&&(s.textContent=l)};a(".n-title",t.title),a(".n-status",fe(t,r)),a(".n-meta",t.kind==="root"?`${M?.nodes.length?M.nodes.length-1:0} sub-agents`:[t.agent,mt(t.model)].filter(Boolean).join(" \xB7 ")),a(".n-sub",t.error??t.summary??""),e.querySelector(".dot")?.setAttribute("class",`dot s-${t.state}`)}function Go(e){M=e,R=jt(e),d.world.hidden=!1,d.empty.hidden=!0,d.world.style.width=`${R.width}px`,d.world.style.height=`${R.height}px`,d.edges.setAttribute("width",String(R.width)),d.edges.setAttribute("height",String(R.height)),d.edges.setAttribute("viewBox",`0 0 ${R.width} ${R.height}`);let t=new Map(e.nodes.map(i=>[i.id,i.state])),r=document.createDocumentFragment();for(let i of R.edges){let g=document.createElementNS(Jt,"path");g.setAttribute("d",i.d),g.dataset.edge=i.id,g.dataset.from=i.from,g.dataset.to=i.to;let f=t.get(i.to)==="running"||t.get(i.to)==="queued";if(g.setAttribute("class",`edge k-${i.kind}${f?" is-live":""}`),g.setAttribute("marker-end","url(#arrow)"),r.append(g),f){let x=document.createElementNS(Jt,"path");x.setAttribute("d",i.d),x.setAttribute("class","flow"),x.setAttribute("pathLength","1"),r.append(x)}}d.edges.querySelectorAll("path.edge, path.flow").forEach(i=>i.remove()),d.edges.append(r),d.bands.replaceChildren(...[...R.columnX].map(([i,g])=>{let f=L("div","band");f.style.left=`${g}px`,f.style.width=`${232}px`;let x=e.columns.find(I=>I.column===i);return i===0?f.textContent="Orchestrator":x&&(f.textContent=`Wave ${i} \xB7 ${x.done}/${x.total} done${x.running?` \xB7 ${x.running} running`:""}`),x?.running&&f.classList.add("is-live"),f}));let a=Date.now(),p=new Set;for(let i of e.nodes){let g=Co(i),f=R.boxes.get(i.id);f&&(g.style.transform=`translate(${f.x}px, ${f.y}px)`),Ro(g,i,a),p.add(i.id)}for(let[i,g]of de)p.has(i)||(g.remove(),de.delete(i));let l=e.nodes.filter(i=>i.kind==="task"),s=l.filter(i=>i.state==="completed"||i.state==="failed"||i.state==="cancelled").length,c=l.filter(i=>i.state==="running").length,h=e.columns.length,A=e.columns.find(i=>i.running>0||i.done<i.total)?.column;d.title.textContent=e.title,d.titleDot.className=`dot s-${c>0?"running":l.some(i=>i.state==="failed")?"failed":s===l.length?"completed":"idle"}`,d.meta.textContent=[A!==void 0?`wave ${A}/${h}`:`${h} wave${h===1?"":"s"}`,`${s}/${l.length} done`,...c?[`${c} running`]:[],...e.models?[`${e.models} model${e.models===1?"":"s"}`]:[],...e.truncated?[`${e.truncated.omittedNodes} omitted`]:[]].join(" \xB7 "),j.setBadge(c>0?c:null).catch(()=>{}),Wt!==e.rootId&&(Wt=e.rootId,requestAnimationFrame(()=>e.focusId!==e.rootId?no(e.focusId):De())),V&&!e.nodes.some(i=>i.id===V)&&(V=void 0),ge(V)}function Io(){if(!M)return;let e=Date.now();for(let t of M.nodes){if(t.state!=="running")continue;let a=de.get(t.id)?.querySelector(".n-status");a&&(a.textContent=fe(t,e))}}setInterval(Io,1e3);async function $o(e){let t=M?.nodes.find(r=>r.id===e);if(t)try{await j.openSession(t.sessionId)}catch(r){let[a,p]=oo(r);await j.toast({kind:"error",message:`${a}: ${p}`}).catch(()=>{})}}var xe=new Ue({fetch:e=>{let t={session:e};return ne.agentDir&&(t.agentDir=ne.agentDir),ne.stateDir&&(t.stateDir=ne.stateDir),j.serviceRequest({method:"GET",path:"/graph",query:t})},show:e=>{if(e.kind==="graph"){Go(e.graph);return}M=void 0,j.setBadge(null).catch(()=>{}),e.kind==="no-session"?gt("No session open","Open a chat to see the OmO agents working behind it."):e.kind==="empty"?gt("No sub-agents yet","When this session launches OmO tasks or workflows, they appear here as a live graph."):gt(...oo(e.error))},schedule:(e,t)=>{ht&&clearTimeout(ht),ht=setTimeout(t,e)},isHidden:()=>document.hidden}),Qt=C("#btn-settings");Qt.addEventListener("click",()=>{d.settings.hidden=!d.settings.hidden,Qt.setAttribute("aria-expanded",String(!d.settings.hidden)),d.settings.hidden||d.settings.querySelector("input")?.focus()});C("#btn-save").addEventListener("click",()=>{let e=new FormData(d.settings),t={},r=String(e.get("agentDir")??"").trim(),a=String(e.get("stateDir")??"").trim();r&&(t.agentDir=r),a&&(t.stateDir=a),ne=t,d.settings.hidden=!0,j.storage.set("paths",t).catch(()=>{}),xe.invalidate()});function No(){for(let e of["agentDir","stateDir"]){let t=d.settings.querySelector(`input[name="${e}"]`);t&&(t.value=ne[e]??"")}}var Zt=!1;j.onReady(e=>{it(e,document.documentElement),d.app.dataset.surface=e.surface,!Zt&&(Zt=!0,Ee=e.session?.id??Ee,j.storage.get("paths").then(t=>{if(t&&typeof t=="object"&&!Array.isArray(t)){let r=t;ne={...typeof r.agentDir=="string"?{agentDir:r.agentDir}:{},...typeof r.stateDir=="string"?{stateDir:r.stateDir}:{}}}No()}).catch(()=>{}).finally(()=>{to=!0,xe.setSession(Ee)}))});j.onSession(e=>{let t=e?.id??null;t!==Ee&&(V=void 0),Ee=t,to&&xe.setSession(t)});j.onSessionLifecycle(()=>{xe.refresh()});document.addEventListener("visibilitychange",()=>{document.hidden||xe.refresh()});window.addEventListener("resize",()=>{R&&_.k<=1&&le()});})();
|
|
176
|
+
`;var So=e=>24+e*308;function qt(e){let t=new Map,r=new Map;for(let i of e.nodes)r.set(i.column,[...r.get(i.column)??[],i]);let a=[...r.keys()].sort((i,g)=>i-g),l=new Map(a.map(i=>[i,So(i)])),p=new Map,s=0;for(let i of a)(r.get(i)??[]).slice().sort((f,k)=>(p.get(f.parentId??"")??-1)-(p.get(k.parentId??"")??-1)||(f.createdAt??0)-(k.createdAt??0)||f.id.localeCompare(k.id)).forEach((f,k)=>{let I=58+k*94;t.set(f.id,{x:l.get(i)??0,y:I}),p.set(f.id,p.size),s=Math.max(s,I+80+24)});let c=t.get(e.rootId),h=e.nodes.filter(i=>i.parentId===e.rootId).map(i=>t.get(i.id)).filter(i=>i!==void 0);if(c&&h.length>0&&(r.get(0)?.length??0)===1){let i=Math.min(...h.map(f=>f.y)),g=Math.max(...h.map(f=>f.y));c.y=Math.round((i+g)/2)}let A=24+(Math.max(0,...a)+1)*308-76+24;return{boxes:t,edges:To(e.edges,t),columnX:l,width:A,height:Math.max(s,162)}}function Yt(e,t){if(t<=1)return 80/2;let r=Math.min(12,80*.44/(t-1));return 80/2+(e-(t-1)/2)*r}function To(e,t){let r=e.filter(s=>t.has(s.from)&&t.has(s.to)),a=s=>(t.get(s)?.y??0)+80/2,l=new Map,p=new Map;for(let s of r)l.set(s.from,[...l.get(s.from)??[],s]),p.set(s.to,[...p.get(s.to)??[],s]);for(let s of l.values())s.sort((c,h)=>a(c.to)-a(h.to));for(let s of p.values())s.sort((c,h)=>a(c.from)-a(h.from));return r.map(s=>{let c=t.get(s.from),h=t.get(s.to),A=l.get(s.from)??[],i=p.get(s.to)??[],g=c.x+232,f=c.y+Yt(A.indexOf(s),A.length),k=h.x,I=h.y+Yt(i.indexOf(s),i.length),J=k>g?Math.max(32,(k-g)*.5):90,E=`M ${g} ${f} C ${g+J} ${f}, ${k-J} ${I}, ${k} ${I}`;return{...s,d:E,sx:g,sy:f,tx:k,ty:I}})}function ht(e,t,r){let a=new Set,l=[t];for(;l.length>0;){let p=l.pop();for(let s of e){let c=r==="down"?s.from===p?s.to:void 0:s.to===p?s.from:void 0;c!==void 0&&!a.has(c)&&c!==t&&(a.add(c),l.push(c))}}return a}function Jt(e){let t=Math.max(0,Math.round(e/1e3));if(t<60)return`${t}s`;let r=Math.floor(t/60);return r<60?`${r}m ${String(t%60).padStart(2,"0")}s`:`${Math.floor(r/60)}h ${String(r%60).padStart(2,"0")}m`}function gt(e){if(e)return e.includes("/")?e.slice(e.indexOf("/")+1):e}function Wt(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(e>=1e4?0:1)}k`:String(e)}var _o={running:"running",queued:"queued",completed:"completed",failed:"failed",cancelled:"cancelled",idle:"idle"};function ue(e,t){if(e.kind==="root")return e.rawStatus;let r=e.rawStatus===e.state?_o[e.state]:e.rawStatus,a=e.startedAt;return a===void 0?r:e.state==="running"?`${r} \xB7 ${Jt(t-a)}`:e.endedAt!==void 0?`${r} \xB7 ${Jt(e.endedAt-a)}`:r}var De=class{drag;get active(){return this.drag!==void 0}down(t,r){t.button===0&&(this.drag={id:t.pointerId,x:t.clientX,y:t.clientY,vx:r.x,vy:r.y,moved:!1})}move(t){let r=this.drag;if(!r||t.pointerId!==r.id)return;if((t.buttons&1)===0)return"end";let a=t.clientX-r.x,l=t.clientY-r.y;if(!r.moved&&Math.hypot(a,l)<4)return;let p=!r.moved;return r.moved=!0,{x:r.vx+a,y:r.vy+l,started:p}}end(t){let r=this.drag;return!r||t!==void 0&&t!==r.id?!1:(this.drag=void 0,r.moved)}};var yo=new Set(["running","queued","completed","failed","cancelled","idle"]),me=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),q=e=>typeof e=="number"&&Number.isFinite(e),fe=e=>e===void 0||typeof e=="string",ae=e=>e===void 0||q(e),Ao=e=>typeof e=="string"&&yo.has(e);function wo(e){return me(e)?typeof e.id=="string"&&(e.kind==="root"||e.kind==="task")&&typeof e.sessionId=="string"&&typeof e.title=="string"&&Ao(e.state)&&typeof e.rawStatus=="string"&&fe(e.agent)&&fe(e.model)&&ae(e.createdAt)&&ae(e.startedAt)&&ae(e.endedAt)&&fe(e.summary)&&fe(e.error)&&ae(e.tokens)&&ae(e.costUsd)&&ae(e.toolCalls)&&fe(e.parentId)&&q(e.column)&&q(e.wave):!1}function vo(e){return me(e)&&typeof e.id=="string"&&typeof e.from=="string"&&typeof e.to=="string"&&(e.kind==="spawn"||e.kind==="uses")}function Mo(e){return me(e)&&q(e.column)&&q(e.total)&&q(e.done)&&q(e.running)}function Oo(e){return me(e)&&q(e.omittedNodes)&&Number.isInteger(e.omittedNodes)&&e.omittedNodes>=0}function Lo(e){return me(e)&&(e.status==="ok"||e.status==="unknown-session")&&typeof e.rootId=="string"&&typeof e.focusId=="string"&&typeof e.title=="string"&&Array.isArray(e.nodes)&&e.nodes.every(wo)&&Array.isArray(e.edges)&&e.edges.every(vo)&&Array.isArray(e.columns)&&e.columns.every(Mo)&&q(e.models)&&(e.truncated===void 0||Oo(e.truncated))}function Co(e){let t=JSON.parse(e);if(!Lo(t))throw new Error("service answered with an unexpected graph shape");return t}var He=class{constructor(t){this.deps=t}deps;session=void 0;generation=0;inFlight=!1;pending=!1;signature="";setSession(t){t!==this.session&&(this.session=t,this.invalidate())}invalidate(){this.generation++,this.signature="",this.refresh()}async refresh(){let t=this.session;if(t===void 0)return;if(t===null){this.signature="",this.deps.show({kind:"no-session"});return}if(this.deps.isHidden()){this.deps.schedule(5e3,()=>{this.refresh()});return}if(this.inFlight){this.pending=!0;return}this.inFlight=!0;let r=this.generation;try{let a=await this.deps.fetch(t);if(r!==this.generation)return;if(a.status!==200)throw new Error(`service answered HTTP ${a.status}: ${a.body.slice(0,200)}`);let l=Co(a.body);l.status!=="ok"||l.nodes.length<=1?(this.signature="",this.deps.show({kind:"empty"})):a.body!==this.signature&&(this.signature=a.body,this.deps.show({kind:"graph",graph:l}));let p=l.nodes.some(s=>s.state==="running"||s.state==="queued");this.deps.schedule(p?1500:5e3,()=>{this.refresh()})}catch(a){if(r!==this.generation)return;this.signature="",this.deps.show({kind:"error",error:a}),this.deps.schedule(1e4,()=>{this.refresh()})}finally{this.inFlight=!1,(this.pending||r!==this.generation)&&(this.pending=!1,this.refresh())}}};var Qt="http://www.w3.org/2000/svg";document.documentElement.style.setProperty("--node-card-width",`${232}px`);document.documentElement.style.setProperty("--node-card-height",`${80}px`);var V=at(),C=e=>document.querySelector(e),d={app:C("#app"),title:C("#title"),titleDot:C("#title-dot"),meta:C("#meta"),viewport:C("#viewport"),world:C("#world"),edges:C("#edges"),bands:C("#bands"),nodes:C("#nodes"),empty:C("#empty"),inspector:C("#inspector"),settings:C("#settings")},ge=null,no=!1,M,R,Zt="",K,oe={},Et,_={x:0,y:0,k:1},ce=new Map;function L(e,t="",r=""){let a=document.createElement(e);return t&&(a.className=t),r&&(a.textContent=r),a}function xt(e,t=""){d.empty.replaceChildren(L("strong","",e),...t?[L("p","",t)]:[]),d.empty.hidden=!1,d.world.hidden=!0,d.inspector.hidden=!0}function ro(e){return e instanceof u?e.code==="NO_SERVICE"?["Local service not allowed yet","Approve OmO Graph in Settings \u2192 Extensions so it can read the OmO task store."]:e.code==="SERVICE_FAILED"?["The local service did not start","Check Settings \u2192 Extensions; the next refresh retries it."]:e.code==="DISABLED"?["OmO Graph is paused","Resume it in Settings \u2192 Extensions."]:[`Request failed (${e.code})`,e.message]:["Request failed",e instanceof Error?e.message:String(e)]}function de(){d.world.style.transform=`translate(${_.x}px, ${_.y}px) scale(${_.k})`,d.app.style.setProperty("--zoom",String(_.k))}function Pe(){if(!R)return;let e=d.viewport.clientWidth,t=d.viewport.clientHeight,r=Math.min(1,Math.max(.62,Math.min((e-16)/R.width,(t-16)/R.height)));_.k=r,_.x=Math.round(R.width*r<=e?(e-R.width*r)/2:8),_.y=Math.round(Math.max(8,(t-R.height*r)/2)),de()}function so(e){let t=e?R?.boxes.get(e):void 0;if(!t)return Pe();_.k=Math.max(_.k,.8),_.x=Math.round(d.viewport.clientWidth/2-(t.x+232/2)*_.k),_.y=Math.round(d.viewport.clientHeight/2-(t.y+80/2)*_.k),de()}function xe(e,t=d.viewport.clientWidth/2,r=d.viewport.clientHeight/2){let a=Math.min(2,Math.max(.2,_.k*e));_.x=t-(t-_.x)*a/_.k,_.y=r-(r-_.y)*a/_.k,_.k=a,de()}var Ee=new De,kt=!1;function ke(e,t=!1){if(!Ee.active)return;let r=Ee.end(e);Ee.active||(r&&t&&(kt=!0,setTimeout(()=>{kt=!1},0)),d.app.classList.remove("is-panning"))}d.viewport.addEventListener("pointerdown",e=>Ee.down(e,_));window.addEventListener("pointermove",e=>{let t=Ee.move(e);if(t==="end")return ke(e.pointerId);t&&(t.started&&(d.viewport.setPointerCapture?.(e.pointerId),d.app.classList.add("is-panning")),_.x=t.x,_.y=t.y,de())});window.addEventListener("pointerup",e=>ke(e.pointerId,!0));window.addEventListener("pointercancel",e=>ke(e.pointerId));d.viewport.addEventListener("lostpointercapture",e=>ke(e.pointerId));window.addEventListener("blur",()=>ke());d.viewport.addEventListener("wheel",e=>{e.preventDefault();let t=d.viewport.getBoundingClientRect();e.ctrlKey||e.metaKey||Math.abs(e.deltaY)>Math.abs(e.deltaX)*2&&!e.shiftKey?xe(Math.exp(-e.deltaY*(e.ctrlKey?.01:.0015)),e.clientX-t.left,e.clientY-t.top):(_.x-=e.deltaX||(e.shiftKey?e.deltaY:0),_.y-=e.shiftKey?0:e.deltaY,de())},{passive:!1});C("#btn-fit").addEventListener("click",Pe);C("#btn-locate").addEventListener("click",()=>{let e=M?.nodes.find(t=>t.kind==="task"&&t.state==="running");so(M?.focusId!==M?.rootId?M?.focusId:e?.id??M?.rootId)});C("#btn-in").addEventListener("click",()=>xe(1.2));C("#btn-out").addEventListener("click",()=>xe(1/1.2));window.addEventListener("keydown",e=>{e.target instanceof HTMLInputElement||(e.key==="0"?Pe():e.key==="+"||e.key==="="?xe(1.2):e.key==="-"&&xe(1/1.2))});function he(e){if(K=e,!M)return;let t=M.edges,r=new Set(e?t.filter(s=>s.to===e).map(s=>s.from):[]),a=new Set(e?t.filter(s=>s.from===e).map(s=>s.to):[]),l=e?ht(t,e,"up"):new Set,p=e?ht(t,e,"down"):new Set;d.app.classList.toggle("has-hover",e!==void 0);for(let[s,c]of ce)c.classList.toggle("is-hover",s===e),c.classList.toggle("is-input",r.has(s)),c.classList.toggle("is-output",a.has(s)),c.classList.toggle("is-reach",l.has(s)||p.has(s));for(let s of d.edges.querySelectorAll("path[data-edge]")){let c=s.dataset.from??"",h=s.dataset.to??"";s.classList.toggle("is-in",e!==void 0&&h===e),s.classList.toggle("is-out",e!==void 0&&c===e),s.classList.toggle("is-reach",e!==void 0&&(l.has(c)&&(l.has(h)||h===e)||p.has(h)&&(p.has(c)||c===e)))}Ro()}function Ro(){let e=M?.nodes.find(A=>A.id===(K??(M?.focusId!==M?.rootId?M?.focusId:void 0)));if(!e||!M){d.inspector.hidden=!0;return}let t=M.edges.filter(A=>A.to===e.id).length,r=M.edges.filter(A=>A.from===e.id).length,a=[ue(e,Date.now())];e.agent&&a.push(e.agent);let l=gt(e.model);l&&a.push(l),e.toolCalls!==void 0&&a.push(`${e.toolCalls} tools`),e.tokens!==void 0&&a.push(`${Wt(e.tokens)} tok`),e.costUsd!==void 0&&a.push(`$${e.costUsd.toFixed(2)}`);let p=L("div","i-head");p.append(L("span",`dot s-${e.state}`),L("strong","",e.title));let s=L("div","i-flow");s.append(L("span","i-in",`\u2190 ${t} input${t===1?"":"s"}`),L("span","i-out",`${r} output${r===1?"":"s"} \u2192`));let c=[p,L("div","i-facts",a.join(" \xB7 ")),s],h=e.error??e.summary;h&&c.push(L("p",e.error?"i-text is-error":"i-text",h)),c.push(L("div","i-hint",e.kind==="root"?"Click to open the orchestrating session":"Click to open this sub-agent's session")),d.inspector.replaceChildren(...c),d.inspector.hidden=!1}function Go(e){let t=ce.get(e.id);return t||(t=L("button","node"),t.type="button",t.dataset.id=e.id,t.append(L("div","n-head"),L("div","n-status"),L("div","n-meta"),L("div","n-sub")),t.querySelector(".n-head")?.append(L("span","dot"),L("span","n-title")),t.addEventListener("pointerenter",()=>he(e.id)),t.addEventListener("pointerleave",()=>{K===e.id&&he(void 0)}),t.addEventListener("focus",()=>he(e.id)),t.addEventListener("blur",()=>{K===e.id&&he(void 0)}),t.addEventListener("click",()=>{kt||Uo(e.id)}),ce.set(e.id,t),d.nodes.append(t)),t}function Io(e,t,r){e.className=`node k-${t.kind} s-${t.state}${M?.focusId===t.id&&t.kind==="task"?" is-current":""}`,K===t.id&&e.classList.add("is-hover"),e.setAttribute("aria-label",`${t.title}, ${ue(t,r)}. Open session.`);let a=(l,p)=>{let s=e.querySelector(l);s&&s.textContent!==p&&(s.textContent=p)};a(".n-title",t.title),a(".n-status",ue(t,r)),a(".n-meta",t.kind==="root"?`${M?.nodes.length?M.nodes.length-1:0} sub-agents`:[t.agent,gt(t.model)].filter(Boolean).join(" \xB7 ")),a(".n-sub",t.error??t.summary??""),e.querySelector(".dot")?.setAttribute("class",`dot s-${t.state}`)}function $o(e){M=e,R=qt(e),d.world.hidden=!1,d.empty.hidden=!0,d.world.style.width=`${R.width}px`,d.world.style.height=`${R.height}px`,d.edges.setAttribute("width",String(R.width)),d.edges.setAttribute("height",String(R.height)),d.edges.setAttribute("viewBox",`0 0 ${R.width} ${R.height}`);let t=new Map(e.nodes.map(i=>[i.id,i.state])),r=document.createDocumentFragment();for(let i of R.edges){let g=document.createElementNS(Qt,"path");g.setAttribute("d",i.d),g.dataset.edge=i.id,g.dataset.from=i.from,g.dataset.to=i.to;let f=t.get(i.to)==="running"||t.get(i.to)==="queued";if(g.setAttribute("class",`edge k-${i.kind}${f?" is-live":""}`),g.setAttribute("marker-end","url(#arrow)"),r.append(g),f){let k=document.createElementNS(Qt,"path");k.setAttribute("d",i.d),k.setAttribute("class","flow"),k.setAttribute("pathLength","1"),r.append(k)}}d.edges.querySelectorAll("path.edge, path.flow").forEach(i=>i.remove()),d.edges.append(r),d.bands.replaceChildren(...[...R.columnX].map(([i,g])=>{let f=L("div","band");f.style.left=`${g}px`,f.style.width=`${232}px`;let k=e.columns.find(I=>I.column===i);return i===0?f.textContent="Orchestrator":k&&(f.textContent=`Wave ${i} \xB7 ${k.done}/${k.total} done${k.running?` \xB7 ${k.running} running`:""}`),k?.running&&f.classList.add("is-live"),f}));let a=Date.now(),l=new Set;for(let i of e.nodes){let g=Go(i),f=R.boxes.get(i.id);f&&(g.style.transform=`translate(${f.x}px, ${f.y}px)`),Io(g,i,a),l.add(i.id)}for(let[i,g]of ce)l.has(i)||(g.remove(),ce.delete(i));let p=e.nodes.filter(i=>i.kind==="task"),s=p.filter(i=>i.state==="completed"||i.state==="failed"||i.state==="cancelled").length,c=p.filter(i=>i.state==="running").length,h=e.columns.length,A=e.columns.find(i=>i.running>0||i.done<i.total)?.column;d.title.textContent=e.title,d.titleDot.className=`dot s-${c>0?"running":p.some(i=>i.state==="failed")?"failed":s===p.length?"completed":"idle"}`,d.meta.textContent=[A!==void 0?`wave ${A}/${h}`:`${h} wave${h===1?"":"s"}`,`${s}/${p.length} done`,...c?[`${c} running`]:[],...e.models?[`${e.models} model${e.models===1?"":"s"}`]:[],...e.truncated?[`${e.truncated.omittedNodes} omitted`]:[]].join(" \xB7 "),V.setBadge(c>0?c:null).catch(()=>{}),Zt!==e.rootId&&(Zt=e.rootId,requestAnimationFrame(()=>e.focusId!==e.rootId?so(e.focusId):Pe())),K&&!e.nodes.some(i=>i.id===K)&&(K=void 0),he(K)}function No(){if(!M)return;let e=Date.now();for(let t of M.nodes){if(t.state!=="running")continue;let a=ce.get(t.id)?.querySelector(".n-status");a&&(a.textContent=ue(t,e))}}setInterval(No,1e3);async function Uo(e){let t=M?.nodes.find(r=>r.id===e);if(t)try{await V.openSession(t.sessionId)}catch(r){let[a,l]=ro(r);await V.toast({kind:"error",message:`${a}: ${l}`}).catch(()=>{})}}var be=new He({fetch:e=>{let t={session:e};return oe.agentDir&&(t.agentDir=oe.agentDir),oe.stateDir&&(t.stateDir=oe.stateDir),V.serviceRequest({method:"GET",path:"/graph",query:t})},show:e=>{if(e.kind==="graph"){$o(e.graph);return}M=void 0,V.setBadge(null).catch(()=>{}),e.kind==="no-session"?xt("No session open","Open a chat to see the OmO agents working behind it."):e.kind==="empty"?xt("No sub-agents yet","When this session launches OmO tasks or workflows, they appear here as a live graph."):xt(...ro(e.error))},schedule:(e,t)=>{Et&&clearTimeout(Et),Et=setTimeout(t,e)},isHidden:()=>document.hidden}),eo=C("#btn-settings");eo.addEventListener("click",()=>{d.settings.hidden=!d.settings.hidden,eo.setAttribute("aria-expanded",String(!d.settings.hidden)),d.settings.hidden||d.settings.querySelector("input")?.focus()});C("#btn-save").addEventListener("click",()=>{let e=new FormData(d.settings),t={},r=String(e.get("agentDir")??"").trim(),a=String(e.get("stateDir")??"").trim();r&&(t.agentDir=r),a&&(t.stateDir=a),oe=t,d.settings.hidden=!0,V.storage.set("paths",t).catch(()=>{}),be.invalidate()});function Do(){for(let e of["agentDir","stateDir"]){let t=d.settings.querySelector(`input[name="${e}"]`);t&&(t.value=oe[e]??"")}}var to=!1;V.onReady(e=>{ct(e,document.documentElement),d.app.dataset.surface=e.surface,!to&&(to=!0,ge=e.session?.id??ge,V.storage.get("paths").then(t=>{if(t&&typeof t=="object"&&!Array.isArray(t)){let r=t;oe={...typeof r.agentDir=="string"?{agentDir:r.agentDir}:{},...typeof r.stateDir=="string"?{stateDir:r.stateDir}:{}}}Do()}).catch(()=>{}).finally(()=>{no=!0,be.setSession(ge)}))});V.onSession(e=>{let t=e?.id??null;t!==ge&&(K=void 0),ge=t,no&&be.setSession(t)});V.onSessionLifecycle(()=>{be.refresh()});document.addEventListener("visibilitychange",()=>{document.hidden||be.refresh()});window.addEventListener("resize",()=>{R&&_.k<=1&&de()});})();
|