blun-king-cli 9.1.565 → 9.1.567
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/LIESMICH.txt +2 -2
- package/README.md +1 -1
- package/agent-spine-plugin/.claude-plugin/marketplace.json +1 -1
- package/agent-spine-plugin/.claude-plugin/plugin.json +1 -1
- package/agent-spine-plugin/.codex-plugin/plugin.json +1 -1
- package/agent-spine-plugin/README.md +13 -1
- package/agent-spine-plugin/blun.plugin.json +1 -1
- package/agent-spine-plugin/hooks/version.json +1 -1
- package/agent-spine-plugin/package.json +1 -1
- package/agent-spine-plugin/src/hook.js +40 -44
- package/agent-spine-plugin/src/index.js +3 -2
- package/agent-spine-plugin/src/lib/gateway-runtime.js +531 -27
- package/agent-spine-plugin/src/lib/selfstarter.js +20 -1
- package/agent-spine-plugin/src/lib/source-roots.js +3 -2
- package/agent-spine-plugin/src/version.js +1 -1
- package/agent-spine-plugin/src/worker.js +12 -4
- package/bin/native-runtime-cache.cjs +76 -0
- package/blun.mjs +10 -0
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge.mjs +4 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { lstat, open, opendir, readFile, realpath, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
3
4
|
import { join, relative, resolve, sep } from "node:path";
|
|
4
5
|
import { buildCatalog } from "./catalog.js";
|
|
5
6
|
import { isFileLockContention, replaceFileWithRetry } from "./filesystem-retry.js";
|
|
6
7
|
import { loadCoordination } from "./coordination.js";
|
|
7
8
|
import { loadGraph } from "./graph.js";
|
|
8
|
-
import { projectStateDir } from "./paths.js";
|
|
9
|
+
import { comparablePath, projectStateDir } from "./paths.js";
|
|
9
10
|
|
|
10
11
|
const POLICY_SCHEMA = "agentspine.execution-policy/v1";
|
|
11
12
|
const JOB_SCHEMA = "agentspine.selfstarter/v1";
|
|
@@ -24,6 +25,18 @@ const SECRET_RE = /-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:sk|gh[opusu])_[A-Za-z
|
|
|
24
25
|
|
|
25
26
|
function workspaceScanError(error) { error.agentSpineScan = true; return error; }
|
|
26
27
|
|
|
28
|
+
function configuredHostProfileRoots(env = process.env) {
|
|
29
|
+
return [env.CODEX_HOME, env.BLUN_HOME, env.CLAUDE_CONFIG_DIR, join(homedir(), ".codex"), join(homedir(), ".claude")]
|
|
30
|
+
.filter((value) => typeof value === "string" && value)
|
|
31
|
+
.map((value) => comparablePath(value));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isConfiguredHostProfileRoot(root, env = process.env) {
|
|
35
|
+
const target = comparablePath(root);
|
|
36
|
+
return configuredHostProfileRoots(env).some((profile) => process.platform === "win32"
|
|
37
|
+
? profile.toLowerCase() === target.toLowerCase() : profile === target);
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
function emptyPolicy(root) {
|
|
28
41
|
return { schema: POLICY_SCHEMA, root, revision: 0, grants: [], history: [] };
|
|
29
42
|
}
|
|
@@ -140,6 +153,9 @@ async function withLock(path, read, operation, save = true) {
|
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
async function pathsFor(root, providedCatalog = null) {
|
|
156
|
+
if (isConfiguredHostProfileRoot(root)) {
|
|
157
|
+
throw new Error("self-starter cannot use a host profile as its workspace root");
|
|
158
|
+
}
|
|
143
159
|
let catalog = providedCatalog;
|
|
144
160
|
if (!catalog) {
|
|
145
161
|
try {
|
|
@@ -224,6 +240,9 @@ export async function collectWorkspaceFiles(root) {
|
|
|
224
240
|
|
|
225
241
|
export async function workspaceFingerprint(inputRoot = process.cwd()) {
|
|
226
242
|
const root = resolve(inputRoot);
|
|
243
|
+
if (isConfiguredHostProfileRoot(root)) {
|
|
244
|
+
throw new Error("self-starter cannot fingerprint a host profile root");
|
|
245
|
+
}
|
|
227
246
|
const collected = await collectWorkspaceFiles(root);
|
|
228
247
|
const files = [];
|
|
229
248
|
const skipped = [...collected.skipped];
|
|
@@ -473,8 +473,9 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
|
|
|
473
473
|
}
|
|
474
474
|
const knownHomeRoots = await homeRoots(env);
|
|
475
475
|
const skippedHomeTree = knownHomeRoots.some((root) => samePath(root, projectRoot));
|
|
476
|
+
const skippedProfileTree = samePath(hostHome, projectRoot);
|
|
476
477
|
const skippedFallbackHomeTree = skippedHomeTree && rootResolution === "cwd-fallback";
|
|
477
|
-
if (!skippedHomeTree) {
|
|
478
|
+
if (!skippedHomeTree && !skippedProfileTree) {
|
|
478
479
|
sources.push(...await boundedMarkdownTree(projectRoot, "agentspine:project", host, "project", 3000, deadline,
|
|
479
480
|
{ projectBoundary: true, maxFiles: MAX_PROJECT_FILES, maxDirectoryEntries: MAX_PROJECT_DIRECTORY_ENTRIES, skipped }));
|
|
480
481
|
}
|
|
@@ -504,7 +505,7 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
|
|
|
504
505
|
reason: documents.length ? null : "No regular, non-symlink host-native Markdown source exists in the checked scope.",
|
|
505
506
|
personalContinuityLoaded: documents.some((item) => item.sourceScope === "user") || Boolean(activeUserState),
|
|
506
507
|
broadHomeScan: false, projectTreeScan: skippedFallbackHomeTree ? "skipped-unmarked-home"
|
|
507
|
-
: skippedHomeTree ? "skipped-home-root" : "bounded",
|
|
508
|
+
: skippedHomeTree ? "skipped-home-root" : skippedProfileTree ? "skipped-profile-root" : "bounded",
|
|
508
509
|
skipped: skipped.sort((a, b) => a.path.localeCompare(b.path) || a.operation.localeCompare(b.operation)),
|
|
509
510
|
rootResolution, registryRevision: registry.revision,
|
|
510
511
|
...(host === "claude" ? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.59.1";
|
|
@@ -3,7 +3,10 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import { watch } from "node:fs";
|
|
4
4
|
import { lstat, realpath } from "node:fs/promises";
|
|
5
5
|
import { isAbsolute, join, resolve } from "node:path";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
claimGatewayWork, completeGatewayRun, deliverPrepared, executionAttemptForStep,
|
|
8
|
+
failGatewayRun, loadGatewayRuntime, reconcileGateway, updateGatewayHealth
|
|
9
|
+
} from "./lib/gateway-runtime.js";
|
|
7
10
|
import { createTelegramAdapter } from "./lib/telegram-adapter.js";
|
|
8
11
|
import { acknowledgeChannelDelivery, loadChannelRuntime } from "./lib/channel-runtime.js";
|
|
9
12
|
import { loadPersonaRuntime, syncPersonaRosterFromEnvironment } from "./lib/persona-runtime.js";
|
|
@@ -93,12 +96,15 @@ async function hostWorkItem(root, item) {
|
|
|
93
96
|
const goalStep = item.goalStepId === null || item.goalStepId === undefined ? null
|
|
94
97
|
: goal?.plan?.steps.find((entry) => entry.stepId === item.goalStepId) || null;
|
|
95
98
|
if (item.goalStepId && !goalStep) throw new Error("claimed work lost its exact goal-plan step");
|
|
99
|
+
const executionAttempt = executionAttemptForStep(goalStep);
|
|
96
100
|
const event = item.channelEventId === null ? null
|
|
97
101
|
: channel.runtime.events.find((entry) => entry.eventId === item.channelEventId) || null;
|
|
98
102
|
if (item.channelEventId && !event) throw new Error("claimed channel work lost its exact event");
|
|
99
103
|
return {
|
|
100
104
|
...structuredClone(item), host: identity.host, profileId: identity.profileId, projectRoot: root,
|
|
101
|
-
goal: goal ? structuredClone(goal) : null,
|
|
105
|
+
goal: goal ? structuredClone(goal) : null,
|
|
106
|
+
goalStep: goalStep ? { ...structuredClone(goalStep),
|
|
107
|
+
...(executionAttempt === null ? {} : { executionAttempt }) } : null,
|
|
102
108
|
hostEnvironment: {
|
|
103
109
|
AGENTSPINE_GATEWAY_CONTEXT: "agentspine.gateway-start/v1",
|
|
104
110
|
AGENTSPINE_ENTITY_ID: item.agentId,
|
|
@@ -156,9 +162,11 @@ export async function runWorkerTick({ root = process.cwd(), workerId = "gateway-
|
|
|
156
162
|
}
|
|
157
163
|
const completed = await completeGatewayRun({ root, queueId: claim.item.queueId, workerId, result, now });
|
|
158
164
|
if (!completed.outbox) return {
|
|
159
|
-
status: completed.clarification ? "needs-clarification"
|
|
165
|
+
status: completed.clarification ? "needs-clarification"
|
|
166
|
+
: completed.exploration ? "exploring" : completed.item.status,
|
|
160
167
|
processed: true, queueId: completed.item.queueId,
|
|
161
|
-
...(completed.clarification ? { clarification: completed.clarification } : {})
|
|
168
|
+
...(completed.clarification ? { clarification: completed.clarification } : {}),
|
|
169
|
+
...(completed.exploration ? { exploration: completed.exploration } : {})
|
|
162
170
|
};
|
|
163
171
|
const delivery = await deliverPrepared({ root, outboxId: completed.outbox.outboxId,
|
|
164
172
|
adapter: deliveryAdapter, now });
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
function isPathInside(root, candidate) {
|
|
9
|
+
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
10
|
+
return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function cacheBase(options = {}) {
|
|
14
|
+
if (options.cacheBase) return path.resolve(options.cacheBase);
|
|
15
|
+
const env = options.env || process.env;
|
|
16
|
+
const platform = options.platform || process.platform;
|
|
17
|
+
if (env.BLUN_CACHE_DIR) return path.resolve(env.BLUN_CACHE_DIR);
|
|
18
|
+
if (platform === 'win32') {
|
|
19
|
+
return path.resolve(env.LOCALAPPDATA || path.join(options.homeDir || os.homedir(), 'AppData', 'Local'), 'blun');
|
|
20
|
+
}
|
|
21
|
+
if (platform === 'darwin') return path.join(options.homeDir || os.homedir(), 'Library', 'Caches', 'blun');
|
|
22
|
+
return path.join(env.XDG_CACHE_HOME || path.join(options.homeDir || os.homedir(), '.cache'), 'blun');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sha256(bytes) {
|
|
26
|
+
return crypto.createHash('sha256').update(bytes).digest('hex');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ensureCachedFile(sourcePath, targetPath, expectedHash) {
|
|
30
|
+
try {
|
|
31
|
+
if (sha256(fs.readFileSync(targetPath)) === expectedHash) return targetPath;
|
|
32
|
+
} catch {}
|
|
33
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
34
|
+
const temporary = `${targetPath}.${process.pid}.${Date.now()}.tmp`;
|
|
35
|
+
fs.copyFileSync(sourcePath, temporary);
|
|
36
|
+
try {
|
|
37
|
+
fs.renameSync(temporary, targetPath);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
try {
|
|
40
|
+
if (sha256(fs.readFileSync(targetPath)) === expectedHash) {
|
|
41
|
+
fs.rmSync(temporary, { force: true });
|
|
42
|
+
return targetPath;
|
|
43
|
+
}
|
|
44
|
+
} catch {}
|
|
45
|
+
fs.rmSync(temporary, { force: true });
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
return targetPath;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveNativeRuntimePath(requestPath, options = {}) {
|
|
52
|
+
if (typeof requestPath !== 'string' || path.extname(requestPath).toLowerCase() !== '.node') return requestPath;
|
|
53
|
+
const packageRoot = options.packageRoot && path.resolve(options.packageRoot);
|
|
54
|
+
const sourcePath = path.resolve(requestPath);
|
|
55
|
+
if (!packageRoot || !isPathInside(packageRoot, sourcePath)) return requestPath;
|
|
56
|
+
let bytes;
|
|
57
|
+
try {
|
|
58
|
+
bytes = fs.readFileSync(sourcePath);
|
|
59
|
+
} catch {
|
|
60
|
+
return requestPath;
|
|
61
|
+
}
|
|
62
|
+
const hash = sha256(bytes);
|
|
63
|
+
const relativePath = path.relative(packageRoot, sourcePath);
|
|
64
|
+
const relativeHash = sha256(Buffer.from(relativePath.replaceAll('\\', '/'))).slice(0, 16);
|
|
65
|
+
const targetPath = path.join(cacheBase(options), 'native-runtime', hash, `${relativeHash}-${path.basename(sourcePath)}`);
|
|
66
|
+
try {
|
|
67
|
+
return ensureCachedFile(sourcePath, targetPath, hash);
|
|
68
|
+
} catch {
|
|
69
|
+
return requestPath;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
isPathInside,
|
|
75
|
+
resolveNativeRuntimePath,
|
|
76
|
+
};
|
package/blun.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import sessionScrollbackArchive from "./bin/session-scrollback-archive.cjs";
|
|
|
17
17
|
import sessionReplayWindowPolicy from "./bin/session-replay-window-policy.cjs";
|
|
18
18
|
import globPatternPolicy from "./bin/glob-pattern-policy.cjs";
|
|
19
19
|
import userHomePathPolicy from "./bin/user-home-path-policy.cjs";
|
|
20
|
+
import nativeRuntimeCachePolicy from "./bin/native-runtime-cache.cjs";
|
|
20
21
|
import editableToolApprovalPolicy from "./bin/editable-tool-approval-policy.cjs";
|
|
21
22
|
import editableToolApprovalRuntime from "./bin/editable-tool-approval-runtime.cjs";
|
|
22
23
|
import crypto$1, { createHash, randomBytes, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -59,6 +60,7 @@ const { SessionScrollbackArchive, sessionScrollbackArchiveDirectory } = sessionS
|
|
|
59
60
|
const { replayWindowStartIndex, resolveReplayActiveTurnCount } = sessionReplayWindowPolicy;
|
|
60
61
|
const { globPatternError } = globPatternPolicy;
|
|
61
62
|
const { unsupportedUserHomePathError } = userHomePathPolicy;
|
|
63
|
+
const { resolveNativeRuntimePath } = nativeRuntimeCachePolicy;
|
|
62
64
|
const {
|
|
63
65
|
buildApprovalEditedEvent,
|
|
64
66
|
canonicalEditedToolInputJson,
|
|
@@ -523527,6 +523529,7 @@ function installNativeModuleHook() {
|
|
|
523527
523529
|
installed = true;
|
|
523528
523530
|
const moduleBuiltin = nodeRequire("node:module");
|
|
523529
523531
|
const originalLoad = moduleBuiltin._load;
|
|
523532
|
+
const originalResolveFilename = moduleBuiltin._resolveFilename;
|
|
523530
523533
|
if (originalLoad === void 0) return;
|
|
523531
523534
|
moduleBuiltin._load = function loadWithNativeAssets(request, parent, isMain) {
|
|
523532
523535
|
if (request === "node-pty") {
|
|
@@ -523543,6 +523546,13 @@ function installNativeModuleHook() {
|
|
|
523543
523546
|
}
|
|
523544
523547
|
}
|
|
523545
523548
|
}
|
|
523549
|
+
if (typeof request === "string" && request.toLowerCase().endsWith(".node") && typeof originalResolveFilename === "function") {
|
|
523550
|
+
try {
|
|
523551
|
+
const resolvedRequest = originalResolveFilename.call(moduleBuiltin, request, parent, isMain);
|
|
523552
|
+
const cachedRequest = resolveNativeRuntimePath(resolvedRequest, { packageRoot: __dirname });
|
|
523553
|
+
if (cachedRequest !== resolvedRequest) return originalLoad.call(this, cachedRequest, parent, isMain);
|
|
523554
|
+
} catch {}
|
|
523555
|
+
}
|
|
523546
523556
|
return originalLoad.call(this, request, parent, isMain);
|
|
523547
523557
|
};
|
|
523548
523558
|
}
|
package/package.json
CHANGED
|
@@ -212,8 +212,11 @@ function gate(ctx, botUsername) {
|
|
|
212
212
|
if (policy === void 0) return { action: "drop" };
|
|
213
213
|
const groupAllowFrom = policy.allowFrom ?? [];
|
|
214
214
|
if (groupAllowFrom.length > 0 && !groupAllowFrom.includes(senderId)) return { action: "drop" };
|
|
215
|
+
const ignored = matchesAny(ctx.text, access.ignorePatterns);
|
|
216
|
+
const mentioned = !ignored && isMentioned(ctx, botUsername, access.mentionPatterns);
|
|
217
|
+
if (policy.requireMention === true && !mentioned) return { action: "drop" };
|
|
215
218
|
const mentionBypass = from.isBot !== true && (policy.alwaysAllowFrom ?? []).includes(senderId);
|
|
216
|
-
const addressed = !
|
|
219
|
+
const addressed = mentioned || !ignored && mentionBypass;
|
|
217
220
|
const targetedElsewhere = from.isBot === true && !addressed;
|
|
218
221
|
return {
|
|
219
222
|
action: "deliver",
|