@jmanuelcorral/openteam 0.11.0 → 0.13.0
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/README.es.md +2 -2
- package/README.md +2 -2
- package/dist/cli/consoleServe.d.ts +1 -1
- package/dist/cli.js +393 -252
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/config/resolve.d.ts +19 -18
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/schema.d.ts +19 -9
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/console/assets.d.ts +2 -2
- package/dist/console/assets.d.ts.map +1 -1
- package/dist/console/configView.d.ts +9 -2
- package/dist/console/configView.d.ts.map +1 -1
- package/dist/console/render.d.ts +20 -29
- package/dist/console/render.d.ts.map +1 -1
- package/dist/index.js +22 -8
- package/dist/orchestrator/coordinator.d.ts +0 -15
- package/dist/orchestrator/coordinator.d.ts.map +1 -1
- package/dist/web/graphRoute.d.ts +31 -14
- package/dist/web/graphRoute.d.ts.map +1 -1
- package/dist/web/server.d.ts +3 -3
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/start.d.ts +3 -4
- package/dist/web/start.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -299,7 +299,7 @@ var GraphEngineSchema = z2.enum(["builtin", "langgraph"]);
|
|
|
299
299
|
var DEFAULT_GRAPH_JOURNAL_ROOT = ".opencode/openteam-local/graph/runs";
|
|
300
300
|
var DEFAULT_GRAPH_WORKTREES_DIR = ".opencode/openteam-local/graph/worktrees";
|
|
301
301
|
var GraphConfigSchema = z2.object({
|
|
302
|
-
mode: GraphModeSchema.default("
|
|
302
|
+
mode: GraphModeSchema.default("shadow"),
|
|
303
303
|
engine: GraphEngineSchema.optional(),
|
|
304
304
|
killSwitch: z2.boolean().default(false),
|
|
305
305
|
journalRoot: z2.string().min(1).default(DEFAULT_GRAPH_JOURNAL_ROOT),
|
|
@@ -322,8 +322,8 @@ var ConsoleConfigSchema = z2.object({
|
|
|
322
322
|
pty: z2.boolean().optional()
|
|
323
323
|
}).strict().default({ enabled: true }),
|
|
324
324
|
graphView: z2.object({
|
|
325
|
-
enabled: z2.boolean().default(
|
|
326
|
-
}).strict().
|
|
325
|
+
enabled: z2.boolean().default(true)
|
|
326
|
+
}).strict().default({ enabled: true })
|
|
327
327
|
}).strict().default({
|
|
328
328
|
host: "127.0.0.1",
|
|
329
329
|
port: 4599,
|
|
@@ -332,7 +332,8 @@ var ConsoleConfigSchema = z2.object({
|
|
|
332
332
|
recentRoutes: 50,
|
|
333
333
|
openBrowser: false,
|
|
334
334
|
remoteStorage: false,
|
|
335
|
-
terminal: { enabled: true }
|
|
335
|
+
terminal: { enabled: true },
|
|
336
|
+
graphView: { enabled: true }
|
|
336
337
|
});
|
|
337
338
|
var DEFAULT_STORAGE_SQLITE_INDEX_PATH = ".opencode/openteam-local/index.sqlite";
|
|
338
339
|
var StorageConfigSchema = z2.object({
|
|
@@ -511,7 +512,13 @@ var OpenTeamConfigObjectSchema = z2.object({
|
|
|
511
512
|
console: ConsoleConfigSchema,
|
|
512
513
|
storage: StorageConfigSchema,
|
|
513
514
|
memory: MemoryConfigSchema.optional(),
|
|
514
|
-
graph: GraphConfigSchema.
|
|
515
|
+
graph: GraphConfigSchema.default({
|
|
516
|
+
mode: "shadow",
|
|
517
|
+
killSwitch: false,
|
|
518
|
+
journalRoot: DEFAULT_GRAPH_JOURNAL_ROOT,
|
|
519
|
+
operatorApproval: false,
|
|
520
|
+
worktrees: { enabled: false }
|
|
521
|
+
}),
|
|
515
522
|
orchestrator: OrchestratorConfigSchema.optional()
|
|
516
523
|
}).strict();
|
|
517
524
|
var OpenTeamConfigSchema = OpenTeamConfigObjectSchema;
|
|
@@ -2040,6 +2047,13 @@ var FAILURE_CAUSE_RULES = [
|
|
|
2040
2047
|
"prompt-server"
|
|
2041
2048
|
]
|
|
2042
2049
|
];
|
|
2050
|
+
var TIMEOUT_NEEDLES = [
|
|
2051
|
+
"timed out",
|
|
2052
|
+
"timeout",
|
|
2053
|
+
"timedout",
|
|
2054
|
+
"etimedout",
|
|
2055
|
+
"deadline exceeded"
|
|
2056
|
+
];
|
|
2043
2057
|
function matchFailureCause(lowerReason) {
|
|
2044
2058
|
for (const [needles, cls] of FAILURE_CAUSE_RULES) {
|
|
2045
2059
|
if (needles.some((needle) => lowerReason.includes(needle))) {
|
|
@@ -2050,7 +2064,7 @@ function matchFailureCause(lowerReason) {
|
|
|
2050
2064
|
}
|
|
2051
2065
|
function classifyFailureForTelemetry(failureReason, failureStage) {
|
|
2052
2066
|
const lower = failureReason.toLowerCase();
|
|
2053
|
-
if (
|
|
2067
|
+
if (TIMEOUT_NEEDLES.some((needle) => lower.includes(needle))) {
|
|
2054
2068
|
return "prompt-timeout";
|
|
2055
2069
|
}
|
|
2056
2070
|
if (lower.includes("did not include a session id") || lower.includes("missing session")) {
|
|
@@ -3517,91 +3531,80 @@ var RESTART_REQUIRED_KEYS = [
|
|
|
3517
3531
|
"console.port"
|
|
3518
3532
|
];
|
|
3519
3533
|
|
|
3534
|
+
// src/web/graphRoute.ts
|
|
3535
|
+
var GRAPH_DISABLED_REMEDIATION = 'Agent tree is off. In .opencode/openteam.json set graph.mode to a value other than "off" and console.graphView.enabled to true, then restart the console.';
|
|
3536
|
+
var GRAPH_DISABLED_ENVELOPE = {
|
|
3537
|
+
disabled: true,
|
|
3538
|
+
reason: "graph-disabled",
|
|
3539
|
+
remediation: GRAPH_DISABLED_REMEDIATION
|
|
3540
|
+
};
|
|
3541
|
+
var JSON_HEADERS = { "content-type": "application/json" };
|
|
3542
|
+
var GRAPH_SSE_EVENT = "graph";
|
|
3543
|
+
function graphSseMessage(json) {
|
|
3544
|
+
return `event: ${GRAPH_SSE_EVENT}
|
|
3545
|
+
data: ${json}
|
|
3546
|
+
|
|
3547
|
+
`;
|
|
3548
|
+
}
|
|
3549
|
+
function bearerFromHeader(request) {
|
|
3550
|
+
const header = request.headers.authorization;
|
|
3551
|
+
if (typeof header !== "string" || !header.startsWith("Bearer ")) {
|
|
3552
|
+
return;
|
|
3553
|
+
}
|
|
3554
|
+
const value = header.slice("Bearer ".length).trim();
|
|
3555
|
+
return value.length === 0 ? undefined : value;
|
|
3556
|
+
}
|
|
3557
|
+
function authorized(request, query, token) {
|
|
3558
|
+
const provided = tokenFromQuery(query) ?? bearerFromHeader(request);
|
|
3559
|
+
return provided !== undefined && safeEqualToken(provided, token);
|
|
3560
|
+
}
|
|
3561
|
+
async function handleGraphRoute(request, response, url, deps) {
|
|
3562
|
+
if (url.pathname !== "/api/graph") {
|
|
3563
|
+
return false;
|
|
3564
|
+
}
|
|
3565
|
+
if (request.method !== "GET") {
|
|
3566
|
+
response.writeHead(405, JSON_HEADERS).end(JSON.stringify({ error: "method not allowed" }));
|
|
3567
|
+
return true;
|
|
3568
|
+
}
|
|
3569
|
+
if (deps.remoteToken !== undefined && !authorized(request, url.searchParams, deps.remoteToken)) {
|
|
3570
|
+
response.writeHead(401, JSON_HEADERS).end(JSON.stringify({ error: "unauthorized" }));
|
|
3571
|
+
return true;
|
|
3572
|
+
}
|
|
3573
|
+
const view = await deps.readView();
|
|
3574
|
+
response.writeHead(200, JSON_HEADERS).end(JSON.stringify(view));
|
|
3575
|
+
return true;
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3520
3578
|
// src/console/assets.ts
|
|
3521
3579
|
var CONSOLE_STYLE = `
|
|
3522
|
-
:root{color-scheme:dark;--bg:#
|
|
3523
|
-
*{box-sizing:border-box}
|
|
3524
|
-
body{
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
.
|
|
3531
|
-
.
|
|
3532
|
-
.
|
|
3533
|
-
.
|
|
3534
|
-
.
|
|
3535
|
-
.
|
|
3536
|
-
.
|
|
3537
|
-
.
|
|
3538
|
-
.
|
|
3539
|
-
|
|
3540
|
-
th,td{text-align:left;padding:
|
|
3541
|
-
|
|
3542
|
-
.
|
|
3543
|
-
.
|
|
3544
|
-
.
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
.tag{color:var(--frontier);font-size:12px}
|
|
3551
|
-
.kind{display:inline-block;min-width:66px;color:var(--muted);font-size:12px}
|
|
3552
|
-
code{font-family:ui-monospace,Consolas,monospace;background:#0b0d11;padding:1px 5px;border-radius:4px}
|
|
3553
|
-
.mono{font-family:ui-monospace,Consolas,monospace;font-size:12px}
|
|
3554
|
-
.tabs{display:flex;gap:6px;padding:12px 24px 0;flex-wrap:wrap;border-bottom:1px solid var(--line)}
|
|
3555
|
-
.tabs .tab{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-bottom:none;border-radius:8px 8px 0 0;padding:8px 14px;cursor:pointer;font:inherit;display:flex;align-items:center;gap:6px}
|
|
3556
|
-
.tabs .tab.active{background:var(--bg);border-color:var(--local);color:var(--fg)}
|
|
3557
|
-
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;background:var(--muted)}
|
|
3558
|
-
.badge{font-size:11px;padding:1px 8px;border-radius:10px;border:1px solid var(--line);color:var(--muted);vertical-align:middle}
|
|
3559
|
-
.status-running{background:var(--good)}
|
|
3560
|
-
.badge.status-running{color:var(--good);border-color:var(--good)}
|
|
3561
|
-
.status-idle{background:var(--muted)}
|
|
3562
|
-
.badge.status-idle{color:var(--muted)}
|
|
3563
|
-
.status-waiting-input{background:var(--frontier)}
|
|
3564
|
-
.badge.status-waiting-input{color:var(--frontier);border-color:var(--frontier)}
|
|
3565
|
-
.status-ended{background:#6e7681}
|
|
3566
|
-
.badge.status-ended{color:#6e7681}
|
|
3567
|
-
.term .termlog{background:#0b0d11;border:1px solid var(--line);border-radius:8px;padding:10px;max-height:340px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:12px;white-space:pre-wrap}
|
|
3568
|
-
.term .tl{padding:1px 0}
|
|
3569
|
-
.term .tl.input{color:var(--local)}
|
|
3570
|
-
.term .tl.tool{color:var(--frontier)}
|
|
3571
|
-
.term .tl.status{color:var(--muted)}
|
|
3572
|
-
.term .tl.error{color:#f85149}
|
|
3573
|
-
.term .tl.permission{color:var(--frontier);font-weight:600}
|
|
3574
|
-
.term form.terminput{display:flex;gap:8px;margin-top:10px}
|
|
3575
|
-
.term form.terminput input{flex:1;background:#0b0d11;color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font:inherit}
|
|
3576
|
-
.term form.terminput button{background:var(--local);color:#04122b;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font:inherit;font-weight:600}
|
|
3577
|
-
.pty .ptylog{background:#05070a;border:1px solid var(--line);border-radius:8px;padding:10px;max-height:340px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:12px;white-space:pre-wrap;color:#c8d1dc}
|
|
3578
|
-
.pty form.ptyinput{display:flex;gap:8px;margin-top:10px}
|
|
3579
|
-
.pty form.ptyinput input{flex:1;background:#05070a;color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font:inherit}
|
|
3580
|
-
.pty form.ptyinput button,.pty .pty-start{background:var(--frontier);color:#1a1200;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font:inherit;font-weight:600;margin-top:8px}
|
|
3581
|
-
.pty .pty-start:disabled{opacity:.5;cursor:default}
|
|
3582
|
-
.config-panel{grid-column:1/-1}
|
|
3583
|
-
.config-form{display:flex;flex-direction:column;gap:12px}
|
|
3584
|
-
.config-note{margin:0;font-size:12px;color:var(--muted)}
|
|
3585
|
-
.config-note.config-overlay{color:var(--frontier)}
|
|
3586
|
-
.reload-legend h3,.config-summary h4{font-size:12px;margin:0 0 6px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
|
|
3587
|
-
.reload-table{width:100%}
|
|
3588
|
-
.reload-table th{white-space:nowrap;vertical-align:top;width:1%}
|
|
3589
|
-
.reload-table th.reload-hot{color:var(--good)}
|
|
3590
|
-
.reload-table th.reload-restart{color:var(--frontier)}
|
|
3591
|
-
.config-label{font-size:12px;color:var(--muted)}
|
|
3592
|
-
.config-json{width:100%;min-height:280px;resize:vertical;background:#0b0d11;color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:10px;font-family:ui-monospace,Consolas,monospace;font-size:12px;line-height:1.5}
|
|
3593
|
-
.config-actions{display:flex;gap:8px}
|
|
3594
|
-
.config-save{background:var(--good);color:#08110a;border:0;border-radius:8px;padding:8px 16px;font-weight:600;cursor:pointer}
|
|
3595
|
-
.config-save:disabled{opacity:.5;cursor:default}
|
|
3596
|
-
.config-result:empty{display:none}
|
|
3597
|
-
.config-summary{border:1px solid var(--line);border-radius:8px;padding:12px;margin-top:4px}
|
|
3598
|
-
.config-ok{margin:0 0 8px;color:var(--good);font-weight:600}
|
|
3599
|
-
.config-err{margin:0;color:var(--frontier);font-weight:600}
|
|
3600
|
-
.config-applied h4{color:var(--good)}
|
|
3601
|
-
.config-restart h4{color:var(--frontier)}
|
|
3602
|
-
.config-warn{margin:0 0 6px;color:var(--frontier);font-size:13px}
|
|
3603
|
-
.config-summary ul{margin:0;padding-left:18px}
|
|
3604
|
-
.config-summary li{padding:2px 0;border:0}
|
|
3580
|
+
:root{color-scheme:dark;--bg:#0f0f0f;--surface:#17181b;--surface-2:#1e2024;--panel:var(--surface);--fg:#eceef1;--muted:#a2a8b2;--faint:#6b7079;--border:rgba(255,255,255,.08);--border-strong:rgba(255,255,255,.16);--line:var(--border);--accent:#35c07a;--accent-bg:rgba(53,192,122,.12);--accent-fg:#57d492;--good:var(--accent-fg);--local:#6aa6f5;--frontier:#e0a53a;--warn:#e0a53a;--warn-bg:rgba(224,165,58,.12);--danger:#f0687f;--danger-bg:rgba(240,104,127,.12);--info:#6aa6f5;--info-bg:rgba(106,166,245,.12);--font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;--font-mono:"JetBrains Mono","IBM Plex Mono",ui-monospace,"SFMono-Regular",Consolas,monospace;--shadow-pop:0 8px 28px rgba(0,0,0,.5);--sidebar-w:248px}
|
|
3581
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
3582
|
+
html,body{height:100%}
|
|
3583
|
+
body{background:var(--bg);color:var(--fg);font:14px/1.55 var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-variant-numeric:tabular-nums}
|
|
3584
|
+
.mono,code,.hash{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
|
|
3585
|
+
button{font:inherit;cursor:pointer}
|
|
3586
|
+
button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
|
|
3587
|
+
::selection{background:var(--accent-bg)}
|
|
3588
|
+
.app{display:grid;grid-template-columns:var(--sidebar-w) 1fr;grid-template-rows:100vh;min-height:100vh}
|
|
3589
|
+
.sidebar{background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0}
|
|
3590
|
+
.brand{display:flex;align-items:center;gap:10px;padding:18px 18px 16px;border-bottom:1px solid var(--border)}
|
|
3591
|
+
.logo{width:26px;height:26px;border-radius:6px;background:var(--accent);color:#fff;display:grid;place-items:center;flex:none}
|
|
3592
|
+
.logo svg{width:15px;height:15px}.brand .name{font-weight:600;letter-spacing:-.01em}.brand .tag{font:10px/1 var(--font-mono);letter-spacing:.08em;color:var(--accent-fg);background:var(--accent-bg);padding:3px 6px;border-radius:4px;margin-left:auto;text-transform:uppercase}
|
|
3593
|
+
.nav{padding:10px;display:flex;flex-direction:column;gap:2px}.nav-label{font:10px/1 var(--font-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--faint);padding:14px 8px 6px}.nav-item{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:7px;color:var(--muted);background:transparent;border:0;width:100%;text-align:left;letter-spacing:-.005em;transition:background .12s,color .12s}.nav-item svg{width:17px;height:17px;flex:none}.nav-item .count{margin-left:auto;font:11px/1 var(--font-mono);color:var(--faint)}.nav-item .pill{margin-left:auto}.nav-item:hover{background:var(--surface-2);color:var(--fg)}.nav-item[aria-current="page"]{background:var(--accent-bg);color:var(--accent-fg);font-weight:550}.nav-item[aria-current="page"] .count{color:var(--accent-fg)}
|
|
3594
|
+
.sidebar-foot{margin-top:auto;padding:14px 16px;border-top:1px solid var(--border);font-size:12px;color:var(--muted);display:flex;flex-direction:column;gap:8px}.host-row{display:flex;align-items:center;gap:8px}.dot{width:8px;height:8px;border-radius:50%;display:inline-block;flex:none;background:var(--muted)}.dot.live,.dot.status-running{background:var(--accent);box-shadow:0 0 0 3px var(--accent-bg)}.dot.warn,.dot.status-waiting-input{background:var(--warn)}.dot.down{background:var(--danger)}.dot.status-idle{background:var(--muted)}.dot.status-ended{background:#6e7681}
|
|
3595
|
+
.main{min-width:0;min-height:0;display:flex;flex-direction:column}.topbar{height:56px;flex:none;border-bottom:1px solid var(--border);background:color-mix(in srgb,var(--surface) 82%,transparent);backdrop-filter:blur(8px);display:flex;align-items:center;gap:14px;padding:0 20px}.topbar h1{font-size:15px;font-weight:600;letter-spacing:-.01em}.refresh,.token-chip{display:inline-flex;align-items:center;gap:7px;font:11px/1 var(--font-mono);color:var(--muted);padding:5px 9px;border:1px solid var(--border);border-radius:6px;background:var(--surface)}.refresh .beat{width:6px;height:6px;border-radius:50%;background:var(--accent);animation:beat 2s ease-in-out infinite}@keyframes beat{0%,100%{opacity:1}50%{opacity:.25}}.spacer{margin-left:auto}.icon-btn{width:34px;height:34px;border-radius:7px;border:1px solid var(--border);background:var(--surface);color:var(--muted);display:grid;place-items:center;transition:background .12s,color .12s,border-color .12s}.icon-btn:hover{background:var(--surface-2);color:var(--fg);border-color:var(--border-strong)}.icon-btn svg{width:17px;height:17px}.token-chip b{color:var(--fg);font-weight:600}
|
|
3596
|
+
.scroll{flex:1;overflow-y:auto;min-height:0}.screen{display:none;padding:22px 24px 40px;max-width:1320px}.screen.active{display:block}.screen[hidden]{display:none!important}.screen-head{margin-bottom:18px}.screen-head h2{font-size:19px;font-weight:600;letter-spacing:-.02em;display:flex;align-items:center;gap:8px}.screen-head p{color:var(--muted);font-size:13px;margin-top:3px;max-width:74ch;text-wrap:pretty}.screen-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px}.screen-grid .wide,.panel.wide{grid-column:1/-1}
|
|
3597
|
+
.panel,.card{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:16px}.panel h2,.panel h3,.card-head h3{font-size:13px;margin:0 0 12px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}.card-head{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--border);margin:-16px -16px 14px}.card-head .sub{font:11px/1 var(--font-mono);color:var(--faint);margin-left:auto}.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}.stat{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:15px 16px;display:flex;flex-direction:column;gap:2px}.stat .k{font:11px/1 var(--font-mono);letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}.stat .v{font-size:20px;font-weight:600;letter-spacing:-.02em;margin-top:6px}.stat .d{font-size:12px;color:var(--muted);margin-top:4px;display:flex;align-items:center;gap:6px}.good{color:var(--good)}.local{color:var(--local)}.frontier{color:var(--frontier)}.muted{color:var(--muted)}.faint{color:var(--faint)}
|
|
3598
|
+
table{width:100%;border-collapse:collapse;font-size:13px}th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--border)}th{font:11px/1 var(--font-mono);letter-spacing:.05em;text-transform:uppercase;color:var(--faint);font-weight:500}tbody tr:last-child td{border-bottom:0}tbody tr:hover{background:var(--surface-2)}td.num{text-align:right;font-family:var(--font-mono)}.hash{color:var(--faint)}
|
|
3599
|
+
.pill,.badge{display:inline-flex;align-items:center;gap:5px;font:11px/1 var(--font-mono);letter-spacing:.02em;padding:3px 8px;border-radius:999px;border:1px solid transparent}.pill.local,.badge.status-running{color:var(--accent-fg);background:var(--accent-bg);border-color:var(--accent-bg)}.pill.frontier{color:var(--info);background:var(--info-bg)}.pill.warn,.badge.status-waiting-input{color:var(--warn);background:var(--warn-bg);border-color:var(--warn-bg)}.pill.danger{color:var(--danger);background:var(--danger-bg)}.pill.idle,.badge.status-idle,.badge.status-ended{color:var(--muted);background:var(--surface-2)}
|
|
3600
|
+
.progress{height:10px;background:var(--surface-2);border-radius:6px;overflow:hidden;border:1px solid var(--border)}.progress .bar{height:100%;background:var(--accent)}ul.items,ul.timeline{list-style:none;margin:8px 0 0;padding:0;max-height:320px;overflow:auto}ul.items li,ul.timeline li{padding:6px 0;border-bottom:1px solid var(--border)}ul.items li.done{color:var(--muted)}ul.items .box{font-family:var(--font-mono)}.who{color:var(--local)}.tag{color:var(--frontier);font-size:12px}.kind{display:inline-block;min-width:66px;color:var(--muted);font-size:12px}code{background:var(--bg);padding:1px 5px;border-radius:4px}
|
|
3601
|
+
.logs-layout{display:grid;grid-template-columns:220px 1fr;gap:16px;align-items:start;margin-bottom:16px}.session-list{padding:6px;max-height:70vh;overflow-y:auto}.session-btn{display:flex;align-items:center;gap:8px;width:100%;padding:8px 10px;border-radius:7px;border:0;background:transparent;color:var(--muted);text-align:left;font:12px/1.3 var(--font-mono)}.session-btn:hover{background:var(--surface-2);color:var(--fg)}.session-btn.sel,.session-btn[aria-selected="true"]{background:var(--accent-bg);color:var(--accent-fg)}.session-btn .sid{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-btn .n{margin-left:auto;color:var(--faint)}.session-detail{display:flex;flex-direction:column;gap:16px}.session-detail[hidden]{display:none}.empty-state{padding:28px;text-align:center;color:var(--muted)}
|
|
3602
|
+
.term .termlog,.pty .ptylog{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:10px;max-height:340px;overflow:auto;font-family:var(--font-mono);font-size:12px;white-space:pre-wrap}.term .tl{padding:1px 0}.term .tl.input{color:var(--local)}.term .tl.tool{color:var(--frontier)}.term .tl.status{color:var(--muted)}.term .tl.error{color:var(--danger)}.term .tl.permission{color:var(--frontier);font-weight:600}.term form.terminput,.pty form.ptyinput{display:flex;gap:8px;margin-top:10px}.term form.terminput input,.pty form.ptyinput input{flex:1;background:var(--bg);color:var(--fg);border:1px solid var(--border);border-radius:8px;padding:8px 10px;font:inherit}.term form.terminput button{background:var(--local);color:#04122b;border:none;border-radius:8px;padding:8px 16px;font:inherit;font-weight:600}.pty .ptylog{background:#05070a;color:#c8d1dc}.pty form.ptyinput button,.pty .pty-start{background:var(--frontier);color:#1a1200;border:none;border-radius:8px;padding:8px 16px;font:inherit;font-weight:600;margin-top:8px}.pty .pty-start:disabled{opacity:.5;cursor:default}
|
|
3603
|
+
.config-panel{grid-column:1/-1}.config-form{display:flex;flex-direction:column;gap:12px}.config-note{margin:0;font-size:12px;color:var(--muted)}.config-note.config-overlay{color:var(--frontier)}.reload-legend h3,.config-summary h4{font-size:12px;margin:0 0 6px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}.reload-table{width:100%}.reload-table th{white-space:nowrap;vertical-align:top;width:1%}.reload-table th.reload-hot{color:var(--good)}.reload-table th.reload-restart{color:var(--frontier)}.config-label{font-size:12px;color:var(--muted)}.config-json{width:100%;min-height:280px;resize:vertical;background:var(--bg);color:var(--fg);border:1px solid var(--border-strong);border-radius:8px;padding:10px;font-family:var(--font-mono);font-size:12px;line-height:1.5}.config-actions{display:flex;gap:8px}.config-save{background:var(--accent);color:#fff;border:0;border-radius:8px;padding:8px 16px;font-weight:600;cursor:pointer}.config-save:disabled{opacity:.5;cursor:default}.config-result:empty{display:none}.config-summary{border:1px solid var(--border);border-radius:8px;padding:12px;margin-top:4px}.config-ok{margin:0 0 8px;color:var(--good);font-weight:600}.config-err{margin:0;color:var(--frontier);font-weight:600}.config-applied h4{color:var(--good)}.config-restart h4{color:var(--frontier)}.config-warn{margin:0 0 6px;color:var(--frontier);font-size:13px}.config-summary ul{margin:0;padding-left:18px}.config-summary li{padding:2px 0;border:0}
|
|
3604
|
+
.diag-banner{border-bottom:1px solid color-mix(in srgb,var(--warn) 40%,transparent);background:var(--warn-bg);color:var(--warn);padding:10px 20px;font-size:13px}.graph-panel{min-height:240px}.graph-disabled,.graph-empty{border:1px solid var(--border);border-radius:8px;padding:14px;background:var(--surface-2)}.graph-empty{border-color:color-mix(in srgb,var(--info) 35%,var(--border));background:var(--info-bg)}.graph-disabled .hint{margin-top:6px}.scrim{display:none}.menu-btn{display:none}
|
|
3605
|
+
@media (max-width:900px){.app{grid-template-columns:1fr}.sidebar{position:fixed;inset:0 auto 0 0;width:280px;z-index:50;transform:translateX(-100%);transition:transform .22s ease}body.nav-open .sidebar{transform:translateX(0);box-shadow:var(--shadow-pop)}body.nav-open .scrim{display:block;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:40}.menu-btn{display:grid}.logs-layout{grid-template-columns:1fr}.grid{grid-template-columns:1fr}.token-chip .lbl{display:none}}
|
|
3606
|
+
@media (max-width:560px){.screen{padding:16px 14px 32px}.screen-grid{grid-template-columns:1fr}.topbar{padding:0 14px}.refresh{display:none}}
|
|
3607
|
+
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.001ms!important;transition-duration:.001ms!important}}
|
|
3605
3608
|
`;
|
|
3606
3609
|
var GRAPH_STYLE = `
|
|
3607
3610
|
.graph-wrap{overflow:auto;background:#0b0d11;border:1px solid var(--line);border-radius:8px;padding:8px}
|
|
@@ -3671,24 +3674,69 @@ var CONSOLE_CLIENT_JS = `
|
|
|
3671
3674
|
var ms=parseInt(document.documentElement.getAttribute('data-refresh')||'2000',10);
|
|
3672
3675
|
setInterval(function(){reloadIfChanged(current)},isNaN(ms)?2000:Math.max(250,ms));
|
|
3673
3676
|
|
|
3674
|
-
function
|
|
3675
|
-
var
|
|
3676
|
-
|
|
3677
|
-
|
|
3677
|
+
function configActive(){
|
|
3678
|
+
var s=document.getElementById('screen-config');
|
|
3679
|
+
return !!(s&&!s.hidden);
|
|
3680
|
+
}
|
|
3681
|
+
function wireLogsScreen(screen){
|
|
3682
|
+
if(screen.__logsWired){return}
|
|
3683
|
+
screen.__logsWired=true;
|
|
3684
|
+
var buttons=screen.querySelectorAll('[data-session-target]');
|
|
3685
|
+
var cards=screen.querySelectorAll('[data-session-view]');
|
|
3686
|
+
function showSession(id){
|
|
3687
|
+
var matched=false;
|
|
3688
|
+
for(var i=0;i<buttons.length;i++){
|
|
3689
|
+
var on=buttons[i].getAttribute('data-session-target')===id;
|
|
3690
|
+
buttons[i].classList.toggle('sel',on);
|
|
3691
|
+
buttons[i].setAttribute('aria-selected',on?'true':'false');
|
|
3692
|
+
}
|
|
3693
|
+
for(var j=0;j<cards.length;j++){
|
|
3694
|
+
var show=cards[j].getAttribute('data-session-view')===id;
|
|
3695
|
+
cards[j].hidden=!show;
|
|
3696
|
+
if(show){matched=true;connectTerminal(cards[j],id)}
|
|
3697
|
+
}
|
|
3698
|
+
return matched;
|
|
3699
|
+
}
|
|
3700
|
+
for(var k=0;k<buttons.length;k++){
|
|
3701
|
+
buttons[k].addEventListener('click',function(e){
|
|
3702
|
+
var id=e.currentTarget.getAttribute('data-session-target');
|
|
3703
|
+
if(id){showSession(id)}
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3706
|
+
if(buttons.length>0){
|
|
3707
|
+
showSession(buttons[0].getAttribute('data-session-target'));
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
function selectScreen(name){
|
|
3711
|
+
var screens=document.querySelectorAll('.screen');
|
|
3712
|
+
var items=document.querySelectorAll('[data-screen]');
|
|
3678
3713
|
var matched=false;
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3714
|
+
var titleText='';
|
|
3715
|
+
var activeScreen=null;
|
|
3716
|
+
for(var i=0;i<screens.length;i++){
|
|
3717
|
+
var show=screens[i].id==='screen-'+name;
|
|
3718
|
+
screens[i].classList.toggle('active',show);
|
|
3719
|
+
screens[i].hidden=!show;
|
|
3720
|
+
if(show){matched=true;activeScreen=screens[i]}
|
|
3683
3721
|
}
|
|
3684
3722
|
if(!matched){return false}
|
|
3685
|
-
|
|
3723
|
+
for(var j=0;j<items.length;j++){
|
|
3724
|
+
var on=items[j].getAttribute('data-screen')===name;
|
|
3725
|
+
if(on){
|
|
3726
|
+
items[j].setAttribute('aria-current','page');
|
|
3727
|
+
titleText=items[j].getAttribute('data-title')||'';
|
|
3728
|
+
}else{
|
|
3729
|
+
items[j].removeAttribute('aria-current');
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
var title=document.getElementById('screen-title');
|
|
3733
|
+
if(title){title.textContent=titleText}
|
|
3734
|
+
if(name==='config'&&activeScreen){loadConfig(activeScreen)}
|
|
3735
|
+
if(name==='logs'&&activeScreen){wireLogsScreen(activeScreen)}
|
|
3736
|
+
try{localStorage.setItem('openteam.screen',name)}catch(e){}
|
|
3737
|
+
document.body.classList.remove('nav-open');
|
|
3686
3738
|
return true;
|
|
3687
3739
|
}
|
|
3688
|
-
function configActive(){
|
|
3689
|
-
var v=document.querySelector('main .view[data-view="config"]');
|
|
3690
|
-
return !!(v&&!v.hidden);
|
|
3691
|
-
}
|
|
3692
3740
|
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')}
|
|
3693
3741
|
// Config editor (W7-S09 / #193). The config values travel only over the
|
|
3694
3742
|
// token-gated /api/config, never in this open page, so the browser fetches
|
|
@@ -3827,7 +3875,7 @@ var CONSOLE_CLIENT_JS = `
|
|
|
3827
3875
|
if(!token){return}
|
|
3828
3876
|
var drill=document.querySelector('.graph-drill');
|
|
3829
3877
|
if(!drill){return}
|
|
3830
|
-
var gview=drill.closest('.
|
|
3878
|
+
var gview=drill.closest('.screen');
|
|
3831
3879
|
if(!gview){return}
|
|
3832
3880
|
var log=drill.querySelector('.drilllog');
|
|
3833
3881
|
var title=drill.querySelector('.drill-title');
|
|
@@ -3861,22 +3909,27 @@ var CONSOLE_CLIENT_JS = `
|
|
|
3861
3909
|
if(t&&t.getAttribute&&t.getAttribute('data-session')){open(t.getAttribute('data-session'))}
|
|
3862
3910
|
});
|
|
3863
3911
|
}
|
|
3864
|
-
var
|
|
3865
|
-
if(
|
|
3866
|
-
|
|
3912
|
+
var nav=document.querySelector('.nav');
|
|
3913
|
+
if(nav){
|
|
3914
|
+
nav.addEventListener('click',function(e){
|
|
3867
3915
|
var t=e.target;
|
|
3868
|
-
while(t&&t!==
|
|
3869
|
-
if(t&&t.getAttribute('data-
|
|
3916
|
+
while(t&&t!==nav&&!(t.getAttribute&&t.getAttribute('data-screen'))){t=t.parentNode}
|
|
3917
|
+
if(t&&t.getAttribute&&t.getAttribute('data-screen')){selectScreen(t.getAttribute('data-screen'))}
|
|
3870
3918
|
});
|
|
3871
3919
|
}
|
|
3872
|
-
var
|
|
3873
|
-
|
|
3874
|
-
|
|
3920
|
+
var toggle=document.querySelector('[data-toggle-nav]');
|
|
3921
|
+
if(toggle){toggle.addEventListener('click',function(){document.body.classList.toggle('nav-open')})}
|
|
3922
|
+
var close=document.querySelector('[data-close-nav]');
|
|
3923
|
+
if(close){close.addEventListener('click',function(){document.body.classList.remove('nav-open')})}
|
|
3924
|
+
var saved='tree';
|
|
3925
|
+
try{saved=localStorage.getItem('openteam.screen')||'tree'}catch(e){}
|
|
3926
|
+
if(!selectScreen(saved)){selectScreen('tree')}
|
|
3875
3927
|
wireGraphDrill();
|
|
3876
3928
|
})();
|
|
3877
3929
|
`;
|
|
3878
3930
|
|
|
3879
3931
|
// src/console/render.ts
|
|
3932
|
+
var GRAPH_EMPTY_REMEDIATION = "No graph run yet. The tree fills when openteam-orchestrate dispatches roles; ordinary chat does not create a graph run.";
|
|
3880
3933
|
var TIERS = [
|
|
3881
3934
|
"trivial",
|
|
3882
3935
|
"simple",
|
|
@@ -3901,6 +3954,13 @@ function avgMs(stats) {
|
|
|
3901
3954
|
function agentModel(agent) {
|
|
3902
3955
|
return agent.model === undefined ? "inherits default" : `${agent.model.providerID}/${agent.model.modelID}`;
|
|
3903
3956
|
}
|
|
3957
|
+
function domIdPart(value) {
|
|
3958
|
+
const cleaned = value.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3959
|
+
return cleaned.length === 0 ? "session" : cleaned;
|
|
3960
|
+
}
|
|
3961
|
+
function panelId(base, idSuffix) {
|
|
3962
|
+
return idSuffix === undefined ? base : `${base}-${idSuffix}`;
|
|
3963
|
+
}
|
|
3904
3964
|
function summaryPanel(cost) {
|
|
3905
3965
|
return [
|
|
3906
3966
|
'<section class="panel" id="panel-summary">',
|
|
@@ -3979,10 +4039,11 @@ function teamPanel(team) {
|
|
|
3979
4039
|
function routeRow(route) {
|
|
3980
4040
|
return `<tr><td>${route.tier}</td><td class="${route.routeKind}">${route.routeKind}</td><td>${escapeHtml(route.model)}</td><td class="hash">${escapeHtml(route.promptHash)}</td><td>${usd(route.estimatedCostUSD)}</td><td class="good">${usd(route.estimatedSavingsUSD)}</td></tr>`;
|
|
3981
4041
|
}
|
|
3982
|
-
function routesPanel(routes) {
|
|
4042
|
+
function routesPanel(routes, idSuffix) {
|
|
4043
|
+
const id = panelId("panel-routes", idSuffix);
|
|
3983
4044
|
if (routes.length === 0) {
|
|
3984
4045
|
return [
|
|
3985
|
-
|
|
4046
|
+
`<section class="panel" id="${id}">`,
|
|
3986
4047
|
"<h2>Recent decisions</h2>",
|
|
3987
4048
|
'<p class="muted">No routing decisions yet.</p>',
|
|
3988
4049
|
"</section>"
|
|
@@ -3990,7 +4051,7 @@ function routesPanel(routes) {
|
|
|
3990
4051
|
}
|
|
3991
4052
|
const rows = routes.map(routeRow).join("");
|
|
3992
4053
|
return [
|
|
3993
|
-
|
|
4054
|
+
`<section class="panel" id="${id}">`,
|
|
3994
4055
|
"<h2>Recent decisions</h2>",
|
|
3995
4056
|
"<table><thead><tr><th>Tier</th><th>Route</th><th>Model</th><th>Prompt (hash)</th><th>Cost</th><th>Savings</th></tr></thead>",
|
|
3996
4057
|
`<tbody>${rows}</tbody></table>`,
|
|
@@ -4001,10 +4062,11 @@ function activityRow(entry) {
|
|
|
4001
4062
|
const who = entry.agent === undefined ? "" : `<span class="who">${escapeHtml(entry.agent)}</span> `;
|
|
4002
4063
|
return `<li class="${entry.kind}"><span class="kind">${entry.kind}</span> ${who}${escapeHtml(entry.summary)}</li>`;
|
|
4003
4064
|
}
|
|
4004
|
-
function activityPanel(activity) {
|
|
4065
|
+
function activityPanel(activity, idSuffix) {
|
|
4066
|
+
const id = panelId("panel-activity", idSuffix);
|
|
4005
4067
|
if (activity.length === 0) {
|
|
4006
4068
|
return [
|
|
4007
|
-
|
|
4069
|
+
`<section class="panel" id="${id}">`,
|
|
4008
4070
|
"<h2>Activity</h2>",
|
|
4009
4071
|
'<p class="muted">No activity recorded.</p>',
|
|
4010
4072
|
"</section>"
|
|
@@ -4012,7 +4074,7 @@ function activityPanel(activity) {
|
|
|
4012
4074
|
}
|
|
4013
4075
|
const rows = activity.map(activityRow).join("");
|
|
4014
4076
|
return [
|
|
4015
|
-
|
|
4077
|
+
`<section class="panel" id="${id}">`,
|
|
4016
4078
|
"<h2>Activity</h2>",
|
|
4017
4079
|
`<ul class="timeline">${rows}</ul>`,
|
|
4018
4080
|
"</section>"
|
|
@@ -4074,10 +4136,11 @@ function decisionRow(decision2) {
|
|
|
4074
4136
|
const tags = decision2.tags === undefined || decision2.tags.length === 0 ? "" : ` ${decision2.tags.map((tag) => `<span class="tag">#${escapeHtml(tag)}</span>`).join(" ")}`;
|
|
4075
4137
|
return `<li class="decision"><span class="kind">decision</span> ${who}${escapeHtml(decision2.summary)}${tags}</li>`;
|
|
4076
4138
|
}
|
|
4077
|
-
function decisionsPanel(decisions) {
|
|
4139
|
+
function decisionsPanel(decisions, idSuffix) {
|
|
4140
|
+
const id = panelId("panel-decisions", idSuffix);
|
|
4078
4141
|
if (decisions.length === 0) {
|
|
4079
4142
|
return [
|
|
4080
|
-
|
|
4143
|
+
`<section class="panel" id="${id}">`,
|
|
4081
4144
|
"<h2>Decisions</h2>",
|
|
4082
4145
|
'<p class="muted">No decisions recorded (the scribe writes to <code>openteam-decisions.md</code>).</p>',
|
|
4083
4146
|
"</section>"
|
|
@@ -4085,7 +4148,7 @@ function decisionsPanel(decisions) {
|
|
|
4085
4148
|
}
|
|
4086
4149
|
const rows = decisions.map(decisionRow).join("");
|
|
4087
4150
|
return [
|
|
4088
|
-
|
|
4151
|
+
`<section class="panel" id="${id}">`,
|
|
4089
4152
|
"<h2>Decisions</h2>",
|
|
4090
4153
|
`<ul class="timeline">${rows}</ul>`,
|
|
4091
4154
|
"</section>"
|
|
@@ -4096,10 +4159,11 @@ function meetingRow(meeting) {
|
|
|
4096
4159
|
const purpose = meeting.purpose === undefined ? "" : `: ${escapeHtml(meeting.purpose)}`;
|
|
4097
4160
|
return `<li class="agent"><span class="kind">meeting</span> <span class="who">${roles}</span>${purpose}</li>`;
|
|
4098
4161
|
}
|
|
4099
|
-
function meetingsPanel(meetings) {
|
|
4162
|
+
function meetingsPanel(meetings, idSuffix) {
|
|
4163
|
+
const id = panelId("panel-meetings", idSuffix);
|
|
4100
4164
|
if (meetings.length === 0) {
|
|
4101
4165
|
return [
|
|
4102
|
-
|
|
4166
|
+
`<section class="panel" id="${id}">`,
|
|
4103
4167
|
"<h2>Meetings</h2>",
|
|
4104
4168
|
'<p class="muted">No multi-agent meetings yet.</p>',
|
|
4105
4169
|
"</section>"
|
|
@@ -4107,7 +4171,7 @@ function meetingsPanel(meetings) {
|
|
|
4107
4171
|
}
|
|
4108
4172
|
const rows = meetings.map(meetingRow).join("");
|
|
4109
4173
|
return [
|
|
4110
|
-
|
|
4174
|
+
`<section class="panel" id="${id}">`,
|
|
4111
4175
|
"<h2>Meetings</h2>",
|
|
4112
4176
|
`<ul class="timeline">${rows}</ul>`,
|
|
4113
4177
|
"</section>"
|
|
@@ -4122,20 +4186,54 @@ var STATUS_LABELS = {
|
|
|
4122
4186
|
function statusBadge(status) {
|
|
4123
4187
|
return `<span class="badge status-${status}">${STATUS_LABELS[status]}</span>`;
|
|
4124
4188
|
}
|
|
4125
|
-
function tabBar(sessions, graphEnabled, configEnabled) {
|
|
4126
|
-
const allTab = '<button type="button" class="tab" data-tab="all">All</button>';
|
|
4127
|
-
const graphTab = graphEnabled ? '<button type="button" class="tab" data-tab="graph">Graph</button>' : "";
|
|
4128
|
-
const configTab = configEnabled ? '<button type="button" class="tab" data-tab="config">Configuration</button>' : "";
|
|
4129
|
-
const tabs = sessions.map((tab) => {
|
|
4130
|
-
const id = escapeHtml(tab.sessionID);
|
|
4131
|
-
const short = escapeHtml(shortSessionID(tab.sessionID));
|
|
4132
|
-
return `<button type="button" class="tab" data-tab="${id}"><span class="dot status-${tab.status}"></span>${short}</button>`;
|
|
4133
|
-
}).join("");
|
|
4134
|
-
return `<nav class="tabs" id="tabbar">${allTab}${graphTab}${configTab}${tabs}</nav>`;
|
|
4135
|
-
}
|
|
4136
4189
|
function shortSessionID(sessionID) {
|
|
4137
4190
|
return sessionID.length <= 12 ? sessionID : `${sessionID.slice(0, 12)}…`;
|
|
4138
4191
|
}
|
|
4192
|
+
var ICON_TREE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="3" width="6" height="5" rx="1"></rect><rect x="3" y="16" width="6" height="5" rx="1"></rect><rect x="15" y="16" width="6" height="5" rx="1"></rect><path d="M12 8v4M6 16v-2h12v2"></path></svg>';
|
|
4193
|
+
var ICON_ROUTING = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"></path><path d="M7 15l3-4 3 2 4-6"></path></svg>';
|
|
4194
|
+
var ICON_LOGS = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5h16M4 10h16M4 15h10M4 20h13"></path></svg>';
|
|
4195
|
+
var ICON_CONFIG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"></path><path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-2.7 1.1V21a2 2 0 1 1-4 0v-.1A1.6 1.6 0 0 0 6 19.4a1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0-1.1-2.7H0"></path></svg>';
|
|
4196
|
+
function navButton(screen, label, icon, options = {}) {
|
|
4197
|
+
const active = options.active === true ? ' aria-current="page"' : "";
|
|
4198
|
+
const count = options.count === undefined ? "" : `<span class="count mono"${screen === "tree" ? " data-live-count" : ""}>${options.count}</span>`;
|
|
4199
|
+
const pill = options.pill === undefined ? "" : `<span class="pill warn">${escapeHtml(options.pill)}</span>`;
|
|
4200
|
+
return `<button type="button" class="nav-item" role="tab" data-screen="${screen}" data-title="${escapeHtml(label)}" aria-controls="screen-${screen}"${active}>${icon} ${escapeHtml(label)}${count}${pill}</button>`;
|
|
4201
|
+
}
|
|
4202
|
+
function sidebarNav(liveCount, logCount) {
|
|
4203
|
+
return [
|
|
4204
|
+
'<aside class="sidebar" id="sidebar">',
|
|
4205
|
+
'<div class="brand">',
|
|
4206
|
+
'<span class="logo" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5" r="2.4"></circle><circle cx="5.5" cy="18" r="2.4"></circle><circle cx="18.5" cy="18" r="2.4"></circle><path d="M12 7.4v4.2M12 11.6 6.6 16M12 11.6 17.4 16"></path></svg></span>',
|
|
4207
|
+
'<span class="name">openteam</span>',
|
|
4208
|
+
'<span class="tag">console</span>',
|
|
4209
|
+
"</div>",
|
|
4210
|
+
'<nav class="nav" aria-label="Console surfaces">',
|
|
4211
|
+
'<div class="nav-label">Operations</div>',
|
|
4212
|
+
navButton("tree", "Agent tree", ICON_TREE, {
|
|
4213
|
+
active: true,
|
|
4214
|
+
count: liveCount
|
|
4215
|
+
}),
|
|
4216
|
+
navButton("routing", "Routing + telemetry", ICON_ROUTING),
|
|
4217
|
+
navButton("logs", "Logs / activity", ICON_LOGS, { count: logCount }),
|
|
4218
|
+
navButton("config", "Configuration", ICON_CONFIG, { pill: "editable" }),
|
|
4219
|
+
"</nav>",
|
|
4220
|
+
'<div class="sidebar-foot">',
|
|
4221
|
+
'<div class="host-row"><span class="dot live"></span><span>loopback console</span></div>',
|
|
4222
|
+
'<div class="host-row faint"><span>prompts stay redacted by hash</span></div>',
|
|
4223
|
+
"</div>",
|
|
4224
|
+
"</aside>"
|
|
4225
|
+
].join("");
|
|
4226
|
+
}
|
|
4227
|
+
function screenSection(screen, label, heading, description, body, active = false) {
|
|
4228
|
+
const className = active ? "screen active" : "screen";
|
|
4229
|
+
const hidden = active ? "" : " hidden";
|
|
4230
|
+
return [
|
|
4231
|
+
`<section class="${className}" id="screen-${screen}" role="tabpanel" aria-label="${escapeHtml(label)}"${hidden}>`,
|
|
4232
|
+
`<div class="screen-head"><h2>${heading}</h2><p>${description}</p></div>`,
|
|
4233
|
+
body,
|
|
4234
|
+
"</section>"
|
|
4235
|
+
].join("");
|
|
4236
|
+
}
|
|
4139
4237
|
function sessionHeaderPanel(tab) {
|
|
4140
4238
|
const endpoint = tab.endpoint === undefined ? '<span class="muted">endpoint not announced</span>' : `<code>${escapeHtml(tab.endpoint.url)}</code>`;
|
|
4141
4239
|
const dir = tab.endpoint?.directory === undefined ? "" : `<div class="stat"><span class="k">Directory</span><span class="v mono">${escapeHtml(tab.endpoint.directory)}</span></div>`;
|
|
@@ -4181,17 +4279,18 @@ function ptyPanel(tab) {
|
|
|
4181
4279
|
"</section>"
|
|
4182
4280
|
].join("");
|
|
4183
4281
|
}
|
|
4184
|
-
function
|
|
4282
|
+
function sessionDetailCard(tab, terminalEnabled, ptyEnabled, visible, index) {
|
|
4283
|
+
const idSuffix = `${index}-${domIdPart(tab.sessionID)}`;
|
|
4185
4284
|
const body = [
|
|
4186
4285
|
sessionHeaderPanel(tab),
|
|
4187
4286
|
terminalEnabled ? terminalPanel(tab) : "",
|
|
4188
4287
|
terminalEnabled && ptyEnabled ? ptyPanel(tab) : "",
|
|
4189
|
-
routesPanel(tab.recentRoutes),
|
|
4190
|
-
decisionsPanel(tab.decisions),
|
|
4191
|
-
meetingsPanel(tab.meetings),
|
|
4192
|
-
activityPanel(tab.activity)
|
|
4288
|
+
routesPanel(tab.recentRoutes, idSuffix),
|
|
4289
|
+
decisionsPanel(tab.decisions, idSuffix),
|
|
4290
|
+
meetingsPanel(tab.meetings, idSuffix),
|
|
4291
|
+
activityPanel(tab.activity, idSuffix)
|
|
4193
4292
|
].join("");
|
|
4194
|
-
return `<
|
|
4293
|
+
return `<article class="session-detail" id="session-${idSuffix}" data-session-view="${escapeHtml(tab.sessionID)}"${visible ? "" : " hidden"}>${body}</article>`;
|
|
4195
4294
|
}
|
|
4196
4295
|
var GRAPH_COL_W = 220;
|
|
4197
4296
|
var GRAPH_ROW_H = 96;
|
|
@@ -4317,7 +4416,7 @@ function renderGraphView(projection, options = {}) {
|
|
|
4317
4416
|
return graphDisabledHint();
|
|
4318
4417
|
}
|
|
4319
4418
|
if (projection === null) {
|
|
4320
|
-
return
|
|
4419
|
+
return graphEmptyHint();
|
|
4321
4420
|
}
|
|
4322
4421
|
if (projection.nodes.length === 0) {
|
|
4323
4422
|
return '<p class="muted">The run has no nodes.</p>';
|
|
@@ -4356,51 +4455,117 @@ function drillPanel() {
|
|
|
4356
4455
|
"</aside>"
|
|
4357
4456
|
].join("");
|
|
4358
4457
|
}
|
|
4359
|
-
function
|
|
4458
|
+
function treeGraphPanel(projection, drilldownEnabled, disabled) {
|
|
4360
4459
|
return [
|
|
4361
|
-
'<
|
|
4362
|
-
|
|
4363
|
-
"<h2>Agent graph</h2>",
|
|
4460
|
+
'<section class="panel graph-panel" id="panel-graph">',
|
|
4461
|
+
"<h3>Live graph</h3>",
|
|
4364
4462
|
disabled || projection === null ? "" : healthBanner(projection.health),
|
|
4463
|
+
'<div id="tree-content" data-graph-mount>',
|
|
4365
4464
|
renderGraphView(projection, { disabled }),
|
|
4465
|
+
"</div>",
|
|
4366
4466
|
!disabled && drilldownEnabled ? drillPanel() : "",
|
|
4367
|
-
"</section>"
|
|
4368
|
-
"</div>"
|
|
4467
|
+
"</section>"
|
|
4369
4468
|
].join("");
|
|
4370
4469
|
}
|
|
4371
4470
|
function graphDisabledHint() {
|
|
4372
4471
|
return [
|
|
4373
4472
|
'<div class="graph-disabled" role="status">',
|
|
4374
|
-
|
|
4375
|
-
'<p class="hint">Set <code>graph.mode</code> to <code>shadow</code> in ',
|
|
4376
|
-
"<code>.opencode/openteam.json</code> to enable it, then restart the session.</p>",
|
|
4473
|
+
`<p class="muted">${escapeHtml(GRAPH_DISABLED_REMEDIATION)}</p>`,
|
|
4377
4474
|
"</div>"
|
|
4378
4475
|
].join("");
|
|
4379
4476
|
}
|
|
4380
|
-
function
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4477
|
+
function graphEmptyHint() {
|
|
4478
|
+
return [
|
|
4479
|
+
'<div class="graph-empty" role="status">',
|
|
4480
|
+
`<p class="muted">${escapeHtml(GRAPH_EMPTY_REMEDIATION)}</p>`,
|
|
4481
|
+
"</div>"
|
|
4482
|
+
].join("");
|
|
4483
|
+
}
|
|
4484
|
+
function routingScreenBody(state) {
|
|
4485
|
+
return [
|
|
4486
|
+
'<div class="screen-grid">',
|
|
4386
4487
|
summaryPanel(state.cost),
|
|
4387
4488
|
totalsPanel(state.totals),
|
|
4388
4489
|
sessionsPanel(state.sessions),
|
|
4389
|
-
loopPanel(state.loop),
|
|
4390
|
-
teamPanel(state.team),
|
|
4391
4490
|
tierPanel(state.cost),
|
|
4392
4491
|
routesPanel(state.recentRoutes),
|
|
4492
|
+
"</div>"
|
|
4493
|
+
].join("");
|
|
4494
|
+
}
|
|
4495
|
+
function emptySessionList() {
|
|
4496
|
+
return [
|
|
4497
|
+
'<div class="card">',
|
|
4498
|
+
'<div class="card-head"><h3>Sessions</h3><span class="sub">0</span></div>',
|
|
4499
|
+
'<div class="empty-state">No sessions recorded yet.</div>',
|
|
4500
|
+
"</div>"
|
|
4501
|
+
].join("");
|
|
4502
|
+
}
|
|
4503
|
+
function sessionList(sessions) {
|
|
4504
|
+
if (sessions.length === 0) {
|
|
4505
|
+
return emptySessionList();
|
|
4506
|
+
}
|
|
4507
|
+
const buttons = sessions.map((tab, index) => {
|
|
4508
|
+
const selected = index === 0;
|
|
4509
|
+
return [
|
|
4510
|
+
`<button type="button" class="session-btn${selected ? " sel" : ""}" role="tab" aria-selected="${selected ? "true" : "false"}" data-session-target="${escapeHtml(tab.sessionID)}">`,
|
|
4511
|
+
`<span class="dot status-${tab.status}"></span>`,
|
|
4512
|
+
`<span class="sid">${escapeHtml(shortSessionID(tab.sessionID))}</span>`,
|
|
4513
|
+
`<span class="n">${tab.summary.messageCount}</span>`,
|
|
4514
|
+
"</button>"
|
|
4515
|
+
].join("");
|
|
4516
|
+
}).join("");
|
|
4517
|
+
return [
|
|
4518
|
+
'<div class="card">',
|
|
4519
|
+
`<div class="card-head"><h3>Sessions</h3><span class="sub">${sessions.length}</span></div>`,
|
|
4520
|
+
'<div class="session-list" role="tablist" aria-label="Sessions">',
|
|
4521
|
+
buttons,
|
|
4522
|
+
"</div>",
|
|
4523
|
+
"</div>"
|
|
4524
|
+
].join("");
|
|
4525
|
+
}
|
|
4526
|
+
function sessionDetailList(sessions, terminalEnabled, ptyEnabled) {
|
|
4527
|
+
if (sessions.length === 0) {
|
|
4528
|
+
return '<div class="card empty-state">No session activity recorded yet.</div>';
|
|
4529
|
+
}
|
|
4530
|
+
return sessions.map((tab, index) => sessionDetailCard(tab, terminalEnabled, ptyEnabled, index === 0, index)).join("");
|
|
4531
|
+
}
|
|
4532
|
+
function logsScreenBody(state, terminalEnabled, ptyEnabled) {
|
|
4533
|
+
return [
|
|
4534
|
+
'<div class="logs-layout">',
|
|
4535
|
+
sessionList(state.sessionsDetail),
|
|
4536
|
+
"<div>",
|
|
4537
|
+
sessionDetailList(state.sessionsDetail, terminalEnabled, ptyEnabled),
|
|
4538
|
+
"</div>",
|
|
4539
|
+
"</div>",
|
|
4540
|
+
'<div class="screen-grid">',
|
|
4541
|
+
loopPanel(state.loop),
|
|
4542
|
+
teamPanel(state.team),
|
|
4393
4543
|
decisionsPanel(state.decisions),
|
|
4394
4544
|
meetingsPanel(state.meetings),
|
|
4395
|
-
activityPanel(state.activity)
|
|
4545
|
+
activityPanel(state.activity),
|
|
4546
|
+
"</div>"
|
|
4396
4547
|
].join("");
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4548
|
+
}
|
|
4549
|
+
function configScreenBody(hasToken) {
|
|
4550
|
+
return renderConfigEditorPanel({ hasToken });
|
|
4551
|
+
}
|
|
4552
|
+
function renderConsoleHtml(state, options = {}) {
|
|
4553
|
+
const refreshMs = options.refreshMs ?? 2000;
|
|
4554
|
+
const terminalEnabled = options.terminalEnabled === true;
|
|
4555
|
+
const ptyEnabled = options.ptyEnabled === true;
|
|
4556
|
+
const graphOptionProvided = options.graphView !== undefined;
|
|
4557
|
+
const graphDrilldown = graphOptionProvided && options.drilldownEnabled === true;
|
|
4558
|
+
const graphProjection = options.graphView?.projection ?? null;
|
|
4559
|
+
const graphDisabled = options.graphView === undefined || options.graphView.disabled === true;
|
|
4560
|
+
const liveCount = graphOptionProvided && graphProjection !== null && !graphDisabled ? graphProjection.nodes.length : state.sessionsDetail.length;
|
|
4401
4561
|
const configEnabled = options.configEditor !== undefined;
|
|
4402
|
-
const
|
|
4403
|
-
const body =
|
|
4562
|
+
const treeBody = treeGraphPanel(graphProjection, graphDrilldown, graphDisabled);
|
|
4563
|
+
const body = [
|
|
4564
|
+
screenSection("tree", "Live agent tree", "Agent tree · live", "Live agent topology. Graph updates arrive over the <code>graph</code> event on <code>/events</code>.", treeBody, true),
|
|
4565
|
+
screenSection("routing", "Routing and telemetry", "Routing + telemetry", "Aggregate routing and cost telemetry from <code>GET /api/state</code> across all sessions.", routingScreenBody(state)),
|
|
4566
|
+
screenSection("logs", "Logs and session activity", "Logs / activity", "Per-session activity. Prompts are referenced by hash only and never rendered in clear text.", logsScreenBody(state, terminalEnabled, ptyEnabled)),
|
|
4567
|
+
screenSection("config", "Editable configuration", 'Configuration <span class="pill warn">editable</span>', "Edit the whole openteam JSON configuration. Roles are editable inside the JSON when present; unsupported surfaces are not shown.", configScreenBody(options.configEditor?.hasToken === true))
|
|
4568
|
+
].join("");
|
|
4404
4569
|
const session = state.session.id === undefined ? "" : ` · session ${escapeHtml(state.session.id)}`;
|
|
4405
4570
|
const tokenAttr = (terminalEnabled || graphDrilldown || configEnabled) && options.consoleToken !== undefined ? ` data-token="${escapeHtml(options.consoleToken)}"${terminalEnabled ? ' data-terminal="1"' : ""}${ptyEnabled ? ' data-pty="1"' : ""}${graphDrilldown ? ' data-drilldown="1"' : ""}${configEnabled ? ' data-config="1"' : ""}` : "";
|
|
4406
4571
|
return [
|
|
@@ -4410,16 +4575,24 @@ function renderConsoleHtml(state, options = {}) {
|
|
|
4410
4575
|
'<meta charset="utf-8">',
|
|
4411
4576
|
'<meta name="viewport" content="width=device-width,initial-scale=1">',
|
|
4412
4577
|
"<title>openteam console</title>",
|
|
4413
|
-
`<style>${CONSOLE_STYLE}${
|
|
4578
|
+
`<style>${CONSOLE_STYLE}${graphOptionProvided ? GRAPH_STYLE : ""}</style>`,
|
|
4414
4579
|
"</head>",
|
|
4415
4580
|
"<body>",
|
|
4416
|
-
"
|
|
4417
|
-
|
|
4418
|
-
|
|
4581
|
+
'<div class="app">',
|
|
4582
|
+
sidebarNav(liveCount, state.sessionsDetail.length),
|
|
4583
|
+
'<div class="scrim" data-close-nav></div>',
|
|
4584
|
+
'<main class="main">',
|
|
4585
|
+
'<header class="topbar">',
|
|
4586
|
+
'<button type="button" class="icon-btn menu-btn" data-toggle-nav aria-label="Open navigation"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 6h16M4 12h16M4 18h16"></path></svg></button>',
|
|
4587
|
+
'<h1 id="screen-title">Agent tree</h1>',
|
|
4588
|
+
`<span class="refresh" title="SSE + polling"><span class="beat"></span> ${refreshMs} ms · SSE</span>`,
|
|
4589
|
+
'<div class="spacer"></div>',
|
|
4590
|
+
`<span class="token-chip" title="Page status"><span class="lbl">updated</span> <b class="mono">${escapeHtml(state.generatedAt)}${session}</b></span>`,
|
|
4419
4591
|
"</header>",
|
|
4420
4592
|
options.diagnosticActive === true ? '<div class="diag-banner" role="alert">Diagnostic mode enabled — this loopback console may show raw prompt and model text locally.</div>' : "",
|
|
4421
|
-
|
|
4422
|
-
|
|
4593
|
+
`<div class="scroll">${body}</div>`,
|
|
4594
|
+
"</main>",
|
|
4595
|
+
"</div>",
|
|
4423
4596
|
`<script>${CONSOLE_CLIENT_JS}</script>`,
|
|
4424
4597
|
"</body>",
|
|
4425
4598
|
"</html>"
|
|
@@ -4482,23 +4655,21 @@ function renderConfigSaveSummary(write) {
|
|
|
4482
4655
|
}
|
|
4483
4656
|
return `<div class="config-summary">${parts.join("")}</div>`;
|
|
4484
4657
|
}
|
|
4485
|
-
function
|
|
4658
|
+
function renderConfigEditorPanel(opts) {
|
|
4486
4659
|
const inner = opts.hasToken ? [
|
|
4487
4660
|
'<p class="muted">Edit the openteam configuration. Portable keys are committed to <code>.opencode/openteam.json</code>; machine-specific keys stay in your local overlay and are never committed; secrets are read from environment variables and are never shown here.</p>',
|
|
4488
4661
|
'<div class="config-mount" data-config-mount><p class="muted">Loading configuration…</p></div>'
|
|
4489
4662
|
].join("") : '<p class="muted">Configuration editing requires the console session token, which is not present in this page. This view is read-only.</p>';
|
|
4490
4663
|
return [
|
|
4491
|
-
'<div class="view" data-view="config" hidden>',
|
|
4492
4664
|
'<section class="panel config-panel">',
|
|
4493
|
-
"<
|
|
4665
|
+
"<h3>Configuration JSON</h3>",
|
|
4494
4666
|
inner,
|
|
4495
|
-
"</section>"
|
|
4496
|
-
"</div>"
|
|
4667
|
+
"</section>"
|
|
4497
4668
|
].join("");
|
|
4498
4669
|
}
|
|
4499
4670
|
|
|
4500
4671
|
// src/web/configRoute.ts
|
|
4501
|
-
var
|
|
4672
|
+
var JSON_HEADERS2 = { "content-type": "application/json" };
|
|
4502
4673
|
var LOOPBACK_HOSTS = new Set([
|
|
4503
4674
|
"127.0.0.1",
|
|
4504
4675
|
"localhost",
|
|
@@ -4506,7 +4677,7 @@ var LOOPBACK_HOSTS = new Set([
|
|
|
4506
4677
|
"::1"
|
|
4507
4678
|
]);
|
|
4508
4679
|
var MAX_CONFIG_BODY_CHARS = 256 * 1024;
|
|
4509
|
-
function
|
|
4680
|
+
function bearerFromHeader2(request) {
|
|
4510
4681
|
const header = request.headers.authorization;
|
|
4511
4682
|
if (typeof header !== "string" || !header.startsWith("Bearer ")) {
|
|
4512
4683
|
return;
|
|
@@ -4519,7 +4690,7 @@ function queryToken(query) {
|
|
|
4519
4690
|
return value === null || value.length === 0 ? undefined : value;
|
|
4520
4691
|
}
|
|
4521
4692
|
function readAuthorized(request, query, token) {
|
|
4522
|
-
const provided =
|
|
4693
|
+
const provided = bearerFromHeader2(request) ?? queryToken(query);
|
|
4523
4694
|
return provided !== undefined && safeEqualToken(provided, token);
|
|
4524
4695
|
}
|
|
4525
4696
|
function isCrossOriginMutation(request) {
|
|
@@ -4570,34 +4741,34 @@ async function handleConfigRoute(request, response, url, deps) {
|
|
|
4570
4741
|
}
|
|
4571
4742
|
if (request.method === "GET") {
|
|
4572
4743
|
if (!readAuthorized(request, url.searchParams, deps.token)) {
|
|
4573
|
-
response.writeHead(401,
|
|
4744
|
+
response.writeHead(401, JSON_HEADERS2).end(JSON.stringify({ error: "unauthorized" }));
|
|
4574
4745
|
return true;
|
|
4575
4746
|
}
|
|
4576
4747
|
const result = await deps.editor.read();
|
|
4577
|
-
response.writeHead(200,
|
|
4748
|
+
response.writeHead(200, JSON_HEADERS2).end(JSON.stringify({ ...result, render: renderConfigEditorForm(result) }));
|
|
4578
4749
|
return true;
|
|
4579
4750
|
}
|
|
4580
4751
|
if (request.method === "POST") {
|
|
4581
4752
|
if (isCrossOriginMutation(request)) {
|
|
4582
|
-
response.writeHead(403,
|
|
4753
|
+
response.writeHead(403, JSON_HEADERS2).end(JSON.stringify({ error: "cross-origin forbidden" }));
|
|
4583
4754
|
return true;
|
|
4584
4755
|
}
|
|
4585
|
-
const bearer =
|
|
4756
|
+
const bearer = bearerFromHeader2(request);
|
|
4586
4757
|
if (bearer === undefined || !safeEqualToken(bearer, deps.token)) {
|
|
4587
|
-
response.writeHead(401,
|
|
4758
|
+
response.writeHead(401, JSON_HEADERS2).end(JSON.stringify({ error: "unauthorized" }));
|
|
4588
4759
|
return true;
|
|
4589
4760
|
}
|
|
4590
4761
|
const body = await readJsonBody(request, MAX_CONFIG_BODY_CHARS);
|
|
4591
4762
|
if (!body.ok) {
|
|
4592
|
-
response.writeHead(400,
|
|
4763
|
+
response.writeHead(400, JSON_HEADERS2).end(JSON.stringify({ ok: false, error: "invalid JSON body" }));
|
|
4593
4764
|
return true;
|
|
4594
4765
|
}
|
|
4595
4766
|
const result = await deps.editor.write(body.value);
|
|
4596
4767
|
const responseBody = result.status === 200 ? { ...result.body, render: renderConfigSaveSummary(result.body) } : result.body;
|
|
4597
|
-
response.writeHead(result.status,
|
|
4768
|
+
response.writeHead(result.status, JSON_HEADERS2).end(JSON.stringify(responseBody));
|
|
4598
4769
|
return true;
|
|
4599
4770
|
}
|
|
4600
|
-
response.writeHead(405,
|
|
4771
|
+
response.writeHead(405, JSON_HEADERS2).end(JSON.stringify({ error: "method not allowed" }));
|
|
4601
4772
|
return true;
|
|
4602
4773
|
}
|
|
4603
4774
|
function createConfigReloader(deps) {
|
|
@@ -4921,8 +5092,8 @@ var SSE_HEADERS = {
|
|
|
4921
5092
|
"cache-control": "no-cache",
|
|
4922
5093
|
connection: "keep-alive"
|
|
4923
5094
|
};
|
|
4924
|
-
var
|
|
4925
|
-
function
|
|
5095
|
+
var JSON_HEADERS3 = { "content-type": "application/json" };
|
|
5096
|
+
function bearerFromHeader3(request) {
|
|
4926
5097
|
const header = request.headers.authorization;
|
|
4927
5098
|
if (typeof header !== "string" || !header.startsWith("Bearer ")) {
|
|
4928
5099
|
return;
|
|
@@ -4930,8 +5101,8 @@ function bearerFromHeader2(request) {
|
|
|
4930
5101
|
const value = header.slice("Bearer ".length).trim();
|
|
4931
5102
|
return value.length === 0 ? undefined : value;
|
|
4932
5103
|
}
|
|
4933
|
-
function
|
|
4934
|
-
const provided = tokenFromQuery(query) ??
|
|
5104
|
+
function authorized2(request, query, token) {
|
|
5105
|
+
const provided = tokenFromQuery(query) ?? bearerFromHeader3(request);
|
|
4935
5106
|
return provided !== undefined && safeEqualToken(provided, token);
|
|
4936
5107
|
}
|
|
4937
5108
|
async function readJsonBody2(request, limit) {
|
|
@@ -4974,7 +5145,7 @@ function parseConsolePath(path) {
|
|
|
4974
5145
|
async function handleStream(request, response, sessionID, deps, diagnostic) {
|
|
4975
5146
|
const endpoint = await deps.resolveEndpoint(sessionID);
|
|
4976
5147
|
if (endpoint === undefined) {
|
|
4977
|
-
response.writeHead(404,
|
|
5148
|
+
response.writeHead(404, JSON_HEADERS3).end(JSON.stringify({ error: "no endpoint" }));
|
|
4978
5149
|
return;
|
|
4979
5150
|
}
|
|
4980
5151
|
response.writeHead(200, SSE_HEADERS);
|
|
@@ -4995,31 +5166,31 @@ data: {}
|
|
|
4995
5166
|
async function handleMessages(_request, response, sessionID, deps, diagnostic) {
|
|
4996
5167
|
const endpoint = await deps.resolveEndpoint(sessionID);
|
|
4997
5168
|
if (endpoint === undefined) {
|
|
4998
|
-
response.writeHead(404,
|
|
5169
|
+
response.writeHead(404, JSON_HEADERS3).end(JSON.stringify({ error: "no endpoint" }));
|
|
4999
5170
|
return;
|
|
5000
5171
|
}
|
|
5001
5172
|
const frames = await deps.createClient(endpoint).listMessages(sessionID, { diagnostic });
|
|
5002
|
-
response.writeHead(200,
|
|
5173
|
+
response.writeHead(200, JSON_HEADERS3).end(JSON.stringify({ frames }));
|
|
5003
5174
|
}
|
|
5004
5175
|
async function handleInput(request, response, sessionID, deps) {
|
|
5005
5176
|
const endpoint = await deps.resolveEndpoint(sessionID);
|
|
5006
5177
|
if (endpoint === undefined) {
|
|
5007
|
-
response.writeHead(404,
|
|
5178
|
+
response.writeHead(404, JSON_HEADERS3).end(JSON.stringify({ error: "no endpoint" }));
|
|
5008
5179
|
return;
|
|
5009
5180
|
}
|
|
5010
5181
|
const body = await readJsonBody2(request, MAX_INPUT_CHARS + 1024);
|
|
5011
5182
|
const input = parseSessionInput(body);
|
|
5012
5183
|
if (input === undefined) {
|
|
5013
|
-
response.writeHead(400,
|
|
5184
|
+
response.writeHead(400, JSON_HEADERS3).end(JSON.stringify({ error: "invalid input" }));
|
|
5014
5185
|
return;
|
|
5015
5186
|
}
|
|
5016
5187
|
await deps.createClient(endpoint).sendPrompt(sessionID, input.text);
|
|
5017
|
-
response.writeHead(202,
|
|
5188
|
+
response.writeHead(202, JSON_HEADERS3).end(JSON.stringify({ ok: true }));
|
|
5018
5189
|
}
|
|
5019
5190
|
async function handlePermission(request, response, sessionID, deps) {
|
|
5020
5191
|
const endpoint = await deps.resolveEndpoint(sessionID);
|
|
5021
5192
|
if (endpoint === undefined) {
|
|
5022
|
-
response.writeHead(404,
|
|
5193
|
+
response.writeHead(404, JSON_HEADERS3).end(JSON.stringify({ error: "no endpoint" }));
|
|
5023
5194
|
return;
|
|
5024
5195
|
}
|
|
5025
5196
|
const body = await readJsonBody2(request, 4096);
|
|
@@ -5027,34 +5198,34 @@ async function handlePermission(request, response, sessionID, deps) {
|
|
|
5027
5198
|
const permissionID = record.permissionID;
|
|
5028
5199
|
const responseValue = record.response;
|
|
5029
5200
|
if (typeof permissionID !== "string" || permissionID.length === 0 || responseValue !== "once" && responseValue !== "always" && responseValue !== "reject") {
|
|
5030
|
-
response.writeHead(400,
|
|
5201
|
+
response.writeHead(400, JSON_HEADERS3).end(JSON.stringify({ error: "invalid permission" }));
|
|
5031
5202
|
return;
|
|
5032
5203
|
}
|
|
5033
5204
|
await deps.createClient(endpoint).respondPermission(sessionID, permissionID, responseValue);
|
|
5034
|
-
response.writeHead(202,
|
|
5205
|
+
response.writeHead(202, JSON_HEADERS3).end(JSON.stringify({ ok: true }));
|
|
5035
5206
|
}
|
|
5036
5207
|
async function handlePty(_request, response, sessionID, deps) {
|
|
5037
5208
|
if (deps.ptyEnabled !== true || deps.createPtyClient === undefined) {
|
|
5038
|
-
response.writeHead(403,
|
|
5209
|
+
response.writeHead(403, JSON_HEADERS3).end(JSON.stringify({ error: "pty disabled" }));
|
|
5039
5210
|
return;
|
|
5040
5211
|
}
|
|
5041
5212
|
const endpoint = await deps.resolveEndpoint(sessionID);
|
|
5042
5213
|
if (endpoint === undefined) {
|
|
5043
|
-
response.writeHead(404,
|
|
5214
|
+
response.writeHead(404, JSON_HEADERS3).end(JSON.stringify({ error: "no endpoint" }));
|
|
5044
5215
|
return;
|
|
5045
5216
|
}
|
|
5046
5217
|
const client = deps.createPtyClient(endpoint);
|
|
5047
5218
|
const pty = await client.createPty({ command: "", title: "openteam" });
|
|
5048
5219
|
if (pty === undefined) {
|
|
5049
|
-
response.writeHead(502,
|
|
5220
|
+
response.writeHead(502, JSON_HEADERS3).end(JSON.stringify({ error: "pty create failed" }));
|
|
5050
5221
|
return;
|
|
5051
5222
|
}
|
|
5052
5223
|
const token = await client.connectToken(pty.id);
|
|
5053
5224
|
if (token === undefined) {
|
|
5054
|
-
response.writeHead(502,
|
|
5225
|
+
response.writeHead(502, JSON_HEADERS3).end(JSON.stringify({ error: "connect token failed" }));
|
|
5055
5226
|
return;
|
|
5056
5227
|
}
|
|
5057
|
-
response.writeHead(200,
|
|
5228
|
+
response.writeHead(200, JSON_HEADERS3).end(JSON.stringify({
|
|
5058
5229
|
ptyID: pty.id,
|
|
5059
5230
|
wsUrl: client.wsUrl(pty.id, token.ticket)
|
|
5060
5231
|
}));
|
|
@@ -5065,17 +5236,17 @@ async function handleConsoleRoute(request, response, url, deps) {
|
|
|
5065
5236
|
}
|
|
5066
5237
|
const route = parseConsolePath(url.pathname);
|
|
5067
5238
|
if (route === undefined) {
|
|
5068
|
-
response.writeHead(404,
|
|
5239
|
+
response.writeHead(404, JSON_HEADERS3).end(JSON.stringify({ error: "not found" }));
|
|
5069
5240
|
return true;
|
|
5070
5241
|
}
|
|
5071
5242
|
const readOnlyAction = route.action === "stream" || route.action === "messages";
|
|
5072
5243
|
const channelEnabled = deps.terminalEnabled || readOnlyAction && deps.drilldownEnabled === true;
|
|
5073
5244
|
if (!channelEnabled) {
|
|
5074
|
-
response.writeHead(403,
|
|
5245
|
+
response.writeHead(403, JSON_HEADERS3).end(JSON.stringify({ error: "terminal disabled" }));
|
|
5075
5246
|
return true;
|
|
5076
5247
|
}
|
|
5077
|
-
if (!
|
|
5078
|
-
response.writeHead(401,
|
|
5248
|
+
if (!authorized2(request, url.searchParams, deps.token)) {
|
|
5249
|
+
response.writeHead(401, JSON_HEADERS3).end(JSON.stringify({ error: "unauthorized" }));
|
|
5079
5250
|
return true;
|
|
5080
5251
|
}
|
|
5081
5252
|
const diagnostic = deps.diagnosticEnabled === true && url.searchParams.get("diagnostic") === "1";
|
|
@@ -5099,45 +5270,7 @@ async function handleConsoleRoute(request, response, url, deps) {
|
|
|
5099
5270
|
await handlePty(request, response, route.sessionID, deps);
|
|
5100
5271
|
return true;
|
|
5101
5272
|
}
|
|
5102
|
-
response.writeHead(405,
|
|
5103
|
-
return true;
|
|
5104
|
-
}
|
|
5105
|
-
|
|
5106
|
-
// src/web/graphRoute.ts
|
|
5107
|
-
var JSON_HEADERS3 = { "content-type": "application/json" };
|
|
5108
|
-
var GRAPH_SSE_EVENT = "graph";
|
|
5109
|
-
function graphSseMessage(json) {
|
|
5110
|
-
return `event: ${GRAPH_SSE_EVENT}
|
|
5111
|
-
data: ${json}
|
|
5112
|
-
|
|
5113
|
-
`;
|
|
5114
|
-
}
|
|
5115
|
-
function bearerFromHeader3(request) {
|
|
5116
|
-
const header = request.headers.authorization;
|
|
5117
|
-
if (typeof header !== "string" || !header.startsWith("Bearer ")) {
|
|
5118
|
-
return;
|
|
5119
|
-
}
|
|
5120
|
-
const value = header.slice("Bearer ".length).trim();
|
|
5121
|
-
return value.length === 0 ? undefined : value;
|
|
5122
|
-
}
|
|
5123
|
-
function authorized2(request, query, token) {
|
|
5124
|
-
const provided = tokenFromQuery(query) ?? bearerFromHeader3(request);
|
|
5125
|
-
return provided !== undefined && safeEqualToken(provided, token);
|
|
5126
|
-
}
|
|
5127
|
-
async function handleGraphRoute(request, response, url, deps) {
|
|
5128
|
-
if (url.pathname !== "/api/graph") {
|
|
5129
|
-
return false;
|
|
5130
|
-
}
|
|
5131
|
-
if (request.method !== "GET") {
|
|
5132
|
-
response.writeHead(405, JSON_HEADERS3).end(JSON.stringify({ error: "method not allowed" }));
|
|
5133
|
-
return true;
|
|
5134
|
-
}
|
|
5135
|
-
if (deps.remoteToken !== undefined && !authorized2(request, url.searchParams, deps.remoteToken)) {
|
|
5136
|
-
response.writeHead(401, JSON_HEADERS3).end(JSON.stringify({ error: "unauthorized" }));
|
|
5137
|
-
return true;
|
|
5138
|
-
}
|
|
5139
|
-
const view = await deps.readView();
|
|
5140
|
-
response.writeHead(200, JSON_HEADERS3).end(JSON.stringify(view));
|
|
5273
|
+
response.writeHead(405, JSON_HEADERS3).end(JSON.stringify({ error: "method not allowed" }));
|
|
5141
5274
|
return true;
|
|
5142
5275
|
}
|
|
5143
5276
|
|
|
@@ -5408,6 +5541,14 @@ async function createConsoleServer(deps) {
|
|
|
5408
5541
|
if (graphDeps !== undefined && await handleGraphRoute(request, response, url2, graphDeps)) {
|
|
5409
5542
|
return;
|
|
5410
5543
|
}
|
|
5544
|
+
if (graphDeps === undefined && url2.pathname === "/api/graph") {
|
|
5545
|
+
if (request.method !== "GET") {
|
|
5546
|
+
response.writeHead(405, { "content-type": "application/json" }).end(JSON.stringify({ error: "method not allowed" }));
|
|
5547
|
+
return;
|
|
5548
|
+
}
|
|
5549
|
+
response.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(GRAPH_DISABLED_ENVELOPE));
|
|
5550
|
+
return;
|
|
5551
|
+
}
|
|
5411
5552
|
if (deps.storageApi !== undefined && await handleStorageRoute(request, response, url2, deps.storageApi)) {
|
|
5412
5553
|
return;
|
|
5413
5554
|
}
|
|
@@ -5982,7 +6123,7 @@ async function createConsoleRuntime(deps) {
|
|
|
5982
6123
|
});
|
|
5983
6124
|
const createServer = deps.serve ?? createConsoleServer;
|
|
5984
6125
|
const graphApi = deps.config.graphView?.enabled === true && deps.graphView !== undefined ? { readView: deps.graphView.readView } : undefined;
|
|
5985
|
-
const graphDisabled =
|
|
6126
|
+
const graphDisabled = graphApi === undefined;
|
|
5986
6127
|
const server = await createServer({
|
|
5987
6128
|
readSnapshot,
|
|
5988
6129
|
config: deps.config,
|
|
@@ -6960,7 +7101,7 @@ async function runConsoleServe(deps, options) {
|
|
|
6960
7101
|
endpoint,
|
|
6961
7102
|
...deps.serverPassword !== undefined ? { password: deps.serverPassword } : {}
|
|
6962
7103
|
}));
|
|
6963
|
-
const graphMode = config.graph?.mode ?? "
|
|
7104
|
+
const graphMode = config.graph?.mode ?? "shadow";
|
|
6964
7105
|
const graphViewEnabled = config.console.graphView?.enabled === true;
|
|
6965
7106
|
const buildGraphView2 = deps.createGraphViewProvider ?? createGraphViewProvider;
|
|
6966
7107
|
const graphViewProvider = graphMode !== "off" && graphViewEnabled ? buildGraphView2({
|
|
@@ -8363,7 +8504,7 @@ import { z as z16 } from "zod";
|
|
|
8363
8504
|
|
|
8364
8505
|
// src/config/resolve.ts
|
|
8365
8506
|
function resolveGraphConfig(config, source) {
|
|
8366
|
-
const configuredMode = config.graph?.mode ?? "
|
|
8507
|
+
const configuredMode = config.graph?.mode ?? "shadow";
|
|
8367
8508
|
const killSwitch = config.graph?.killSwitch ?? false;
|
|
8368
8509
|
const effectiveMode = killSwitch ? "off" : configuredMode;
|
|
8369
8510
|
return {
|
|
@@ -15315,7 +15456,7 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
15315
15456
|
// package.json
|
|
15316
15457
|
var package_default = {
|
|
15317
15458
|
name: "@jmanuelcorral/openteam",
|
|
15318
|
-
version: "0.
|
|
15459
|
+
version: "0.13.0",
|
|
15319
15460
|
packageManager: "bun@1.3.14",
|
|
15320
15461
|
description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
15321
15462
|
license: "MIT",
|