oh-my-agent-web 0.2.0 → 0.4.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.md +86 -447
- package/dist/server/approval/read-only.js +107 -0
- package/dist/server/approval/rules.js +85 -0
- package/dist/server/approval/scope.js +150 -0
- package/dist/server/brand.js +2 -0
- package/dist/server/cli-config.js +52 -0
- package/dist/server/cli-info.js +101 -0
- package/dist/server/cli.js +304 -0
- package/dist/server/engine/builtin.js +61 -0
- package/dist/server/engine/catalog.js +8 -0
- package/dist/server/engine/codex-approvals.js +45 -0
- package/dist/server/engine/codex-map.js +298 -0
- package/dist/server/engine/codex-sessions.js +159 -0
- package/dist/server/engine/codex-transport.js +49 -0
- package/dist/server/engine/codex.js +314 -0
- package/dist/server/engine/dsh-history.js +195 -0
- package/dist/server/engine/dsh-log.js +245 -0
- package/dist/server/engine/dsh-map.js +221 -0
- package/dist/server/engine/dsh-sessions.js +207 -0
- package/dist/server/engine/dsh-transport.js +37 -0
- package/dist/server/engine/dsh.js +284 -0
- package/dist/server/engine/fake.js +231 -0
- package/dist/server/engine/jsonrpc-stdio.js +241 -0
- package/dist/server/engine/model-registry-reload.js +115 -0
- package/dist/server/engine/pi-events.js +148 -0
- package/dist/server/engine/pi-rename.js +14 -0
- package/dist/server/engine/pi-sdk.js +343 -0
- package/dist/server/engine/pi-session-model.js +23 -0
- package/dist/server/engine/pi-thinking.js +34 -0
- package/dist/server/engine/pi-transcript.js +225 -0
- package/dist/server/engine/pi-version.js +11 -0
- package/dist/server/engine/port.js +1 -0
- package/dist/server/engine/registry.js +47 -0
- package/dist/server/engine/scratch.js +13 -0
- package/dist/server/engine/suggestion-guidance.js +9 -0
- package/dist/server/engine/switchable.js +22 -0
- package/dist/server/engine/wiring.js +107 -0
- package/dist/server/files/dir-routes.js +40 -0
- package/dist/server/files/file-search.js +63 -0
- package/dist/server/files/file-service.js +232 -0
- package/dist/server/files/files-routes.js +182 -0
- package/dist/server/files/git-info.js +186 -0
- package/dist/server/http/auth-routes.js +99 -0
- package/dist/server/http/auth.js +191 -0
- package/dist/server/http/aux-routes.js +24 -0
- package/dist/server/http/compress.js +75 -0
- package/dist/server/http/health-and-static.js +33 -0
- package/dist/server/http/live-routes.js +113 -0
- package/dist/server/http/login-page.js +200 -0
- package/dist/server/index.js +342 -520
- package/dist/server/markers/actions.js +262 -0
- package/dist/server/protocol-parse.js +201 -0
- package/dist/server/protocol-settings.js +1 -0
- package/dist/server/protocol.js +6 -0
- package/dist/server/providers/catalog-store.js +85 -0
- package/dist/server/providers/catalog-writes.js +197 -0
- package/dist/server/providers/catalog.js +77 -0
- package/dist/server/providers/enabled-models.js +118 -0
- package/dist/server/providers/import-models.js +69 -0
- package/dist/server/providers/import-routes.js +136 -0
- package/dist/server/providers/model-test.js +52 -0
- package/dist/server/providers/probe.js +64 -0
- package/dist/server/providers/provider-handlers.js +203 -0
- package/dist/server/providers/provider-store.js +327 -0
- package/dist/server/providers/request-headers.js +7 -0
- package/dist/server/providers/validate.js +241 -0
- package/dist/server/push/push-routes.js +114 -0
- package/dist/server/push/push.js +120 -0
- package/dist/server/session/approval-bridge.js +103 -0
- package/dist/server/session/attachment-resolve.js +21 -0
- package/dist/server/session/client-view.js +82 -0
- package/dist/server/session/conversation-create.js +65 -0
- package/dist/server/session/delete-session.js +84 -0
- package/dist/server/session/engine-switch.js +108 -0
- package/dist/server/session/lock.js +82 -0
- package/dist/server/session/model-controller.js +111 -0
- package/dist/server/session/notices.js +31 -0
- package/dist/server/session/pi-opener.js +123 -0
- package/dist/server/session/pi-refresh.js +51 -0
- package/dist/server/session/pi-sessions-provider.js +46 -0
- package/dist/server/session/pi-sessions.js +272 -0
- package/dist/server/session/refresh-actions.js +42 -0
- package/dist/server/session/rename-session.js +23 -0
- package/dist/server/session/runtime-services.js +28 -0
- package/dist/server/session/runtime-types.js +1 -0
- package/dist/server/session/runtime.js +358 -0
- package/dist/server/session/session-stats.js +88 -0
- package/dist/server/session/settings.js +367 -0
- package/dist/server/session/snapshot-for.js +7 -0
- package/dist/server/session/stats-refresher.js +50 -0
- package/dist/server/session/switch-conversation.js +86 -0
- package/dist/server/session/throttle.js +14 -0
- package/dist/server/session/turn-events.js +126 -0
- package/dist/server/session/turn-finish.js +79 -0
- package/dist/server/session/turn-markers.js +26 -0
- package/dist/server/session/turn-queue.js +111 -0
- package/dist/server/session/turn-send.js +88 -0
- package/dist/server/session/turn-watchdog.js +77 -0
- package/dist/server/sessions/session-routes.js +17 -0
- package/dist/server/skills/skill-routes.js +28 -0
- package/dist/server/skills/skill-store.js +134 -0
- package/dist/server/snapshot/fan-out.js +10 -0
- package/dist/server/snapshot/messages.js +75 -0
- package/dist/server/snapshot/sender.js +62 -0
- package/dist/server/snapshot/state.js +193 -0
- package/dist/server/terminal/terminal-routes.js +176 -0
- package/dist/server/terminal/terminal-session.js +140 -0
- package/dist/server/uploads/upload-routes.js +64 -0
- package/dist/server/uploads/upload-store.js +115 -0
- package/dist/server/util/data-dir.js +36 -0
- package/dist/server/util/env.js +140 -0
- package/dist/server/util/ids.js +28 -0
- package/dist/server/util/log-file.js +67 -0
- package/dist/server/util/log.js +52 -0
- package/dist/server/util/origin.js +75 -0
- package/dist/server/util/paths.js +60 -0
- package/dist/server/util/pricing-index.js +121 -0
- package/dist/server/util/pricing.js +214 -0
- package/dist/server/util/startup.js +34 -0
- package/dist/server/util/upstream-version.js +140 -0
- package/dist/server/util/usage.js +19 -0
- package/dist/server/util/version.js +50 -0
- package/dist/server/ws/handle-message.js +95 -0
- package/package.json +49 -63
- package/web/dist/apple-touch-icon-precomposed.png +0 -0
- package/web/dist/apple-touch-icon.png +0 -0
- package/web/dist/assets/abnfDiagram-WEYOIA3C-NLp09iEC.js +1 -0
- package/web/dist/assets/arc-DviL9o3c.js +1 -0
- package/web/dist/assets/architecture-WOLXFQ4H-fjsM_Sy3.js +1 -0
- package/web/dist/assets/architectureDiagram-ZHVYKT53-Cn3B4TE8.js +36 -0
- package/web/dist/assets/array-BifhSqXX.js +1 -0
- package/web/dist/assets/blockDiagram-RJAE7QA3-CIuvxWvi.js +140 -0
- package/web/dist/assets/c4Diagram-XDPPPE54-DvdKmEpF.js +38 -0
- package/web/dist/assets/channel-C3AQ6nAs.js +1 -0
- package/web/dist/assets/chunk-2E7WHVMD-HNNASGCS.js +88 -0
- package/web/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/web/dist/assets/chunk-4EMNKR2T-Cg-eRwBE.js +1 -0
- package/web/dist/assets/chunk-55XCU76K-BweiisZL.js +1 -0
- package/web/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/web/dist/assets/chunk-7S6XSTFO-BCzIiP-6.js +2 -0
- package/web/dist/assets/chunk-AQDTSKZB-Dl0NRFuU.js +72 -0
- package/web/dist/assets/chunk-DSNLSHJZ-zhHCjjN9.js +129 -0
- package/web/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/web/dist/assets/chunk-HAGFOTED-BDQqU2EH.js +62 -0
- package/web/dist/assets/chunk-HNE4Q36P-B-qRzMjX.js +1 -0
- package/web/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/web/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/web/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
- package/web/dist/assets/chunk-NQOKRVIF-CNcvhMn_.js +213 -0
- package/web/dist/assets/chunk-UHW7PFBP-Dwr8LUzr.js +1 -0
- package/web/dist/assets/chunk-VQ5W62QI-O8sN6jJu.js +1 -0
- package/web/dist/assets/chunk-WQQL4CDK-B6-59Zg2.js +10 -0
- package/web/dist/assets/chunk-XXDRQBXY-DvSbeUlZ.js +1 -0
- package/web/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/web/dist/assets/chunk-YZO67HFG-BXpKYKsw.js +1 -0
- package/web/dist/assets/chunk-ZAUX2LTX-B1Z5VSkz.js +1 -0
- package/web/dist/assets/chunk-ZCHMQSOR-h_5mBLIA.js +2 -0
- package/web/dist/assets/classDiagram-v2-4MYWR3UA-Dr6JnaaV.js +217 -0
- package/web/dist/assets/cose-bilkent-JH36ORCC-36ShkR7R.js +1 -0
- package/web/dist/assets/cynefin-EF2NZ3EQ-Dg6bI7pC.js +1 -0
- package/web/dist/assets/cynefinDiagram-WF24UDRP-B0YHpBlR.js +62 -0
- package/web/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/web/dist/assets/dagre-NK4GIZVX-Cx3mn7YS.js +4 -0
- package/web/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/web/dist/assets/diagram-CBK3MWTC-l8aZG_Bm.js +24 -0
- package/web/dist/assets/diagram-I5XIIV2D-Bd5f14_h.js +24 -0
- package/web/dist/assets/diagram-ODIL2UCW-hBsxCYUj.js +30 -0
- package/web/dist/assets/diagram-PFBEJUXE-BgpIBUYl.js +3 -0
- package/web/dist/assets/diagram-RJA3I4VV-CDU1gUVD.js +41 -0
- package/web/dist/assets/diagram-TYUNFSNR-Iuxh7Tqf.js +200 -0
- package/web/dist/assets/dist-C86cKTuE.js +1 -0
- package/web/dist/assets/ebnfDiagram-V4P3TTPT-Cms0HFKh.js +1 -0
- package/web/dist/assets/elk-QONTBCTQ-B7yEy0PQ.js +27 -0
- package/web/dist/assets/erDiagram-KYQIGQAA-CL48j4aU.js +99 -0
- package/web/dist/assets/eventmodeling-K75KTNOO-CFvkaejh.js +1 -0
- package/web/dist/assets/flowDiagram-IUGLLWBU-6UyKXpqG.js +1 -0
- package/web/dist/assets/ganttDiagram-R7GPNTJY-vdMr-0ZV.js +292 -0
- package/web/dist/assets/gitGraph-VSP46ZUC-2z85Wuwo.js +1 -0
- package/web/dist/assets/gitGraphDiagram-PDYL7XHY-BY4GPiqC.js +106 -0
- package/web/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/web/dist/assets/index-Bjn56a2i.js +76 -0
- package/web/dist/assets/index-Drp6e5iU.css +1 -0
- package/web/dist/assets/info-OHQRW6UA-ECzkYTx-.js +1 -0
- package/web/dist/assets/infoDiagram-MVDRKAX5-DDk5EAeb.js +2 -0
- package/web/dist/assets/init-C-OQMol4.js +1 -0
- package/web/dist/assets/ishikawaDiagram-HPEHV2GE-9yPH4J94.js +70 -0
- package/web/dist/assets/journeyDiagram-BOSHW7O7-BPTIap3l.js +139 -0
- package/web/dist/assets/kanban-definition-FNSA4HC5-BbADHrA8.js +89 -0
- package/web/dist/assets/katex-B_JcIwqj.js +257 -0
- package/web/dist/assets/line-CHV9_4lL.js +1 -0
- package/web/dist/assets/linear-C-eZsBTv.js +1 -0
- package/web/dist/assets/mermaid-parser.core-BiAhX1l1.js +7 -0
- package/web/dist/assets/mermaid.core-BxP_A_zj.js +44 -0
- package/web/dist/assets/mindmap-definition-6OY57UG3-BMWlit95.js +98 -0
- package/web/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/web/dist/assets/packet-JDAUHWVQ-FVApuKOp.js +1 -0
- package/web/dist/assets/path-fybaL0A-.js +1 -0
- package/web/dist/assets/pegDiagram-ECIUJNBX-WjWJvDHM.js +1 -0
- package/web/dist/assets/pie-XZMESJXO-BGCZnvhp.js +1 -0
- package/web/dist/assets/pieDiagram-GNMPY74T-DJpAC_JC.js +39 -0
- package/web/dist/assets/quadrantDiagram-D57K4MC3-hxx5c7oq.js +7 -0
- package/web/dist/assets/radar-ABXABTNO-BU9Shc-O.js +1 -0
- package/web/dist/assets/railroad-I3PHUGI6-pWj8d-G4.js +1 -0
- package/web/dist/assets/railroad-abnf-LNEFI6M7-D7HZBTAj.js +1 -0
- package/web/dist/assets/railroad-ebnf-DWYD2UWJ-CKiXJSuM.js +1 -0
- package/web/dist/assets/railroad-peg-JR7BVNR7-DB3VJHgR.js +1 -0
- package/web/dist/assets/railroadDiagram-Y72NRBL7-Cev7Y65k.js +1 -0
- package/web/dist/assets/requirementDiagram-EQYSW5RA-CqbwMLD8.js +84 -0
- package/web/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/web/dist/assets/sankeyDiagram-SY24JHO5-V2cyVi8q.js +40 -0
- package/web/dist/assets/sequenceDiagram-BLEHNHQU-By6NJOPM.js +169 -0
- package/web/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/web/dist/assets/src-Da91fG3g.js +1 -0
- package/web/dist/assets/stateDiagram-v2-A6IESKWZ-BnPGX5UI.js +291 -0
- package/web/dist/assets/swimlanes-B3ENRKFQ-C58BpIpm.js +1 -0
- package/web/dist/assets/swimlanesDiagram-AYQRV4AK-RaivxSsc.js +8 -0
- package/web/dist/assets/timeline-definition-JXYKGLGB-Hgo1DhnS.js +120 -0
- package/web/dist/assets/treeView-D4JQ5SDB-DF0dfjqB.js +1 -0
- package/web/dist/assets/treemap-SAJKECNS-CSSM5FqB.js +1 -0
- package/web/dist/assets/usecaseDiagram-WPOLBNNX-BkBBD1NY.js +328 -0
- package/web/dist/assets/vennDiagram-Q3Y3IDCW-Bm28keX_.js +34 -0
- package/web/dist/assets/wardley-7MLQ67FV-By7KgEQX.js +1 -0
- package/web/dist/assets/wardleyDiagram-IVAIDKWA-oVtIrnGa.js +78 -0
- package/web/dist/assets/xychartDiagram-4URNHDU4-aI8FH4g2.js +7 -0
- package/web/dist/favicon-32.png +0 -0
- package/web/dist/favicon.ico +0 -0
- package/web/dist/favicon.svg +20 -8
- package/web/dist/icons/apple-touch-icon-180.png +0 -0
- package/web/dist/icons/icon-192.png +0 -0
- package/web/dist/icons/icon-512.png +0 -0
- package/web/dist/icons/icon-maskable-512.png +0 -0
- package/web/dist/index.html +21 -27
- package/web/dist/manifest.webmanifest +17 -0
- package/web/dist/maskable.svg +25 -0
- package/web/dist/sw.js +40 -0
- package/bin/oh-my-agent-web-ps.mjs +0 -203
- package/bin/oh-my-agent-web-ws.mjs +0 -593
- package/bin/oh-my-agent-web.mjs +0 -321
- package/bin/update-check.mjs +0 -142
- package/dist/server/codex-client.js +0 -310
- package/dist/server/codex-supervisor.js +0 -446
- package/dist/server/fs-service.js +0 -144
- package/dist/server/request-inspector.js +0 -458
- package/dist/shared/protocol.js +0 -112
- package/scripts/manage-server.mjs +0 -155
- package/scripts/omaw.mjs +0 -74
- package/shared/protocol.ts +0 -228
- package/web/dist/assets/index-B8-VzDf2.css +0 -1
- package/web/dist/assets/index-DeC2eiiq.js +0 -42
- package/web/dist/assets/markdown-CYXe6JZx.js +0 -55
|
@@ -1,593 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* oh-my-agent-web ws — 连接 codex app-server WebSocket 的命令行客户端。
|
|
4
|
-
*
|
|
5
|
-
* 参考 /Users/dds/data/ai/work/codex-ws-client.mjs,整理为 oh-my-agent-web 的子命令:
|
|
6
|
-
* oh-my-agent-web ws # 交互模式(Ctrl-C 中断当前 turn / Ctrl-D 退出)
|
|
7
|
-
* oh-my-agent-web ws "帮我看看这个目录" # 单次提问后退出
|
|
8
|
-
* echo "写个 hello world" | oh-my-agent-web ws # 从管道读
|
|
9
|
-
* oh-my-agent-web ws --port 25259 # 指定端口
|
|
10
|
-
* oh-my-agent-web ws --url ws://host:port # 指定完整地址
|
|
11
|
-
*
|
|
12
|
-
* 退出语义:turn 跑在 app-server 上而不在本进程里,所以直接掉线会留下一个没人看的
|
|
13
|
-
* 生成过程(继续烧 token)。因此所有退出路径都走 shutdown():先 `turn/interrupt`,
|
|
14
|
-
* 再关连接。Ctrl-C 第一次 = 中断+关闭,第二次 = 立即退出。
|
|
15
|
-
*
|
|
16
|
-
* 仅用 Node 内置能力(WebSocket 需要 Node >= 22),无额外依赖。
|
|
17
|
-
*/
|
|
18
|
-
import readline from "node:readline";
|
|
19
|
-
import { pathToFileURL } from "node:url";
|
|
20
|
-
|
|
21
|
-
const DEFAULT_HOST = "127.0.0.1";
|
|
22
|
-
const DEFAULT_PORT = 25258;
|
|
23
|
-
|
|
24
|
-
const HELP = `oh-my-agent-web ws — connect to the codex app-server over WebSocket
|
|
25
|
-
|
|
26
|
-
Usage:
|
|
27
|
-
oh-my-agent-web ws [options] ["message" ...]
|
|
28
|
-
|
|
29
|
-
Connection:
|
|
30
|
-
--url <ws://host:port> Full address (also accepts bare host:port). Default ws://${DEFAULT_HOST}:${DEFAULT_PORT}
|
|
31
|
-
--addr <host:port> Address + port (same as --host + --port)
|
|
32
|
-
--host <host> Host, default ${DEFAULT_HOST}
|
|
33
|
-
-p, --port <n> Port, default ${DEFAULT_PORT}
|
|
34
|
-
Env: CODEX_WS_URL / CODEX_WS_HOST / CODEX_WS_PORT
|
|
35
|
-
|
|
36
|
-
Session:
|
|
37
|
-
--cwd <dir> Working directory (default: current dir)
|
|
38
|
-
-t, --thread <id> Resume an existing thread
|
|
39
|
-
--model <name> Model id
|
|
40
|
-
--sandbox <mode> read-only | workspace-write | danger-full-access
|
|
41
|
-
--approval <mode> untrusted | on-request | on-failure | never
|
|
42
|
-
-y, --auto-approve Auto-accept approvals (acceptForSession)
|
|
43
|
-
--decline Auto-decline approvals
|
|
44
|
-
-v, --verbose Print reasoning deltas
|
|
45
|
-
--json Print raw event JSON only
|
|
46
|
-
--timeout <sec> Max wait for a turn, default 1800
|
|
47
|
-
-h, --help Show this help
|
|
48
|
-
`;
|
|
49
|
-
|
|
50
|
-
/** Parse `oh-my-agent-web ws [ ... ]` arguments. */
|
|
51
|
-
export function parseWsArgs(argv) {
|
|
52
|
-
const opts = {
|
|
53
|
-
url: null,
|
|
54
|
-
host: process.env.CODEX_WS_HOST || DEFAULT_HOST,
|
|
55
|
-
port: Number(process.env.CODEX_WS_PORT) || DEFAULT_PORT,
|
|
56
|
-
hostExplicit: false,
|
|
57
|
-
portExplicit: false,
|
|
58
|
-
cwd: process.cwd(),
|
|
59
|
-
thread: null,
|
|
60
|
-
model: null,
|
|
61
|
-
sandbox: null,
|
|
62
|
-
approval: null,
|
|
63
|
-
autoApprove: false,
|
|
64
|
-
autoDecline: false,
|
|
65
|
-
verbose: false,
|
|
66
|
-
json: false,
|
|
67
|
-
timeout: 1800,
|
|
68
|
-
messages: [],
|
|
69
|
-
help: false,
|
|
70
|
-
};
|
|
71
|
-
for (let i = 0; i < argv.length; i++) {
|
|
72
|
-
const a = argv[i];
|
|
73
|
-
const next = () => argv[++i];
|
|
74
|
-
switch (a) {
|
|
75
|
-
case "--url":
|
|
76
|
-
opts.url = next();
|
|
77
|
-
break;
|
|
78
|
-
case "--host":
|
|
79
|
-
case "--addr": {
|
|
80
|
-
const v = String(next() ?? "").trim().replace(/^\[|\]$/g, "");
|
|
81
|
-
const m = v.match(/^(\[?[^\]]+\]?):(\d+)$/);
|
|
82
|
-
if (m) {
|
|
83
|
-
opts.host = m[1].replace(/^\[|\]$/g, "");
|
|
84
|
-
opts.port = Number(m[2]);
|
|
85
|
-
opts.hostExplicit = true;
|
|
86
|
-
opts.portExplicit = true;
|
|
87
|
-
} else if (/^\d+$/.test(v)) {
|
|
88
|
-
opts.port = Number(v);
|
|
89
|
-
opts.portExplicit = true;
|
|
90
|
-
} else {
|
|
91
|
-
opts.host = v;
|
|
92
|
-
opts.hostExplicit = true;
|
|
93
|
-
}
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
case "-p":
|
|
97
|
-
case "--port":
|
|
98
|
-
opts.port = Number(next());
|
|
99
|
-
opts.portExplicit = true;
|
|
100
|
-
break;
|
|
101
|
-
case "--cwd":
|
|
102
|
-
opts.cwd = next();
|
|
103
|
-
break;
|
|
104
|
-
case "-t":
|
|
105
|
-
case "--thread":
|
|
106
|
-
opts.thread = next();
|
|
107
|
-
break;
|
|
108
|
-
case "--model":
|
|
109
|
-
opts.model = next();
|
|
110
|
-
break;
|
|
111
|
-
case "--sandbox":
|
|
112
|
-
opts.sandbox = next();
|
|
113
|
-
break;
|
|
114
|
-
case "--approval":
|
|
115
|
-
opts.approval = next();
|
|
116
|
-
break;
|
|
117
|
-
case "--auto-approve":
|
|
118
|
-
case "-y":
|
|
119
|
-
opts.autoApprove = true;
|
|
120
|
-
break;
|
|
121
|
-
case "--decline":
|
|
122
|
-
opts.autoDecline = true;
|
|
123
|
-
break;
|
|
124
|
-
case "--verbose":
|
|
125
|
-
case "-v":
|
|
126
|
-
opts.verbose = true;
|
|
127
|
-
break;
|
|
128
|
-
case "--json":
|
|
129
|
-
opts.json = true;
|
|
130
|
-
break;
|
|
131
|
-
case "--timeout":
|
|
132
|
-
opts.timeout = Number(next());
|
|
133
|
-
break;
|
|
134
|
-
case "-h":
|
|
135
|
-
case "--help":
|
|
136
|
-
opts.help = true;
|
|
137
|
-
break;
|
|
138
|
-
default:
|
|
139
|
-
opts.messages.push(a);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return opts;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Normalise a raw address into a ws:// URL (accepts ws/wss/http/https/bare host:port). */
|
|
146
|
-
function normalizeWsUrl(raw) {
|
|
147
|
-
if (/^wss?:\/\//i.test(raw)) return raw;
|
|
148
|
-
if (/^https?:\/\//i.test(raw)) return raw.replace(/^http/i, "ws");
|
|
149
|
-
return "ws://" + raw;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/** Combine --url / --host / --port / env into a single ws:// URL.
|
|
153
|
-
*
|
|
154
|
-
* Priority: --url > --host/--port > env (CODEX_WS_URL / CODEX_WS_HOST / CODEX_WS_PORT) > defaults.
|
|
155
|
-
*/
|
|
156
|
-
export function resolveWsUrl(o) {
|
|
157
|
-
const flagUrl = String(o.url ?? "").trim().replace(/^["']|["']$/g, "");
|
|
158
|
-
if (flagUrl) return normalizeWsUrl(flagUrl);
|
|
159
|
-
|
|
160
|
-
// An explicit --host/--port (or --addr) must beat an env URL.
|
|
161
|
-
const explicitHostPort = Boolean(o.hostExplicit || o.portExplicit);
|
|
162
|
-
if (!explicitHostPort) {
|
|
163
|
-
const envUrl = String(process.env.CODEX_WS_URL ?? "").trim().replace(/^["']|["']$/g, "");
|
|
164
|
-
if (envUrl) return normalizeWsUrl(envUrl);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (!Number.isInteger(o.port) || o.port < 1 || o.port > 65535) {
|
|
168
|
-
throw new Error(`invalid port: ${o.port} (expected 1-65535)`);
|
|
169
|
-
}
|
|
170
|
-
const host = o.host.includes(":") && !o.host.startsWith("[") ? `[${o.host}]` : o.host;
|
|
171
|
-
return `ws://${host}:${o.port}`;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
class CodexClient {
|
|
175
|
-
#listeners = new Map();
|
|
176
|
-
|
|
177
|
-
constructor(url) {
|
|
178
|
-
this.url = url;
|
|
179
|
-
this.ws = null;
|
|
180
|
-
this.nextId = 1;
|
|
181
|
-
this.pending = new Map();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
on(name, fn, { once = false } = {}) {
|
|
185
|
-
const list = this.#listeners.get(name) ?? [];
|
|
186
|
-
list.push({ fn, once });
|
|
187
|
-
this.#listeners.set(name, list);
|
|
188
|
-
return () => this.off(name, fn);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
off(name, fn) {
|
|
192
|
-
const list = this.#listeners.get(name) ?? [];
|
|
193
|
-
this.#listeners.set(name, list.filter((l) => l.fn !== fn));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
emit(name, detail) {
|
|
197
|
-
for (const l of [...(this.#listeners.get(name) ?? [])]) {
|
|
198
|
-
if (l.once) this.off(name, l.fn);
|
|
199
|
-
try {
|
|
200
|
-
l.fn({ detail });
|
|
201
|
-
} catch (e) {
|
|
202
|
-
log(`[listener error] ${e.stack}`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
connect() {
|
|
208
|
-
return new Promise((resolve, reject) => {
|
|
209
|
-
const ws = new WebSocket(this.url);
|
|
210
|
-
this.ws = ws;
|
|
211
|
-
ws.onopen = () => {
|
|
212
|
-
log(`[connected] ${this.url}`);
|
|
213
|
-
resolve();
|
|
214
|
-
};
|
|
215
|
-
ws.onerror = (e) => reject(new Error(`WebSocket connect failed: ${e.message ?? e.type}`));
|
|
216
|
-
ws.onclose = (e) => {
|
|
217
|
-
this.emit("closed", { code: e.code, reason: e.reason });
|
|
218
|
-
for (const { reject: rj } of this.pending.values()) rj(new Error("connection closed"));
|
|
219
|
-
this.pending.clear();
|
|
220
|
-
};
|
|
221
|
-
ws.onmessage = (e) => this.#onMessage(String(e.data));
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
#onMessage(raw) {
|
|
226
|
-
let msg;
|
|
227
|
-
try {
|
|
228
|
-
msg = JSON.parse(raw);
|
|
229
|
-
} catch {
|
|
230
|
-
return this.emit("raw", raw);
|
|
231
|
-
}
|
|
232
|
-
// 1) response to one of our requests
|
|
233
|
-
if (msg.id !== undefined && msg.method === undefined) {
|
|
234
|
-
const p = this.pending.get(msg.id);
|
|
235
|
-
if (p) {
|
|
236
|
-
this.pending.delete(msg.id);
|
|
237
|
-
msg.error ? p.reject(new Error(JSON.stringify(msg.error))) : p.resolve(msg.result);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
// 2) server -> client request (needs a reply)
|
|
242
|
-
if (msg.id !== undefined && msg.method) return this.emit("serverRequest", msg);
|
|
243
|
-
// 3) notification
|
|
244
|
-
this.emit("notification", msg);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
send(msg) {
|
|
248
|
-
this.ws.send(JSON.stringify(msg));
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
request(method, params) {
|
|
252
|
-
const id = this.nextId++;
|
|
253
|
-
return new Promise((resolve, reject) => {
|
|
254
|
-
this.pending.set(id, { resolve, reject });
|
|
255
|
-
this.send({ method, id, params });
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
respond(id, result) {
|
|
260
|
-
this.send({ id, result });
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
close() {
|
|
264
|
-
try {
|
|
265
|
-
this.ws?.close();
|
|
266
|
-
} catch {
|
|
267
|
-
/* ignore */
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const log = (...a) => process.stderr.write(a.join(" ") + "\n");
|
|
273
|
-
|
|
274
|
-
/** Run the ws client. Resolves when the session ends. */
|
|
275
|
-
export async function runWsClient(argv) {
|
|
276
|
-
const opts = parseWsArgs(argv);
|
|
277
|
-
|
|
278
|
-
if (opts.help) {
|
|
279
|
-
process.stdout.write(HELP);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
let url;
|
|
284
|
-
try {
|
|
285
|
-
url = resolveWsUrl(opts);
|
|
286
|
-
} catch (e) {
|
|
287
|
-
console.error(`[oh-my-agent-web ws] ${e.message}`);
|
|
288
|
-
process.exitCode = 2;
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const client = new CodexClient(url);
|
|
293
|
-
|
|
294
|
-
/* -------- exit / Ctrl-C --------
|
|
295
|
-
* A turn lives on the app-server, not in this process: dropping the socket
|
|
296
|
-
* mid-turn would leave the agent generating (and billing) with nobody
|
|
297
|
-
* watching — and it stays that way, since nothing tells codex to stop. So
|
|
298
|
-
* every exit path funnels through `shutdown()`: interrupt the in-flight turn
|
|
299
|
-
* first, then close the socket. A second Ctrl-C stops waiting for the
|
|
300
|
-
* interrupt round-trip and exits immediately.
|
|
301
|
-
*/
|
|
302
|
-
let activeTurn = null; // { threadId, turnId } while a turn is in flight
|
|
303
|
-
let socketClosed = false;
|
|
304
|
-
let shuttingDown = false;
|
|
305
|
-
let interrupts = 0;
|
|
306
|
-
|
|
307
|
-
const waitForSocketClose = (timeoutMs = 500) =>
|
|
308
|
-
new Promise((resolve) => {
|
|
309
|
-
if (socketClosed) return resolve();
|
|
310
|
-
const done = () => {
|
|
311
|
-
clearTimeout(timer);
|
|
312
|
-
resolve();
|
|
313
|
-
};
|
|
314
|
-
const timer = setTimeout(done, timeoutMs);
|
|
315
|
-
client.on("closed", done, { once: true });
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
client.on("closed", (ev) => {
|
|
319
|
-
socketClosed = true;
|
|
320
|
-
if (shuttingDown) return;
|
|
321
|
-
const { code, reason } = ev.detail ?? {};
|
|
322
|
-
const detail = code ? ` (code=${code})` : "";
|
|
323
|
-
log(`\x1b[31m[closed] connection closed${detail}${reason ? ` ${reason}` : ""}\x1b[0m`);
|
|
324
|
-
void shutdown(1);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
/** Interrupt whatever is running, close the socket, exit. Idempotent. */
|
|
328
|
-
async function shutdown(code, reason) {
|
|
329
|
-
if (shuttingDown) return;
|
|
330
|
-
shuttingDown = true;
|
|
331
|
-
if (reason) log(reason);
|
|
332
|
-
const turn = activeTurn;
|
|
333
|
-
if (turn?.turnId && !socketClosed) {
|
|
334
|
-
try {
|
|
335
|
-
await client.request("turn/interrupt", { threadId: turn.threadId, turnId: turn.turnId });
|
|
336
|
-
log(`\x1b[2m[interrupt] turn ${turn.turnId} interrupted\x1b[0m`);
|
|
337
|
-
} catch (e) {
|
|
338
|
-
log(`\x1b[31m[interrupt] failed: ${e.message}\x1b[0m`);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
activeTurn = null;
|
|
342
|
-
client.close();
|
|
343
|
-
await waitForSocketClose();
|
|
344
|
-
process.exit(code);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function handleInterrupt(signal) {
|
|
348
|
-
interrupts += 1;
|
|
349
|
-
if (interrupts > 1) {
|
|
350
|
-
log(`\n\x1b[31m[${signal}] again — exiting now\x1b[0m`);
|
|
351
|
-
client.close();
|
|
352
|
-
process.exit(130);
|
|
353
|
-
}
|
|
354
|
-
void shutdown(130, `\n\x1b[2m[${signal}] interrupting current turn and closing…\x1b[0m`);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// readline 在 TTY 下会自己接管 Ctrl-C(raw mode 下不会产生进程级 SIGINT),
|
|
358
|
-
// 所以进程级和 rl 级都要挂,靠 shuttingDown / interrupts 去重。
|
|
359
|
-
process.on("SIGINT", () => handleInterrupt("SIGINT"));
|
|
360
|
-
process.on("SIGTERM", () => handleInterrupt("SIGTERM"));
|
|
361
|
-
try {
|
|
362
|
-
await client.connect();
|
|
363
|
-
} catch (e) {
|
|
364
|
-
console.error(`[oh-my-agent-web ws] failed to connect ${url} — ${e.message}`);
|
|
365
|
-
console.error(" make sure the app-server is running, e.g. `oh-my-agent-web` (default ws://127.0.0.1:25258)");
|
|
366
|
-
process.exitCode = 1;
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const init = await client.request("initialize", {
|
|
371
|
-
clientInfo: { name: "oh-my-agent-web-ws", title: "Oh My Agent Web WS Client", version: "1.0.0" },
|
|
372
|
-
capabilities: { experimentalApi: true, requestAttestation: false },
|
|
373
|
-
});
|
|
374
|
-
log(`[server] ${init.userAgent} codexHome=${init.codexHome}`);
|
|
375
|
-
|
|
376
|
-
const threadParams = {
|
|
377
|
-
...(opts.model ? { model: opts.model } : {}),
|
|
378
|
-
...(opts.cwd ? { cwd: opts.cwd } : {}),
|
|
379
|
-
...(opts.approval ? { approvalPolicy: opts.approval } : {}),
|
|
380
|
-
...(opts.sandbox ? { sandbox: opts.sandbox } : {}),
|
|
381
|
-
};
|
|
382
|
-
const threadResp = opts.thread
|
|
383
|
-
? await client.request("thread/resume", { threadId: opts.thread, ...threadParams })
|
|
384
|
-
: await client.request("thread/start", threadParams);
|
|
385
|
-
|
|
386
|
-
const threadId = threadResp.thread.id;
|
|
387
|
-
log(
|
|
388
|
-
`[thread] ${threadId} model=${threadResp.model} cwd=${threadResp.cwd} sandbox=${JSON.stringify(threadResp.sandbox)}`,
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
/* -------- event rendering -------- */
|
|
392
|
-
let inAgentMessage = false;
|
|
393
|
-
let inReasoning = false;
|
|
394
|
-
let pendingApproval = null;
|
|
395
|
-
let rl = null;
|
|
396
|
-
|
|
397
|
-
client.on("notification", (ev) => {
|
|
398
|
-
const { method, params } = ev.detail;
|
|
399
|
-
if (opts.json) return void process.stdout.write(JSON.stringify(ev.detail) + "\n");
|
|
400
|
-
|
|
401
|
-
switch (method) {
|
|
402
|
-
case "item/agentMessage/delta":
|
|
403
|
-
if (!inAgentMessage) {
|
|
404
|
-
process.stdout.write("\n");
|
|
405
|
-
inAgentMessage = true;
|
|
406
|
-
}
|
|
407
|
-
process.stdout.write(params.delta);
|
|
408
|
-
break;
|
|
409
|
-
|
|
410
|
-
case "item/reasoning/summaryTextDelta":
|
|
411
|
-
case "item/reasoning/textDelta":
|
|
412
|
-
if (!opts.verbose) break;
|
|
413
|
-
if (inAgentMessage) {
|
|
414
|
-
process.stdout.write("\n");
|
|
415
|
-
inAgentMessage = false;
|
|
416
|
-
}
|
|
417
|
-
if (!inReasoning) {
|
|
418
|
-
process.stdout.write("\x1b[2m💭 ");
|
|
419
|
-
inReasoning = true;
|
|
420
|
-
}
|
|
421
|
-
process.stdout.write(params.delta);
|
|
422
|
-
break;
|
|
423
|
-
|
|
424
|
-
case "item/started": {
|
|
425
|
-
const it = params.item ?? {};
|
|
426
|
-
if (inAgentMessage) {
|
|
427
|
-
process.stdout.write("\n");
|
|
428
|
-
inAgentMessage = false;
|
|
429
|
-
}
|
|
430
|
-
if (inReasoning) {
|
|
431
|
-
process.stdout.write("\x1b[0m\n");
|
|
432
|
-
inReasoning = false;
|
|
433
|
-
}
|
|
434
|
-
if (it.type === "commandExecution") log(`\x1b[36m▶ exec\x1b[0m ${it.command ?? ""}`);
|
|
435
|
-
else if (it.type === "fileChange") log(`\x1b[33m✎ patch\x1b[0m ${(it.changes ?? []).length} file(s)`);
|
|
436
|
-
else if (it.type === "mcpToolCall" || it.type === "dynamicToolCall") log(`\x1b[35m⚙ tool\x1b[0m ${it.tool ?? it.name ?? ""}`);
|
|
437
|
-
break;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
case "item/completed": {
|
|
441
|
-
const it = params.item ?? {};
|
|
442
|
-
if (inReasoning) {
|
|
443
|
-
process.stdout.write("\x1b[0m");
|
|
444
|
-
inReasoning = false;
|
|
445
|
-
}
|
|
446
|
-
if (it.type === "commandExecution") log(`\x1b[36m◀ exec done\x1b[0m exit=${it.exitCode ?? "?"}`);
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
case "turn/completed": {
|
|
451
|
-
if (inAgentMessage) {
|
|
452
|
-
process.stdout.write("\n");
|
|
453
|
-
inAgentMessage = false;
|
|
454
|
-
}
|
|
455
|
-
if (inReasoning) {
|
|
456
|
-
process.stdout.write("\x1b[0m\n");
|
|
457
|
-
inReasoning = false;
|
|
458
|
-
}
|
|
459
|
-
const t = params.turn ?? {};
|
|
460
|
-
const err = t.error ? ` error=${JSON.stringify(t.error)}` : "";
|
|
461
|
-
log(`\x1b[2m── turn ${t.status ?? "done"}${err} ──\x1b[0m`);
|
|
462
|
-
client.emit("turnDone", t);
|
|
463
|
-
break;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
case "error":
|
|
467
|
-
log(`\x1b[31m[error] ${JSON.stringify(params)}\x1b[0m`);
|
|
468
|
-
break;
|
|
469
|
-
|
|
470
|
-
default:
|
|
471
|
-
break;
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
function decide(id, decision) {
|
|
476
|
-
pendingApproval = null;
|
|
477
|
-
client.respond(id, { decision });
|
|
478
|
-
log(` → ${decision}`);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
client.on("serverRequest", async (ev) => {
|
|
482
|
-
const { id, method, params } = ev.detail;
|
|
483
|
-
if (opts.json) process.stdout.write(JSON.stringify(ev.detail) + "\n");
|
|
484
|
-
|
|
485
|
-
if (opts.autoApprove) return decide(id, "acceptForSession");
|
|
486
|
-
if (opts.autoDecline) return decide(id, "decline");
|
|
487
|
-
|
|
488
|
-
if (method === "item/commandExecution/requestApproval") {
|
|
489
|
-
log(`\x1b[33m⚠ approval needed:\x1b[0m ${params.command ?? ""} (cwd=${params.cwd ?? ""})${params.reason ? " reason: " + params.reason : ""}`);
|
|
490
|
-
} else if (method === "item/fileChange/requestApproval") {
|
|
491
|
-
log(`\x1b[33m⚠ file change approval\x1b[0m${params.reason ? " reason: " + params.reason : ""}`);
|
|
492
|
-
} else {
|
|
493
|
-
log(`\x1b[33m⚠ server request ${method}:\x1b[0m ${JSON.stringify(params).slice(0, 400)}`);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
if (process.stdin.isTTY && rl) {
|
|
497
|
-
pendingApproval = { id, method };
|
|
498
|
-
rl.setPrompt("approve? [y=once / a=session / n=decline] > ");
|
|
499
|
-
rl.prompt();
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
log(" non-interactive, declining by default (use --auto-approve / --decline)");
|
|
503
|
-
decide(id, "decline");
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
async function ask(text) {
|
|
507
|
-
const done = new Promise((resolve) => client.on("turnDone", resolve, { once: true }));
|
|
508
|
-
const started = await client.request("turn/start", {
|
|
509
|
-
threadId,
|
|
510
|
-
input: [{ type: "text", text, text_elements: [] }],
|
|
511
|
-
});
|
|
512
|
-
activeTurn = { threadId, turnId: started?.turn?.id ?? null };
|
|
513
|
-
const timer = setTimeout(() => {
|
|
514
|
-
log(`\x1b[31m[timeout] turn did not finish within ${opts.timeout}s\x1b[0m`);
|
|
515
|
-
client.emit("turnDone", { status: "timeout" });
|
|
516
|
-
}, opts.timeout * 1000);
|
|
517
|
-
await done;
|
|
518
|
-
activeTurn = null;
|
|
519
|
-
clearTimeout(timer);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function setupRepl() {
|
|
523
|
-
rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: process.stdin.isTTY });
|
|
524
|
-
rl.on("line", async (line) => {
|
|
525
|
-
const text = line.trim();
|
|
526
|
-
if (!text) {
|
|
527
|
-
if (process.stdin.isTTY) rl.prompt();
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
if (pendingApproval) {
|
|
531
|
-
if (/^(y|yes)$/i.test(text)) decide(pendingApproval.id, "accept");
|
|
532
|
-
else if (/^(a|all)$/i.test(text)) decide(pendingApproval.id, "acceptForSession");
|
|
533
|
-
else decide(pendingApproval.id, "decline");
|
|
534
|
-
if (process.stdin.isTTY) rl.prompt();
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
rl.pause();
|
|
538
|
-
try {
|
|
539
|
-
await ask(text);
|
|
540
|
-
} catch (e) {
|
|
541
|
-
log(`\x1b[31m[error] ${e.message}\x1b[0m`);
|
|
542
|
-
}
|
|
543
|
-
rl.resume();
|
|
544
|
-
if (process.stdin.isTTY) rl.prompt();
|
|
545
|
-
});
|
|
546
|
-
rl.on("SIGINT", () => handleInterrupt("SIGINT"));
|
|
547
|
-
rl.on("close", () => {
|
|
548
|
-
void shutdown(0);
|
|
549
|
-
});
|
|
550
|
-
if (process.stdin.isTTY) {
|
|
551
|
-
log("\ntype a message and press Enter (Ctrl-C: interrupt & close / Ctrl-D: exit). approvals: y=once / a=session / n=decline\n");
|
|
552
|
-
rl.prompt();
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
if (opts.messages.length) {
|
|
557
|
-
for (const msg of opts.messages) {
|
|
558
|
-
try {
|
|
559
|
-
await ask(msg);
|
|
560
|
-
} catch (e) {
|
|
561
|
-
log(`\x1b[31m[error] ${e.message}\x1b[0m`);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
await shutdown(0);
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
if (process.stdin.isTTY) {
|
|
568
|
-
setupRepl();
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
// piped stdin: the whole input is one message
|
|
572
|
-
let buf = "";
|
|
573
|
-
process.stdin.setEncoding("utf8");
|
|
574
|
-
for await (const chunk of process.stdin) buf += chunk;
|
|
575
|
-
const text = buf.trim();
|
|
576
|
-
if (text) {
|
|
577
|
-
try {
|
|
578
|
-
await ask(text);
|
|
579
|
-
} catch (e) {
|
|
580
|
-
log(`\x1b[31m[error] ${e.message}\x1b[0m`);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
await shutdown(0);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// Allow running this file directly: `node bin/oh-my-agent-web-ws.mjs ...`
|
|
587
|
-
const invoked = process.argv[1] ? pathToFileURL(process.argv[1]).href : "";
|
|
588
|
-
if (invoked === import.meta.url) {
|
|
589
|
-
runWsClient(process.argv.slice(2)).catch((err) => {
|
|
590
|
-
console.error(`[oh-my-agent-web ws] ${err instanceof Error ? err.message : String(err)}`);
|
|
591
|
-
process.exit(1);
|
|
592
|
-
});
|
|
593
|
-
}
|