@rallycry/conveyor-agent 7.1.1 → 7.1.4
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/chunk-C5YAMQJ2.js +17 -0
- package/dist/chunk-C5YAMQJ2.js.map +1 -0
- package/dist/{chunk-23JCJ2GV.js → chunk-H2GKXPFI.js} +232 -742
- package/dist/chunk-H2GKXPFI.js.map +1 -0
- package/dist/{chunk-KNBG2634.js → chunk-U6AYNVS7.js} +25 -2
- package/dist/chunk-U6AYNVS7.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/index.d.ts +10 -15
- package/dist/index.js +3 -3
- package/dist/{tag-audit-handler-PACJJEDB.js → tag-audit-handler-BM6MMS5T.js} +3 -5
- package/dist/{tag-audit-handler-PACJJEDB.js.map → tag-audit-handler-BM6MMS5T.js.map} +1 -1
- package/dist/task-audit-handler-URD2BOC4.js +381 -0
- package/dist/task-audit-handler-URD2BOC4.js.map +1 -0
- package/package.json +2 -5
- package/dist/chunk-23JCJ2GV.js.map +0 -1
- package/dist/chunk-CYZPFJGN.js +0 -26
- package/dist/chunk-CYZPFJGN.js.map +0 -1
- package/dist/chunk-KNBG2634.js.map +0 -1
- package/dist/tunnel-client.d.ts +0 -1
- package/dist/tunnel-client.js +0 -226
- package/dist/tunnel-client.js.map +0 -1
package/dist/chunk-CYZPFJGN.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// src/utils/logger.ts
|
|
2
|
-
function createServiceLogger(service) {
|
|
3
|
-
const prefix = `[conveyor-agent:${service}]`;
|
|
4
|
-
return {
|
|
5
|
-
info(message, data) {
|
|
6
|
-
const extra = data ? ` ${JSON.stringify(data)}` : "";
|
|
7
|
-
process.stderr.write(`${prefix} ${message}${extra}
|
|
8
|
-
`);
|
|
9
|
-
},
|
|
10
|
-
warn(message, data) {
|
|
11
|
-
const extra = data ? ` ${JSON.stringify(data)}` : "";
|
|
12
|
-
process.stderr.write(`${prefix} WARN ${message}${extra}
|
|
13
|
-
`);
|
|
14
|
-
},
|
|
15
|
-
error(message, data) {
|
|
16
|
-
const extra = data ? ` ${JSON.stringify(data)}` : "";
|
|
17
|
-
process.stderr.write(`${prefix} ERROR ${message}${extra}
|
|
18
|
-
`);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export {
|
|
24
|
-
createServiceLogger
|
|
25
|
-
};
|
|
26
|
-
//# sourceMappingURL=chunk-CYZPFJGN.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/logger.ts"],"sourcesContent":["/** Minimal structured logger for conveyor-agent (writes to stderr). */\nexport function createServiceLogger(service: string) {\n const prefix = `[conveyor-agent:${service}]`;\n return {\n info(message: string, data?: Record<string, unknown>): void {\n const extra = data ? ` ${JSON.stringify(data)}` : \"\";\n process.stderr.write(`${prefix} ${message}${extra}\\n`);\n },\n warn(message: string, data?: Record<string, unknown>): void {\n const extra = data ? ` ${JSON.stringify(data)}` : \"\";\n process.stderr.write(`${prefix} WARN ${message}${extra}\\n`);\n },\n error(message: string, data?: Record<string, unknown>): void {\n const extra = data ? ` ${JSON.stringify(data)}` : \"\";\n process.stderr.write(`${prefix} ERROR ${message}${extra}\\n`);\n },\n };\n}\n"],"mappings":";AACO,SAAS,oBAAoB,SAAiB;AACnD,QAAM,SAAS,mBAAmB,OAAO;AACzC,SAAO;AAAA,IACL,KAAK,SAAiB,MAAsC;AAC1D,YAAM,QAAQ,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AAClD,cAAQ,OAAO,MAAM,GAAG,MAAM,IAAI,OAAO,GAAG,KAAK;AAAA,CAAI;AAAA,IACvD;AAAA,IACA,KAAK,SAAiB,MAAsC;AAC1D,YAAM,QAAQ,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AAClD,cAAQ,OAAO,MAAM,GAAG,MAAM,SAAS,OAAO,GAAG,KAAK;AAAA,CAAI;AAAA,IAC5D;AAAA,IACA,MAAM,SAAiB,MAAsC;AAC3D,YAAM,QAAQ,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AAClD,cAAQ,OAAO,MAAM,GAAG,MAAM,UAAU,OAAO,GAAG,KAAK;AAAA,CAAI;AAAA,IAC7D;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/harness/types.ts","../src/harness/claude-code/index.ts","../src/harness/index.ts"],"sourcesContent":["/**\n * Harness-neutral types for agent query execution.\n *\n * These types abstract the underlying agent SDK so that the runner,\n * execution, and tool layers never reference SDK-specific imports\n * directly. The only place that should import from\n * `@anthropic-ai/claude-agent-sdk` is `harness/claude-code/`.\n */\n\nimport type { z } from \"zod\";\n\n// ── Events emitted by the harness during a query ────────────────────────\n\nexport interface HarnessSystemInitEvent {\n type: \"system\";\n subtype: \"init\";\n session_id?: string;\n model: string;\n}\n\nexport interface HarnessCompactBoundaryEvent {\n type: \"system\";\n subtype: \"compact_boundary\";\n compact_metadata: { trigger: \"manual\" | \"auto\"; pre_tokens: number };\n}\n\nexport interface HarnessTaskStartedEvent {\n type: \"system\";\n subtype: \"task_started\";\n task_id: string;\n description: string;\n}\n\nexport interface HarnessTaskProgressEvent {\n type: \"system\";\n subtype: \"task_progress\";\n task_id: string;\n description: string;\n usage?: { tool_uses: number; duration_ms: number };\n}\n\nexport type HarnessSystemEvent =\n | HarnessSystemInitEvent\n | HarnessCompactBoundaryEvent\n | HarnessTaskStartedEvent\n | HarnessTaskProgressEvent;\n\nexport interface HarnessContentBlock {\n type: string;\n text?: string;\n name?: string;\n input?: unknown;\n id?: string;\n}\n\nexport interface HarnessAssistantEvent {\n type: \"assistant\";\n message: {\n role: \"assistant\";\n content: HarnessContentBlock[];\n usage?: {\n input_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n };\n };\n}\n\nexport interface HarnessResultSuccessEvent {\n type: \"result\";\n subtype: \"success\";\n result: string;\n total_cost_usd: number;\n modelUsage?: Record<string, unknown>;\n sessionId?: string;\n}\n\nexport interface HarnessResultErrorEvent {\n type: \"result\";\n subtype: \"error\";\n errors: string[];\n sessionId?: string;\n}\n\nexport type HarnessResultEvent = HarnessResultSuccessEvent | HarnessResultErrorEvent;\n\nexport interface HarnessRateLimitEvent {\n type: \"rate_limit_event\";\n rate_limit_info: {\n status: string;\n rateLimitType?: string;\n utilization?: number;\n resetsAt?: unknown;\n };\n}\n\nexport interface HarnessToolProgressEvent {\n type: \"tool_progress\";\n tool_name?: string;\n elapsed_time_seconds?: number;\n}\n\nexport type HarnessEvent =\n | HarnessSystemEvent\n | HarnessAssistantEvent\n | HarnessResultEvent\n | HarnessRateLimitEvent\n | HarnessToolProgressEvent;\n\n// ── User message fed into a query ───────────────────────────────────────\n\nexport interface HarnessUserMessage {\n type: \"user\";\n session_id: string;\n message: { role: \"user\"; content: string | unknown[] };\n parent_tool_use_id: null;\n}\n\n// ── Tool definition (harness-neutral) ───────────────────────────────────\n\nexport interface HarnessToolAnnotations {\n readOnlyHint?: boolean;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- generic tool handler */\nexport interface HarnessToolDefinition {\n name: string;\n description: string;\n schema: z.ZodRawShape;\n handler: (\n input: any,\n ) => Promise<{ content: { type: string; text?: string; data?: string; mimeType?: string }[] }>;\n annotations?: HarnessToolAnnotations;\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n// ── Hook types ──────────────────────────────────────────────────────────\n\nexport interface HarnessHookInput {\n hook_event_name: string;\n tool_name: string;\n tool_response: unknown;\n}\n\nexport interface HarnessHookOutput {\n continue: boolean;\n}\n\nexport type HarnessPostToolUseHook = (input: HarnessHookInput) => Promise<HarnessHookOutput>;\n\n// ── MCP server handle (opaque to the runner) ────────────────────────────\n\n/** Opaque handle returned by `AgentHarness.createMcpServer()`. */\nexport type HarnessMcpServer = unknown;\n\n// ── Query options ───────────────────────────────────────────────────────\n\nexport interface HarnessQueryOptions {\n model: string;\n systemPrompt: unknown;\n cwd: string;\n permissionMode: \"plan\" | \"bypassPermissions\";\n allowDangerouslySkipPermissions: boolean;\n tools: { type: \"preset\"; preset: \"claude_code\" };\n mcpServers: Record<string, HarnessMcpServer>;\n settingSources?: (\"user\" | \"project\" | \"local\")[];\n sandbox?: { enabled: boolean };\n maxTurns?: number;\n maxBudgetUsd?: number;\n effort?: string;\n thinking?: unknown;\n betas?: unknown;\n disallowedTools?: string[];\n abortController?: AbortController;\n enableFileCheckpointing?: boolean;\n canUseTool?: (\n toolName: string,\n input: Record<string, unknown>,\n ) => Promise<\n | { behavior: \"allow\"; updatedInput?: Record<string, unknown> }\n | { behavior: \"deny\"; message: string }\n >;\n hooks?: Record<string, { hooks: HarnessPostToolUseHook[]; timeout: number }[]>;\n resume?: string;\n stderr?: (data: string) => void;\n}\n\n// ── Tool definition helper ──────────────────────────────────────────────\n\n/**\n * Construct a `HarnessToolDefinition` with the same signature as the SDK's\n * `tool()` helper, but without importing the SDK. The harness implementation\n * converts these into SDK-native tools when `createMcpServer()` is called.\n */\nexport function defineTool<T extends z.ZodRawShape>(\n name: string,\n description: string,\n schema: T,\n handler: (\n input: z.infer<z.ZodObject<T>>,\n ) => Promise<{ content: { type: string; text?: string; data?: string; mimeType?: string }[] }>,\n options?: { annotations?: HarnessToolAnnotations },\n): HarnessToolDefinition {\n return {\n name,\n description,\n schema,\n handler: handler as HarnessToolDefinition[\"handler\"],\n annotations: options?.annotations,\n };\n}\n\n// ── AgentHarness interface ──────────────────────────────────────────────\n\nexport interface AgentHarness {\n /** Start a streaming query and return an async generator of events. */\n executeQuery(options: {\n prompt: string | AsyncGenerator<HarnessUserMessage, void, unknown>;\n options: HarnessQueryOptions;\n resume?: string;\n }): AsyncGenerator<HarnessEvent, void>;\n\n /** Wrap an array of harness-neutral tool definitions into an MCP server. */\n createMcpServer(config: { name: string; tools: HarnessToolDefinition[] }): HarnessMcpServer;\n}\n","/**\n * ClaudeCodeHarness — wraps `@anthropic-ai/claude-agent-sdk` behind the\n * generic `AgentHarness` interface.\n *\n * This is the ONLY module that should import from the SDK.\n */\n\nimport { query, tool, createSdkMcpServer } from \"@anthropic-ai/claude-agent-sdk\";\nimport type {\n AgentHarness,\n HarnessEvent,\n HarnessQueryOptions,\n HarnessToolDefinition,\n HarnessMcpServer,\n HarnessUserMessage,\n} from \"../types.js\";\n\nexport class ClaudeCodeHarness implements AgentHarness {\n async *executeQuery(opts: {\n prompt: string | AsyncGenerator<HarnessUserMessage, void, unknown>;\n options: HarnessQueryOptions;\n resume?: string;\n }): AsyncGenerator<HarnessEvent, void> {\n const sdkEvents = query({\n prompt: opts.prompt as Parameters<typeof query>[0][\"prompt\"],\n options: {\n ...(opts.options as Parameters<typeof query>[0][\"options\"]),\n ...(opts.resume ? { resume: opts.resume } : {}),\n ...(opts.options.abortController ? { abortController: opts.options.abortController } : {}),\n },\n });\n\n for await (const event of sdkEvents) {\n yield event as unknown as HarnessEvent;\n }\n }\n\n createMcpServer(config: { name: string; tools: HarnessToolDefinition[] }): HarnessMcpServer {\n const sdkTools = config.tools.map((t) =>\n tool(\n t.name,\n t.description,\n t.schema,\n t.handler as Parameters<typeof tool>[3],\n t.annotations ? { annotations: t.annotations } : undefined,\n ),\n );\n return createSdkMcpServer({ name: config.name, tools: sdkTools });\n }\n}\n","export { defineTool } from \"./types.js\";\n\nexport type {\n AgentHarness,\n HarnessEvent,\n HarnessSystemEvent,\n HarnessSystemInitEvent,\n HarnessCompactBoundaryEvent,\n HarnessTaskStartedEvent,\n HarnessTaskProgressEvent,\n HarnessAssistantEvent,\n HarnessContentBlock,\n HarnessResultEvent,\n HarnessResultSuccessEvent,\n HarnessResultErrorEvent,\n HarnessRateLimitEvent,\n HarnessToolProgressEvent,\n HarnessUserMessage,\n HarnessToolDefinition,\n HarnessToolAnnotations,\n HarnessMcpServer,\n HarnessQueryOptions,\n HarnessHookInput,\n HarnessHookOutput,\n HarnessPostToolUseHook,\n} from \"./types.js\";\n\nexport { ClaudeCodeHarness } from \"./claude-code/index.js\";\n\nimport { ClaudeCodeHarness } from \"./claude-code/index.js\";\nimport type { AgentHarness } from \"./types.js\";\n\nexport function createHarness(): AgentHarness {\n return new ClaudeCodeHarness();\n}\n"],"mappings":";AAkMO,SAAS,WACd,MACA,aACA,QACA,SAGA,SACuB;AACvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,SAAS;AAAA,EACxB;AACF;;;AC3MA,SAAS,OAAO,MAAM,0BAA0B;AAUzC,IAAM,oBAAN,MAAgD;AAAA,EACrD,OAAO,aAAa,MAImB;AACrC,UAAM,YAAY,MAAM;AAAA,MACtB,QAAQ,KAAK;AAAA,MACb,SAAS;AAAA,QACP,GAAI,KAAK;AAAA,QACT,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,QAC7C,GAAI,KAAK,QAAQ,kBAAkB,EAAE,iBAAiB,KAAK,QAAQ,gBAAgB,IAAI,CAAC;AAAA,MAC1F;AAAA,IACF,CAAC;AAED,qBAAiB,SAAS,WAAW;AACnC,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,gBAAgB,QAA4E;AAC1F,UAAM,WAAW,OAAO,MAAM;AAAA,MAAI,CAAC,MACjC;AAAA,QACE,EAAE;AAAA,QACF,EAAE;AAAA,QACF,EAAE;AAAA,QACF,EAAE;AAAA,QACF,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,IAAI;AAAA,MACnD;AAAA,IACF;AACA,WAAO,mBAAmB,EAAE,MAAM,OAAO,MAAM,OAAO,SAAS,CAAC;AAAA,EAClE;AACF;;;ACjBO,SAAS,gBAA8B;AAC5C,SAAO,IAAI,kBAAkB;AAC/B;","names":[]}
|
package/dist/tunnel-client.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
package/dist/tunnel-client.js
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
createServiceLogger
|
|
4
|
-
} from "./chunk-CYZPFJGN.js";
|
|
5
|
-
|
|
6
|
-
// src/tunnel-client.ts
|
|
7
|
-
import { WebSocket } from "ws";
|
|
8
|
-
import * as http from "http";
|
|
9
|
-
import { URL } from "url";
|
|
10
|
-
var logger = createServiceLogger("TunnelClient");
|
|
11
|
-
var DEFAULT_PORT = 3050;
|
|
12
|
-
var RECONNECT_MIN_MS = 2e3;
|
|
13
|
-
var RECONNECT_MAX_MS = 3e4;
|
|
14
|
-
var apiUrl = process.env.CONVEYOR_API_URL;
|
|
15
|
-
var taskToken = process.env.CONVEYOR_TASK_TOKEN;
|
|
16
|
-
if (!apiUrl || !taskToken) {
|
|
17
|
-
logger.error("CONVEYOR_API_URL and CONVEYOR_TASK_TOKEN are required");
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
var activeRequests = /* @__PURE__ */ new Map();
|
|
21
|
-
var activeWebSockets = /* @__PURE__ */ new Map();
|
|
22
|
-
var reconnectDelay = RECONNECT_MIN_MS;
|
|
23
|
-
var shuttingDown = false;
|
|
24
|
-
function buildTunnelUrl() {
|
|
25
|
-
const base = apiUrl.replace(/^http/, "ws");
|
|
26
|
-
return `${base}/api/tunnel?token=${encodeURIComponent(taskToken)}`;
|
|
27
|
-
}
|
|
28
|
-
function connect() {
|
|
29
|
-
if (shuttingDown) return;
|
|
30
|
-
const url = buildTunnelUrl();
|
|
31
|
-
logger.info("Connecting...");
|
|
32
|
-
const ws = new WebSocket(url);
|
|
33
|
-
ws.on("open", () => {
|
|
34
|
-
logger.info("Connected");
|
|
35
|
-
reconnectDelay = RECONNECT_MIN_MS;
|
|
36
|
-
});
|
|
37
|
-
ws.on("message", (data) => {
|
|
38
|
-
handleMessage(ws, data);
|
|
39
|
-
});
|
|
40
|
-
ws.on("close", (code) => {
|
|
41
|
-
logger.info(`Disconnected (code=${code}), reconnecting in ${reconnectDelay}ms`);
|
|
42
|
-
cleanupAll();
|
|
43
|
-
if (!shuttingDown) {
|
|
44
|
-
setTimeout(connect, reconnectDelay);
|
|
45
|
-
reconnectDelay = Math.min(reconnectDelay * 2, RECONNECT_MAX_MS);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
ws.on("error", (err) => {
|
|
49
|
-
logger.error(`WebSocket error: ${err.message}`);
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
function handleMessage(tunnel, raw) {
|
|
53
|
-
const buf = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
|
|
54
|
-
if (buf.length > 0 && buf[0] !== 123) {
|
|
55
|
-
if (buf.length <= 4) return;
|
|
56
|
-
const id = buf.readUInt32BE(0);
|
|
57
|
-
const req = activeRequests.get(id);
|
|
58
|
-
if (req) req.write(buf.subarray(4));
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
let frame;
|
|
62
|
-
try {
|
|
63
|
-
frame = JSON.parse(buf.toString("utf-8"));
|
|
64
|
-
} catch {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
switch (frame.type) {
|
|
68
|
-
case "http-req":
|
|
69
|
-
handleHttpReq(tunnel, frame);
|
|
70
|
-
break;
|
|
71
|
-
case "http-req-end":
|
|
72
|
-
handleHttpReqEnd(frame);
|
|
73
|
-
break;
|
|
74
|
-
case "ws-upgrade":
|
|
75
|
-
handleWsUpgrade(tunnel, frame);
|
|
76
|
-
break;
|
|
77
|
-
case "ws-data":
|
|
78
|
-
handleWsData(frame);
|
|
79
|
-
break;
|
|
80
|
-
case "ws-close":
|
|
81
|
-
handleWsClose(frame);
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function handleHttpReq(tunnel, frame) {
|
|
86
|
-
const port = frame.port ?? DEFAULT_PORT;
|
|
87
|
-
const url = new URL(frame.path, `http://127.0.0.1:${port}`);
|
|
88
|
-
const req = http.request(
|
|
89
|
-
{
|
|
90
|
-
hostname: "127.0.0.1",
|
|
91
|
-
port,
|
|
92
|
-
path: url.pathname + url.search,
|
|
93
|
-
method: frame.method,
|
|
94
|
-
headers: frame.headers
|
|
95
|
-
},
|
|
96
|
-
(res) => {
|
|
97
|
-
const headers = {};
|
|
98
|
-
for (const [key, val] of Object.entries(res.headers)) {
|
|
99
|
-
if (typeof val === "string") headers[key] = val;
|
|
100
|
-
else if (Array.isArray(val)) headers[key] = val.join(", ");
|
|
101
|
-
}
|
|
102
|
-
sendJson(tunnel, {
|
|
103
|
-
type: "http-res",
|
|
104
|
-
id: frame.id,
|
|
105
|
-
statusCode: res.statusCode ?? 200,
|
|
106
|
-
headers
|
|
107
|
-
});
|
|
108
|
-
res.on("data", (chunk) => {
|
|
109
|
-
const out = Buffer.alloc(4 + chunk.length);
|
|
110
|
-
out.writeUInt32BE(frame.id, 0);
|
|
111
|
-
chunk.copy(out, 4);
|
|
112
|
-
if (tunnel.readyState === WebSocket.OPEN) tunnel.send(out);
|
|
113
|
-
});
|
|
114
|
-
res.on("end", () => {
|
|
115
|
-
sendJson(tunnel, { type: "http-end", id: frame.id });
|
|
116
|
-
activeRequests.delete(frame.id);
|
|
117
|
-
});
|
|
118
|
-
res.on("error", () => {
|
|
119
|
-
sendJson(tunnel, { type: "http-end", id: frame.id });
|
|
120
|
-
activeRequests.delete(frame.id);
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
);
|
|
124
|
-
req.on("error", (err) => {
|
|
125
|
-
logger.error(`HTTP request error (id=${frame.id}): ${err.message}`);
|
|
126
|
-
sendJson(tunnel, {
|
|
127
|
-
type: "http-res",
|
|
128
|
-
id: frame.id,
|
|
129
|
-
statusCode: 502,
|
|
130
|
-
headers: { "content-type": "application/json" }
|
|
131
|
-
});
|
|
132
|
-
const body = Buffer.from(JSON.stringify({ error: `Upstream error: ${err.message}` }));
|
|
133
|
-
const out = Buffer.alloc(4 + body.length);
|
|
134
|
-
out.writeUInt32BE(frame.id, 0);
|
|
135
|
-
body.copy(out, 4);
|
|
136
|
-
if (tunnel.readyState === WebSocket.OPEN) tunnel.send(out);
|
|
137
|
-
sendJson(tunnel, { type: "http-end", id: frame.id });
|
|
138
|
-
activeRequests.delete(frame.id);
|
|
139
|
-
});
|
|
140
|
-
activeRequests.set(frame.id, req);
|
|
141
|
-
}
|
|
142
|
-
function handleHttpReqEnd(frame) {
|
|
143
|
-
const req = activeRequests.get(frame.id);
|
|
144
|
-
if (req) req.end();
|
|
145
|
-
}
|
|
146
|
-
function handleWsUpgrade(tunnel, frame) {
|
|
147
|
-
const port = frame.port ?? DEFAULT_PORT;
|
|
148
|
-
const url = `ws://127.0.0.1:${port}${frame.path}`;
|
|
149
|
-
const passthrough = {};
|
|
150
|
-
for (const [k, v] of Object.entries(frame.headers)) {
|
|
151
|
-
const key = k.toLowerCase();
|
|
152
|
-
if (key === "host") continue;
|
|
153
|
-
if (key === "connection") continue;
|
|
154
|
-
if (key === "upgrade") continue;
|
|
155
|
-
if (key.startsWith("sec-websocket-")) continue;
|
|
156
|
-
passthrough[k] = v;
|
|
157
|
-
}
|
|
158
|
-
const upstream = new WebSocket(url, { headers: passthrough });
|
|
159
|
-
upstream.on("open", () => {
|
|
160
|
-
sendJson(tunnel, { type: "ws-open", id: frame.id });
|
|
161
|
-
activeWebSockets.set(frame.id, upstream);
|
|
162
|
-
});
|
|
163
|
-
upstream.on("message", (data, isBinary) => {
|
|
164
|
-
const buf = Buffer.isBuffer(data) ? data : Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data);
|
|
165
|
-
sendJson(tunnel, {
|
|
166
|
-
type: "ws-data",
|
|
167
|
-
id: frame.id,
|
|
168
|
-
data: isBinary ? buf.toString("base64") : buf.toString("utf-8"),
|
|
169
|
-
binary: isBinary
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
upstream.on("close", () => {
|
|
173
|
-
sendJson(tunnel, { type: "ws-close", id: frame.id });
|
|
174
|
-
activeWebSockets.delete(frame.id);
|
|
175
|
-
});
|
|
176
|
-
upstream.on("error", (err) => {
|
|
177
|
-
logger.error(`WS upstream error (id=${frame.id}): ${err.message}`);
|
|
178
|
-
sendJson(tunnel, { type: "ws-close", id: frame.id });
|
|
179
|
-
activeWebSockets.delete(frame.id);
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
function handleWsData(frame) {
|
|
183
|
-
const ws = activeWebSockets.get(frame.id);
|
|
184
|
-
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
185
|
-
if (frame.binary && frame.data) {
|
|
186
|
-
ws.send(Buffer.from(frame.data, "base64"), { binary: true });
|
|
187
|
-
} else {
|
|
188
|
-
ws.send(frame.data);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
function handleWsClose(frame) {
|
|
192
|
-
const ws = activeWebSockets.get(frame.id);
|
|
193
|
-
if (ws) {
|
|
194
|
-
ws.close(1e3);
|
|
195
|
-
activeWebSockets.delete(frame.id);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
function sendJson(ws, obj) {
|
|
199
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
200
|
-
ws.send(JSON.stringify(obj));
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function cleanupAll() {
|
|
204
|
-
for (const [id, req] of activeRequests) {
|
|
205
|
-
req.destroy();
|
|
206
|
-
activeRequests.delete(id);
|
|
207
|
-
}
|
|
208
|
-
for (const [id, ws] of activeWebSockets) {
|
|
209
|
-
ws.close(1e3);
|
|
210
|
-
activeWebSockets.delete(id);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
process.on("SIGTERM", () => {
|
|
214
|
-
logger.info("SIGTERM received, shutting down");
|
|
215
|
-
shuttingDown = true;
|
|
216
|
-
cleanupAll();
|
|
217
|
-
process.exit(0);
|
|
218
|
-
});
|
|
219
|
-
process.on("SIGINT", () => {
|
|
220
|
-
logger.info("SIGINT received, shutting down");
|
|
221
|
-
shuttingDown = true;
|
|
222
|
-
cleanupAll();
|
|
223
|
-
process.exit(0);
|
|
224
|
-
});
|
|
225
|
-
connect();
|
|
226
|
-
//# sourceMappingURL=tunnel-client.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tunnel-client.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Pod-side tunnel client.\n *\n * Connects to the API's WebSocket tunnel endpoint and proxies HTTP/WS\n * requests from the API to localhost ports inside the pod.\n *\n * Protocol matches tunnel-server.ts:\n * API → Pod (this client receives):\n * { type: \"http-req\", id, method, path, headers, port? }\n * <binary: 4-byte BE requestId + body chunk>\n * { type: \"http-req-end\", id }\n * { type: \"ws-upgrade\", id, path, headers, port? }\n * { type: \"ws-data\", id, data }\n * { type: \"ws-close\", id }\n *\n * Pod → API (this client sends):\n * { type: \"http-res\", id, statusCode, headers }\n * <binary: 4-byte BE requestId + response chunk>\n * { type: \"http-end\", id }\n * { type: \"ws-open\", id }\n * { type: \"ws-data\", id, data }\n * { type: \"ws-close\", id }\n */\nimport { WebSocket } from \"ws\";\nimport * as http from \"node:http\";\nimport { URL } from \"node:url\";\nimport { createServiceLogger } from \"./utils/logger.js\";\n\nconst logger = createServiceLogger(\"TunnelClient\");\n\nconst DEFAULT_PORT = 3050;\nconst RECONNECT_MIN_MS = 2_000;\nconst RECONNECT_MAX_MS = 30_000;\n\nconst apiUrl = process.env.CONVEYOR_API_URL;\nconst taskToken = process.env.CONVEYOR_TASK_TOKEN;\n\nif (!apiUrl || !taskToken) {\n logger.error(\"CONVEYOR_API_URL and CONVEYOR_TASK_TOKEN are required\");\n process.exit(1);\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface HttpReqFrame {\n type: \"http-req\";\n id: number;\n method: string;\n path: string;\n headers: Record<string, string>;\n port?: number;\n}\n\ninterface HttpReqEndFrame {\n type: \"http-req-end\";\n id: number;\n}\n\ninterface WsUpgradeFrame {\n type: \"ws-upgrade\";\n id: number;\n path: string;\n headers: Record<string, string>;\n port?: number;\n}\n\ninterface WsDataFrame {\n type: \"ws-data\";\n id: number;\n data: string;\n /** When true, `data` is base64-encoded binary; otherwise it's a text frame. */\n binary?: boolean;\n}\n\ninterface WsCloseFrame {\n type: \"ws-close\";\n id: number;\n}\n\ntype IncomingFrame = HttpReqFrame | HttpReqEndFrame | WsUpgradeFrame | WsDataFrame | WsCloseFrame;\n\n// ---------------------------------------------------------------------------\n// Active request/websocket tracking\n// ---------------------------------------------------------------------------\n\nconst activeRequests = new Map<number, http.ClientRequest>();\nconst activeWebSockets = new Map<number, WebSocket>();\n\n// ---------------------------------------------------------------------------\n// Connection\n// ---------------------------------------------------------------------------\n\nlet reconnectDelay = RECONNECT_MIN_MS;\nlet shuttingDown = false;\n\nfunction buildTunnelUrl(): string {\n // apiUrl and taskToken are guaranteed non-null by the guard at module init\n const base = (apiUrl as string).replace(/^http/, \"ws\");\n return `${base}/api/tunnel?token=${encodeURIComponent(taskToken as string)}`;\n}\n\nfunction connect(): void {\n if (shuttingDown) return;\n\n const url = buildTunnelUrl();\n logger.info(\"Connecting...\");\n const ws = new WebSocket(url);\n\n ws.on(\"open\", () => {\n logger.info(\"Connected\");\n reconnectDelay = RECONNECT_MIN_MS;\n });\n\n ws.on(\"message\", (data: Buffer | string) => {\n handleMessage(ws, data);\n });\n\n ws.on(\"close\", (code: number) => {\n logger.info(`Disconnected (code=${code}), reconnecting in ${reconnectDelay}ms`);\n cleanupAll();\n if (!shuttingDown) {\n setTimeout(connect, reconnectDelay);\n reconnectDelay = Math.min(reconnectDelay * 2, RECONNECT_MAX_MS);\n }\n });\n\n ws.on(\"error\", (err: Error) => {\n logger.error(`WebSocket error: ${err.message}`);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Frame handling\n// ---------------------------------------------------------------------------\n\nfunction handleMessage(tunnel: WebSocket, raw: Buffer | string): void {\n // Binary frame: 4-byte BE request ID + body chunk (request body from client)\n const buf = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);\n\n if (buf.length > 0 && buf[0] !== 0x7b) {\n // Binary data frame — forward body chunk to active request\n if (buf.length <= 4) return;\n const id = buf.readUInt32BE(0);\n const req = activeRequests.get(id);\n if (req) req.write(buf.subarray(4));\n return;\n }\n\n // JSON control frame\n let frame: IncomingFrame;\n try {\n frame = JSON.parse(buf.toString(\"utf-8\"));\n } catch {\n return;\n }\n\n switch (frame.type) {\n case \"http-req\":\n handleHttpReq(tunnel, frame);\n break;\n case \"http-req-end\":\n handleHttpReqEnd(frame);\n break;\n case \"ws-upgrade\":\n handleWsUpgrade(tunnel, frame);\n break;\n case \"ws-data\":\n handleWsData(frame);\n break;\n case \"ws-close\":\n handleWsClose(frame);\n break;\n }\n}\n\nfunction handleHttpReq(tunnel: WebSocket, frame: HttpReqFrame): void {\n const port = frame.port ?? DEFAULT_PORT;\n const url = new URL(frame.path, `http://127.0.0.1:${port}`);\n\n const req = http.request(\n {\n hostname: \"127.0.0.1\",\n port,\n path: url.pathname + url.search,\n method: frame.method,\n headers: frame.headers,\n },\n (res) => {\n // Send response headers\n const headers: Record<string, string> = {};\n for (const [key, val] of Object.entries(res.headers)) {\n if (typeof val === \"string\") headers[key] = val;\n else if (Array.isArray(val)) headers[key] = val.join(\", \");\n }\n\n sendJson(tunnel, {\n type: \"http-res\",\n id: frame.id,\n statusCode: res.statusCode ?? 200,\n headers,\n });\n\n // Stream response body as binary frames\n res.on(\"data\", (chunk: Buffer) => {\n const out = Buffer.alloc(4 + chunk.length);\n out.writeUInt32BE(frame.id, 0);\n chunk.copy(out, 4);\n if (tunnel.readyState === WebSocket.OPEN) tunnel.send(out);\n });\n\n res.on(\"end\", () => {\n sendJson(tunnel, { type: \"http-end\", id: frame.id });\n activeRequests.delete(frame.id);\n });\n\n res.on(\"error\", () => {\n sendJson(tunnel, { type: \"http-end\", id: frame.id });\n activeRequests.delete(frame.id);\n });\n },\n );\n\n req.on(\"error\", (err) => {\n logger.error(`HTTP request error (id=${frame.id}): ${err.message}`);\n sendJson(tunnel, {\n type: \"http-res\",\n id: frame.id,\n statusCode: 502,\n headers: { \"content-type\": \"application/json\" },\n });\n const body = Buffer.from(JSON.stringify({ error: `Upstream error: ${err.message}` }));\n const out = Buffer.alloc(4 + body.length);\n out.writeUInt32BE(frame.id, 0);\n body.copy(out, 4);\n if (tunnel.readyState === WebSocket.OPEN) tunnel.send(out);\n sendJson(tunnel, { type: \"http-end\", id: frame.id });\n activeRequests.delete(frame.id);\n });\n\n activeRequests.set(frame.id, req);\n}\n\nfunction handleHttpReqEnd(frame: HttpReqEndFrame): void {\n const req = activeRequests.get(frame.id);\n if (req) req.end();\n}\n\nfunction handleWsUpgrade(tunnel: WebSocket, frame: WsUpgradeFrame): void {\n const port = frame.port ?? DEFAULT_PORT;\n const url = `ws://127.0.0.1:${port}${frame.path}`;\n\n // Strip hop-by-hop headers and the Sec-WebSocket-* headers — the upstream\n // WebSocket constructor generates its own. Forwarding the browser's key\n // would only confuse the upstream.\n const passthrough: Record<string, string> = {};\n for (const [k, v] of Object.entries(frame.headers)) {\n const key = k.toLowerCase();\n if (key === \"host\") continue;\n if (key === \"connection\") continue;\n if (key === \"upgrade\") continue;\n if (key.startsWith(\"sec-websocket-\")) continue;\n passthrough[k] = v;\n }\n\n const upstream = new WebSocket(url, { headers: passthrough });\n\n upstream.on(\"open\", () => {\n sendJson(tunnel, { type: \"ws-open\", id: frame.id });\n activeWebSockets.set(frame.id, upstream);\n });\n\n upstream.on(\"message\", (data: Buffer | ArrayBuffer | Buffer[], isBinary: boolean) => {\n const buf = Buffer.isBuffer(data)\n ? data\n : Array.isArray(data)\n ? Buffer.concat(data)\n : Buffer.from(data);\n sendJson(tunnel, {\n type: \"ws-data\",\n id: frame.id,\n data: isBinary ? buf.toString(\"base64\") : buf.toString(\"utf-8\"),\n binary: isBinary,\n });\n });\n\n upstream.on(\"close\", () => {\n sendJson(tunnel, { type: \"ws-close\", id: frame.id });\n activeWebSockets.delete(frame.id);\n });\n\n upstream.on(\"error\", (err: Error) => {\n logger.error(`WS upstream error (id=${frame.id}): ${err.message}`);\n sendJson(tunnel, { type: \"ws-close\", id: frame.id });\n activeWebSockets.delete(frame.id);\n });\n}\n\nfunction handleWsData(frame: WsDataFrame): void {\n const ws = activeWebSockets.get(frame.id);\n if (!ws || ws.readyState !== WebSocket.OPEN) return;\n if (frame.binary && frame.data) {\n ws.send(Buffer.from(frame.data, \"base64\"), { binary: true });\n } else {\n ws.send(frame.data);\n }\n}\n\nfunction handleWsClose(frame: WsCloseFrame): void {\n const ws = activeWebSockets.get(frame.id);\n if (ws) {\n ws.close(1000);\n activeWebSockets.delete(frame.id);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sendJson(ws: WebSocket, obj: Record<string, unknown>): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(obj));\n }\n}\n\nfunction cleanupAll(): void {\n for (const [id, req] of activeRequests) {\n req.destroy();\n activeRequests.delete(id);\n }\n for (const [id, ws] of activeWebSockets) {\n ws.close(1000);\n activeWebSockets.delete(id);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Lifecycle\n// ---------------------------------------------------------------------------\n\nprocess.on(\"SIGTERM\", () => {\n logger.info(\"SIGTERM received, shutting down\");\n shuttingDown = true;\n cleanupAll();\n process.exit(0);\n});\n\nprocess.on(\"SIGINT\", () => {\n logger.info(\"SIGINT received, shutting down\");\n shuttingDown = true;\n cleanupAll();\n process.exit(0);\n});\n\nconnect();\n"],"mappings":"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","names":[]}
|