@slock-ai/daemon 0.56.1 → 0.57.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/dist/{chunk-RIBO24KM.js → chunk-DQN3TW2I.js} +921 -647
- package/dist/cli/index.js +69 -11
- package/dist/core.js +2 -6
- package/dist/index.js +1 -2
- package/package.json +3 -1
- package/dist/chat-bridge.js +0 -96
- package/dist/chunk-M2KQBJR3.js +0 -260
package/dist/cli/index.js
CHANGED
|
@@ -15110,10 +15110,22 @@ var channelAddMemberOperationSchema = external_exports.object({
|
|
|
15110
15110
|
agents: external_exports.array(idOrHandleSchema).max(64).optional(),
|
|
15111
15111
|
draftHint: draftHintSchema
|
|
15112
15112
|
});
|
|
15113
|
+
var integrationApproveAgentLoginOperationSchema = external_exports.object({
|
|
15114
|
+
type: external_exports.literal("integration:approve_agent_login"),
|
|
15115
|
+
requestId: uuidSchema,
|
|
15116
|
+
agentId: uuidSchema,
|
|
15117
|
+
agentName: external_exports.string().trim().min(1).max(120),
|
|
15118
|
+
clientId: uuidSchema,
|
|
15119
|
+
clientKey: external_exports.string().trim().min(1).max(120),
|
|
15120
|
+
clientName: external_exports.string().trim().min(1).max(120),
|
|
15121
|
+
scopes: external_exports.array(external_exports.string().trim().min(1).max(120)).max(64),
|
|
15122
|
+
draftHint: draftHintSchema
|
|
15123
|
+
});
|
|
15113
15124
|
var actionCardActionSchema = external_exports.discriminatedUnion("type", [
|
|
15114
15125
|
channelCreateOperationSchema,
|
|
15115
15126
|
agentCreateOperationSchema,
|
|
15116
|
-
channelAddMemberOperationSchema
|
|
15127
|
+
channelAddMemberOperationSchema,
|
|
15128
|
+
integrationApproveAgentLoginOperationSchema
|
|
15117
15129
|
]);
|
|
15118
15130
|
function validateActionCardAction(action) {
|
|
15119
15131
|
if (action.type === "agent:create") {
|
|
@@ -15741,7 +15753,8 @@ var knowledgeGetCommand = defineCommand(
|
|
|
15741
15753
|
flags: "--trace-id <id>",
|
|
15742
15754
|
description: "Optional trace id for correlation in the knowledge event"
|
|
15743
15755
|
}
|
|
15744
|
-
]
|
|
15756
|
+
],
|
|
15757
|
+
helpAfter: "\nTopics:\n Run `slock manual get index` to list available manual topics.\n"
|
|
15745
15758
|
},
|
|
15746
15759
|
async (ctx, topic, opts) => {
|
|
15747
15760
|
const agentContext = ctx.loadAgentContext();
|
|
@@ -17766,6 +17779,24 @@ function formatIntegrationList(data) {
|
|
|
17766
17779
|
return lines.join("\n");
|
|
17767
17780
|
}
|
|
17768
17781
|
function formatIntegrationLogin(data) {
|
|
17782
|
+
if (data.status === "approval_required") {
|
|
17783
|
+
const lines2 = [
|
|
17784
|
+
`Human approval required: ${data.service.name}`,
|
|
17785
|
+
`service: ${data.service.clientId}`,
|
|
17786
|
+
`id: ${data.service.id}`,
|
|
17787
|
+
`scopes: ${data.scopes.length > 0 ? data.scopes.join(", ") : "-"}`,
|
|
17788
|
+
`request id: ${data.approval?.requestId ?? data.requestId}`
|
|
17789
|
+
];
|
|
17790
|
+
if (data.approval?.actionCardMessageId) {
|
|
17791
|
+
lines2.push(`approval card: ${data.approval.actionCardMessageId}`);
|
|
17792
|
+
lines2.push(`target: ${data.approval.target ?? "-"}`);
|
|
17793
|
+
lines2.push("next: ask a server owner/admin to approve the card, then rerun this command");
|
|
17794
|
+
} else {
|
|
17795
|
+
lines2.push("approval card: not posted");
|
|
17796
|
+
lines2.push("next: rerun with --target <channel-or-thread> to post a human approval card, or ask a server owner/admin to approve this request");
|
|
17797
|
+
}
|
|
17798
|
+
return lines2.join("\n");
|
|
17799
|
+
}
|
|
17769
17800
|
const verb = data.status === "already_logged_in" ? "Already logged in" : "Agent login ready";
|
|
17770
17801
|
const lines = [
|
|
17771
17802
|
`${verb}: ${data.service.name}`,
|
|
@@ -17845,6 +17876,7 @@ var integrationLoginCommand = defineCommand(
|
|
|
17845
17876
|
return previous;
|
|
17846
17877
|
}
|
|
17847
17878
|
},
|
|
17879
|
+
{ flags: "--target <target>", description: "Conversation target to post a human approval card when approval is required" },
|
|
17848
17880
|
{ flags: "--json", description: "Emit machine-readable JSON" }
|
|
17849
17881
|
]
|
|
17850
17882
|
},
|
|
@@ -17861,7 +17893,8 @@ var integrationLoginCommand = defineCommand(
|
|
|
17861
17893
|
`/internal/agent/${encodeURIComponent(agentContext.agentId)}/integrations/login`,
|
|
17862
17894
|
{
|
|
17863
17895
|
service,
|
|
17864
|
-
scopes
|
|
17896
|
+
scopes,
|
|
17897
|
+
target: opts.target?.trim() || void 0
|
|
17865
17898
|
}
|
|
17866
17899
|
);
|
|
17867
17900
|
if (!res.ok || !res.data) {
|
|
@@ -17885,6 +17918,13 @@ import fs3 from "fs";
|
|
|
17885
17918
|
import os3 from "os";
|
|
17886
17919
|
import path5 from "path";
|
|
17887
17920
|
var AGENT_MANIFEST_MAX_BYTES = 64 * 1024;
|
|
17921
|
+
var AgentManifestFetchError = class extends Error {
|
|
17922
|
+
constructor(message, status) {
|
|
17923
|
+
super(message);
|
|
17924
|
+
this.status = status;
|
|
17925
|
+
this.name = "AgentManifestFetchError";
|
|
17926
|
+
}
|
|
17927
|
+
};
|
|
17888
17928
|
function isRecord(value) {
|
|
17889
17929
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
17890
17930
|
}
|
|
@@ -17971,7 +18011,7 @@ async function fetchAgentManifest(url2) {
|
|
|
17971
18011
|
throw new Error("manifest redirects must remain on credential-free HTTPS URLs");
|
|
17972
18012
|
}
|
|
17973
18013
|
if (!response.ok) {
|
|
17974
|
-
throw new
|
|
18014
|
+
throw new AgentManifestFetchError(`manifest fetch failed with HTTP ${response.status}`, response.status);
|
|
17975
18015
|
}
|
|
17976
18016
|
const contentType = response.headers.get("content-type") ?? "";
|
|
17977
18017
|
if (contentType && !contentType.includes("application/json")) {
|
|
@@ -18079,6 +18119,12 @@ function describeNoLocalEnv(manifest) {
|
|
|
18079
18119
|
}
|
|
18080
18120
|
return "manifest does not request local CLI env exports";
|
|
18081
18121
|
}
|
|
18122
|
+
function describeMissingManifest(input) {
|
|
18123
|
+
if (input.manifestUrl) {
|
|
18124
|
+
return "agent behavior manifest was not found; no local CLI env is required";
|
|
18125
|
+
}
|
|
18126
|
+
return `${input.service.name} does not expose an agent behavior manifest; no local CLI env is required`;
|
|
18127
|
+
}
|
|
18082
18128
|
var IntegrationEnvError = class extends Error {
|
|
18083
18129
|
constructor(code, message) {
|
|
18084
18130
|
super(message);
|
|
@@ -18088,16 +18134,28 @@ var IntegrationEnvError = class extends Error {
|
|
|
18088
18134
|
};
|
|
18089
18135
|
async function resolveIntegrationEnv(input) {
|
|
18090
18136
|
if (!input.service.agentManifestUrl) {
|
|
18091
|
-
|
|
18092
|
-
"
|
|
18093
|
-
|
|
18094
|
-
|
|
18137
|
+
return {
|
|
18138
|
+
kind: "no-local-env",
|
|
18139
|
+
service: input.service,
|
|
18140
|
+
manifestUrl: null,
|
|
18141
|
+
manifest: null,
|
|
18142
|
+
message: describeMissingManifest({ service: input.service })
|
|
18143
|
+
};
|
|
18095
18144
|
}
|
|
18096
18145
|
const fetchManifestImpl = input.fetchManifest ?? fetchAgentManifest;
|
|
18097
18146
|
let manifest;
|
|
18098
18147
|
try {
|
|
18099
18148
|
manifest = await fetchManifestImpl(input.service.agentManifestUrl);
|
|
18100
18149
|
} catch (err) {
|
|
18150
|
+
if (err instanceof AgentManifestFetchError && (err.status === 404 || err.status === 410)) {
|
|
18151
|
+
return {
|
|
18152
|
+
kind: "no-local-env",
|
|
18153
|
+
service: input.service,
|
|
18154
|
+
manifestUrl: input.service.agentManifestUrl,
|
|
18155
|
+
manifest: null,
|
|
18156
|
+
message: describeMissingManifest({ service: input.service, manifestUrl: input.service.agentManifestUrl })
|
|
18157
|
+
};
|
|
18158
|
+
}
|
|
18101
18159
|
throw new IntegrationEnvError("INTEGRATION_MANIFEST_INVALID", err.message);
|
|
18102
18160
|
}
|
|
18103
18161
|
if (manifest.execution.mode !== "local_cli" || manifest.credential_boundary?.storage !== "per_agent_home") {
|
|
@@ -18128,8 +18186,8 @@ async function resolveIntegrationEnv(input) {
|
|
|
18128
18186
|
function formatNoLocalEnv(input) {
|
|
18129
18187
|
return [
|
|
18130
18188
|
`# Slock integration profile for ${input.service}`,
|
|
18131
|
-
`# manifest: ${input.manifestUrl}
|
|
18132
|
-
"# No local CLI environment exports are required
|
|
18189
|
+
input.manifestUrl ? `# manifest: ${input.manifestUrl}` : "# manifest: none declared",
|
|
18190
|
+
"# No local CLI environment exports are required for this service.",
|
|
18133
18191
|
`# ${input.message}`,
|
|
18134
18192
|
"# Slock did not set HOME/XDG exports and did not execute manifest commands."
|
|
18135
18193
|
].join("\n");
|
|
@@ -18175,7 +18233,7 @@ var integrationEnvCommand = defineCommand(
|
|
|
18175
18233
|
service: resolution.service.clientId,
|
|
18176
18234
|
manifestUrl: resolution.manifestUrl,
|
|
18177
18235
|
requiresLocalEnv: false,
|
|
18178
|
-
command: resolution.manifest
|
|
18236
|
+
command: resolution.manifest?.execution.command ?? null,
|
|
18179
18237
|
env: {},
|
|
18180
18238
|
message: resolution.message
|
|
18181
18239
|
}
|
package/dist/core.js
CHANGED
|
@@ -5,14 +5,11 @@ import {
|
|
|
5
5
|
detectRuntimes,
|
|
6
6
|
parseDaemonCliArgs,
|
|
7
7
|
readDaemonVersion,
|
|
8
|
-
resolveChatBridgePath,
|
|
9
8
|
resolveSlockCliPath,
|
|
10
9
|
resolveWorkspaceDirectoryPath,
|
|
11
|
-
scanWorkspaceDirectories
|
|
12
|
-
} from "./chunk-RIBO24KM.js";
|
|
13
|
-
import {
|
|
10
|
+
scanWorkspaceDirectories,
|
|
14
11
|
subscribeDaemonLogs
|
|
15
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-DQN3TW2I.js";
|
|
16
13
|
export {
|
|
17
14
|
DAEMON_CLI_USAGE,
|
|
18
15
|
DaemonCore,
|
|
@@ -20,7 +17,6 @@ export {
|
|
|
20
17
|
detectRuntimes,
|
|
21
18
|
parseDaemonCliArgs,
|
|
22
19
|
readDaemonVersion,
|
|
23
|
-
resolveChatBridgePath,
|
|
24
20
|
resolveSlockCliPath,
|
|
25
21
|
resolveWorkspaceDirectoryPath,
|
|
26
22
|
scanWorkspaceDirectories,
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slock-ai/daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"slock-daemon": "dist/index.js"
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"prepack": "pnpm run build",
|
|
31
31
|
"prepublishOnly": "pnpm run build",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
|
+
"generate:slock-cli-guide": "tsx scripts/generate-slock-cli-guide.ts",
|
|
34
|
+
"check:slock-cli-guide-fresh": "pnpm generate:slock-cli-guide && git diff --exit-code ../../docs/agent-knowledge/slock-cli-overview.mdx",
|
|
33
35
|
"release:patch": "npm version patch --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
|
|
34
36
|
"release:minor": "npm version minor --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
|
|
35
37
|
"release:major": "npm version major --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
|
package/dist/chat-bridge.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
daemonFetch,
|
|
4
|
-
executeJsonRequest
|
|
5
|
-
} from "./chunk-M2KQBJR3.js";
|
|
6
|
-
|
|
7
|
-
// src/chat-bridge.ts
|
|
8
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
-
import { z } from "zod";
|
|
11
|
-
|
|
12
|
-
// src/perfAttribution.ts
|
|
13
|
-
var PERF_CALLER_CONTEXT_HEADER = "X-Perf-Caller-Context";
|
|
14
|
-
var AGENT_ORIGINATED_CALLER_CONTEXT = "agent_originated";
|
|
15
|
-
function buildChatBridgeCommonHeaders(authToken2, { includeContentType = true } = {}) {
|
|
16
|
-
const headers = {
|
|
17
|
-
[PERF_CALLER_CONTEXT_HEADER]: AGENT_ORIGINATED_CALLER_CONTEXT
|
|
18
|
-
};
|
|
19
|
-
if (includeContentType) {
|
|
20
|
-
headers["Content-Type"] = "application/json";
|
|
21
|
-
}
|
|
22
|
-
if (authToken2) {
|
|
23
|
-
headers.Authorization = `Bearer ${authToken2}`;
|
|
24
|
-
}
|
|
25
|
-
return headers;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// src/chat-bridge.ts
|
|
29
|
-
var args = process.argv.slice(2);
|
|
30
|
-
var agentId = "";
|
|
31
|
-
var serverUrl = "http://localhost:3001";
|
|
32
|
-
var authToken = "";
|
|
33
|
-
var launchId = "";
|
|
34
|
-
for (let i = 0; i < args.length; i++) {
|
|
35
|
-
if (args[i] === "--agent-id" && args[i + 1]) agentId = args[++i];
|
|
36
|
-
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
37
|
-
if (args[i] === "--auth-token" && args[i + 1]) authToken = args[++i];
|
|
38
|
-
if (args[i] === "--launch-id" && args[i + 1]) launchId = args[++i];
|
|
39
|
-
}
|
|
40
|
-
if (!agentId) {
|
|
41
|
-
console.error("Missing --agent-id");
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
var commonHeaders = buildChatBridgeCommonHeaders(authToken);
|
|
45
|
-
var runtimeActionHeaders = {
|
|
46
|
-
...commonHeaders,
|
|
47
|
-
...launchId ? { "X-Agent-Launch-Id": launchId } : {}
|
|
48
|
-
};
|
|
49
|
-
function bridgeFetch(url, init = {}) {
|
|
50
|
-
return daemonFetch(url, init);
|
|
51
|
-
}
|
|
52
|
-
var server = new McpServer({
|
|
53
|
-
name: "chat",
|
|
54
|
-
version: "1.0.0"
|
|
55
|
-
});
|
|
56
|
-
var RUNTIME_PROFILE_MIGRATION_DONE_TOOL_NAME = "runtime_profile_migration_done";
|
|
57
|
-
server.tool(
|
|
58
|
-
RUNTIME_PROFILE_MIGRATION_DONE_TOOL_NAME,
|
|
59
|
-
"Deprecated compatibility no-op. Runtime Profile changes now reset the session automatically; this tool is kept so old prompts can acknowledge without blocking.",
|
|
60
|
-
{
|
|
61
|
-
migration_key: z.string().optional().describe("Ignored legacy migration key, if an old prompt provided one")
|
|
62
|
-
},
|
|
63
|
-
async ({ migration_key }) => {
|
|
64
|
-
const key = migration_key?.trim() ?? "";
|
|
65
|
-
try {
|
|
66
|
-
const { response: res, data } = await executeJsonRequest(
|
|
67
|
-
`${serverUrl}/internal/agent/${agentId}/runtime-profile/migration-done`,
|
|
68
|
-
{
|
|
69
|
-
method: "POST",
|
|
70
|
-
headers: runtimeActionHeaders,
|
|
71
|
-
body: JSON.stringify({ migrationKey: key })
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
toolName: RUNTIME_PROFILE_MIGRATION_DONE_TOOL_NAME,
|
|
75
|
-
fetchImpl: bridgeFetch
|
|
76
|
-
}
|
|
77
|
-
);
|
|
78
|
-
if (!res.ok) {
|
|
79
|
-
return {
|
|
80
|
-
isError: true,
|
|
81
|
-
content: [{ type: "text", text: `Error: ${data.error || "Runtime Profile reset acknowledgment failed"}` }]
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
content: [{ type: "text", text: data.message || "Runtime Profile migration acknowledgments are deprecated; runtime changes reset the session automatically." }]
|
|
86
|
-
};
|
|
87
|
-
} catch (err) {
|
|
88
|
-
return {
|
|
89
|
-
isError: true,
|
|
90
|
-
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
);
|
|
95
|
-
var transport = new StdioServerTransport();
|
|
96
|
-
await server.connect(transport);
|
package/dist/chunk-M2KQBJR3.js
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
// src/logger.ts
|
|
2
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
3
|
-
function timestamp() {
|
|
4
|
-
const d = /* @__PURE__ */ new Date();
|
|
5
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
6
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
7
|
-
}
|
|
8
|
-
function format(level, msg) {
|
|
9
|
-
return `${timestamp()} [${level}] ${msg}`;
|
|
10
|
-
}
|
|
11
|
-
function emit(event) {
|
|
12
|
-
for (const listener of listeners) {
|
|
13
|
-
listener(event);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
function subscribeDaemonLogs(listener) {
|
|
17
|
-
listeners.add(listener);
|
|
18
|
-
return () => {
|
|
19
|
-
listeners.delete(listener);
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
var logger = {
|
|
23
|
-
info(msg) {
|
|
24
|
-
const line = format("INFO", msg);
|
|
25
|
-
console.log(line);
|
|
26
|
-
emit({ level: "INFO", line, message: msg });
|
|
27
|
-
},
|
|
28
|
-
warn(msg) {
|
|
29
|
-
const line = format("WARN", msg);
|
|
30
|
-
console.warn(line);
|
|
31
|
-
emit({ level: "WARN", line, message: msg });
|
|
32
|
-
},
|
|
33
|
-
error(msg, err) {
|
|
34
|
-
const line = format("ERROR", msg);
|
|
35
|
-
if (err) {
|
|
36
|
-
console.error(line, err);
|
|
37
|
-
} else {
|
|
38
|
-
console.error(line);
|
|
39
|
-
}
|
|
40
|
-
emit({ level: "ERROR", line, message: msg, error: err });
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// src/chatBridgeRequest.ts
|
|
45
|
-
var DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS = Number.parseInt(
|
|
46
|
-
process.env.SLOCK_CHAT_BRIDGE_TOOL_TIMEOUT_MS || "",
|
|
47
|
-
10
|
|
48
|
-
) || 6e4;
|
|
49
|
-
var ChatBridgeToolTimeoutError = class extends Error {
|
|
50
|
-
toolName;
|
|
51
|
-
target;
|
|
52
|
-
timeoutMs;
|
|
53
|
-
durationMs;
|
|
54
|
-
constructor(toolName, target, timeoutMs, durationMs) {
|
|
55
|
-
super(`${toolName} timed out after ${timeoutMs}ms${target ? ` (target: ${target})` : ""}`);
|
|
56
|
-
this.name = "ChatBridgeToolTimeoutError";
|
|
57
|
-
this.toolName = toolName;
|
|
58
|
-
this.target = target;
|
|
59
|
-
this.timeoutMs = timeoutMs;
|
|
60
|
-
this.durationMs = durationMs;
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
function describeError(err) {
|
|
64
|
-
if (err instanceof Error) return `${err.name}: ${err.message}`;
|
|
65
|
-
return String(err);
|
|
66
|
-
}
|
|
67
|
-
async function executeJsonRequest(url, init, {
|
|
68
|
-
toolName,
|
|
69
|
-
target = null,
|
|
70
|
-
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
71
|
-
fetchImpl,
|
|
72
|
-
now = () => Date.now(),
|
|
73
|
-
warn = (message) => logger.warn(message)
|
|
74
|
-
}) {
|
|
75
|
-
const startedAt = now();
|
|
76
|
-
const timeoutController = new AbortController();
|
|
77
|
-
const signals = [timeoutController.signal];
|
|
78
|
-
if (init.signal) signals.push(init.signal);
|
|
79
|
-
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
80
|
-
const timeout = setTimeout(() => {
|
|
81
|
-
timeoutController.abort();
|
|
82
|
-
}, timeoutMs);
|
|
83
|
-
timeout.unref?.();
|
|
84
|
-
try {
|
|
85
|
-
const response = await fetchImpl(url, { ...init, signal });
|
|
86
|
-
const data = await response.json();
|
|
87
|
-
return { response, data, durationMs: now() - startedAt };
|
|
88
|
-
} catch (err) {
|
|
89
|
-
const durationMs = now() - startedAt;
|
|
90
|
-
if (timeoutController.signal.aborted && !init.signal?.aborted) {
|
|
91
|
-
warn(
|
|
92
|
-
`[ChatBridgeTimeout] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} timeout_ms=${timeoutMs} outcome=timeout`
|
|
93
|
-
);
|
|
94
|
-
throw new ChatBridgeToolTimeoutError(toolName, target, timeoutMs, durationMs);
|
|
95
|
-
}
|
|
96
|
-
warn(
|
|
97
|
-
`[ChatBridgeError] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} outcome=error error=${describeError(err)}`
|
|
98
|
-
);
|
|
99
|
-
throw err;
|
|
100
|
-
} finally {
|
|
101
|
-
clearTimeout(timeout);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
async function executeResponseRequest(url, init, {
|
|
105
|
-
toolName,
|
|
106
|
-
target = null,
|
|
107
|
-
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
108
|
-
fetchImpl,
|
|
109
|
-
now = () => Date.now(),
|
|
110
|
-
warn = (message) => logger.warn(message)
|
|
111
|
-
}) {
|
|
112
|
-
const startedAt = now();
|
|
113
|
-
const timeoutController = new AbortController();
|
|
114
|
-
const signals = [timeoutController.signal];
|
|
115
|
-
if (init.signal) signals.push(init.signal);
|
|
116
|
-
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
117
|
-
const timeout = setTimeout(() => {
|
|
118
|
-
timeoutController.abort();
|
|
119
|
-
}, timeoutMs);
|
|
120
|
-
timeout.unref?.();
|
|
121
|
-
try {
|
|
122
|
-
const response = await fetchImpl(url, { ...init, signal });
|
|
123
|
-
return { response, durationMs: now() - startedAt };
|
|
124
|
-
} catch (err) {
|
|
125
|
-
const durationMs = now() - startedAt;
|
|
126
|
-
if (timeoutController.signal.aborted && !init.signal?.aborted) {
|
|
127
|
-
warn(
|
|
128
|
-
`[ChatBridgeTimeout] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} timeout_ms=${timeoutMs} outcome=timeout`
|
|
129
|
-
);
|
|
130
|
-
throw new ChatBridgeToolTimeoutError(toolName, target, timeoutMs, durationMs);
|
|
131
|
-
}
|
|
132
|
-
warn(
|
|
133
|
-
`[ChatBridgeError] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} outcome=error error=${describeError(err)}`
|
|
134
|
-
);
|
|
135
|
-
throw err;
|
|
136
|
-
} finally {
|
|
137
|
-
clearTimeout(timeout);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// src/proxy.ts
|
|
142
|
-
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
143
|
-
import { ProxyAgent } from "undici";
|
|
144
|
-
var fetchDispatcherCache = /* @__PURE__ */ new Map();
|
|
145
|
-
function getFetchPreResponseTimeoutMs(env) {
|
|
146
|
-
const parsed = Number.parseInt(env.SLOCK_DAEMON_FETCH_PRE_RESPONSE_TIMEOUT_MS || "", 10);
|
|
147
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : 3e4;
|
|
148
|
-
}
|
|
149
|
-
function getDefaultPort(protocol) {
|
|
150
|
-
switch (protocol) {
|
|
151
|
-
case "https:":
|
|
152
|
-
case "wss:":
|
|
153
|
-
return "443";
|
|
154
|
-
case "http:":
|
|
155
|
-
case "ws:":
|
|
156
|
-
return "80";
|
|
157
|
-
default:
|
|
158
|
-
return "";
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
function hostMatchesNoProxyEntry(hostname, ruleHost) {
|
|
162
|
-
if (!ruleHost) return false;
|
|
163
|
-
const normalizedRule = ruleHost.replace(/^\*\./, ".").replace(/^\./, "").toLowerCase();
|
|
164
|
-
const normalizedHost = hostname.toLowerCase();
|
|
165
|
-
return normalizedHost === normalizedRule || normalizedHost.endsWith(`.${normalizedRule}`);
|
|
166
|
-
}
|
|
167
|
-
function getProxyUrlForTarget(targetUrl, env) {
|
|
168
|
-
const protocol = new URL(targetUrl).protocol;
|
|
169
|
-
switch (protocol) {
|
|
170
|
-
case "wss:":
|
|
171
|
-
return env.WSS_PROXY || env.wss_proxy || env.HTTPS_PROXY || env.https_proxy || env.ALL_PROXY || env.all_proxy;
|
|
172
|
-
case "ws:":
|
|
173
|
-
return env.WS_PROXY || env.ws_proxy || env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
|
|
174
|
-
case "https:":
|
|
175
|
-
return env.HTTPS_PROXY || env.https_proxy || env.ALL_PROXY || env.all_proxy;
|
|
176
|
-
case "http:":
|
|
177
|
-
return env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
|
|
178
|
-
default:
|
|
179
|
-
return env.ALL_PROXY || env.all_proxy;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
function shouldBypassProxy(targetUrl, env) {
|
|
183
|
-
const rawNoProxy = env.NO_PROXY || env.no_proxy;
|
|
184
|
-
if (!rawNoProxy) return false;
|
|
185
|
-
const url = new URL(targetUrl);
|
|
186
|
-
const hostname = url.hostname.toLowerCase();
|
|
187
|
-
const port = url.port || getDefaultPort(url.protocol);
|
|
188
|
-
return rawNoProxy.split(",").map((entry) => entry.trim()).filter(Boolean).some((entry) => {
|
|
189
|
-
if (entry === "*") return true;
|
|
190
|
-
const [ruleHost, rulePort] = entry.split(":", 2);
|
|
191
|
-
if (rulePort && rulePort !== port) return false;
|
|
192
|
-
return hostMatchesNoProxyEntry(hostname, ruleHost);
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
function buildWebSocketOptions(wsUrl, env) {
|
|
196
|
-
const proxyUrl = getProxyUrlForTarget(wsUrl, env);
|
|
197
|
-
if (!proxyUrl) return void 0;
|
|
198
|
-
if (shouldBypassProxy(wsUrl, env)) return void 0;
|
|
199
|
-
return {
|
|
200
|
-
agent: new HttpsProxyAgent(proxyUrl)
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
function resolveProxyUrl(targetUrl, env) {
|
|
204
|
-
const proxyUrl = getProxyUrlForTarget(targetUrl, env);
|
|
205
|
-
if (!proxyUrl) return void 0;
|
|
206
|
-
if (shouldBypassProxy(targetUrl, env)) return void 0;
|
|
207
|
-
return proxyUrl;
|
|
208
|
-
}
|
|
209
|
-
function buildFetchDispatcher(targetUrl, env) {
|
|
210
|
-
const proxyUrl = resolveProxyUrl(targetUrl, env);
|
|
211
|
-
if (!proxyUrl) return void 0;
|
|
212
|
-
const cached = fetchDispatcherCache.get(proxyUrl);
|
|
213
|
-
if (cached) return cached;
|
|
214
|
-
const timeoutMs = getFetchPreResponseTimeoutMs(env);
|
|
215
|
-
const dispatcher = new ProxyAgent({
|
|
216
|
-
uri: proxyUrl,
|
|
217
|
-
// All three are pre-response and body-agnostic (see getFetchPreResponseTimeoutMs):
|
|
218
|
-
// headersTimeout = headers-hang leg; requestTls.timeout = CONNECT-tunnel
|
|
219
|
-
// establish leg; connect.timeout = socket to the proxy itself (defensive).
|
|
220
|
-
connect: { timeout: timeoutMs },
|
|
221
|
-
requestTls: { timeout: timeoutMs },
|
|
222
|
-
headersTimeout: timeoutMs
|
|
223
|
-
});
|
|
224
|
-
fetchDispatcherCache.set(proxyUrl, dispatcher);
|
|
225
|
-
return dispatcher;
|
|
226
|
-
}
|
|
227
|
-
function evictFetchDispatcher(targetUrl, env) {
|
|
228
|
-
const proxyUrl = resolveProxyUrl(targetUrl, env);
|
|
229
|
-
if (!proxyUrl) return false;
|
|
230
|
-
const cached = fetchDispatcherCache.get(proxyUrl);
|
|
231
|
-
if (!cached) return false;
|
|
232
|
-
fetchDispatcherCache.delete(proxyUrl);
|
|
233
|
-
void Promise.resolve().then(() => cached.close()).catch(() => cached.destroy?.(new Error("evicted"))).catch(() => {
|
|
234
|
-
});
|
|
235
|
-
return true;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// src/daemonFetch.ts
|
|
239
|
-
function withDaemonFetchProxy(input, init = {}, env = process.env) {
|
|
240
|
-
const dispatcher = buildFetchDispatcher(input.toString(), env);
|
|
241
|
-
return dispatcher ? { ...init, dispatcher } : init;
|
|
242
|
-
}
|
|
243
|
-
async function daemonFetch(input, init, env = process.env) {
|
|
244
|
-
try {
|
|
245
|
-
return await fetch(input, withDaemonFetchProxy(input, init, env));
|
|
246
|
-
} catch (err) {
|
|
247
|
-
evictFetchDispatcher(input.toString(), env);
|
|
248
|
-
throw err;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export {
|
|
253
|
-
subscribeDaemonLogs,
|
|
254
|
-
logger,
|
|
255
|
-
DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
256
|
-
executeJsonRequest,
|
|
257
|
-
executeResponseRequest,
|
|
258
|
-
buildWebSocketOptions,
|
|
259
|
-
daemonFetch
|
|
260
|
-
};
|