@chengchenccc/oh-my-agent 0.2.0-rc.1 → 0.2.0-rc.3
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.md +11 -6
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -2
- package/dist/cli/gateway-commands.d.ts +14 -2
- package/dist/cli/gateway-commands.d.ts.map +1 -1
- package/dist/cli/gateway-commands.js +133 -8
- package/dist/cli/initial-input.d.ts +3 -2
- package/dist/cli/initial-input.d.ts.map +1 -1
- package/dist/core/coordination/registry.d.ts +11 -0
- package/dist/core/coordination/registry.d.ts.map +1 -1
- package/dist/core/coordination/registry.js +23 -0
- package/dist/core/gateway/artifact.d.ts +3 -2
- package/dist/core/gateway/artifact.d.ts.map +1 -1
- package/dist/core/gateway/artifact.js +62 -10
- package/dist/core/gateway/daemon.d.ts.map +1 -1
- package/dist/core/gateway/daemon.js +3 -1
- package/dist/core/gateway/doctor.d.ts +21 -0
- package/dist/core/gateway/doctor.d.ts.map +1 -0
- package/dist/core/gateway/doctor.js +405 -0
- package/dist/core/goals/index.d.ts +8 -0
- package/dist/core/goals/index.d.ts.map +1 -0
- package/dist/core/goals/index.js +7 -0
- package/dist/core/goals/runtime.d.ts +112 -0
- package/dist/core/goals/runtime.d.ts.map +1 -0
- package/dist/core/goals/runtime.js +241 -0
- package/dist/core/goals/state.d.ts +82 -0
- package/dist/core/goals/state.d.ts.map +1 -0
- package/dist/core/goals/state.js +225 -0
- package/dist/core/goals/tool.d.ts +10 -0
- package/dist/core/goals/tool.d.ts.map +1 -0
- package/dist/core/goals/tool.js +87 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -3
- package/dist/core/loops/condition.d.ts +57 -0
- package/dist/core/loops/condition.d.ts.map +1 -0
- package/dist/core/loops/condition.js +153 -0
- package/dist/core/loops/index.d.ts +9 -0
- package/dist/core/loops/index.d.ts.map +1 -0
- package/dist/core/loops/index.js +8 -0
- package/dist/core/loops/limits.d.ts +43 -0
- package/dist/core/loops/limits.d.ts.map +1 -0
- package/dist/core/loops/limits.js +219 -0
- package/dist/core/loops/ralph.d.ts +37 -0
- package/dist/core/loops/ralph.d.ts.map +1 -0
- package/dist/core/loops/ralph.js +72 -0
- package/dist/core/loops/runtime.d.ts +95 -0
- package/dist/core/loops/runtime.d.ts.map +1 -0
- package/dist/core/loops/runtime.js +222 -0
- package/dist/core/memory/managed-skills.d.ts +0 -11
- package/dist/core/memory/managed-skills.d.ts.map +1 -1
- package/dist/core/memory/managed-skills.js +2 -2
- package/dist/core/memory/vector-store.d.ts +0 -1
- package/dist/core/memory/vector-store.d.ts.map +1 -1
- package/dist/core/memory/vector-store.js +0 -3
- package/dist/core/plans/index.d.ts +7 -0
- package/dist/core/plans/index.d.ts.map +1 -0
- package/dist/core/plans/index.js +6 -0
- package/dist/core/plans/prompts.d.ts +20 -0
- package/dist/core/plans/prompts.d.ts.map +1 -0
- package/dist/core/plans/prompts.js +72 -0
- package/dist/core/plans/runtime.d.ts +89 -0
- package/dist/core/plans/runtime.d.ts.map +1 -0
- package/dist/core/plans/runtime.js +170 -0
- package/dist/core/plans/state.d.ts +55 -0
- package/dist/core/plans/state.d.ts.map +1 -0
- package/dist/core/plans/state.js +88 -0
- package/dist/core/runtime/agent-loop-messages.d.ts +6 -0
- package/dist/core/runtime/agent-loop-messages.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-messages.js +40 -15
- package/dist/core/runtime/agent-loop-run.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-run.js +18 -27
- package/dist/core/runtime/agent-loop-runner.d.ts +1 -23
- package/dist/core/runtime/agent-loop-runner.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-runner.js +84 -30
- package/dist/core/runtime/agent-loop-types.d.ts +4 -0
- package/dist/core/runtime/agent-loop-types.d.ts.map +1 -1
- package/dist/core/runtime/compaction.d.ts +9 -0
- package/dist/core/runtime/compaction.d.ts.map +1 -1
- package/dist/core/runtime/compaction.js +28 -0
- package/dist/core/runtime/context-estimate.d.ts +37 -4
- package/dist/core/runtime/context-estimate.d.ts.map +1 -1
- package/dist/core/runtime/context-estimate.js +70 -0
- package/dist/core/runtime/create-runtime.d.ts +27 -5
- package/dist/core/runtime/create-runtime.d.ts.map +1 -1
- package/dist/core/runtime/create-runtime.js +22 -2
- package/dist/core/runtime/permission-classifier.d.ts.map +1 -1
- package/dist/core/runtime/permission-classifier.js +83 -12
- package/dist/core/runtime/plugin.d.ts +7 -0
- package/dist/core/runtime/plugin.d.ts.map +1 -1
- package/dist/core/runtime/plugin.js +13 -0
- package/dist/core/runtime/process-tree.d.ts +0 -9
- package/dist/core/runtime/process-tree.d.ts.map +1 -1
- package/dist/core/runtime/process-tree.js +1 -1
- package/dist/core/runtime/run-runtime.d.ts.map +1 -1
- package/dist/core/runtime/run-runtime.js +88 -49
- package/dist/core/runtime/runtime-catalog.d.ts +0 -4
- package/dist/core/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/core/runtime/runtime-catalog.js +1 -1
- package/dist/core/runtime/stream-rules.d.ts +9 -2
- package/dist/core/runtime/stream-rules.d.ts.map +1 -1
- package/dist/core/runtime/stream-rules.js +19 -5
- package/dist/core/runtime/title.d.ts +13 -1
- package/dist/core/runtime/title.d.ts.map +1 -1
- package/dist/core/runtime/title.js +51 -17
- package/dist/core/runtime/tool-pruning.d.ts.map +1 -1
- package/dist/core/runtime/tool-pruning.js +8 -16
- package/dist/core/session/session-file.d.ts +91 -5
- package/dist/core/session/session-file.d.ts.map +1 -1
- package/dist/core/session/session-file.js +206 -24
- package/dist/core/session/session-loop.d.ts +3 -1
- package/dist/core/session/session-loop.d.ts.map +1 -1
- package/dist/core/session/session-loop.js +8 -4
- package/dist/core/settings/project-settings.d.ts +63 -9
- package/dist/core/settings/project-settings.d.ts.map +1 -1
- package/dist/core/settings/project-settings.js +43 -2
- package/dist/core/settings/workspace-context.js +4 -4
- package/dist/core/store/in-memory-session-store.d.ts.map +1 -1
- package/dist/core/store/in-memory-session-store.js +3 -2
- package/dist/core/store/session-store.contract.test-helper.d.ts +7 -5
- package/dist/core/store/session-store.contract.test-helper.d.ts.map +1 -1
- package/dist/core/store/session-store.contract.test-helper.js +7 -5
- package/dist/core/store/session-store.d.ts +4 -2
- package/dist/core/store/session-store.d.ts.map +1 -1
- package/dist/core/store/session-store.js +4 -2
- package/dist/core/tools/ask-question.d.ts.map +1 -1
- package/dist/core/tools/ask-question.js +51 -5
- package/dist/core/tools/bash-pty.d.ts +13 -4
- package/dist/core/tools/bash-pty.d.ts.map +1 -1
- package/dist/core/tools/bash-pty.js +15 -8
- package/dist/core/tools/bash-sandbox.d.ts +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +18 -8
- package/dist/core/tools/browser.d.ts +54 -0
- package/dist/core/tools/browser.d.ts.map +1 -1
- package/dist/core/tools/browser.js +138 -1
- package/dist/core/tools/file-fingerprint.d.ts +24 -0
- package/dist/core/tools/file-fingerprint.d.ts.map +1 -0
- package/dist/core/tools/file-fingerprint.js +61 -0
- package/dist/core/tools/file-tools.d.ts +26 -0
- package/dist/core/tools/file-tools.d.ts.map +1 -1
- package/dist/core/tools/file-tools.js +113 -17
- package/dist/core/tools/index.d.ts +2 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/mcp-mount.d.ts +1 -1
- package/dist/core/tools/mcp-mount.d.ts.map +1 -1
- package/dist/core/tools/mcp-mount.js +72 -26
- package/dist/core/tools/url-guard.d.ts +15 -0
- package/dist/core/tools/url-guard.d.ts.map +1 -1
- package/dist/core/tools/url-guard.js +54 -14
- package/dist/core/tools/web-ports-std.d.ts.map +1 -1
- package/dist/core/tools/web-ports-std.js +5 -2
- package/dist/modes/print-mode.d.ts +3 -2
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -2
- package/dist/modes/shared.d.ts +12 -0
- package/dist/modes/shared.d.ts.map +1 -1
- package/dist/modes/shared.js +23 -6
- package/dist/modes/tui/agent-status.d.ts +20 -0
- package/dist/modes/tui/agent-status.d.ts.map +1 -0
- package/dist/modes/tui/agent-status.js +35 -0
- package/dist/modes/tui/ask-panel.d.ts +166 -0
- package/dist/modes/tui/ask-panel.d.ts.map +1 -0
- package/dist/modes/tui/ask-panel.js +888 -0
- package/dist/modes/tui/pty-console.d.ts +3 -0
- package/dist/modes/tui/pty-console.d.ts.map +1 -1
- package/dist/modes/tui/pty-console.js +19 -0
- package/dist/modes/tui/settings-overlay.d.ts +19 -0
- package/dist/modes/tui/settings-overlay.d.ts.map +1 -1
- package/dist/modes/tui/settings-overlay.js +46 -10
- package/dist/modes/tui/tui-commands-session.d.ts.map +1 -1
- package/dist/modes/tui/tui-commands-session.js +20 -6
- package/dist/modes/tui/tui-commands.d.ts +44 -3
- package/dist/modes/tui/tui-commands.d.ts.map +1 -1
- package/dist/modes/tui/tui-commands.js +380 -6
- package/dist/modes/tui/tui-components.d.ts +10 -0
- package/dist/modes/tui/tui-components.d.ts.map +1 -1
- package/dist/modes/tui/tui-components.js +26 -0
- package/dist/modes/tui/tui-format.d.ts +53 -4
- package/dist/modes/tui/tui-format.d.ts.map +1 -1
- package/dist/modes/tui/tui-format.js +137 -42
- package/dist/modes/tui/tui-frame-provider.d.ts +11 -3
- package/dist/modes/tui/tui-frame-provider.d.ts.map +1 -1
- package/dist/modes/tui/tui-frame-provider.js +14 -5
- package/dist/modes/tui/tui-interactive.d.ts.map +1 -1
- package/dist/modes/tui/tui-interactive.js +14 -0
- package/dist/modes/tui/tui-io.d.ts.map +1 -1
- package/dist/modes/tui/tui-io.js +150 -55
- package/dist/modes/tui/tui-mode.d.ts.map +1 -1
- package/dist/modes/tui/tui-mode.js +428 -23
- package/dist/modes/tui/tui-overlays.d.ts +6 -10
- package/dist/modes/tui/tui-overlays.d.ts.map +1 -1
- package/dist/modes/tui/tui-overlays.js +0 -43
- package/dist/modes/tui/tui-render.d.ts +10 -0
- package/dist/modes/tui/tui-render.d.ts.map +1 -1
- package/dist/modes/tui/tui-render.js +73 -38
- package/dist/modes/tui/tui-seam.d.ts +28 -6
- package/dist/modes/tui/tui-seam.d.ts.map +1 -1
- package/dist/modes/tui/tui-slash.d.ts.map +1 -1
- package/dist/modes/tui/tui-slash.js +3 -1
- package/dist/modes/tui/tui-tool-render.d.ts +14 -1
- package/dist/modes/tui/tui-tool-render.d.ts.map +1 -1
- package/dist/modes/tui/tui-tool-render.js +158 -50
- package/dist/modes/tui/tui-transcript-reconciler.d.ts +4 -0
- package/dist/modes/tui/tui-transcript-reconciler.d.ts.map +1 -1
- package/dist/modes/tui/tui-transcript-reconciler.js +12 -2
- package/dist/modes/tui/view-state.d.ts.map +1 -1
- package/dist/modes/tui/view-state.js +4 -1
- package/dist/protocol/transport.d.ts +24 -14
- package/dist/protocol/transport.d.ts.map +1 -1
- package/dist/protocol/transport.js +1 -0
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Product Backend → Agent Run → Adapter → spawn oma --mode rpc
|
|
|
7
7
|
→ per-Run Runtime → BackendRunOutcome → stdout → child 退出
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
**不是 daemon
|
|
10
|
+
**不是 daemon**(就 Run 执行模型而言):一个 Run = 一个子进程 = 一个 Runtime = 一个 outcome。没有常驻的 session supervisor,没有 worker pool。同一个包里的 `core/gateway/` 是另一回事——`oma gateway up -d` 确实会起一个 detached daemon,那是它自己的 pidfile 加 supervisor,与 Run 无关。
|
|
11
11
|
|
|
12
12
|
## TUI
|
|
13
13
|
|
|
@@ -38,13 +38,13 @@ Product Backend → Agent Run → Adapter → spawn oma --mode rpc
|
|
|
38
38
|
|
|
39
39
|
## Runtime
|
|
40
40
|
|
|
41
|
-
`createOmaRuntime()`(`src/core/create-runtime.ts`)构造一个 Runtime = 一个 Run:
|
|
41
|
+
`createOmaRuntime()`(`src/core/runtime/create-runtime.ts`)构造一个 Runtime = 一个 Run:
|
|
42
42
|
|
|
43
43
|
- `run(input)` 返回唯一 segment,其 `outcome` 是 Run 的唯一终态;
|
|
44
44
|
- `steer(input)` 注入 live loop;`stop()` 中止;
|
|
45
45
|
- `close()` 拆除 MCP clients 与 SessionStore。
|
|
46
46
|
|
|
47
|
-
Runtime 主体就在本 app 的 `src/core/`:OmaSession(model/tool loop、retry、compaction、插件、todo、tool-result pruning)与 in-memory SessionStore(`core/store/`)。
|
|
47
|
+
Runtime 主体就在本 app 的 `src/core/`:OmaSession(model/tool loop、retry、compaction、插件、todo、tool-result pruning)与 in-memory SessionStore(`core/store/`)。in-memory store 随 Run 销毁,但子进程会把本轮写进自己的 session 文件(`core/session/` 的 JSONL),并通过 outcome 回传 `cliSessionRef`;下一个 Run 用它 resume 那个文件,扁平的历史桥只在没有引用时才用。
|
|
48
48
|
|
|
49
49
|
## 运行
|
|
50
50
|
|
|
@@ -53,7 +53,7 @@ bun run --cwd apps/oh-my-agent dev -- -p "hello" # source CLI (dev)
|
|
|
53
53
|
bun apps/oh-my-agent/src/cli.ts -p "hello" # source CLI, direct
|
|
54
54
|
|
|
55
55
|
bun run --cwd apps/oh-my-agent build # → dist/cli.js (executable)
|
|
56
|
-
|
|
56
|
+
./dist/cli.js -p "hello" # built binary (inside apps/oh-my-agent)
|
|
57
57
|
|
|
58
58
|
cd apps/oh-my-agent && bun link # optional local install
|
|
59
59
|
oma -p "hello" # then run from anywhere
|
|
@@ -69,9 +69,14 @@ oma # standalone interactive TUI
|
|
|
69
69
|
src/
|
|
70
70
|
cli.ts 可执行入口(shebang + runCli())
|
|
71
71
|
main.ts main()/runCli():参数解析、模式分发、退出码(无 process.exit)
|
|
72
|
-
cli/
|
|
72
|
+
cli/ 参数解析、初始输入构建、gateway 子命令
|
|
73
73
|
modes/ tui / print / json / rpc 模式
|
|
74
|
-
core/
|
|
74
|
+
core/
|
|
75
|
+
runtime/ create-runtime.ts(per-Run Runtime 装配)
|
|
76
|
+
session/ session 文件(JSONL):/resume、--continue 与跨 Run resume 的持久侧
|
|
77
|
+
settings/ project-settings.ts(.oma/settings.json)
|
|
78
|
+
loops/ goals/ plans/ 三种持续模式的状态机与工具面
|
|
79
|
+
gateway/ `oma gateway` 的 daemon、supervisor、doctor、artifact
|
|
75
80
|
```
|
|
76
81
|
|
|
77
82
|
## 相关文档
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type GatewayCommand } from "./gateway-commands.js";
|
|
2
2
|
export type CliMode = "print" | "json" | "rpc" | "tui";
|
|
3
|
-
export type PermissionFlag = "ask" | "auto" | "deny" | "off";
|
|
3
|
+
export type PermissionFlag = "ask" | "auto" | "deny" | "off" | "yolo";
|
|
4
4
|
export interface CliArgs {
|
|
5
5
|
mode: CliMode;
|
|
6
6
|
/** True when -p/--mode was given explicitly; a bare positional prompt
|
package/dist/cli/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAoB,MAAM,uBAAuB,CAAC;AAE7F,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAoB,MAAM,uBAAuB,CAAC;AAE7F,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEtE,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,OAAO,CAAC;IACd;yEACqE;IACrE,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yCAAyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;mCAG+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAkCxC;6DAC6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CA6G1D"}
|
package/dist/cli/args.js
CHANGED
|
@@ -19,7 +19,7 @@ Usage:
|
|
|
19
19
|
oma --continue resume the most recent session
|
|
20
20
|
oma --session <id> -p "..." one-shot follow-up on a session
|
|
21
21
|
oma --permission ask -p "..." gate bash/write/mcp behind approval cards
|
|
22
|
-
oma --
|
|
22
|
+
oma --yolo -p "..." ungated run (OS sandbox forced when present)
|
|
23
23
|
oma --model <provider/model> -p "<prompt>"
|
|
24
24
|
pick a model by canonical id
|
|
25
25
|
(default: first available model)
|
|
@@ -30,7 +30,7 @@ Piped stdin (print/json modes):
|
|
|
30
30
|
cat error.log | oma -p (stdin only)
|
|
31
31
|
`;
|
|
32
32
|
const MODES = ["print", "json", "rpc", "tui"];
|
|
33
|
-
const PERMISSIONS = ["ask", "auto", "deny", "off"];
|
|
33
|
+
const PERMISSIONS = ["ask", "auto", "deny", "off", "yolo"];
|
|
34
34
|
/** Parse argv SYNTAX only: whether a run actually has an input (prompt or
|
|
35
35
|
* piped stdin) is decided in main() after stdin is read. */
|
|
36
36
|
export function parseArgs(argv) {
|
|
@@ -111,6 +111,13 @@ export function parseArgs(argv) {
|
|
|
111
111
|
else if (arg === "--read-only") {
|
|
112
112
|
args.readOnly = true;
|
|
113
113
|
}
|
|
114
|
+
else if (arg === "--yolo") {
|
|
115
|
+
// The industry's skip-permissions flag (crush --yolo / CC
|
|
116
|
+
// --dangerously-skip-permissions): ungated run. Boundaries that do
|
|
117
|
+
// not depend on judgment (workspace containment, protected files,
|
|
118
|
+
// secret stripping, egress rules, write freshness) all remain.
|
|
119
|
+
args.permission = "yolo";
|
|
120
|
+
}
|
|
114
121
|
else if (arg === "--permission") {
|
|
115
122
|
const value = argv[i + 1];
|
|
116
123
|
if (!value || !PERMISSIONS.includes(value)) {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/** The gateway domain's verbs. `oma gateway <verb>` is the CLI surface for
|
|
2
2
|
* backend + web: fetch their artifact, run it, report on it. */
|
|
3
|
-
export declare const GATEWAY_VERBS: readonly ["up", "down", "fetch", "status"];
|
|
3
|
+
export declare const GATEWAY_VERBS: readonly ["up", "down", "fetch", "status", "doctor", "passwd"];
|
|
4
4
|
export type GatewayCommand = (typeof GATEWAY_VERBS)[number];
|
|
5
|
-
export declare const GATEWAY_USAGE = "oma gateway <command>\n\n up start backend + web; stays in the foreground (Ctrl-C stops it)\n up -d same, but detached: returns once it is healthy\n down stop a detached gateway\n fetch download, verify and unpack the gateway artifact for a version\n status which version is installed, is it running, is it answering\n\n --version <v> artifact version for up/fetch (default: oma's own)\n\nNote: 'gateway' is a reserved first word. To send it as a prompt, use\n'oma -p \"gateway ...\"'.";
|
|
5
|
+
export declare const GATEWAY_USAGE = "oma gateway <command>\n\n up start backend + web; stays in the foreground (Ctrl-C stops it)\n up -d same, but detached: returns once it is healthy\n down stop a detached gateway\n fetch download, verify and unpack the gateway artifact for a version\n status which version is installed, is it running, is it answering\n doctor why nothing can run: artifact, tools, catalog, keys, ports, oma bin\n passwd replace the login password with a fresh random one\n\n --version <v> artifact version for up/fetch (default: oma's own)\n\nNote: 'gateway' is a reserved first word. To send it as a prompt, use\n'oma -p \"gateway ...\"'.";
|
|
6
6
|
export declare function isGatewayCommand(value: string): value is GatewayCommand;
|
|
7
|
+
/** What to tell the operator about logging in. The password is only echoed on
|
|
8
|
+
* an interactive terminal: a detached run's stdout is redirected into a log
|
|
9
|
+
* file, and a secret must not be filed away there. The pointer is always
|
|
10
|
+
* printed, so a missed banner is never a dead end. */
|
|
11
|
+
export declare function credentialLines(home: string, secrets: Record<string, string>, interactive: boolean): string[];
|
|
7
12
|
export interface GatewayCommandOptions {
|
|
8
13
|
home?: string;
|
|
9
14
|
version?: string;
|
|
@@ -21,6 +26,13 @@ export declare function runGatewayStatus(opts?: GatewayCommandOptions): Promise<
|
|
|
21
26
|
/** `oma gateway up`: fetch the version if it is missing, then run the whole gateway in
|
|
22
27
|
* the foreground (Ctrl-C stops it, children are signalled in reverse order). */
|
|
23
28
|
export declare function runGatewayUp(opts?: GatewayCommandOptions): Promise<number>;
|
|
29
|
+
/** `oma gateway passwd`: rotate the login password. Generated, never read from
|
|
30
|
+
* argv — an argument would land in shell history and process listings.
|
|
31
|
+
* Takes effect on the next start, because the web component reads it from its
|
|
32
|
+
* environment. */
|
|
33
|
+
export declare function runGatewayPasswd(opts?: GatewayCommandOptions): Promise<number>;
|
|
34
|
+
/** `oma gateway doctor`: check the deployment and say how to fix what is wrong. */
|
|
35
|
+
export declare function runGatewayDoctor(opts?: GatewayCommandOptions): Promise<number>;
|
|
24
36
|
/** `oma gateway down`: stop whatever `up -d` started. */
|
|
25
37
|
export declare function runGatewayDown(opts?: GatewayCommandOptions): Promise<number>;
|
|
26
38
|
/** `oma gateway <verb>`: single entry point so the CLI owns one code path. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-commands.d.ts","sourceRoot":"","sources":["../../src/cli/gateway-commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gateway-commands.d.ts","sourceRoot":"","sources":["../../src/cli/gateway-commands.ts"],"names":[],"mappings":"AAwBA;iEACiE;AACjE,eAAO,MAAM,aAAa,gEAAiE,CAAC;AAC5F,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,eAAO,MAAM,aAAa,0pBAaF,CAAC;AAEzB,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,cAAc,CAKvE;AAED;;;uDAGuD;AACvD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,WAAW,EAAE,OAAO,GACnB,MAAM,EAAE,CAMV;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAyBD;qEACqE;AACrE,wBAAsB,eAAe,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBvF;AAED;qEACqE;AACrE,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAgExF;AAED;iFACiF;AACjF,wBAAsB,YAAY,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCpF;AAED;;;mBAGmB;AACnB,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BxF;AAmDD,mFAAmF;AACnF,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBxF;AAED,yDAAyD;AACzD,wBAAsB,cAAc,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAUtF;AA+DD,8EAA8E;AAC9E,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,MAAM,CAAC,CAOjB"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
2
3
|
import { join, resolve } from "node:path";
|
|
3
|
-
import { currentVersion, fetchGatewayArtifact, gatewayPaths, installedVersions, omaHome, omaVersion, readSecrets, } from "../core/gateway/artifact.js";
|
|
4
|
+
import { currentVersion, fetchGatewayArtifact, gatewayPaths, generatePassword, installedVersions, omaHome, omaVersion, readSecrets, } from "../core/gateway/artifact.js";
|
|
4
5
|
import { isProcessAlive, readDaemon, startDetachedGateway, stopDetachedGateway, tailDaemonLog, } from "../core/gateway/daemon.js";
|
|
6
|
+
import { diagnoseGateway } from "../core/gateway/doctor.js";
|
|
5
7
|
import { readGatewayManifest } from "../core/gateway/manifest.js";
|
|
6
8
|
import { runGateway } from "../core/gateway/supervisor.js";
|
|
7
9
|
/** The gateway domain's verbs. `oma gateway <verb>` is the CLI surface for
|
|
8
10
|
* backend + web: fetch their artifact, run it, report on it. */
|
|
9
|
-
export const GATEWAY_VERBS = ["up", "down", "fetch", "status"];
|
|
11
|
+
export const GATEWAY_VERBS = ["up", "down", "fetch", "status", "doctor", "passwd"];
|
|
10
12
|
export const GATEWAY_USAGE = `oma gateway <command>
|
|
11
13
|
|
|
12
14
|
up start backend + web; stays in the foreground (Ctrl-C stops it)
|
|
@@ -14,6 +16,8 @@ export const GATEWAY_USAGE = `oma gateway <command>
|
|
|
14
16
|
down stop a detached gateway
|
|
15
17
|
fetch download, verify and unpack the gateway artifact for a version
|
|
16
18
|
status which version is installed, is it running, is it answering
|
|
19
|
+
doctor why nothing can run: artifact, tools, catalog, keys, ports, oma bin
|
|
20
|
+
passwd replace the login password with a fresh random one
|
|
17
21
|
|
|
18
22
|
--version <v> artifact version for up/fetch (default: oma's own)
|
|
19
23
|
|
|
@@ -26,6 +30,18 @@ export function isGatewayCommand(value) {
|
|
|
26
30
|
}
|
|
27
31
|
return false;
|
|
28
32
|
}
|
|
33
|
+
/** What to tell the operator about logging in. The password is only echoed on
|
|
34
|
+
* an interactive terminal: a detached run's stdout is redirected into a log
|
|
35
|
+
* file, and a secret must not be filed away there. The pointer is always
|
|
36
|
+
* printed, so a missed banner is never a dead end. */
|
|
37
|
+
export function credentialLines(home, secrets, interactive) {
|
|
38
|
+
const lines = [];
|
|
39
|
+
const password = secrets.MOCK_PASSWORD;
|
|
40
|
+
if (password && interactive)
|
|
41
|
+
lines.push(`login: user-001 / ${password}`);
|
|
42
|
+
lines.push(`login password: oma gateway status (or read ${gatewayPaths(home).secrets})`);
|
|
43
|
+
return lines;
|
|
44
|
+
}
|
|
29
45
|
/** A source checkout runs the gateway from the repo, so the release artifact it
|
|
30
46
|
* would download is unused. Only stay quiet there: an explicit CLI call still
|
|
31
47
|
* fetches. `npm_lifecycle_event` is set while a package's postinstall runs. */
|
|
@@ -81,6 +97,13 @@ export async function runGatewayStatus(opts = {}) {
|
|
|
81
97
|
log(`gateway home: ${paths.home}`);
|
|
82
98
|
log(`versions: ${versions.length > 0 ? versions.join(", ") : "(none installed)"}`);
|
|
83
99
|
log(`current: ${current ?? "(none)"}`);
|
|
100
|
+
// Which catalog the gateway's children will read: product runs deliberately
|
|
101
|
+
// ignore ~/.oma/models.yml (the agent's own bash tool can write there), so a
|
|
102
|
+
// custom provider only counts when OMA_HOME points at its models.yml.
|
|
103
|
+
const catalogHome = process.env.OMA_HOME;
|
|
104
|
+
log(catalogHome
|
|
105
|
+
? `catalog: ${catalogHome}/models.yml via OMA_HOME`
|
|
106
|
+
: "catalog: built-in providers only (OMA_HOME unset — a custom models.yml is not read)");
|
|
84
107
|
if (versions.length === 0) {
|
|
85
108
|
log(`install one: oma gateway fetch (oma ${omaVersion()})`);
|
|
86
109
|
return 1;
|
|
@@ -154,8 +177,9 @@ export async function runGatewayUp(opts = {}) {
|
|
|
154
177
|
log("");
|
|
155
178
|
log(`gateway ${handle.manifest.version} is up (${handle.manifest.components.length} processes)`);
|
|
156
179
|
log(`open http://127.0.0.1:${web?.port ?? 3001}/login`);
|
|
157
|
-
|
|
158
|
-
log(
|
|
180
|
+
for (const line of credentialLines(home, handle.secrets, process.stdout.isTTY === true)) {
|
|
181
|
+
log(line);
|
|
182
|
+
}
|
|
159
183
|
log("Ctrl-C stops the gateway");
|
|
160
184
|
},
|
|
161
185
|
});
|
|
@@ -165,6 +189,103 @@ export async function runGatewayUp(opts = {}) {
|
|
|
165
189
|
return 1;
|
|
166
190
|
}
|
|
167
191
|
}
|
|
192
|
+
/** `oma gateway passwd`: rotate the login password. Generated, never read from
|
|
193
|
+
* argv — an argument would land in shell history and process listings.
|
|
194
|
+
* Takes effect on the next start, because the web component reads it from its
|
|
195
|
+
* environment. */
|
|
196
|
+
export async function runGatewayPasswd(opts = {}) {
|
|
197
|
+
const home = opts.home ?? omaHome();
|
|
198
|
+
const log = loggerFor(opts, "stdout");
|
|
199
|
+
const secretsPath = gatewayPaths(home).secrets;
|
|
200
|
+
if (!existsSync(secretsPath)) {
|
|
201
|
+
log(`no secrets at ${secretsPath} — run 'oma gateway up' once first`);
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
const password = generatePassword();
|
|
205
|
+
const secrets = { ...readSecrets(home), MOCK_PASSWORD: password };
|
|
206
|
+
await writeFile(secretsPath, `${JSON.stringify(secrets, null, 2)}\n`, { mode: 0o600 });
|
|
207
|
+
log(`login password replaced: user-001 / ${password}`);
|
|
208
|
+
// A password set in the console is stored as a hash in the stack's settings
|
|
209
|
+
// and wins over this file, so the new one has to land there too — otherwise
|
|
210
|
+
// rotating here would silently not take effect.
|
|
211
|
+
const pushed = await pushPasswordToRunningGateway(secrets, password);
|
|
212
|
+
if (pushed) {
|
|
213
|
+
log("the running gateway took it too (console-set password updated)");
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
// Gateway not reachable: leave a reset request instead, so the next start
|
|
217
|
+
// adopts the secret above. Without this, "rotate while down" is a no-op — the
|
|
218
|
+
// stored hash outlives the rotation and the new password is refused.
|
|
219
|
+
const marker = requestPasswordReset(home);
|
|
220
|
+
if (marker) {
|
|
221
|
+
log(`the stored password will be dropped on the next start (${marker})`);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
log("restart to apply: oma gateway down && oma gateway up -d");
|
|
225
|
+
}
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
/** Marker filename. Owned by the backend
|
|
229
|
+
* (`apps/backend/src/features/auth/password.ts` PASSWORD_RESET_MARKER); oma
|
|
230
|
+
* cannot import a backend module, so the literal is repeated here on purpose —
|
|
231
|
+
* change both together. */
|
|
232
|
+
const PASSWORD_RESET_MARKER = "password-reset";
|
|
233
|
+
/** Ask the backend to drop its stored password by leaving the reset marker in
|
|
234
|
+
* the gateway data dir; the backend consumes it on the next boot and the
|
|
235
|
+
* secret file becomes authoritative again.
|
|
236
|
+
*
|
|
237
|
+
* Deliberately NOT a database write: the backend owns backend.db (see
|
|
238
|
+
* apps/oh-my-agent/AGENTS.md — "backend is the source of truth for product
|
|
239
|
+
* state; the runtime is the runtime"). Writing another process's SQLite file
|
|
240
|
+
* from the CLI would also race a running gateway. Returns the path written, or
|
|
241
|
+
* null when there is no gateway data dir to write to yet. */
|
|
242
|
+
function requestPasswordReset(home) {
|
|
243
|
+
const dataDir = join(gatewayPaths(home).data, "backend");
|
|
244
|
+
if (!existsSync(join(gatewayPaths(home).data)))
|
|
245
|
+
return null;
|
|
246
|
+
mkdirSync(dataDir, { recursive: true });
|
|
247
|
+
const marker = join(dataDir, PASSWORD_RESET_MARKER);
|
|
248
|
+
writeFileSync(marker, `${new Date().toISOString()}\n`);
|
|
249
|
+
return marker;
|
|
250
|
+
}
|
|
251
|
+
/** Update the console-set password (a hash in the backend's settings) when the
|
|
252
|
+
* gateway is reachable. Returns false when it is not: the file value then
|
|
253
|
+
* applies after the next start. */
|
|
254
|
+
async function pushPasswordToRunningGateway(secrets, password) {
|
|
255
|
+
const token = secrets.BACKEND_AUTH_TOKEN;
|
|
256
|
+
if (!token)
|
|
257
|
+
return false;
|
|
258
|
+
try {
|
|
259
|
+
const res = await fetch(`${process.env.BACKEND_URL ?? "http://127.0.0.1:3000"}/api/auth/password`, {
|
|
260
|
+
method: "PUT",
|
|
261
|
+
headers: { "content-type": "application/json", "x-auth-token": token },
|
|
262
|
+
body: JSON.stringify({ password }),
|
|
263
|
+
signal: AbortSignal.timeout(2000),
|
|
264
|
+
});
|
|
265
|
+
return res.ok;
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/** `oma gateway doctor`: check the deployment and say how to fix what is wrong. */
|
|
272
|
+
export async function runGatewayDoctor(opts = {}) {
|
|
273
|
+
const home = opts.home ?? omaHome();
|
|
274
|
+
const log = loggerFor(opts, "stdout");
|
|
275
|
+
const checks = await diagnoseGateway({ home });
|
|
276
|
+
log(`oma gateway doctor (home: ${home})`);
|
|
277
|
+
for (const check of checks) {
|
|
278
|
+
log(`${check.ok ? "✓" : "✗"} ${check.id.padEnd(9)} ${check.detail}`);
|
|
279
|
+
if (check.fix)
|
|
280
|
+
log(` ${check.ok ? "note" : "fix"}: ${check.fix}`);
|
|
281
|
+
}
|
|
282
|
+
const failed = checks.filter((check) => !check.ok);
|
|
283
|
+
log("");
|
|
284
|
+
log(failed.length === 0
|
|
285
|
+
? "no problems found"
|
|
286
|
+
: `${failed.length} problem(s): ${failed.map((c) => c.id).join(", ")}`);
|
|
287
|
+
return failed.length === 0 ? 0 : 1;
|
|
288
|
+
}
|
|
168
289
|
/** `oma gateway down`: stop whatever `up -d` started. */
|
|
169
290
|
export async function runGatewayDown(opts = {}) {
|
|
170
291
|
const home = opts.home ?? omaHome();
|
|
@@ -206,12 +327,12 @@ async function startGatewayDetached(opts) {
|
|
|
206
327
|
try {
|
|
207
328
|
const res = await fetch(healthUrl, { signal: AbortSignal.timeout(2000) });
|
|
208
329
|
if (res.ok) {
|
|
209
|
-
const password = readSecrets(opts.home).MOCK_PASSWORD;
|
|
210
330
|
opts.log("");
|
|
211
331
|
opts.log(`gateway ${opts.version} is up in the background (pid ${daemon.pid})`);
|
|
212
332
|
opts.log(`open http://127.0.0.1:${web?.port ?? 3001}/login`);
|
|
213
|
-
|
|
214
|
-
opts.log(
|
|
333
|
+
for (const line of credentialLines(opts.home, readSecrets(opts.home), process.stdout.isTTY === true)) {
|
|
334
|
+
opts.log(line);
|
|
335
|
+
}
|
|
215
336
|
opts.log("stop it with: oma gateway down");
|
|
216
337
|
return 0;
|
|
217
338
|
}
|
|
@@ -238,5 +359,9 @@ export async function runGatewayCommand(verb, opts = {}) {
|
|
|
238
359
|
return runGatewayDown(opts);
|
|
239
360
|
if (verb === "fetch")
|
|
240
361
|
return runGatewayFetch(opts);
|
|
362
|
+
if (verb === "doctor")
|
|
363
|
+
return runGatewayDoctor(opts);
|
|
364
|
+
if (verb === "passwd")
|
|
365
|
+
return runGatewayPasswd(opts);
|
|
241
366
|
return runGatewayStatus(opts);
|
|
242
367
|
}
|
|
@@ -28,8 +28,9 @@ export declare function buildCliRunInput(opts: {
|
|
|
28
28
|
modelRuntime: ModelRuntime;
|
|
29
29
|
/** Canonical `<provider>/<model>` id; undefined = first available. */
|
|
30
30
|
modelId?: string;
|
|
31
|
-
/** Standalone permission gate. undefined = ungated (legacy default)
|
|
32
|
-
|
|
31
|
+
/** Standalone permission gate. undefined = ungated (legacy default);
|
|
32
|
+
* "yolo" = ungated + OS bash sandbox forced when available. */
|
|
33
|
+
permissionMode?: "ask" | "auto" | "deny" | "yolo";
|
|
33
34
|
/** --read-only: advertise no write/edit/bash/eval to the model. */
|
|
34
35
|
readOnly?: boolean;
|
|
35
36
|
/** Clipboard/pasted images riding this prompt (vision input). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initial-input.d.ts","sourceRoot":"","sources":["../../src/cli/initial-input.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAUrD;;;;;wDAKwD;AACxD,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,CA+C3E;AAMD;;yCAEyC;AACzC,wBAAsB,cAAc,CAClC,MAAM,GAAE,cAAc,CAAC,UAAU,CAAsB,GACtD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAsB7B;AAED;;iDAEiD;AACjD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAKpF;AA0BD;;;wBAGwB;AAExB,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;IAC3B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB
|
|
1
|
+
{"version":3,"file":"initial-input.d.ts","sourceRoot":"","sources":["../../src/cli/initial-input.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAUrD;;;;;wDAKwD;AACxD,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,CA+C3E;AAMD;;yCAEyC;AACzC,wBAAsB,cAAc,CAClC,MAAM,GAAE,cAAc,CAAC,UAAU,CAAsB,GACtD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAsB7B;AAED;;iDAEiD;AACjD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAKpF;AA0BD;;;wBAGwB;AAExB,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;IAC3B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;oEACgE;IAChE,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClD,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iEAAiE;IACjE,MAAM,CAAC,EAAE,aAAa,CAAC;QACrB,SAAS,EAAE,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,CAAC;QACnE,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ,GAAG,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAyClC"}
|
|
@@ -73,6 +73,17 @@ export interface CoordinationRegistry {
|
|
|
73
73
|
isDeliveryAcknowledged(id: string): boolean;
|
|
74
74
|
settleEntry(id: string, patch: Partial<RegistryEntry>): void;
|
|
75
75
|
setCompletionListener(cb: ((entry: RegistryEntry) => void) | null): void;
|
|
76
|
+
/** Remove THIS callback only. A TUI session installs a listener and must
|
|
77
|
+
* remove it on close without clobbering whichever listener a NEWER
|
|
78
|
+
* session has since installed (single-slot setCompletionListener(null)
|
|
79
|
+
* would). */
|
|
80
|
+
removeCompletionListener(cb: (entry: RegistryEntry) => void): void;
|
|
81
|
+
/** Kill every RUNNING entry that owns a kill callback (bash/eval bg
|
|
82
|
+
* jobs). Run teardown: the table is about to be dropped, so the children
|
|
83
|
+
* must go with it — clearAll() alone frees the bookkeeping and orphans
|
|
84
|
+
* the processes. Subagents are excluded (the delegation executor owns
|
|
85
|
+
* those) and settled entries have nothing to kill. */
|
|
86
|
+
stopRunningEntries(scope?: string): number;
|
|
76
87
|
notifyEntryCompletion(entry: RegistryEntry): void;
|
|
77
88
|
/** Drop every entry (Run teardown). */
|
|
78
89
|
clearAll(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/core/coordination/registry.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/core/coordination/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;AACzE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAOD;;;;sDAIsD;AACtD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACjF,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAChD,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;IAC7D,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC;IACvC,WAAW,CAAC,IAAI,EAAE;QAChB,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD,gBAAgB,IAAI,MAAM,CAAC;IAC3B;;sEAEkE;IAClE,qBAAqB,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACpD,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5C,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;IAC7D,qBAAqB,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;IACzE;;;kBAGc;IACd,wBAAwB,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI,CAAC;IACnE;;;;2DAIuD;IACvD,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3C,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAClD,uCAAuC;IACvC,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,wBAAgB,0BAA0B,IAAI,oBAAoB,CA6LjE;AAED;;kEAEkE;AAClE,eAAO,MAAM,eAAe,EAAE,oBAAmD,CAAC"}
|
|
@@ -130,6 +130,27 @@ export function createCoordinationRegistry() {
|
|
|
130
130
|
function setCompletionListener(cb) {
|
|
131
131
|
completionListener = cb;
|
|
132
132
|
}
|
|
133
|
+
function removeCompletionListener(cb) {
|
|
134
|
+
if (completionListener === cb)
|
|
135
|
+
completionListener = null;
|
|
136
|
+
}
|
|
137
|
+
function stopRunningEntries(scope) {
|
|
138
|
+
let stopped = 0;
|
|
139
|
+
for (const e of entries.values()) {
|
|
140
|
+
if (e.status !== "running" || e.kind === "subagent" || !e.kill)
|
|
141
|
+
continue;
|
|
142
|
+
if (scope !== undefined && e.scope !== scope)
|
|
143
|
+
continue;
|
|
144
|
+
try {
|
|
145
|
+
e.kill();
|
|
146
|
+
stopped++;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
/* a broken kill never blocks the rest of teardown */
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return stopped;
|
|
153
|
+
}
|
|
133
154
|
function acknowledgeDeliveries(ids) {
|
|
134
155
|
for (const id of ids)
|
|
135
156
|
ackedDeliveries.add(id);
|
|
@@ -154,6 +175,8 @@ export function createCoordinationRegistry() {
|
|
|
154
175
|
isDeliveryAcknowledged,
|
|
155
176
|
settleEntry,
|
|
156
177
|
setCompletionListener,
|
|
178
|
+
removeCompletionListener,
|
|
179
|
+
stopRunningEntries,
|
|
157
180
|
notifyEntryCompletion,
|
|
158
181
|
clearAll,
|
|
159
182
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type GatewayManifest } from "./manifest.js";
|
|
2
2
|
export declare class GatewayArtifactError extends Error {
|
|
3
3
|
}
|
|
4
|
-
export declare const DEFAULT_STACK_REPO = "Chengchcc/my-agent-team";
|
|
5
4
|
export declare function omaHome(env?: NodeJS.ProcessEnv): string;
|
|
6
5
|
/** The launcher's on-disk layout, in one place: code lives under
|
|
7
6
|
* gateway/versions/<version>/, state under gateway-data/ so an upgrade that swaps
|
|
@@ -29,7 +28,7 @@ export declare function releaseLocation(version: string, env?: NodeJS.ProcessEnv
|
|
|
29
28
|
export declare function sha256File(path: string): Promise<string>;
|
|
30
29
|
/** Parse the SHA256SUMS entry for one file (GNU coreutils format). */
|
|
31
30
|
export declare function parseSums(sums: string, fileName: string): string;
|
|
32
|
-
|
|
31
|
+
export declare function generatePassword(): string;
|
|
33
32
|
export declare function readSecrets(home: string): Record<string, string>;
|
|
34
33
|
/** Read the launcher's secrets, generating any missing one. 0600: they
|
|
35
34
|
* authenticate the local single-user gateway. */
|
|
@@ -39,6 +38,8 @@ export interface FetchOptions {
|
|
|
39
38
|
version?: string;
|
|
40
39
|
env?: NodeJS.ProcessEnv;
|
|
41
40
|
log?: (line: string) => void;
|
|
41
|
+
/** No-byte allowance before a download is declared dead (test seam). */
|
|
42
|
+
stallMs?: number;
|
|
42
43
|
}
|
|
43
44
|
export interface FetchResult {
|
|
44
45
|
version: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../../../src/core/gateway/artifact.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAuB,MAAM,eAAe,CAAC;AAE1E,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;
|
|
1
|
+
{"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../../../src/core/gateway/artifact.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAuB,MAAM,eAAe,CAAC;AAE1E,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAQlD,wBAAgB,OAAO,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAEpE;AAED;;sDAEsD;AACtD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,YAAY,CAAC,IAAI,GAAE,MAAkB,GAAG,YAAY,CAUnE;AAED;qDACqD;AACrD,wBAAgB,UAAU,IAAI,MAAM,CAQnC;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;oFACoF;AACpF,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,eAAe,CAMjB;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI9D;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMhE;AAiFD,wBAAgB,gBAAgB,IAAI,MAAM,CAYzC;AASD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBhE;AAED;kDACkD;AAClD,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiBjC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;6EAC6E;AAC7E,wBAAsB,oBAAoB,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAuDxF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,EAAE,CAMpE;AAED,wBAAgB,cAAc,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,GAAG,SAAS,CAO3E"}
|
|
@@ -5,7 +5,7 @@ import { join, resolve } from "node:path";
|
|
|
5
5
|
import { readGatewayManifest } from "./manifest.js";
|
|
6
6
|
export class GatewayArtifactError extends Error {
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
const DEFAULT_STACK_REPO = "Chengchcc/my-agent-team";
|
|
9
9
|
/** Written last, after a version is fully unpacked: its presence is what makes
|
|
10
10
|
* an install idempotent. */
|
|
11
11
|
const INSTALL_MARKER = ".complete";
|
|
@@ -56,7 +56,24 @@ export function parseSums(sums, fileName) {
|
|
|
56
56
|
}
|
|
57
57
|
throw new GatewayArtifactError(`SHA256SUMS has no entry for ${fileName}`);
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
/** How long a download may go without delivering a byte before it is called
|
|
60
|
+
* dead. A slow-but-moving transfer is fine; a wedged one must not hang an
|
|
61
|
+
* install (a postinstall sat for 10 minutes before this existed). */
|
|
62
|
+
const DEFAULT_STALL_MS = 60_000;
|
|
63
|
+
async function withStallTimeout(work, ms, onStall) {
|
|
64
|
+
let timer;
|
|
65
|
+
const stall = new Promise((_, reject) => {
|
|
66
|
+
timer = setTimeout(() => reject(onStall()), ms);
|
|
67
|
+
});
|
|
68
|
+
try {
|
|
69
|
+
return await Promise.race([work, stall]);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
if (timer)
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function download(url, dest, stallMs) {
|
|
60
77
|
let res;
|
|
61
78
|
try {
|
|
62
79
|
res = await fetch(url, { redirect: "follow" });
|
|
@@ -73,11 +90,17 @@ async function download(url, dest) {
|
|
|
73
90
|
// also keeps a 60MB artifact out of memory.
|
|
74
91
|
const writer = Bun.file(dest).writer();
|
|
75
92
|
const reader = res.body.getReader();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
try {
|
|
94
|
+
for (;;) {
|
|
95
|
+
const { done, value } = await withStallTimeout(reader.read(), stallMs, () => new GatewayArtifactError(`download stalled for ${Math.round(stallMs / 1000)}s at ${url} — check the connection, then retry`));
|
|
96
|
+
if (done)
|
|
97
|
+
break;
|
|
98
|
+
writer.write(value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
await reader.cancel().catch(() => { });
|
|
103
|
+
throw err;
|
|
81
104
|
}
|
|
82
105
|
await writer.end();
|
|
83
106
|
}
|
|
@@ -99,6 +122,34 @@ async function unpack(tarball, dest) {
|
|
|
99
122
|
}
|
|
100
123
|
}
|
|
101
124
|
/** Read the launcher's secrets without generating anything (status paths). */
|
|
125
|
+
/** Human-typed login password. Entropy comes from length, not from requiring
|
|
126
|
+
* character classes: 22 chars over a 56-symbol alphabet is ~128 bits. The
|
|
127
|
+
* alphabet drops look-alikes (l, 1, I, O, 0) and shell-hostile punctuation,
|
|
128
|
+
* because the person typing it may be reading it off a terminal on another
|
|
129
|
+
* device. */
|
|
130
|
+
const PASSWORD_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
|
|
131
|
+
const PASSWORD_LENGTH = 22;
|
|
132
|
+
export function generatePassword() {
|
|
133
|
+
// Rejection sampling keeps the alphabet uniform (256 % 56 is not zero).
|
|
134
|
+
const ceiling = Math.floor(256 / PASSWORD_ALPHABET.length) * PASSWORD_ALPHABET.length;
|
|
135
|
+
let password = "";
|
|
136
|
+
while (password.length < PASSWORD_LENGTH) {
|
|
137
|
+
for (const byte of crypto.getRandomValues(new Uint8Array(PASSWORD_LENGTH))) {
|
|
138
|
+
if (byte >= ceiling)
|
|
139
|
+
continue;
|
|
140
|
+
password += PASSWORD_ALPHABET.charAt(byte % PASSWORD_ALPHABET.length);
|
|
141
|
+
if (password.length === PASSWORD_LENGTH)
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return password;
|
|
146
|
+
}
|
|
147
|
+
/** Internal tokens (backend auth, session signing): never typed, so the cheap
|
|
148
|
+
* hex encoding is fine. */
|
|
149
|
+
function generateToken() {
|
|
150
|
+
const bytes = crypto.getRandomValues(new Uint8Array(24));
|
|
151
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
152
|
+
}
|
|
102
153
|
export function readSecrets(home) {
|
|
103
154
|
const path = gatewayPaths(home).secrets;
|
|
104
155
|
if (!existsSync(path))
|
|
@@ -127,8 +178,9 @@ export async function ensureSecrets(home, names) {
|
|
|
127
178
|
for (const name of names) {
|
|
128
179
|
if (existing[name])
|
|
129
180
|
continue;
|
|
130
|
-
|
|
131
|
-
|
|
181
|
+
// MOCK_PASSWORD is the one a human retypes at the login page; the rest are
|
|
182
|
+
// machine-to-machine tokens.
|
|
183
|
+
existing[name] = name === "MOCK_PASSWORD" ? generatePassword() : generateToken();
|
|
132
184
|
generated = true;
|
|
133
185
|
}
|
|
134
186
|
if (generated) {
|
|
@@ -163,7 +215,7 @@ export async function fetchGatewayArtifact(opts = {}) {
|
|
|
163
215
|
if (!sums.ok)
|
|
164
216
|
throw new GatewayArtifactError(`${sumsUrl} -> HTTP ${sums.status}`);
|
|
165
217
|
const expected = parseSums(await sums.text(), `oma-gateway-${version}.tar.zst`);
|
|
166
|
-
await download(tarballUrl, tarball);
|
|
218
|
+
await download(tarballUrl, tarball, opts.stallMs ?? DEFAULT_STALL_MS);
|
|
167
219
|
const actual = await sha256File(tarball);
|
|
168
220
|
if (actual !== expected) {
|
|
169
221
|
throw new GatewayArtifactError(`checksum mismatch for ${version}: expected ${expected.slice(0, 12)}…, got ${actual.slice(0, 12)}…`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/core/gateway/daemon.ts"],"names":[],"mappings":"AAaA;mDACmD;AACnD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAGrD;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CASlE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CASnD;AAED;;;gFAGgF;AAChF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAexD;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;6CAE6C;AAC7C,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/core/gateway/daemon.ts"],"names":[],"mappings":"AAaA;mDACmD;AACnD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAGrD;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CASlE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CASnD;AAED;;;gFAGgF;AAChF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAexD;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;6CAE6C;AAC7C,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CA+B7F;AAED;uBACuB;AACvB,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CA4B7F;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,EAAE,CAKhE"}
|
|
@@ -68,7 +68,9 @@ export async function startDetachedGateway(opts) {
|
|
|
68
68
|
const gateway = gatewayPaths(opts.home);
|
|
69
69
|
await mkdir(gateway.root, { recursive: true });
|
|
70
70
|
const paths = daemonPaths(opts.home);
|
|
71
|
-
|
|
71
|
+
// 0600: the log carries the supervisor's own output, and nothing that runs
|
|
72
|
+
// unattended should be world-readable.
|
|
73
|
+
const logFd = openSync(paths.log, "a", 0o600);
|
|
72
74
|
let pid;
|
|
73
75
|
try {
|
|
74
76
|
const proc = Bun.spawn([...opts.command], {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** One diagnosis line: what was checked, what was found, and how to fix it. */
|
|
2
|
+
export interface GatewayCheck {
|
|
3
|
+
id: string;
|
|
4
|
+
ok: boolean;
|
|
5
|
+
detail: string;
|
|
6
|
+
fix?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DoctorOptions {
|
|
9
|
+
home?: string;
|
|
10
|
+
/** Seam for tests: replaces the `oma --list-models` probe. */
|
|
11
|
+
probeModels?: () => Promise<{
|
|
12
|
+
providers: string[];
|
|
13
|
+
}>;
|
|
14
|
+
/** Seam for tests: replaces the release HEAD probe. Returns the HTTP status,
|
|
15
|
+
* or undefined when the host could not be reached. */
|
|
16
|
+
probeRelease?: (url: string) => Promise<number | undefined>;
|
|
17
|
+
}
|
|
18
|
+
/** Diagnose why a gateway may not be able to run anything: artifact, tools,
|
|
19
|
+
* catalog source, runnable providers, ports, and the per-run oma binary. */
|
|
20
|
+
export declare function diagnoseGateway(opts?: DoctorOptions): Promise<GatewayCheck[]>;
|
|
21
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/core/gateway/doctor.ts"],"names":[],"mappings":"AAeA,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACrD;2DACuD;IACvD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC7D;AAoED;6EAC6E;AAC7E,wBAAsB,eAAe,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA2UvF"}
|