agent-dealer 1.2.5 → 1.2.7
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/bundle/server/dist/adapters/agent-deck-bind.js +33 -3
- package/bundle/server/dist/adapters/agent-deck-bind.test.js +60 -2
- package/bundle/server/dist/adapters/agent-health.js +3 -4
- package/bundle/server/dist/adapters/agent-health.test.js +11 -9
- package/bundle/server/dist/adapters/git-worktree.js +187 -18
- package/bundle/server/dist/adapters/git-worktree.test.js +293 -0
- package/bundle/server/dist/adapters/muse-capability.js +51 -25
- package/bundle/server/dist/adapters/muse-capability.test.js +138 -52
- package/bundle/server/dist/capacity/claude-local-cache.js +59 -27
- package/bundle/server/dist/capacity/claude-local-cache.test.js +163 -9
- package/bundle/server/dist/capacity/muse-host.js +34 -32
- package/bundle/server/dist/capacity/muse-host.test.js +5 -3
- package/bundle/server/dist/coordinator/args.js +4 -3
- package/bundle/server/dist/coordinator/checkpoint.js +5 -3
- package/bundle/server/dist/coordinator/checkpoint.test.js +23 -0
- package/bundle/server/dist/coordinator/commands.js +66 -5
- package/bundle/server/dist/coordinator/developer-effect.js +88 -10
- package/bundle/server/dist/coordinator/developer-effect.test.js +170 -0
- package/bundle/server/dist/coordinator/human-resolution.js +2 -0
- package/bundle/server/dist/coordinator/muse-developer.integration.test.js +291 -55
- package/bundle/server/dist/coordinator/muse-spawn.js +72 -174
- package/bundle/server/dist/coordinator/projection.js +1 -0
- package/bundle/server/dist/coordinator/prompts.js +10 -6
- package/bundle/server/dist/coordinator/prompts.test.js +16 -8
- package/bundle/server/dist/coordinator/routing.js +1 -0
- package/bundle/server/dist/coordinator/routing.test.js +20 -0
- package/bundle/server/dist/coordinator/spawn.js +2 -1
- package/bundle/server/dist/coordinator/worktree-owner-liveness.js +6 -1
- package/bundle/server/dist/coordinator/worktree-owner-liveness.test.js +2 -1
- package/bundle/server/dist/repository/human-actions.js +13 -0
- package/bundle/server/dist/routes/index.js +11 -8
- package/bundle/server/dist/routes/version.js +30 -0
- package/bundle/server/dist/routes/version.test.js +21 -0
- package/bundle/server/dist/runners/muse-config-core.js +63 -16
- package/bundle/server/dist/runners/muse-config.js +1 -1
- package/bundle/server/dist/runners/muse-config.test.js +135 -29
- package/bundle/server/dist/runners/spawn-cli.js +17 -3
- package/bundle/server/dist/runners/spawn-cli.test.js +59 -0
- package/bundle/server/package.json +2 -2
- package/bundle/server/static-ui/assets/index-DyAJNyfV.css +1 -0
- package/bundle/server/static-ui/assets/index-yLyxRd-7.js +64 -0
- package/bundle/server/static-ui/index.html +2 -2
- package/bundle/shared/dist/action-presentation.d.ts +51 -0
- package/bundle/shared/dist/action-presentation.js +280 -0
- package/bundle/shared/dist/action-presentation.test.d.ts +1 -0
- package/bundle/shared/dist/action-presentation.test.js +180 -0
- package/bundle/shared/dist/attempt-waste.d.ts +2 -2
- package/bundle/shared/dist/execution-report.d.ts +2 -2
- package/bundle/shared/dist/human-actions.d.ts +12 -12
- package/bundle/shared/dist/index.d.ts +41 -40
- package/bundle/shared/dist/index.js +1 -0
- package/bundle/shared/dist/issues.d.ts +12 -12
- package/bundle/shared/dist/outbound-draft.d.ts +6 -6
- package/bundle/shared/dist/profile-snapshot.d.ts +2 -2
- package/bundle/shared/dist/usage-events.d.ts +2 -2
- package/bundle/shared/dist/worker-sessions.d.ts +6 -6
- package/bundle/shared/dist/workflow.d.ts +2 -2
- package/bundle/shared/package.json +1 -1
- package/dist/doctor.js +4 -1
- package/dist/managed/index.d.ts +1 -1
- package/dist/managed/index.js +1 -1
- package/dist/managed/updater.d.ts +16 -6
- package/dist/managed/updater.js +28 -11
- package/dist/managed-update-restart.test.d.ts +1 -0
- package/dist/managed-update-restart.test.js +227 -0
- package/dist/ports.d.ts +6 -0
- package/dist/ports.js +10 -0
- package/dist/runtime-state.d.ts +10 -0
- package/dist/runtime-state.js +26 -0
- package/dist/start.js +67 -2
- package/dist/status.js +30 -2
- package/dist/update-check.js +3 -6
- package/package.json +1 -1
- package/bundle/server/static-ui/assets/index-BII-LgB8.css +0 -1
- package/bundle/server/static-ui/assets/index-Bq8wWpZm.js +0 -60
|
@@ -13,7 +13,11 @@ export const NOT_177_EVIDENCE = Object.freeze({
|
|
|
13
13
|
});
|
|
14
14
|
export const MUSE_MODEL = "muse-spark-1.3-contributor";
|
|
15
15
|
export const AGENT_DECK_SERVER = "agent-deck";
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* NOT-278: the read-only allowlist is a reviewer requirement, not a developer requirement.
|
|
18
|
+
* Developers receive the selected deck's full surface, including `call_service_tool`. These
|
|
19
|
+
* lists only ever materialize for a reviewer build that proves allowlist enforcement.
|
|
20
|
+
*/
|
|
17
21
|
export const AGENT_DECK_READ_TOOLS = [
|
|
18
22
|
"get_bound_deck",
|
|
19
23
|
"get_playbook",
|
|
@@ -46,6 +50,10 @@ const FORBIDDEN_FLAGS = [
|
|
|
46
50
|
];
|
|
47
51
|
/** Which ambient env vars reach Muse. Everything else (META_API_KEY, MUSE_*, CODEX_HOME, ...) is dropped. */
|
|
48
52
|
const ENV_ALLOWLIST = ["PATH", "HOME", "USER", "LOGNAME", "SHELL", "LANG", "TERM", "TMPDIR"];
|
|
53
|
+
/** Test-only harness vars for the fake `muse` fixture (fixtures/fake-muse.mjs), which can only be
|
|
54
|
+
* selected via the operator/test `MUSE_CLI` override — and passed through only when that
|
|
55
|
+
* override is set, so production runs of the default binary never carry them. */
|
|
56
|
+
const FAKE_HARNESS_PREFIX = "FAKE_MUSE_";
|
|
49
57
|
const ATTEMPT_PREFIX = "muse-attempt-";
|
|
50
58
|
const SENTINEL = ".dealer-muse-attempt";
|
|
51
59
|
/** Messages name fields and paths, never values that could be credentials. */
|
|
@@ -59,10 +67,17 @@ export class MuseIsolationError extends Error {
|
|
|
59
67
|
this.name = "MuseIsolationError";
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
70
|
+
/**
|
|
71
|
+
* NOT-278: capabilities a role needs that the evidence does not prove. Developers receive the
|
|
72
|
+
* selected deck's full surface (including `call_service_tool`), so no allowlist enforcement is
|
|
73
|
+
* required; `cron_*` stays prohibited by the developer prompt and is detected post-run
|
|
74
|
+
* (`muse_cron_used`), not mechanically disabled. Reviewers still need both controls proven.
|
|
75
|
+
*/
|
|
76
|
+
export function unenforceableRestrictions(role, evidence = NOT_177_EVIDENCE) {
|
|
77
|
+
if (role === "developer")
|
|
78
|
+
return [];
|
|
64
79
|
const missing = [];
|
|
65
|
-
// Outbound mutation (`call_service_tool`) must stay denied for
|
|
80
|
+
// Outbound mutation (`call_service_tool`) must stay denied for reviewers, and Muse only
|
|
66
81
|
// stores enabled_tools/disabled_tools unless this is proven.
|
|
67
82
|
if (!evidence.mcp_tool_allowlist_enforcement)
|
|
68
83
|
missing.push("mcp_tool_allowlist_enforcement");
|
|
@@ -71,15 +86,37 @@ export function unenforceableRestrictions(_role, evidence = NOT_177_EVIDENCE) {
|
|
|
71
86
|
missing.push("cron_tool_disable");
|
|
72
87
|
return missing;
|
|
73
88
|
}
|
|
74
|
-
|
|
89
|
+
/**
|
|
90
|
+
* NOT-278: the deckless worker posture shared by every Muse settings.json — workflows and
|
|
91
|
+
* subagents off, reminder child runs off, plus `cron_*` only when proven mechanically
|
|
92
|
+
* disableable. The capacity serve host (which is shared across decks and never runs a
|
|
93
|
+
* session) uses this directly; per-attempt sessions add the required `agent-deck` server
|
|
94
|
+
* below. Exported only for that host — it is not a session attempt and `assertMuseSettings`
|
|
95
|
+
* rejects it (exactly one required `agent-deck` server is mandatory for a launch).
|
|
96
|
+
*/
|
|
97
|
+
export function buildMuseBaseSettings(role = "developer", evidence = NOT_177_EVIDENCE) {
|
|
75
98
|
const runtime_capabilities = {};
|
|
76
99
|
for (const name of REMINDERS) {
|
|
77
100
|
runtime_capabilities[`plugin:tbh-reminders:reminder:${name}`] = { enabled: false };
|
|
78
101
|
}
|
|
79
|
-
|
|
102
|
+
// NOT-278: `cron_*` can only be claimed disabled for reviewers on a build that proves it.
|
|
103
|
+
// Developers are prohibited by prompt and detected post-run; their settings never claim it.
|
|
104
|
+
if (role === "reviewer" && evidence.cron_tool_disable) {
|
|
80
105
|
for (const tool of CRON_TOOLS)
|
|
81
106
|
runtime_capabilities[`tool:${tool}`] = { enabled: false };
|
|
82
107
|
}
|
|
108
|
+
return {
|
|
109
|
+
schema_version: 1,
|
|
110
|
+
run: { workflow_trigger_mode: "off", subagent_delegation_mode: "off" },
|
|
111
|
+
runtime_capabilities,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* NOT-278: developers always receive the selected deck's full surface unfiltered — their
|
|
116
|
+
* settings never carry `enabled_tools`/`disabled_tools` claims. The read-only allowlist only
|
|
117
|
+
* materializes for reviewers on a build that proves enforcement.
|
|
118
|
+
*/
|
|
119
|
+
export function buildMuseSettings(agentDeck, role = "developer", evidence = NOT_177_EVIDENCE) {
|
|
83
120
|
const server = {
|
|
84
121
|
type: "streamable-http",
|
|
85
122
|
url: agentDeck.url,
|
|
@@ -90,20 +127,18 @@ export function buildMuseSettings(agentDeck, evidence = NOT_177_EVIDENCE) {
|
|
|
90
127
|
// `mode`, not `required`: writing both drops the whole MCP block. Never "optional".
|
|
91
128
|
mode: "required",
|
|
92
129
|
};
|
|
93
|
-
if (evidence.mcp_tool_allowlist_enforcement) {
|
|
130
|
+
if (role === "reviewer" && evidence.mcp_tool_allowlist_enforcement) {
|
|
94
131
|
server.enabled_tools = [...AGENT_DECK_READ_TOOLS];
|
|
95
132
|
server.disabled_tools = [...AGENT_DECK_DENIED_TOOLS];
|
|
96
133
|
}
|
|
97
134
|
return {
|
|
98
|
-
|
|
99
|
-
run: { workflow_trigger_mode: "off", subagent_delegation_mode: "off" },
|
|
100
|
-
runtime_capabilities,
|
|
135
|
+
...buildMuseBaseSettings(role, evidence),
|
|
101
136
|
mcpServers: { [AGENT_DECK_SERVER]: server },
|
|
102
137
|
};
|
|
103
138
|
}
|
|
104
139
|
/** Whitelist check on a settings object (generated or re-read from disk). */
|
|
105
|
-
export function assertMuseSettings(settings, agentDeck, evidence = NOT_177_EVIDENCE) {
|
|
106
|
-
const expected = buildMuseSettings(agentDeck, evidence);
|
|
140
|
+
export function assertMuseSettings(settings, agentDeck, role = "developer", evidence = NOT_177_EVIDENCE) {
|
|
141
|
+
const expected = buildMuseSettings(agentDeck, role, evidence);
|
|
107
142
|
if (JSON.stringify(sortKeys(settings)) !== JSON.stringify(sortKeys(expected))) {
|
|
108
143
|
throw new MuseIsolationError("invalid_settings", "Muse settings differ from the approved role settings (extra, missing, or altered keys)");
|
|
109
144
|
}
|
|
@@ -194,6 +229,17 @@ export function buildMuseEnv(root, ambient = process.env) {
|
|
|
194
229
|
if (key.startsWith("LC_") && value !== undefined)
|
|
195
230
|
env[key] = value;
|
|
196
231
|
}
|
|
232
|
+
// Test-only seam: the fake fixture is driven by FAKE_MUSE_* (scenario, record path, version
|
|
233
|
+
// pins) and the exec lane is exact, so without this the fixture cannot be driven at all.
|
|
234
|
+
// These vars join attempt.env only when the operator/test MUSE_CLI override selects the
|
|
235
|
+
// binary — i.e. only the fixture can ever receive them, never the default real binary —
|
|
236
|
+
// and the pre-spawn verify() snapshot covers them exactly like every other approved var.
|
|
237
|
+
if (ambient.MUSE_CLI?.trim()) {
|
|
238
|
+
for (const [key, value] of Object.entries(ambient)) {
|
|
239
|
+
if (key.startsWith(FAKE_HARNESS_PREFIX) && value !== undefined)
|
|
240
|
+
env[key] = value;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
197
243
|
env.MUSE_NO_AUTO_UPDATE = "1";
|
|
198
244
|
env.XDG_CONFIG_HOME = path.join(root, "config");
|
|
199
245
|
env.XDG_DATA_HOME = path.join(root, "data");
|
|
@@ -337,7 +383,8 @@ function sameJson(a, b) {
|
|
|
337
383
|
* Validates, refuses unenforceable restrictions, then writes the per-attempt dir (0700) with
|
|
338
384
|
* settings.json (0600) and an auth link. Throws before spawn; leaves nothing behind on failure.
|
|
339
385
|
*
|
|
340
|
-
* Always uses the pinned `NOT_177_EVIDENCE`, under which
|
|
386
|
+
* Always uses the pinned `NOT_177_EVIDENCE`, under which developers are admitted unfiltered
|
|
387
|
+
* (NOT-278: the full deck surface, no allowlist claims) and reviewers are still refused. There is
|
|
341
388
|
* deliberately no runtime seam that accepts other evidence: the enforced-path tests load a
|
|
342
389
|
* source-rewritten copy of this file instead (see muse-config.test.ts).
|
|
343
390
|
*/
|
|
@@ -355,8 +402,8 @@ function prepareWithEvidence(rawInput, evidence) {
|
|
|
355
402
|
const { worktree, baseDir } = validateInput(input);
|
|
356
403
|
const agentDeck = input.agentDeck;
|
|
357
404
|
const credentialKind = input.credential.kind;
|
|
358
|
-
const settings = deepFreeze(buildMuseSettings(agentDeck, evidence));
|
|
359
|
-
assertMuseSettings(settings, agentDeck, evidence);
|
|
405
|
+
const settings = deepFreeze(buildMuseSettings(agentDeck, input.role, evidence));
|
|
406
|
+
assertMuseSettings(settings, agentDeck, input.role, evidence);
|
|
360
407
|
const apiKey = input.credential.kind === "api-key" ? input.credential.apiKey : undefined;
|
|
361
408
|
const argv = buildMuseArgv({
|
|
362
409
|
role: input.role,
|
|
@@ -445,7 +492,7 @@ function prepareWithEvidence(rawInput, evidence) {
|
|
|
445
492
|
throw err;
|
|
446
493
|
throw new MuseIsolationError("invalid_settings", "Muse settings.json is missing or unreadable");
|
|
447
494
|
}
|
|
448
|
-
assertMuseSettings(onDisk, agentDeck, evidence);
|
|
495
|
+
assertMuseSettings(onDisk, agentDeck, input.role, evidence);
|
|
449
496
|
const entries = fs.readdirSync(path.dirname(settingsPath)).sort();
|
|
450
497
|
const allowed = credentialKind === "auth-file" ? ["auth.json", "settings.json"] : ["settings.json"];
|
|
451
498
|
if (entries.join() !== allowed.join()) {
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
//
|
|
3
3
|
// The implementation lives in muse-config-core.ts, which pins `prepareMuseAttempt` to
|
|
4
4
|
// `NOT_177_EVIDENCE`. No module in the build exports a preparer that accepts caller-supplied evidence.
|
|
5
|
-
export { AGENT_DECK_DENIED_TOOLS, AGENT_DECK_READ_TOOLS, AGENT_DECK_SERVER, MUSE_MODEL, MuseIsolationError, NOT_177_EVIDENCE, assertMuseArgv, assertMuseSettings, buildMuseArgv, buildMuseEnv, buildMuseSettings, createRedactor, defaultOperatorAuthPath, prepareMuseAttempt, unenforceableRestrictions, } from "./muse-config-core.js";
|
|
5
|
+
export { AGENT_DECK_DENIED_TOOLS, AGENT_DECK_READ_TOOLS, AGENT_DECK_SERVER, MUSE_MODEL, MuseIsolationError, NOT_177_EVIDENCE, assertMuseArgv, assertMuseSettings, buildMuseArgv, buildMuseBaseSettings, buildMuseEnv, buildMuseSettings, createRedactor, defaultOperatorAuthPath, prepareMuseAttempt, unenforceableRestrictions, } from "./muse-config-core.js";
|
|
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { assertMuseArgv, buildMuseArgv, prepareMuseAttempt as prepareMuseAttemptPinned, unenforceableRestrictions, } from "./muse-config.js";
|
|
6
|
+
import { assertMuseArgv, buildMuseArgv, NOT_177_EVIDENCE, prepareMuseAttempt as prepareMuseAttemptPinned, unenforceableRestrictions, } from "./muse-config.js";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
8
|
// The Muse sandbox leaves temp dirs writable, so the module refuses them: fixtures live under $HOME.
|
|
9
9
|
const SCRATCH = fs.mkdtempSync(path.join(os.homedir(), ".dealer-muse-config-test-"));
|
|
@@ -68,26 +68,54 @@ function walk(dir) {
|
|
|
68
68
|
return e.isDirectory() ? walk(p) : [p];
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
// NOT-278: developers get the selected deck's full surface unfiltered, so the pinned evidence
|
|
72
|
+
// admits them; reviewers still need both controls proven and fail before anything is written.
|
|
73
|
+
test("with NOT-177 evidence the developer is admitted unfiltered and the reviewer still fails closed", () => {
|
|
74
|
+
const fx = fixture();
|
|
75
|
+
const attempt = prepareMuseAttemptPinned(input(fx, "developer"));
|
|
76
|
+
try {
|
|
77
|
+
const onDisk = JSON.parse(fs.readFileSync(attempt.settingsPath, "utf8"));
|
|
78
|
+
assert.deepEqual(Object.keys(onDisk.mcpServers), ["agent-deck"]);
|
|
79
|
+
assert.equal(onDisk.mcpServers["agent-deck"].mode, "required");
|
|
80
|
+
assert.deepEqual(onDisk.mcpServers["agent-deck"].headers, {
|
|
81
|
+
"x-agent-deck-deck-id": "deck-123",
|
|
82
|
+
"x-agent-deck-workspace": fs.realpathSync(fx.worktree),
|
|
83
|
+
});
|
|
84
|
+
assert.equal("enabled_tools" in onDisk.mcpServers["agent-deck"], false);
|
|
85
|
+
assert.equal("disabled_tools" in onDisk.mcpServers["agent-deck"], false);
|
|
86
|
+
for (const tool of ["cron_create", "cron_list", "cron_delete"]) {
|
|
87
|
+
assert.equal(`tool:${tool}` in onDisk.runtime_capabilities, false, tool);
|
|
80
88
|
}
|
|
81
|
-
assert.
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
assert.doesNotThrow(() => attempt.verify());
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
attempt.cleanup();
|
|
93
|
+
}
|
|
94
|
+
const fxReviewer = fixture();
|
|
95
|
+
let thrown;
|
|
96
|
+
try {
|
|
97
|
+
prepareMuseAttemptPinned(input(fxReviewer, "reviewer"));
|
|
84
98
|
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
thrown = err;
|
|
101
|
+
}
|
|
102
|
+
assert.equal(thrown?.code, "unenforceable_restriction");
|
|
103
|
+
assert.deepEqual([...thrown.capabilities], ["mcp_tool_allowlist_enforcement", "cron_tool_disable"]);
|
|
104
|
+
assert.deepEqual(fs.readdirSync(fxReviewer.baseDir), []);
|
|
105
|
+
assert.deepEqual(unenforceableRestrictions("developer", NOT_177_EVIDENCE), []);
|
|
106
|
+
assert.deepEqual(unenforceableRestrictions("reviewer", NOT_177_EVIDENCE), [
|
|
107
|
+
"mcp_tool_allowlist_enforcement",
|
|
108
|
+
"cron_tool_disable",
|
|
109
|
+
]);
|
|
85
110
|
assert.deepEqual(unenforceableRestrictions("developer", ENFORCED), []);
|
|
111
|
+
assert.deepEqual(unenforceableRestrictions("reviewer", ENFORCED), []);
|
|
86
112
|
assert.deepEqual(unenforceableRestrictions("reviewer", { ...ENFORCED, cron_tool_disable: false }), [
|
|
87
113
|
"cron_tool_disable",
|
|
88
114
|
]);
|
|
89
115
|
});
|
|
90
|
-
|
|
116
|
+
// NOT-278: the developer's deck surface is the full one — exactly one required server, no
|
|
117
|
+
// allowlist claims even when enforcement is proven (the enforced-core copy below proves it).
|
|
118
|
+
test("developer: the full assigned Agent Deck surface, sandbox on, no unrestricted flags", () => {
|
|
91
119
|
const fx = fixture();
|
|
92
120
|
const attempt = prepareMuseAttempt(input(fx, "developer"));
|
|
93
121
|
try {
|
|
@@ -102,11 +130,11 @@ test("developer: only the assigned Agent Deck surface, sandbox on, no unrestrict
|
|
|
102
130
|
"x-agent-deck-deck-id": "deck-123",
|
|
103
131
|
"x-agent-deck-workspace": fs.realpathSync(fx.worktree),
|
|
104
132
|
});
|
|
105
|
-
assert.
|
|
106
|
-
assert.
|
|
133
|
+
assert.equal("enabled_tools" in server, false, "developers are unfiltered: no allowlist claim");
|
|
134
|
+
assert.equal("disabled_tools" in server, false, "developers are unfiltered: no denylist claim");
|
|
107
135
|
assert.deepEqual(onDisk.run, { workflow_trigger_mode: "off", subagent_delegation_mode: "off" });
|
|
108
136
|
for (const tool of ["cron_create", "cron_list", "cron_delete"]) {
|
|
109
|
-
assert.
|
|
137
|
+
assert.equal(`tool:${tool}` in onDisk.runtime_capabilities, false, tool);
|
|
110
138
|
}
|
|
111
139
|
const flag = (f) => attempt.argv[attempt.argv.indexOf(f) + 1];
|
|
112
140
|
assert.equal(attempt.argv[0], "exec");
|
|
@@ -130,16 +158,21 @@ test("developer: only the assigned Agent Deck surface, sandbox on, no unrestrict
|
|
|
130
158
|
attempt.cleanup();
|
|
131
159
|
}
|
|
132
160
|
});
|
|
133
|
-
test("reviewer: read-only flags and the
|
|
161
|
+
test("reviewer: read-only flags and the allowlisted deck reads, mutation denied", () => {
|
|
134
162
|
const fx = fixture();
|
|
135
163
|
const attempt = prepareMuseAttempt(input(fx, "reviewer"));
|
|
136
164
|
try {
|
|
137
165
|
assert.ok(attempt.argv.includes("--disable-write"));
|
|
138
166
|
assert.ok(attempt.argv.includes("--disable-shell"));
|
|
139
167
|
const server = attempt.settings.mcpServers["agent-deck"];
|
|
168
|
+
assert.deepEqual(server.enabled_tools, ["get_bound_deck", "get_playbook", "list_service_tools", "bind_workspace"]);
|
|
140
169
|
assert.equal(server.enabled_tools?.includes("call_service_tool"), false);
|
|
141
170
|
assert.deepEqual(server.disabled_tools, ["call_service_tool"]);
|
|
142
171
|
assert.deepEqual(Object.keys(attempt.settings.mcpServers), ["agent-deck"]);
|
|
172
|
+
for (const tool of ["cron_create", "cron_list", "cron_delete"]) {
|
|
173
|
+
assert.deepEqual(attempt.settings.runtime_capabilities[`tool:${tool}`], { enabled: false }, tool);
|
|
174
|
+
}
|
|
175
|
+
assert.doesNotThrow(() => attempt.verify());
|
|
143
176
|
}
|
|
144
177
|
finally {
|
|
145
178
|
attempt.cleanup();
|
|
@@ -219,6 +252,53 @@ test("ambient Muse config, env, and workspace files cannot expand the attempt",
|
|
|
219
252
|
}
|
|
220
253
|
assert.equal(fs.readFileSync(ambientSettings, "utf8"), ambientBody);
|
|
221
254
|
});
|
|
255
|
+
test("test-only FAKE_MUSE_* harness vars reach the child; credentials and unrelated vars do not", () => {
|
|
256
|
+
const fx = fixture();
|
|
257
|
+
const attempt = prepareMuseAttempt(input(fx, "developer", {
|
|
258
|
+
env: {
|
|
259
|
+
PATH: "/usr/bin",
|
|
260
|
+
HOME: os.homedir(),
|
|
261
|
+
MUSE_CLI: "/operator/fake-muse.mjs",
|
|
262
|
+
META_API_KEY: API_KEY,
|
|
263
|
+
MUSE_SENTINEL: "must-not-reach-child",
|
|
264
|
+
CODEX_HOME: "/operator/codex",
|
|
265
|
+
DEALER_MUSE_SENTINEL: "must-not-reach-child",
|
|
266
|
+
FAKE_MUSE_SCENARIO: "success",
|
|
267
|
+
FAKE_MUSE_RECORD: "/tmp/record.json",
|
|
268
|
+
},
|
|
269
|
+
}));
|
|
270
|
+
try {
|
|
271
|
+
// The fake fixture (selected via MUSE_CLI) is driven by FAKE_MUSE_*; they join
|
|
272
|
+
// attempt.env only under that override, so the pre-spawn verify() snapshot covers them.
|
|
273
|
+
assert.equal(attempt.env.FAKE_MUSE_SCENARIO, "success");
|
|
274
|
+
assert.equal(attempt.env.FAKE_MUSE_RECORD, "/tmp/record.json");
|
|
275
|
+
assert.doesNotThrow(() => attempt.verify({ cwd: attempt.cwd, argv: [...attempt.argv], env: { ...attempt.env }, stdin: attempt.stdin }));
|
|
276
|
+
for (const dropped of ["META_API_KEY", "MUSE_SENTINEL", "CODEX_HOME", "DEALER_MUSE_SENTINEL"]) {
|
|
277
|
+
assert.equal(dropped in attempt.env, false, dropped);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
attempt.cleanup();
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
test("FAKE_MUSE_* harness vars never reach the default real binary (no MUSE_CLI override)", () => {
|
|
285
|
+
const fx = fixture();
|
|
286
|
+
const attempt = prepareMuseAttempt(input(fx, "developer", {
|
|
287
|
+
env: {
|
|
288
|
+
PATH: "/usr/bin",
|
|
289
|
+
HOME: os.homedir(),
|
|
290
|
+
FAKE_MUSE_SCENARIO: "success",
|
|
291
|
+
FAKE_MUSE_RECORD: "/tmp/record.json",
|
|
292
|
+
},
|
|
293
|
+
}));
|
|
294
|
+
try {
|
|
295
|
+
assert.equal("FAKE_MUSE_SCENARIO" in attempt.env, false);
|
|
296
|
+
assert.equal("FAKE_MUSE_RECORD" in attempt.env, false);
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
attempt.cleanup();
|
|
300
|
+
}
|
|
301
|
+
});
|
|
222
302
|
test("required Agent Deck server: optional mode, bad url, and missing identity fail before spawn", () => {
|
|
223
303
|
const fx = fixture();
|
|
224
304
|
const attempt = prepareMuseAttempt(input(fx, "developer"));
|
|
@@ -400,11 +480,23 @@ test("api key never reaches argv, env, disk, or error text; redact scrubs it", (
|
|
|
400
480
|
finally {
|
|
401
481
|
attempt.cleanup();
|
|
402
482
|
}
|
|
403
|
-
for (const over of [{ sessionId: "bad" }, { prompt: "-x" }
|
|
483
|
+
for (const over of [{ sessionId: "bad" }, { prompt: "-x" }]) {
|
|
484
|
+
try {
|
|
485
|
+
prepareMuseAttempt(input(fx, "developer", { credential: { kind: "api-key", apiKey: API_KEY }, ...over }));
|
|
486
|
+
assert.fail("expected refusal");
|
|
487
|
+
}
|
|
488
|
+
catch (err) {
|
|
489
|
+
assert.ok(err instanceof Error && err.name === "MuseIsolationError");
|
|
490
|
+
assert.ok(!err.message.includes(API_KEY));
|
|
491
|
+
assert.ok(!String(err.stack).includes(API_KEY));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
// The pinned entry point admits developers too: a valid api-key input succeeds there as well,
|
|
495
|
+
// and its errors never carry the key either.
|
|
496
|
+
{
|
|
497
|
+
let created;
|
|
404
498
|
try {
|
|
405
|
-
|
|
406
|
-
const prepare = Object.keys(over).length === 0 ? prepareMuseAttemptPinned : prepareMuseAttempt;
|
|
407
|
-
prepare(input(fx, "developer", { credential: { kind: "api-key", apiKey: API_KEY }, ...over }));
|
|
499
|
+
created = prepareMuseAttemptPinned(input(fx, "developer", { credential: { kind: "api-key", apiKey: API_KEY }, sessionId: "bad" }));
|
|
408
500
|
assert.fail("expected refusal");
|
|
409
501
|
}
|
|
410
502
|
catch (err) {
|
|
@@ -412,16 +504,30 @@ test("api key never reaches argv, env, disk, or error text; redact scrubs it", (
|
|
|
412
504
|
assert.ok(!err.message.includes(API_KEY));
|
|
413
505
|
assert.ok(!String(err.stack).includes(API_KEY));
|
|
414
506
|
}
|
|
507
|
+
finally {
|
|
508
|
+
created?.cleanup();
|
|
509
|
+
}
|
|
415
510
|
}
|
|
416
511
|
assert.deepEqual(fs.readdirSync(fx.baseDir), []);
|
|
417
512
|
});
|
|
418
513
|
test("callers cannot supply enforcement evidence: an injected evidence field is ignored", () => {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
514
|
+
// The pinned entry point ignores the forged field: the developer is still admitted unfiltered.
|
|
515
|
+
const fx = fixture();
|
|
516
|
+
const forged = { ...input(fx, "developer"), evidence: ENFORCED };
|
|
517
|
+
const attempt = prepareMuseAttemptPinned(forged);
|
|
518
|
+
try {
|
|
519
|
+
assert.equal("enabled_tools" in attempt.settings.mcpServers["agent-deck"], false);
|
|
520
|
+
assert.equal("disabled_tools" in attempt.settings.mcpServers["agent-deck"], false);
|
|
521
|
+
assert.doesNotThrow(() => attempt.verify());
|
|
522
|
+
}
|
|
523
|
+
finally {
|
|
524
|
+
attempt.cleanup();
|
|
424
525
|
}
|
|
526
|
+
// ... and the reviewer is still refused.
|
|
527
|
+
const fxReviewer = fixture();
|
|
528
|
+
const forgedReviewer = { ...input(fxReviewer, "reviewer"), evidence: ENFORCED };
|
|
529
|
+
assert.equal(code(() => prepareMuseAttemptPinned(forgedReviewer)), "unenforceable_restriction");
|
|
530
|
+
assert.deepEqual(fs.readdirSync(fxReviewer.baseDir), []);
|
|
425
531
|
});
|
|
426
532
|
test("launch contract: cwd is the real worktree and argv/env/cwd/stdin must match exactly", () => {
|
|
427
533
|
const fx = fixture();
|
|
@@ -501,10 +607,10 @@ test("no runtime export can produce a launchable attempt from caller-supplied ev
|
|
|
501
607
|
assert.equal(value.length, 1, `${mod}#${name}`);
|
|
502
608
|
}
|
|
503
609
|
}
|
|
504
|
-
// The pinned core cannot be talked into success with a forged evidence argument or field.
|
|
610
|
+
// The pinned core cannot be talked into reviewer success with a forged evidence argument or field.
|
|
505
611
|
const core = (await import("./muse-config-core.js"));
|
|
506
612
|
const fx = fixture();
|
|
507
|
-
const forged = () => core.prepareMuseAttempt(input(fx, "
|
|
613
|
+
const forged = () => core.prepareMuseAttempt(input(fx, "reviewer"), ENFORCED);
|
|
508
614
|
assert.equal(code(forged), "unenforceable_restriction");
|
|
509
615
|
assert.deepEqual(fs.readdirSync(fx.baseDir), []);
|
|
510
616
|
});
|
|
@@ -71,10 +71,24 @@ export async function spawnCli(runId, cmd, args, cwd, opts) {
|
|
|
71
71
|
cwd,
|
|
72
72
|
// Extra vars (e.g. codex's bearer-token env var for its per-attempt CODEX_HOME
|
|
73
73
|
// MCP config, agent-deck-bind.ts) are added on top of, never in place of, the
|
|
74
|
-
// process env the CLI itself needs to run
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
// process env the CLI itself needs to run — unless the caller asked for the exact
|
|
75
|
+
// environment (NOT-278: the Muse exec lane's approved attempt.env, stdin key).
|
|
76
|
+
env: opts.exactEnv ? { ...opts.env } : { ...process.env, ...opts.env },
|
|
77
|
+
stdio: opts.stdin === undefined ? ["ignore", "pipe", "pipe"] : ["pipe", "pipe", "pipe"],
|
|
77
78
|
});
|
|
79
|
+
// NOT-278: the stdin payload (Muse API key) is written once and closed right after
|
|
80
|
+
// spawn. It never reaches the log stream, the transcript, or any error below.
|
|
81
|
+
if (opts.stdin !== undefined) {
|
|
82
|
+
const payload = opts.stdin;
|
|
83
|
+
const stdin = child.stdin;
|
|
84
|
+
if (stdin) {
|
|
85
|
+
stdin.on("error", () => {
|
|
86
|
+
// The child exited before reading (e.g. instant spawn error) — the 'error'/'close'
|
|
87
|
+
// handlers below still settle the promise; a broken pipe must not throw here.
|
|
88
|
+
});
|
|
89
|
+
stdin.end(payload);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
78
92
|
registerChild(runId, child, opts.logPath);
|
|
79
93
|
// Best-effort bookkeeping: a throwing callback must never take down the spawn.
|
|
80
94
|
if (child.pid !== undefined) {
|
|
@@ -157,3 +157,62 @@ test("spawnCli runs normally when an un-aborted signal is supplied", { timeout:
|
|
|
157
157
|
assert.equal(result.timedOut, false);
|
|
158
158
|
assert.match(result.transcript, /untouched/);
|
|
159
159
|
});
|
|
160
|
+
// NOT-278: the Muse exec lane passes the approved attempt.env exactly — ambient
|
|
161
|
+
// META_API_KEY, MUSE_*, CODEX_HOME, and unrelated variables must not reach the child.
|
|
162
|
+
test("spawnCli exactEnv delivers only the supplied environment", async () => {
|
|
163
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "dealer-spawn-cli-"));
|
|
164
|
+
const logPath = path.join(dir, "out.ndjson");
|
|
165
|
+
process.env.DEALER_SPAWN_CLI_AMBIENT_PROBE = "ambient-leaks";
|
|
166
|
+
process.env.MUSE_PROBE_SHOULD_NOT_ARRIVE = "1";
|
|
167
|
+
try {
|
|
168
|
+
const result = await spawnCli("test-run-exact-env", process.execPath, ["-e", "console.log(JSON.stringify(process.env))"], process.cwd(), { logPath, timeoutMs: 5000, env: { EXACT_ONLY: "yes" }, exactEnv: true });
|
|
169
|
+
assert.equal(result.exitCode, 0);
|
|
170
|
+
const childEnv = JSON.parse(result.transcript);
|
|
171
|
+
assert.equal(childEnv.EXACT_ONLY, "yes");
|
|
172
|
+
assert.equal("DEALER_SPAWN_CLI_AMBIENT_PROBE" in childEnv, false);
|
|
173
|
+
assert.equal("MUSE_PROBE_SHOULD_NOT_ARRIVE" in childEnv, false);
|
|
174
|
+
assert.equal("META_API_KEY" in childEnv, false);
|
|
175
|
+
for (const key of Object.keys(childEnv)) {
|
|
176
|
+
assert.equal(key.startsWith("MUSE_"), false, key);
|
|
177
|
+
assert.equal(key.startsWith("CODEX_"), false, key);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
delete process.env.DEALER_SPAWN_CLI_AMBIENT_PROBE;
|
|
182
|
+
delete process.env.MUSE_PROBE_SHOULD_NOT_ARRIVE;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
test("spawnCli merges process.env by default (existing callers unchanged)", async () => {
|
|
186
|
+
const logPath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "dealer-spawn-cli-")), "out.ndjson");
|
|
187
|
+
process.env.DEALER_SPAWN_CLI_AMBIENT_PROBE = "ambient-present";
|
|
188
|
+
try {
|
|
189
|
+
const result = await spawnCli("test-run-merged-env", process.execPath, ["-e", "console.log(`${process.env.DEALER_SPAWN_CLI_AMBIENT_PROBE}|${process.env.MERGED_ONLY}`)"], process.cwd(), { logPath, timeoutMs: 5000, env: { MERGED_ONLY: "yes" } });
|
|
190
|
+
assert.equal(result.exitCode, 0);
|
|
191
|
+
assert.match(result.transcript, /ambient-present\|yes/);
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
delete process.env.DEALER_SPAWN_CLI_AMBIENT_PROBE;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
// NOT-278: the Muse API key travels on stdin only — written once, closed, never logged.
|
|
198
|
+
test("spawnCli stdin payload is delivered on stdin and never logged", async () => {
|
|
199
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "dealer-spawn-cli-"));
|
|
200
|
+
const logPath = path.join(dir, "out.ndjson");
|
|
201
|
+
const secret = `mk-test-secret-${Date.now()}`;
|
|
202
|
+
const result = await spawnCli("test-run-stdin", process.execPath, ["-e", "let b='';process.stdin.on('data',c=>b+=c).on('end',()=>console.log('got:'+b.length))"], process.cwd(), { logPath, timeoutMs: 5000, stdin: `${secret}\n` });
|
|
203
|
+
assert.equal(result.exitCode, 0);
|
|
204
|
+
assert.match(result.transcript, new RegExp(`got:${secret.length + 1}`));
|
|
205
|
+
const logged = fs.readFileSync(logPath, "utf8");
|
|
206
|
+
assert.equal(logged.includes(secret), false);
|
|
207
|
+
assert.equal(result.transcript.includes(secret), false);
|
|
208
|
+
});
|
|
209
|
+
test("spawnCli stdin close on an instantly-exiting child still settles", async () => {
|
|
210
|
+
const logPath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "dealer-spawn-cli-")), "out.ndjson");
|
|
211
|
+
const result = await spawnCli("test-run-stdin-early-exit", process.execPath, ["-e", "process.exit(3)"], process.cwd(), {
|
|
212
|
+
logPath,
|
|
213
|
+
timeoutMs: 5000,
|
|
214
|
+
stdin: "payload-that-nobody-reads\n",
|
|
215
|
+
});
|
|
216
|
+
assert.equal(result.exitCode, 3);
|
|
217
|
+
assert.equal(result.timedOut, false);
|
|
218
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-dealer/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"typecheck": "tsc --noEmit"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-dealer/shared": "1.2.
|
|
25
|
+
"@agent-dealer/shared": "1.2.7",
|
|
26
26
|
"@fastify/cors": "^11.0.1",
|
|
27
27
|
"@fastify/static": "^8.2.0",
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800&display=swap";/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:Monaco, "Nunito Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono:Monaco, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-950:oklch(25.8% .092 26.042);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-emerald-400:oklch(76.5% .177 163.223);--color-teal-200:oklch(91% .096 180.426);--color-cyan-500:oklch(71.5% .143 215.221);--color-blue-500:oklch(62.3% .214 259.815);--color-pink-500:oklch(65.6% .241 354.308);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-3xl:48rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--tracking-wider:.05em;--leading-tight:1.25;--radius-lg:.5rem;--radius-2xl:1rem;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--blur-3xl:64px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-ui-display:"Avenir Next", Avenir, "Helvetica Neue", system-ui, sans-serif;--color-cyber-gold:#c4b643;--color-cyber-teal:#92e4dd;--color-cyber-violet:#a78bfa;--color-cyber-violet-light:#d4bcff;--color-panel-elevated:#161612;--glass-blur:14px;--glass-bg:#ffffff07;--glass-border:#ffffff13;--glass-highlight:#ffffff0a}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{top:0;right:0;bottom:0;left:0}.top-1\/2{top:50%}.top-16{top:calc(var(--spacing) * 16)}.right-12{right:calc(var(--spacing) * 12)}.bottom-20{bottom:calc(var(--spacing) * 20)}.left-1\/2{left:50%}.left-12{left:calc(var(--spacing) * 12)}.z-0{z-index:0}.z-10{z-index:10}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1{margin-left:var(--spacing)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-2{margin-left:calc(var(--spacing) * 2)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-\[22rem\]{height:22rem}.h-\[26rem\]{height:26rem}.h-\[70\%\]{height:70%}.max-h-64{max-height:calc(var(--spacing) * 64)}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-32{width:calc(var(--spacing) * 32)}.w-36{width:calc(var(--spacing) * 36)}.w-40{width:calc(var(--spacing) * 40)}.w-\[22rem\]{width:22rem}.w-\[26rem\]{width:26rem}.w-\[70\%\]{width:70%}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-\[10rem\]{max-width:10rem}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:0}.min-w-\[560px\]{min-width:560px}.min-w-\[1440px\]{min-width:1440px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.animate-pulse{animation:var(--animate-pulse)}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-1\.5{column-gap:calc(var(--spacing) * 1.5)}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-y-0\.5{row-gap:calc(var(--spacing) * .5)}.gap-y-1{row-gap:var(--spacing)}.gap-y-1\.5{row-gap:calc(var(--spacing) * 1.5)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-white\/5>:not(:last-child)){border-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){:where(.divide-white\/5>:not(:last-child)){border-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.self-center{align-self:center}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-\[\#92E4DD\]\/25{border-color:#92e4dd40}.border-\[\#C4B643\]\/40{border-color:#c4b64366}.border-\[\#C4B643\]\/50{border-color:#c4b64380}.border-amber-300\/30{border-color:#ffd2364d}@supports (color:color-mix(in lab,red,red)){.border-amber-300\/30{border-color:color-mix(in oklab,var(--color-amber-300) 30%,transparent)}}.border-amber-400\/30{border-color:#fcbb004d}@supports (color:color-mix(in lab,red,red)){.border-amber-400\/30{border-color:color-mix(in oklab,var(--color-amber-400) 30%,transparent)}}.border-amber-400\/40{border-color:#fcbb0066}@supports (color:color-mix(in lab,red,red)){.border-amber-400\/40{border-color:color-mix(in oklab,var(--color-amber-400) 40%,transparent)}}.border-cyber-gold\/40{border-color:#c4b64366}@supports (color:color-mix(in lab,red,red)){.border-cyber-gold\/40{border-color:color-mix(in oklab,var(--color-cyber-gold) 40%,transparent)}}.border-cyber-teal\/20{border-color:#92e4dd33}@supports (color:color-mix(in lab,red,red)){.border-cyber-teal\/20{border-color:color-mix(in oklab,var(--color-cyber-teal) 20%,transparent)}}.border-cyber-teal\/25{border-color:#92e4dd40}@supports (color:color-mix(in lab,red,red)){.border-cyber-teal\/25{border-color:color-mix(in oklab,var(--color-cyber-teal) 25%,transparent)}}.border-cyber-teal\/30{border-color:#92e4dd4d}@supports (color:color-mix(in lab,red,red)){.border-cyber-teal\/30{border-color:color-mix(in oklab,var(--color-cyber-teal) 30%,transparent)}}.border-cyber-teal\/35{border-color:#92e4dd59}@supports (color:color-mix(in lab,red,red)){.border-cyber-teal\/35{border-color:color-mix(in oklab,var(--color-cyber-teal) 35%,transparent)}}.border-cyber-teal\/40{border-color:#92e4dd66}@supports (color:color-mix(in lab,red,red)){.border-cyber-teal\/40{border-color:color-mix(in oklab,var(--color-cyber-teal) 40%,transparent)}}.border-cyber-violet\/40{border-color:#a78bfa66}@supports (color:color-mix(in lab,red,red)){.border-cyber-violet\/40{border-color:color-mix(in oklab,var(--color-cyber-violet) 40%,transparent)}}.border-cyber-violet\/50{border-color:#a78bfa80}@supports (color:color-mix(in lab,red,red)){.border-cyber-violet\/50{border-color:color-mix(in oklab,var(--color-cyber-violet) 50%,transparent)}}.border-red-300\/40{border-color:#ffa3a366}@supports (color:color-mix(in lab,red,red)){.border-red-300\/40{border-color:color-mix(in oklab,var(--color-red-300) 40%,transparent)}}.border-red-400\/20{border-color:#ff656833}@supports (color:color-mix(in lab,red,red)){.border-red-400\/20{border-color:color-mix(in oklab,var(--color-red-400) 20%,transparent)}}.border-red-400\/30{border-color:#ff65684d}@supports (color:color-mix(in lab,red,red)){.border-red-400\/30{border-color:color-mix(in oklab,var(--color-red-400) 30%,transparent)}}.border-red-400\/40{border-color:#ff656866}@supports (color:color-mix(in lab,red,red)){.border-red-400\/40{border-color:color-mix(in oklab,var(--color-red-400) 40%,transparent)}}.border-red-400\/50{border-color:#ff656880}@supports (color:color-mix(in lab,red,red)){.border-red-400\/50{border-color:color-mix(in oklab,var(--color-red-400) 50%,transparent)}}.border-white\/5{border-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.border-white\/5{border-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.border-white\/15{border-color:#ffffff26}@supports (color:color-mix(in lab,red,red)){.border-white\/15{border-color:color-mix(in oklab,var(--color-white) 15%,transparent)}}.border-white\/20{border-color:#fff3}@supports (color:color-mix(in lab,red,red)){.border-white\/20{border-color:color-mix(in oklab,var(--color-white) 20%,transparent)}}.bg-\[\#92E4DD\]\/10{background-color:#92e4dd1a}.bg-\[\#C4B643\]\/5{background-color:#c4b6430d}.bg-\[\#C4B643\]\/25{background-color:#c4b64340}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-400\/5{background-color:#fcbb000d}@supports (color:color-mix(in lab,red,red)){.bg-amber-400\/5{background-color:color-mix(in oklab,var(--color-amber-400) 5%,transparent)}}.bg-amber-400\/10{background-color:#fcbb001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-400\/10{background-color:color-mix(in oklab,var(--color-amber-400) 10%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500) 10%,transparent)}}.bg-amber-500\/20{background-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/20{background-color:color-mix(in oklab,var(--color-amber-500) 20%,transparent)}}.bg-black\/20{background-color:#0003}@supports (color:color-mix(in lab,red,red)){.bg-black\/20{background-color:color-mix(in oklab,var(--color-black) 20%,transparent)}}.bg-black\/30{background-color:#0000004d}@supports (color:color-mix(in lab,red,red)){.bg-black\/30{background-color:color-mix(in oklab,var(--color-black) 30%,transparent)}}.bg-blue-500\/\[0\.07\]{background-color:#3080ff12}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/\[0\.07\]{background-color:color-mix(in oklab,var(--color-blue-500) 7%,transparent)}}.bg-cyan-500\/10{background-color:#00b7d71a}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/10{background-color:color-mix(in oklab,var(--color-cyan-500) 10%,transparent)}}.bg-cyber-gold\/20{background-color:#c4b64333}@supports (color:color-mix(in lab,red,red)){.bg-cyber-gold\/20{background-color:color-mix(in oklab,var(--color-cyber-gold) 20%,transparent)}}.bg-cyber-teal{background-color:var(--color-cyber-teal)}.bg-cyber-teal\/5{background-color:#92e4dd0d}@supports (color:color-mix(in lab,red,red)){.bg-cyber-teal\/5{background-color:color-mix(in oklab,var(--color-cyber-teal) 5%,transparent)}}.bg-cyber-teal\/10{background-color:#92e4dd1a}@supports (color:color-mix(in lab,red,red)){.bg-cyber-teal\/10{background-color:color-mix(in oklab,var(--color-cyber-teal) 10%,transparent)}}.bg-cyber-teal\/15{background-color:#92e4dd26}@supports (color:color-mix(in lab,red,red)){.bg-cyber-teal\/15{background-color:color-mix(in oklab,var(--color-cyber-teal) 15%,transparent)}}.bg-cyber-teal\/20{background-color:#92e4dd33}@supports (color:color-mix(in lab,red,red)){.bg-cyber-teal\/20{background-color:color-mix(in oklab,var(--color-cyber-teal) 20%,transparent)}}.bg-cyber-violet\/10{background-color:#a78bfa1a}@supports (color:color-mix(in lab,red,red)){.bg-cyber-violet\/10{background-color:color-mix(in oklab,var(--color-cyber-violet) 10%,transparent)}}.bg-cyber-violet\/20{background-color:#a78bfa33}@supports (color:color-mix(in lab,red,red)){.bg-cyber-violet\/20{background-color:color-mix(in oklab,var(--color-cyber-violet) 20%,transparent)}}.bg-cyber-violet\/25{background-color:#a78bfa40}@supports (color:color-mix(in lab,red,red)){.bg-cyber-violet\/25{background-color:color-mix(in oklab,var(--color-cyber-violet) 25%,transparent)}}.bg-emerald-400{background-color:var(--color-emerald-400)}.bg-panel-elevated\/40{background-color:#16161266}@supports (color:color-mix(in lab,red,red)){.bg-panel-elevated\/40{background-color:color-mix(in oklab,var(--color-panel-elevated) 40%,transparent)}}.bg-panel-elevated\/60{background-color:#16161299}@supports (color:color-mix(in lab,red,red)){.bg-panel-elevated\/60{background-color:color-mix(in oklab,var(--color-panel-elevated) 60%,transparent)}}.bg-pink-500\/10{background-color:#f6339a1a}@supports (color:color-mix(in lab,red,red)){.bg-pink-500\/10{background-color:color-mix(in oklab,var(--color-pink-500) 10%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.bg-red-500\/15{background-color:#fb2c3626}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/15{background-color:color-mix(in oklab,var(--color-red-500) 15%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.bg-transparent{background-color:#0000}.bg-white\/5{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.bg-white\/5{background-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.bg-white\/10{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.bg-white\/10{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.bg-white\/\[0\.02\]{background-color:#ffffff05}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.02\]{background-color:color-mix(in oklab,var(--color-white) 2%,transparent)}}.bg-white\/\[0\.03\]{background-color:#ffffff08}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.03\]{background-color:color-mix(in oklab,var(--color-white) 3%,transparent)}}.bg-white\/\[0\.04\]{background-color:#ffffff0a}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.04\]{background-color:color-mix(in oklab,var(--color-white) 4%,transparent)}}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-12{padding-left:calc(var(--spacing) * 12)}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-mono)}.font-ui-display{font-family:var(--font-ui-display)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#92E4DD\]{color:#92e4dd}.text-\[\#92E4DD\]\/80{color:#92e4ddcc}.text-\[\#A8C4C0\]{color:#a8c4c0}.text-\[\#C4B643\]{color:#c4b643}.text-\[\#E8DC7A\]{color:#e8dc7a}.text-\[\#E8F6F4\]{color:#e8f6f4}.text-amber-100{color:var(--color-amber-100)}.text-amber-100\/80{color:#fef3c6cc}@supports (color:color-mix(in lab,red,red)){.text-amber-100\/80{color:color-mix(in oklab,var(--color-amber-100) 80%,transparent)}}.text-amber-200\/80{color:#fee685cc}@supports (color:color-mix(in lab,red,red)){.text-amber-200\/80{color:color-mix(in oklab,var(--color-amber-200) 80%,transparent)}}.text-amber-200\/90{color:#fee685e6}@supports (color:color-mix(in lab,red,red)){.text-amber-200\/90{color:color-mix(in oklab,var(--color-amber-200) 90%,transparent)}}.text-amber-300{color:var(--color-amber-300)}.text-amber-300\/80{color:#ffd236cc}@supports (color:color-mix(in lab,red,red)){.text-amber-300\/80{color:color-mix(in oklab,var(--color-amber-300) 80%,transparent)}}.text-amber-300\/90{color:#ffd236e6}@supports (color:color-mix(in lab,red,red)){.text-amber-300\/90{color:color-mix(in oklab,var(--color-amber-300) 90%,transparent)}}.text-cyber-teal{color:var(--color-cyber-teal)}.text-cyber-violet-light{color:var(--color-cyber-violet-light)}.text-red-100{color:var(--color-red-100)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-red-300\/80{color:#ffa3a3cc}@supports (color:color-mix(in lab,red,red)){.text-red-300\/80{color:color-mix(in oklab,var(--color-red-300) 80%,transparent)}}.text-red-300\/90{color:#ffa3a3e6}@supports (color:color-mix(in lab,red,red)){.text-red-300\/90{color:color-mix(in oklab,var(--color-red-300) 90%,transparent)}}.text-red-400{color:var(--color-red-400)}.text-teal-200\/80{color:#96f7e4cc}@supports (color:color-mix(in lab,red,red)){.text-teal-200\/80{color:color-mix(in oklab,var(--color-teal-200) 80%,transparent)}}.text-white{color:var(--color-white)}.text-white\/25{color:#ffffff40}@supports (color:color-mix(in lab,red,red)){.text-white\/25{color:color-mix(in oklab,var(--color-white) 25%,transparent)}}.text-white\/30{color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.text-white\/30{color:color-mix(in oklab,var(--color-white) 30%,transparent)}}.text-white\/35{color:#ffffff59}@supports (color:color-mix(in lab,red,red)){.text-white\/35{color:color-mix(in oklab,var(--color-white) 35%,transparent)}}.text-white\/40{color:#fff6}@supports (color:color-mix(in lab,red,red)){.text-white\/40{color:color-mix(in oklab,var(--color-white) 40%,transparent)}}.text-white\/45{color:#ffffff73}@supports (color:color-mix(in lab,red,red)){.text-white\/45{color:color-mix(in oklab,var(--color-white) 45%,transparent)}}.text-white\/50{color:#ffffff80}@supports (color:color-mix(in lab,red,red)){.text-white\/50{color:color-mix(in oklab,var(--color-white) 50%,transparent)}}.text-white\/55{color:#ffffff8c}@supports (color:color-mix(in lab,red,red)){.text-white\/55{color:color-mix(in oklab,var(--color-white) 55%,transparent)}}.text-white\/60{color:#fff9}@supports (color:color-mix(in lab,red,red)){.text-white\/60{color:color-mix(in oklab,var(--color-white) 60%,transparent)}}.text-white\/70{color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.text-white\/70{color:color-mix(in oklab,var(--color-white) 70%,transparent)}}.text-white\/75{color:#ffffffbf}@supports (color:color-mix(in lab,red,red)){.text-white\/75{color:color-mix(in oklab,var(--color-white) 75%,transparent)}}.text-white\/80{color:#fffc}@supports (color:color-mix(in lab,red,red)){.text-white\/80{color:color-mix(in oklab,var(--color-white) 80%,transparent)}}.text-white\/85{color:#ffffffd9}@supports (color:color-mix(in lab,red,red)){.text-white\/85{color:color-mix(in oklab,var(--color-white) 85%,transparent)}}.text-white\/90{color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.text-white\/90{color:color-mix(in oklab,var(--color-white) 90%,transparent)}}.text-yellow-300{color:var(--color-yellow-300)}.text-yellow-400{color:var(--color-yellow-400)}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.accent-\[\#C4B643\]{accent-color:#c4b643}.opacity-80{opacity:.8}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-3xl{--tw-blur:blur(var(--blur-3xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.placeholder\:text-white\/30::placeholder{color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-white\/30::placeholder{color:color-mix(in oklab,var(--color-white) 30%,transparent)}}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media(hover:hover){.hover\:border-cyber-teal\/50:hover{border-color:#92e4dd80}@supports (color:color-mix(in lab,red,red)){.hover\:border-cyber-teal\/50:hover{border-color:color-mix(in oklab,var(--color-cyber-teal) 50%,transparent)}}.hover\:bg-cyber-teal\/10:hover{background-color:#92e4dd1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-cyber-teal\/10:hover{background-color:color-mix(in oklab,var(--color-cyber-teal) 10%,transparent)}}.hover\:bg-panel-elevated\/70:hover{background-color:#161612b3}@supports (color:color-mix(in lab,red,red)){.hover\:bg-panel-elevated\/70:hover{background-color:color-mix(in oklab,var(--color-panel-elevated) 70%,transparent)}}.hover\:bg-red-500\/20:hover{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/20:hover{background-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.hover\:bg-white\/5:hover{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/5:hover{background-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.hover\:text-cyber-teal:hover{color:var(--color-cyber-teal)}.hover\:text-red-300:hover{color:var(--color-red-300)}.hover\:text-white:hover{color:var(--color-white)}.hover\:text-white\/70:hover{color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.hover\:text-white\/70:hover{color:color-mix(in oklab,var(--color-white) 70%,transparent)}}.hover\:text-white\/80:hover{color:#fffc}@supports (color:color-mix(in lab,red,red)){.hover\:text-white\/80:hover{color:color-mix(in oklab,var(--color-white) 80%,transparent)}}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-cyber-teal\/45:focus-visible{outline-color:#92e4dd73}@supports (color:color-mix(in lab,red,red)){.focus-visible\:outline-cyber-teal\/45:focus-visible{outline-color:color-mix(in oklab,var(--color-cyber-teal) 45%,transparent)}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media(min-width:40rem){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-start{align-items:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}@media(min-width:64rem){.lg\:col-span-4{grid-column:span 4/span 4}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:80rem){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}}@font-face{font-family:Monaco;font-style:normal;font-weight:400;font-display:block;src:url(/fonts/Monaco.ttf)format("truetype")}html{font-size:17px}html,body{font-family:var(--font-sans)}body{color:#92e4dd;-webkit-font-smoothing:antialiased;background-color:#0a0a07;min-height:100vh;margin:0;font-size:1rem;line-height:1.5}@keyframes ambient-drift-a{0%,to{opacity:1;transform:translate(0)scale(1)}50%{opacity:.72;transform:translate(28px,-22px)scale(1.06)}}@keyframes ambient-drift-b{0%,to{opacity:1;transform:translate(0)scale(1)}50%{opacity:.75;transform:translate(-24px,18px)scale(1.05)}}@keyframes ambient-drift-c{0%,to{opacity:1;transform:translate(0)scale(1)}50%{opacity:.68;transform:translate(16px,20px)scale(.96)}}.ambient-orb{will-change:transform,opacity}.ambient-orb-a{animation:14s ease-in-out infinite ambient-drift-a}.ambient-orb-b{animation:17s ease-in-out -6s infinite ambient-drift-b}.ambient-orb-c{animation:20s ease-in-out -11s infinite ambient-drift-c}.glass-header{-webkit-backdrop-filter:blur(12px);background:#0a0a07b8}.field{border-style:var(--tw-border-style);border-width:1px;border-color:#ffffff1a;border-radius:.25rem;width:100%}@supports (color:color-mix(in lab,red,red)){.field{border-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.field{background-color:#ffffff08}@supports (color:color-mix(in lab,red,red)){.field{background-color:color-mix(in oklab,var(--color-white) 3%,transparent)}}.field{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));color:#e8f6f4;--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);font-family:var(--font-mono)}input,select,textarea{font-family:var(--font-mono)}button{font-family:var(--font-ui-display)}.panel{border-radius:var(--radius-2xl);padding:calc(var(--spacing) * 4);background:var(--glass-bg);-webkit-backdrop-filter:blur(var(--glass-blur));border:1px solid var(--glass-border);box-shadow:inset 0 1px 0 var(--glass-highlight)}.btn-gold{padding-block:calc(var(--spacing) * 2);font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:#0a0a07;font-family:var(--font-ui-display);background:#c4b643;border-radius:.25rem}.btn-gold:hover{background:#d4c760}.btn-ghost{border-style:var(--tw-border-style);border-width:1px;border-color:#fff3;border-radius:.25rem}@supports (color:color-mix(in lab,red,red)){.btn-ghost{border-color:color-mix(in oklab,var(--color-white) 20%,transparent)}}.btn-ghost{padding-block:calc(var(--spacing) * 2);font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));color:#e8f6f4;font-family:var(--font-ui-display)}.btn-ghost-danger{border-style:var(--tw-border-style);border-width:1px;border-color:#fb2c36bf;border-radius:.25rem}@supports (color:color-mix(in lab,red,red)){.btn-ghost-danger{border-color:color-mix(in oklab,var(--color-red-500) 75%,transparent)}}.btn-ghost-danger{background-color:#46080933}@supports (color:color-mix(in lab,red,red)){.btn-ghost-danger{background-color:color-mix(in oklab,var(--color-red-950) 20%,transparent)}}.btn-ghost-danger{padding-block:calc(var(--spacing) * 2);font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-red-400);font-family:var(--font-ui-display)}.btn-ghost-danger:hover:not(:disabled){border-color:var(--color-red-400);background-color:#46080966}@supports (color:color-mix(in lab,red,red)){.btn-ghost-danger:hover:not(:disabled){background-color:color-mix(in oklab,var(--color-red-950) 40%,transparent)}}.btn-ghost-danger:hover:not(:disabled){color:var(--color-red-300)}.heading-page{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:#c4b643;font-family:var(--font-ui-display)}.heading-panel{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:#92e4dd;font-family:var(--font-ui-display)}::-webkit-scrollbar{width:8px}::-webkit-scrollbar-track{background:#ffffff1a;border-radius:10px}::-webkit-scrollbar-thumb{background:#ffffff4d;border-radius:10px}::-webkit-scrollbar-thumb:hover{background:#ffffff80}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}}
|