@poolzin/pool-bot 2026.3.25 → 2026.3.26
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/agents/model-fallback.js +5 -4
- package/dist/agents/tools/common.js +16 -201
- package/dist/auto-reply/auto-reply/reply/agent-runner-execution.js +502 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-helpers.js +65 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-memory.js +160 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-payloads.js +85 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-utils.js +101 -0
- package/dist/auto-reply/auto-reply/reply/bash-command.js +338 -0
- package/dist/auto-reply/auto-reply/reply/block-streaming.js +91 -0
- package/dist/auto-reply/auto-reply/reply/commands-approve.js +88 -0
- package/dist/auto-reply/auto-reply/reply/commands-bash.js +26 -0
- package/dist/auto-reply/auto-reply/reply/commands-compact.js +107 -0
- package/dist/auto-reply/auto-reply/reply/commands-config.js +241 -0
- package/dist/auto-reply/auto-reply/reply/commands-context-report.js +295 -0
- package/dist/auto-reply/auto-reply/reply/commands-context.js +30 -0
- package/dist/auto-reply/auto-reply/reply/commands-core.js +151 -0
- package/dist/auto-reply/auto-reply/reply/commands-export-session.js +163 -0
- package/dist/auto-reply/auto-reply/reply/commands-info.js +184 -0
- package/dist/auto-reply/auto-reply/reply/commands-models.js +299 -0
- package/dist/auto-reply/auto-reply/reply/commands-plugin.js +35 -0
- package/dist/auto-reply/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/auto-reply/auto-reply/reply/commands-setunset.js +73 -0
- package/dist/auto-reply/auto-reply/reply/commands-slash-parse.js +31 -0
- package/dist/auto-reply/auto-reply/reply/commands-status.js +178 -0
- package/dist/auto-reply/auto-reply/reply/commands-subagents.js +73 -0
- package/dist/auto-reply/auto-reply/reply/commands-system-prompt.js +117 -0
- package/dist/auto-reply/auto-reply/reply/commands-tts.js +231 -0
- package/dist/auto-reply/auto-reply/reply/directive-handling.impl.js +380 -0
- package/dist/auto-reply/auto-reply/reply/followup-runner.js +227 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives-apply.js +201 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives-utils.js +54 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives.js +332 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-inline-actions.js +258 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-run.js +297 -0
- package/dist/auto-reply/auto-reply/reply/groups.js +102 -0
- package/dist/auto-reply/auto-reply/reply/mentions.js +129 -0
- package/dist/auto-reply/auto-reply/reply/reply-delivery.js +92 -0
- package/dist/auto-reply/auto-reply/reply/reply-directives.js +30 -0
- package/dist/auto-reply/auto-reply/reply/reply-dispatcher.js +152 -0
- package/dist/auto-reply/auto-reply/reply/reply-elevated.js +166 -0
- package/dist/auto-reply/auto-reply/reply/reply-inline.js +28 -0
- package/dist/auto-reply/auto-reply/reply/reply-payloads.js +114 -0
- package/dist/auto-reply/auto-reply/reply/reply-reference.js +36 -0
- package/dist/auto-reply/auto-reply/reply/reply-tags.js +13 -0
- package/dist/auto-reply/auto-reply/reply/reply-threading.js +41 -0
- package/dist/auto-reply/auto-reply/reply/session-updates.js +233 -0
- package/dist/auto-reply/auto-reply/reply/stage-sandbox-media.js +146 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/a2ui/a2ui.bundle.js +2 -17772
- package/dist/canvas-host/a2ui/index.html +1 -307
- package/dist/channels/channels/directory-config.js +185 -0
- package/dist/channels/channels/discord/handle-action.guild-admin.js +332 -0
- package/dist/channels/channels/discord/handle-action.js +165 -0
- package/dist/channels/channels/discord.js +413 -0
- package/dist/channels/channels/dock.js +436 -0
- package/dist/channels/channels/index.js +51 -0
- package/dist/channels/channels/plugins/outbound/discord.js +101 -0
- package/dist/channels/channels/whatsapp.js +17 -0
- package/dist/channels/plugins/types.js +1 -1
- package/dist/channels/run-state-machine.js +7 -0
- package/dist/commands/models/auth.js +47 -1
- package/dist/commands-subagents/action-agents.js +44 -0
- package/dist/commands-subagents/action-focus.js +64 -0
- package/dist/commands-subagents/action-help.js +4 -0
- package/dist/commands-subagents/action-info.js +45 -0
- package/dist/commands-subagents/action-kill.js +60 -0
- package/dist/commands-subagents/action-list.js +44 -0
- package/dist/commands-subagents/action-log.js +29 -0
- package/dist/commands-subagents/action-send.js +119 -0
- package/dist/commands-subagents/action-spawn.js +52 -0
- package/dist/commands-subagents/action-unfocus.js +30 -0
- package/dist/commands-subagents/shared.js +303 -0
- package/dist/config/config.js +1 -8
- package/dist/config/types.secrets.js +61 -0
- package/dist/control-ui/assets/{index-D7shnQwQ.js → index-umCsvrWy.js} +884 -741
- package/dist/control-ui/assets/index-umCsvrWy.js.map +1 -0
- package/dist/control-ui/assets/pt-BR-DedEVAvY.js +2 -0
- package/dist/control-ui/assets/pt-BR-DedEVAvY.js.map +1 -0
- package/dist/control-ui/assets/zh-CN-CDzeklK-.js +2 -0
- package/dist/control-ui/assets/zh-CN-CDzeklK-.js.map +1 -0
- package/dist/control-ui/assets/zh-TW-BJCRYNWH.js +2 -0
- package/dist/control-ui/assets/zh-TW-BJCRYNWH.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/method-scopes.js +9 -1
- package/dist/gateway/node-pending-work.js +142 -0
- package/dist/gateway/protocol/index.js +5 -1
- package/dist/gateway/protocol/schema/nodes.js +18 -0
- package/dist/gateway/server-methods/nodes-pending.js +96 -0
- package/dist/gateway/server-methods-list.js +4 -0
- package/dist/gateway/server-methods.js +2 -0
- package/dist/imessage/channel.js +253 -0
- package/dist/imessage/monitor/echo-cache.js +70 -0
- package/dist/imessage/monitor/loop-rate-limiter.js +51 -0
- package/dist/imessage/monitor/reflection-guard.js +50 -0
- package/dist/imessage/monitor/sanitize-outbound.js +25 -0
- package/dist/imessage/monitor/self-chat-cache.js +75 -0
- package/dist/imessage/runtime.js +3 -0
- package/dist/infra/exec-approval-reply.js +7 -0
- package/dist/infra/tmp-openclaw-dir.js +84 -0
- package/dist/pairing/pairing-challenge.js +15 -0
- package/dist/plugin-sdk/account-id.d.ts +1 -0
- package/dist/plugin-sdk/agent-media-payload.d.ts +12 -0
- package/dist/plugin-sdk/allow-from.d.ts +27 -0
- package/dist/plugin-sdk/command-auth.d.ts +25 -0
- package/dist/plugin-sdk/command-auth.js +3 -1
- package/dist/plugin-sdk/config-paths.d.ts +6 -0
- package/dist/plugin-sdk/file-lock.d.ts +16 -0
- package/dist/plugin-sdk/index.d.ts +428 -0
- package/dist/plugin-sdk/index.js +237 -103
- package/dist/plugin-sdk/json-store.d.ts +5 -0
- package/dist/plugin-sdk/keyed-async-queue.d.ts +12 -0
- package/dist/plugin-sdk/onboarding.d.ts +11 -0
- package/dist/plugin-sdk/provider-auth-result.d.ts +14 -0
- package/dist/plugin-sdk/slack-message-actions.d.ts +11 -0
- package/dist/plugin-sdk/status-helpers.d.ts +25 -0
- package/dist/plugin-sdk/temp-path.d.ts +12 -0
- package/dist/plugin-sdk/text-chunking.d.ts +1 -0
- package/dist/plugin-sdk/tool-send.d.ts +4 -0
- package/dist/plugin-sdk/webhook-path.d.ts +6 -0
- package/dist/plugin-sdk/webhook-targets.d.ts +23 -0
- package/dist/plugin-sdk/windows-spawn.d.ts +39 -0
- package/dist/plugin-sdk-internal/accounts.js +6 -0
- package/dist/plugin-sdk-internal/discord.js +23 -0
- package/dist/plugin-sdk-internal/imessage.js +13 -0
- package/dist/plugin-sdk-internal/setup.js +9 -0
- package/dist/plugin-sdk-internal/signal.js +13 -0
- package/dist/plugin-sdk-internal/slack.js +22 -0
- package/dist/plugin-sdk-internal/telegram.js +32 -0
- package/dist/plugin-sdk-internal/whatsapp.js +29 -0
- package/dist/routing/session-key.js +4 -185
- package/dist/shared/pid-alive.js +2 -61
- package/dist/shared/process-scoped-map.js +5 -7
- package/dist/signal/channel.js +264 -0
- package/dist/signal/monitor/access-policy.js +60 -0
- package/dist/signal/runtime.js +3 -0
- package/dist/slack/account-inspect.js +135 -0
- package/dist/slack/blocks-input.js +7 -38
- package/dist/slack/channel.js +394 -0
- package/dist/slack/interactive-replies.js +28 -0
- package/dist/slack/monitor/channel-type.js +31 -0
- package/dist/slack/monitor/dm-auth.js +49 -0
- package/dist/slack/monitor/events/interactions.modal.js +137 -0
- package/dist/slack/monitor/events/message-subtype-handlers.js +68 -0
- package/dist/slack/monitor/events/system-event-context.js +29 -0
- package/dist/slack/monitor/events/system-event-test-harness.js +41 -0
- package/dist/slack/monitor/external-arg-menu-store.js +46 -0
- package/dist/slack/monitor/message-handler/prepare-content.js +69 -0
- package/dist/slack/monitor/message-handler/prepare-thread-context.js +91 -0
- package/dist/slack/monitor/message-handler/prepare.test-helpers.js +55 -0
- package/dist/slack/monitor/reconnect-policy.js +78 -0
- package/dist/slack/monitor/slash-commands.runtime.js +1 -0
- package/dist/slack/monitor/slash-dispatch.runtime.js +9 -0
- package/dist/slack/monitor/slash-skill-commands.runtime.js +1 -0
- package/dist/slack/resolve-allowlist-common.js +36 -0
- package/dist/slack/runtime.js +3 -0
- package/dist/slack/sent-thread-cache.js +61 -0
- package/dist/slack/truncate.js +10 -0
- package/dist/telegram/account-inspect.js +175 -0
- package/dist/telegram/allow-from.js +10 -0
- package/dist/telegram/api-fetch.js +18 -0
- package/dist/telegram/approval-buttons.js +30 -0
- package/dist/telegram/audit-membership-runtime.js +61 -0
- package/dist/telegram/bot/delivery.replies.js +508 -0
- package/dist/telegram/bot/delivery.resolve-media.js +227 -0
- package/dist/telegram/bot/delivery.send.js +132 -0
- package/dist/telegram/bot/reply-threading.js +46 -0
- package/dist/telegram/bot-message-context.body.js +186 -0
- package/dist/telegram/bot-message-context.session.js +207 -0
- package/dist/telegram/bot-message-context.types.js +1 -0
- package/dist/telegram/bot-native-commands.test-helpers.js +117 -0
- package/dist/telegram/bot.media.e2e-harness.js +81 -0
- package/dist/telegram/bot.media.test-utils.js +81 -0
- package/dist/telegram/channel-actions.js +225 -0
- package/dist/telegram/channel.js +515 -0
- package/dist/telegram/conversation-route.js +107 -0
- package/dist/telegram/delivery.js +2 -0
- package/dist/telegram/delivery.replies.js +508 -0
- package/dist/telegram/dm-access.js +86 -0
- package/dist/telegram/draft-stream.test-helpers.js +62 -0
- package/dist/telegram/exec-approvals-handler.js +281 -0
- package/dist/telegram/exec-approvals.js +62 -0
- package/dist/telegram/forum-service-message.js +22 -0
- package/dist/telegram/group-config-helpers.js +10 -0
- package/dist/telegram/lane-delivery-state.js +19 -0
- package/dist/telegram/lane-delivery-text-deliverer.js +357 -0
- package/dist/telegram/lane-delivery.js +2 -0
- package/dist/telegram/normalize.js +37 -0
- package/dist/telegram/onboarding.js +192 -0
- package/dist/telegram/outbound-adapter.js +100 -0
- package/dist/telegram/polling-session.js +275 -0
- package/dist/telegram/runtime.js +3 -0
- package/dist/telegram/sendchataction-401-backoff.js +71 -0
- package/dist/telegram/sequential-key.js +46 -0
- package/dist/telegram/status-issues.js +105 -0
- package/dist/telegram/target-writeback.js +165 -0
- package/dist/telegram/thread-bindings.js +560 -0
- package/dist/utils.js +10 -276
- package/dist/wizard/prompts.js +5 -5
- package/extensions/feishu/src/policy.ts +1 -1
- package/extensions/firecrawl/index.test.ts +82 -0
- package/extensions/firecrawl/index.ts +20 -0
- package/extensions/firecrawl/openclaw.plugin.json +8 -0
- package/extensions/firecrawl/package.json +12 -0
- package/extensions/firecrawl/src/config.ts +159 -0
- package/extensions/firecrawl/src/firecrawl-client.ts +446 -0
- package/extensions/firecrawl/src/firecrawl-scrape-tool.ts +89 -0
- package/extensions/firecrawl/src/firecrawl-search-provider.ts +63 -0
- package/extensions/firecrawl/src/firecrawl-search-tool.ts +76 -0
- package/package.json +1 -1
- package/dist/.buildstamp +0 -1
- package/dist/acp/bindings-store.js +0 -209
- package/dist/acp/control-plane/runtime-cache.js +0 -54
- package/dist/acp/control-plane/runtime-options.js +0 -215
- package/dist/acp/control-plane/session-actor-queue.js +0 -36
- package/dist/acp/index.js +0 -2
- package/dist/acp/runtime/errors.js +0 -47
- package/dist/acp/runtime/registry.js +0 -86
- package/dist/acp/secret-file.js +0 -22
- package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +0 -23
- package/dist/agents/bash-process-registry.test-helpers.js +0 -29
- package/dist/agents/bash-tools.exec-approval-request.js +0 -20
- package/dist/agents/bash-tools.exec-host-gateway.js +0 -240
- package/dist/agents/bash-tools.exec-host-node.js +0 -235
- package/dist/agents/checkpoint-manager.js +0 -290
- package/dist/agents/claude-cli-runner.js +0 -3
- package/dist/agents/error-classifier.js +0 -251
- package/dist/agents/live-model-filter.js +0 -84
- package/dist/agents/nvidia-models.js +0 -228
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +0 -34
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +0 -156
- package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +0 -30
- package/dist/agents/provider/config-loader.js +0 -76
- package/dist/agents/provider/index.js +0 -15
- package/dist/agents/provider/models-dev.js +0 -129
- package/dist/agents/provider/session-binding.js +0 -376
- package/dist/agents/queued-file-writer.js +0 -22
- package/dist/agents/skills/bundled-context.js +0 -23
- package/dist/agents/skills/security.js +0 -211
- package/dist/agents/skills/tools-dir.js +0 -9
- package/dist/agents/skills-install-download.js +0 -290
- package/dist/agents/skills-install-output.js +0 -30
- package/dist/agents/skills-install.download-test-utils.js +0 -36
- package/dist/agents/skills.test-helpers.js +0 -13
- package/dist/agents/subagent-announce-reliability.js +0 -160
- package/dist/agents/subagent-registry.mocks.shared.js +0 -12
- package/dist/agents/test-helpers/assistant-message-fixtures.js +0 -29
- package/dist/agents/test-helpers/fast-coding-tools.js +0 -1
- package/dist/agents/test-helpers/fast-core-tools.js +0 -8
- package/dist/agents/test-helpers/fast-tool-stubs.js +0 -18
- package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +0 -74
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +0 -27
- package/dist/agents/tool-display-common.js +0 -915
- package/dist/agents/tool-policy-shared.js +0 -108
- package/dist/agents/tool-policy.conformance.js +0 -14
- package/dist/agents/tool-result-truncation.js +0 -299
- package/dist/agents/tools/cron-tool.test-helpers.js +0 -12
- package/dist/agents/tools/discord-actions-moderation-shared.js +0 -27
- package/dist/agents/tools/discord-actions-presence.js +0 -78
- package/dist/control-ui/assets/index-D7shnQwQ.js.map +0 -1
- package/dist/discord/discord-improvements.js +0 -167
- package/dist/discord/index.js +0 -2
- package/dist/hooks/bundled/boot-md/HOOK.md +0 -19
- package/dist/hooks/bundled/command-logger/HOOK.md +0 -122
- package/dist/hooks/bundled/session-memory/HOOK.md +0 -86
- package/dist/hooks/bundled/soul-evil/HOOK.md +0 -71
- package/dist/whatsapp/normalize.js +0 -66
- package/dist/whatsapp/resolve-outbound-target.js +0 -42
- /package/dist/{acp/runtime/types.js → auto-reply/auto-reply/reply/commands-types.js} +0 -0
- /package/dist/{agents/pi-embedded-payloads.js → slack/account-surface-fields.js} +0 -0
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { Readable } from "node:stream";
|
|
4
|
-
import { pipeline } from "node:stream/promises";
|
|
5
|
-
import { extractArchive as extractArchiveSafe } from "../infra/archive.js";
|
|
6
|
-
import { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
|
|
7
|
-
import { isWithinDir, resolveSafeBaseDir } from "../infra/path-safety.js";
|
|
8
|
-
import { runCommandWithTimeout } from "../process/exec.js";
|
|
9
|
-
import { ensureDir, resolveUserPath } from "../utils.js";
|
|
10
|
-
import { formatInstallFailureMessage } from "./skills-install-output.js";
|
|
11
|
-
import { hasBinary } from "./skills.js";
|
|
12
|
-
import { resolveSkillToolsRootDir } from "./skills/tools-dir.js";
|
|
13
|
-
function isNodeReadableStream(value) {
|
|
14
|
-
return Boolean(value && typeof value.pipe === "function");
|
|
15
|
-
}
|
|
16
|
-
function isWindowsDrivePath(p) {
|
|
17
|
-
return /^[a-zA-Z]:[\\/]/.test(p);
|
|
18
|
-
}
|
|
19
|
-
function resolveDownloadTargetDir(entry, spec) {
|
|
20
|
-
const safeRoot = resolveSkillToolsRootDir(entry);
|
|
21
|
-
const raw = spec.targetDir?.trim();
|
|
22
|
-
if (!raw) {
|
|
23
|
-
return safeRoot;
|
|
24
|
-
}
|
|
25
|
-
// Treat non-absolute paths as relative to the per-skill tools root.
|
|
26
|
-
const resolved = raw.startsWith("~") || path.isAbsolute(raw) || isWindowsDrivePath(raw)
|
|
27
|
-
? resolveUserPath(raw)
|
|
28
|
-
: path.resolve(safeRoot, raw);
|
|
29
|
-
if (!isWithinDir(safeRoot, resolved)) {
|
|
30
|
-
throw new Error(`Refusing to install outside the skill tools directory. targetDir="${raw}" resolves to "${resolved}". Allowed root: "${safeRoot}".`);
|
|
31
|
-
}
|
|
32
|
-
return resolved;
|
|
33
|
-
}
|
|
34
|
-
function resolveArchiveType(spec, filename) {
|
|
35
|
-
const explicit = spec.archive?.trim().toLowerCase();
|
|
36
|
-
if (explicit) {
|
|
37
|
-
return explicit;
|
|
38
|
-
}
|
|
39
|
-
const lower = filename.toLowerCase();
|
|
40
|
-
if (lower.endsWith(".tar.gz") || lower.endsWith(".tgz")) {
|
|
41
|
-
return "tar.gz";
|
|
42
|
-
}
|
|
43
|
-
if (lower.endsWith(".tar.bz2") || lower.endsWith(".tbz2")) {
|
|
44
|
-
return "tar.bz2";
|
|
45
|
-
}
|
|
46
|
-
if (lower.endsWith(".zip")) {
|
|
47
|
-
return "zip";
|
|
48
|
-
}
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
function normalizeArchiveEntryPath(raw) {
|
|
52
|
-
return raw.replaceAll("\\", "/");
|
|
53
|
-
}
|
|
54
|
-
function validateArchiveEntryPath(entryPath) {
|
|
55
|
-
if (!entryPath || entryPath === "." || entryPath === "./") {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
if (isWindowsDrivePath(entryPath)) {
|
|
59
|
-
throw new Error(`archive entry uses a drive path: ${entryPath}`);
|
|
60
|
-
}
|
|
61
|
-
const normalized = path.posix.normalize(normalizeArchiveEntryPath(entryPath));
|
|
62
|
-
if (normalized === ".." || normalized.startsWith("../")) {
|
|
63
|
-
throw new Error(`archive entry escapes targetDir: ${entryPath}`);
|
|
64
|
-
}
|
|
65
|
-
if (path.posix.isAbsolute(normalized) || normalized.startsWith("//")) {
|
|
66
|
-
throw new Error(`archive entry is absolute: ${entryPath}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function stripArchivePath(entryPath, stripComponents) {
|
|
70
|
-
const raw = normalizeArchiveEntryPath(entryPath);
|
|
71
|
-
if (!raw || raw === "." || raw === "./") {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
// Important: tar's --strip-components semantics operate on raw path segments,
|
|
75
|
-
// before any normalization that would collapse "..". We mimic that so we
|
|
76
|
-
// can detect strip-induced escapes like "a/../b" with stripComponents=1.
|
|
77
|
-
const parts = raw.split("/").filter((part) => part.length > 0 && part !== ".");
|
|
78
|
-
const strip = Math.max(0, Math.floor(stripComponents));
|
|
79
|
-
const stripped = strip === 0 ? parts.join("/") : parts.slice(strip).join("/");
|
|
80
|
-
const result = path.posix.normalize(stripped);
|
|
81
|
-
if (!result || result === "." || result === "./") {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
return result;
|
|
85
|
-
}
|
|
86
|
-
function validateExtractedPathWithinRoot(params) {
|
|
87
|
-
const safeBase = resolveSafeBaseDir(params.rootDir);
|
|
88
|
-
const outPath = path.resolve(params.rootDir, params.relPath);
|
|
89
|
-
if (!outPath.startsWith(safeBase)) {
|
|
90
|
-
throw new Error(`archive entry escapes targetDir: ${params.originalPath}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
async function downloadFile(url, destPath, timeoutMs) {
|
|
94
|
-
const { response, release } = await fetchWithSsrFGuard({
|
|
95
|
-
url,
|
|
96
|
-
timeoutMs: Math.max(1_000, timeoutMs),
|
|
97
|
-
});
|
|
98
|
-
try {
|
|
99
|
-
if (!response.ok || !response.body) {
|
|
100
|
-
throw new Error(`Download failed (${response.status} ${response.statusText})`);
|
|
101
|
-
}
|
|
102
|
-
await ensureDir(path.dirname(destPath));
|
|
103
|
-
const file = fs.createWriteStream(destPath);
|
|
104
|
-
const body = response.body;
|
|
105
|
-
const readable = isNodeReadableStream(body)
|
|
106
|
-
? body
|
|
107
|
-
: Readable.fromWeb(body);
|
|
108
|
-
await pipeline(readable, file);
|
|
109
|
-
const stat = await fs.promises.stat(destPath);
|
|
110
|
-
return { bytes: stat.size };
|
|
111
|
-
}
|
|
112
|
-
finally {
|
|
113
|
-
await release();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
async function extractArchive(params) {
|
|
117
|
-
const { archivePath, archiveType, targetDir, stripComponents, timeoutMs } = params;
|
|
118
|
-
const strip = typeof stripComponents === "number" && Number.isFinite(stripComponents)
|
|
119
|
-
? Math.max(0, Math.floor(stripComponents))
|
|
120
|
-
: 0;
|
|
121
|
-
try {
|
|
122
|
-
if (archiveType === "zip") {
|
|
123
|
-
await extractArchiveSafe({
|
|
124
|
-
archivePath,
|
|
125
|
-
destDir: targetDir,
|
|
126
|
-
timeoutMs,
|
|
127
|
-
kind: "zip",
|
|
128
|
-
stripComponents: strip,
|
|
129
|
-
});
|
|
130
|
-
return { stdout: "", stderr: "", code: 0 };
|
|
131
|
-
}
|
|
132
|
-
if (archiveType === "tar.gz") {
|
|
133
|
-
await extractArchiveSafe({
|
|
134
|
-
archivePath,
|
|
135
|
-
destDir: targetDir,
|
|
136
|
-
timeoutMs,
|
|
137
|
-
kind: "tar",
|
|
138
|
-
stripComponents: strip,
|
|
139
|
-
tarGzip: true,
|
|
140
|
-
});
|
|
141
|
-
return { stdout: "", stderr: "", code: 0 };
|
|
142
|
-
}
|
|
143
|
-
if (archiveType === "tar.bz2") {
|
|
144
|
-
if (!hasBinary("tar")) {
|
|
145
|
-
return { stdout: "", stderr: "tar not found on PATH", code: null };
|
|
146
|
-
}
|
|
147
|
-
// Preflight list to prevent zip-slip style traversal before extraction.
|
|
148
|
-
const listResult = await runCommandWithTimeout(["tar", "tf", archivePath], { timeoutMs });
|
|
149
|
-
if (listResult.code !== 0) {
|
|
150
|
-
return {
|
|
151
|
-
stdout: listResult.stdout,
|
|
152
|
-
stderr: listResult.stderr || "tar list failed",
|
|
153
|
-
code: listResult.code,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
const entries = listResult.stdout
|
|
157
|
-
.split("\n")
|
|
158
|
-
.map((line) => line.trim())
|
|
159
|
-
.filter(Boolean);
|
|
160
|
-
const verboseResult = await runCommandWithTimeout(["tar", "tvf", archivePath], { timeoutMs });
|
|
161
|
-
if (verboseResult.code !== 0) {
|
|
162
|
-
return {
|
|
163
|
-
stdout: verboseResult.stdout,
|
|
164
|
-
stderr: verboseResult.stderr || "tar verbose list failed",
|
|
165
|
-
code: verboseResult.code,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
for (const line of verboseResult.stdout.split("\n")) {
|
|
169
|
-
const trimmed = line.trim();
|
|
170
|
-
if (!trimmed) {
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
const typeChar = trimmed[0];
|
|
174
|
-
if (typeChar === "l" || typeChar === "h" || trimmed.includes(" -> ")) {
|
|
175
|
-
return {
|
|
176
|
-
stdout: verboseResult.stdout,
|
|
177
|
-
stderr: "tar archive contains link entries; refusing to extract for safety",
|
|
178
|
-
code: 1,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
for (const entry of entries) {
|
|
183
|
-
validateArchiveEntryPath(entry);
|
|
184
|
-
const relPath = stripArchivePath(entry, strip);
|
|
185
|
-
if (!relPath) {
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
validateArchiveEntryPath(relPath);
|
|
189
|
-
validateExtractedPathWithinRoot({ rootDir: targetDir, relPath, originalPath: entry });
|
|
190
|
-
}
|
|
191
|
-
const argv = ["tar", "xf", archivePath, "-C", targetDir];
|
|
192
|
-
if (strip > 0) {
|
|
193
|
-
argv.push("--strip-components", String(strip));
|
|
194
|
-
}
|
|
195
|
-
return await runCommandWithTimeout(argv, { timeoutMs });
|
|
196
|
-
}
|
|
197
|
-
return { stdout: "", stderr: `unsupported archive type: ${archiveType}`, code: null };
|
|
198
|
-
}
|
|
199
|
-
catch (err) {
|
|
200
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
201
|
-
return { stdout: "", stderr: message, code: 1 };
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
export async function installDownloadSpec(params) {
|
|
205
|
-
const { entry, spec, timeoutMs } = params;
|
|
206
|
-
const url = spec.url?.trim();
|
|
207
|
-
if (!url) {
|
|
208
|
-
return {
|
|
209
|
-
ok: false,
|
|
210
|
-
message: "missing download url",
|
|
211
|
-
stdout: "",
|
|
212
|
-
stderr: "",
|
|
213
|
-
code: null,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
let filename = "";
|
|
217
|
-
try {
|
|
218
|
-
const parsed = new URL(url);
|
|
219
|
-
filename = path.basename(parsed.pathname);
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
filename = path.basename(url);
|
|
223
|
-
}
|
|
224
|
-
if (!filename) {
|
|
225
|
-
filename = "download";
|
|
226
|
-
}
|
|
227
|
-
let targetDir = "";
|
|
228
|
-
try {
|
|
229
|
-
targetDir = resolveDownloadTargetDir(entry, spec);
|
|
230
|
-
await ensureDir(targetDir);
|
|
231
|
-
const stat = await fs.promises.lstat(targetDir);
|
|
232
|
-
if (stat.isSymbolicLink()) {
|
|
233
|
-
throw new Error(`targetDir is a symlink: ${targetDir}`);
|
|
234
|
-
}
|
|
235
|
-
if (!stat.isDirectory()) {
|
|
236
|
-
throw new Error(`targetDir is not a directory: ${targetDir}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
catch (err) {
|
|
240
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
241
|
-
return { ok: false, message, stdout: "", stderr: message, code: null };
|
|
242
|
-
}
|
|
243
|
-
const archivePath = path.join(targetDir, filename);
|
|
244
|
-
let downloaded = 0;
|
|
245
|
-
try {
|
|
246
|
-
const result = await downloadFile(url, archivePath, timeoutMs);
|
|
247
|
-
downloaded = result.bytes;
|
|
248
|
-
}
|
|
249
|
-
catch (err) {
|
|
250
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
251
|
-
return { ok: false, message, stdout: "", stderr: message, code: null };
|
|
252
|
-
}
|
|
253
|
-
const archiveType = resolveArchiveType(spec, filename);
|
|
254
|
-
const shouldExtract = spec.extract ?? Boolean(archiveType);
|
|
255
|
-
if (!shouldExtract) {
|
|
256
|
-
return {
|
|
257
|
-
ok: true,
|
|
258
|
-
message: `Downloaded to ${archivePath}`,
|
|
259
|
-
stdout: `downloaded=${downloaded}`,
|
|
260
|
-
stderr: "",
|
|
261
|
-
code: 0,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
if (!archiveType) {
|
|
265
|
-
return {
|
|
266
|
-
ok: false,
|
|
267
|
-
message: "extract requested but archive type could not be detected",
|
|
268
|
-
stdout: "",
|
|
269
|
-
stderr: "",
|
|
270
|
-
code: null,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
const extractResult = await extractArchive({
|
|
274
|
-
archivePath,
|
|
275
|
-
archiveType,
|
|
276
|
-
targetDir,
|
|
277
|
-
stripComponents: spec.stripComponents,
|
|
278
|
-
timeoutMs,
|
|
279
|
-
});
|
|
280
|
-
const success = extractResult.code === 0;
|
|
281
|
-
return {
|
|
282
|
-
ok: success,
|
|
283
|
-
message: success
|
|
284
|
-
? `Downloaded and extracted to ${targetDir}`
|
|
285
|
-
: formatInstallFailureMessage(extractResult),
|
|
286
|
-
stdout: extractResult.stdout.trim(),
|
|
287
|
-
stderr: extractResult.stderr.trim(),
|
|
288
|
-
code: extractResult.code,
|
|
289
|
-
};
|
|
290
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
function summarizeInstallOutput(text) {
|
|
2
|
-
const raw = text.trim();
|
|
3
|
-
if (!raw) {
|
|
4
|
-
return undefined;
|
|
5
|
-
}
|
|
6
|
-
const lines = raw
|
|
7
|
-
.split("\n")
|
|
8
|
-
.map((line) => line.trim())
|
|
9
|
-
.filter(Boolean);
|
|
10
|
-
if (lines.length === 0) {
|
|
11
|
-
return undefined;
|
|
12
|
-
}
|
|
13
|
-
const preferred = lines.find((line) => /^error\b/i.test(line)) ??
|
|
14
|
-
lines.find((line) => /\b(err!|error:|failed)\b/i.test(line)) ??
|
|
15
|
-
lines.at(-1);
|
|
16
|
-
if (!preferred) {
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
19
|
-
const normalized = preferred.replace(/\s+/g, " ").trim();
|
|
20
|
-
const maxLen = 200;
|
|
21
|
-
return normalized.length > maxLen ? `${normalized.slice(0, maxLen - 1)}…` : normalized;
|
|
22
|
-
}
|
|
23
|
-
export function formatInstallFailureMessage(result) {
|
|
24
|
-
const code = typeof result.code === "number" ? `exit ${result.code}` : "unknown exit";
|
|
25
|
-
const summary = summarizeInstallOutput(result.stderr) ?? summarizeInstallOutput(result.stdout);
|
|
26
|
-
if (!summary) {
|
|
27
|
-
return `Install failed (${code})`;
|
|
28
|
-
}
|
|
29
|
-
return `Install failed (${code}): ${summary}`;
|
|
30
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
export function setTempStateDir(workspaceDir) {
|
|
4
|
-
const stateDir = path.join(workspaceDir, "state");
|
|
5
|
-
process.env.POOLBOT_STATE_DIR = stateDir;
|
|
6
|
-
return stateDir;
|
|
7
|
-
}
|
|
8
|
-
export async function writeDownloadSkill(params) {
|
|
9
|
-
const skillDir = path.join(params.workspaceDir, "skills", params.name);
|
|
10
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
11
|
-
const meta = {
|
|
12
|
-
openclaw: {
|
|
13
|
-
install: [
|
|
14
|
-
{
|
|
15
|
-
id: params.installId,
|
|
16
|
-
kind: "download",
|
|
17
|
-
url: params.url,
|
|
18
|
-
archive: params.archive,
|
|
19
|
-
extract: true,
|
|
20
|
-
stripComponents: params.stripComponents,
|
|
21
|
-
targetDir: params.targetDir,
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
await fs.writeFile(path.join(skillDir, "SKILL.md"), `---
|
|
27
|
-
name: ${params.name}
|
|
28
|
-
description: test skill
|
|
29
|
-
metadata: ${JSON.stringify(meta)}
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
# ${params.name}
|
|
33
|
-
`, "utf-8");
|
|
34
|
-
await fs.writeFile(path.join(skillDir, "runner.js"), "export {};\n", "utf-8");
|
|
35
|
-
return skillDir;
|
|
36
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
export async function writeSkill(params) {
|
|
4
|
-
const { dir, name, description, body } = params;
|
|
5
|
-
await fs.mkdir(dir, { recursive: true });
|
|
6
|
-
await fs.writeFile(path.join(dir, "SKILL.md"), `---
|
|
7
|
-
name: ${name}
|
|
8
|
-
description: ${description}
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
${body ?? `# ${name}\n`}
|
|
12
|
-
`, "utf-8");
|
|
13
|
-
}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subagent Announce Chain Delivery Reliability
|
|
3
|
-
*
|
|
4
|
-
* Implements:
|
|
5
|
-
* - Retry budget with exponential backoff
|
|
6
|
-
* - Dedupe of announce completions
|
|
7
|
-
* - Delivery params validation
|
|
8
|
-
* - Session model override persistence
|
|
9
|
-
*
|
|
10
|
-
* OpenClaw #32384, #30951
|
|
11
|
-
*/
|
|
12
|
-
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
13
|
-
const log = createSubsystemLogger("subagent-announce");
|
|
14
|
-
/**
|
|
15
|
-
* Default retry budget: 3 attempts with exponential backoff
|
|
16
|
-
*/
|
|
17
|
-
export const DEFAULT_ANNOUNCE_RETRY_BUDGET = {
|
|
18
|
-
maxRetries: 3,
|
|
19
|
-
baseDelayMs: 1000,
|
|
20
|
-
maxDelayMs: 10_000,
|
|
21
|
-
backoffMultiplier: 2,
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* In-memory store for announce delivery states
|
|
25
|
-
*/
|
|
26
|
-
const announceStates = new Map();
|
|
27
|
-
/**
|
|
28
|
-
* Cleanup old announce states every 30 minutes
|
|
29
|
-
*/
|
|
30
|
-
setInterval(() => {
|
|
31
|
-
const now = Date.now();
|
|
32
|
-
const maxAge = 30 * 60 * 1000; // 30 minutes
|
|
33
|
-
for (const [id, state] of announceStates.entries()) {
|
|
34
|
-
if (now - state.lastAttemptAt > maxAge) {
|
|
35
|
-
announceStates.delete(id);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}, 30 * 60 * 1000).unref();
|
|
39
|
-
/**
|
|
40
|
-
* Initialize or get announce delivery state
|
|
41
|
-
*/
|
|
42
|
-
export function getOrCreateAnnounceState(announceId) {
|
|
43
|
-
const existing = announceStates.get(announceId);
|
|
44
|
-
if (existing) {
|
|
45
|
-
return existing;
|
|
46
|
-
}
|
|
47
|
-
const newState = {
|
|
48
|
-
announceId,
|
|
49
|
-
attempts: 0,
|
|
50
|
-
lastAttemptAt: 0,
|
|
51
|
-
delivered: false,
|
|
52
|
-
retriesExhausted: false,
|
|
53
|
-
};
|
|
54
|
-
announceStates.set(announceId, newState);
|
|
55
|
-
return newState;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Record a delivery attempt and check if retry is allowed
|
|
59
|
-
*/
|
|
60
|
-
export function recordDeliveryAttempt(params) {
|
|
61
|
-
const { announceId, success, error, budget = DEFAULT_ANNOUNCE_RETRY_BUDGET } = params;
|
|
62
|
-
const state = getOrCreateAnnounceState(announceId);
|
|
63
|
-
state.attempts += 1;
|
|
64
|
-
state.lastAttemptAt = Date.now();
|
|
65
|
-
if (success) {
|
|
66
|
-
state.delivered = true;
|
|
67
|
-
state.lastError = undefined;
|
|
68
|
-
return { canRetry: false, delayMs: 0, attemptsRemaining: 0 };
|
|
69
|
-
}
|
|
70
|
-
state.lastError = error;
|
|
71
|
-
const attemptsRemaining = Math.max(0, budget.maxRetries - state.attempts);
|
|
72
|
-
state.retriesExhausted = attemptsRemaining === 0;
|
|
73
|
-
if (!state.retriesExhausted) {
|
|
74
|
-
// Calculate exponential backoff delay
|
|
75
|
-
const exponentialDelay = budget.baseDelayMs * Math.pow(budget.backoffMultiplier, state.attempts - 1);
|
|
76
|
-
const delayMs = Math.min(exponentialDelay, budget.maxDelayMs);
|
|
77
|
-
log.warn(`Announce delivery attempt ${state.attempts}/${budget.maxRetries} failed, retrying in ${delayMs}ms`, { announceId, error });
|
|
78
|
-
return { canRetry: true, delayMs, attemptsRemaining };
|
|
79
|
-
}
|
|
80
|
-
log.error(`Announce delivery failed after ${state.attempts} attempts (retries exhausted)`, {
|
|
81
|
-
announceId,
|
|
82
|
-
error,
|
|
83
|
-
});
|
|
84
|
-
return { canRetry: false, delayMs: 0, attemptsRemaining: 0 };
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Check if an announce completion is duplicate (dedupe)
|
|
88
|
-
* OpenClaw #32384 - prevents duplicate announce deliveries
|
|
89
|
-
*/
|
|
90
|
-
const completedAnnounces = new Map();
|
|
91
|
-
export function isAnnounceDuplicate(announceId) {
|
|
92
|
-
return completedAnnounces.has(announceId);
|
|
93
|
-
}
|
|
94
|
-
export function markAnnounceComplete(announceId) {
|
|
95
|
-
completedAnnounces.set(announceId, Date.now());
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Cleanup completed announces every hour
|
|
99
|
-
*/
|
|
100
|
-
setInterval(() => {
|
|
101
|
-
const now = Date.now();
|
|
102
|
-
const maxAge = 60 * 60 * 1000; // 1 hour
|
|
103
|
-
for (const [id, timestamp] of completedAnnounces.entries()) {
|
|
104
|
-
if (now - timestamp > maxAge) {
|
|
105
|
-
completedAnnounces.delete(id);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}, 60 * 60 * 1000).unref();
|
|
109
|
-
/**
|
|
110
|
-
* Validate announce delivery parameters
|
|
111
|
-
*/
|
|
112
|
-
export function validateAnnounceDeliveryParams(params) {
|
|
113
|
-
const { sessionKey, prompt, origin } = params;
|
|
114
|
-
if (!sessionKey || !sessionKey.trim()) {
|
|
115
|
-
return { valid: false, reason: "Missing or empty sessionKey" };
|
|
116
|
-
}
|
|
117
|
-
if (!prompt || !prompt.trim()) {
|
|
118
|
-
return { valid: false, reason: "Missing or empty prompt" };
|
|
119
|
-
}
|
|
120
|
-
// Origin is optional but if provided should be an object
|
|
121
|
-
if (origin !== undefined && origin !== null && typeof origin !== "object") {
|
|
122
|
-
return { valid: false, reason: "Origin must be an object if provided" };
|
|
123
|
-
}
|
|
124
|
-
return { valid: true };
|
|
125
|
-
}
|
|
126
|
-
const modelOverrides = new Map();
|
|
127
|
-
/**
|
|
128
|
-
* Set a session model override
|
|
129
|
-
*/
|
|
130
|
-
export function setSessionModelOverride(params) {
|
|
131
|
-
const { sessionKey, model, ttlMs } = params;
|
|
132
|
-
const override = {
|
|
133
|
-
sessionKey,
|
|
134
|
-
model,
|
|
135
|
-
setAt: Date.now(),
|
|
136
|
-
expiresAt: ttlMs ? Date.now() + ttlMs : undefined,
|
|
137
|
-
};
|
|
138
|
-
modelOverrides.set(sessionKey, override);
|
|
139
|
-
return override;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Get session model override (if not expired)
|
|
143
|
-
*/
|
|
144
|
-
export function getSessionModelOverride(sessionKey) {
|
|
145
|
-
const override = modelOverrides.get(sessionKey);
|
|
146
|
-
if (!override) {
|
|
147
|
-
return undefined;
|
|
148
|
-
}
|
|
149
|
-
if (override.expiresAt && Date.now() > override.expiresAt) {
|
|
150
|
-
modelOverrides.delete(sessionKey);
|
|
151
|
-
return undefined;
|
|
152
|
-
}
|
|
153
|
-
return override;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Clear session model override
|
|
157
|
-
*/
|
|
158
|
-
export function clearSessionModelOverride(sessionKey) {
|
|
159
|
-
return modelOverrides.delete(sessionKey);
|
|
160
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { vi } from "vitest";
|
|
2
|
-
const noop = () => { };
|
|
3
|
-
vi.mock("../gateway/call.js", () => ({
|
|
4
|
-
callGateway: vi.fn(async () => ({
|
|
5
|
-
status: "ok",
|
|
6
|
-
startedAt: 111,
|
|
7
|
-
endedAt: 222,
|
|
8
|
-
})),
|
|
9
|
-
}));
|
|
10
|
-
vi.mock("../infra/agent-events.js", () => ({
|
|
11
|
-
onAgentEvent: vi.fn(() => noop),
|
|
12
|
-
}));
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const ZERO_USAGE = {
|
|
2
|
-
input: 0,
|
|
3
|
-
output: 0,
|
|
4
|
-
cacheRead: 0,
|
|
5
|
-
cacheWrite: 0,
|
|
6
|
-
totalTokens: 0,
|
|
7
|
-
cost: {
|
|
8
|
-
input: 0,
|
|
9
|
-
output: 0,
|
|
10
|
-
cacheRead: 0,
|
|
11
|
-
cacheWrite: 0,
|
|
12
|
-
total: 0,
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
export function makeAssistantMessageFixture(overrides = {}) {
|
|
16
|
-
const errorText = typeof overrides.errorMessage === "string" ? overrides.errorMessage : "error";
|
|
17
|
-
return {
|
|
18
|
-
role: "assistant",
|
|
19
|
-
api: "openai-responses",
|
|
20
|
-
provider: "openai",
|
|
21
|
-
model: "test-model",
|
|
22
|
-
usage: ZERO_USAGE,
|
|
23
|
-
timestamp: 0,
|
|
24
|
-
stopReason: "error",
|
|
25
|
-
errorMessage: errorText,
|
|
26
|
-
content: [{ type: "text", text: errorText }],
|
|
27
|
-
...overrides,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import "./fast-tool-stubs.js";
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { vi } from "vitest";
|
|
2
|
-
import { stubTool } from "./fast-tool-stubs.js";
|
|
3
|
-
vi.mock("../tools/browser-tool.js", () => ({
|
|
4
|
-
createBrowserTool: () => stubTool("browser"),
|
|
5
|
-
}));
|
|
6
|
-
vi.mock("../tools/canvas-tool.js", () => ({
|
|
7
|
-
createCanvasTool: () => stubTool("canvas"),
|
|
8
|
-
}));
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { vi } from "vitest";
|
|
2
|
-
export const stubTool = (name) => ({
|
|
3
|
-
name,
|
|
4
|
-
description: `${name} stub`,
|
|
5
|
-
parameters: { type: "object", properties: {} },
|
|
6
|
-
execute: vi.fn(),
|
|
7
|
-
});
|
|
8
|
-
vi.mock("../tools/image-tool.js", () => ({
|
|
9
|
-
createImageTool: () => stubTool("image"),
|
|
10
|
-
}));
|
|
11
|
-
vi.mock("../tools/web-tools.js", () => ({
|
|
12
|
-
createWebSearchTool: () => null,
|
|
13
|
-
createWebFetchTool: () => null,
|
|
14
|
-
}));
|
|
15
|
-
vi.mock("../../plugins/tools.js", () => ({
|
|
16
|
-
resolvePluginTools: () => [],
|
|
17
|
-
getPluginToolMeta: () => undefined,
|
|
18
|
-
}));
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { resolveSandboxPath } from "../sandbox-paths.js";
|
|
4
|
-
export function createSandboxFsBridgeFromResolver(resolvePath) {
|
|
5
|
-
return {
|
|
6
|
-
resolvePath: ({ filePath, cwd }) => resolvePath(filePath, cwd),
|
|
7
|
-
readFile: async ({ filePath, cwd }) => {
|
|
8
|
-
const target = resolvePath(filePath, cwd);
|
|
9
|
-
return fs.readFile(target.hostPath);
|
|
10
|
-
},
|
|
11
|
-
writeFile: async ({ filePath, cwd, data, mkdir = true }) => {
|
|
12
|
-
const target = resolvePath(filePath, cwd);
|
|
13
|
-
if (mkdir) {
|
|
14
|
-
await fs.mkdir(path.dirname(target.hostPath), { recursive: true });
|
|
15
|
-
}
|
|
16
|
-
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
17
|
-
await fs.writeFile(target.hostPath, buffer);
|
|
18
|
-
},
|
|
19
|
-
mkdirp: async ({ filePath, cwd }) => {
|
|
20
|
-
const target = resolvePath(filePath, cwd);
|
|
21
|
-
await fs.mkdir(target.hostPath, { recursive: true });
|
|
22
|
-
},
|
|
23
|
-
remove: async ({ filePath, cwd, recursive, force }) => {
|
|
24
|
-
const target = resolvePath(filePath, cwd);
|
|
25
|
-
await fs.rm(target.hostPath, {
|
|
26
|
-
recursive: recursive ?? false,
|
|
27
|
-
force: force ?? false,
|
|
28
|
-
});
|
|
29
|
-
},
|
|
30
|
-
rename: async ({ from, to, cwd }) => {
|
|
31
|
-
const source = resolvePath(from, cwd);
|
|
32
|
-
const target = resolvePath(to, cwd);
|
|
33
|
-
await fs.mkdir(path.dirname(target.hostPath), { recursive: true });
|
|
34
|
-
await fs.rename(source.hostPath, target.hostPath);
|
|
35
|
-
},
|
|
36
|
-
stat: async ({ filePath, cwd }) => {
|
|
37
|
-
try {
|
|
38
|
-
const target = resolvePath(filePath, cwd);
|
|
39
|
-
const stats = await fs.stat(target.hostPath);
|
|
40
|
-
return {
|
|
41
|
-
type: stats.isDirectory() ? "directory" : stats.isFile() ? "file" : "other",
|
|
42
|
-
size: stats.size,
|
|
43
|
-
mtimeMs: stats.mtimeMs,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
if (error.code === "ENOENT") {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
throw error;
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
export function createHostSandboxFsBridge(rootDir) {
|
|
56
|
-
const root = path.resolve(rootDir);
|
|
57
|
-
const resolvePath = (filePath, cwd) => {
|
|
58
|
-
const resolved = resolveSandboxPath({
|
|
59
|
-
filePath,
|
|
60
|
-
cwd: cwd ?? root,
|
|
61
|
-
root,
|
|
62
|
-
});
|
|
63
|
-
const relativePath = resolved.relative
|
|
64
|
-
? resolved.relative.split(path.sep).filter(Boolean).join(path.posix.sep)
|
|
65
|
-
: "";
|
|
66
|
-
const containerPath = relativePath ? path.posix.join("/workspace", relativePath) : "/workspace";
|
|
67
|
-
return {
|
|
68
|
-
hostPath: resolved.resolved,
|
|
69
|
-
relativePath,
|
|
70
|
-
containerPath,
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
return createSandboxFsBridgeFromResolver(resolvePath);
|
|
74
|
-
}
|