nexus-agentd 0.1.0 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +351 -179
  3. package/dist/a2a/runtime.d.ts +30 -0
  4. package/dist/a2a/runtime.js +374 -0
  5. package/dist/acp/runtime.d.ts +12 -3
  6. package/dist/acp/runtime.js +248 -17
  7. package/dist/artifact-store.d.ts +41 -0
  8. package/dist/artifact-store.js +340 -0
  9. package/dist/auth.d.ts +13 -0
  10. package/dist/auth.js +77 -0
  11. package/dist/cli.js +58 -1
  12. package/dist/config.d.ts +10 -0
  13. package/dist/config.js +281 -53
  14. package/dist/control-plane.d.ts +91 -0
  15. package/dist/control-plane.js +629 -0
  16. package/dist/drivers/hermes.d.ts +2 -0
  17. package/dist/drivers/hermes.js +10 -0
  18. package/dist/drivers/index.js +6 -0
  19. package/dist/events.js +13 -2
  20. package/dist/index.d.ts +6 -0
  21. package/dist/index.js +15 -3
  22. package/dist/run-store.d.ts +49 -0
  23. package/dist/run-store.js +291 -0
  24. package/dist/server.d.ts +3 -2
  25. package/dist/server.js +911 -41
  26. package/dist/session-contract.d.ts +16 -4
  27. package/dist/session.d.ts +74 -8
  28. package/dist/session.js +433 -38
  29. package/dist/types.d.ts +193 -6
  30. package/dist/types.js +2 -1
  31. package/dist/webui/app.d.ts +1 -0
  32. package/dist/webui/app.js +1072 -0
  33. package/dist/webui/icons.d.ts +9 -0
  34. package/dist/webui/icons.js +10 -0
  35. package/dist/webui/index.d.ts +3 -0
  36. package/dist/webui/index.js +32 -0
  37. package/dist/webui/markup.d.ts +1 -0
  38. package/dist/webui/markup.js +68 -0
  39. package/dist/webui/styles.d.ts +1 -0
  40. package/dist/webui/styles.js +269 -0
  41. package/dist/workspace-files.d.ts +48 -0
  42. package/dist/workspace-files.js +269 -0
  43. package/nexus-agentd.example.json +30 -27
  44. package/package.json +6 -6
