newcraw 1.0.0 → 1.0.2
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/dist/REPL-IAK7ZN2Z.js +42 -0
- package/dist/{acp-A55ZRIPP.js → acp-J4WDYGRX.js} +26 -26
- package/dist/{agentsValidate-XWFIEHJ2.js → agentsValidate-UBOER2IN.js} +7 -7
- package/dist/{ask-4ACYU23S.js → ask-MGUO3L35.js} +25 -25
- package/dist/{autoUpdater-O2WHHSGP.js → autoUpdater-2GS6LRPK.js} +3 -3
- package/dist/{chunk-FHOCKJOW.js → chunk-2C43OXE7.js} +2 -2
- package/dist/{chunk-YYPJWXSA.js → chunk-2EFL22PV.js} +1 -1
- package/dist/{chunk-QF6YDTME.js → chunk-3LMXSKZ7.js} +1 -1
- package/dist/{chunk-B26ZUMJI.js → chunk-53A4JHFW.js} +3 -3
- package/dist/{chunk-EENI5HG7.js → chunk-A7X6OCZE.js} +1 -1
- package/dist/{chunk-GPKVTIYJ.js → chunk-BWYKUDJR.js} +3 -3
- package/dist/{chunk-LC4CL3YJ.js → chunk-DEF3KFP7.js} +1 -1
- package/dist/{chunk-EXPWPWA4.js → chunk-F3COCCAE.js} +3 -3
- package/dist/{chunk-H5BCUDEN.js → chunk-GZTCXXSS.js} +1 -1
- package/dist/{chunk-MXSB7IOZ.js → chunk-HSJ6HYAO.js} +1 -1
- package/dist/{chunk-SBE6Y327.js → chunk-IIFUDVGS.js} +3 -3
- package/dist/{chunk-3C73U2IU.js → chunk-IM33F5CM.js} +1 -1
- package/dist/{chunk-BUI6KGVA.js → chunk-JWXQNBBA.js} +1 -1
- package/dist/{chunk-S5Y5IF2H.js → chunk-LOIZNQOU.js} +2 -2
- package/dist/{chunk-ULVAAZ2U.js → chunk-OJIMOLIC.js} +38 -32
- package/dist/{chunk-ULVAAZ2U.js.map → chunk-OJIMOLIC.js.map} +1 -1
- package/dist/{chunk-WWLFALT7.js → chunk-OZHBEG7U.js} +3 -3
- package/dist/{chunk-DY5D4SS7.js → chunk-QH2M65BR.js} +2 -2
- package/dist/{chunk-U224EQOS.js → chunk-RUXIBQ3B.js} +4 -4
- package/dist/{chunk-S3ZAJPYZ.js → chunk-UYRR6F5S.js} +2 -2
- package/dist/{chunk-Y7VZUSIM.js → chunk-V5U6BHT2.js} +3 -3
- package/dist/{chunk-YVNBXMIP.js → chunk-VHS2MZQS.js} +2 -2
- package/dist/{chunk-3U5X4VWP.js → chunk-VKI7ORIO.js} +11 -11
- package/dist/{chunk-XOMW5QTV.js → chunk-VQSCECTS.js} +3 -3
- package/dist/{chunk-L6WKZEK4.js → chunk-WWDVA4NV.js} +4 -4
- package/dist/{chunk-T6VKT5FR.js → chunk-XS6PU75S.js} +1 -1
- package/dist/{chunk-QTX7AJFQ.js → chunk-XXU2NVOE.js} +2 -2
- package/dist/{chunk-TSGGSPYD.js → chunk-ZYSVG4X3.js} +1 -1
- package/dist/{chunk-TSGGSPYD.js.map → chunk-ZYSVG4X3.js.map} +1 -1
- package/dist/{cli-BW34VKCN.js → cli-KZGF3FV5.js} +82 -82
- package/dist/commands-AVEBLFVS.js +46 -0
- package/dist/{config-XKRCXCSS.js → config-GTJWCNPF.js} +4 -4
- package/dist/{context-T5CR3RP6.js → context-WF3TTXQU.js} +5 -5
- package/dist/{customCommands-OCUMXZDN.js → customCommands-QOWK57EX.js} +4 -4
- package/dist/{env-DYDNFB4D.js → env-37BAP7QF.js} +2 -2
- package/dist/gateway-IZYO6YFJ.js +1170 -0
- package/dist/gateway-IZYO6YFJ.js.map +7 -0
- package/dist/index.js +5 -3
- package/dist/index.js.map +2 -2
- package/dist/{kodeAgentSessionLoad-ZKR2VGHO.js → kodeAgentSessionLoad-6F7SJXBC.js} +4 -4
- package/dist/{kodeAgentSessionResume-NFZCTIBZ.js → kodeAgentSessionResume-UEEDRJ3N.js} +4 -4
- package/dist/{kodeAgentStreamJsonSession-6CKTK6AF.js → kodeAgentStreamJsonSession-XC3IPREZ.js} +1 -1
- package/dist/{kodeHooks-5RXJRDCY.js → kodeHooks-V36SHCTC.js} +4 -4
- package/dist/{llm-QJA3QMXR.js → llm-CYUDKJNR.js} +26 -26
- package/dist/{llmLazy-VCEV22DK.js → llmLazy-IXVVBRTN.js} +1 -1
- package/dist/{loader-CKNYMPCZ.js → loader-OEJ6C3TN.js} +4 -4
- package/dist/{mcp-2SZTOKZX.js → mcp-KE3SILMX.js} +7 -7
- package/dist/{mentionProcessor-O7NWOH6S.js → mentionProcessor-GAU2WAYB.js} +5 -5
- package/dist/{messages-PRKIHXMK.js → messages-WCSGGSEU.js} +1 -1
- package/dist/{model-WG6RA25G.js → model-4TQIV5J2.js} +5 -5
- package/dist/{openai-VQLYFQ6B.js → openai-KTZV6F7N.js} +5 -5
- package/dist/{outputStyles-VQ57E3B6.js → outputStyles-WX5RYQOA.js} +4 -4
- package/dist/{pluginRuntime-NWOLU73K.js → pluginRuntime-JXMJZ2LC.js} +6 -6
- package/dist/{pluginValidation-R7B6QQ6T.js → pluginValidation-JNQZYLUP.js} +6 -6
- package/dist/prompts-LE6GK75N.js +48 -0
- package/dist/query-GGIP6PWG.js +50 -0
- package/dist/{ripgrep-SVBVC46X.js → ripgrep-KDPQAMB2.js} +3 -3
- package/dist/{skillMarketplace-5Z7Y6FTD.js → skillMarketplace-IXAGP3Q2.js} +3 -3
- package/dist/{state-X3R7BV7A.js → state-MSCYLB6Y.js} +2 -2
- package/dist/{theme-4VA64EWF.js → theme-GAMFOLBW.js} +5 -5
- package/dist/{toolPermissionSettings-35DJQEKG.js → toolPermissionSettings-EUZKGZU2.js} +6 -6
- package/dist/tools-3HOUIDM3.js +47 -0
- package/dist/{userInput-NID2UYXG.js → userInput-LJL4CVOB.js} +27 -27
- package/package.json +1 -1
- package/dist/REPL-74GZVSMY.js +0 -42
- package/dist/commands-4CNZZBTE.js +0 -46
- package/dist/prompts-W4V4Y67M.js +0 -48
- package/dist/query-GFFKKURO.js +0 -50
- package/dist/tools-USOBTPOI.js +0 -47
- /package/dist/{REPL-74GZVSMY.js.map → REPL-IAK7ZN2Z.js.map} +0 -0
- /package/dist/{acp-A55ZRIPP.js.map → acp-J4WDYGRX.js.map} +0 -0
- /package/dist/{agentsValidate-XWFIEHJ2.js.map → agentsValidate-UBOER2IN.js.map} +0 -0
- /package/dist/{ask-4ACYU23S.js.map → ask-MGUO3L35.js.map} +0 -0
- /package/dist/{autoUpdater-O2WHHSGP.js.map → autoUpdater-2GS6LRPK.js.map} +0 -0
- /package/dist/{chunk-FHOCKJOW.js.map → chunk-2C43OXE7.js.map} +0 -0
- /package/dist/{chunk-YYPJWXSA.js.map → chunk-2EFL22PV.js.map} +0 -0
- /package/dist/{chunk-QF6YDTME.js.map → chunk-3LMXSKZ7.js.map} +0 -0
- /package/dist/{chunk-B26ZUMJI.js.map → chunk-53A4JHFW.js.map} +0 -0
- /package/dist/{chunk-EENI5HG7.js.map → chunk-A7X6OCZE.js.map} +0 -0
- /package/dist/{chunk-GPKVTIYJ.js.map → chunk-BWYKUDJR.js.map} +0 -0
- /package/dist/{chunk-LC4CL3YJ.js.map → chunk-DEF3KFP7.js.map} +0 -0
- /package/dist/{chunk-EXPWPWA4.js.map → chunk-F3COCCAE.js.map} +0 -0
- /package/dist/{chunk-H5BCUDEN.js.map → chunk-GZTCXXSS.js.map} +0 -0
- /package/dist/{chunk-MXSB7IOZ.js.map → chunk-HSJ6HYAO.js.map} +0 -0
- /package/dist/{chunk-SBE6Y327.js.map → chunk-IIFUDVGS.js.map} +0 -0
- /package/dist/{chunk-3C73U2IU.js.map → chunk-IM33F5CM.js.map} +0 -0
- /package/dist/{chunk-BUI6KGVA.js.map → chunk-JWXQNBBA.js.map} +0 -0
- /package/dist/{chunk-S5Y5IF2H.js.map → chunk-LOIZNQOU.js.map} +0 -0
- /package/dist/{chunk-WWLFALT7.js.map → chunk-OZHBEG7U.js.map} +0 -0
- /package/dist/{chunk-DY5D4SS7.js.map → chunk-QH2M65BR.js.map} +0 -0
- /package/dist/{chunk-U224EQOS.js.map → chunk-RUXIBQ3B.js.map} +0 -0
- /package/dist/{chunk-S3ZAJPYZ.js.map → chunk-UYRR6F5S.js.map} +0 -0
- /package/dist/{chunk-Y7VZUSIM.js.map → chunk-V5U6BHT2.js.map} +0 -0
- /package/dist/{chunk-YVNBXMIP.js.map → chunk-VHS2MZQS.js.map} +0 -0
- /package/dist/{chunk-3U5X4VWP.js.map → chunk-VKI7ORIO.js.map} +0 -0
- /package/dist/{chunk-XOMW5QTV.js.map → chunk-VQSCECTS.js.map} +0 -0
- /package/dist/{chunk-L6WKZEK4.js.map → chunk-WWDVA4NV.js.map} +0 -0
- /package/dist/{chunk-T6VKT5FR.js.map → chunk-XS6PU75S.js.map} +0 -0
- /package/dist/{chunk-QTX7AJFQ.js.map → chunk-XXU2NVOE.js.map} +0 -0
- /package/dist/{cli-BW34VKCN.js.map → cli-KZGF3FV5.js.map} +0 -0
- /package/dist/{commands-4CNZZBTE.js.map → commands-AVEBLFVS.js.map} +0 -0
- /package/dist/{config-XKRCXCSS.js.map → config-GTJWCNPF.js.map} +0 -0
- /package/dist/{context-T5CR3RP6.js.map → context-WF3TTXQU.js.map} +0 -0
- /package/dist/{customCommands-OCUMXZDN.js.map → customCommands-QOWK57EX.js.map} +0 -0
- /package/dist/{env-DYDNFB4D.js.map → env-37BAP7QF.js.map} +0 -0
- /package/dist/{kodeAgentSessionLoad-ZKR2VGHO.js.map → kodeAgentSessionLoad-6F7SJXBC.js.map} +0 -0
- /package/dist/{kodeAgentSessionResume-NFZCTIBZ.js.map → kodeAgentSessionResume-UEEDRJ3N.js.map} +0 -0
- /package/dist/{kodeAgentStreamJsonSession-6CKTK6AF.js.map → kodeAgentStreamJsonSession-XC3IPREZ.js.map} +0 -0
- /package/dist/{kodeHooks-5RXJRDCY.js.map → kodeHooks-V36SHCTC.js.map} +0 -0
- /package/dist/{llm-QJA3QMXR.js.map → llm-CYUDKJNR.js.map} +0 -0
- /package/dist/{llmLazy-VCEV22DK.js.map → llmLazy-IXVVBRTN.js.map} +0 -0
- /package/dist/{loader-CKNYMPCZ.js.map → loader-OEJ6C3TN.js.map} +0 -0
- /package/dist/{mcp-2SZTOKZX.js.map → mcp-KE3SILMX.js.map} +0 -0
- /package/dist/{mentionProcessor-O7NWOH6S.js.map → mentionProcessor-GAU2WAYB.js.map} +0 -0
- /package/dist/{messages-PRKIHXMK.js.map → messages-WCSGGSEU.js.map} +0 -0
- /package/dist/{model-WG6RA25G.js.map → model-4TQIV5J2.js.map} +0 -0
- /package/dist/{openai-VQLYFQ6B.js.map → openai-KTZV6F7N.js.map} +0 -0
- /package/dist/{outputStyles-VQ57E3B6.js.map → outputStyles-WX5RYQOA.js.map} +0 -0
- /package/dist/{pluginRuntime-NWOLU73K.js.map → pluginRuntime-JXMJZ2LC.js.map} +0 -0
- /package/dist/{pluginValidation-R7B6QQ6T.js.map → pluginValidation-JNQZYLUP.js.map} +0 -0
- /package/dist/{prompts-W4V4Y67M.js.map → prompts-LE6GK75N.js.map} +0 -0
- /package/dist/{query-GFFKKURO.js.map → query-GGIP6PWG.js.map} +0 -0
- /package/dist/{ripgrep-SVBVC46X.js.map → ripgrep-KDPQAMB2.js.map} +0 -0
- /package/dist/{skillMarketplace-5Z7Y6FTD.js.map → skillMarketplace-IXAGP3Q2.js.map} +0 -0
- /package/dist/{state-X3R7BV7A.js.map → state-MSCYLB6Y.js.map} +0 -0
- /package/dist/{theme-4VA64EWF.js.map → theme-GAMFOLBW.js.map} +0 -0
- /package/dist/{toolPermissionSettings-35DJQEKG.js.map → toolPermissionSettings-EUZKGZU2.js.map} +0 -0
- /package/dist/{tools-USOBTPOI.js.map → tools-3HOUIDM3.js.map} +0 -0
- /package/dist/{userInput-NID2UYXG.js.map → userInput-LJL4CVOB.js.map} +0 -0
|
@@ -0,0 +1,1170 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { createRequire as __newcrawCreateRequire } from "node:module";
|
|
3
|
+
const require = __newcrawCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
getDomain,
|
|
6
|
+
getIdentity,
|
|
7
|
+
setIdentity
|
|
8
|
+
} from "./chunk-OJIMOLIC.js";
|
|
9
|
+
import "./chunk-XS6PU75S.js";
|
|
10
|
+
import "./chunk-WWDVA4NV.js";
|
|
11
|
+
import "./chunk-VQSCECTS.js";
|
|
12
|
+
import "./chunk-HSJ6HYAO.js";
|
|
13
|
+
import "./chunk-3LMXSKZ7.js";
|
|
14
|
+
import "./chunk-XXU2NVOE.js";
|
|
15
|
+
import "./chunk-JX5ZQYTQ.js";
|
|
16
|
+
import "./chunk-XJQATPV7.js";
|
|
17
|
+
import "./chunk-V5U6BHT2.js";
|
|
18
|
+
import "./chunk-7ZFLZNOW.js";
|
|
19
|
+
import "./chunk-VHS2MZQS.js";
|
|
20
|
+
import "./chunk-QH2M65BR.js";
|
|
21
|
+
import "./chunk-RUXIBQ3B.js";
|
|
22
|
+
import "./chunk-JWXQNBBA.js";
|
|
23
|
+
import "./chunk-2C43OXE7.js";
|
|
24
|
+
import "./chunk-BWYKUDJR.js";
|
|
25
|
+
import "./chunk-2EFL22PV.js";
|
|
26
|
+
import "./chunk-U7S4MEYP.js";
|
|
27
|
+
import "./chunk-UYRR6F5S.js";
|
|
28
|
+
import "./chunk-TY3CCSAT.js";
|
|
29
|
+
import "./chunk-LOIZNQOU.js";
|
|
30
|
+
import "./chunk-INJSFEKL.js";
|
|
31
|
+
import "./chunk-2UO3BFZH.js";
|
|
32
|
+
import {
|
|
33
|
+
createAssistantMessage,
|
|
34
|
+
createUserMessage
|
|
35
|
+
} from "./chunk-A7X6OCZE.js";
|
|
36
|
+
import "./chunk-F3COCCAE.js";
|
|
37
|
+
import "./chunk-53A4JHFW.js";
|
|
38
|
+
import "./chunk-DEF3KFP7.js";
|
|
39
|
+
import "./chunk-IIFUDVGS.js";
|
|
40
|
+
import "./chunk-KQSHIOZK.js";
|
|
41
|
+
import "./chunk-GZTCXXSS.js";
|
|
42
|
+
import "./chunk-IM33F5CM.js";
|
|
43
|
+
import "./chunk-ZYSVG4X3.js";
|
|
44
|
+
import "./chunk-755HIAI3.js";
|
|
45
|
+
import "./chunk-AXWJI6N5.js";
|
|
46
|
+
|
|
47
|
+
// src/sessions/manager.ts
|
|
48
|
+
var SessionManager = class {
|
|
49
|
+
constructor(storage) {
|
|
50
|
+
this.storage = storage;
|
|
51
|
+
}
|
|
52
|
+
async getOrCreate(key) {
|
|
53
|
+
const existing = await this.storage.load(key);
|
|
54
|
+
if (existing) return existing;
|
|
55
|
+
return this.createNew(key);
|
|
56
|
+
}
|
|
57
|
+
async save(session) {
|
|
58
|
+
await this.storage.save(session.key, session);
|
|
59
|
+
}
|
|
60
|
+
async get(key) {
|
|
61
|
+
return this.storage.load(key);
|
|
62
|
+
}
|
|
63
|
+
async delete(key) {
|
|
64
|
+
await this.storage.delete(key);
|
|
65
|
+
}
|
|
66
|
+
async listKeys() {
|
|
67
|
+
return this.storage.list();
|
|
68
|
+
}
|
|
69
|
+
createNew(key) {
|
|
70
|
+
return {
|
|
71
|
+
key,
|
|
72
|
+
messages: [],
|
|
73
|
+
createdAt: Date.now(),
|
|
74
|
+
updatedAt: Date.now(),
|
|
75
|
+
metadata: {}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/gateway/agentCore.ts
|
|
81
|
+
import { cwd as processCwd } from "process";
|
|
82
|
+
var gatewayPermissionHandler = async (tool, _input, _context, _assistantMessage) => {
|
|
83
|
+
if (tool.isReadOnly()) {
|
|
84
|
+
return { result: true };
|
|
85
|
+
}
|
|
86
|
+
return { result: true };
|
|
87
|
+
};
|
|
88
|
+
async function loadGatewayTools(domainId) {
|
|
89
|
+
const { getTools } = await import("./tools-3HOUIDM3.js");
|
|
90
|
+
const domain = getDomain(domainId);
|
|
91
|
+
const allTools = await getTools();
|
|
92
|
+
if (!domain) return allTools;
|
|
93
|
+
const allowedNames = new Set(domain.tools());
|
|
94
|
+
const filtered = allTools.filter((t) => allowedNames.has(t.name));
|
|
95
|
+
return filtered.length > 0 ? filtered : allTools;
|
|
96
|
+
}
|
|
97
|
+
function convertSessionHistory(history) {
|
|
98
|
+
return history.map(
|
|
99
|
+
(msg) => msg.role === "user" ? createUserMessage(msg.content) : createAssistantMessage(msg.content)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
async function gatewayQuery(options) {
|
|
103
|
+
const { ask } = await import("./ask-MGUO3L35.js");
|
|
104
|
+
const { setCwd } = await import("./state-MSCYLB6Y.js");
|
|
105
|
+
const identity = getIdentity();
|
|
106
|
+
const domainId = options.domain ?? identity.domain;
|
|
107
|
+
const cwd = options.cwd ?? processCwd();
|
|
108
|
+
await setCwd(cwd);
|
|
109
|
+
const tools = options.tools ?? await loadGatewayTools(domainId);
|
|
110
|
+
const result = await ask({
|
|
111
|
+
commands: [],
|
|
112
|
+
safeMode: options.safeMode ?? false,
|
|
113
|
+
hasPermissionsToUseTool: gatewayPermissionHandler,
|
|
114
|
+
messageLogName: `gateway-${Date.now()}`,
|
|
115
|
+
prompt: options.prompt,
|
|
116
|
+
cwd,
|
|
117
|
+
tools,
|
|
118
|
+
verbose: false,
|
|
119
|
+
initialMessages: options.sessionMessages,
|
|
120
|
+
persistSession: false,
|
|
121
|
+
domain: domainId
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
resultText: result.resultText,
|
|
125
|
+
messages: [],
|
|
126
|
+
totalCost: result.totalCost
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function* gatewayQueryStream(options) {
|
|
130
|
+
const { query } = await import("./query-GGIP6PWG.js");
|
|
131
|
+
const { getSystemPrompt } = await import("./prompts-LE6GK75N.js");
|
|
132
|
+
const { getContext } = await import("./context-WF3TTXQU.js");
|
|
133
|
+
const { setCwd } = await import("./state-MSCYLB6Y.js");
|
|
134
|
+
const identity = getIdentity();
|
|
135
|
+
const domainId = options.domain ?? identity.domain;
|
|
136
|
+
const cwd = options.cwd ?? processCwd();
|
|
137
|
+
await setCwd(cwd);
|
|
138
|
+
const tools = options.tools ?? await loadGatewayTools(domainId);
|
|
139
|
+
const [systemPrompt, context] = await Promise.all([
|
|
140
|
+
getSystemPrompt({ domain: domainId }),
|
|
141
|
+
getContext()
|
|
142
|
+
]);
|
|
143
|
+
const userMessage = createUserMessage(options.prompt);
|
|
144
|
+
const messages = [...options.sessionMessages ?? [], userMessage];
|
|
145
|
+
const abortController = new AbortController();
|
|
146
|
+
const seenToolUseIds = /* @__PURE__ */ new Set();
|
|
147
|
+
for await (const msg of query(
|
|
148
|
+
messages,
|
|
149
|
+
systemPrompt,
|
|
150
|
+
context,
|
|
151
|
+
gatewayPermissionHandler,
|
|
152
|
+
{
|
|
153
|
+
options: {
|
|
154
|
+
commands: [],
|
|
155
|
+
tools,
|
|
156
|
+
verbose: false,
|
|
157
|
+
safeMode: options.safeMode ?? false,
|
|
158
|
+
forkNumber: 0,
|
|
159
|
+
messageLogName: `gateway-${Date.now()}`,
|
|
160
|
+
maxThinkingTokens: 0,
|
|
161
|
+
persistSession: false,
|
|
162
|
+
shouldAvoidPermissionPrompts: true
|
|
163
|
+
},
|
|
164
|
+
abortController,
|
|
165
|
+
messageId: void 0,
|
|
166
|
+
readFileTimestamps: {},
|
|
167
|
+
setToolJSX: () => {
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
)) {
|
|
171
|
+
if (msg.type === "assistant") {
|
|
172
|
+
for (const block of msg.message.content) {
|
|
173
|
+
if (block.type === "text" && block.text) {
|
|
174
|
+
yield { type: "text", content: block.text };
|
|
175
|
+
} else if (block.type === "thinking" && block.thinking) {
|
|
176
|
+
yield { type: "thinking", content: block.thinking };
|
|
177
|
+
} else if (block.type === "tool_use") {
|
|
178
|
+
seenToolUseIds.add(block.id);
|
|
179
|
+
const inputBrief = JSON.stringify(block.input).slice(0, 200);
|
|
180
|
+
yield { type: "tool_call", content: `${block.name}(${inputBrief})` };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} else if (msg.type === "progress") {
|
|
184
|
+
for (const block of msg.content.message.content) {
|
|
185
|
+
if (block.type === "tool_use" && !seenToolUseIds.has(block.id)) {
|
|
186
|
+
seenToolUseIds.add(block.id);
|
|
187
|
+
const inputBrief = JSON.stringify(block.input).slice(0, 200);
|
|
188
|
+
yield { type: "tool_call", content: `${block.name}(${inputBrief})` };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else if (msg.type === "user") {
|
|
192
|
+
const content = msg.message.content;
|
|
193
|
+
if (Array.isArray(content)) {
|
|
194
|
+
for (const block of content) {
|
|
195
|
+
if (block.type === "tool_result") {
|
|
196
|
+
const raw = typeof block.content === "string" ? block.content : JSON.stringify(block.content);
|
|
197
|
+
yield { type: "tool_result", content: raw.slice(0, 500) };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
yield { type: "done", content: "" };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/gateway/middleware.ts
|
|
207
|
+
function composeMiddleware(middlewares) {
|
|
208
|
+
return async (ctx, next) => {
|
|
209
|
+
let index = -1;
|
|
210
|
+
async function dispatch(i) {
|
|
211
|
+
if (i <= index) throw new Error("next() called multiple times");
|
|
212
|
+
index = i;
|
|
213
|
+
const fn = i < middlewares.length ? middlewares[i] : next;
|
|
214
|
+
await fn(ctx, () => dispatch(i + 1));
|
|
215
|
+
}
|
|
216
|
+
await dispatch(0);
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function loggingMiddleware() {
|
|
220
|
+
return async (ctx, next) => {
|
|
221
|
+
const { channelId, message } = ctx;
|
|
222
|
+
const preview = message.text.length > 80 ? message.text.slice(0, 80) + "..." : message.text;
|
|
223
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] ${channelId}:${message.sessionKey} \u2190 "${preview}"`);
|
|
224
|
+
await next();
|
|
225
|
+
const elapsed = Date.now() - ctx.startTime;
|
|
226
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] ${channelId}:${message.sessionKey} \u2192 done (${elapsed}ms)`);
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function rateLimitMiddleware(options = {}) {
|
|
230
|
+
const windowMs = options.windowMs ?? 6e4;
|
|
231
|
+
const maxRequests = options.maxRequests ?? 30;
|
|
232
|
+
const hits = /* @__PURE__ */ new Map();
|
|
233
|
+
return async (ctx, next) => {
|
|
234
|
+
const key = `${ctx.message.sessionKey}`;
|
|
235
|
+
const now = Date.now();
|
|
236
|
+
const timestamps = hits.get(key) ?? [];
|
|
237
|
+
const windowStart = now - windowMs;
|
|
238
|
+
const recent = timestamps.filter((t) => t > windowStart);
|
|
239
|
+
if (recent.length >= maxRequests) {
|
|
240
|
+
ctx.metadata.rateLimited = true;
|
|
241
|
+
throw new Error(`Rate limit exceeded: ${maxRequests} requests per ${windowMs / 1e3}s`);
|
|
242
|
+
}
|
|
243
|
+
recent.push(now);
|
|
244
|
+
hits.set(key, recent);
|
|
245
|
+
if (hits.size > 1e4) {
|
|
246
|
+
for (const [k, v] of hits) {
|
|
247
|
+
const filtered = v.filter((t) => t > windowStart);
|
|
248
|
+
if (filtered.length === 0) hits.delete(k);
|
|
249
|
+
else hits.set(k, filtered);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
await next();
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function maxConcurrencyMiddleware(options = {}) {
|
|
256
|
+
const max = options.maxPerSession ?? 1;
|
|
257
|
+
const active = /* @__PURE__ */ new Map();
|
|
258
|
+
return async (ctx, next) => {
|
|
259
|
+
const key = ctx.message.sessionKey;
|
|
260
|
+
const current = active.get(key) ?? 0;
|
|
261
|
+
if (current >= max) {
|
|
262
|
+
throw new Error("Another request is already in progress for this session");
|
|
263
|
+
}
|
|
264
|
+
active.set(key, current + 1);
|
|
265
|
+
try {
|
|
266
|
+
await next();
|
|
267
|
+
} finally {
|
|
268
|
+
const val = active.get(key) ?? 1;
|
|
269
|
+
if (val <= 1) active.delete(key);
|
|
270
|
+
else active.set(key, val - 1);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/channels/http.ts
|
|
276
|
+
import { createServer } from "http";
|
|
277
|
+
import { randomUUID } from "crypto";
|
|
278
|
+
import { resolve, join, extname } from "path";
|
|
279
|
+
import { readFile, stat } from "fs/promises";
|
|
280
|
+
var MIME_TYPES = {
|
|
281
|
+
".html": "text/html; charset=utf-8",
|
|
282
|
+
".js": "application/javascript; charset=utf-8",
|
|
283
|
+
".css": "text/css; charset=utf-8",
|
|
284
|
+
".json": "application/json; charset=utf-8",
|
|
285
|
+
".png": "image/png",
|
|
286
|
+
".jpg": "image/jpeg",
|
|
287
|
+
".jpeg": "image/jpeg",
|
|
288
|
+
".gif": "image/gif",
|
|
289
|
+
".svg": "image/svg+xml",
|
|
290
|
+
".ico": "image/x-icon",
|
|
291
|
+
".woff": "font/woff",
|
|
292
|
+
".woff2": "font/woff2",
|
|
293
|
+
".ttf": "font/ttf",
|
|
294
|
+
".webp": "image/webp"
|
|
295
|
+
};
|
|
296
|
+
var HttpChannel = class {
|
|
297
|
+
constructor(config) {
|
|
298
|
+
this.config = config;
|
|
299
|
+
}
|
|
300
|
+
id = "http";
|
|
301
|
+
name = "HTTP API";
|
|
302
|
+
server = null;
|
|
303
|
+
handler = null;
|
|
304
|
+
streamHandler = null;
|
|
305
|
+
pendingResponses = /* @__PURE__ */ new Map();
|
|
306
|
+
onMessage(handler) {
|
|
307
|
+
this.handler = handler;
|
|
308
|
+
}
|
|
309
|
+
onStreamMessage(handler) {
|
|
310
|
+
this.streamHandler = handler;
|
|
311
|
+
}
|
|
312
|
+
async start() {
|
|
313
|
+
this.server = createServer((req, res) => this.handleRequest(req, res));
|
|
314
|
+
await new Promise((resolve2) => {
|
|
315
|
+
this.server.listen(this.config.port, this.config.host ?? "0.0.0.0", resolve2);
|
|
316
|
+
});
|
|
317
|
+
console.log(`[HTTP] Listening on ${this.config.host ?? "0.0.0.0"}:${this.config.port}`);
|
|
318
|
+
}
|
|
319
|
+
async stop() {
|
|
320
|
+
if (this.server) {
|
|
321
|
+
await new Promise((resolve2) => this.server.close(() => resolve2()));
|
|
322
|
+
this.server = null;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async sendResponse(requestId, response) {
|
|
326
|
+
const pending = this.pendingResponses.get(requestId);
|
|
327
|
+
if (pending) {
|
|
328
|
+
pending.push(response);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
setCorsHeaders(res) {
|
|
332
|
+
const origin = this.config.corsOrigin ?? "*";
|
|
333
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
334
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
335
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
336
|
+
}
|
|
337
|
+
checkAuth(req, res) {
|
|
338
|
+
if (!this.config.authToken) return true;
|
|
339
|
+
const auth = req.headers.authorization;
|
|
340
|
+
if (auth !== `Bearer ${this.config.authToken}`) {
|
|
341
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
342
|
+
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
async handleRequest(req, res) {
|
|
348
|
+
this.setCorsHeaders(res);
|
|
349
|
+
if (req.method === "OPTIONS") {
|
|
350
|
+
res.writeHead(204);
|
|
351
|
+
res.end();
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (req.method === "GET" && req.url === "/health") {
|
|
355
|
+
res.setHeader("Content-Type", "application/json");
|
|
356
|
+
res.writeHead(200);
|
|
357
|
+
res.end(JSON.stringify({ status: "ok", timestamp: Date.now() }));
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (req.method === "POST" && req.url === "/api/chat") {
|
|
361
|
+
await this.handleChat(req, res);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (req.method === "POST" && req.url === "/api/chat/stream") {
|
|
365
|
+
await this.handleChatStream(req, res);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (req.method === "GET" && this.config.webDistDir) {
|
|
369
|
+
const served = await this.serveStatic(req, res);
|
|
370
|
+
if (served) return;
|
|
371
|
+
}
|
|
372
|
+
res.setHeader("Content-Type", "application/json");
|
|
373
|
+
res.writeHead(404);
|
|
374
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
375
|
+
}
|
|
376
|
+
async serveStatic(req, res) {
|
|
377
|
+
const distDir = this.config.webDistDir;
|
|
378
|
+
if (!distDir) return false;
|
|
379
|
+
const urlPath = new URL(req.url ?? "/", `http://${req.headers.host}`).pathname;
|
|
380
|
+
const safePath = urlPath.replace(/\.\./g, "").replace(/\/+/g, "/");
|
|
381
|
+
const candidates = [
|
|
382
|
+
join(distDir, safePath),
|
|
383
|
+
join(distDir, safePath, "index.html")
|
|
384
|
+
];
|
|
385
|
+
for (const filePath of candidates) {
|
|
386
|
+
const resolved = resolve(filePath);
|
|
387
|
+
if (!resolved.startsWith(resolve(distDir))) continue;
|
|
388
|
+
try {
|
|
389
|
+
const fileStat = await stat(resolved);
|
|
390
|
+
if (fileStat.isFile()) {
|
|
391
|
+
const ext = extname(resolved);
|
|
392
|
+
const mime = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
393
|
+
const content = await readFile(resolved);
|
|
394
|
+
res.setHeader("Content-Type", mime);
|
|
395
|
+
if (ext !== ".html") {
|
|
396
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
397
|
+
}
|
|
398
|
+
res.writeHead(200);
|
|
399
|
+
res.end(content);
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
const indexPath = join(distDir, "index.html");
|
|
407
|
+
const content = await readFile(indexPath);
|
|
408
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
409
|
+
res.writeHead(200);
|
|
410
|
+
res.end(content);
|
|
411
|
+
return true;
|
|
412
|
+
} catch {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async handleChat(req, res) {
|
|
417
|
+
res.setHeader("Content-Type", "application/json");
|
|
418
|
+
if (!this.checkAuth(req, res)) return;
|
|
419
|
+
try {
|
|
420
|
+
const body = await readBody(req);
|
|
421
|
+
const { message, userId = "default", sessionKey } = JSON.parse(body);
|
|
422
|
+
if (!message || typeof message !== "string") {
|
|
423
|
+
res.writeHead(400);
|
|
424
|
+
res.end(JSON.stringify({ error: 'Missing "message" field' }));
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (!this.handler) {
|
|
428
|
+
res.writeHead(503);
|
|
429
|
+
res.end(JSON.stringify({ error: "No handler registered" }));
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const requestId = randomUUID();
|
|
433
|
+
this.pendingResponses.set(requestId, []);
|
|
434
|
+
await this.handler({
|
|
435
|
+
channelId: "http",
|
|
436
|
+
userId: requestId,
|
|
437
|
+
sessionKey: sessionKey ?? `http:${userId}`,
|
|
438
|
+
text: message,
|
|
439
|
+
metadata: { originalUserId: userId, requestId }
|
|
440
|
+
});
|
|
441
|
+
const responses = this.pendingResponses.get(requestId) ?? [];
|
|
442
|
+
this.pendingResponses.delete(requestId);
|
|
443
|
+
const textParts = responses.filter((r) => r.type === "text").map((r) => r.content);
|
|
444
|
+
res.writeHead(200);
|
|
445
|
+
res.end(JSON.stringify({
|
|
446
|
+
response: textParts.join("\n"),
|
|
447
|
+
details: responses.filter((r) => r.type !== "done")
|
|
448
|
+
}));
|
|
449
|
+
} catch (err) {
|
|
450
|
+
res.writeHead(500);
|
|
451
|
+
res.end(JSON.stringify({ error: err.message ?? "Internal server error" }));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
async handleChatStream(req, res) {
|
|
455
|
+
if (!this.checkAuth(req, res)) return;
|
|
456
|
+
try {
|
|
457
|
+
const body = await readBody(req);
|
|
458
|
+
const { message, userId = "default", sessionKey } = JSON.parse(body);
|
|
459
|
+
if (!message || typeof message !== "string") {
|
|
460
|
+
res.setHeader("Content-Type", "application/json");
|
|
461
|
+
res.writeHead(400);
|
|
462
|
+
res.end(JSON.stringify({ error: 'Missing "message" field' }));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (!this.streamHandler) {
|
|
466
|
+
res.setHeader("Content-Type", "application/json");
|
|
467
|
+
res.writeHead(503);
|
|
468
|
+
res.end(JSON.stringify({ error: "Streaming not available" }));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
res.writeHead(200, {
|
|
472
|
+
"Content-Type": "text/event-stream",
|
|
473
|
+
"Cache-Control": "no-cache",
|
|
474
|
+
"Connection": "keep-alive",
|
|
475
|
+
"X-Accel-Buffering": "no"
|
|
476
|
+
});
|
|
477
|
+
const unifiedMsg = {
|
|
478
|
+
channelId: "http",
|
|
479
|
+
userId,
|
|
480
|
+
sessionKey: sessionKey ?? `http:${userId}`,
|
|
481
|
+
text: message,
|
|
482
|
+
metadata: { requestId: randomUUID() }
|
|
483
|
+
};
|
|
484
|
+
await this.streamHandler(unifiedMsg, res);
|
|
485
|
+
} catch (err) {
|
|
486
|
+
if (!res.headersSent) {
|
|
487
|
+
res.setHeader("Content-Type", "application/json");
|
|
488
|
+
res.writeHead(500);
|
|
489
|
+
res.end(JSON.stringify({ error: err.message ?? "Internal server error" }));
|
|
490
|
+
} else {
|
|
491
|
+
writeSseEvent(res, "error", { error: err.message ?? "Internal server error" });
|
|
492
|
+
res.end();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
function writeSseEvent(res, event, data) {
|
|
498
|
+
res.write(`event: ${event}
|
|
499
|
+
data: ${JSON.stringify(data)}
|
|
500
|
+
|
|
501
|
+
`);
|
|
502
|
+
}
|
|
503
|
+
function readBody(req) {
|
|
504
|
+
return new Promise((resolve2, reject) => {
|
|
505
|
+
const chunks = [];
|
|
506
|
+
let size = 0;
|
|
507
|
+
const MAX_BODY_SIZE = 1024 * 1024;
|
|
508
|
+
req.on("data", (chunk) => {
|
|
509
|
+
size += chunk.length;
|
|
510
|
+
if (size > MAX_BODY_SIZE) {
|
|
511
|
+
req.destroy();
|
|
512
|
+
reject(new Error("Request body too large"));
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
chunks.push(chunk);
|
|
516
|
+
});
|
|
517
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8")));
|
|
518
|
+
req.on("error", reject);
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// src/gateway/gateway.ts
|
|
523
|
+
var Gateway = class {
|
|
524
|
+
channels = /* @__PURE__ */ new Map();
|
|
525
|
+
sessions;
|
|
526
|
+
config;
|
|
527
|
+
middlewareChain = null;
|
|
528
|
+
constructor(config) {
|
|
529
|
+
this.config = config;
|
|
530
|
+
this.sessions = new SessionManager(config.storage);
|
|
531
|
+
if (config.middleware && config.middleware.length > 0) {
|
|
532
|
+
this.middlewareChain = composeMiddleware(config.middleware);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
registerChannel(channel) {
|
|
536
|
+
if (this.channels.has(channel.id)) {
|
|
537
|
+
throw new Error(`Channel "${channel.id}" is already registered`);
|
|
538
|
+
}
|
|
539
|
+
this.channels.set(channel.id, channel);
|
|
540
|
+
channel.onMessage((msg) => this.handleMessage(channel, msg));
|
|
541
|
+
if (channel instanceof HttpChannel) {
|
|
542
|
+
channel.onStreamMessage((msg, res) => this.handleSseStream(msg, res));
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
getChannel(id) {
|
|
546
|
+
return this.channels.get(id);
|
|
547
|
+
}
|
|
548
|
+
listChannels() {
|
|
549
|
+
return [...this.channels.values()];
|
|
550
|
+
}
|
|
551
|
+
getSessionManager() {
|
|
552
|
+
return this.sessions;
|
|
553
|
+
}
|
|
554
|
+
async runWithMiddleware(channel, msg, handler) {
|
|
555
|
+
if (!this.middlewareChain) {
|
|
556
|
+
await handler();
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const ctx = {
|
|
560
|
+
message: msg,
|
|
561
|
+
channelId: channel.id,
|
|
562
|
+
startTime: Date.now(),
|
|
563
|
+
metadata: {}
|
|
564
|
+
};
|
|
565
|
+
await this.middlewareChain(ctx, handler);
|
|
566
|
+
}
|
|
567
|
+
async handleMessage(channel, msg) {
|
|
568
|
+
try {
|
|
569
|
+
await this.runWithMiddleware(channel, msg, async () => {
|
|
570
|
+
const session = await this.sessions.getOrCreate(msg.sessionKey);
|
|
571
|
+
const domainId = this.config.domain ?? getIdentity().domain;
|
|
572
|
+
session.messages.push({
|
|
573
|
+
role: "user",
|
|
574
|
+
content: msg.text,
|
|
575
|
+
timestamp: Date.now()
|
|
576
|
+
});
|
|
577
|
+
const isStreamCapable = !(channel instanceof HttpChannel);
|
|
578
|
+
if (isStreamCapable) {
|
|
579
|
+
await this.handleStreamingChannel(channel, msg, domainId, session);
|
|
580
|
+
} else {
|
|
581
|
+
await this.handleBlockingChannel(channel, msg, domainId, session);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
} catch (err) {
|
|
585
|
+
this.config.onError?.(err, `channel=${channel.id} user=${msg.userId}`);
|
|
586
|
+
await channel.sendResponse(msg.userId, {
|
|
587
|
+
type: "error",
|
|
588
|
+
content: err.message ?? "Internal error"
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async handleBlockingChannel(channel, msg, domainId, session) {
|
|
593
|
+
const history = session.messages.slice(0, -1);
|
|
594
|
+
const result = await gatewayQuery({
|
|
595
|
+
prompt: msg.text,
|
|
596
|
+
domain: domainId,
|
|
597
|
+
cwd: this.config.cwd,
|
|
598
|
+
safeMode: this.config.safeMode,
|
|
599
|
+
sessionMessages: convertSessionHistory(history)
|
|
600
|
+
});
|
|
601
|
+
session.messages.push({
|
|
602
|
+
role: "assistant",
|
|
603
|
+
content: result.resultText,
|
|
604
|
+
timestamp: Date.now()
|
|
605
|
+
});
|
|
606
|
+
await this.sessions.save(session);
|
|
607
|
+
await channel.sendResponse(msg.userId, {
|
|
608
|
+
type: "text",
|
|
609
|
+
content: result.resultText
|
|
610
|
+
});
|
|
611
|
+
await channel.sendResponse(msg.userId, { type: "done", content: "" });
|
|
612
|
+
}
|
|
613
|
+
async handleStreamingChannel(channel, msg, domainId, session) {
|
|
614
|
+
const chunks = [];
|
|
615
|
+
const history = session.messages.slice(0, -1);
|
|
616
|
+
for await (const chunk of gatewayQueryStream({
|
|
617
|
+
prompt: msg.text,
|
|
618
|
+
domain: domainId,
|
|
619
|
+
cwd: this.config.cwd,
|
|
620
|
+
safeMode: this.config.safeMode,
|
|
621
|
+
sessionMessages: convertSessionHistory(history)
|
|
622
|
+
})) {
|
|
623
|
+
if (chunk.type === "text") {
|
|
624
|
+
chunks.push(chunk.content);
|
|
625
|
+
}
|
|
626
|
+
await channel.sendResponse(msg.userId, {
|
|
627
|
+
type: chunk.type,
|
|
628
|
+
content: chunk.content
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
session.messages.push({
|
|
632
|
+
role: "assistant",
|
|
633
|
+
content: chunks.join(""),
|
|
634
|
+
timestamp: Date.now()
|
|
635
|
+
});
|
|
636
|
+
await this.sessions.save(session);
|
|
637
|
+
}
|
|
638
|
+
async handleSseStream(msg, res) {
|
|
639
|
+
const domainId = this.config.domain ?? getIdentity().domain;
|
|
640
|
+
const session = await this.sessions.getOrCreate(msg.sessionKey);
|
|
641
|
+
session.messages.push({
|
|
642
|
+
role: "user",
|
|
643
|
+
content: msg.text,
|
|
644
|
+
timestamp: Date.now()
|
|
645
|
+
});
|
|
646
|
+
const chunks = [];
|
|
647
|
+
const history = session.messages.slice(0, -1);
|
|
648
|
+
try {
|
|
649
|
+
for await (const chunk of gatewayQueryStream({
|
|
650
|
+
prompt: msg.text,
|
|
651
|
+
domain: domainId,
|
|
652
|
+
cwd: this.config.cwd,
|
|
653
|
+
safeMode: this.config.safeMode,
|
|
654
|
+
sessionMessages: convertSessionHistory(history)
|
|
655
|
+
})) {
|
|
656
|
+
if (chunk.type === "text") {
|
|
657
|
+
chunks.push(chunk.content);
|
|
658
|
+
}
|
|
659
|
+
writeSseEvent(res, chunk.type, { content: chunk.content });
|
|
660
|
+
}
|
|
661
|
+
session.messages.push({
|
|
662
|
+
role: "assistant",
|
|
663
|
+
content: chunks.join(""),
|
|
664
|
+
timestamp: Date.now()
|
|
665
|
+
});
|
|
666
|
+
await this.sessions.save(session);
|
|
667
|
+
} catch (err) {
|
|
668
|
+
writeSseEvent(res, "error", { error: err.message ?? "Internal error" });
|
|
669
|
+
this.config.onError?.(err, `sse user=${msg.userId}`);
|
|
670
|
+
} finally {
|
|
671
|
+
res.end();
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
async start() {
|
|
675
|
+
const startPromises = [...this.channels.values()].map((ch) => ch.start());
|
|
676
|
+
await Promise.all(startPromises);
|
|
677
|
+
const identity = getIdentity();
|
|
678
|
+
console.log(`[Gateway] Started with ${this.channels.size} channel(s) \u2014 identity: ${identity.name}, domain: ${this.config.domain ?? identity.domain}`);
|
|
679
|
+
}
|
|
680
|
+
async stop() {
|
|
681
|
+
const stopPromises = [...this.channels.values()].map((ch) => ch.stop());
|
|
682
|
+
await Promise.all(stopPromises);
|
|
683
|
+
console.log("[Gateway] Stopped");
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// src/channels/websocket.ts
|
|
688
|
+
import { createServer as createServer2 } from "http";
|
|
689
|
+
import { WebSocketServer } from "ws";
|
|
690
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
691
|
+
var WebSocketChannel = class {
|
|
692
|
+
constructor(config) {
|
|
693
|
+
this.config = config;
|
|
694
|
+
}
|
|
695
|
+
id = "websocket";
|
|
696
|
+
name = "WebSocket";
|
|
697
|
+
wss = null;
|
|
698
|
+
ownServer = null;
|
|
699
|
+
handler = null;
|
|
700
|
+
clients = /* @__PURE__ */ new Map();
|
|
701
|
+
pendingResponses = /* @__PURE__ */ new Map();
|
|
702
|
+
onMessage(handler) {
|
|
703
|
+
this.handler = handler;
|
|
704
|
+
}
|
|
705
|
+
async start() {
|
|
706
|
+
if (this.config.server) {
|
|
707
|
+
this.wss = new WebSocketServer({ server: this.config.server });
|
|
708
|
+
} else {
|
|
709
|
+
this.ownServer = createServer2();
|
|
710
|
+
this.wss = new WebSocketServer({ server: this.ownServer });
|
|
711
|
+
await new Promise((resolve2) => {
|
|
712
|
+
this.ownServer.listen(this.config.port, this.config.host ?? "0.0.0.0", resolve2);
|
|
713
|
+
});
|
|
714
|
+
console.log(`[WebSocket] Listening on ${this.config.host ?? "0.0.0.0"}:${this.config.port}`);
|
|
715
|
+
}
|
|
716
|
+
this.wss.on("connection", (ws, req) => this.handleConnection(ws, req));
|
|
717
|
+
}
|
|
718
|
+
async stop() {
|
|
719
|
+
if (this.wss) {
|
|
720
|
+
for (const client of this.clients.values()) {
|
|
721
|
+
client.ws.close(1001, "Server shutting down");
|
|
722
|
+
}
|
|
723
|
+
this.clients.clear();
|
|
724
|
+
this.wss.close();
|
|
725
|
+
this.wss = null;
|
|
726
|
+
}
|
|
727
|
+
if (this.ownServer) {
|
|
728
|
+
await new Promise((resolve2) => this.ownServer.close(() => resolve2()));
|
|
729
|
+
this.ownServer = null;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
async sendResponse(clientId, response) {
|
|
733
|
+
const client = this.clients.get(clientId);
|
|
734
|
+
if (client && client.ws.readyState === 1) {
|
|
735
|
+
client.ws.send(JSON.stringify({
|
|
736
|
+
type: "response",
|
|
737
|
+
data: response
|
|
738
|
+
}));
|
|
739
|
+
}
|
|
740
|
+
const pending = this.pendingResponses.get(clientId);
|
|
741
|
+
if (pending) {
|
|
742
|
+
pending.push(response);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
handleConnection(ws, req) {
|
|
746
|
+
if (this.config.authToken) {
|
|
747
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
748
|
+
const token = url.searchParams.get("token");
|
|
749
|
+
if (token !== this.config.authToken) {
|
|
750
|
+
ws.close(4001, "Unauthorized");
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const clientId = randomUUID2();
|
|
755
|
+
this.clients.set(clientId, {
|
|
756
|
+
ws,
|
|
757
|
+
userId: clientId,
|
|
758
|
+
sessionKey: `ws:${clientId}`
|
|
759
|
+
});
|
|
760
|
+
ws.on("message", (raw) => this.handleWsMessage(clientId, raw));
|
|
761
|
+
ws.on("close", () => {
|
|
762
|
+
this.clients.delete(clientId);
|
|
763
|
+
});
|
|
764
|
+
ws.on("error", (err) => {
|
|
765
|
+
console.error(`[WebSocket] Client ${clientId} error:`, err.message);
|
|
766
|
+
this.clients.delete(clientId);
|
|
767
|
+
});
|
|
768
|
+
ws.send(JSON.stringify({
|
|
769
|
+
type: "connected",
|
|
770
|
+
data: { clientId }
|
|
771
|
+
}));
|
|
772
|
+
}
|
|
773
|
+
async handleWsMessage(clientId, raw) {
|
|
774
|
+
let parsed;
|
|
775
|
+
try {
|
|
776
|
+
parsed = JSON.parse(String(raw));
|
|
777
|
+
} catch {
|
|
778
|
+
this.sendToClient(clientId, {
|
|
779
|
+
type: "error",
|
|
780
|
+
data: { error: "Invalid JSON" }
|
|
781
|
+
});
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const { type, message, userId, sessionKey } = parsed;
|
|
785
|
+
if (type === "ping") {
|
|
786
|
+
this.sendToClient(clientId, { type: "pong", data: {} });
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (type !== "chat" || !message || typeof message !== "string") {
|
|
790
|
+
this.sendToClient(clientId, {
|
|
791
|
+
type: "error",
|
|
792
|
+
data: { error: 'Expected { type: "chat", message: "..." }' }
|
|
793
|
+
});
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
if (!this.handler) {
|
|
797
|
+
this.sendToClient(clientId, {
|
|
798
|
+
type: "error",
|
|
799
|
+
data: { error: "No handler registered" }
|
|
800
|
+
});
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
const resolvedUserId = userId ?? clientId;
|
|
804
|
+
const resolvedSessionKey = sessionKey ?? `ws:${resolvedUserId}`;
|
|
805
|
+
const client = this.clients.get(clientId);
|
|
806
|
+
if (client) {
|
|
807
|
+
client.userId = resolvedUserId;
|
|
808
|
+
client.sessionKey = resolvedSessionKey;
|
|
809
|
+
}
|
|
810
|
+
try {
|
|
811
|
+
await this.handler({
|
|
812
|
+
channelId: "websocket",
|
|
813
|
+
userId: clientId,
|
|
814
|
+
sessionKey: resolvedSessionKey,
|
|
815
|
+
text: message,
|
|
816
|
+
metadata: { originalUserId: resolvedUserId }
|
|
817
|
+
});
|
|
818
|
+
} catch (err) {
|
|
819
|
+
this.sendToClient(clientId, {
|
|
820
|
+
type: "error",
|
|
821
|
+
data: { error: err.message ?? "Internal error" }
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
sendToClient(clientId, msg) {
|
|
826
|
+
const client = this.clients.get(clientId);
|
|
827
|
+
if (client && client.ws.readyState === 1) {
|
|
828
|
+
client.ws.send(JSON.stringify(msg));
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
// src/channels/cli.ts
|
|
834
|
+
import { createInterface } from "readline";
|
|
835
|
+
var CliChannel = class {
|
|
836
|
+
id = "cli";
|
|
837
|
+
name = "CLI";
|
|
838
|
+
handler = null;
|
|
839
|
+
rl = null;
|
|
840
|
+
userId;
|
|
841
|
+
promptStr;
|
|
842
|
+
input;
|
|
843
|
+
output;
|
|
844
|
+
active = false;
|
|
845
|
+
constructor(options = {}) {
|
|
846
|
+
this.userId = options.userId ?? "cli-user";
|
|
847
|
+
this.promptStr = options.prompt ?? "> ";
|
|
848
|
+
this.input = options.input ?? process.stdin;
|
|
849
|
+
this.output = options.output ?? process.stdout;
|
|
850
|
+
}
|
|
851
|
+
onMessage(handler) {
|
|
852
|
+
this.handler = handler;
|
|
853
|
+
}
|
|
854
|
+
async start() {
|
|
855
|
+
this.active = true;
|
|
856
|
+
this.rl = createInterface({
|
|
857
|
+
input: this.input,
|
|
858
|
+
output: this.output,
|
|
859
|
+
prompt: this.promptStr
|
|
860
|
+
});
|
|
861
|
+
this.rl.on("line", async (line) => {
|
|
862
|
+
const text = line.trim();
|
|
863
|
+
if (!text) {
|
|
864
|
+
this.rl?.prompt();
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (text === "/exit" || text === "/quit") {
|
|
868
|
+
await this.stop();
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (!this.handler) {
|
|
872
|
+
this.rl?.prompt();
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
this.rl?.pause();
|
|
876
|
+
try {
|
|
877
|
+
await this.handler({
|
|
878
|
+
channelId: this.id,
|
|
879
|
+
userId: this.userId,
|
|
880
|
+
sessionKey: `cli:${this.userId}`,
|
|
881
|
+
text
|
|
882
|
+
});
|
|
883
|
+
} finally {
|
|
884
|
+
if (this.active) {
|
|
885
|
+
this.rl?.resume();
|
|
886
|
+
this.rl?.prompt();
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
this.rl.on("close", () => {
|
|
891
|
+
this.active = false;
|
|
892
|
+
});
|
|
893
|
+
setTimeout(() => {
|
|
894
|
+
if (this.active) {
|
|
895
|
+
this.output.write("\n");
|
|
896
|
+
this.rl?.prompt();
|
|
897
|
+
}
|
|
898
|
+
}, 100);
|
|
899
|
+
}
|
|
900
|
+
async stop() {
|
|
901
|
+
this.active = false;
|
|
902
|
+
this.rl?.close();
|
|
903
|
+
this.rl = null;
|
|
904
|
+
}
|
|
905
|
+
async sendResponse(_userId, response) {
|
|
906
|
+
if (!this.active) return;
|
|
907
|
+
switch (response.type) {
|
|
908
|
+
case "text":
|
|
909
|
+
this.write(response.content);
|
|
910
|
+
break;
|
|
911
|
+
case "thinking":
|
|
912
|
+
this.write(`[thinking] ${response.content}`);
|
|
913
|
+
break;
|
|
914
|
+
case "error":
|
|
915
|
+
this.write(`[error] ${response.content}`);
|
|
916
|
+
break;
|
|
917
|
+
case "tool_call":
|
|
918
|
+
this.write(`[tool] ${response.content}`);
|
|
919
|
+
break;
|
|
920
|
+
case "tool_result":
|
|
921
|
+
this.write(`[result] ${response.content}`);
|
|
922
|
+
break;
|
|
923
|
+
case "done":
|
|
924
|
+
this.write("");
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
async requestPermission(_userId, description) {
|
|
929
|
+
if (!this.rl) return false;
|
|
930
|
+
return new Promise((resolve2) => {
|
|
931
|
+
this.write(`
|
|
932
|
+
[Permission] ${description}`);
|
|
933
|
+
this.rl.question("Allow? (y/n) ", (answer) => {
|
|
934
|
+
resolve2(answer.trim().toLowerCase().startsWith("y"));
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
write(text) {
|
|
939
|
+
this.output.write(`${text}
|
|
940
|
+
`);
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
// src/sessions/storage.ts
|
|
945
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync } from "fs";
|
|
946
|
+
import { join as join2 } from "path";
|
|
947
|
+
var FileSessionStorage = class {
|
|
948
|
+
constructor(dir) {
|
|
949
|
+
this.dir = dir;
|
|
950
|
+
if (!existsSync(dir)) {
|
|
951
|
+
mkdirSync(dir, { recursive: true });
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
filePath(key) {
|
|
955
|
+
const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
956
|
+
return join2(this.dir, `${safeKey}.json`);
|
|
957
|
+
}
|
|
958
|
+
async load(key) {
|
|
959
|
+
const path = this.filePath(key);
|
|
960
|
+
if (!existsSync(path)) return null;
|
|
961
|
+
try {
|
|
962
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
963
|
+
} catch {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
async save(key, session) {
|
|
968
|
+
session.updatedAt = Date.now();
|
|
969
|
+
writeFileSync(this.filePath(key), JSON.stringify(session, null, 2), "utf-8");
|
|
970
|
+
}
|
|
971
|
+
async list() {
|
|
972
|
+
if (!existsSync(this.dir)) return [];
|
|
973
|
+
return readdirSync(this.dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
974
|
+
}
|
|
975
|
+
async delete(key) {
|
|
976
|
+
const path = this.filePath(key);
|
|
977
|
+
if (existsSync(path)) unlinkSync(path);
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// src/entrypoints/gateway.ts
|
|
982
|
+
import { join as join3, dirname } from "path";
|
|
983
|
+
import { homedir } from "os";
|
|
984
|
+
import { existsSync as existsSync2 } from "fs";
|
|
985
|
+
import { fileURLToPath } from "url";
|
|
986
|
+
function parseArgs(argv) {
|
|
987
|
+
const opts = {
|
|
988
|
+
port: 3e3,
|
|
989
|
+
host: "0.0.0.0",
|
|
990
|
+
domain: "general",
|
|
991
|
+
safeMode: false,
|
|
992
|
+
enableLogging: true
|
|
993
|
+
};
|
|
994
|
+
for (let i = 2; i < argv.length; i++) {
|
|
995
|
+
const arg = argv[i];
|
|
996
|
+
const next = argv[i + 1];
|
|
997
|
+
switch (arg) {
|
|
998
|
+
case "--port":
|
|
999
|
+
opts.port = parseInt(next, 10);
|
|
1000
|
+
i++;
|
|
1001
|
+
break;
|
|
1002
|
+
case "--ws-port":
|
|
1003
|
+
opts.wsPort = parseInt(next, 10);
|
|
1004
|
+
i++;
|
|
1005
|
+
break;
|
|
1006
|
+
case "--host":
|
|
1007
|
+
opts.host = next;
|
|
1008
|
+
i++;
|
|
1009
|
+
break;
|
|
1010
|
+
case "--domain":
|
|
1011
|
+
opts.domain = next;
|
|
1012
|
+
i++;
|
|
1013
|
+
break;
|
|
1014
|
+
case "--auth-token":
|
|
1015
|
+
opts.authToken = next;
|
|
1016
|
+
i++;
|
|
1017
|
+
break;
|
|
1018
|
+
case "--cors-origin":
|
|
1019
|
+
opts.corsOrigin = next;
|
|
1020
|
+
i++;
|
|
1021
|
+
break;
|
|
1022
|
+
case "--session-dir":
|
|
1023
|
+
opts.sessionDir = next;
|
|
1024
|
+
i++;
|
|
1025
|
+
break;
|
|
1026
|
+
case "--cwd":
|
|
1027
|
+
opts.cwd = next;
|
|
1028
|
+
i++;
|
|
1029
|
+
break;
|
|
1030
|
+
case "--safe-mode":
|
|
1031
|
+
opts.safeMode = true;
|
|
1032
|
+
break;
|
|
1033
|
+
case "--no-logging":
|
|
1034
|
+
opts.enableLogging = false;
|
|
1035
|
+
break;
|
|
1036
|
+
case "--rate-limit":
|
|
1037
|
+
opts.rateLimit = parseInt(next, 10);
|
|
1038
|
+
i++;
|
|
1039
|
+
break;
|
|
1040
|
+
case "--name":
|
|
1041
|
+
opts.name = next;
|
|
1042
|
+
i++;
|
|
1043
|
+
break;
|
|
1044
|
+
case "--description":
|
|
1045
|
+
opts.description = next;
|
|
1046
|
+
i++;
|
|
1047
|
+
break;
|
|
1048
|
+
case "--cli":
|
|
1049
|
+
opts.cli = true;
|
|
1050
|
+
break;
|
|
1051
|
+
case "--web-dir":
|
|
1052
|
+
opts.webDir = next;
|
|
1053
|
+
i++;
|
|
1054
|
+
break;
|
|
1055
|
+
case "--help":
|
|
1056
|
+
printHelp();
|
|
1057
|
+
process.exit(0);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return opts;
|
|
1061
|
+
}
|
|
1062
|
+
function printHelp() {
|
|
1063
|
+
console.log(`
|
|
1064
|
+
Usage: newcraw gateway [options]
|
|
1065
|
+
|
|
1066
|
+
Options:
|
|
1067
|
+
--port <number> HTTP port (default: 3000)
|
|
1068
|
+
--ws-port <number> WebSocket port (if omitted, WS is disabled)
|
|
1069
|
+
--host <string> Bind host (default: 0.0.0.0)
|
|
1070
|
+
--domain <string> Domain: coding | general (default: general)
|
|
1071
|
+
--auth-token <string> Bearer token for API / WS auth
|
|
1072
|
+
--cors-origin <string> CORS origin (default: *)
|
|
1073
|
+
--session-dir <path> Session storage directory
|
|
1074
|
+
--cwd <path> Working directory for agent
|
|
1075
|
+
--safe-mode Enable safe mode (restrict dangerous operations)
|
|
1076
|
+
--no-logging Disable request logging
|
|
1077
|
+
--rate-limit <number> Max requests per minute per session (default: 30)
|
|
1078
|
+
--name <string> Agent name
|
|
1079
|
+
--description <string> Agent description
|
|
1080
|
+
--cli Enable interactive CLI channel (stdin/stdout REPL)
|
|
1081
|
+
--web-dir <path> Serve web UI from directory (default: auto-detect web/dist)
|
|
1082
|
+
--help Show this help
|
|
1083
|
+
|
|
1084
|
+
Endpoints:
|
|
1085
|
+
GET /health Health check
|
|
1086
|
+
POST /api/chat Blocking JSON response
|
|
1087
|
+
POST /api/chat/stream SSE streaming response
|
|
1088
|
+
WS ws://host:ws-port WebSocket (bidirectional streaming)
|
|
1089
|
+
`);
|
|
1090
|
+
}
|
|
1091
|
+
function resolveWebDir(explicit) {
|
|
1092
|
+
if (explicit) {
|
|
1093
|
+
return existsSync2(explicit) ? explicit : void 0;
|
|
1094
|
+
}
|
|
1095
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
1096
|
+
const candidates = [
|
|
1097
|
+
join3(process.cwd(), "web", "dist"),
|
|
1098
|
+
// Built output lives in dist/ — one level up reaches package root
|
|
1099
|
+
join3(thisDir, "..", "web", "dist"),
|
|
1100
|
+
// Source layout lives in src/entrypoints/ — two levels up reaches project root
|
|
1101
|
+
join3(thisDir, "..", "..", "web", "dist")
|
|
1102
|
+
];
|
|
1103
|
+
for (const candidate of candidates) {
|
|
1104
|
+
if (existsSync2(join3(candidate, "index.html"))) {
|
|
1105
|
+
return candidate;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
return void 0;
|
|
1109
|
+
}
|
|
1110
|
+
async function main() {
|
|
1111
|
+
const opts = parseArgs(process.argv);
|
|
1112
|
+
if (opts.name || opts.description) {
|
|
1113
|
+
setIdentity({
|
|
1114
|
+
...opts.name ? { name: opts.name } : {},
|
|
1115
|
+
...opts.description ? { description: opts.description } : {},
|
|
1116
|
+
domain: opts.domain
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
const sessionDir = opts.sessionDir ?? join3(homedir(), ".newcraw", "gateway-sessions");
|
|
1120
|
+
const storage = new FileSessionStorage(sessionDir);
|
|
1121
|
+
const middleware = [];
|
|
1122
|
+
if (opts.enableLogging) middleware.push(loggingMiddleware());
|
|
1123
|
+
middleware.push(rateLimitMiddleware({ maxRequests: opts.rateLimit ?? 30 }));
|
|
1124
|
+
middleware.push(maxConcurrencyMiddleware());
|
|
1125
|
+
const gateway = new Gateway({
|
|
1126
|
+
storage,
|
|
1127
|
+
domain: opts.domain,
|
|
1128
|
+
cwd: opts.cwd,
|
|
1129
|
+
safeMode: opts.safeMode,
|
|
1130
|
+
middleware,
|
|
1131
|
+
onError: (err, ctx) => console.error(`[Gateway Error] ${ctx}:`, err.message)
|
|
1132
|
+
});
|
|
1133
|
+
const webDistDir = resolveWebDir(opts.webDir);
|
|
1134
|
+
if (webDistDir) {
|
|
1135
|
+
console.log(`[Gateway] Serving web UI from ${webDistDir}`);
|
|
1136
|
+
}
|
|
1137
|
+
const httpChannel = new HttpChannel({
|
|
1138
|
+
port: opts.port,
|
|
1139
|
+
host: opts.host,
|
|
1140
|
+
authToken: opts.authToken,
|
|
1141
|
+
corsOrigin: opts.corsOrigin,
|
|
1142
|
+
webDistDir
|
|
1143
|
+
});
|
|
1144
|
+
gateway.registerChannel(httpChannel);
|
|
1145
|
+
if (opts.wsPort) {
|
|
1146
|
+
const wsChannel = new WebSocketChannel({
|
|
1147
|
+
port: opts.wsPort,
|
|
1148
|
+
host: opts.host,
|
|
1149
|
+
authToken: opts.authToken
|
|
1150
|
+
});
|
|
1151
|
+
gateway.registerChannel(wsChannel);
|
|
1152
|
+
}
|
|
1153
|
+
if (opts.cli) {
|
|
1154
|
+
const cliChannel = new CliChannel({ prompt: `[${opts.name ?? "NewCraw"}] > ` });
|
|
1155
|
+
gateway.registerChannel(cliChannel);
|
|
1156
|
+
}
|
|
1157
|
+
process.on("SIGINT", async () => {
|
|
1158
|
+
await gateway.stop();
|
|
1159
|
+
process.exit(0);
|
|
1160
|
+
});
|
|
1161
|
+
process.on("SIGTERM", async () => {
|
|
1162
|
+
await gateway.stop();
|
|
1163
|
+
process.exit(0);
|
|
1164
|
+
});
|
|
1165
|
+
await gateway.start();
|
|
1166
|
+
}
|
|
1167
|
+
main().catch((err) => {
|
|
1168
|
+
console.error("Fatal:", err);
|
|
1169
|
+
process.exit(1);
|
|
1170
|
+
});
|