@poolzin/pool-bot 2026.2.23 → 2026.2.25
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/CHANGELOG.md +29 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-scope.js +10 -0
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/models-config.providers.js +299 -182
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.js +15 -18
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/browser/config.js +26 -0
- package/dist/browser/navigation-guard.js +31 -0
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +25 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +2 -1
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +21 -0
- package/dist/cli/plugins-cli.js +245 -61
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +223 -136
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/route.js +1 -1
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/commands/agent.js +163 -87
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/onboard-helpers.js +4 -4
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/compat/legacy-names.js +2 -2
- package/dist/config/commands.js +3 -0
- package/dist/config/config.js +1 -1
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +9 -0
- package/dist/config/io.js +571 -171
- package/dist/config/merge-patch.js +50 -4
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.js +58 -570
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +8 -7
- package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
- package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
- package/dist/control-ui/index.html +1 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/gateway/auth.js +38 -3
- package/dist/gateway/call.js +149 -68
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +23 -0
- package/dist/gateway/openresponses-http.js +120 -110
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +3 -2
- package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-http.js +236 -52
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +461 -130
- package/dist/gateway/server-methods/config.js +193 -150
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +5 -0
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/hooks/bundled/session-memory/handler.js +165 -34
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/device-pairing.js +148 -167
- package/dist/infra/exec-approvals-allowlist.js +19 -70
- package/dist/infra/exec-approvals-analysis.js +44 -17
- package/dist/infra/exec-safe-bin-policy.js +269 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/update-startup.js +86 -9
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/memory/test-manager.js +8 -0
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/security/audit-channel.js +8 -19
- package/dist/security/audit-extra.async.js +354 -182
- package/dist/security/audit-extra.js +11 -1
- package/dist/security/audit-extra.sync.js +340 -33
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +145 -371
- package/dist/security/dm-policy-shared.js +24 -0
- package/dist/security/external-content.js +20 -8
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/operator-scope-compat.js +37 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/telegram/bot-message-dispatch.js +366 -164
- package/dist/telegram/draft-stream.js +30 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tui/components/chat-log.js +9 -0
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui.js +181 -45
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/media.js +4 -0
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
|
@@ -23,8 +23,86 @@ export async function resolveArchiveSourcePath(archivePath) {
|
|
|
23
23
|
}
|
|
24
24
|
return { ok: true, path: resolved };
|
|
25
25
|
}
|
|
26
|
+
function toOptionalString(value) {
|
|
27
|
+
if (typeof value !== "string") {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const trimmed = value.trim();
|
|
31
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
32
|
+
}
|
|
33
|
+
function parseResolvedSpecFromId(id) {
|
|
34
|
+
const at = id.lastIndexOf("@");
|
|
35
|
+
if (at <= 0 || at >= id.length - 1) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const name = id.slice(0, at).trim();
|
|
39
|
+
const version = id.slice(at + 1).trim();
|
|
40
|
+
if (!name || !version) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return `${name}@${version}`;
|
|
44
|
+
}
|
|
45
|
+
function normalizeNpmPackEntry(entry) {
|
|
46
|
+
if (!entry || typeof entry !== "object") {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const rec = entry;
|
|
50
|
+
const name = toOptionalString(rec.name);
|
|
51
|
+
const version = toOptionalString(rec.version);
|
|
52
|
+
const id = toOptionalString(rec.id);
|
|
53
|
+
const resolvedSpec = (name && version ? `${name}@${version}` : undefined) ??
|
|
54
|
+
(id ? parseResolvedSpecFromId(id) : undefined);
|
|
55
|
+
return {
|
|
56
|
+
filename: toOptionalString(rec.filename),
|
|
57
|
+
metadata: {
|
|
58
|
+
name,
|
|
59
|
+
version,
|
|
60
|
+
resolvedSpec,
|
|
61
|
+
integrity: toOptionalString(rec.integrity),
|
|
62
|
+
shasum: toOptionalString(rec.shasum),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function parseNpmPackJsonOutput(raw) {
|
|
67
|
+
const trimmed = raw.trim();
|
|
68
|
+
if (!trimmed) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const candidates = [trimmed];
|
|
72
|
+
const arrayStart = trimmed.indexOf("[");
|
|
73
|
+
if (arrayStart > 0) {
|
|
74
|
+
candidates.push(trimmed.slice(arrayStart));
|
|
75
|
+
}
|
|
76
|
+
for (const candidate of candidates) {
|
|
77
|
+
let parsed;
|
|
78
|
+
try {
|
|
79
|
+
parsed = JSON.parse(candidate);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const entries = Array.isArray(parsed) ? parsed : [parsed];
|
|
85
|
+
let fallback = null;
|
|
86
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
87
|
+
const normalized = normalizeNpmPackEntry(entries[i]);
|
|
88
|
+
if (!normalized) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!fallback) {
|
|
92
|
+
fallback = normalized;
|
|
93
|
+
}
|
|
94
|
+
if (normalized.filename) {
|
|
95
|
+
return normalized;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (fallback) {
|
|
99
|
+
return fallback;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
26
104
|
export async function packNpmSpecToArchive(params) {
|
|
27
|
-
const res = await runCommandWithTimeout(["npm", "pack", params.spec, "--ignore-scripts"], {
|
|
105
|
+
const res = await runCommandWithTimeout(["npm", "pack", params.spec, "--ignore-scripts", "--json"], {
|
|
28
106
|
timeoutMs: Math.max(params.timeoutMs, 300_000),
|
|
29
107
|
cwd: params.cwd,
|
|
30
108
|
env: {
|
|
@@ -35,13 +113,19 @@ export async function packNpmSpecToArchive(params) {
|
|
|
35
113
|
if (res.code !== 0) {
|
|
36
114
|
return { ok: false, error: `npm pack failed: ${res.stderr.trim() || res.stdout.trim()}` };
|
|
37
115
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
116
|
+
const parsedJson = parseNpmPackJsonOutput(res.stdout || "");
|
|
117
|
+
const packed = parsedJson?.filename ??
|
|
118
|
+
(res.stdout || "")
|
|
119
|
+
.split("\n")
|
|
120
|
+
.map((line) => line.trim())
|
|
121
|
+
.filter(Boolean)
|
|
122
|
+
.pop();
|
|
43
123
|
if (!packed) {
|
|
44
124
|
return { ok: false, error: "npm pack produced no archive" };
|
|
45
125
|
}
|
|
46
|
-
return {
|
|
126
|
+
return {
|
|
127
|
+
ok: true,
|
|
128
|
+
archivePath: path.join(params.cwd, packed),
|
|
129
|
+
metadata: parsedJson?.metadata ?? {},
|
|
130
|
+
};
|
|
47
131
|
}
|
|
@@ -1,97 +1,38 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { resolveStateDir } from "../config/paths.js";
|
|
2
|
+
import { createAsyncLock, pruneExpiredPending, readJsonFile, resolvePairingPaths, upsertPendingPairingRequest, writeJsonAtomic, } from "./pairing-files.js";
|
|
3
|
+
import { generatePairingToken, verifyPairingToken } from "./pairing-token.js";
|
|
5
4
|
const PENDING_TTL_MS = 5 * 60 * 1000;
|
|
6
|
-
|
|
7
|
-
const root = baseDir ?? resolveStateDir();
|
|
8
|
-
const dir = path.join(root, "nodes");
|
|
9
|
-
return {
|
|
10
|
-
dir,
|
|
11
|
-
pendingPath: path.join(dir, "pending.json"),
|
|
12
|
-
pairedPath: path.join(dir, "paired.json"),
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
async function readJSON(filePath) {
|
|
16
|
-
try {
|
|
17
|
-
const raw = await fs.readFile(filePath, "utf8");
|
|
18
|
-
return JSON.parse(raw);
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
async function writeJSONAtomic(filePath, value) {
|
|
25
|
-
const dir = path.dirname(filePath);
|
|
26
|
-
await fs.mkdir(dir, { recursive: true });
|
|
27
|
-
const tmp = `${filePath}.${randomUUID()}.tmp`;
|
|
28
|
-
await fs.writeFile(tmp, JSON.stringify(value, null, 2), "utf8");
|
|
29
|
-
try {
|
|
30
|
-
await fs.chmod(tmp, 0o600);
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
// best-effort; ignore on platforms without chmod
|
|
34
|
-
}
|
|
35
|
-
await fs.rename(tmp, filePath);
|
|
36
|
-
try {
|
|
37
|
-
await fs.chmod(filePath, 0o600);
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
// best-effort; ignore on platforms without chmod
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function pruneExpiredPending(pendingById, nowMs) {
|
|
44
|
-
for (const [id, req] of Object.entries(pendingById)) {
|
|
45
|
-
if (nowMs - req.ts > PENDING_TTL_MS) {
|
|
46
|
-
delete pendingById[id];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
let lock = Promise.resolve();
|
|
51
|
-
async function withLock(fn) {
|
|
52
|
-
const prev = lock;
|
|
53
|
-
let release;
|
|
54
|
-
lock = new Promise((resolve) => {
|
|
55
|
-
release = resolve;
|
|
56
|
-
});
|
|
57
|
-
await prev;
|
|
58
|
-
try {
|
|
59
|
-
return await fn();
|
|
60
|
-
}
|
|
61
|
-
finally {
|
|
62
|
-
release?.();
|
|
63
|
-
}
|
|
64
|
-
}
|
|
5
|
+
const withLock = createAsyncLock();
|
|
65
6
|
async function loadState(baseDir) {
|
|
66
|
-
const { pendingPath, pairedPath } =
|
|
7
|
+
const { pendingPath, pairedPath } = resolvePairingPaths(baseDir, "nodes");
|
|
67
8
|
const [pending, paired] = await Promise.all([
|
|
68
|
-
|
|
69
|
-
|
|
9
|
+
readJsonFile(pendingPath),
|
|
10
|
+
readJsonFile(pairedPath),
|
|
70
11
|
]);
|
|
71
12
|
const state = {
|
|
72
13
|
pendingById: pending ?? {},
|
|
73
14
|
pairedByNodeId: paired ?? {},
|
|
74
15
|
};
|
|
75
|
-
pruneExpiredPending(state.pendingById, Date.now());
|
|
16
|
+
pruneExpiredPending(state.pendingById, Date.now(), PENDING_TTL_MS);
|
|
76
17
|
return state;
|
|
77
18
|
}
|
|
78
19
|
async function persistState(state, baseDir) {
|
|
79
|
-
const { pendingPath, pairedPath } =
|
|
20
|
+
const { pendingPath, pairedPath } = resolvePairingPaths(baseDir, "nodes");
|
|
80
21
|
await Promise.all([
|
|
81
|
-
|
|
82
|
-
|
|
22
|
+
writeJsonAtomic(pendingPath, state.pendingById),
|
|
23
|
+
writeJsonAtomic(pairedPath, state.pairedByNodeId),
|
|
83
24
|
]);
|
|
84
25
|
}
|
|
85
26
|
function normalizeNodeId(nodeId) {
|
|
86
27
|
return nodeId.trim();
|
|
87
28
|
}
|
|
88
29
|
function newToken() {
|
|
89
|
-
return
|
|
30
|
+
return generatePairingToken();
|
|
90
31
|
}
|
|
91
32
|
export async function listNodePairing(baseDir) {
|
|
92
33
|
const state = await loadState(baseDir);
|
|
93
|
-
const pending = Object.values(state.pendingById).
|
|
94
|
-
const paired = Object.values(state.pairedByNodeId).
|
|
34
|
+
const pending = Object.values(state.pendingById).toSorted((a, b) => b.ts - a.ts);
|
|
35
|
+
const paired = Object.values(state.pairedByNodeId).toSorted((a, b) => b.approvedAtMs - a.approvedAtMs);
|
|
95
36
|
return { pending, paired };
|
|
96
37
|
}
|
|
97
38
|
export async function getPairedNode(nodeId, baseDir) {
|
|
@@ -105,40 +46,39 @@ export async function requestNodePairing(req, baseDir) {
|
|
|
105
46
|
if (!nodeId) {
|
|
106
47
|
throw new Error("nodeId required");
|
|
107
48
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
await persistState(state, baseDir);
|
|
133
|
-
return { status: "pending", request, created: true };
|
|
49
|
+
return await upsertPendingPairingRequest({
|
|
50
|
+
pendingById: state.pendingById,
|
|
51
|
+
isExisting: (pending) => pending.nodeId === nodeId,
|
|
52
|
+
isRepair: Boolean(state.pairedByNodeId[nodeId]),
|
|
53
|
+
createRequest: (isRepair) => ({
|
|
54
|
+
requestId: randomUUID(),
|
|
55
|
+
nodeId,
|
|
56
|
+
displayName: req.displayName,
|
|
57
|
+
platform: req.platform,
|
|
58
|
+
version: req.version,
|
|
59
|
+
coreVersion: req.coreVersion,
|
|
60
|
+
uiVersion: req.uiVersion,
|
|
61
|
+
deviceFamily: req.deviceFamily,
|
|
62
|
+
modelIdentifier: req.modelIdentifier,
|
|
63
|
+
caps: req.caps,
|
|
64
|
+
commands: req.commands,
|
|
65
|
+
permissions: req.permissions,
|
|
66
|
+
remoteIp: req.remoteIp,
|
|
67
|
+
silent: req.silent,
|
|
68
|
+
isRepair,
|
|
69
|
+
ts: Date.now(),
|
|
70
|
+
}),
|
|
71
|
+
persist: async () => await persistState(state, baseDir),
|
|
72
|
+
});
|
|
134
73
|
});
|
|
135
74
|
}
|
|
136
75
|
export async function approveNodePairing(requestId, baseDir) {
|
|
137
76
|
return await withLock(async () => {
|
|
138
77
|
const state = await loadState(baseDir);
|
|
139
78
|
const pending = state.pendingById[requestId];
|
|
140
|
-
if (!pending)
|
|
79
|
+
if (!pending) {
|
|
141
80
|
return null;
|
|
81
|
+
}
|
|
142
82
|
const now = Date.now();
|
|
143
83
|
const existing = state.pairedByNodeId[pending.nodeId];
|
|
144
84
|
const node = {
|
|
@@ -168,8 +108,9 @@ export async function rejectNodePairing(requestId, baseDir) {
|
|
|
168
108
|
return await withLock(async () => {
|
|
169
109
|
const state = await loadState(baseDir);
|
|
170
110
|
const pending = state.pendingById[requestId];
|
|
171
|
-
if (!pending)
|
|
111
|
+
if (!pending) {
|
|
172
112
|
return null;
|
|
113
|
+
}
|
|
173
114
|
delete state.pendingById[requestId];
|
|
174
115
|
await persistState(state, baseDir);
|
|
175
116
|
return { requestId, nodeId: pending.nodeId };
|
|
@@ -179,17 +120,19 @@ export async function verifyNodeToken(nodeId, token, baseDir) {
|
|
|
179
120
|
const state = await loadState(baseDir);
|
|
180
121
|
const normalized = normalizeNodeId(nodeId);
|
|
181
122
|
const node = state.pairedByNodeId[normalized];
|
|
182
|
-
if (!node)
|
|
123
|
+
if (!node) {
|
|
183
124
|
return { ok: false };
|
|
184
|
-
|
|
125
|
+
}
|
|
126
|
+
return verifyPairingToken(token, node.token) ? { ok: true, node } : { ok: false };
|
|
185
127
|
}
|
|
186
128
|
export async function updatePairedNodeMetadata(nodeId, patch, baseDir) {
|
|
187
129
|
await withLock(async () => {
|
|
188
130
|
const state = await loadState(baseDir);
|
|
189
131
|
const normalized = normalizeNodeId(nodeId);
|
|
190
132
|
const existing = state.pairedByNodeId[normalized];
|
|
191
|
-
if (!existing)
|
|
133
|
+
if (!existing) {
|
|
192
134
|
return;
|
|
135
|
+
}
|
|
193
136
|
const next = {
|
|
194
137
|
...existing,
|
|
195
138
|
displayName: patch.displayName ?? existing.displayName,
|
|
@@ -215,11 +158,13 @@ export async function renamePairedNode(nodeId, displayName, baseDir) {
|
|
|
215
158
|
const state = await loadState(baseDir);
|
|
216
159
|
const normalized = normalizeNodeId(nodeId);
|
|
217
160
|
const existing = state.pairedByNodeId[normalized];
|
|
218
|
-
if (!existing)
|
|
161
|
+
if (!existing) {
|
|
219
162
|
return null;
|
|
163
|
+
}
|
|
220
164
|
const trimmed = displayName.trim();
|
|
221
|
-
if (!trimmed)
|
|
165
|
+
if (!trimmed) {
|
|
222
166
|
throw new Error("displayName required");
|
|
167
|
+
}
|
|
223
168
|
const next = { ...existing, displayName: trimmed };
|
|
224
169
|
state.pairedByNodeId[normalized] = next;
|
|
225
170
|
await persistState(state, baseDir);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export async function resolveNpmIntegrityDrift(params) {
|
|
2
|
+
if (!params.expectedIntegrity || !params.resolution.integrity) {
|
|
3
|
+
return { proceed: true };
|
|
4
|
+
}
|
|
5
|
+
if (params.expectedIntegrity === params.resolution.integrity) {
|
|
6
|
+
return { proceed: true };
|
|
7
|
+
}
|
|
8
|
+
const integrityDrift = {
|
|
9
|
+
expectedIntegrity: params.expectedIntegrity,
|
|
10
|
+
actualIntegrity: params.resolution.integrity,
|
|
11
|
+
};
|
|
12
|
+
const payload = params.createPayload({
|
|
13
|
+
spec: params.spec,
|
|
14
|
+
expectedIntegrity: integrityDrift.expectedIntegrity,
|
|
15
|
+
actualIntegrity: integrityDrift.actualIntegrity,
|
|
16
|
+
resolution: params.resolution,
|
|
17
|
+
});
|
|
18
|
+
let proceed = true;
|
|
19
|
+
if (params.onIntegrityDrift) {
|
|
20
|
+
proceed = await params.onIntegrityDrift(payload);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
params.warn?.(payload);
|
|
24
|
+
}
|
|
25
|
+
return { integrityDrift, proceed, payload };
|
|
26
|
+
}
|
|
27
|
+
export async function resolveNpmIntegrityDriftWithDefaultMessage(params) {
|
|
28
|
+
const driftResult = await resolveNpmIntegrityDrift({
|
|
29
|
+
spec: params.spec,
|
|
30
|
+
expectedIntegrity: params.expectedIntegrity,
|
|
31
|
+
resolution: params.resolution,
|
|
32
|
+
createPayload: (drift) => ({ ...drift }),
|
|
33
|
+
onIntegrityDrift: params.onIntegrityDrift,
|
|
34
|
+
warn: (driftPayload) => {
|
|
35
|
+
params.warn?.(`Integrity drift detected for ${driftPayload.resolution.resolvedSpec ?? driftPayload.spec}: expected ${driftPayload.expectedIntegrity}, got ${driftPayload.actualIntegrity}`);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
if (!driftResult.proceed && driftResult.payload) {
|
|
39
|
+
return {
|
|
40
|
+
integrityDrift: driftResult.integrityDrift,
|
|
41
|
+
error: `aborted: npm package integrity drift detected for ${driftResult.payload.resolution.resolvedSpec ?? driftResult.payload.spec}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { integrityDrift: driftResult.integrityDrift };
|
|
45
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { packNpmSpecToArchive, withTempDir, } from "./install-source-utils.js";
|
|
2
|
+
import { resolveNpmIntegrityDriftWithDefaultMessage, } from "./npm-integrity.js";
|
|
3
|
+
export async function installFromNpmSpecArchive(params) {
|
|
4
|
+
return await withTempDir(params.tempDirPrefix, async (tmpDir) => {
|
|
5
|
+
const packedResult = await packNpmSpecToArchive({
|
|
6
|
+
spec: params.spec,
|
|
7
|
+
timeoutMs: params.timeoutMs,
|
|
8
|
+
cwd: tmpDir,
|
|
9
|
+
});
|
|
10
|
+
if (!packedResult.ok) {
|
|
11
|
+
return packedResult;
|
|
12
|
+
}
|
|
13
|
+
const npmResolution = {
|
|
14
|
+
...packedResult.metadata,
|
|
15
|
+
resolvedAt: new Date().toISOString(),
|
|
16
|
+
};
|
|
17
|
+
const driftResult = await resolveNpmIntegrityDriftWithDefaultMessage({
|
|
18
|
+
spec: params.spec,
|
|
19
|
+
expectedIntegrity: params.expectedIntegrity,
|
|
20
|
+
resolution: npmResolution,
|
|
21
|
+
onIntegrityDrift: params.onIntegrityDrift,
|
|
22
|
+
warn: params.warn,
|
|
23
|
+
});
|
|
24
|
+
if (driftResult.error) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
error: driftResult.error,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const installResult = await params.installFromArchive({
|
|
31
|
+
archivePath: packedResult.archivePath,
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
installResult,
|
|
36
|
+
npmResolution,
|
|
37
|
+
integrityDrift: driftResult.integrityDrift,
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -1,16 +1,29 @@
|
|
|
1
|
+
import { Separator, TextDisplay } from "@buape/carbon";
|
|
2
|
+
import { DiscordUiContainer } from "../../discord/ui.js";
|
|
3
|
+
class CrossContextContainer extends DiscordUiContainer {
|
|
4
|
+
constructor({ originLabel, message, cfg, accountId }) {
|
|
5
|
+
const trimmed = message.trim();
|
|
6
|
+
const components = [];
|
|
7
|
+
if (trimmed) {
|
|
8
|
+
components.push(new TextDisplay(message));
|
|
9
|
+
components.push(new Separator({ divider: true, spacing: "small" }));
|
|
10
|
+
}
|
|
11
|
+
components.push(new TextDisplay(`*From ${originLabel}*`));
|
|
12
|
+
super({ cfg, accountId, components });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
1
15
|
const DEFAULT_ADAPTER = {
|
|
2
|
-
|
|
16
|
+
supportsComponentsV2: false,
|
|
3
17
|
};
|
|
4
18
|
const DISCORD_ADAPTER = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
{
|
|
8
|
-
description: `From ${originLabel}`,
|
|
9
|
-
},
|
|
19
|
+
supportsComponentsV2: true,
|
|
20
|
+
buildCrossContextComponents: ({ originLabel, message, cfg, accountId }) => [
|
|
21
|
+
new CrossContextContainer({ originLabel, message, cfg, accountId }),
|
|
10
22
|
],
|
|
11
23
|
};
|
|
12
24
|
export function getChannelMessageAdapter(channel) {
|
|
13
|
-
if (channel === "discord")
|
|
25
|
+
if (channel === "discord") {
|
|
14
26
|
return DISCORD_ADAPTER;
|
|
27
|
+
}
|
|
15
28
|
return DEFAULT_ADAPTER;
|
|
16
29
|
}
|