@@ -0,0 +1,9 @@
1
+ export declare const icons: {
2
+ overview: string;
3
+ runs: string;
4
+ agents: string;
5
+ workspaces: string;
6
+ files: string;
7
+ keys: string;
8
+ settings: string;
9
+ };
@@ -0,0 +1,10 @@
1
+ const base = (paths) => `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">${paths}</svg>`;
2
+ export const icons = {
3
+ overview: base('<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>'),
4
+ runs: base('<path d="M4 4v16h16"/><path d="m7 15 3-4 3 2 4-6"/><circle cx="7" cy="15" r="1"/><circle cx="10" cy="11" r="1"/><circle cx="13" cy="13" r="1"/><circle cx="17" cy="7" r="1"/>'),
5
+ agents: base('<path d="M12 8V4H8"/><rect x="4" y="8" width="16" height="12" rx="2"/><path d="M9 13h.01M15 13h.01M9 17h6"/>'),
6
+ workspaces: base('<path d="M3 7.5h7l2 2h9v9.5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M3 7.5V5a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v2.5"/>'),
7
+ files: base('<path d="M4 3h10l6 6v12H4z"/><path d="M14 3v6h6"/><path d="M8 14h8M8 18h6"/>'),
8
+ keys: base('<circle cx="8" cy="15" r="4"/><path d="m11 12 8-8M15 8l3 3M17 6l3 3"/>'),
9
+ settings: base('<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.83 2.83-.06-.06A1.7 1.7 0 0 0 15 19.4a1.7 1.7 0 0 0-1 .6 1.7 1.7 0 0 0-.4 1.1V21H9.6v-.09A1.7 1.7 0 0 0 8.5 19.4a1.7 1.7 0 0 0-1.88.34l-.06.06-2.83-2.83.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-.6-1 1.7 1.7 0 0 0-1.1-.4H3V9.6h.09A1.7 1.7 0 0 0 4.6 8.5a1.7 1.7 0 0 0-.34-1.88l-.06-.06 2.83-2.83.06.06A1.7 1.7 0 0 0 9 4.6a1.7 1.7 0 0 0 1-.6 1.7 1.7 0 0 0 .4-1.1V3h4v.09A1.7 1.7 0 0 0 15.5 4.6a1.7 1.7 0 0 0 1.88-.34l.06-.06 2.83 2.83-.06.06A1.7 1.7 0 0 0 19.4 9c.15.38.38.72.7 1 .3.27.7.4 1.1.4h.09v4h-.09c-.4 0-.8.14-1.1.4-.32.28-.55.62-.7 1Z"/>')
10
+ };
@@ -0,0 +1,3 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ export declare function redirectToAgentdWebUi(response: ServerResponse): void;
3
+ export declare function writeAgentdWebUi(response: ServerResponse): void;
@@ -0,0 +1,32 @@
1
+ import { app } from './app.js';
2
+ import { markup } from './markup.js';
3
+ import { styles } from './styles.js';
4
+ const page = `<!doctype html>
5
+ <html lang="zh-CN">
6
+ <head>
7
+ <meta charset="utf-8">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <meta name="color-scheme" content="light dark">
10
+ <title>Agent Nexus</title>
11
+ <style>${styles}</style>
12
+ </head>
13
+ <body>${markup}<script>${app}</script></body>
14
+ </html>`;
15
+ export function redirectToAgentdWebUi(response) {
16
+ response.writeHead(302, {
17
+ Location: '/ui/',
18
+ 'Cache-Control': 'no-store'
19
+ });
20
+ response.end();
21
+ }
22
+ export function writeAgentdWebUi(response) {
23
+ response.writeHead(200, {
24
+ 'Content-Type': 'text/html; charset=utf-8',
25
+ 'Content-Length': Buffer.byteLength(page),
26
+ 'Cache-Control': 'no-store',
27
+ 'X-Content-Type-Options': 'nosniff',
28
+ 'Referrer-Policy': 'no-referrer',
29
+ 'Content-Security-Policy': "default-src 'none'; connect-src 'self'; img-src 'self' data:; media-src 'self' data:; style-src 'unsafe-inline'; script-src 'unsafe-inline'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'"
30
+ });
31
+ response.end(page);
32
+ }
@@ -0,0 +1 @@
1
+ export declare const markup: string;
@@ -0,0 +1,68 @@
1
+ import { icons } from './icons.js';
2
+ export const markup = String.raw `
3
+ <div id="setup-screen" class="auth-screen hidden">
4
+ <form id="setup-form" class="auth-card">
5
+ <div class="brand-mark">N</div>
6
+ <h1>初始化 Agent Nexus</h1>
7
+ <p>创建仅用于后台管理的控制台密码。客户端 API 密钥将在登录后单独管理。</p>
8
+ <label>控制台密码<input name="password" type="password" minlength="12" autocomplete="new-password" required></label>
9
+ <label>确认密码<input name="confirmPassword" type="password" minlength="12" autocomplete="new-password" required></label>
10
+ <button class="button primary" type="submit">完成初始化</button>
11
+ <p class="form-error" data-form-error></p>
12
+ </form>
13
+ </div>
14
+
15
+ <div id="login-screen" class="auth-screen hidden">
16
+ <form id="login-form" class="auth-card">
17
+ <div class="brand-mark">N</div>
18
+ <h1>Agent Nexus</h1>
19
+ <p>登录后管理智能体、工作区和客户端 API 密钥。</p>
20
+ <label>控制台密码<input name="password" type="password" autocomplete="current-password" required autofocus></label>
21
+ <button class="button primary" type="submit">登录</button>
22
+ <p class="form-error" data-form-error></p>
23
+ </form>
24
+ </div>
25
+
26
+ <div id="app" class="app-shell hidden">
27
+ <aside class="sidebar">
28
+ <div class="brand"><span class="brand-mark small">N</span><span>Agent Nexus</span></div>
29
+ <nav aria-label="主导航">
30
+ <button class="nav-item active" data-page="overview">${icons.overview}<span>总览</span></button>
31
+ <button class="nav-item" data-page="runs">${icons.runs}<span>运行记录</span></button>
32
+ <button class="nav-item" data-page="agents">${icons.agents}<span>智能体</span></button>
33
+ <button class="nav-item" data-page="workspaces">${icons.workspaces}<span>工作区</span></button>
34
+ <button class="nav-item" data-page="files">${icons.files}<span>文件</span></button>
35
+ <button class="nav-item" data-page="keys">${icons.keys}<span>API 密钥</span></button>
36
+ <button class="nav-item" data-page="settings">${icons.settings}<span>运行设置</span></button>
37
+ </nav>
38
+ <div class="admin-menu-wrap">
39
+ <div id="admin-popover" class="admin-popover hidden">
40
+ <label>界面主题
41
+ <select id="theme-select">
42
+ <option value="system">跟随系统</option>
43
+ <option value="light">浅色</option>
44
+ <option value="dark">深色</option>
45
+ </select>
46
+ </label>
47
+ <button id="change-password" class="menu-button">修改密码</button>
48
+ <button id="logout" class="menu-button danger-text">退出登录</button>
49
+ </div>
50
+ <button id="admin-menu" class="admin-button" aria-expanded="false">${icons.settings}<span><strong>管理员</strong><small>控制台设置</small></span></button>
51
+ </div>
52
+ </aside>
53
+ <main class="main">
54
+ <header class="page-header">
55
+ <div><h1 id="page-title"></h1><p id="page-description"></p></div>
56
+ <div id="page-actions" class="page-actions"></div>
57
+ </header>
58
+ <section id="page-content" class="page-content" aria-live="polite"></section>
59
+ </main>
60
+ </div>
61
+
62
+ <div id="drawer-backdrop" class="drawer-backdrop hidden"></div>
63
+ <aside id="drawer" class="drawer hidden" aria-modal="true" role="dialog">
64
+ <div class="drawer-header"><h2 id="drawer-title"></h2><button id="drawer-close" class="icon-button" aria-label="关闭">×</button></div>
65
+ <form id="drawer-form" class="drawer-body"></form>
66
+ </aside>
67
+ <div id="toast-region" class="toast-region" aria-live="assertive"></div>
68
+ `;
@@ -0,0 +1 @@
1
+ export declare const styles: string;
@@ -0,0 +1,269 @@
1
+ export const styles = String.raw `
2
+ :root {
3
+ color-scheme: light;
4
+ --bg: #f7f7f8;
5
+ --surface: #ffffff;
6
+ --surface-subtle: #f1f1f3;
7
+ --surface-hover: #ebebee;
8
+ --text: #17171a;
9
+ --text-muted: #696970;
10
+ --text-faint: #929299;
11
+ --border: #ddddE2;
12
+ --border-strong: #c7c7cd;
13
+ --accent: #6d5dfc;
14
+ --accent-soft: #eeeafe;
15
+ --accent-text: #ffffff;
16
+ --success: #18864b;
17
+ --success-soft: #e8f5ed;
18
+ --danger: #c93636;
19
+ --danger-soft: #fceaea;
20
+ --focus: #6d5dfc;
21
+ --overlay: rgba(18, 18, 20, .42);
22
+ --radius: 8px;
23
+ --radius-small: 6px;
24
+ --sidebar-width: 232px;
25
+ }
26
+
27
+ html[data-theme="dark"] {
28
+ color-scheme: dark;
29
+ --bg: #111113;
30
+ --surface: #19191c;
31
+ --surface-subtle: #222226;
32
+ --surface-hover: #29292e;
33
+ --text: #f3f3f4;
34
+ --text-muted: #a3a3aa;
35
+ --text-faint: #777780;
36
+ --border: #303036;
37
+ --border-strong: #44444c;
38
+ --accent: #8b7cff;
39
+ --accent-soft: #292447;
40
+ --accent-text: #ffffff;
41
+ --success: #48bd79;
42
+ --success-soft: #173525;
43
+ --danger: #f06a6a;
44
+ --danger-soft: #402020;
45
+ --focus: #8b7cff;
46
+ --overlay: rgba(0, 0, 0, .62);
47
+ }
48
+
49
+ * { box-sizing: border-box; }
50
+ html, body { margin: 0; min-height: 100%; background: var(--bg); color: var(--text); font-family: "Noto Sans SC", "Microsoft YaHei UI", "PingFang SC", "Hiragino Sans GB", ui-sans-serif, sans-serif; }
51
+ body { font-size: 14px; line-height: 1.45; }
52
+ button, input, select, textarea { font: inherit; }
53
+ button { color: inherit; }
54
+ .hidden { display: none !important; }
55
+
56
+ .auth-screen { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
57
+ .auth-card { width: min(400px, 100%); padding: 28px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; }
58
+ .auth-card h1 { margin: 18px 0 6px; font-size: 22px; letter-spacing: -.02em; }
59
+ .auth-card p { color: var(--text-muted); margin: 0 0 22px; }
60
+ .brand-mark { width: 38px; height: 38px; border-radius: 9px; display: grid; place-items: center; background: var(--text); color: var(--surface); font-weight: 750; font-size: 18px; }
61
+ .brand-mark.small { width: 28px; height: 28px; border-radius: 7px; font-size: 14px; }
62
+
63
+ label { display: grid; gap: 7px; margin-bottom: 16px; color: var(--text-muted); font-size: 13px; font-weight: 600; }
64
+ input, select, textarea { width: 100%; min-height: 38px; padding: 8px 10px; color: var(--text); background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-small); outline: none; }
65
+ textarea { min-height: 90px; resize: vertical; }
66
+ input:focus, select:focus, textarea:focus, button:focus-visible { border-color: var(--focus); outline: 2px solid var(--accent-soft); outline-offset: 1px; }
67
+ .form-error { min-height: 20px; margin: 12px 0 0 !important; color: var(--danger) !important; }
68
+
69
+ .app-shell { min-height: 100vh; }
70
+ .sidebar { position: fixed; inset: 0 auto 0 0; width: var(--sidebar-width); padding: 16px 12px; background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; z-index: 10; }
71
+ .brand { display: flex; align-items: center; gap: 10px; height: 44px; padding: 0 8px; font-weight: 700; letter-spacing: -.01em; }
72
+ nav { display: grid; gap: 3px; margin-top: 18px; }
73
+ .nav-item, .admin-button, .menu-button { border: 0; background: transparent; cursor: pointer; }
74
+ .nav-item { width: 100%; min-height: 38px; display: flex; align-items: center; gap: 10px; padding: 0 10px; border-radius: var(--radius-small); color: var(--text-muted); text-align: left; }
75
+ .nav-item:hover { background: var(--surface-subtle); color: var(--text); }
76
+ .nav-item.active { background: var(--accent-soft); color: var(--accent); font-weight: 650; }
77
+ .nav-item svg, .admin-button svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: none; }
78
+ .admin-menu-wrap { position: relative; margin-top: auto; }
79
+ .admin-button { width: 100%; display: flex; gap: 10px; align-items: center; padding: 10px; border-radius: var(--radius); text-align: left; }
80
+ .admin-button:hover { background: var(--surface-subtle); }
81
+ .admin-button span { display: grid; min-width: 0; }
82
+ .admin-button strong { font-size: 13px; }
83
+ .admin-button small { color: var(--text-muted); }
84
+ .admin-popover { position: absolute; inset: auto 0 calc(100% + 8px); padding: 10px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
85
+ .admin-popover label { margin: 0 0 8px; }
86
+ .menu-button { width: 100%; padding: 9px 8px; border-radius: var(--radius-small); text-align: left; }
87
+ .menu-button:hover { background: var(--surface-subtle); }
88
+ .danger-text { color: var(--danger); }
89
+
90
+ .main { margin-left: var(--sidebar-width); min-height: 100vh; padding: 32px clamp(24px, 5vw, 68px) 64px; }
91
+ .page-header { min-height: 66px; display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; margin-bottom: 26px; }
92
+ .page-header h1 { margin: 0; font-size: 24px; letter-spacing: -.025em; }
93
+ .page-header p { margin: 5px 0 0; color: var(--text-muted); }
94
+ .page-actions { display: flex; gap: 8px; align-items: center; }
95
+ .page-content { max-width: 1180px; }
96
+ .button, .icon-button { min-height: 36px; border: 1px solid var(--border-strong); border-radius: var(--radius-small); background: var(--surface); cursor: pointer; font-weight: 600; }
97
+ .button { padding: 0 13px; }
98
+ .button:hover, .icon-button:hover { background: var(--surface-hover); }
99
+ .button.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
100
+ .button.primary:hover { background: var(--accent); }
101
+ .button.danger { border-color: var(--danger); color: var(--danger); }
102
+ .button.ghost { border-color: transparent; background: transparent; }
103
+ .button.small { min-height: 30px; padding: 0 9px; font-size: 12px; }
104
+ .icon-button { width: 36px; padding: 0; font-size: 22px; }
105
+
106
+ .stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 22px; }
107
+ .stat, .panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
108
+ .stat { padding: 16px; }
109
+ .stat span { display: block; color: var(--text-muted); font-size: 12px; font-weight: 650; }
110
+ .stat strong { display: block; margin-top: 7px; font-size: 25px; letter-spacing: -.03em; }
111
+ .panel-header { min-height: 52px; padding: 0 16px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--border); }
112
+ .panel-header h2 { margin: 0; font-size: 14px; }
113
+ .panel-body { padding: 16px; }
114
+ .empty { padding: 36px 18px; color: var(--text-muted); text-align: center; }
115
+
116
+ .toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
117
+ .toolbar input { max-width: 300px; }
118
+ .toolbar select { width: 150px; }
119
+ .table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
120
+ table { width: 100%; border-collapse: collapse; min-width: 760px; }
121
+ th, td { padding: 12px 14px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: middle; }
122
+ th { color: var(--text-muted); background: var(--surface-subtle); font-size: 11px; letter-spacing: .04em; text-transform: uppercase; }
123
+ tr:last-child td { border-bottom: 0; }
124
+ .agent-name { display: grid; gap: 2px; }
125
+ .agent-name strong { font-size: 13px; }
126
+ .agent-name small, .muted { color: var(--text-muted); }
127
+ .status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 650; }
128
+ .status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--text-faint); }
129
+ .status.ready { color: var(--success); }
130
+ .status.ready::before { background: var(--success); }
131
+ .status.checking { color: var(--text-muted); }
132
+ .status.checking::before { background: var(--text-faint); }
133
+ .status.failed { color: var(--danger); }
134
+ .status.failed::before { background: var(--danger); }
135
+ .status.running { color: #1d68b3; }
136
+ .status.running::before { background: #2b7ecb; box-shadow: 0 0 0 3px rgba(43, 126, 203, .13); }
137
+ .status.waiting { color: #9a6500; }
138
+ .status.waiting::before { background: #d49313; }
139
+ .status.completed { color: var(--success); }
140
+ .status.completed::before { background: var(--success); }
141
+ .status.canceled { color: var(--text-muted); }
142
+ .badge { display: inline-flex; padding: 3px 7px; border-radius: 999px; background: var(--surface-subtle); color: var(--text-muted); font-size: 11px; font-weight: 700; text-transform: uppercase; }
143
+ .row-actions { display: flex; justify-content: flex-end; gap: 5px; }
144
+ .error-detail { max-width: 280px; color: var(--danger); font-size: 12px; overflow-wrap: anywhere; }
145
+
146
+ .run-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; margin-bottom: 16px; overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius); background: var(--border); }
147
+ .run-summary-item { min-height: 74px; padding: 13px 15px; background: var(--surface); }
148
+ .run-summary-item span { display: block; color: var(--text-muted); font-size: 11px; font-weight: 700; letter-spacing: .04em; }
149
+ .run-summary-item strong { display: block; margin-top: 5px; font: 700 22px/1 ui-monospace, SFMono-Regular, Consolas, monospace; }
150
+ .run-section { margin-top: 18px; }
151
+ .run-section-title { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 9px; }
152
+ .run-section-title h2 { margin: 0; font-size: 14px; }
153
+ .run-section-title span { color: var(--text-muted); font-size: 12px; }
154
+ .run-live-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
155
+ .run-card { position: relative; min-height: 148px; padding: 14px 15px; border: 1px solid var(--border); border-left: 3px solid #2b7ecb; border-radius: var(--radius); background: var(--surface); cursor: pointer; }
156
+ .run-card:hover { border-color: var(--border-strong); border-left-color: var(--accent); }
157
+ .run-card.waiting { border-left-color: #d49313; }
158
+ .run-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
159
+ .run-card-agent { display: flex; align-items: center; gap: 8px; min-width: 0; }
160
+ .run-card-agent strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
161
+ .run-task { margin: 13px 0 15px; display: -webkit-box; overflow: hidden; color: var(--text); font-size: 13px; line-height: 1.55; -webkit-line-clamp: 2; -webkit-box-orient: vertical; white-space: pre-wrap; }
162
+ .run-progress { display: grid; gap: 3px; }
163
+ .run-progress strong { font-size: 12px; }
164
+ .run-progress small { color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
165
+ .run-meta { margin-top: 12px; display: flex; align-items: center; justify-content: space-between; color: var(--text-muted); font: 11px/1.4 ui-monospace, SFMono-Regular, Consolas, monospace; }
166
+ .run-table { min-width: 940px; }
167
+ .run-table tbody tr { cursor: pointer; }
168
+ .run-table tbody tr:hover td { background: var(--surface-hover); }
169
+ .run-table .run-task-cell { max-width: 360px; }
170
+ .run-table .run-task-cell strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
171
+ .run-table .run-task-cell small { display: block; margin-top: 3px; color: var(--text-muted); }
172
+ .run-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; margin-bottom: 18px; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
173
+ .run-detail-item { padding: 11px 12px; border-bottom: 1px solid var(--border); background: var(--surface-subtle); }
174
+ .run-detail-item:nth-child(odd) { border-right: 1px solid var(--border); }
175
+ .run-detail-item span { display: block; color: var(--text-muted); font-size: 11px; }
176
+ .run-detail-item strong { display: block; margin-top: 4px; font-size: 13px; overflow-wrap: anywhere; }
177
+ .run-detail-block { margin: 0 0 16px; }
178
+ .run-detail-block h3 { margin: 0 0 7px; color: var(--text-muted); font-size: 11px; letter-spacing: .05em; }
179
+ .run-detail-code { margin: 0; padding: 12px; max-height: 320px; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius-small); background: var(--surface-subtle); color: var(--text); font: 12px/1.6 ui-monospace, SFMono-Regular, Consolas, monospace; overflow-wrap: anywhere; white-space: pre-wrap; }
180
+ .run-id { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
181
+
182
+ .key-table { min-width: 900px; }
183
+ .key-table th:nth-child(1) { width: 21%; }
184
+ .key-table th:nth-child(2) { width: 12%; }
185
+ .key-table th:nth-child(3) { width: 18%; }
186
+ .key-table th:nth-child(4) { width: 17%; }
187
+ .key-table th:nth-child(5) { width: 19%; }
188
+ .key-table td { height: 66px; }
189
+ .key-actions-heading { text-align: right; }
190
+ .key-secret-cell { display: flex; align-items: center; gap: 7px; white-space: nowrap; }
191
+ .key-secret-cell code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; color: var(--text-muted); letter-spacing: .02em; }
192
+ .key-copy-button, .key-icon-button { display: inline-grid; place-items: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--radius-small); background: transparent; color: var(--text-muted); cursor: pointer; }
193
+ .key-copy-button { width: 28px; height: 28px; }
194
+ .key-copy-button:hover, .key-icon-button:hover, .key-icon-button[aria-expanded="true"] { color: var(--text); background: var(--surface-hover); }
195
+ .key-copy-button svg, .key-icon-button svg, .key-action-menu svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
196
+ .key-icon-button.danger-action { color: var(--danger); }
197
+ .key-icon-button.danger-action:hover { background: var(--danger-soft); }
198
+ .key-scope, .key-last-used { color: var(--text-muted); font-size: 13px; }
199
+ .key-actions-cell { text-align: right; }
200
+ .key-actions { display: inline-flex; align-items: center; justify-content: flex-end; gap: 3px; }
201
+ .key-action-menu { position: fixed; z-index: 45; width: 190px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
202
+ .key-action-menu button { width: 100%; min-height: 38px; display: flex; align-items: center; gap: 10px; padding: 0 10px; border: 0; border-radius: var(--radius-small); background: transparent; color: var(--text); cursor: pointer; text-align: left; font-size: 13px; }
203
+ .key-action-menu button:hover { background: var(--surface-hover); }
204
+ .key-action-menu button.danger-text { color: var(--danger); border-top: 1px solid var(--border); border-radius: 0 0 var(--radius-small) var(--radius-small); margin-top: 4px; padding-top: 4px; min-height: 42px; }
205
+ .key-action-menu button.danger-text:hover { background: var(--danger-soft); }
206
+
207
+ .workspace-list, .key-list, .overview-list { display: grid; gap: 8px; }
208
+ .list-row { min-height: 58px; padding: 10px 12px; display: flex; align-items: center; justify-content: space-between; gap: 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
209
+ .list-row-main { min-width: 0; display: grid; gap: 3px; }
210
+ .list-row-main strong, .path { overflow-wrap: anywhere; }
211
+ .list-row-main small { color: var(--text-muted); }
212
+ .tip { margin-top: 14px; padding: 12px; background: var(--surface-subtle); border-radius: var(--radius-small); color: var(--text-muted); font-size: 12px; }
213
+ .file-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
214
+ .file-toolbar select { min-width: 280px; max-width: 520px; }
215
+ .file-current-path { min-width: 0; overflow-wrap: anywhere; color: var(--text-muted); }
216
+ .file-table td:first-child { min-width: 260px; }
217
+ .file-name-button { border: 0; background: none; color: var(--text); padding: 0; cursor: pointer; font: inherit; display: inline-flex; gap: 8px; align-items: center; }
218
+ .file-name-button:hover span { text-decoration: underline; }
219
+ .file-name { display: inline-flex; gap: 8px; align-items: center; }
220
+ .published-files { display: grid; gap: 12px; }
221
+ .published-file { display: grid; gap: 6px; padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-small); }
222
+ .published-file a { overflow-wrap: anywhere; }
223
+
224
+ .drawer-backdrop { position: fixed; inset: 0; background: var(--overlay); z-index: 30; }
225
+ .drawer { position: fixed; inset: 0 0 0 auto; width: min(460px, 100vw); background: var(--surface); border-left: 1px solid var(--border); z-index: 31; display: flex; flex-direction: column; }
226
+ .drawer-header { min-height: 64px; padding: 0 18px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--border); }
227
+ .drawer-header h2 { margin: 0; font-size: 17px; }
228
+ .drawer-body { padding: 20px; overflow-y: auto; }
229
+ .drawer-footer { display: flex; justify-content: flex-end; gap: 8px; padding-top: 12px; }
230
+ .field-help { margin-top: -2px; color: var(--text-muted); font-size: 12px; font-weight: 400; line-height: 1.5; }
231
+ .field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
232
+ .settings-layout { display: grid; grid-template-columns: minmax(0, 760px); }
233
+ .settings-form { overflow: hidden; }
234
+ .settings-form .panel-header { align-items: flex-start; padding-top: 16px; padding-bottom: 14px; }
235
+ .settings-form .panel-header h2 { font-size: 16px; }
236
+ .settings-subtitle { margin: 5px 0 0; color: var(--text-muted); font-size: 12px; }
237
+ .settings-fields { display: grid; gap: 18px; padding: 20px 16px 4px; }
238
+ .settings-fields label { gap: 7px; }
239
+ .settings-fields input { max-width: 260px; }
240
+ .settings-divider { height: 1px; margin: 18px 16px 0; background: var(--border); }
241
+ .settings-note { display: grid; gap: 4px; margin: 16px; padding: 12px; border-radius: var(--radius-small); background: var(--surface-subtle); color: var(--text-muted); font-size: 12px; line-height: 1.5; }
242
+ .settings-note strong { color: var(--text); font-size: 13px; }
243
+ .settings-actions { display: flex; justify-content: flex-end; padding: 0 16px 16px; }
244
+ .checkbox { display: flex; align-items: center; gap: 8px; min-height: 34px; }
245
+ .checkbox input { width: 16px; min-height: 16px; }
246
+ .agent-scope { max-height: 170px; overflow: auto; padding: 10px; border: 1px solid var(--border); border-radius: var(--radius-small); }
247
+ .secret-box { margin: 12px 0; padding: 12px; background: var(--surface-subtle); border: 1px solid var(--border); border-radius: var(--radius); }
248
+ .secret-value { display: block; margin: 8px 0 10px; overflow-wrap: anywhere; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
249
+
250
+ .toast-region { position: fixed; right: 18px; bottom: 18px; z-index: 50; display: grid; gap: 8px; }
251
+ .toast { max-width: 380px; padding: 11px 13px; background: var(--text); color: var(--surface); border-radius: var(--radius-small); }
252
+ .toast.error { background: var(--danger); color: var(--accent-text); }
253
+
254
+ @media (max-width: 760px) {
255
+ :root { --sidebar-width: 72px; }
256
+ .brand span:last-child, .nav-item span, .admin-button span { display: none; }
257
+ .brand { justify-content: center; }
258
+ .nav-item, .admin-button { justify-content: center; }
259
+ .main { padding: 24px 16px 48px; }
260
+ .stats { grid-template-columns: 1fr; }
261
+ .run-summary, .run-live-grid { grid-template-columns: 1fr; }
262
+ .page-header { align-items: flex-start; }
263
+ .toolbar { align-items: stretch; flex-direction: column; }
264
+ .toolbar input, .toolbar select { max-width: none; width: 100%; }
265
+ .file-toolbar { align-items: stretch; flex-direction: column; }
266
+ .file-toolbar select { min-width: 0; max-width: none; width: 100%; }
267
+ .admin-popover { left: 0; right: auto; width: 190px; }
268
+ }
269
+ `;
@@ -0,0 +1,48 @@
1
+ import { type FileHandle } from 'node:fs/promises';
2
+ import { type Readable } from 'node:stream';
3
+ import type { AgentdFileEntryView, AgentdFileRootView } from './types.js';
4
+ export declare class WorkspaceFileError extends Error {
5
+ readonly status: number;
6
+ constructor(status: number, message: string);
7
+ }
8
+ export declare class WorkspaceFiles {
9
+ private readonly configuredRoots;
10
+ private readonly maxWriteBytes;
11
+ constructor(configuredRoots: () => string[], maxWriteBytes: number);
12
+ roots(): Promise<AgentdFileRootView[]>;
13
+ adminRoot(id: string): Promise<string>;
14
+ sessionRoot(workspace?: string): Promise<string>;
15
+ list(root: string, relative?: string): Promise<{
16
+ path: string;
17
+ entries: AgentdFileEntryView[];
18
+ }>;
19
+ open(root: string, relative: string): Promise<{
20
+ handle: FileHandle;
21
+ filePath: string;
22
+ name: string;
23
+ size: number;
24
+ }>;
25
+ publishable(root: string, input: string): Promise<{
26
+ handle: FileHandle;
27
+ filePath: string;
28
+ name: string;
29
+ size: number;
30
+ }>;
31
+ write(root: string, relative: string, input: Readable, declaredSize?: number): Promise<{
32
+ path: string;
33
+ size: number;
34
+ }>;
35
+ createDirectory(root: string, relative: string): Promise<{
36
+ path: string;
37
+ }>;
38
+ move(root: string, source: string, destination: string): Promise<{
39
+ path: string;
40
+ destination: string;
41
+ }>;
42
+ remove(root: string, relative: string): Promise<{
43
+ path: string;
44
+ }>;
45
+ private existing;
46
+ private writable;
47
+ }
48
+ export declare function workspaceRootId(root: string): string;