negotium 0.3.14 → 0.4.1
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/README.md +2 -6
- package/dist/agent-helpers.js +333 -187
- package/dist/agent-helpers.js.map +9 -8
- package/dist/{chunk-0ynjwr50.js → chunk-zq2tcq4k.js} +12 -29
- package/dist/{chunk-0ynjwr50.js.map → chunk-zq2tcq4k.js.map} +4 -4
- package/dist/hosted-agent.js +214 -65
- package/dist/hosted-agent.js.map +8 -7
- package/dist/main.js +712 -895
- package/dist/main.js.map +10 -11
- package/dist/mcp-factories.js +305 -470
- package/dist/mcp-factories.js.map +9 -10
- package/dist/registry.js +3 -3
- package/dist/registry.js.map +2 -2
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/codex-provider.ts +40 -9
- package/dist/runtime/src/agents/codex-vault-hook-bridge.ts +198 -0
- package/dist/runtime/src/agents/codex-vault-hook.mjs +50 -0
- package/dist/runtime/src/agents/execution-host.ts +1 -14
- package/dist/runtime/src/agents/public-helpers.ts +0 -9
- package/dist/runtime/src/agents/vault-tool-policy.ts +9 -51
- package/dist/runtime/src/mcp/factories/index.ts +1 -8
- package/dist/runtime/src/mcp/factories/vault.ts +6 -104
- package/dist/runtime/src/mcp/vault-server.ts +4 -20
- package/dist/runtime/src/prompts/sessions/_shared-tools.md +1 -1
- package/dist/runtime/src/version.ts +1 -1
- package/dist/types/packages/core/src/agents/codex-vault-hook-bridge.d.ts +28 -0
- package/dist/types/packages/core/src/agents/execution-host.d.ts +0 -2
- package/dist/types/packages/core/src/agents/public-helpers.d.ts +0 -1
- package/dist/types/packages/core/src/agents/vault-tool-policy.d.ts +1 -14
- package/dist/types/packages/core/src/mcp/factories/index.d.ts +1 -3
- package/dist/types/packages/core/src/mcp/factories/vault.d.ts +4 -13
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/dist/runtime/src/mcp/factories/vault-host.ts +0 -8
- package/dist/runtime/src/mcp/vault-http.ts +0 -235
- package/dist/runtime/src/mcp/vault-run.ts +0 -154
- package/dist/types/packages/core/src/mcp/factories/vault-host.d.ts +0 -7
- package/dist/types/packages/core/src/mcp/vault-http.d.ts +0 -23
- package/dist/types/packages/core/src/mcp/vault-run.d.ts +0 -19
package/dist/main.js
CHANGED
|
@@ -1869,7 +1869,7 @@ var exports_version = {};
|
|
|
1869
1869
|
__export(exports_version, {
|
|
1870
1870
|
NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
|
|
1871
1871
|
});
|
|
1872
|
-
var NEGOTIUM_VERSION = "0.
|
|
1872
|
+
var NEGOTIUM_VERSION = "0.4.1";
|
|
1873
1873
|
|
|
1874
1874
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
1875
1875
|
import { spawn } from "child_process";
|
|
@@ -2139,30 +2139,21 @@ function shouldSubstituteVaultToolInput(toolName) {
|
|
|
2139
2139
|
const leaf = leafToolName(toolName);
|
|
2140
2140
|
return leaf.startsWith("browser_") || DIRECT_VAULT_EXECUTION_TOOLS.has(leaf);
|
|
2141
2141
|
}
|
|
2142
|
-
function
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
const lower = value.toLowerCase();
|
|
2149
|
-
if (SENSITIVE_RUNTIME_NAMES.some((name) => lower.includes(name)))
|
|
2150
|
-
return true;
|
|
2151
|
-
return value.startsWith("/") && host.isSensitivePath(value);
|
|
2152
|
-
}
|
|
2153
|
-
if (Array.isArray(value))
|
|
2154
|
-
return value.some(referencesRuntimeSecretStorage);
|
|
2155
|
-
if (value && typeof value === "object") {
|
|
2156
|
-
return Object.values(value).some(referencesRuntimeSecretStorage);
|
|
2157
|
-
}
|
|
2158
|
-
return false;
|
|
2142
|
+
function referencesRuntimeSecretStorage(value) {
|
|
2143
|
+
if (typeof value === "string") {
|
|
2144
|
+
const lower = value.toLowerCase();
|
|
2145
|
+
if (SENSITIVE_RUNTIME_NAMES.some((name) => lower.includes(name)))
|
|
2146
|
+
return true;
|
|
2147
|
+
return value.startsWith("/") && isSensitivePath(value);
|
|
2159
2148
|
}
|
|
2160
|
-
|
|
2161
|
-
return
|
|
2149
|
+
if (Array.isArray(value))
|
|
2150
|
+
return value.some(referencesRuntimeSecretStorage);
|
|
2151
|
+
if (value && typeof value === "object") {
|
|
2152
|
+
return Object.values(value).some(referencesRuntimeSecretStorage);
|
|
2162
2153
|
}
|
|
2163
|
-
return
|
|
2154
|
+
return false;
|
|
2164
2155
|
}
|
|
2165
|
-
var SENSITIVE_RUNTIME_NAMES, DIRECT_VAULT_EXECUTION_TOOLS
|
|
2156
|
+
var SENSITIVE_RUNTIME_NAMES, DIRECT_VAULT_EXECUTION_TOOLS;
|
|
2166
2157
|
var init_vault_tool_policy = __esm(() => {
|
|
2167
2158
|
init_sensitive_path();
|
|
2168
2159
|
SENSITIVE_RUNTIME_NAMES = [
|
|
@@ -2172,13 +2163,6 @@ var init_vault_tool_policy = __esm(() => {
|
|
|
2172
2163
|
"sessions.db"
|
|
2173
2164
|
];
|
|
2174
2165
|
DIRECT_VAULT_EXECUTION_TOOLS = new Set(["Bash", "WebFetch"]);
|
|
2175
|
-
defaultVaultToolPolicy = createVaultToolPolicy({
|
|
2176
|
-
isSensitivePath,
|
|
2177
|
-
valueReferencesVaultKey: () => false
|
|
2178
|
-
});
|
|
2179
|
-
isVaultBrokerTool = defaultVaultToolPolicy.isVaultBrokerTool;
|
|
2180
|
-
referencesRuntimeSecretStorage = defaultVaultToolPolicy.referencesRuntimeSecretStorage;
|
|
2181
|
-
shouldRedirectVaultTool = defaultVaultToolPolicy.shouldRedirectVaultTool;
|
|
2182
2166
|
});
|
|
2183
2167
|
|
|
2184
2168
|
// ../../packages/core/src/mcp/canonical-bridge-config.ts
|
|
@@ -3317,7 +3301,6 @@ var init_execution_host = __esm(async () => {
|
|
|
3317
3301
|
redactVaultSecrets,
|
|
3318
3302
|
substituteVaultSecrets: (userId, value) => vaultSubstituteDetailed(userId, value).text,
|
|
3319
3303
|
referencesRuntimeSecretStorage,
|
|
3320
|
-
shouldRedirectVaultTool,
|
|
3321
3304
|
claudeCodeExecutablePath: () => CLAUDE_EXECUTABLE,
|
|
3322
3305
|
codexAuthFilePath
|
|
3323
3306
|
};
|
|
@@ -5256,9 +5239,163 @@ var init_claude_provider = __esm(async () => {
|
|
|
5256
5239
|
]);
|
|
5257
5240
|
});
|
|
5258
5241
|
|
|
5242
|
+
// ../../packages/core/src/agents/codex-vault-hook-bridge.ts
|
|
5243
|
+
import { randomBytes as randomBytes5 } from "crypto";
|
|
5244
|
+
import { existsSync as existsSync13 } from "fs";
|
|
5245
|
+
import { chmod, mkdtemp, rm, writeFile } from "fs/promises";
|
|
5246
|
+
import { createServer } from "net";
|
|
5247
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
5248
|
+
import { dirname as dirname9, join as join13, resolve as resolve7 } from "path";
|
|
5249
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5250
|
+
function evaluateCodexVaultPreToolUse(input, userId, operations) {
|
|
5251
|
+
if (operations.referencesSensitiveStorage(input.tool_input)) {
|
|
5252
|
+
return {
|
|
5253
|
+
hookSpecificOutput: {
|
|
5254
|
+
hookEventName: "PreToolUse",
|
|
5255
|
+
permissionDecision: "deny",
|
|
5256
|
+
permissionDecisionReason: SENSITIVE_STORAGE_DENIAL
|
|
5257
|
+
}
|
|
5258
|
+
};
|
|
5259
|
+
}
|
|
5260
|
+
if (!shouldSubstituteVaultToolInput(input.tool_name))
|
|
5261
|
+
return {};
|
|
5262
|
+
const updatedInput = deepMapStrings(input.tool_input, (value) => operations.substitute(userId, value));
|
|
5263
|
+
if (JSON.stringify(updatedInput) === JSON.stringify(input.tool_input))
|
|
5264
|
+
return {};
|
|
5265
|
+
return {
|
|
5266
|
+
hookSpecificOutput: {
|
|
5267
|
+
hookEventName: "PreToolUse",
|
|
5268
|
+
permissionDecision: "allow",
|
|
5269
|
+
updatedInput
|
|
5270
|
+
}
|
|
5271
|
+
};
|
|
5272
|
+
}
|
|
5273
|
+
function shellQuote(value) {
|
|
5274
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
5275
|
+
}
|
|
5276
|
+
function hookClientPath() {
|
|
5277
|
+
const moduleDir = dirname9(fileURLToPath2(import.meta.url));
|
|
5278
|
+
const adjacent = resolve7(moduleDir, "codex-vault-hook.mjs");
|
|
5279
|
+
if (existsSync13(adjacent))
|
|
5280
|
+
return adjacent;
|
|
5281
|
+
const packaged = resolve7(moduleDir, "runtime/src/agents/codex-vault-hook.mjs");
|
|
5282
|
+
if (existsSync13(packaged))
|
|
5283
|
+
return packaged;
|
|
5284
|
+
throw new Error("Codex Vault hook client is missing from this installation");
|
|
5285
|
+
}
|
|
5286
|
+
function privateCodexWrapper(codexScript, socketPath, token) {
|
|
5287
|
+
return [
|
|
5288
|
+
"#!/bin/sh",
|
|
5289
|
+
`export ${HOOK_SOCKET_ENV}=${shellQuote(socketPath)}`,
|
|
5290
|
+
`export ${HOOK_TOKEN_ENV}=${shellQuote(token)}`,
|
|
5291
|
+
'if [ "$1" = "exec" ]; then',
|
|
5292
|
+
" shift",
|
|
5293
|
+
` exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} exec --dangerously-bypass-hook-trust "$@"`,
|
|
5294
|
+
"fi",
|
|
5295
|
+
`exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} "$@"`,
|
|
5296
|
+
""
|
|
5297
|
+
].join(`
|
|
5298
|
+
`);
|
|
5299
|
+
}
|
|
5300
|
+
async function createCodexVaultHookBridge(userId) {
|
|
5301
|
+
const root = await mkdtemp(join13(tmpdir2(), "negotium-codex-vault-"));
|
|
5302
|
+
await chmod(root, 448);
|
|
5303
|
+
const socketPath = join13(root, "hook.sock");
|
|
5304
|
+
const wrapperPath = join13(root, "codex-with-hooks");
|
|
5305
|
+
const token = randomBytes5(32).toString("hex");
|
|
5306
|
+
const connections = new Set;
|
|
5307
|
+
const server = createServer((socket) => {
|
|
5308
|
+
connections.add(socket);
|
|
5309
|
+
socket.once("close", () => connections.delete(socket));
|
|
5310
|
+
socket.setEncoding("utf8");
|
|
5311
|
+
let request = "";
|
|
5312
|
+
let handled = false;
|
|
5313
|
+
const handleRequest = () => {
|
|
5314
|
+
if (handled)
|
|
5315
|
+
return;
|
|
5316
|
+
handled = true;
|
|
5317
|
+
try {
|
|
5318
|
+
const parsed = JSON.parse(request.trimEnd());
|
|
5319
|
+
if (parsed.token !== token)
|
|
5320
|
+
throw new Error("invalid hook capability");
|
|
5321
|
+
if (!parsed.input || typeof parsed.input !== "object" || typeof parsed.input.tool_name !== "string") {
|
|
5322
|
+
throw new Error("invalid PreToolUse payload");
|
|
5323
|
+
}
|
|
5324
|
+
const output = evaluateCodexVaultPreToolUse(parsed.input, userId, {
|
|
5325
|
+
referencesSensitiveStorage: referencesHostedSecretStorage,
|
|
5326
|
+
substitute: substituteHostedSecrets
|
|
5327
|
+
});
|
|
5328
|
+
socket.end(JSON.stringify({ ok: true, output }));
|
|
5329
|
+
} catch (error) {
|
|
5330
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5331
|
+
socket.end(JSON.stringify({ ok: false, error: message }));
|
|
5332
|
+
}
|
|
5333
|
+
};
|
|
5334
|
+
socket.on("data", (chunk) => {
|
|
5335
|
+
request += chunk;
|
|
5336
|
+
if (Buffer.byteLength(request) > MAX_HOOK_REQUEST_BYTES)
|
|
5337
|
+
socket.destroy();
|
|
5338
|
+
else if (request.endsWith(`
|
|
5339
|
+
`))
|
|
5340
|
+
handleRequest();
|
|
5341
|
+
});
|
|
5342
|
+
socket.on("end", handleRequest);
|
|
5343
|
+
});
|
|
5344
|
+
try {
|
|
5345
|
+
await new Promise((resolveListen, reject) => {
|
|
5346
|
+
server.once("error", reject);
|
|
5347
|
+
server.listen(socketPath, () => {
|
|
5348
|
+
server.off("error", reject);
|
|
5349
|
+
resolveListen();
|
|
5350
|
+
});
|
|
5351
|
+
});
|
|
5352
|
+
await chmod(socketPath, 384);
|
|
5353
|
+
await writeFile(wrapperPath, privateCodexWrapper(codexCliScriptPath(), socketPath, token), {
|
|
5354
|
+
mode: 448
|
|
5355
|
+
});
|
|
5356
|
+
const command = [shellQuote(process.execPath), shellQuote(hookClientPath())].join(" ");
|
|
5357
|
+
return {
|
|
5358
|
+
codexPathOverride: wrapperPath,
|
|
5359
|
+
environment: { [HOOK_SOCKET_ENV]: socketPath, [HOOK_TOKEN_ENV]: token },
|
|
5360
|
+
hooks: {
|
|
5361
|
+
PreToolUse: [
|
|
5362
|
+
{
|
|
5363
|
+
matcher: "*",
|
|
5364
|
+
hooks: [
|
|
5365
|
+
{
|
|
5366
|
+
type: "command",
|
|
5367
|
+
command,
|
|
5368
|
+
timeout: 30,
|
|
5369
|
+
statusMessage: "Resolving Vault placeholders"
|
|
5370
|
+
}
|
|
5371
|
+
]
|
|
5372
|
+
}
|
|
5373
|
+
]
|
|
5374
|
+
},
|
|
5375
|
+
async close() {
|
|
5376
|
+
for (const socket of connections)
|
|
5377
|
+
socket.destroy();
|
|
5378
|
+
await new Promise((resolveClose) => server.close(() => resolveClose()));
|
|
5379
|
+
await rm(root, { recursive: true, force: true });
|
|
5380
|
+
}
|
|
5381
|
+
};
|
|
5382
|
+
} catch (error) {
|
|
5383
|
+
server.close();
|
|
5384
|
+
await rm(root, { recursive: true, force: true });
|
|
5385
|
+
throw error;
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
var MAX_HOOK_REQUEST_BYTES, SENSITIVE_STORAGE_DENIAL = "Runtime secret storage access is not permitted", HOOK_SOCKET_ENV = "NEGOTIUM_CODEX_VAULT_HOOK_SOCKET", HOOK_TOKEN_ENV = "NEGOTIUM_CODEX_VAULT_HOOK_TOKEN";
|
|
5389
|
+
var init_codex_vault_hook_bridge = __esm(async () => {
|
|
5390
|
+
init_codex_native_multi_agent();
|
|
5391
|
+
await init_execution_host();
|
|
5392
|
+
init_vault_tool_policy();
|
|
5393
|
+
MAX_HOOK_REQUEST_BYTES = 1024 * 1024;
|
|
5394
|
+
});
|
|
5395
|
+
|
|
5259
5396
|
// ../../packages/core/src/agents/tool-format.ts
|
|
5260
5397
|
import { readFileSync as readFileSync10, statSync as statSync4 } from "fs";
|
|
5261
|
-
import { isAbsolute, resolve as
|
|
5398
|
+
import { isAbsolute, resolve as resolve8 } from "path";
|
|
5262
5399
|
import { diffLines as computeLineDiff } from "diff";
|
|
5263
5400
|
function summarizeDisplayText(value) {
|
|
5264
5401
|
const normalized = value.replace(/\s+/g, " ").trim();
|
|
@@ -5582,7 +5719,7 @@ function sourceStartLine(input, before, after, cwd) {
|
|
|
5582
5719
|
const rawPath = input.file_path ?? input.path;
|
|
5583
5720
|
if (typeof rawPath !== "string" || !rawPath.trim())
|
|
5584
5721
|
return 1;
|
|
5585
|
-
const path = isAbsolute(rawPath) ? rawPath :
|
|
5722
|
+
const path = isAbsolute(rawPath) ? rawPath : resolve8(cwd ?? process.cwd(), rawPath);
|
|
5586
5723
|
try {
|
|
5587
5724
|
if (statSync4(path).size > TOOL_DIFF_SOURCE_MAX_BYTES)
|
|
5588
5725
|
return 1;
|
|
@@ -5780,9 +5917,9 @@ __export(exports_codex_provider, {
|
|
|
5780
5917
|
codexProvider: () => codexProvider
|
|
5781
5918
|
});
|
|
5782
5919
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
5783
|
-
import { existsSync as
|
|
5920
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync5 } from "fs";
|
|
5784
5921
|
import { homedir as homedir7 } from "os";
|
|
5785
|
-
import { dirname as
|
|
5922
|
+
import { dirname as dirname10, isAbsolute as isAbsolute2, join as join14, relative, resolve as resolve9 } from "path";
|
|
5786
5923
|
import { Codex } from "@openai/codex-sdk";
|
|
5787
5924
|
function sameCodexUsage(usage, total) {
|
|
5788
5925
|
return usage.input_tokens === total.inputTokens && usage.output_tokens === total.outputTokens && (usage.cached_input_tokens ?? 0) === total.cachedInputTokens && (usage.cache_write_input_tokens ?? 0) === total.cacheWriteInputTokens;
|
|
@@ -5810,8 +5947,8 @@ function codexMcpServerName(name) {
|
|
|
5810
5947
|
return CODEX_MCP_SERVER_NAME_OVERRIDES[name] ?? name;
|
|
5811
5948
|
}
|
|
5812
5949
|
function globalCodexMcpServerNames(authFilePath) {
|
|
5813
|
-
const configPath =
|
|
5814
|
-
if (!
|
|
5950
|
+
const configPath = join14(dirname10(authFilePath), "config.toml");
|
|
5951
|
+
if (!existsSync14(configPath))
|
|
5815
5952
|
return [];
|
|
5816
5953
|
try {
|
|
5817
5954
|
const names = new Set;
|
|
@@ -5906,13 +6043,13 @@ function summarizeMcpToolCallResult(item) {
|
|
|
5906
6043
|
}
|
|
5907
6044
|
function canonicalPath(path) {
|
|
5908
6045
|
try {
|
|
5909
|
-
return realpathSync3(
|
|
6046
|
+
return realpathSync3(resolve9(path));
|
|
5910
6047
|
} catch {
|
|
5911
|
-
return
|
|
6048
|
+
return resolve9(path);
|
|
5912
6049
|
}
|
|
5913
6050
|
}
|
|
5914
6051
|
function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
|
|
5915
|
-
if (!
|
|
6052
|
+
if (!existsSync14(path))
|
|
5916
6053
|
return null;
|
|
5917
6054
|
try {
|
|
5918
6055
|
const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
|
|
@@ -5952,7 +6089,7 @@ class CodexFilePreviewTracker {
|
|
|
5952
6089
|
const code = record.slice(0, 2);
|
|
5953
6090
|
const path = record.slice(3);
|
|
5954
6091
|
const remainingBytes = CODEX_DIFF_BASELINE_BYTE_LIMIT - loadedBytes;
|
|
5955
|
-
const content = loadedFiles < CODEX_DIFF_BASELINE_FILE_LIMIT && remainingBytes > 0 ? textFile(
|
|
6092
|
+
const content = loadedFiles < CODEX_DIFF_BASELINE_FILE_LIMIT && remainingBytes > 0 ? textFile(resolve9(this.#root, path), remainingBytes) : undefined;
|
|
5956
6093
|
this.#baseline.set(path, content);
|
|
5957
6094
|
if (typeof content === "string") {
|
|
5958
6095
|
loadedFiles += 1;
|
|
@@ -5965,7 +6102,7 @@ class CodexFilePreviewTracker {
|
|
|
5965
6102
|
preview(path, succeeded) {
|
|
5966
6103
|
if (!this.#root || !this.#baselineAvailable || !succeeded)
|
|
5967
6104
|
return {};
|
|
5968
|
-
const absolute = canonicalPath(isAbsolute2(path) ? path :
|
|
6105
|
+
const absolute = canonicalPath(isAbsolute2(path) ? path : resolve9(this.#cwd, path));
|
|
5969
6106
|
const relativePath = relative(this.#root, absolute);
|
|
5970
6107
|
if (!relativePath || relativePath.startsWith("..") || isAbsolute2(relativePath))
|
|
5971
6108
|
return {};
|
|
@@ -6000,7 +6137,7 @@ class CodexFilePreviewTracker {
|
|
|
6000
6137
|
}
|
|
6001
6138
|
}
|
|
6002
6139
|
async function fileChangeEvents(item, previews, cwd, threadId, consumedPatchCallIds) {
|
|
6003
|
-
const expectedPaths = item.changes.map((change) => isAbsolute2(change.path) ? change.path :
|
|
6140
|
+
const expectedPaths = item.changes.map((change) => isAbsolute2(change.path) ? change.path : resolve9(cwd, change.path));
|
|
6004
6141
|
let nativePreview;
|
|
6005
6142
|
if (threadId && item.status === "completed") {
|
|
6006
6143
|
await new Promise((resolveDelay) => setTimeout(resolveDelay, 20));
|
|
@@ -6124,7 +6261,7 @@ async function* codexProvider(opts) {
|
|
|
6124
6261
|
const filePreviews = new CodexFilePreviewTracker(opts.cwd);
|
|
6125
6262
|
const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
|
|
6126
6263
|
const codexAuthPath = hostedCodexAuthFilePath();
|
|
6127
|
-
if (!
|
|
6264
|
+
if (!existsSync14(codexAuthPath)) {
|
|
6128
6265
|
yield {
|
|
6129
6266
|
type: "error",
|
|
6130
6267
|
content: `Codex auth file not found at ${codexAuthPath}. Run \`codex login\` to authenticate.`
|
|
@@ -6163,21 +6300,30 @@ async function* codexProvider(opts) {
|
|
|
6163
6300
|
model: opts.model ?? "(sdk default)",
|
|
6164
6301
|
effort: opts.effort ?? "(off)",
|
|
6165
6302
|
cwd: opts.cwd,
|
|
6166
|
-
cwdExists:
|
|
6303
|
+
cwdExists: existsSync14(opts.cwd),
|
|
6167
6304
|
resume: Boolean(opts.sessionId),
|
|
6168
6305
|
mcpServerCount: Object.keys(codexMcpServers).length
|
|
6169
6306
|
}, "codexProvider: starting turn");
|
|
6170
6307
|
const hostedCodexHome = hostedCodexHomePath();
|
|
6171
|
-
const inheritedCodexHome = process.env.CODEX_HOME ||
|
|
6172
|
-
const codexEnvironment = scopedBrowserCapability ||
|
|
6308
|
+
const inheritedCodexHome = process.env.CODEX_HOME || join14(homedir7(), ".codex");
|
|
6309
|
+
const codexEnvironment = scopedBrowserCapability || resolve9(hostedCodexHome) !== resolve9(inheritedCodexHome) ? {
|
|
6173
6310
|
...Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string")),
|
|
6174
6311
|
CODEX_HOME: hostedCodexHome,
|
|
6175
6312
|
...scopedBrowserCapability ? { [CODEX_BROWSER_CAPABILITY_ENV]: scopedBrowserCapability } : {}
|
|
6176
6313
|
} : undefined;
|
|
6314
|
+
let vaultHook;
|
|
6315
|
+
try {
|
|
6316
|
+
vaultHook = await createCodexVaultHookBridge(opts.vaultUserId ?? opts.userId ?? "");
|
|
6317
|
+
} catch (err) {
|
|
6318
|
+
yield { type: "error", content: `Failed to initialize Codex Vault hooks: ${errMsg(err)}` };
|
|
6319
|
+
return;
|
|
6320
|
+
}
|
|
6177
6321
|
const codex = new Codex({
|
|
6322
|
+
codexPathOverride: vaultHook.codexPathOverride,
|
|
6178
6323
|
...codexEnvironment ? { env: codexEnvironment } : {},
|
|
6179
6324
|
config: {
|
|
6180
|
-
features: { multi_agent: false, multi_agent_v2: false, enable_fanout: false },
|
|
6325
|
+
features: { hooks: true, multi_agent: false, multi_agent_v2: false, enable_fanout: false },
|
|
6326
|
+
hooks: vaultHook.hooks,
|
|
6181
6327
|
model_catalog_json: codexModelCatalogPath,
|
|
6182
6328
|
mcp_servers: codexMcpServers,
|
|
6183
6329
|
...opts.toolPolicy ? { sandbox_permissions: [] } : {}
|
|
@@ -6281,7 +6427,7 @@ async function* codexProvider(opts) {
|
|
|
6281
6427
|
if (!item)
|
|
6282
6428
|
break;
|
|
6283
6429
|
if (item.type === "command_execution") {
|
|
6284
|
-
const command = String(item.command ?? "");
|
|
6430
|
+
const command = redactHostedSecrets(opts.vaultUserId ?? opts.userId ?? "", String(item.command ?? ""));
|
|
6285
6431
|
yield {
|
|
6286
6432
|
type: "tool_use",
|
|
6287
6433
|
name: "Bash",
|
|
@@ -6289,10 +6435,11 @@ async function* codexProvider(opts) {
|
|
|
6289
6435
|
toolUseId: String(item.id ?? "")
|
|
6290
6436
|
};
|
|
6291
6437
|
} else if (item.type === "mcp_tool_call") {
|
|
6438
|
+
const input = item.arguments && typeof item.arguments === "object" ? item.arguments : {};
|
|
6292
6439
|
yield {
|
|
6293
6440
|
type: "tool_use",
|
|
6294
6441
|
name: String(item.tool ?? "unknown"),
|
|
6295
|
-
input:
|
|
6442
|
+
input: deepMapStrings(input, (value) => redactHostedSecrets(opts.vaultUserId ?? opts.userId ?? "", value)),
|
|
6296
6443
|
toolUseId: String(item.id ?? "")
|
|
6297
6444
|
};
|
|
6298
6445
|
}
|
|
@@ -6335,7 +6482,7 @@ async function* codexProvider(opts) {
|
|
|
6335
6482
|
yield {
|
|
6336
6483
|
type: "tool_result",
|
|
6337
6484
|
toolUseId: String(item.id ?? ""),
|
|
6338
|
-
content: summarizeMcpToolCallResult(item),
|
|
6485
|
+
content: redactHostedSecrets(opts.vaultUserId ?? opts.userId ?? "", summarizeMcpToolCallResult(item)),
|
|
6339
6486
|
...isError ? { isError: true } : {}
|
|
6340
6487
|
};
|
|
6341
6488
|
} else if (item.type === "command_execution") {
|
|
@@ -6343,7 +6490,7 @@ async function* codexProvider(opts) {
|
|
|
6343
6490
|
yield {
|
|
6344
6491
|
type: "tool_result",
|
|
6345
6492
|
toolUseId: String(item.id ?? ""),
|
|
6346
|
-
content: String(item.aggregated_output ?? "").slice(0, 200),
|
|
6493
|
+
content: redactHostedSecrets(opts.vaultUserId ?? opts.userId ?? "", String(item.aggregated_output ?? "").slice(0, 200)),
|
|
6347
6494
|
...isError ? { isError: true } : {}
|
|
6348
6495
|
};
|
|
6349
6496
|
} else if (item.type === "file_change") {
|
|
@@ -6352,7 +6499,10 @@ async function* codexProvider(opts) {
|
|
|
6352
6499
|
yield fileEvent;
|
|
6353
6500
|
}
|
|
6354
6501
|
} else if (item.type === "error") {
|
|
6355
|
-
|
|
6502
|
+
const message = String(item.message ?? "");
|
|
6503
|
+
if (message !== CODEX_HOOK_TRUST_BYPASS_NOTICE) {
|
|
6504
|
+
yield { type: "error", content: message };
|
|
6505
|
+
}
|
|
6356
6506
|
}
|
|
6357
6507
|
break;
|
|
6358
6508
|
}
|
|
@@ -6440,12 +6590,14 @@ async function* codexProvider(opts) {
|
|
|
6440
6590
|
abortSignal?.removeEventListener("abort", onAbortKill);
|
|
6441
6591
|
if (trackedPids.pids.length > 0)
|
|
6442
6592
|
unregisterOwnedCodexPids(trackedPids.pids);
|
|
6593
|
+
await vaultHook.close();
|
|
6443
6594
|
}
|
|
6444
6595
|
}
|
|
6445
|
-
var CODEX_MCP_SERVER_NAME_OVERRIDES, CODEX_DIFF_FILE_LIMIT, CODEX_DIFF_BASELINE_FILE_LIMIT = 200, CODEX_DIFF_BASELINE_BYTE_LIMIT, CODEX_STARTUP_TIMEOUT_MS = 90000;
|
|
6596
|
+
var CODEX_HOOK_TRUST_BYPASS_NOTICE = "`--dangerously-bypass-hook-trust` is enabled. Enabled hooks may run without review for this invocation.", CODEX_MCP_SERVER_NAME_OVERRIDES, CODEX_DIFF_FILE_LIMIT, CODEX_DIFF_BASELINE_FILE_LIMIT = 200, CODEX_DIFF_BASELINE_BYTE_LIMIT, CODEX_STARTUP_TIMEOUT_MS = 90000;
|
|
6446
6597
|
var init_codex_provider = __esm(async () => {
|
|
6447
6598
|
init_codex_native_multi_agent();
|
|
6448
6599
|
init_codex_tree_kill();
|
|
6600
|
+
await init_codex_vault_hook_bridge();
|
|
6449
6601
|
await init_execution_host();
|
|
6450
6602
|
await init_codex();
|
|
6451
6603
|
init_tool_format();
|
|
@@ -6468,7 +6620,7 @@ __export(exports_maestro_provider, {
|
|
|
6468
6620
|
buildMaestroToolHooks: () => buildMaestroToolHooks,
|
|
6469
6621
|
buildMaestroDisallowedTools: () => buildMaestroDisallowedTools
|
|
6470
6622
|
});
|
|
6471
|
-
import { resolve as
|
|
6623
|
+
import { resolve as resolve10 } from "path";
|
|
6472
6624
|
import { maestroProvider as sdkMaestroProvider, setMcpResolver } from "maestro-agent-sdk";
|
|
6473
6625
|
function buildMaestroDisallowedTools(callerDisallowedTools = [], toolPolicy) {
|
|
6474
6626
|
return [
|
|
@@ -6602,13 +6754,13 @@ var init_maestro_provider = __esm(async () => {
|
|
|
6602
6754
|
"ToolSearch",
|
|
6603
6755
|
...DEFAULT_MAESTRO_DISALLOWED_TOOLS
|
|
6604
6756
|
];
|
|
6605
|
-
MAESTRO_TOOL_OUTPUT_DIR =
|
|
6757
|
+
MAESTRO_TOOL_OUTPUT_DIR = resolve10(RUN_DIR, "maestro-tool-outputs");
|
|
6606
6758
|
});
|
|
6607
6759
|
|
|
6608
6760
|
// ../../packages/core/src/agents/index.ts
|
|
6609
|
-
import { existsSync as
|
|
6761
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6610
6762
|
import { homedir as homedir8 } from "os";
|
|
6611
|
-
import { join as
|
|
6763
|
+
import { join as join15 } from "path";
|
|
6612
6764
|
async function* dispatchAgent(opts) {
|
|
6613
6765
|
switch (opts.agent) {
|
|
6614
6766
|
case "claude": {
|
|
@@ -6642,11 +6794,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
|
|
|
6642
6794
|
switch (agent) {
|
|
6643
6795
|
case "claude": {
|
|
6644
6796
|
const encodedCwd = encodeClaudeCwd(cwd);
|
|
6645
|
-
const path =
|
|
6646
|
-
return !
|
|
6797
|
+
const path = join15(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
|
|
6798
|
+
return !existsSync15(path);
|
|
6647
6799
|
}
|
|
6648
6800
|
case "codex": {
|
|
6649
|
-
const sessionsDir =
|
|
6801
|
+
const sessionsDir = join15(hostedCodexHomePath(), "sessions");
|
|
6650
6802
|
const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
|
|
6651
6803
|
for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
|
|
6652
6804
|
return false;
|
|
@@ -6655,7 +6807,7 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
|
|
|
6655
6807
|
}
|
|
6656
6808
|
case "maestro": {
|
|
6657
6809
|
const { hasActiveMaestroSession, maestroSessionPath: maestroSessionPath2 } = await import("maestro-agent-sdk");
|
|
6658
|
-
return !
|
|
6810
|
+
return !existsSync15(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
|
|
6659
6811
|
}
|
|
6660
6812
|
default: {
|
|
6661
6813
|
const _exhaustive = agent;
|
|
@@ -8874,7 +9026,7 @@ async function waitForRuntimeProcessLease(role, options = {}) {
|
|
|
8874
9026
|
const remaining = deadline - Date.now();
|
|
8875
9027
|
if (remaining <= 0)
|
|
8876
9028
|
return null;
|
|
8877
|
-
await new Promise((
|
|
9029
|
+
await new Promise((resolve11) => setTimeout(resolve11, Math.min(interval, remaining)));
|
|
8878
9030
|
}
|
|
8879
9031
|
}
|
|
8880
9032
|
async function waitForRequiredRuntimeProcessLease(role, options = {}) {
|
|
@@ -9151,8 +9303,8 @@ function createAskUserRuntime(host) {
|
|
|
9151
9303
|
return errorResult(`Error: failed to persist ask_user_question: ${error instanceof Error ? error.message : String(error)}`);
|
|
9152
9304
|
}
|
|
9153
9305
|
let resolveAnswer;
|
|
9154
|
-
const promise = new Promise((
|
|
9155
|
-
resolveAnswer =
|
|
9306
|
+
const promise = new Promise((resolve11) => {
|
|
9307
|
+
resolveAnswer = resolve11;
|
|
9156
9308
|
});
|
|
9157
9309
|
pendingAsks.set(message.id, {
|
|
9158
9310
|
topicId: ctx.topicId,
|
|
@@ -9808,11 +9960,11 @@ var init_manager_utils = () => {};
|
|
|
9808
9960
|
// ../../packages/core/src/platform/playwright/browser-processes.ts
|
|
9809
9961
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
9810
9962
|
import { readdirSync as readdirSync2, unlinkSync as unlinkSync10 } from "fs";
|
|
9811
|
-
import { createServer } from "net";
|
|
9812
|
-
import { resolve as
|
|
9963
|
+
import { createServer as createServer2 } from "net";
|
|
9964
|
+
import { resolve as resolve11, sep } from "path";
|
|
9813
9965
|
function isPortInUse(port) {
|
|
9814
9966
|
return new Promise((resolveProbe) => {
|
|
9815
|
-
const server =
|
|
9967
|
+
const server = createServer2();
|
|
9816
9968
|
let settled = false;
|
|
9817
9969
|
const finish = (occupied) => {
|
|
9818
9970
|
if (settled)
|
|
@@ -9842,8 +9994,8 @@ async function reserveAvailableLoopbackPort(minPort, maxPort, reservedPorts, pro
|
|
|
9842
9994
|
return null;
|
|
9843
9995
|
}
|
|
9844
9996
|
function killBrowserProcsForUserDataDir(userDataDir) {
|
|
9845
|
-
const target =
|
|
9846
|
-
const profileRoot =
|
|
9997
|
+
const target = resolve11(userDataDir);
|
|
9998
|
+
const profileRoot = resolve11(BROWSER_PROFILES_DIR);
|
|
9847
9999
|
if (target !== profileRoot && !target.startsWith(`${profileRoot}${sep}`))
|
|
9848
10000
|
return;
|
|
9849
10001
|
let pids;
|
|
@@ -9864,7 +10016,7 @@ function killBrowserProcsForUserDataDir(userDataDir) {
|
|
|
9864
10016
|
stdio: "pipe"
|
|
9865
10017
|
}).toString().trim();
|
|
9866
10018
|
const argDir = extractUserDataDirArg(cmdline);
|
|
9867
|
-
if (!argDir ||
|
|
10019
|
+
if (!argDir || resolve11(argDir) !== target)
|
|
9868
10020
|
continue;
|
|
9869
10021
|
killProcessTreeChildren(pidNum);
|
|
9870
10022
|
process.kill(pidNum, "SIGKILL");
|
|
@@ -9875,13 +10027,13 @@ function killBrowserProcsForUserDataDir(userDataDir) {
|
|
|
9875
10027
|
}
|
|
9876
10028
|
}
|
|
9877
10029
|
function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid) {
|
|
9878
|
-
const root =
|
|
9879
|
-
const live = new Set([...liveUserDataDirs].map((d) =>
|
|
10030
|
+
const root = resolve11(profileRoot);
|
|
10031
|
+
const live = new Set([...liveUserDataDirs].map((d) => resolve11(d)));
|
|
9880
10032
|
const out = [];
|
|
9881
10033
|
for (const { pid, userDataDir } of procs) {
|
|
9882
10034
|
if (pid === selfPid || !userDataDir)
|
|
9883
10035
|
continue;
|
|
9884
|
-
const dir =
|
|
10036
|
+
const dir = resolve11(userDataDir);
|
|
9885
10037
|
if (dir !== root && !dir.startsWith(`${root}${sep}`))
|
|
9886
10038
|
continue;
|
|
9887
10039
|
if (live.has(dir))
|
|
@@ -9897,7 +10049,7 @@ function reapOrphanBrowsers(liveUserDataDirs) {
|
|
|
9897
10049
|
const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
|
|
9898
10050
|
if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
|
|
9899
10051
|
return;
|
|
9900
|
-
const profileRoot =
|
|
10052
|
+
const profileRoot = resolve11(BROWSER_PROFILES_DIR);
|
|
9901
10053
|
let pids;
|
|
9902
10054
|
try {
|
|
9903
10055
|
pids = execFileSync6("pgrep", ["-f", "--", profileRoot], { stdio: "pipe" }).toString().trim();
|
|
@@ -9936,7 +10088,7 @@ function cleanSingletonFiles(userDataDir) {
|
|
|
9936
10088
|
for (const f of files) {
|
|
9937
10089
|
if (f.startsWith("Singleton")) {
|
|
9938
10090
|
try {
|
|
9939
|
-
unlinkSync10(
|
|
10091
|
+
unlinkSync10(resolve11(userDataDir, f));
|
|
9940
10092
|
logger.info({ file: f, userDataDir }, "Removed stale Singleton file");
|
|
9941
10093
|
} catch (e) {
|
|
9942
10094
|
logger.warn({ err: e, file: f }, "Failed to remove stale Chrome Singleton file");
|
|
@@ -9976,9 +10128,9 @@ var init_browser_processes = __esm(async () => {
|
|
|
9976
10128
|
|
|
9977
10129
|
// ../../packages/core/src/platform/playwright/headed-launch.ts
|
|
9978
10130
|
import { accessSync as accessSync2, constants as constants2 } from "fs";
|
|
9979
|
-
import { delimiter, isAbsolute as isAbsolute3, resolve as
|
|
10131
|
+
import { delimiter, isAbsolute as isAbsolute3, resolve as resolve12 } from "path";
|
|
9980
10132
|
function findExecutableOnPath(command, environment = process.env) {
|
|
9981
|
-
const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) =>
|
|
10133
|
+
const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve12(directory, command));
|
|
9982
10134
|
for (const candidate of candidates) {
|
|
9983
10135
|
try {
|
|
9984
10136
|
accessSync2(candidate, constants2.X_OK);
|
|
@@ -10099,11 +10251,11 @@ var init_vault_public = __esm(async () => {
|
|
|
10099
10251
|
});
|
|
10100
10252
|
|
|
10101
10253
|
// ../../packages/core/src/platform/playwright/vault-broker.ts
|
|
10102
|
-
import { randomBytes as
|
|
10254
|
+
import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
10103
10255
|
import { chmodSync as chmodSync4 } from "fs";
|
|
10104
|
-
import { createServer as
|
|
10105
|
-
import { tmpdir as
|
|
10106
|
-
import { join as
|
|
10256
|
+
import { createServer as createServer3 } from "net";
|
|
10257
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
10258
|
+
import { join as join16 } from "path";
|
|
10107
10259
|
function deepMapStrings2(value, transform) {
|
|
10108
10260
|
if (typeof value === "string")
|
|
10109
10261
|
return transform(value);
|
|
@@ -10189,8 +10341,8 @@ function authorized(actual, expected) {
|
|
|
10189
10341
|
return left.length === right.length && timingSafeEqual2(left, right);
|
|
10190
10342
|
}
|
|
10191
10343
|
async function createBrowserVaultBroker(userId) {
|
|
10192
|
-
const token =
|
|
10193
|
-
const socketPath =
|
|
10344
|
+
const token = randomBytes6(32).toString("hex");
|
|
10345
|
+
const socketPath = join16(process.platform === "win32" ? tmpdir3() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes6(8).toString("hex")}.sock`);
|
|
10194
10346
|
const retainedForms = new Map;
|
|
10195
10347
|
const leases = new Map;
|
|
10196
10348
|
const sockets = new Set;
|
|
@@ -10229,7 +10381,7 @@ async function createBrowserVaultBroker(userId) {
|
|
|
10229
10381
|
return result.text;
|
|
10230
10382
|
});
|
|
10231
10383
|
const prepared = prepareInput(request.tool, substituted);
|
|
10232
|
-
const lease =
|
|
10384
|
+
const lease = randomBytes6(24).toString("hex");
|
|
10233
10385
|
leases.set(lease, { boundary: prepared.boundary, createdAt: Date.now() });
|
|
10234
10386
|
return {
|
|
10235
10387
|
id,
|
|
@@ -10261,7 +10413,7 @@ async function createBrowserVaultBroker(userId) {
|
|
|
10261
10413
|
}
|
|
10262
10414
|
return { id, ok: false, error: "unknown operation" };
|
|
10263
10415
|
};
|
|
10264
|
-
const server =
|
|
10416
|
+
const server = createServer3((socket) => {
|
|
10265
10417
|
sockets.add(socket);
|
|
10266
10418
|
let pending = "";
|
|
10267
10419
|
socket.setEncoding("utf8");
|
|
@@ -10446,24 +10598,24 @@ var init_browser_profiles = __esm(async () => {
|
|
|
10446
10598
|
|
|
10447
10599
|
// ../../packages/core/src/platform/playwright/manager.ts
|
|
10448
10600
|
import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
|
|
10449
|
-
import { createHash as createHash4, randomBytes as
|
|
10601
|
+
import { createHash as createHash4, randomBytes as randomBytes7, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
10450
10602
|
import {
|
|
10451
10603
|
cpSync,
|
|
10452
|
-
existsSync as
|
|
10604
|
+
existsSync as existsSync16,
|
|
10453
10605
|
mkdirSync as mkdirSync10,
|
|
10454
10606
|
readFileSync as readFileSync12,
|
|
10455
10607
|
rmSync as rmSync2,
|
|
10456
10608
|
unlinkSync as unlinkSync11,
|
|
10457
10609
|
writeFileSync as writeFileSync8
|
|
10458
10610
|
} from "fs";
|
|
10459
|
-
import { dirname as
|
|
10611
|
+
import { dirname as dirname11, join as join17, resolve as resolve13 } from "path";
|
|
10460
10612
|
function removeDefaultProfileDataDir(userDataDir) {
|
|
10461
|
-
const root =
|
|
10462
|
-
const target =
|
|
10463
|
-
if (
|
|
10613
|
+
const root = resolve13(BROWSER_PROFILES_DIR);
|
|
10614
|
+
const target = resolve13(userDataDir);
|
|
10615
|
+
if (dirname11(target) !== root) {
|
|
10464
10616
|
throw new Error(`Refusing to delete browser profile outside managed root: ${target}`);
|
|
10465
10617
|
}
|
|
10466
|
-
const existed =
|
|
10618
|
+
const existed = existsSync16(target);
|
|
10467
10619
|
rmSync2(target, { recursive: true, force: true });
|
|
10468
10620
|
return existed;
|
|
10469
10621
|
}
|
|
@@ -10546,14 +10698,14 @@ function portFileName(instanceKey) {
|
|
|
10546
10698
|
function writePortFile(instanceKey, port) {
|
|
10547
10699
|
try {
|
|
10548
10700
|
mkdirSync10(managerHost.portsDir, { recursive: true });
|
|
10549
|
-
writeFileSync8(
|
|
10701
|
+
writeFileSync8(join17(managerHost.portsDir, portFileName(instanceKey)), String(port));
|
|
10550
10702
|
} catch (e) {
|
|
10551
10703
|
logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
|
|
10552
10704
|
}
|
|
10553
10705
|
}
|
|
10554
10706
|
function deletePortFile(instanceKey) {
|
|
10555
10707
|
try {
|
|
10556
|
-
unlinkSync11(
|
|
10708
|
+
unlinkSync11(join17(managerHost.portsDir, portFileName(instanceKey)));
|
|
10557
10709
|
} catch (e) {
|
|
10558
10710
|
if (e.code === "ENOENT")
|
|
10559
10711
|
return;
|
|
@@ -10700,7 +10852,7 @@ function releasePort(port) {
|
|
|
10700
10852
|
usedPorts.delete(port);
|
|
10701
10853
|
}
|
|
10702
10854
|
function defaultProfileDir(_ownerId, profile) {
|
|
10703
|
-
return
|
|
10855
|
+
return resolve13(BROWSER_PROFILES_DIR, profile);
|
|
10704
10856
|
}
|
|
10705
10857
|
function resolveUserDataDir(instanceKey) {
|
|
10706
10858
|
return managerHost.resolveInstanceDataDir(instanceKey);
|
|
@@ -10792,8 +10944,8 @@ async function spawnPlaywright(instanceKey, ownerId, reservedPort, browserBin =
|
|
|
10792
10944
|
releasePort(port);
|
|
10793
10945
|
throw new Error("Browser.rs managed mode does not support authenticated egress proxies");
|
|
10794
10946
|
}
|
|
10795
|
-
const capability =
|
|
10796
|
-
const spawnNonce =
|
|
10947
|
+
const capability = randomBytes7(32).toString("hex");
|
|
10948
|
+
const spawnNonce = randomBytes7(32).toString("hex");
|
|
10797
10949
|
let vaultBroker;
|
|
10798
10950
|
try {
|
|
10799
10951
|
vaultBroker = await createBrowserVaultBroker(ownerId);
|
|
@@ -11061,14 +11213,14 @@ async function cloneProfileForChild(opts) {
|
|
|
11061
11213
|
killInstance(dstKey);
|
|
11062
11214
|
await delay(500);
|
|
11063
11215
|
}
|
|
11064
|
-
if (!
|
|
11216
|
+
if (!existsSync16(srcDir)) {
|
|
11065
11217
|
return { copied: false, srcDir, dstDir, reason: "src-missing" };
|
|
11066
11218
|
}
|
|
11067
11219
|
try {
|
|
11068
|
-
if (
|
|
11220
|
+
if (existsSync16(dstDir)) {
|
|
11069
11221
|
rmSync2(dstDir, { recursive: true, force: true });
|
|
11070
11222
|
}
|
|
11071
|
-
mkdirSync10(
|
|
11223
|
+
mkdirSync10(dirname11(dstDir), { recursive: true });
|
|
11072
11224
|
if (process.platform === "darwin") {
|
|
11073
11225
|
try {
|
|
11074
11226
|
execFileSync7("cp", ["-cR", srcDir, dstDir], { stdio: "pipe" });
|
|
@@ -11086,7 +11238,7 @@ async function cloneProfileForChild(opts) {
|
|
|
11086
11238
|
cleanSingletonFiles(dstDir);
|
|
11087
11239
|
for (const f of ["DevToolsActivePort", "LOCK"]) {
|
|
11088
11240
|
try {
|
|
11089
|
-
unlinkSync11(
|
|
11241
|
+
unlinkSync11(resolve13(dstDir, f));
|
|
11090
11242
|
} catch {}
|
|
11091
11243
|
}
|
|
11092
11244
|
logger.info({ srcKey, dstKey, srcDir, dstDir }, "Cloned Playwright profile for child topic");
|
|
@@ -11589,9 +11741,9 @@ var init_personal_general = __esm(async () => {
|
|
|
11589
11741
|
|
|
11590
11742
|
// ../../packages/core/src/prompts/builders.ts
|
|
11591
11743
|
import { readFileSync as readFileSync13 } from "fs";
|
|
11592
|
-
import { resolve as
|
|
11744
|
+
import { resolve as resolve14 } from "path";
|
|
11593
11745
|
function loadPrompt(filename, dir = SESSIONS_DIR) {
|
|
11594
|
-
const raw = readFileSync13(
|
|
11746
|
+
const raw = readFileSync13(resolve14(dir, filename), "utf-8");
|
|
11595
11747
|
return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
|
|
11596
11748
|
}
|
|
11597
11749
|
function replaceVars(template, vars) {
|
|
@@ -11640,7 +11792,7 @@ function visualDesignGuide() {
|
|
|
11640
11792
|
return _visualDesignGuide;
|
|
11641
11793
|
}
|
|
11642
11794
|
function loadAgentPrompt(filename) {
|
|
11643
|
-
const raw = readFileSync13(
|
|
11795
|
+
const raw = readFileSync13(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
|
|
11644
11796
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
11645
11797
|
if (!match)
|
|
11646
11798
|
throw new Error(`Agent prompt ${filename} is missing frontmatter`);
|
|
@@ -11955,8 +12107,8 @@ var init_builders = __esm(() => {
|
|
|
11955
12107
|
init_model_catalog();
|
|
11956
12108
|
init_config();
|
|
11957
12109
|
init_logger();
|
|
11958
|
-
PROMPTS_DIR =
|
|
11959
|
-
SESSIONS_DIR =
|
|
12110
|
+
PROMPTS_DIR = resolve14(PROJECT_ROOT, "src/prompts");
|
|
12111
|
+
SESSIONS_DIR = resolve14(PROMPTS_DIR, "sessions");
|
|
11960
12112
|
defaultPromptBuilders = createPromptBuilders();
|
|
11961
12113
|
buildTopicSystemPrompt = defaultPromptBuilders.buildTopicSystemPrompt;
|
|
11962
12114
|
buildChannelSystemPrompt = defaultPromptBuilders.buildChannelSystemPrompt;
|
|
@@ -12098,50 +12250,50 @@ __export(exports_wiki, {
|
|
|
12098
12250
|
getTopicMemoryFilePaths: () => getTopicMemoryFilePaths,
|
|
12099
12251
|
getSharedWikiDir: () => getSharedWikiDir
|
|
12100
12252
|
});
|
|
12101
|
-
import { existsSync as
|
|
12102
|
-
import { basename as basename2, dirname as
|
|
12253
|
+
import { existsSync as existsSync17, readdirSync as readdirSync3, statSync as statSync6 } from "fs";
|
|
12254
|
+
import { basename as basename2, dirname as dirname12, join as join18 } from "path";
|
|
12103
12255
|
function getWikiDir(_userId, workspaceDir = resolveStorageWorkspaceDir()) {
|
|
12104
|
-
return
|
|
12256
|
+
return join18(workspaceDir, "wiki");
|
|
12105
12257
|
}
|
|
12106
12258
|
function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
|
|
12107
|
-
return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() :
|
|
12259
|
+
return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join18(workspaceDir, "wiki");
|
|
12108
12260
|
}
|
|
12109
12261
|
function findLatestSummaryFile(wikiDir, safeTopic) {
|
|
12110
|
-
const summariesDir =
|
|
12111
|
-
if (!
|
|
12262
|
+
const summariesDir = join18(wikiDir, "summaries");
|
|
12263
|
+
if (!existsSync17(summariesDir))
|
|
12112
12264
|
return null;
|
|
12113
12265
|
const files = readdirSync3(summariesDir).filter((f) => f.endsWith(".md") && f.match(new RegExp(`^\\d{4}-\\d{2}-\\d{2}-${safeTopic}(\\.md|~\\d+\\.md)$`)) && !f.endsWith("-sent-files.md")).sort((left, right) => {
|
|
12114
|
-
const mtimeDelta = statSync6(
|
|
12266
|
+
const mtimeDelta = statSync6(join18(summariesDir, right)).mtimeMs - statSync6(join18(summariesDir, left)).mtimeMs;
|
|
12115
12267
|
return mtimeDelta || right.localeCompare(left, undefined, { numeric: true });
|
|
12116
12268
|
});
|
|
12117
|
-
return files.length > 0 ?
|
|
12269
|
+
return files.length > 0 ? join18(summariesDir, files[0]) : null;
|
|
12118
12270
|
}
|
|
12119
12271
|
function getTopicMemoryFilePaths(_userId, topicName, forkOrigin, workspaceDir = resolveStorageWorkspaceDir()) {
|
|
12120
12272
|
const wikiDir = getSharedWikiDir(workspaceDir);
|
|
12121
12273
|
const resolveBrief = (name) => {
|
|
12122
12274
|
const safe = sanitizeTopicName(name, true);
|
|
12123
|
-
const brief =
|
|
12275
|
+
const brief = join18(wikiDir, "topic", `${safe}.md`);
|
|
12124
12276
|
const latestSummary = findLatestSummaryFile(wikiDir, safe);
|
|
12125
12277
|
return { brief, latestSummary };
|
|
12126
12278
|
};
|
|
12127
|
-
const archiveDir =
|
|
12279
|
+
const archiveDir = join18(wikiDir, "archive");
|
|
12128
12280
|
const archiveExistsFor = (name) => {
|
|
12129
12281
|
const safe = sanitizeTopicName(name);
|
|
12130
|
-
return
|
|
12282
|
+
return existsSync17(archiveDir) && readdirSync3(archiveDir).some((f) => f.endsWith(".jsonl") && f.startsWith(`${safe}_`));
|
|
12131
12283
|
};
|
|
12132
12284
|
const sourceTopic = forkOrigin ?? topicName;
|
|
12133
12285
|
const hasArchive = archiveExistsFor(sourceTopic) || (forkOrigin ? archiveExistsFor(topicName) : false);
|
|
12134
12286
|
const target = resolveBrief(sourceTopic);
|
|
12135
|
-
if (
|
|
12287
|
+
if (existsSync17(target.brief)) {
|
|
12136
12288
|
return {
|
|
12137
|
-
memoryDir:
|
|
12289
|
+
memoryDir: dirname12(target.brief),
|
|
12138
12290
|
memoryFiles: [basename2(target.brief)],
|
|
12139
12291
|
...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
|
|
12140
12292
|
...hasArchive ? { hasArchive: true } : {}
|
|
12141
12293
|
};
|
|
12142
12294
|
}
|
|
12143
12295
|
return {
|
|
12144
|
-
memoryDir:
|
|
12296
|
+
memoryDir: join18(wikiDir, "topic"),
|
|
12145
12297
|
memoryFiles: [],
|
|
12146
12298
|
...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
|
|
12147
12299
|
...hasArchive ? { hasArchive: true } : {}
|
|
@@ -12153,8 +12305,8 @@ var init_wiki = __esm(async () => {
|
|
|
12153
12305
|
|
|
12154
12306
|
// ../../packages/core/src/agents/archiver.ts
|
|
12155
12307
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
12156
|
-
import { existsSync as
|
|
12157
|
-
import { join as
|
|
12308
|
+
import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
|
|
12309
|
+
import { join as join19 } from "path";
|
|
12158
12310
|
function resolveMemoryLanguage() {
|
|
12159
12311
|
const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
|
|
12160
12312
|
return override && override.length > 0 ? override : resolveOutputLanguage();
|
|
@@ -12405,10 +12557,10 @@ function distillOneLine(summaryMd) {
|
|
|
12405
12557
|
return "";
|
|
12406
12558
|
}
|
|
12407
12559
|
function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
|
|
12408
|
-
const dir =
|
|
12560
|
+
const dir = join19(storage.getWikiDir(), "summaries");
|
|
12409
12561
|
if (!storage.fileExists(dir))
|
|
12410
12562
|
return null;
|
|
12411
|
-
const predicted =
|
|
12563
|
+
const predicted = join19(dir, wikiSummaryFilename(date, topicTitle, topicId));
|
|
12412
12564
|
if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
|
|
12413
12565
|
return predicted;
|
|
12414
12566
|
let best = null;
|
|
@@ -12416,7 +12568,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
|
|
|
12416
12568
|
if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
|
|
12417
12569
|
continue;
|
|
12418
12570
|
}
|
|
12419
|
-
const p =
|
|
12571
|
+
const p = join19(dir, f);
|
|
12420
12572
|
try {
|
|
12421
12573
|
const m = storage.fileModifiedAt(p);
|
|
12422
12574
|
if (m >= sinceMs && (!best || m > best.mtime))
|
|
@@ -12500,7 +12652,7 @@ var init_archiver = __esm(async () => {
|
|
|
12500
12652
|
defaultArchiverRuntime = createArchiverRuntime({
|
|
12501
12653
|
storage: {
|
|
12502
12654
|
getWikiDir: getSharedWikiDir,
|
|
12503
|
-
fileExists:
|
|
12655
|
+
fileExists: existsSync18,
|
|
12504
12656
|
listDirectory: readdirSync4,
|
|
12505
12657
|
readTextFile: (path) => readFileSync14(path, "utf-8"),
|
|
12506
12658
|
fileSize: (path) => statSync7(path).size,
|
|
@@ -12632,13 +12784,13 @@ __export(exports_topic_archive, {
|
|
|
12632
12784
|
archiveConversationEvents: () => archiveConversationEvents
|
|
12633
12785
|
});
|
|
12634
12786
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
12635
|
-
import { join as
|
|
12787
|
+
import { join as join20 } from "path";
|
|
12636
12788
|
function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
12637
12789
|
const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
|
|
12638
12790
|
if (rows.length === 0)
|
|
12639
12791
|
return null;
|
|
12640
12792
|
const safeTopic = sanitizeTopicName(topicTitle, true);
|
|
12641
|
-
const archiveDir =
|
|
12793
|
+
const archiveDir = join20(getSharedWikiDir(), "archive");
|
|
12642
12794
|
mkdirSync11(archiveDir, { recursive: true });
|
|
12643
12795
|
const date = new Date().toISOString().slice(0, 10);
|
|
12644
12796
|
const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
|
|
@@ -12651,7 +12803,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
|
12651
12803
|
let path;
|
|
12652
12804
|
while (true) {
|
|
12653
12805
|
filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
|
|
12654
|
-
path =
|
|
12806
|
+
path = join20(archiveDir, filename);
|
|
12655
12807
|
try {
|
|
12656
12808
|
writeFileSync9(path, body, { flag: "wx" });
|
|
12657
12809
|
break;
|
|
@@ -12696,7 +12848,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
|
|
|
12696
12848
|
const entries = readRawConversation(userId, topicTitle);
|
|
12697
12849
|
if (entries.length === 0)
|
|
12698
12850
|
return null;
|
|
12699
|
-
const archiveDir =
|
|
12851
|
+
const archiveDir = join20(getSharedWikiDir(), "archive");
|
|
12700
12852
|
mkdirSync11(archiveDir, { recursive: true });
|
|
12701
12853
|
const safeTopic = sanitizeTopicName(topicTitle, true);
|
|
12702
12854
|
const date = new Date().toISOString().slice(0, 10);
|
|
@@ -12724,7 +12876,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
|
|
|
12724
12876
|
let counter = 1;
|
|
12725
12877
|
while (true) {
|
|
12726
12878
|
const suffix = counter === 1 ? "" : `_${counter}`;
|
|
12727
|
-
const path =
|
|
12879
|
+
const path = join20(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
|
|
12728
12880
|
try {
|
|
12729
12881
|
writeFileSync9(path, body, { flag: "wx" });
|
|
12730
12882
|
logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
|
|
@@ -13051,7 +13203,7 @@ var init_idle_archiver = __esm(async () => {
|
|
|
13051
13203
|
|
|
13052
13204
|
// ../../packages/core/src/agents/topic-cleanup.ts
|
|
13053
13205
|
import { mkdirSync as mkdirSync12, renameSync as renameSync6, unlinkSync as unlinkSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
13054
|
-
import { dirname as
|
|
13206
|
+
import { dirname as dirname13 } from "path";
|
|
13055
13207
|
function collectSessionIdsByAgent(entries, extraSessions = []) {
|
|
13056
13208
|
const out = new Map;
|
|
13057
13209
|
for (const e of entries) {
|
|
@@ -13117,7 +13269,7 @@ function createTopicLogMaintenance(host) {
|
|
|
13117
13269
|
const path = runtimeHost.activeConversationPath(opts.userId, opts.topicName);
|
|
13118
13270
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
13119
13271
|
try {
|
|
13120
|
-
mkdirSync12(
|
|
13272
|
+
mkdirSync12(dirname13(path), { recursive: true });
|
|
13121
13273
|
writeFileSync10(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
|
|
13122
13274
|
`)}
|
|
13123
13275
|
` : "", { flag: "wx" });
|
|
@@ -13734,15 +13886,15 @@ var init_runtime_turn_requests = __esm(async () => {
|
|
|
13734
13886
|
// ../../packages/core/src/topics/session.ts
|
|
13735
13887
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
13736
13888
|
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
|
|
13737
|
-
import { tmpdir as
|
|
13738
|
-
import { join as
|
|
13889
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
13890
|
+
import { join as join21 } from "path";
|
|
13739
13891
|
async function waitForMemoryArchive(settled, timeoutMs) {
|
|
13740
13892
|
let timer;
|
|
13741
13893
|
try {
|
|
13742
13894
|
return await Promise.race([
|
|
13743
13895
|
settled.then(() => true),
|
|
13744
|
-
new Promise((
|
|
13745
|
-
timer = setTimeout(() =>
|
|
13896
|
+
new Promise((resolve15) => {
|
|
13897
|
+
timer = setTimeout(() => resolve15(false), timeoutMs);
|
|
13746
13898
|
timer.unref?.();
|
|
13747
13899
|
})
|
|
13748
13900
|
]);
|
|
@@ -13806,8 +13958,8 @@ async function restartTopicSession(topicId, userId, reason = "topic-session-rest
|
|
|
13806
13958
|
};
|
|
13807
13959
|
}
|
|
13808
13960
|
let settleMemoryArchive;
|
|
13809
|
-
const memoryArchiveSettled = new Promise((
|
|
13810
|
-
settleMemoryArchive =
|
|
13961
|
+
const memoryArchiveSettled = new Promise((resolve15) => {
|
|
13962
|
+
settleMemoryArchive = resolve15;
|
|
13811
13963
|
});
|
|
13812
13964
|
const archiveStatus = (options.archiveMemory ?? archiveActiveTopicForMemory)(topicId, userId, {
|
|
13813
13965
|
reason: "reset",
|
|
@@ -13988,7 +14140,7 @@ function formatCompactElapsed(startedAt) {
|
|
|
13988
14140
|
async function summarizeTopicContext(request) {
|
|
13989
14141
|
const startedAt = Date.now();
|
|
13990
14142
|
const sessionIds = [];
|
|
13991
|
-
const compactCwd = mkdtempSync2(
|
|
14143
|
+
const compactCwd = mkdtempSync2(join21(tmpdir4(), "negotium-compact-"));
|
|
13992
14144
|
const abortController = new AbortController;
|
|
13993
14145
|
const relayAbort = () => abortController.abort(request.signal?.reason);
|
|
13994
14146
|
if (request.signal?.aborted)
|
|
@@ -14015,7 +14167,7 @@ async function summarizeTopicContext(request) {
|
|
|
14015
14167
|
let error = "";
|
|
14016
14168
|
let toolViolation = false;
|
|
14017
14169
|
let compactionLogCalls = 0;
|
|
14018
|
-
const compactionLogPath =
|
|
14170
|
+
const compactionLogPath = join21(compactCwd, "conversation.log");
|
|
14019
14171
|
try {
|
|
14020
14172
|
const compactionMcp = useCompactionLog ? {
|
|
14021
14173
|
compact_log: {
|
|
@@ -15336,14 +15488,14 @@ var init_self_config = __esm(async () => {
|
|
|
15336
15488
|
});
|
|
15337
15489
|
|
|
15338
15490
|
// ../../packages/core/src/query/session-inbox-path.ts
|
|
15339
|
-
import { join as
|
|
15491
|
+
import { join as join22 } from "path";
|
|
15340
15492
|
function sessionInboxPath(userId, topicId) {
|
|
15341
15493
|
const key = Buffer.from(topicId, "utf8").toString("base64url");
|
|
15342
|
-
return
|
|
15494
|
+
return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
|
|
15343
15495
|
}
|
|
15344
15496
|
function scheduledSessionInboxPath(userId, topicId) {
|
|
15345
15497
|
const key = Buffer.from(topicId, "utf8").toString("base64url");
|
|
15346
|
-
return
|
|
15498
|
+
return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
|
|
15347
15499
|
}
|
|
15348
15500
|
function decodeTopicIdFileName(fileName, suffix) {
|
|
15349
15501
|
if (!fileName.startsWith(TOPIC_ID_FILE_PREFIX) || !fileName.endsWith(suffix))
|
|
@@ -15371,13 +15523,13 @@ var init_session_inbox_path = __esm(() => {
|
|
|
15371
15523
|
|
|
15372
15524
|
// ../../packages/core/src/query/session-inbox-cleanup.ts
|
|
15373
15525
|
import { unlinkSync as unlinkSync14 } from "fs";
|
|
15374
|
-
import { basename as basename3, join as
|
|
15526
|
+
import { basename as basename3, join as join23 } from "path";
|
|
15375
15527
|
function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
|
|
15376
15528
|
const live = sessionInboxPath(userId, topicId);
|
|
15377
15529
|
const scheduled = scheduledSessionInboxPath(userId, topicId);
|
|
15378
15530
|
const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
|
|
15379
15531
|
if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename3(legacyTopicTitle) === legacyTopicTitle) {
|
|
15380
|
-
const legacyBase =
|
|
15532
|
+
const legacyBase = join23(SESSION_INBOX_DIR, userId, legacyTopicTitle);
|
|
15381
15533
|
for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
|
|
15382
15534
|
candidates.add(`${legacyBase}${suffix}`);
|
|
15383
15535
|
}
|
|
@@ -15403,16 +15555,16 @@ var init_session_inbox_cleanup = __esm(() => {
|
|
|
15403
15555
|
|
|
15404
15556
|
// ../../packages/core/src/query/state.ts
|
|
15405
15557
|
import { mkdirSync as mkdirSync14, renameSync as renameSync7, unlinkSync as unlinkSync15, writeFileSync as writeFileSync12 } from "fs";
|
|
15406
|
-
import { basename as basename4, join as
|
|
15558
|
+
import { basename as basename4, join as join24 } from "path";
|
|
15407
15559
|
function createQueryStateStore(options) {
|
|
15408
15560
|
const sanitize = options.sanitizeTopicId ?? sanitizeId;
|
|
15409
|
-
const queryStateDirPath = (userId) =>
|
|
15410
|
-
const queryStateFile = (userId, topicId) =>
|
|
15561
|
+
const queryStateDirPath = (userId) => join24(options.usersLogDir, String(userId), "active-queries");
|
|
15562
|
+
const queryStateFile = (userId, topicId) => join24(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
|
|
15411
15563
|
const legacyQueryStateFile = (userId, topicName) => {
|
|
15412
15564
|
if (!topicName || topicName === "." || topicName === ".." || basename4(topicName) !== topicName) {
|
|
15413
15565
|
return null;
|
|
15414
15566
|
}
|
|
15415
|
-
return
|
|
15567
|
+
return join24(queryStateDirPath(userId), `${topicName}.json`);
|
|
15416
15568
|
};
|
|
15417
15569
|
return {
|
|
15418
15570
|
write(userId, topicId, topicName, task) {
|
|
@@ -15573,30 +15725,30 @@ import {
|
|
|
15573
15725
|
unlinkSync as unlinkSync16,
|
|
15574
15726
|
writeFileSync as writeFileSync13
|
|
15575
15727
|
} from "fs";
|
|
15576
|
-
import { dirname as
|
|
15728
|
+
import { dirname as dirname14, join as join25 } from "path";
|
|
15577
15729
|
function pendingAskDir(userId) {
|
|
15578
15730
|
const rawUserId = String(userId);
|
|
15579
15731
|
const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash6("sha256").update(rawUserId).digest("hex")}`;
|
|
15580
|
-
return
|
|
15732
|
+
return join25(resolveStorageSessionAsksDir(), safeUserId);
|
|
15581
15733
|
}
|
|
15582
15734
|
function encodeAskKey(key) {
|
|
15583
15735
|
return JSON.stringify([key.from, key.to]);
|
|
15584
15736
|
}
|
|
15585
15737
|
function pendingAskPath(key) {
|
|
15586
15738
|
const digest = createHash6("sha256").update(encodeAskKey(key)).digest("hex");
|
|
15587
|
-
return
|
|
15739
|
+
return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
|
|
15588
15740
|
}
|
|
15589
15741
|
function v2PendingAskPath(key) {
|
|
15590
15742
|
const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
|
|
15591
|
-
return
|
|
15743
|
+
return join25(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
|
|
15592
15744
|
}
|
|
15593
15745
|
function legacyPendingAskPath(key) {
|
|
15594
15746
|
if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
|
|
15595
15747
|
return null;
|
|
15596
15748
|
}
|
|
15597
15749
|
const dir = pendingAskDir(key.userId);
|
|
15598
|
-
const candidate =
|
|
15599
|
-
return
|
|
15750
|
+
const candidate = join25(dir, `${key.from}___${key.to}.pending`);
|
|
15751
|
+
return dirname14(candidate) === dir ? candidate : null;
|
|
15600
15752
|
}
|
|
15601
15753
|
function parsePendingAskFilename(fileName) {
|
|
15602
15754
|
if (!fileName.endsWith(".pending"))
|
|
@@ -15836,7 +15988,7 @@ function listPendingAsksForCaller(args) {
|
|
|
15836
15988
|
const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
|
|
15837
15989
|
if (!parsed)
|
|
15838
15990
|
continue;
|
|
15839
|
-
const path =
|
|
15991
|
+
const path = join25(dir, fileName);
|
|
15840
15992
|
const record = readPendingAskFile(path, {
|
|
15841
15993
|
userId: args.userId,
|
|
15842
15994
|
from: parsed.from,
|
|
@@ -15878,7 +16030,7 @@ function deletePendingAsksForTopic(args) {
|
|
|
15878
16030
|
}
|
|
15879
16031
|
let deleted = 0;
|
|
15880
16032
|
for (const fileName of files) {
|
|
15881
|
-
const path =
|
|
16033
|
+
const path = join25(dir, fileName);
|
|
15882
16034
|
const parsed = parsePendingAskFilename(fileName);
|
|
15883
16035
|
const record = readPendingAskFile(path, {
|
|
15884
16036
|
userId: args.userId,
|
|
@@ -16296,7 +16448,7 @@ __export(exports_token_stats, {
|
|
|
16296
16448
|
});
|
|
16297
16449
|
import { createHash as createHash7 } from "crypto";
|
|
16298
16450
|
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
16299
|
-
import { join as
|
|
16451
|
+
import { join as join26 } from "path";
|
|
16300
16452
|
function emptyBucket() {
|
|
16301
16453
|
return {
|
|
16302
16454
|
inputTokens: 0,
|
|
@@ -16315,7 +16467,7 @@ function queriesPath(userId) {
|
|
|
16315
16467
|
const fileId = tokenStatsFileId(userId);
|
|
16316
16468
|
const logDir = resolveStorageLogDir();
|
|
16317
16469
|
mkdirSync16(logDir, { recursive: true });
|
|
16318
|
-
return
|
|
16470
|
+
return join26(logDir, `token-queries-${fileId}.jsonl`);
|
|
16319
16471
|
}
|
|
16320
16472
|
function loadRecords(userId) {
|
|
16321
16473
|
try {
|
|
@@ -16744,7 +16896,7 @@ var init_lifecycle = __esm(async () => {
|
|
|
16744
16896
|
// ../../packages/core/src/runtime/attachments.ts
|
|
16745
16897
|
import { randomUUID as randomUUID14 } from "crypto";
|
|
16746
16898
|
import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
|
|
16747
|
-
import { basename as basename5, join as
|
|
16899
|
+
import { basename as basename5, join as join27 } from "path";
|
|
16748
16900
|
function workspaceCwdFor(topicId) {
|
|
16749
16901
|
return resolveTopicWorkspaceDir(topicId);
|
|
16750
16902
|
}
|
|
@@ -16757,7 +16909,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
|
|
|
16757
16909
|
if (!attachmentIds?.length)
|
|
16758
16910
|
return [];
|
|
16759
16911
|
const out = [];
|
|
16760
|
-
const destDir =
|
|
16912
|
+
const destDir = join27(workspaceCwdFor(topicId), "attachments", queryId);
|
|
16761
16913
|
for (const rawId of attachmentIds) {
|
|
16762
16914
|
if (typeof rawId !== "string")
|
|
16763
16915
|
continue;
|
|
@@ -16774,7 +16926,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
|
|
|
16774
16926
|
mkdirSync17(destDir, { recursive: true });
|
|
16775
16927
|
const index = String(out.length + 1).padStart(2, "0");
|
|
16776
16928
|
const safeName = safeAttachmentFilename(attachment.filename, fileId);
|
|
16777
|
-
const destPath =
|
|
16929
|
+
const destPath = join27(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
|
|
16778
16930
|
copyFileSync2(sourcePath, destPath);
|
|
16779
16931
|
out.push({
|
|
16780
16932
|
id: attachment.id,
|
|
@@ -16804,10 +16956,10 @@ function promptWithAttachments(prompt, attachments) {
|
|
|
16804
16956
|
return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
|
|
16805
16957
|
}
|
|
16806
16958
|
function ingestAttachment(args) {
|
|
16807
|
-
const destDir =
|
|
16959
|
+
const destDir = join27(UPLOADS_DIR, args.topicId);
|
|
16808
16960
|
mkdirSync17(destDir, { recursive: true });
|
|
16809
16961
|
const safeName = safeAttachmentFilename(args.filename, "upload");
|
|
16810
|
-
const destPath =
|
|
16962
|
+
const destPath = join27(destDir, `${Date.now()}-${randomUUID14().slice(0, 8)}-${safeName}`);
|
|
16811
16963
|
if (args.sourcePath !== undefined) {
|
|
16812
16964
|
copyFileSync2(args.sourcePath, destPath);
|
|
16813
16965
|
} else if (args.bytes !== undefined) {
|
|
@@ -16982,12 +17134,12 @@ var init_errors = __esm(() => {
|
|
|
16982
17134
|
|
|
16983
17135
|
// ../../packages/core/src/runtime/event-heartbeat.ts
|
|
16984
17136
|
function nextOrHeartbeat(pending, intervalMs) {
|
|
16985
|
-
return new Promise((
|
|
16986
|
-
const timer = setTimeout(() =>
|
|
17137
|
+
return new Promise((resolve15, reject) => {
|
|
17138
|
+
const timer = setTimeout(() => resolve15({ kind: "heartbeat" }), intervalMs);
|
|
16987
17139
|
timer.unref?.();
|
|
16988
17140
|
pending.then((result) => {
|
|
16989
17141
|
clearTimeout(timer);
|
|
16990
|
-
|
|
17142
|
+
resolve15({ kind: "event", result });
|
|
16991
17143
|
}, (error) => {
|
|
16992
17144
|
clearTimeout(timer);
|
|
16993
17145
|
reject(error);
|
|
@@ -17324,7 +17476,7 @@ var init_visual_html = __esm(() => {
|
|
|
17324
17476
|
|
|
17325
17477
|
// ../../packages/core/src/runtime/visuals.ts
|
|
17326
17478
|
import { realpathSync as realpathSync4 } from "fs";
|
|
17327
|
-
import { isAbsolute as isAbsolute4, resolve as
|
|
17479
|
+
import { isAbsolute as isAbsolute4, resolve as resolve15 } from "path";
|
|
17328
17480
|
function activeVisualHtmlForPrompt(html) {
|
|
17329
17481
|
if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
|
|
17330
17482
|
return { html, omittedChars: 0 };
|
|
@@ -17375,8 +17527,8 @@ function topicAllowsVisualFileId(topicId, fileId) {
|
|
|
17375
17527
|
return topicHasAttachmentFileId(topicId, fileId) || topicHasVisualFileId(topicId, fileId);
|
|
17376
17528
|
}
|
|
17377
17529
|
function isPathInside(baseDir, filePath) {
|
|
17378
|
-
const base =
|
|
17379
|
-
const normalized =
|
|
17530
|
+
const base = resolve15(baseDir);
|
|
17531
|
+
const normalized = resolve15(filePath);
|
|
17380
17532
|
try {
|
|
17381
17533
|
const realBase = realpathSync4(base);
|
|
17382
17534
|
const real = realpathSync4(normalized);
|
|
@@ -17438,7 +17590,7 @@ function resolveVisualMediaInput(topicId, input) {
|
|
|
17438
17590
|
}
|
|
17439
17591
|
const rawPath = input.file_path.trim();
|
|
17440
17592
|
const cwd = workspaceCwdFor(topicId);
|
|
17441
|
-
const candidate = isAbsolute4(rawPath) ? rawPath :
|
|
17593
|
+
const candidate = isAbsolute4(rawPath) ? rawPath : resolve15(cwd, rawPath);
|
|
17442
17594
|
if (!isPathInside(cwd, candidate)) {
|
|
17443
17595
|
return { error: "file_path must be inside the topic workspace" };
|
|
17444
17596
|
}
|
|
@@ -17476,7 +17628,7 @@ var init_visuals = __esm(async () => {
|
|
|
17476
17628
|
// ../../packages/core/src/runtime/turn-event-stream.ts
|
|
17477
17629
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
17478
17630
|
import { realpathSync as realpathSync5, statSync as statSync9 } from "fs";
|
|
17479
|
-
import { isAbsolute as isAbsolute5, resolve as
|
|
17631
|
+
import { isAbsolute as isAbsolute5, resolve as resolve16 } from "path";
|
|
17480
17632
|
function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
|
|
17481
17633
|
if (getRoomQuery(topicId)?.queryId !== queryId)
|
|
17482
17634
|
return false;
|
|
@@ -17829,7 +17981,7 @@ ${JSON.stringify(event.input ?? {})}`);
|
|
|
17829
17981
|
case "file":
|
|
17830
17982
|
if (!silent && peerBridge) {
|
|
17831
17983
|
const cwd = workspaceCwdFor(topicId);
|
|
17832
|
-
const path = isAbsolute5(event.path) ? event.path :
|
|
17984
|
+
const path = isAbsolute5(event.path) ? event.path : resolve16(cwd, event.path);
|
|
17833
17985
|
if (!isPathInside(cwd, path)) {
|
|
17834
17986
|
logger.warn({ topicId, path }, "peer output file is outside the topic workspace");
|
|
17835
17987
|
break;
|
|
@@ -18117,15 +18269,15 @@ __export(exports_app_settings, {
|
|
|
18117
18269
|
getGlobalAiName: () => getGlobalAiName,
|
|
18118
18270
|
DEFAULT_AI_NAME: () => DEFAULT_AI_NAME
|
|
18119
18271
|
});
|
|
18120
|
-
import { existsSync as
|
|
18121
|
-
import { dirname as
|
|
18272
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync16 } from "fs";
|
|
18273
|
+
import { dirname as dirname15, join as join28 } from "path";
|
|
18122
18274
|
function settingsFile() {
|
|
18123
|
-
return
|
|
18275
|
+
return join28(resolveStorageDataDir(), "otium-settings.json");
|
|
18124
18276
|
}
|
|
18125
18277
|
function getGlobalAiName() {
|
|
18126
18278
|
const path = settingsFile();
|
|
18127
18279
|
try {
|
|
18128
|
-
if (
|
|
18280
|
+
if (existsSync19(path)) {
|
|
18129
18281
|
const data = JSON.parse(readFileSync16(path, "utf8"));
|
|
18130
18282
|
if (typeof data.aiName === "string" && data.aiName.trim()) {
|
|
18131
18283
|
return data.aiName.trim();
|
|
@@ -18138,7 +18290,7 @@ function setGlobalAiName(name) {
|
|
|
18138
18290
|
const path = settingsFile();
|
|
18139
18291
|
const aiName = name.trim() || DEFAULT_AI_NAME;
|
|
18140
18292
|
try {
|
|
18141
|
-
mkdirSync18(
|
|
18293
|
+
mkdirSync18(dirname15(path), { recursive: true });
|
|
18142
18294
|
writeFileSync16(path, JSON.stringify({ aiName }, null, 2));
|
|
18143
18295
|
} catch {}
|
|
18144
18296
|
return aiName;
|
|
@@ -18223,8 +18375,8 @@ __export(exports_turn_runner, {
|
|
|
18223
18375
|
AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
|
|
18224
18376
|
});
|
|
18225
18377
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
18226
|
-
import { existsSync as
|
|
18227
|
-
import { join as
|
|
18378
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync19, readdirSync as readdirSync6, statSync as statSync10 } from "fs";
|
|
18379
|
+
import { join as join29 } from "path";
|
|
18228
18380
|
function withDefaultPlaywright(configuredMcp, isManager) {
|
|
18229
18381
|
if (isManager)
|
|
18230
18382
|
return configuredMcp;
|
|
@@ -18279,8 +18431,8 @@ function appendAskReplyMessage(topicId, text2, agentType) {
|
|
|
18279
18431
|
return message;
|
|
18280
18432
|
}
|
|
18281
18433
|
function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
|
|
18282
|
-
const preferred =
|
|
18283
|
-
if (preferExact &&
|
|
18434
|
+
const preferred = join29(directory, preferredFilename);
|
|
18435
|
+
if (preferExact && existsSync20(preferred))
|
|
18284
18436
|
return preferred;
|
|
18285
18437
|
let newestLegacyId = null;
|
|
18286
18438
|
let newestTitle = null;
|
|
@@ -18290,7 +18442,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
|
|
|
18290
18442
|
const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
|
|
18291
18443
|
if (!titleMatch && !legacyIdMatch)
|
|
18292
18444
|
continue;
|
|
18293
|
-
const path =
|
|
18445
|
+
const path = join29(directory, filename);
|
|
18294
18446
|
const mtimeMs = statSync10(path).mtimeMs;
|
|
18295
18447
|
if (titleMatch) {
|
|
18296
18448
|
if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
|
|
@@ -18304,10 +18456,10 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
|
|
|
18304
18456
|
return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
|
|
18305
18457
|
}
|
|
18306
18458
|
function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
|
|
18307
|
-
const briefFile = resolveWikiMirrorPath(
|
|
18308
|
-
const hasBriefFile =
|
|
18309
|
-
const latestSummaryCandidate = resolveWikiMirrorPath(
|
|
18310
|
-
const latestSummaryFile =
|
|
18459
|
+
const briefFile = resolveWikiMirrorPath(join29(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
|
|
18460
|
+
const hasBriefFile = existsSync20(briefFile) && statSync10(briefFile).isFile();
|
|
18461
|
+
const latestSummaryCandidate = resolveWikiMirrorPath(join29(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
|
|
18462
|
+
const latestSummaryFile = existsSync20(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
|
|
18311
18463
|
return { briefFile, hasBriefFile, latestSummaryFile };
|
|
18312
18464
|
}
|
|
18313
18465
|
async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
|
|
@@ -21054,9 +21206,9 @@ var init_vault_command = __esm(async () => {
|
|
|
21054
21206
|
|
|
21055
21207
|
// ../../packages/core/src/media/text-extractor.ts
|
|
21056
21208
|
import { execFile } from "child_process";
|
|
21057
|
-
import { existsSync as
|
|
21209
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync21, readFileSync as readFileSync17, rmSync as rmSync7 } from "fs";
|
|
21058
21210
|
import { readFile } from "fs/promises";
|
|
21059
|
-
import { extname, join as
|
|
21211
|
+
import { extname, join as join30 } from "path";
|
|
21060
21212
|
import { promisify } from "util";
|
|
21061
21213
|
async function extractText(filePath) {
|
|
21062
21214
|
const ext = extname(filePath).toLowerCase();
|
|
@@ -21161,7 +21313,7 @@ async function extractFromImage(filePath) {
|
|
|
21161
21313
|
function isTranscriptionConfigured(opts = {}) {
|
|
21162
21314
|
const ffmpeg = opts.ffmpegBin ?? FFMPEG_BIN;
|
|
21163
21315
|
const wrapper = opts.wrapperPath ?? FASTER_WHISPER_WRAPPER;
|
|
21164
|
-
return Boolean(ffmpeg) &&
|
|
21316
|
+
return Boolean(ffmpeg) && existsSync21(wrapper);
|
|
21165
21317
|
}
|
|
21166
21318
|
async function transcribeAudio(filePath, opts = {}) {
|
|
21167
21319
|
if (!isTranscriptionConfigured(opts))
|
|
@@ -21173,7 +21325,7 @@ async function extractFromAudio(filePath, opts = {}) {
|
|
|
21173
21325
|
const tmpDir = `${filePath}_whisper_tmp`;
|
|
21174
21326
|
try {
|
|
21175
21327
|
mkdirSync21(tmpDir, { recursive: true });
|
|
21176
|
-
const mp3Path =
|
|
21328
|
+
const mp3Path = join30(tmpDir, "audio.mp3");
|
|
21177
21329
|
await execFileAsync(opts.ffmpegBin ?? FFMPEG_BIN2, [
|
|
21178
21330
|
"-y",
|
|
21179
21331
|
"-i",
|
|
@@ -21200,8 +21352,8 @@ async function extractFromAudio(filePath, opts = {}) {
|
|
|
21200
21352
|
"--output-format",
|
|
21201
21353
|
"txt"
|
|
21202
21354
|
], { timeout: 120000 });
|
|
21203
|
-
const txtPath =
|
|
21204
|
-
const text2 =
|
|
21355
|
+
const txtPath = join30(tmpDir, "audio.txt");
|
|
21356
|
+
const text2 = existsSync21(txtPath) ? readFileSync17(txtPath, "utf-8").trim() : null;
|
|
21205
21357
|
if (!text2) {
|
|
21206
21358
|
return { text: null, method: "whisper", error: "Whisper produced no text" };
|
|
21207
21359
|
}
|
|
@@ -21372,19 +21524,19 @@ var init_lifecycle2 = __esm(() => {
|
|
|
21372
21524
|
});
|
|
21373
21525
|
|
|
21374
21526
|
// ../../packages/core/src/platform/log-rotation.ts
|
|
21375
|
-
import { existsSync as
|
|
21376
|
-
import { join as
|
|
21527
|
+
import { existsSync as existsSync22, renameSync as renameSync8, statSync as statSync11 } from "fs";
|
|
21528
|
+
import { join as join31 } from "path";
|
|
21377
21529
|
function rotateOversizedLog(logPath, maxBytes = DEFAULT_DAEMON_LOG_ROTATE_BYTES) {
|
|
21378
21530
|
try {
|
|
21379
|
-
if (!
|
|
21531
|
+
if (!existsSync22(logPath))
|
|
21380
21532
|
return;
|
|
21381
21533
|
const { size } = statSync11(logPath);
|
|
21382
21534
|
if (size < maxBytes)
|
|
21383
21535
|
return;
|
|
21384
21536
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
21385
|
-
const dir =
|
|
21537
|
+
const dir = join31(logPath, "..");
|
|
21386
21538
|
const base = logPath.slice(dir.length + 1);
|
|
21387
|
-
renameSync8(logPath,
|
|
21539
|
+
renameSync8(logPath, join31(dir, `${base}.${stamp}`));
|
|
21388
21540
|
} catch {}
|
|
21389
21541
|
}
|
|
21390
21542
|
var DEFAULT_DAEMON_LOG_ROTATE_BYTES;
|
|
@@ -21577,7 +21729,7 @@ import {
|
|
|
21577
21729
|
rmSync as rmSync8,
|
|
21578
21730
|
statSync as statSync12
|
|
21579
21731
|
} from "fs";
|
|
21580
|
-
import { join as
|
|
21732
|
+
import { join as join32 } from "path";
|
|
21581
21733
|
function setBashrsCompletionSink(sink) {
|
|
21582
21734
|
completionSink = sink ?? defaultSink;
|
|
21583
21735
|
}
|
|
@@ -21604,15 +21756,15 @@ function readTail(filePath) {
|
|
|
21604
21756
|
}
|
|
21605
21757
|
}
|
|
21606
21758
|
function buildMessage(dir, result) {
|
|
21607
|
-
const stdout = readTail(
|
|
21608
|
-
const stderr = readTail(
|
|
21759
|
+
const stdout = readTail(join32(dir, "stdout.log"));
|
|
21760
|
+
const stderr = readTail(join32(dir, "stderr.log"));
|
|
21609
21761
|
const parts = [];
|
|
21610
21762
|
if (stdout.text.trim() || stdout.truncated) {
|
|
21611
|
-
parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${
|
|
21763
|
+
parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${join32(dir, "stdout.log")})` : ""}:
|
|
21612
21764
|
${stdout.text.trim()}`);
|
|
21613
21765
|
}
|
|
21614
21766
|
if (stderr.text.trim() || stderr.truncated) {
|
|
21615
|
-
parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${
|
|
21767
|
+
parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${join32(dir, "stderr.log")})` : ""}:
|
|
21616
21768
|
${stderr.text.trim()}`);
|
|
21617
21769
|
}
|
|
21618
21770
|
const header = watchHeader(result) ?? `[background_bash ${result.bash_id} finished]`;
|
|
@@ -21646,7 +21798,7 @@ function parseOwner(owner) {
|
|
|
21646
21798
|
return { userId, topicId };
|
|
21647
21799
|
}
|
|
21648
21800
|
function injectedMarker(dir) {
|
|
21649
|
-
return
|
|
21801
|
+
return join32(dir, "result.json.injected");
|
|
21650
21802
|
}
|
|
21651
21803
|
async function flushBashrsCompletions() {
|
|
21652
21804
|
let entries;
|
|
@@ -21657,8 +21809,8 @@ async function flushBashrsCompletions() {
|
|
|
21657
21809
|
}
|
|
21658
21810
|
const now = Date.now();
|
|
21659
21811
|
for (const bashId of entries) {
|
|
21660
|
-
const dir =
|
|
21661
|
-
const resultPath =
|
|
21812
|
+
const dir = join32(BASHRS_SPILL_ROOT, bashId);
|
|
21813
|
+
const resultPath = join32(dir, "result.json");
|
|
21662
21814
|
const marker = injectedMarker(dir);
|
|
21663
21815
|
try {
|
|
21664
21816
|
const markerStat = statSync12(marker);
|
|
@@ -21794,8 +21946,8 @@ function prepareDeliveryAck(messageId, claimTimeoutMs, deliveryTimeoutMs) {
|
|
|
21794
21946
|
let finishPromise = () => {};
|
|
21795
21947
|
let timer;
|
|
21796
21948
|
let unsubscribe = () => {};
|
|
21797
|
-
const promise = new Promise((
|
|
21798
|
-
finishPromise =
|
|
21949
|
+
const promise = new Promise((resolve17) => {
|
|
21950
|
+
finishPromise = resolve17;
|
|
21799
21951
|
});
|
|
21800
21952
|
unsubscribe = runtimeBus().subscribe((event) => {
|
|
21801
21953
|
if (event.type !== "ai-status")
|
|
@@ -21864,7 +22016,7 @@ function renderTurnFooter(msg) {
|
|
|
21864
22016
|
|
|
21865
22017
|
// ../../packages/core/src/outbox/file-ops.ts
|
|
21866
22018
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
21867
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
22019
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync23, readFileSync as readFileSync19, renameSync as renameSync10, unlinkSync as unlinkSync17 } from "fs";
|
|
21868
22020
|
function host() {
|
|
21869
22021
|
return hostContext.getStore() ?? defaultHost2;
|
|
21870
22022
|
}
|
|
@@ -21881,8 +22033,8 @@ function tryUnlink(path, label, reason) {
|
|
|
21881
22033
|
}
|
|
21882
22034
|
function drainOutboxFile(filePath, label) {
|
|
21883
22035
|
const processingPath = filePath + PROCESSING_SUFFIX;
|
|
21884
|
-
const hasPending =
|
|
21885
|
-
const hasLeftover =
|
|
22036
|
+
const hasPending = existsSync23(filePath);
|
|
22037
|
+
const hasLeftover = existsSync23(processingPath);
|
|
21886
22038
|
if (hasLeftover) {
|
|
21887
22039
|
const logMsg = `${label}: merging leftover .processing before claim`;
|
|
21888
22040
|
const logCtx = { filePath, processingPath, hadPending: hasPending };
|
|
@@ -21902,7 +22054,7 @@ function drainOutboxFile(filePath, label) {
|
|
|
21902
22054
|
tryUnlink(processingPath, label, "failed to clean up leftover after merge error");
|
|
21903
22055
|
}
|
|
21904
22056
|
}
|
|
21905
|
-
if (!
|
|
22057
|
+
if (!existsSync23(filePath))
|
|
21906
22058
|
return null;
|
|
21907
22059
|
try {
|
|
21908
22060
|
renameSync10(filePath, processingPath);
|
|
@@ -21956,7 +22108,7 @@ var init_file_ops = __esm(() => {
|
|
|
21956
22108
|
// ../../packages/core/src/runtime/inbox.ts
|
|
21957
22109
|
import { createHash as createHash9, randomUUID as randomUUID20 } from "crypto";
|
|
21958
22110
|
import { readdirSync as readdirSync8, statSync as statSync13, writeFileSync as writeFileSync17 } from "fs";
|
|
21959
|
-
import { join as
|
|
22111
|
+
import { join as join33 } from "path";
|
|
21960
22112
|
async function createAskForkPlan(options) {
|
|
21961
22113
|
const snapshot = structuredClone(options.entries);
|
|
21962
22114
|
const prepareSession = async () => options.synthesize(structuredClone(snapshot));
|
|
@@ -22096,7 +22248,7 @@ async function flushSessionInbox() {
|
|
|
22096
22248
|
return;
|
|
22097
22249
|
}
|
|
22098
22250
|
for (const uid of userDirs) {
|
|
22099
|
-
const userInboxDir =
|
|
22251
|
+
const userInboxDir = join33(SESSION_INBOX_DIR, uid);
|
|
22100
22252
|
let entries;
|
|
22101
22253
|
try {
|
|
22102
22254
|
entries = readdirSync8(userInboxDir);
|
|
@@ -22104,7 +22256,7 @@ async function flushSessionInbox() {
|
|
|
22104
22256
|
continue;
|
|
22105
22257
|
}
|
|
22106
22258
|
for (const entry of entries) {
|
|
22107
|
-
const entryPath =
|
|
22259
|
+
const entryPath = join33(userInboxDir, entry);
|
|
22108
22260
|
let isDir = false;
|
|
22109
22261
|
try {
|
|
22110
22262
|
isDir = statSync13(entryPath).isDirectory();
|
|
@@ -22194,7 +22346,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
|
|
|
22194
22346
|
return;
|
|
22195
22347
|
}
|
|
22196
22348
|
for (const userId of userDirs) {
|
|
22197
|
-
const userInboxDir =
|
|
22349
|
+
const userInboxDir = join33(SESSION_INBOX_DIR, userId);
|
|
22198
22350
|
let files;
|
|
22199
22351
|
try {
|
|
22200
22352
|
files = readdirSync8(userInboxDir);
|
|
@@ -22206,7 +22358,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
|
|
|
22206
22358
|
const topicId = topicIdFromScheduledSessionInboxFileName(file);
|
|
22207
22359
|
if (!topicId)
|
|
22208
22360
|
continue;
|
|
22209
|
-
const schedulePath =
|
|
22361
|
+
const schedulePath = join33(userInboxDir, file);
|
|
22210
22362
|
const hasProcessingClaim = files.includes(`${file}.processing`);
|
|
22211
22363
|
if (!hasProcessingClaim && !scheduledFileNeedsClaim(schedulePath, nowMs))
|
|
22212
22364
|
continue;
|
|
@@ -23042,8 +23194,8 @@ var init_src = __esm(async () => {
|
|
|
23042
23194
|
});
|
|
23043
23195
|
|
|
23044
23196
|
// ../../packages/core/src/storage/conversation-migration.ts
|
|
23045
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
23046
|
-
import { dirname as
|
|
23197
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync24, mkdirSync as mkdirSync23, readFileSync as readFileSync20 } from "fs";
|
|
23198
|
+
import { dirname as dirname16, join as join34 } from "path";
|
|
23047
23199
|
function readEntries(path) {
|
|
23048
23200
|
const entries = [];
|
|
23049
23201
|
for (const [index, line] of readFileSync20(path, "utf8").split(`
|
|
@@ -23106,20 +23258,20 @@ function migrateLegacyCompactedConversations() {
|
|
|
23106
23258
|
const userId = participant.userId;
|
|
23107
23259
|
const rawPath = getConversationPath(userId, topic.title);
|
|
23108
23260
|
const activePath = getActiveConversationPath(userId, topic.title);
|
|
23109
|
-
if (!
|
|
23261
|
+
if (!existsSync24(rawPath) || existsSync24(activePath)) {
|
|
23110
23262
|
result.skipped++;
|
|
23111
23263
|
continue;
|
|
23112
23264
|
}
|
|
23113
|
-
const backupPath =
|
|
23114
|
-
const sourcePath =
|
|
23265
|
+
const backupPath = join34(resolveStorageDataDir(), "conversation-migration-backups", MIGRATION_NAME, userId, `${sanitizeTopicName(topic.title, true)}.jsonl`);
|
|
23266
|
+
const sourcePath = existsSync24(backupPath) ? backupPath : rawPath;
|
|
23115
23267
|
const legacyEntries = readEntries(sourcePath);
|
|
23116
23268
|
const compactIndex = compactionEntryIndex(legacyEntries);
|
|
23117
23269
|
if (compactIndex < 0) {
|
|
23118
23270
|
result.skipped++;
|
|
23119
23271
|
continue;
|
|
23120
23272
|
}
|
|
23121
|
-
if (!
|
|
23122
|
-
mkdirSync23(
|
|
23273
|
+
if (!existsSync24(backupPath)) {
|
|
23274
|
+
mkdirSync23(dirname16(backupPath), { recursive: true });
|
|
23123
23275
|
copyFileSync3(rawPath, backupPath);
|
|
23124
23276
|
}
|
|
23125
23277
|
const compactedAt = legacyEntries[compactIndex]?.ts;
|
|
@@ -23169,8 +23321,8 @@ __export(exports_decisions, {
|
|
|
23169
23321
|
createDecisions: () => createDecisions,
|
|
23170
23322
|
DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES
|
|
23171
23323
|
});
|
|
23172
|
-
import { existsSync as
|
|
23173
|
-
import { dirname as
|
|
23324
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync24, readFileSync as readFileSync21, renameSync as renameSync11, writeFileSync as writeFileSync18 } from "fs";
|
|
23325
|
+
import { dirname as dirname17, join as join35 } from "path";
|
|
23174
23326
|
function safeDecisionScopeKey(scopeKey) {
|
|
23175
23327
|
const safe = sanitizeFileName(scopeKey);
|
|
23176
23328
|
if (!safe || safe === "." || safe === "..") {
|
|
@@ -23182,14 +23334,14 @@ function decisionScopeKey(opts) {
|
|
|
23182
23334
|
return opts.topicId?.trim() || opts.session || "default";
|
|
23183
23335
|
}
|
|
23184
23336
|
function getDecisionFilePath(userId, scopeKey) {
|
|
23185
|
-
return
|
|
23337
|
+
return join35(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
|
|
23186
23338
|
}
|
|
23187
23339
|
function getDecisionGraphSvgPath(userId, scopeKey) {
|
|
23188
|
-
return
|
|
23340
|
+
return join35(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
|
|
23189
23341
|
}
|
|
23190
23342
|
function writeDecisionGraphSvg(userId, scopeKey, svg) {
|
|
23191
23343
|
const path = getDecisionGraphSvgPath(userId, scopeKey);
|
|
23192
|
-
mkdirSync24(
|
|
23344
|
+
mkdirSync24(dirname17(path), { recursive: true });
|
|
23193
23345
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
23194
23346
|
writeFileSync18(tmp, svg, "utf-8");
|
|
23195
23347
|
renameSync11(tmp, path);
|
|
@@ -23197,7 +23349,7 @@ function writeDecisionGraphSvg(userId, scopeKey, svg) {
|
|
|
23197
23349
|
}
|
|
23198
23350
|
function readDecisions(userId, scopeKey) {
|
|
23199
23351
|
const path = getDecisionFilePath(userId, scopeKey);
|
|
23200
|
-
if (!
|
|
23352
|
+
if (!existsSync25(path))
|
|
23201
23353
|
return [];
|
|
23202
23354
|
try {
|
|
23203
23355
|
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
@@ -23209,7 +23361,7 @@ function readDecisions(userId, scopeKey) {
|
|
|
23209
23361
|
}
|
|
23210
23362
|
function writeDecisions(userId, scopeKey, decisions) {
|
|
23211
23363
|
const path = getDecisionFilePath(userId, scopeKey);
|
|
23212
|
-
mkdirSync24(
|
|
23364
|
+
mkdirSync24(dirname17(path), { recursive: true });
|
|
23213
23365
|
const payload = { version: 1, decisions };
|
|
23214
23366
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
23215
23367
|
writeFileSync18(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
@@ -23468,8 +23620,8 @@ var init_node_host = __esm(async () => {
|
|
|
23468
23620
|
});
|
|
23469
23621
|
|
|
23470
23622
|
// ../../packages/core/src/agents/mcp-tools/inline-assets.ts
|
|
23471
|
-
import { existsSync as
|
|
23472
|
-
import { extname as extname2, isAbsolute as isAbsolute6, resolve as
|
|
23623
|
+
import { existsSync as existsSync26, readFileSync as readFileSync22, statSync as statSync14 } from "fs";
|
|
23624
|
+
import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve17 } from "path";
|
|
23473
23625
|
function isNonLocal(ref) {
|
|
23474
23626
|
return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
|
|
23475
23627
|
}
|
|
@@ -23493,10 +23645,10 @@ function inlineLocalAssets(html, options) {
|
|
|
23493
23645
|
const relPath = stripSuffix(ref);
|
|
23494
23646
|
if (!relPath)
|
|
23495
23647
|
return null;
|
|
23496
|
-
const abs = isAbsolute6(relPath) ? relPath :
|
|
23648
|
+
const abs = isAbsolute6(relPath) ? relPath : resolve17(baseDir, relPath);
|
|
23497
23649
|
let bytes;
|
|
23498
23650
|
try {
|
|
23499
|
-
if (!
|
|
23651
|
+
if (!existsSync26(abs) || !statSync14(abs).isFile()) {
|
|
23500
23652
|
unresolved.push(ref);
|
|
23501
23653
|
cache.set(ref, null);
|
|
23502
23654
|
return null;
|
|
@@ -24444,8 +24596,8 @@ __export(exports_agent_health, {
|
|
|
24444
24596
|
createAgentHealthMcpServer: () => createAgentHealthMcpServer
|
|
24445
24597
|
});
|
|
24446
24598
|
import { spawn as spawn6 } from "child_process";
|
|
24447
|
-
import { existsSync as
|
|
24448
|
-
import { join as
|
|
24599
|
+
import { existsSync as existsSync27, readdirSync as readdirSync9 } from "fs";
|
|
24600
|
+
import { join as join36 } from "path";
|
|
24449
24601
|
import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24450
24602
|
import { z as z10 } from "zod";
|
|
24451
24603
|
function signalProcessTree2(child, signal) {
|
|
@@ -24461,7 +24613,7 @@ function signalProcessTree2(child, signal) {
|
|
|
24461
24613
|
}
|
|
24462
24614
|
}
|
|
24463
24615
|
function spawnCapture(cmd, args, timeoutMs, signal, env) {
|
|
24464
|
-
return new Promise((
|
|
24616
|
+
return new Promise((resolve18, reject) => {
|
|
24465
24617
|
const child = spawn6(cmd, args, {
|
|
24466
24618
|
env: env ?? process.env,
|
|
24467
24619
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -24519,7 +24671,7 @@ function spawnCapture(cmd, args, timeoutMs, signal, env) {
|
|
|
24519
24671
|
if (killTimer)
|
|
24520
24672
|
clearTimeout(killTimer);
|
|
24521
24673
|
if (code === 0)
|
|
24522
|
-
|
|
24674
|
+
resolve18({ stdout, stderr });
|
|
24523
24675
|
else
|
|
24524
24676
|
reject(new Error(stderr.trim() || `${cmd} exited with ${code ?? exitSignal}`));
|
|
24525
24677
|
});
|
|
@@ -24597,11 +24749,11 @@ async function checkMaestro(model, timeoutMs, signal) {
|
|
|
24597
24749
|
const remaining = deadline - Date.now();
|
|
24598
24750
|
if (remaining <= 0)
|
|
24599
24751
|
throw new Error("timeout");
|
|
24600
|
-
const eventResult = await new Promise((
|
|
24752
|
+
const eventResult = await new Promise((resolve18, reject) => {
|
|
24601
24753
|
const timeout = setTimeout(() => reject(new Error("timeout")), remaining);
|
|
24602
24754
|
const onAbort = () => reject(new Error("aborted"));
|
|
24603
24755
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
24604
|
-
activeIterator.next().then(
|
|
24756
|
+
activeIterator.next().then(resolve18, reject).finally(() => {
|
|
24605
24757
|
clearTimeout(timeout);
|
|
24606
24758
|
signal.removeEventListener("abort", onAbort);
|
|
24607
24759
|
});
|
|
@@ -24631,7 +24783,7 @@ function getDefaultModel(registry) {
|
|
|
24631
24783
|
}
|
|
24632
24784
|
async function withCheckSlot(operation) {
|
|
24633
24785
|
if (activeChecks >= MAX_CONCURRENT_CHECKS) {
|
|
24634
|
-
await new Promise((
|
|
24786
|
+
await new Promise((resolve18) => checkWaiters.push(resolve18));
|
|
24635
24787
|
}
|
|
24636
24788
|
activeChecks += 1;
|
|
24637
24789
|
try {
|
|
@@ -24689,8 +24841,8 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
|
|
|
24689
24841
|
server.tool("list_active_queries", "\uD604\uC7AC \uC0AC\uC6A9\uC790 \uBC94\uC704\uC5D0\uC11C \uC2E4\uD589 \uC911\uC778 \uD1A0\uD53D \uCFFC\uB9AC \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.", {}, async () => {
|
|
24690
24842
|
if (!context.userId)
|
|
24691
24843
|
return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
|
|
24692
|
-
const stateDir =
|
|
24693
|
-
if (!
|
|
24844
|
+
const stateDir = join36(USERS_LOG_DIR, context.userId, "active-queries");
|
|
24845
|
+
if (!existsSync27(stateDir))
|
|
24694
24846
|
return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
|
|
24695
24847
|
let files;
|
|
24696
24848
|
try {
|
|
@@ -24702,7 +24854,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
|
|
|
24702
24854
|
const entries = files.flatMap((file) => {
|
|
24703
24855
|
if (!file.endsWith(".json"))
|
|
24704
24856
|
return [];
|
|
24705
|
-
const state = readJsonFile(
|
|
24857
|
+
const state = readJsonFile(join36(stateDir, file));
|
|
24706
24858
|
if (!state)
|
|
24707
24859
|
return [];
|
|
24708
24860
|
const sinceMs = new Date(state.since).getTime();
|
|
@@ -24935,7 +25087,7 @@ var exports_default_host = {};
|
|
|
24935
25087
|
__export(exports_default_host, {
|
|
24936
25088
|
createDefaultSessionCommMcpHost: () => createDefaultSessionCommMcpHost
|
|
24937
25089
|
});
|
|
24938
|
-
import { basename as basename6, join as
|
|
25090
|
+
import { basename as basename6, join as join37 } from "path";
|
|
24939
25091
|
function ok2(text2) {
|
|
24940
25092
|
return { content: [{ type: "text", text: text2 }] };
|
|
24941
25093
|
}
|
|
@@ -24982,10 +25134,10 @@ function remoteTarget(context, to) {
|
|
|
24982
25134
|
return { node: to.slice(0, slash), topic: to.slice(slash + 1) };
|
|
24983
25135
|
}
|
|
24984
25136
|
function activeQuery(context, topicId, title) {
|
|
24985
|
-
const dir =
|
|
24986
|
-
const candidates = [
|
|
25137
|
+
const dir = join37(USERS_LOG_DIR, context.userId, "active-queries");
|
|
25138
|
+
const candidates = [join37(dir, `${sanitizeId(topicId)}.json`)];
|
|
24987
25139
|
if (title && basename6(title) === title && title !== "." && title !== "..") {
|
|
24988
|
-
candidates.push(
|
|
25140
|
+
candidates.push(join37(dir, `${title}.json`));
|
|
24989
25141
|
}
|
|
24990
25142
|
for (const path of candidates) {
|
|
24991
25143
|
const state = readJsonFile(path);
|
|
@@ -25276,298 +25428,15 @@ var init_default_host = __esm(async () => {
|
|
|
25276
25428
|
init_types();
|
|
25277
25429
|
});
|
|
25278
25430
|
|
|
25279
|
-
// ../../packages/core/src/mcp/vault-http.ts
|
|
25280
|
-
function substituteObject(userId, values, host2) {
|
|
25281
|
-
const used = new Set;
|
|
25282
|
-
const substituted = {};
|
|
25283
|
-
for (const [key, value] of Object.entries(values)) {
|
|
25284
|
-
if (FORBIDDEN_REQUEST_HEADERS.has(key.toLowerCase())) {
|
|
25285
|
-
throw new Error(`Header "${key}" is not allowed`);
|
|
25286
|
-
}
|
|
25287
|
-
const result = host2.substitute(userId, value);
|
|
25288
|
-
for (const usedKey of result.usedKeys)
|
|
25289
|
-
used.add(usedKey);
|
|
25290
|
-
substituted[key] = result.text;
|
|
25291
|
-
}
|
|
25292
|
-
return { values: substituted, usedKeys: [...used] };
|
|
25293
|
-
}
|
|
25294
|
-
function safeResponseHeaders(userId, headers, host2) {
|
|
25295
|
-
const output = {};
|
|
25296
|
-
for (const [key, value] of headers.entries()) {
|
|
25297
|
-
if (!SAFE_RESPONSE_HEADERS.has(key.toLowerCase()))
|
|
25298
|
-
continue;
|
|
25299
|
-
output[key] = host2.redact(userId, value);
|
|
25300
|
-
}
|
|
25301
|
-
return output;
|
|
25302
|
-
}
|
|
25303
|
-
async function readBoundedBody(response, maxBytes) {
|
|
25304
|
-
if (!response.body)
|
|
25305
|
-
return { bytes: new Uint8Array, truncated: false };
|
|
25306
|
-
const reader = response.body.getReader();
|
|
25307
|
-
const chunks = [];
|
|
25308
|
-
let keptBytes = 0;
|
|
25309
|
-
let truncated = false;
|
|
25310
|
-
try {
|
|
25311
|
-
while (true) {
|
|
25312
|
-
const next = await reader.read();
|
|
25313
|
-
if (next.done)
|
|
25314
|
-
break;
|
|
25315
|
-
const remaining = maxBytes - keptBytes;
|
|
25316
|
-
if (remaining <= 0) {
|
|
25317
|
-
truncated = true;
|
|
25318
|
-
await reader.cancel();
|
|
25319
|
-
break;
|
|
25320
|
-
}
|
|
25321
|
-
const visible = next.value.subarray(0, remaining);
|
|
25322
|
-
chunks.push(visible);
|
|
25323
|
-
keptBytes += visible.byteLength;
|
|
25324
|
-
if (visible.byteLength < next.value.byteLength) {
|
|
25325
|
-
truncated = true;
|
|
25326
|
-
await reader.cancel();
|
|
25327
|
-
break;
|
|
25328
|
-
}
|
|
25329
|
-
}
|
|
25330
|
-
} finally {
|
|
25331
|
-
reader.releaseLock();
|
|
25332
|
-
}
|
|
25333
|
-
const bytes = new Uint8Array(keptBytes);
|
|
25334
|
-
let offset = 0;
|
|
25335
|
-
for (const chunk of chunks) {
|
|
25336
|
-
bytes.set(chunk, offset);
|
|
25337
|
-
offset += chunk.byteLength;
|
|
25338
|
-
}
|
|
25339
|
-
return { bytes, truncated };
|
|
25340
|
-
}
|
|
25341
|
-
async function executeVaultHttpRequest(userId, request, host2, fetchImpl = fetch) {
|
|
25342
|
-
let parsedUrl;
|
|
25343
|
-
try {
|
|
25344
|
-
parsedUrl = new URL(request.url);
|
|
25345
|
-
} catch {
|
|
25346
|
-
return { ok: false, error: "url must be an absolute HTTPS URL" };
|
|
25347
|
-
}
|
|
25348
|
-
if (parsedUrl.protocol !== "https:") {
|
|
25349
|
-
return {
|
|
25350
|
-
ok: false,
|
|
25351
|
-
error: "Vault credentials may only be sent over HTTPS"
|
|
25352
|
-
};
|
|
25353
|
-
}
|
|
25354
|
-
if (parsedUrl.username || parsedUrl.password || /\{\{[^}]+\}\}/.test(request.url)) {
|
|
25355
|
-
return {
|
|
25356
|
-
ok: false,
|
|
25357
|
-
error: "Keep Vault placeholders out of URLs; put credentials in headers or body"
|
|
25358
|
-
};
|
|
25359
|
-
}
|
|
25360
|
-
let headers;
|
|
25361
|
-
let headerKeys;
|
|
25362
|
-
try {
|
|
25363
|
-
const substituted = substituteObject(userId, request.headers ?? {}, host2);
|
|
25364
|
-
headers = substituted.values;
|
|
25365
|
-
headerKeys = substituted.usedKeys;
|
|
25366
|
-
} catch (error2) {
|
|
25367
|
-
return {
|
|
25368
|
-
ok: false,
|
|
25369
|
-
error: error2 instanceof Error ? error2.message : String(error2)
|
|
25370
|
-
};
|
|
25371
|
-
}
|
|
25372
|
-
const bodyResult = request.body === undefined ? { text: undefined, usedKeys: [] } : host2.substitute(userId, request.body);
|
|
25373
|
-
const usedKeys = [...new Set([...headerKeys, ...bodyResult.usedKeys])].sort();
|
|
25374
|
-
if (usedKeys.length === 0) {
|
|
25375
|
-
return {
|
|
25376
|
-
ok: false,
|
|
25377
|
-
error: "No valid Vault placeholder was found in headers or body"
|
|
25378
|
-
};
|
|
25379
|
-
}
|
|
25380
|
-
if (request.method === "GET" && request.body !== undefined) {
|
|
25381
|
-
return {
|
|
25382
|
-
ok: false,
|
|
25383
|
-
error: "GET requests cannot include a credential-bearing body"
|
|
25384
|
-
};
|
|
25385
|
-
}
|
|
25386
|
-
const controller = new AbortController;
|
|
25387
|
-
const timeoutMs = Math.min(Math.max(request.timeoutMs ?? 30000, 1000), 120000);
|
|
25388
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
25389
|
-
const startedAt = Date.now();
|
|
25390
|
-
try {
|
|
25391
|
-
const response = await fetchImpl(parsedUrl, {
|
|
25392
|
-
method: request.method,
|
|
25393
|
-
headers,
|
|
25394
|
-
body: request.method === "GET" ? undefined : bodyResult.text,
|
|
25395
|
-
redirect: "manual",
|
|
25396
|
-
signal: controller.signal
|
|
25397
|
-
});
|
|
25398
|
-
const maxBytes = Math.min(Math.max(request.maxResponseBytes ?? 256 * 1024, 1024), 1024 * 1024);
|
|
25399
|
-
const { bytes, truncated } = await readBoundedBody(response, maxBytes);
|
|
25400
|
-
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
25401
|
-
const textual = contentType.startsWith("text/") || contentType.includes("json") || contentType.includes("xml") || contentType.includes("javascript") || contentType === "";
|
|
25402
|
-
const body = textual ? host2.redact(userId, new TextDecoder().decode(bytes)) : `[binary response omitted: ${bytes.byteLength} bytes, ${contentType || "unknown content type"}]`;
|
|
25403
|
-
host2.log?.("info", {
|
|
25404
|
-
userId,
|
|
25405
|
-
vaultKeys: usedKeys,
|
|
25406
|
-
host: parsedUrl.hostname,
|
|
25407
|
-
method: request.method,
|
|
25408
|
-
status: response.status,
|
|
25409
|
-
durationMs: Date.now() - startedAt
|
|
25410
|
-
}, "vault credential used");
|
|
25411
|
-
return {
|
|
25412
|
-
ok: response.ok,
|
|
25413
|
-
status: response.status,
|
|
25414
|
-
statusText: response.statusText,
|
|
25415
|
-
headers: safeResponseHeaders(userId, response.headers, host2),
|
|
25416
|
-
body,
|
|
25417
|
-
truncated
|
|
25418
|
-
};
|
|
25419
|
-
} catch (error2) {
|
|
25420
|
-
const raw = error2 instanceof Error ? error2.message : String(error2);
|
|
25421
|
-
host2.log?.("warn", {
|
|
25422
|
-
userId,
|
|
25423
|
-
vaultKeys: usedKeys,
|
|
25424
|
-
host: parsedUrl.hostname,
|
|
25425
|
-
method: request.method,
|
|
25426
|
-
durationMs: Date.now() - startedAt
|
|
25427
|
-
}, "vault credential request failed");
|
|
25428
|
-
return { ok: false, error: host2.redact(userId, raw) };
|
|
25429
|
-
} finally {
|
|
25430
|
-
clearTimeout(timeout);
|
|
25431
|
-
}
|
|
25432
|
-
}
|
|
25433
|
-
var SAFE_RESPONSE_HEADERS, FORBIDDEN_REQUEST_HEADERS;
|
|
25434
|
-
var init_vault_http = __esm(() => {
|
|
25435
|
-
SAFE_RESPONSE_HEADERS = new Set([
|
|
25436
|
-
"content-type",
|
|
25437
|
-
"content-length",
|
|
25438
|
-
"location",
|
|
25439
|
-
"retry-after",
|
|
25440
|
-
"x-ratelimit-limit",
|
|
25441
|
-
"x-ratelimit-remaining",
|
|
25442
|
-
"x-ratelimit-reset"
|
|
25443
|
-
]);
|
|
25444
|
-
FORBIDDEN_REQUEST_HEADERS = new Set([
|
|
25445
|
-
"connection",
|
|
25446
|
-
"content-length",
|
|
25447
|
-
"host",
|
|
25448
|
-
"proxy-authorization",
|
|
25449
|
-
"transfer-encoding"
|
|
25450
|
-
]);
|
|
25451
|
-
});
|
|
25452
|
-
|
|
25453
|
-
// ../../packages/core/src/mcp/vault-run.ts
|
|
25454
|
-
import { spawn as spawn7 } from "child_process";
|
|
25455
|
-
function appendBounded(chunks, chunk, state, maxBytes) {
|
|
25456
|
-
if (state.bytes >= maxBytes) {
|
|
25457
|
-
state.truncated = true;
|
|
25458
|
-
return;
|
|
25459
|
-
}
|
|
25460
|
-
const remaining = maxBytes - state.bytes;
|
|
25461
|
-
const visible = chunk.subarray(0, remaining);
|
|
25462
|
-
chunks.push(visible);
|
|
25463
|
-
state.bytes += visible.byteLength;
|
|
25464
|
-
if (visible.byteLength < chunk.byteLength)
|
|
25465
|
-
state.truncated = true;
|
|
25466
|
-
}
|
|
25467
|
-
async function executeVaultRun(userId, request, host2) {
|
|
25468
|
-
const substitution = host2.substitute(userId, request.command);
|
|
25469
|
-
if (substitution.usedKeys.length === 0) {
|
|
25470
|
-
return {
|
|
25471
|
-
ok: false,
|
|
25472
|
-
exitCode: null,
|
|
25473
|
-
signal: null,
|
|
25474
|
-
stdout: "",
|
|
25475
|
-
stderr: "",
|
|
25476
|
-
truncated: false,
|
|
25477
|
-
usedKeys: [],
|
|
25478
|
-
error: "No valid Vault placeholder was found in command"
|
|
25479
|
-
};
|
|
25480
|
-
}
|
|
25481
|
-
const timeoutMs = Math.min(Math.max(request.timeoutMs ?? 120000, 1000), 600000);
|
|
25482
|
-
const maxOutputBytes = Math.min(Math.max(request.maxOutputBytes ?? 512 * 1024, 1024), 2 * 1024 * 1024);
|
|
25483
|
-
const stdoutChunks = [];
|
|
25484
|
-
const stderrChunks = [];
|
|
25485
|
-
const stdoutState = { bytes: 0, truncated: false };
|
|
25486
|
-
const stderrState = { bytes: 0, truncated: false };
|
|
25487
|
-
const startedAt = Date.now();
|
|
25488
|
-
return await new Promise((resolve17) => {
|
|
25489
|
-
const child = spawn7(process.env.SHELL || "/bin/sh", ["-s"], {
|
|
25490
|
-
cwd: request.cwd,
|
|
25491
|
-
detached: true,
|
|
25492
|
-
env: process.env,
|
|
25493
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
25494
|
-
});
|
|
25495
|
-
let settled = false;
|
|
25496
|
-
let timedOut = false;
|
|
25497
|
-
const signalTree = (signal) => {
|
|
25498
|
-
if (!child.pid)
|
|
25499
|
-
return;
|
|
25500
|
-
try {
|
|
25501
|
-
process.kill(-child.pid, signal);
|
|
25502
|
-
} catch {
|
|
25503
|
-
try {
|
|
25504
|
-
child.kill(signal);
|
|
25505
|
-
} catch {}
|
|
25506
|
-
}
|
|
25507
|
-
};
|
|
25508
|
-
const finish = (result) => {
|
|
25509
|
-
if (settled)
|
|
25510
|
-
return;
|
|
25511
|
-
settled = true;
|
|
25512
|
-
clearTimeout(timeout);
|
|
25513
|
-
host2.log?.("info", {
|
|
25514
|
-
userId,
|
|
25515
|
-
vaultKeys: substitution.usedKeys,
|
|
25516
|
-
exitCode: result.exitCode,
|
|
25517
|
-
signal: result.signal,
|
|
25518
|
-
durationMs: Date.now() - startedAt,
|
|
25519
|
-
timedOut
|
|
25520
|
-
}, "vault credential command used");
|
|
25521
|
-
resolve17({ ...result, usedKeys: substitution.usedKeys });
|
|
25522
|
-
};
|
|
25523
|
-
child.stdout.on("data", (chunk) => appendBounded(stdoutChunks, chunk, stdoutState, maxOutputBytes));
|
|
25524
|
-
child.stderr.on("data", (chunk) => appendBounded(stderrChunks, chunk, stderrState, maxOutputBytes));
|
|
25525
|
-
child.once("error", (error2) => {
|
|
25526
|
-
finish({
|
|
25527
|
-
ok: false,
|
|
25528
|
-
exitCode: null,
|
|
25529
|
-
signal: null,
|
|
25530
|
-
stdout: "",
|
|
25531
|
-
stderr: "",
|
|
25532
|
-
truncated: false,
|
|
25533
|
-
error: host2.redact(userId, error2.message)
|
|
25534
|
-
});
|
|
25535
|
-
});
|
|
25536
|
-
child.once("close", (exitCode, signal) => {
|
|
25537
|
-
signalTree("SIGTERM");
|
|
25538
|
-
const stdout = host2.redact(userId, Buffer.concat(stdoutChunks).toString("utf8"));
|
|
25539
|
-
const stderr = host2.redact(userId, Buffer.concat(stderrChunks).toString("utf8"));
|
|
25540
|
-
finish({
|
|
25541
|
-
ok: !timedOut && exitCode === 0,
|
|
25542
|
-
exitCode,
|
|
25543
|
-
signal,
|
|
25544
|
-
stdout,
|
|
25545
|
-
stderr,
|
|
25546
|
-
truncated: stdoutState.truncated || stderrState.truncated,
|
|
25547
|
-
...timedOut ? { error: `Command timed out after ${timeoutMs}ms` } : {}
|
|
25548
|
-
});
|
|
25549
|
-
});
|
|
25550
|
-
const timeout = setTimeout(() => {
|
|
25551
|
-
timedOut = true;
|
|
25552
|
-
signalTree("SIGKILL");
|
|
25553
|
-
}, timeoutMs);
|
|
25554
|
-
child.stdin.end(substitution.text);
|
|
25555
|
-
});
|
|
25556
|
-
}
|
|
25557
|
-
var init_vault_run = () => {};
|
|
25558
|
-
|
|
25559
25431
|
// ../../packages/core/src/mcp/factories/vault.ts
|
|
25560
25432
|
var exports_vault = {};
|
|
25561
25433
|
__export(exports_vault, {
|
|
25562
25434
|
createVaultMcpServer: () => createVaultMcpServer
|
|
25563
25435
|
});
|
|
25564
25436
|
import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
25565
|
-
|
|
25566
|
-
function createVaultMcpServer(context, host2, executors = {}) {
|
|
25437
|
+
function createVaultMcpServer(context, host2) {
|
|
25567
25438
|
const server = new McpServer7({ name: "vault", version: "1.0.0" });
|
|
25568
|
-
|
|
25569
|
-
const http = executors.http ?? executeVaultHttpRequest;
|
|
25570
|
-
server.tool("vault_list", context.httpOnly ? "List the user's Vault keys and descriptions without exposing values. Use vault_http_request for HTTPS APIs that need a credential." : "List the user's Vault keys and descriptions without exposing values. Use vault_http_request for APIs and vault_run for shell/CLI work that needs a credential.", {}, () => {
|
|
25439
|
+
server.tool("vault_list", "List the user's Vault keys and descriptions without exposing values. Use {{KEY}} placeholders directly in supported transient tool inputs.", {}, () => {
|
|
25571
25440
|
if (!context.userId)
|
|
25572
25441
|
return mcpOk("(vault unavailable: no user context)");
|
|
25573
25442
|
const entries = host2.list(context.userId);
|
|
@@ -25578,54 +25447,14 @@ function createVaultMcpServer(context, host2, executors = {}) {
|
|
|
25578
25447
|
${lines.join(`
|
|
25579
25448
|
`)}`);
|
|
25580
25449
|
});
|
|
25581
|
-
if (!context.listOnly && !context.httpOnly) {
|
|
25582
|
-
server.tool("vault_run", "Run a shell command containing {{KEY}} references inside Otium's credential broker. Expanded command input never reaches the model/provider, and stdout/stderr are redacted before return. Prefer vault_http_request for HTTP APIs.", {
|
|
25583
|
-
command: z12.string().min(1).max(64 * 1024),
|
|
25584
|
-
timeout_ms: z12.number().int().min(1000).max(600000).optional(),
|
|
25585
|
-
max_output_bytes: z12.number().int().min(1024).max(2 * 1024 * 1024).optional()
|
|
25586
|
-
}, async ({ command, timeout_ms, max_output_bytes }) => {
|
|
25587
|
-
if (!context.userId)
|
|
25588
|
-
return mcpError("Vault unavailable: no user context");
|
|
25589
|
-
const result = await run(context.userId, {
|
|
25590
|
-
command,
|
|
25591
|
-
timeoutMs: timeout_ms,
|
|
25592
|
-
maxOutputBytes: max_output_bytes,
|
|
25593
|
-
cwd: context.cwd
|
|
25594
|
-
}, host2);
|
|
25595
|
-
return result.error && result.exitCode === null ? mcpError(result.error) : mcpOk(JSON.stringify(result, null, 2));
|
|
25596
|
-
});
|
|
25597
|
-
}
|
|
25598
|
-
if (!context.listOnly)
|
|
25599
|
-
server.tool("vault_http_request", "Make an HTTPS request with {{KEY}} references resolved inside Otium. Put secrets in headers or body, never in the URL. The expanded request is not returned; the response is redacted before the model sees it.", {
|
|
25600
|
-
method: z12.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
25601
|
-
url: z12.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
|
|
25602
|
-
headers: z12.record(z12.string(), z12.string()).optional(),
|
|
25603
|
-
body: z12.string().optional(),
|
|
25604
|
-
timeout_ms: z12.number().int().min(1000).max(120000).optional(),
|
|
25605
|
-
max_response_bytes: z12.number().int().min(1024).max(1024 * 1024).optional()
|
|
25606
|
-
}, async ({ method, url, headers, body, timeout_ms, max_response_bytes }) => {
|
|
25607
|
-
if (!context.userId)
|
|
25608
|
-
return mcpError("Vault unavailable: no user context");
|
|
25609
|
-
const result = await http(context.userId, {
|
|
25610
|
-
method,
|
|
25611
|
-
url,
|
|
25612
|
-
headers,
|
|
25613
|
-
body,
|
|
25614
|
-
timeoutMs: timeout_ms,
|
|
25615
|
-
maxResponseBytes: max_response_bytes
|
|
25616
|
-
}, host2);
|
|
25617
|
-
return result.error ? mcpError(result.error) : mcpOk(JSON.stringify(result, null, 2));
|
|
25618
|
-
});
|
|
25619
25450
|
return server;
|
|
25620
25451
|
}
|
|
25621
25452
|
var init_vault2 = __esm(() => {
|
|
25622
|
-
init_vault_http();
|
|
25623
|
-
init_vault_run();
|
|
25624
25453
|
init_mcp_helpers();
|
|
25625
25454
|
});
|
|
25626
25455
|
|
|
25627
25456
|
// ../../packages/core/src/mcp/wiki-search-index.ts
|
|
25628
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
25457
|
+
import { chmodSync as chmodSync5, existsSync as existsSync28, readFileSync as readFileSync23, unlinkSync as unlinkSync18 } from "fs";
|
|
25629
25458
|
function documentId(source) {
|
|
25630
25459
|
return `${source.kind}:${source.key}`;
|
|
25631
25460
|
}
|
|
@@ -25827,7 +25656,7 @@ class WikiSearchIndex {
|
|
|
25827
25656
|
}
|
|
25828
25657
|
function resetCorruptWikiSearchIndex(path) {
|
|
25829
25658
|
for (const candidate of [path, `${path}-journal`, `${path}-wal`, `${path}-shm`]) {
|
|
25830
|
-
if (!
|
|
25659
|
+
if (!existsSync28(candidate))
|
|
25831
25660
|
continue;
|
|
25832
25661
|
unlinkSync18(candidate);
|
|
25833
25662
|
}
|
|
@@ -25847,7 +25676,7 @@ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
|
25847
25676
|
import { randomUUID as randomUUID21 } from "crypto";
|
|
25848
25677
|
import {
|
|
25849
25678
|
closeSync as closeSync4,
|
|
25850
|
-
existsSync as
|
|
25679
|
+
existsSync as existsSync29,
|
|
25851
25680
|
mkdirSync as mkdirSync25,
|
|
25852
25681
|
openSync as openSync4,
|
|
25853
25682
|
readdirSync as readdirSync10,
|
|
@@ -25857,7 +25686,7 @@ import {
|
|
|
25857
25686
|
unlinkSync as unlinkSync19,
|
|
25858
25687
|
writeFileSync as writeFileSync19
|
|
25859
25688
|
} from "fs";
|
|
25860
|
-
import { basename as basename7, dirname as
|
|
25689
|
+
import { basename as basename7, dirname as dirname18, join as join38, relative as relative2, resolve as resolve18 } from "path";
|
|
25861
25690
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
25862
25691
|
import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
25863
25692
|
import {
|
|
@@ -26092,7 +25921,7 @@ function trigramSimilarity(left, right) {
|
|
|
26092
25921
|
return 2 * overlap / (a.size + b.size);
|
|
26093
25922
|
}
|
|
26094
25923
|
function openWikiSearchIndex() {
|
|
26095
|
-
const path =
|
|
25924
|
+
const path = resolve18(runtime().wikiDir, WIKI_SEARCH_INDEX_FILENAME);
|
|
26096
25925
|
try {
|
|
26097
25926
|
return new WikiSearchIndex(path);
|
|
26098
25927
|
} catch (error2) {
|
|
@@ -26119,7 +25948,7 @@ function prepareWikiSearchDocument(kind, key, text2) {
|
|
|
26119
25948
|
}
|
|
26120
25949
|
function indexWikiSearchDocument(kind, key, text2) {
|
|
26121
25950
|
const root = kind === "article" ? runtime().articlesDir : runtime().summariesDir;
|
|
26122
|
-
const path =
|
|
25951
|
+
const path = resolve18(root, `${key}.md`);
|
|
26123
25952
|
const index = openWikiSearchIndex();
|
|
26124
25953
|
if (!index)
|
|
26125
25954
|
return false;
|
|
@@ -26134,7 +25963,7 @@ function indexWikiSearchDocument(kind, key, text2) {
|
|
|
26134
25963
|
}
|
|
26135
25964
|
}
|
|
26136
25965
|
function wikiIndexPath(kind) {
|
|
26137
|
-
return
|
|
25966
|
+
return resolve18(runtime().wikiDir, WIKI_INDEX_FILENAMES[kind]);
|
|
26138
25967
|
}
|
|
26139
25968
|
function normalizeIndexDescription(raw) {
|
|
26140
25969
|
const description = raw.replaceAll(/\s+/g, " ").trim();
|
|
@@ -26190,7 +26019,7 @@ function indexCandidateExists(candidate) {
|
|
|
26190
26019
|
if (candidate.kind === "topic")
|
|
26191
26020
|
return true;
|
|
26192
26021
|
const root = candidate.kind === "article" ? runtime().articlesDir : runtime().summariesDir;
|
|
26193
|
-
const filePath =
|
|
26022
|
+
const filePath = resolve18(root, `${candidate.key}.md`);
|
|
26194
26023
|
const relativePath = relative2(root, filePath);
|
|
26195
26024
|
if (relativePath.startsWith("..") || relativePath.startsWith("/"))
|
|
26196
26025
|
return false;
|
|
@@ -26272,8 +26101,8 @@ function wikiQuery(args) {
|
|
|
26272
26101
|
const canReadTopicMemory = runtime().host.canReadTopicMemory;
|
|
26273
26102
|
const temporalDirection = /\b(?:latest|newest|recent)\b|\uCD5C\uADFC|\uCD5C\uC2E0/.test(normalizedQuery) ? "newest" : /\b(?:earliest|oldest)\b|\uAC00\uC7A5 \uC624\uB798\uB41C|\uCD5C\uCD08/.test(normalizedQuery) ? "oldest" : undefined;
|
|
26274
26103
|
const visibleIndexCandidates = [
|
|
26275
|
-
...parseWikiIndex(
|
|
26276
|
-
...parseWikiIndex(
|
|
26104
|
+
...parseWikiIndex(resolve18(runtime().wikiDir, "topic-index.md")),
|
|
26105
|
+
...parseWikiIndex(resolve18(runtime().wikiDir, "article-index.md"))
|
|
26277
26106
|
].filter((candidate) => kind === "all" || candidate.kind === kind).filter((candidate) => candidate.kind !== "topic" || !canReadTopicMemory || canReadTopicMemory(candidate.key, runtime().userId));
|
|
26278
26107
|
const hasStaleExactIndexMatch = visibleIndexCandidates.some((candidate) => !indexCandidateExists(candidate) && scoreIndexKey(question, candidate.key) >= 100);
|
|
26279
26108
|
const indexCandidates = visibleIndexCandidates.filter(indexCandidateExists).map((candidate) => ({
|
|
@@ -26307,13 +26136,13 @@ function wikiQuery(args) {
|
|
|
26307
26136
|
try {
|
|
26308
26137
|
for (const entry of readdirSync10(dir, { withFileTypes: true })) {
|
|
26309
26138
|
if (entry.isDirectory()) {
|
|
26310
|
-
const sub =
|
|
26139
|
+
const sub = join38(dir, entry.name);
|
|
26311
26140
|
if (label === "articles" || label === "skills") {
|
|
26312
26141
|
try {
|
|
26313
26142
|
for (const f of readdirSync10(sub, { withFileTypes: true })) {
|
|
26314
26143
|
if (!f.isFile() || !f.name.endsWith(".md"))
|
|
26315
26144
|
continue;
|
|
26316
|
-
const fp =
|
|
26145
|
+
const fp = join38(sub, f.name);
|
|
26317
26146
|
try {
|
|
26318
26147
|
const text2 = readFileSync24(fp, "utf-8");
|
|
26319
26148
|
const key = `${entry.name}/${f.name.replace(/\.md$/i, "")}`;
|
|
@@ -26334,7 +26163,7 @@ function wikiQuery(args) {
|
|
|
26334
26163
|
if (label === "topic" && canReadTopicMemory && !canReadTopicMemory(entry.name.replace(/\.md$/i, ""), runtime().userId)) {
|
|
26335
26164
|
continue;
|
|
26336
26165
|
}
|
|
26337
|
-
const fp =
|
|
26166
|
+
const fp = join38(dir, entry.name);
|
|
26338
26167
|
try {
|
|
26339
26168
|
const text2 = readFileSync24(fp, "utf-8");
|
|
26340
26169
|
const key = entry.name.replace(/\.md$/i, "");
|
|
@@ -26618,7 +26447,7 @@ function wikiTopicBrief(args) {
|
|
|
26618
26447
|
};
|
|
26619
26448
|
}
|
|
26620
26449
|
const topicKey = wikiSummarySlug(rawTopic.replace(/^topic\//, "").replace(/\.md$/i, ""));
|
|
26621
|
-
const filePath =
|
|
26450
|
+
const filePath = resolve18(runtime().topicsDir, `${topicKey}.md`);
|
|
26622
26451
|
try {
|
|
26623
26452
|
const content = readFileSync24(filePath, "utf-8");
|
|
26624
26453
|
return {
|
|
@@ -26666,7 +26495,7 @@ Adopted topic memory: ${memoryKey}` }]
|
|
|
26666
26495
|
}
|
|
26667
26496
|
const root = kind === "summary" ? runtime().summariesDir : runtime().articlesDir;
|
|
26668
26497
|
const key = rawKey.replace(new RegExp(`^(?:${kind === "summary" ? "summaries" : "articles"})/`), "").replace(/\.md$/i, "");
|
|
26669
|
-
const filePath =
|
|
26498
|
+
const filePath = resolve18(root, `${key}.md`);
|
|
26670
26499
|
const relativePath = relative2(root, filePath);
|
|
26671
26500
|
if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
|
|
26672
26501
|
return { content: [{ type: "text", text: "Invalid wiki document key." }], isError: true };
|
|
@@ -26690,18 +26519,18 @@ function wikiLastConversation(args) {
|
|
|
26690
26519
|
return { content: [{ type: "text", text: "No topic specified." }] };
|
|
26691
26520
|
}
|
|
26692
26521
|
const name = slugify(topicNameFrom(rawTopic));
|
|
26693
|
-
const archiveDir =
|
|
26522
|
+
const archiveDir = resolve18(runtime().archiveDir, name);
|
|
26694
26523
|
try {
|
|
26695
|
-
let files =
|
|
26524
|
+
let files = existsSync29(archiveDir) ? readdirSync10(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
|
|
26696
26525
|
if (files.length === 0) {
|
|
26697
|
-
files =
|
|
26526
|
+
files = existsSync29(runtime().archiveDir) ? readdirSync10(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
|
|
26698
26527
|
}
|
|
26699
26528
|
if (files.length === 0) {
|
|
26700
26529
|
return {
|
|
26701
26530
|
content: [{ type: "text", text: `No archive found for topic: ${rawTopic}` }]
|
|
26702
26531
|
};
|
|
26703
26532
|
}
|
|
26704
|
-
const actualPath = files[0].includes("/") ? files[0] :
|
|
26533
|
+
const actualPath = files[0].includes("/") ? files[0] : resolve18(existsSync29(archiveDir) && readdirSync10(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
|
|
26705
26534
|
try {
|
|
26706
26535
|
const raw = readFileSync24(actualPath, "utf-8");
|
|
26707
26536
|
const lines = raw.trim().split(`
|
|
@@ -26774,7 +26603,7 @@ function skillQuery(args) {
|
|
|
26774
26603
|
})) {
|
|
26775
26604
|
if (!entry.isDirectory())
|
|
26776
26605
|
continue;
|
|
26777
|
-
const skillFile =
|
|
26606
|
+
const skillFile = resolve18(runtime().skillsDir, entry.name, "skill.md");
|
|
26778
26607
|
try {
|
|
26779
26608
|
const text2 = readFileSync24(skillFile, "utf-8");
|
|
26780
26609
|
const lower = text2.toLowerCase();
|
|
@@ -26842,9 +26671,9 @@ ${fresh.join(`
|
|
|
26842
26671
|
`)}
|
|
26843
26672
|
`;
|
|
26844
26673
|
}
|
|
26845
|
-
const skillDir =
|
|
26674
|
+
const skillDir = resolve18(runtime().skillsDir, name);
|
|
26846
26675
|
ensureDir(skillDir);
|
|
26847
|
-
const skillPath =
|
|
26676
|
+
const skillPath = resolve18(skillDir, "skill.md");
|
|
26848
26677
|
let finalContent = content;
|
|
26849
26678
|
try {
|
|
26850
26679
|
const existing = readFileSync24(skillPath, "utf-8");
|
|
@@ -26869,7 +26698,7 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26869
26698
|
let counter = 1;
|
|
26870
26699
|
while (true) {
|
|
26871
26700
|
try {
|
|
26872
|
-
writeFileSync19(
|
|
26701
|
+
writeFileSync19(resolve18(runtime().summariesDir, summaryName), content, {
|
|
26873
26702
|
encoding: "utf-8",
|
|
26874
26703
|
flag: "wx"
|
|
26875
26704
|
});
|
|
@@ -26886,8 +26715,8 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26886
26715
|
let sqliteUpdated = false;
|
|
26887
26716
|
if (topicId && setTopicBrief2) {
|
|
26888
26717
|
try {
|
|
26889
|
-
const briefPath =
|
|
26890
|
-
let briefMd =
|
|
26718
|
+
const briefPath = resolve18(runtime().topicsDir, `${fileSlug}.md`);
|
|
26719
|
+
let briefMd = existsSync29(briefPath) ? readFileSync24(briefPath, "utf-8") : undefined;
|
|
26891
26720
|
if (briefMd === undefined && getTopicBrief2 && !getTopicBrief2(fileSlug)?.briefMd) {
|
|
26892
26721
|
const legacy = getTopicBrief2(topicId)?.briefMd;
|
|
26893
26722
|
if (legacy)
|
|
@@ -26906,9 +26735,9 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26906
26735
|
function writeTopicDocument(rawTopic, content) {
|
|
26907
26736
|
const topicId = runtime().topicId;
|
|
26908
26737
|
const fileSlug = wikiBriefStorageKey(rawTopic, topicId);
|
|
26909
|
-
const briefPath =
|
|
26910
|
-
ensureDir(
|
|
26911
|
-
const existed =
|
|
26738
|
+
const briefPath = resolve18(runtime().topicsDir, `${fileSlug}.md`);
|
|
26739
|
+
ensureDir(dirname18(briefPath));
|
|
26740
|
+
const existed = existsSync29(briefPath);
|
|
26912
26741
|
writeFileSync19(briefPath, content, "utf-8");
|
|
26913
26742
|
const setTopicBrief2 = runtime().host.setTopicBrief;
|
|
26914
26743
|
let sqliteUpdated = false;
|
|
@@ -26921,9 +26750,9 @@ function writeTopicDocument(rawTopic, content) {
|
|
|
26921
26750
|
return { slug: fileSlug, existed, sqliteUpdated };
|
|
26922
26751
|
}
|
|
26923
26752
|
function writeArticleDocument(slug, content) {
|
|
26924
|
-
const articlePath =
|
|
26925
|
-
ensureDir(
|
|
26926
|
-
const existed =
|
|
26753
|
+
const articlePath = resolve18(runtime().articlesDir, `${slug}.md`);
|
|
26754
|
+
ensureDir(dirname18(articlePath));
|
|
26755
|
+
const existed = existsSync29(articlePath);
|
|
26927
26756
|
atomicWriteFile(articlePath, content);
|
|
26928
26757
|
return { existed };
|
|
26929
26758
|
}
|
|
@@ -27055,21 +26884,21 @@ function collectIndexableDocuments() {
|
|
|
27055
26884
|
}
|
|
27056
26885
|
for (const entry of entries) {
|
|
27057
26886
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
27058
|
-
add(kind, entry.name.replace(/\.md$/i, ""),
|
|
26887
|
+
add(kind, entry.name.replace(/\.md$/i, ""), join38(root, entry.name));
|
|
27059
26888
|
continue;
|
|
27060
26889
|
}
|
|
27061
26890
|
if (!entry.isDirectory() || !allowSubdirectories)
|
|
27062
26891
|
continue;
|
|
27063
26892
|
let nested;
|
|
27064
26893
|
try {
|
|
27065
|
-
nested = readdirSync10(
|
|
26894
|
+
nested = readdirSync10(join38(root, entry.name), { withFileTypes: true });
|
|
27066
26895
|
} catch {
|
|
27067
26896
|
continue;
|
|
27068
26897
|
}
|
|
27069
26898
|
for (const file of nested) {
|
|
27070
26899
|
if (!file.isFile() || !file.name.endsWith(".md"))
|
|
27071
26900
|
continue;
|
|
27072
|
-
add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`,
|
|
26901
|
+
add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join38(root, entry.name, file.name));
|
|
27073
26902
|
}
|
|
27074
26903
|
}
|
|
27075
26904
|
}
|
|
@@ -27126,7 +26955,7 @@ function indexRowOrPartialWrite(label, options) {
|
|
|
27126
26955
|
function upsertIndexRow(options) {
|
|
27127
26956
|
const { kind, slug, description, date, section } = options;
|
|
27128
26957
|
const indexPath = wikiIndexPath(kind);
|
|
27129
|
-
ensureDir(
|
|
26958
|
+
ensureDir(dirname18(indexPath));
|
|
27130
26959
|
const releaseLock = acquireFileLock(indexPath);
|
|
27131
26960
|
try {
|
|
27132
26961
|
let index;
|
|
@@ -27183,7 +27012,7 @@ function upsertIndexRow(options) {
|
|
|
27183
27012
|
}
|
|
27184
27013
|
function wikiDocumentExists(kind, slug) {
|
|
27185
27014
|
const root = kind === "article" ? runtime().articlesDir : kind === "summary" ? runtime().summariesDir : kind === "topic" ? runtime().topicsDir : runtime().skillsDir;
|
|
27186
|
-
const filePath =
|
|
27015
|
+
const filePath = resolve18(root, `${slug}.md`);
|
|
27187
27016
|
if (relative2(root, filePath).startsWith(".."))
|
|
27188
27017
|
return false;
|
|
27189
27018
|
try {
|
|
@@ -27235,7 +27064,7 @@ function indexUpsert(args) {
|
|
|
27235
27064
|
}
|
|
27236
27065
|
function createWikiMcpServer(context, host2) {
|
|
27237
27066
|
const surface = context.surface ?? "all";
|
|
27238
|
-
const wikiDir =
|
|
27067
|
+
const wikiDir = resolve18(host2.wikiRoot);
|
|
27239
27068
|
const current3 = {
|
|
27240
27069
|
userId: context.userId,
|
|
27241
27070
|
currentTopicId: context.currentTopicId ?? context.topicId,
|
|
@@ -27243,11 +27072,11 @@ function createWikiMcpServer(context, host2) {
|
|
|
27243
27072
|
surface,
|
|
27244
27073
|
host: host2,
|
|
27245
27074
|
wikiDir,
|
|
27246
|
-
skillsDir:
|
|
27247
|
-
topicsDir:
|
|
27248
|
-
summariesDir:
|
|
27249
|
-
articlesDir:
|
|
27250
|
-
archiveDir:
|
|
27075
|
+
skillsDir: resolve18(wikiDir, "skills"),
|
|
27076
|
+
topicsDir: resolve18(wikiDir, "topic"),
|
|
27077
|
+
summariesDir: resolve18(wikiDir, "summaries"),
|
|
27078
|
+
articlesDir: resolve18(wikiDir, "articles"),
|
|
27079
|
+
archiveDir: resolve18(wikiDir, "archive")
|
|
27251
27080
|
};
|
|
27252
27081
|
const tools = surface === "wiki" ? WIKI_TOOLS : surface === "skills" ? SKILL_TOOLS : [...WIKI_TOOLS, ...SKILL_TOOLS];
|
|
27253
27082
|
for (const directory of [
|
|
@@ -27570,7 +27399,7 @@ __export(exports_activity_log, {
|
|
|
27570
27399
|
readSentFilesForTopic: () => readSentFilesForTopic
|
|
27571
27400
|
});
|
|
27572
27401
|
import {
|
|
27573
|
-
existsSync as
|
|
27402
|
+
existsSync as existsSync30,
|
|
27574
27403
|
mkdirSync as mkdirSync26,
|
|
27575
27404
|
readdirSync as readdirSync11,
|
|
27576
27405
|
renameSync as renameSync13,
|
|
@@ -27578,7 +27407,7 @@ import {
|
|
|
27578
27407
|
unlinkSync as unlinkSync20,
|
|
27579
27408
|
writeFileSync as writeFileSync20
|
|
27580
27409
|
} from "fs";
|
|
27581
|
-
import { basename as basename8, join as
|
|
27410
|
+
import { basename as basename8, join as join39 } from "path";
|
|
27582
27411
|
function startLogRotation() {
|
|
27583
27412
|
if (rotationTimer)
|
|
27584
27413
|
return;
|
|
@@ -27592,7 +27421,7 @@ function writeLog(entry) {
|
|
|
27592
27421
|
mkdirSync26(logDir, { recursive: true });
|
|
27593
27422
|
const safeSession = sanitizeTopicName(entry.session);
|
|
27594
27423
|
const sidShort = entry.sessionId ? entry.sessionId.slice(0, 8) : "new";
|
|
27595
|
-
const file =
|
|
27424
|
+
const file = join39(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
|
|
27596
27425
|
try {
|
|
27597
27426
|
const stat = statSync16(file);
|
|
27598
27427
|
if (stat.size >= MAX_FILE_SIZE) {
|
|
@@ -27612,7 +27441,7 @@ function rotateOldLogs() {
|
|
|
27612
27441
|
try {
|
|
27613
27442
|
const files = readdirSync11(logDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
27614
27443
|
try {
|
|
27615
|
-
const stat = statSync16(
|
|
27444
|
+
const stat = statSync16(join39(logDir, f));
|
|
27616
27445
|
return { name: f, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
27617
27446
|
} catch {
|
|
27618
27447
|
return null;
|
|
@@ -27628,7 +27457,7 @@ function rotateOldLogs() {
|
|
|
27628
27457
|
if (freed >= toFree)
|
|
27629
27458
|
break;
|
|
27630
27459
|
try {
|
|
27631
|
-
unlinkSync20(
|
|
27460
|
+
unlinkSync20(join39(logDir, file.name));
|
|
27632
27461
|
freed += file.size;
|
|
27633
27462
|
logger.info({ file: file.name, sizeKB: (file.size / 1024).toFixed(0) }, "Rotated out old log");
|
|
27634
27463
|
} catch (e) {
|
|
@@ -27640,9 +27469,9 @@ function rotateOldLogs() {
|
|
|
27640
27469
|
}
|
|
27641
27470
|
}
|
|
27642
27471
|
function writeSentFileLog(entry) {
|
|
27643
|
-
const dir =
|
|
27472
|
+
const dir = join39(resolveStorageUsersLogDir(), String(entry.userId));
|
|
27644
27473
|
mkdirSync26(dir, { recursive: true });
|
|
27645
|
-
const file =
|
|
27474
|
+
const file = join39(dir, "sent-files.jsonl");
|
|
27646
27475
|
try {
|
|
27647
27476
|
appendJsonlEntry(file, { ...entry, fileName: basename8(entry.filePath) });
|
|
27648
27477
|
} catch (e) {
|
|
@@ -27653,8 +27482,8 @@ function entryMatchesTopic(e, topicName) {
|
|
|
27653
27482
|
return e.topicName === topicName;
|
|
27654
27483
|
}
|
|
27655
27484
|
function readSentFilesForTopic(userId, topicName) {
|
|
27656
|
-
const file =
|
|
27657
|
-
if (!
|
|
27485
|
+
const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
|
|
27486
|
+
if (!existsSync30(file))
|
|
27658
27487
|
return [];
|
|
27659
27488
|
try {
|
|
27660
27489
|
return readJsonlLines(file).map((line) => JSON.parse(line)).filter((e) => entryMatchesTopic(e, topicName));
|
|
@@ -27664,8 +27493,8 @@ function readSentFilesForTopic(userId, topicName) {
|
|
|
27664
27493
|
}
|
|
27665
27494
|
}
|
|
27666
27495
|
function removeSentFilesForTopic(userId, topicName) {
|
|
27667
|
-
const file =
|
|
27668
|
-
if (!
|
|
27496
|
+
const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
|
|
27497
|
+
if (!existsSync30(file))
|
|
27669
27498
|
return;
|
|
27670
27499
|
try {
|
|
27671
27500
|
const remaining = readJsonlLines(file).filter((line) => {
|
|
@@ -28429,7 +28258,7 @@ var init_storage_public = __esm(async () => {
|
|
|
28429
28258
|
});
|
|
28430
28259
|
|
|
28431
28260
|
// ../../packages/mcp/src/hosted-surfaces.ts
|
|
28432
|
-
import { resolve as
|
|
28261
|
+
import { resolve as resolve19 } from "path";
|
|
28433
28262
|
function isActiveHostedMcpSurface(surface) {
|
|
28434
28263
|
return ACTIVE_HOSTED_MCP_SURFACES.includes(surface);
|
|
28435
28264
|
}
|
|
@@ -28486,23 +28315,11 @@ async function buildHostedSurfaceServer(surface, context) {
|
|
|
28486
28315
|
});
|
|
28487
28316
|
}
|
|
28488
28317
|
case "vault": {
|
|
28489
|
-
const [{ createVaultMcpServer: createVaultMcpServer2 }, vault
|
|
28318
|
+
const [{ createVaultMcpServer: createVaultMcpServer2 }, vault] = await Promise.all([
|
|
28490
28319
|
Promise.resolve().then(() => (init_vault2(), exports_vault)),
|
|
28491
|
-
init_vault_public().then(() => exports_vault_public)
|
|
28492
|
-
init_mcp_runtime_host().then(() => exports_mcp_runtime_host)
|
|
28320
|
+
init_vault_public().then(() => exports_vault_public)
|
|
28493
28321
|
]);
|
|
28494
|
-
return createVaultMcpServer2({
|
|
28495
|
-
userId: context.userId,
|
|
28496
|
-
listOnly: context.agent !== "codex",
|
|
28497
|
-
cwd: context.cwd
|
|
28498
|
-
}, {
|
|
28499
|
-
list: vault.vaultList,
|
|
28500
|
-
substitute: vault.vaultSubstituteDetailed,
|
|
28501
|
-
redact: vault.redactVaultSecrets,
|
|
28502
|
-
log(level, details, message) {
|
|
28503
|
-
logger2[level](details, message);
|
|
28504
|
-
}
|
|
28505
|
-
});
|
|
28322
|
+
return createVaultMcpServer2({ userId: context.userId }, { list: vault.vaultList });
|
|
28506
28323
|
}
|
|
28507
28324
|
case "wiki":
|
|
28508
28325
|
case "skills": {
|
|
@@ -28525,7 +28342,7 @@ async function buildHostedSurfaceServer(surface, context) {
|
|
|
28525
28342
|
...topicId ? { topicId } : {},
|
|
28526
28343
|
surface
|
|
28527
28344
|
}, {
|
|
28528
|
-
wikiRoot:
|
|
28345
|
+
wikiRoot: resolve19(WORKSPACE_DIR2, "wiki"),
|
|
28529
28346
|
getTopicBrief: storage.getTopicBrief,
|
|
28530
28347
|
resolveTopicBrief: accessibleTopicBrief,
|
|
28531
28348
|
setTopicBrief: storage.setTopicBrief
|
|
@@ -28656,10 +28473,10 @@ var init_sse_transport = () => {};
|
|
|
28656
28473
|
// ../../packages/mcp/src/server.ts
|
|
28657
28474
|
import { createHash as createHash10, randomUUID as randomUUID23 } from "crypto";
|
|
28658
28475
|
import { realpathSync as realpathSync6, statSync as statSync17 } from "fs";
|
|
28659
|
-
import { basename as basename9, extname as extname3, resolve as
|
|
28476
|
+
import { basename as basename9, extname as extname3, resolve as resolve20 } from "path";
|
|
28660
28477
|
import { McpServer as McpServer8 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28661
28478
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
28662
|
-
import { z as
|
|
28479
|
+
import { z as z12 } from "zod";
|
|
28663
28480
|
function requireTopicAccess(ctx) {
|
|
28664
28481
|
const topic = getTopic(ctx.topicId);
|
|
28665
28482
|
if (!topic)
|
|
@@ -28670,8 +28487,8 @@ function requireTopicAccess(ctx) {
|
|
|
28670
28487
|
return { topic };
|
|
28671
28488
|
}
|
|
28672
28489
|
function isPathInside2(baseDir, filePath) {
|
|
28673
|
-
const cwd =
|
|
28674
|
-
const normalized =
|
|
28490
|
+
const cwd = resolve20(baseDir);
|
|
28491
|
+
const normalized = resolve20(filePath);
|
|
28675
28492
|
try {
|
|
28676
28493
|
const realCwd = realpathSync6(cwd);
|
|
28677
28494
|
const real = realpathSync6(normalized);
|
|
@@ -28687,7 +28504,7 @@ function localFileInfo(ctx, filePath) {
|
|
|
28687
28504
|
if (!isPathInside2(ctx.cwd, filePath)) {
|
|
28688
28505
|
return { error: "Access denied. File must be within the topic workspace." };
|
|
28689
28506
|
}
|
|
28690
|
-
const normalizedPath =
|
|
28507
|
+
const normalizedPath = resolve20(filePath);
|
|
28691
28508
|
if (isSensitivePath(normalizedPath)) {
|
|
28692
28509
|
return { error: "Access denied. Path matches the sensitive-file blacklist." };
|
|
28693
28510
|
}
|
|
@@ -28879,8 +28696,8 @@ function buildNegotiumMcpServer(ctx) {
|
|
|
28879
28696
|
}
|
|
28880
28697
|
}
|
|
28881
28698
|
if (ctx.fileDeliveryTools === true) {
|
|
28882
|
-
server.tool("send_file", "Send a local file to the user in the chat. Use this when you want to share a file (image, document, PDF, code, etc.) with the user. The file will appear as a downloadable item in the chat.", { file_path:
|
|
28883
|
-
server.tool("send_files", "Send multiple local files to the user in the chat at once.", { file_paths:
|
|
28699
|
+
server.tool("send_file", "Send a local file to the user in the chat. Use this when you want to share a file (image, document, PDF, code, etc.) with the user. The file will appear as a downloadable item in the chat.", { file_path: z12.string().describe("Absolute path to the file to send") }, async ({ file_path }) => deliverFile(ctx, file_path));
|
|
28700
|
+
server.tool("send_files", "Send multiple local files to the user in the chat at once.", { file_paths: z12.array(z12.string()).describe("Array of absolute file paths to send") }, async ({ file_paths }) => {
|
|
28884
28701
|
const results = await Promise.all(file_paths.map((filePath) => deliverFile(ctx, filePath)));
|
|
28885
28702
|
const hasError = results.some((result) => ("isError" in result) && result.isError);
|
|
28886
28703
|
return {
|
|
@@ -29164,37 +28981,37 @@ var init_src2 = __esm(async () => {
|
|
|
29164
28981
|
|
|
29165
28982
|
// ../../packages/mcp-host/src/paths.ts
|
|
29166
28983
|
import { homedir as homedir9 } from "os";
|
|
29167
|
-
import { resolve as
|
|
28984
|
+
import { resolve as resolve21 } from "path";
|
|
29168
28985
|
function stateDir() {
|
|
29169
28986
|
const env = process.env.NEGOTIUM_STATE_DIR?.trim();
|
|
29170
|
-
return env ?
|
|
28987
|
+
return env ? resolve21(env) : resolve21(homedir9(), ".negotium");
|
|
29171
28988
|
}
|
|
29172
28989
|
function defaultPortsDir() {
|
|
29173
|
-
return
|
|
28990
|
+
return resolve21(stateDir(), "run", "mcp-ports");
|
|
29174
28991
|
}
|
|
29175
28992
|
function defaultManifestFile() {
|
|
29176
|
-
return
|
|
28993
|
+
return resolve21(stateDir(), "data", "mcp-manifest.json");
|
|
29177
28994
|
}
|
|
29178
28995
|
var init_paths = () => {};
|
|
29179
28996
|
|
|
29180
28997
|
// ../../packages/mcp-host/src/spec.ts
|
|
29181
|
-
import { z as
|
|
28998
|
+
import { z as z13 } from "zod";
|
|
29182
28999
|
var KEY_PATTERN, portSchema, mcpServerSpecSchema;
|
|
29183
29000
|
var init_spec = __esm(() => {
|
|
29184
29001
|
KEY_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
29185
|
-
portSchema =
|
|
29186
|
-
mcpServerSpecSchema =
|
|
29187
|
-
key:
|
|
29188
|
-
transport:
|
|
29189
|
-
command:
|
|
29190
|
-
args:
|
|
29191
|
-
env:
|
|
29192
|
-
cwd:
|
|
29193
|
-
portRange:
|
|
29194
|
-
scope:
|
|
29195
|
-
readyTimeoutMs:
|
|
29196
|
-
idleEvictMs:
|
|
29197
|
-
healthIntervalMs:
|
|
29002
|
+
portSchema = z13.number().int().min(1).max(65535);
|
|
29003
|
+
mcpServerSpecSchema = z13.object({
|
|
29004
|
+
key: z13.string().min(1).regex(KEY_PATTERN, "key may only contain [a-zA-Z0-9._-]").refine((k) => k !== "." && k !== "..", "key may not be a dot path"),
|
|
29005
|
+
transport: z13.enum(["stdio", "http"]),
|
|
29006
|
+
command: z13.string().min(1),
|
|
29007
|
+
args: z13.array(z13.string()).optional(),
|
|
29008
|
+
env: z13.record(z13.string(), z13.string()).optional(),
|
|
29009
|
+
cwd: z13.string().optional(),
|
|
29010
|
+
portRange: z13.object({ base: portSchema, max: portSchema }).optional(),
|
|
29011
|
+
scope: z13.enum(["node", "instance"]),
|
|
29012
|
+
readyTimeoutMs: z13.number().int().positive().optional(),
|
|
29013
|
+
idleEvictMs: z13.number().int().positive().optional(),
|
|
29014
|
+
healthIntervalMs: z13.number().int().positive().optional()
|
|
29198
29015
|
}).superRefine((spec, ctx) => {
|
|
29199
29016
|
if (spec.transport === "http") {
|
|
29200
29017
|
if (!spec.portRange) {
|
|
@@ -29215,9 +29032,9 @@ var init_spec = __esm(() => {
|
|
|
29215
29032
|
});
|
|
29216
29033
|
|
|
29217
29034
|
// ../../packages/mcp-host/src/manifest.ts
|
|
29218
|
-
import { existsSync as
|
|
29219
|
-
import { dirname as
|
|
29220
|
-
import { z as
|
|
29035
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync27, readFileSync as readFileSync25, renameSync as renameSync14, writeFileSync as writeFileSync21 } from "fs";
|
|
29036
|
+
import { dirname as dirname19 } from "path";
|
|
29037
|
+
import { z as z14 } from "zod";
|
|
29221
29038
|
|
|
29222
29039
|
class McpManifest {
|
|
29223
29040
|
file;
|
|
@@ -29260,7 +29077,7 @@ class McpManifest {
|
|
|
29260
29077
|
return this.entries.get(key)?.enabled ?? false;
|
|
29261
29078
|
}
|
|
29262
29079
|
load() {
|
|
29263
|
-
if (!
|
|
29080
|
+
if (!existsSync31(this.file))
|
|
29264
29081
|
return;
|
|
29265
29082
|
let parsed;
|
|
29266
29083
|
try {
|
|
@@ -29276,7 +29093,7 @@ class McpManifest {
|
|
|
29276
29093
|
}
|
|
29277
29094
|
}
|
|
29278
29095
|
save() {
|
|
29279
|
-
mkdirSync27(
|
|
29096
|
+
mkdirSync27(dirname19(this.file), { recursive: true });
|
|
29280
29097
|
const payload = {
|
|
29281
29098
|
version: 1,
|
|
29282
29099
|
servers: [...this.entries.values()]
|
|
@@ -29291,13 +29108,13 @@ var manifestEntrySchema, manifestFileSchema;
|
|
|
29291
29108
|
var init_manifest = __esm(() => {
|
|
29292
29109
|
init_paths();
|
|
29293
29110
|
init_spec();
|
|
29294
|
-
manifestEntrySchema =
|
|
29111
|
+
manifestEntrySchema = z14.object({
|
|
29295
29112
|
spec: mcpServerSpecSchema,
|
|
29296
|
-
enabled:
|
|
29113
|
+
enabled: z14.boolean()
|
|
29297
29114
|
});
|
|
29298
|
-
manifestFileSchema =
|
|
29299
|
-
version:
|
|
29300
|
-
servers:
|
|
29115
|
+
manifestFileSchema = z14.object({
|
|
29116
|
+
version: z14.literal(1),
|
|
29117
|
+
servers: z14.array(manifestEntrySchema)
|
|
29301
29118
|
});
|
|
29302
29119
|
});
|
|
29303
29120
|
|
|
@@ -29310,7 +29127,7 @@ function sanitizePathComponent(name) {
|
|
|
29310
29127
|
}
|
|
29311
29128
|
|
|
29312
29129
|
// ../../packages/mcp-host/src/manager.ts
|
|
29313
|
-
import { execFileSync as execFileSync8, spawn as
|
|
29130
|
+
import { execFileSync as execFileSync8, spawn as spawn7 } from "child_process";
|
|
29314
29131
|
import {
|
|
29315
29132
|
mkdirSync as mkdirSync28,
|
|
29316
29133
|
readdirSync as readdirSync12,
|
|
@@ -29319,8 +29136,8 @@ import {
|
|
|
29319
29136
|
unlinkSync as unlinkSync21,
|
|
29320
29137
|
writeFileSync as writeFileSync22
|
|
29321
29138
|
} from "fs";
|
|
29322
|
-
import { connect, createServer as
|
|
29323
|
-
import { join as
|
|
29139
|
+
import { connect, createServer as createServer4 } from "net";
|
|
29140
|
+
import { join as join40 } from "path";
|
|
29324
29141
|
function regKey(key, instanceKey) {
|
|
29325
29142
|
return `${key}\x00${instanceKey}`;
|
|
29326
29143
|
}
|
|
@@ -29336,16 +29153,16 @@ function resolveInstanceKey(spec, instanceKey) {
|
|
|
29336
29153
|
return instanceKey;
|
|
29337
29154
|
}
|
|
29338
29155
|
function canBind(port) {
|
|
29339
|
-
return new Promise((
|
|
29340
|
-
const srv =
|
|
29341
|
-
srv.once("error", () =>
|
|
29156
|
+
return new Promise((resolve22) => {
|
|
29157
|
+
const srv = createServer4();
|
|
29158
|
+
srv.once("error", () => resolve22(false));
|
|
29342
29159
|
srv.listen({ port, host: "127.0.0.1", exclusive: true }, () => {
|
|
29343
|
-
srv.close(() =>
|
|
29160
|
+
srv.close(() => resolve22(true));
|
|
29344
29161
|
});
|
|
29345
29162
|
});
|
|
29346
29163
|
}
|
|
29347
29164
|
function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
|
|
29348
|
-
return new Promise((
|
|
29165
|
+
return new Promise((resolve22) => {
|
|
29349
29166
|
const sock = connect({ port, host: "127.0.0.1" });
|
|
29350
29167
|
let settled = false;
|
|
29351
29168
|
const done = (ok3) => {
|
|
@@ -29353,7 +29170,7 @@ function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
|
|
|
29353
29170
|
return;
|
|
29354
29171
|
settled = true;
|
|
29355
29172
|
sock.destroy();
|
|
29356
|
-
|
|
29173
|
+
resolve22(ok3);
|
|
29357
29174
|
};
|
|
29358
29175
|
sock.once("connect", () => done(true));
|
|
29359
29176
|
sock.once("error", () => done(false));
|
|
@@ -29381,12 +29198,12 @@ function killProcessTree(pid) {
|
|
|
29381
29198
|
function waitExit(proc, timeoutMs) {
|
|
29382
29199
|
if (proc.exitCode !== null || proc.signalCode !== null)
|
|
29383
29200
|
return Promise.resolve(true);
|
|
29384
|
-
return new Promise((
|
|
29385
|
-
const timer = setTimeout(() =>
|
|
29201
|
+
return new Promise((resolve22) => {
|
|
29202
|
+
const timer = setTimeout(() => resolve22(false), timeoutMs);
|
|
29386
29203
|
timer.unref?.();
|
|
29387
29204
|
const settle = () => {
|
|
29388
29205
|
clearTimeout(timer);
|
|
29389
|
-
|
|
29206
|
+
resolve22(true);
|
|
29390
29207
|
};
|
|
29391
29208
|
proc.once("exit", settle);
|
|
29392
29209
|
proc.once("error", settle);
|
|
@@ -29520,7 +29337,7 @@ class McpHost {
|
|
|
29520
29337
|
let spawnFailed = false;
|
|
29521
29338
|
let proc;
|
|
29522
29339
|
try {
|
|
29523
|
-
proc =
|
|
29340
|
+
proc = spawn7(spec.command, args, {
|
|
29524
29341
|
stdio: "ignore",
|
|
29525
29342
|
detached: false,
|
|
29526
29343
|
cwd: spec.cwd,
|
|
@@ -29622,7 +29439,7 @@ class McpHost {
|
|
|
29622
29439
|
if (file.includes(".tmp-"))
|
|
29623
29440
|
continue;
|
|
29624
29441
|
try {
|
|
29625
|
-
const port = Number.parseInt(readFileSync26(
|
|
29442
|
+
const port = Number.parseInt(readFileSync26(join40(this.portsDir, file), "utf8").trim(), 10);
|
|
29626
29443
|
if (!Number.isNaN(port))
|
|
29627
29444
|
claims.set(file, port);
|
|
29628
29445
|
} catch {}
|
|
@@ -29632,7 +29449,7 @@ class McpHost {
|
|
|
29632
29449
|
writePortFile(fileName, port) {
|
|
29633
29450
|
try {
|
|
29634
29451
|
mkdirSync28(this.portsDir, { recursive: true });
|
|
29635
|
-
const file =
|
|
29452
|
+
const file = join40(this.portsDir, fileName);
|
|
29636
29453
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
29637
29454
|
writeFileSync22(tmp, String(port));
|
|
29638
29455
|
renameSync15(tmp, file);
|
|
@@ -29642,7 +29459,7 @@ class McpHost {
|
|
|
29642
29459
|
}
|
|
29643
29460
|
deletePortFile(fileName) {
|
|
29644
29461
|
try {
|
|
29645
|
-
unlinkSync21(
|
|
29462
|
+
unlinkSync21(join40(this.portsDir, fileName));
|
|
29646
29463
|
} catch {}
|
|
29647
29464
|
}
|
|
29648
29465
|
cleanupRunning(rkey, inst) {
|
|
@@ -29729,7 +29546,7 @@ var DEFAULT_READY_TIMEOUT_MS = 20000, READY_POLL_MS = 150, KILL_GRACE_MS = 5000,
|
|
|
29729
29546
|
fn(`[mcp-host] ${msg}`, ctx);
|
|
29730
29547
|
else
|
|
29731
29548
|
fn(`[mcp-host] ${msg}`);
|
|
29732
|
-
}, delay2 = (ms) => new Promise((
|
|
29549
|
+
}, delay2 = (ms) => new Promise((resolve22) => setTimeout(resolve22, ms));
|
|
29733
29550
|
var init_manager3 = __esm(() => {
|
|
29734
29551
|
init_manifest();
|
|
29735
29552
|
init_paths();
|
|
@@ -29747,14 +29564,14 @@ var init_src3 = __esm(() => {
|
|
|
29747
29564
|
import { randomUUID as randomUUID24 } from "crypto";
|
|
29748
29565
|
import {
|
|
29749
29566
|
copyFileSync as copyFileSync4,
|
|
29750
|
-
existsSync as
|
|
29567
|
+
existsSync as existsSync32,
|
|
29751
29568
|
mkdirSync as mkdirSync29,
|
|
29752
29569
|
readFileSync as readFileSync27,
|
|
29753
29570
|
rmSync as rmSync9,
|
|
29754
29571
|
statSync as statSync18,
|
|
29755
29572
|
writeFileSync as writeFileSync23
|
|
29756
29573
|
} from "fs";
|
|
29757
|
-
import { basename as basename10, extname as extname4, join as
|
|
29574
|
+
import { basename as basename10, extname as extname4, join as join41 } from "path";
|
|
29758
29575
|
function safeExtension(filename) {
|
|
29759
29576
|
const extension = extname4(basename10(filename));
|
|
29760
29577
|
return /^\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : "";
|
|
@@ -29772,14 +29589,14 @@ function contentDisposition(filename) {
|
|
|
29772
29589
|
|
|
29773
29590
|
class NodeFileStore {
|
|
29774
29591
|
uploadDir;
|
|
29775
|
-
constructor(uploadDir =
|
|
29592
|
+
constructor(uploadDir = join41(DATA_DIR, "uploads")) {
|
|
29776
29593
|
this.uploadDir = uploadDir;
|
|
29777
29594
|
}
|
|
29778
29595
|
#ensureDir() {
|
|
29779
29596
|
mkdirSync29(this.uploadDir, { recursive: true });
|
|
29780
29597
|
}
|
|
29781
29598
|
#metadataPath(fileId) {
|
|
29782
|
-
return
|
|
29599
|
+
return join41(this.uploadDir, `${fileId}.meta.json`);
|
|
29783
29600
|
}
|
|
29784
29601
|
#metadata(fileId) {
|
|
29785
29602
|
if (!FILE_ID_RE.test(fileId))
|
|
@@ -29808,7 +29625,7 @@ class NodeFileStore {
|
|
|
29808
29625
|
hooks = {
|
|
29809
29626
|
resolveAttachmentByFileId: (fileId) => {
|
|
29810
29627
|
const metadata = this.#metadata(fileId);
|
|
29811
|
-
if (!metadata || !
|
|
29628
|
+
if (!metadata || !existsSync32(join41(this.uploadDir, metadata.savedName)))
|
|
29812
29629
|
return null;
|
|
29813
29630
|
return this.#attachment(fileId, metadata);
|
|
29814
29631
|
},
|
|
@@ -29816,8 +29633,8 @@ class NodeFileStore {
|
|
|
29816
29633
|
const metadata = this.#metadata(fileId);
|
|
29817
29634
|
if (!metadata)
|
|
29818
29635
|
return null;
|
|
29819
|
-
const path =
|
|
29820
|
-
return
|
|
29636
|
+
const path = join41(this.uploadDir, metadata.savedName);
|
|
29637
|
+
return existsSync32(path) ? path : null;
|
|
29821
29638
|
},
|
|
29822
29639
|
storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),
|
|
29823
29640
|
deleteFilesForTopic: (topicId) => this.deleteForTopic(topicId)
|
|
@@ -29831,11 +29648,11 @@ class NodeFileStore {
|
|
|
29831
29648
|
const mimeType = file.type || MIME_BY_EXT2[extension] || "application/octet-stream";
|
|
29832
29649
|
const existing = this.#metadata(fileId);
|
|
29833
29650
|
if (existing) {
|
|
29834
|
-
const matches = existing.filename === filename && existing.mimeType === mimeType && existing.sizeBytes === file.size && existing.ownerUserId === access.ownerUserId && existing.topicId === access.topicId &&
|
|
29651
|
+
const matches = existing.filename === filename && existing.mimeType === mimeType && existing.sizeBytes === file.size && existing.ownerUserId === access.ownerUserId && existing.topicId === access.topicId && existsSync32(join41(this.uploadDir, existing.savedName));
|
|
29835
29652
|
return matches ? this.#attachment(fileId, existing) : null;
|
|
29836
29653
|
}
|
|
29837
29654
|
const savedName = `${fileId}${extension}`;
|
|
29838
|
-
const savedPath =
|
|
29655
|
+
const savedPath = join41(this.uploadDir, savedName);
|
|
29839
29656
|
try {
|
|
29840
29657
|
const sizeBytes = await Bun.write(savedPath, file);
|
|
29841
29658
|
if (sizeBytes !== file.size)
|
|
@@ -29860,14 +29677,14 @@ class NodeFileStore {
|
|
|
29860
29677
|
}
|
|
29861
29678
|
allows(fileId, access) {
|
|
29862
29679
|
const metadata = this.#metadata(fileId);
|
|
29863
|
-
return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId &&
|
|
29680
|
+
return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId && existsSync32(join41(this.uploadDir, metadata.savedName)));
|
|
29864
29681
|
}
|
|
29865
29682
|
store(absPath, access = {}) {
|
|
29866
29683
|
this.#ensureDir();
|
|
29867
29684
|
const fileId = randomUUID24();
|
|
29868
29685
|
const extension = safeExtension(absPath);
|
|
29869
29686
|
const savedName = `${fileId}${extension}`;
|
|
29870
|
-
const savedPath =
|
|
29687
|
+
const savedPath = join41(this.uploadDir, savedName);
|
|
29871
29688
|
try {
|
|
29872
29689
|
const stats = statSync18(absPath);
|
|
29873
29690
|
if (!stats.isFile() || stats.size > MAX_NODE_UPLOAD_BYTES)
|
|
@@ -29899,8 +29716,8 @@ class NodeFileStore {
|
|
|
29899
29716
|
const allowed = metadata.visibility === "workspace" || metadata.ownerUserId === userId || Boolean(topic && isParticipant(topic, userId));
|
|
29900
29717
|
if (!allowed)
|
|
29901
29718
|
return null;
|
|
29902
|
-
const path =
|
|
29903
|
-
if (!
|
|
29719
|
+
const path = join41(this.uploadDir, metadata.savedName);
|
|
29720
|
+
if (!existsSync32(path))
|
|
29904
29721
|
return null;
|
|
29905
29722
|
return new Response(Bun.file(path), {
|
|
29906
29723
|
headers: {
|
|
@@ -29918,7 +29735,7 @@ class NodeFileStore {
|
|
|
29918
29735
|
const metadata = this.#metadata(fileId);
|
|
29919
29736
|
if (metadata?.topicId !== topicId)
|
|
29920
29737
|
continue;
|
|
29921
|
-
rmSync9(
|
|
29738
|
+
rmSync9(join41(this.uploadDir, metadata.savedName), { force: true });
|
|
29922
29739
|
rmSync9(this.#metadataPath(fileId), { force: true });
|
|
29923
29740
|
}
|
|
29924
29741
|
}
|
|
@@ -30070,14 +29887,14 @@ function createPollingSseStream(req, options) {
|
|
|
30070
29887
|
import { randomUUID as randomUUID25, timingSafeEqual as timingSafeEqual4 } from "crypto";
|
|
30071
29888
|
import {
|
|
30072
29889
|
chmodSync as chmodSync6,
|
|
30073
|
-
existsSync as
|
|
29890
|
+
existsSync as existsSync33,
|
|
30074
29891
|
mkdirSync as mkdirSync30,
|
|
30075
29892
|
readFileSync as readFileSync28,
|
|
30076
29893
|
renameSync as renameSync16,
|
|
30077
29894
|
unlinkSync as unlinkSync22,
|
|
30078
29895
|
writeFileSync as writeFileSync24
|
|
30079
29896
|
} from "fs";
|
|
30080
|
-
import { dirname as
|
|
29897
|
+
import { dirname as dirname20, resolve as resolve22 } from "path";
|
|
30081
29898
|
function jsonError(status, error2) {
|
|
30082
29899
|
return Response.json({ ok: false, error: error2 }, { status });
|
|
30083
29900
|
}
|
|
@@ -30906,7 +30723,7 @@ function writeNodeDaemonInfo(port, startedAt) {
|
|
|
30906
30723
|
stateDir: STATE_DIR,
|
|
30907
30724
|
startedAt
|
|
30908
30725
|
};
|
|
30909
|
-
mkdirSync30(
|
|
30726
|
+
mkdirSync30(dirname20(NODE_DAEMON_INFO_PATH), { recursive: true });
|
|
30910
30727
|
const temporary = `${NODE_DAEMON_INFO_PATH}.${process.pid}.${randomUUID25()}.tmp`;
|
|
30911
30728
|
writeFileSync24(temporary, `${JSON.stringify(info, null, 2)}
|
|
30912
30729
|
`, { mode: 384 });
|
|
@@ -30916,7 +30733,7 @@ function writeNodeDaemonInfo(port, startedAt) {
|
|
|
30916
30733
|
return info;
|
|
30917
30734
|
}
|
|
30918
30735
|
function readNodeDaemonInfo() {
|
|
30919
|
-
if (!
|
|
30736
|
+
if (!existsSync33(NODE_DAEMON_INFO_PATH))
|
|
30920
30737
|
return null;
|
|
30921
30738
|
try {
|
|
30922
30739
|
const value = JSON.parse(readFileSync28(NODE_DAEMON_INFO_PATH, "utf8"));
|
|
@@ -31001,7 +30818,7 @@ var init_control = __esm(async () => {
|
|
|
31001
30818
|
await init_node_host();
|
|
31002
30819
|
await init_files();
|
|
31003
30820
|
NODE_RUNTIME_CONTRACT_BASE_PATH = `${NODE_CONTROL_BASE_PATH}/runtime/v1`;
|
|
31004
|
-
NODE_DAEMON_INFO_PATH =
|
|
30821
|
+
NODE_DAEMON_INFO_PATH = resolve22(RUN_DIR, "node-daemon.json");
|
|
31005
30822
|
NODE_VERSION = NEGOTIUM_VERSION;
|
|
31006
30823
|
ControlRequestError = class ControlRequestError extends Error {
|
|
31007
30824
|
constructor(message) {
|
|
@@ -31202,9 +31019,9 @@ var init_schedule = __esm(() => {
|
|
|
31202
31019
|
});
|
|
31203
31020
|
|
|
31204
31021
|
// ../../packages/module-cron/src/scripts.ts
|
|
31205
|
-
import { spawn as
|
|
31206
|
-
import { existsSync as
|
|
31207
|
-
import { resolve as
|
|
31022
|
+
import { spawn as spawn8 } from "child_process";
|
|
31023
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync31, readdirSync as readdirSync13 } from "fs";
|
|
31024
|
+
import { resolve as resolve23, sep as sep2 } from "path";
|
|
31208
31025
|
function validateCronScriptName(script) {
|
|
31209
31026
|
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*\.py$/.test(script) || script.includes("..") || script.includes("/") || script.includes("\\")) {
|
|
31210
31027
|
return { ok: false, error: `script must be a plain .py filename under ${CRON_JOBS_DIR}` };
|
|
@@ -31215,7 +31032,7 @@ function resolveCronScriptPath(script) {
|
|
|
31215
31032
|
const valid = validateCronScriptName(script);
|
|
31216
31033
|
if (!valid.ok)
|
|
31217
31034
|
throw new Error(valid.error);
|
|
31218
|
-
const path =
|
|
31035
|
+
const path = resolve23(CRON_JOBS_DIR, script);
|
|
31219
31036
|
if (!path.startsWith(`${CRON_JOBS_DIR}${sep2}`))
|
|
31220
31037
|
throw new Error("script path escaped jobs directory");
|
|
31221
31038
|
return path;
|
|
@@ -31226,7 +31043,7 @@ function listCronScripts() {
|
|
|
31226
31043
|
}
|
|
31227
31044
|
function cronScriptExists(script) {
|
|
31228
31045
|
try {
|
|
31229
|
-
return
|
|
31046
|
+
return existsSync34(resolveCronScriptPath(script));
|
|
31230
31047
|
} catch {
|
|
31231
31048
|
return false;
|
|
31232
31049
|
}
|
|
@@ -31258,14 +31075,14 @@ async function runCronPromptScript(options) {
|
|
|
31258
31075
|
if (options.signal?.aborted)
|
|
31259
31076
|
throw new CronScriptError("script run aborted");
|
|
31260
31077
|
const scriptPath = resolveCronScriptPath(options.script);
|
|
31261
|
-
if (!
|
|
31078
|
+
if (!existsSync34(scriptPath))
|
|
31262
31079
|
throw new CronScriptError(`cron script not found: ${options.script}`);
|
|
31263
31080
|
const timeoutMs = Math.max(5000, options.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS);
|
|
31264
31081
|
const outputLimitBytes = Math.max(1024, options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES);
|
|
31265
31082
|
const command = commandForScript(scriptPath);
|
|
31266
31083
|
let child;
|
|
31267
31084
|
try {
|
|
31268
|
-
child =
|
|
31085
|
+
child = spawn8(command.executable, command.args, {
|
|
31269
31086
|
cwd: options.cwd,
|
|
31270
31087
|
detached: true,
|
|
31271
31088
|
env: {
|
|
@@ -31331,7 +31148,7 @@ async function runCronPromptScript(options) {
|
|
|
31331
31148
|
var CRON_JOBS_DIR, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_OUTPUT_LIMIT_BYTES, KILL_GRACE_MS2 = 2500, CronScriptError;
|
|
31332
31149
|
var init_scripts = __esm(async () => {
|
|
31333
31150
|
await init_cron_host();
|
|
31334
|
-
CRON_JOBS_DIR =
|
|
31151
|
+
CRON_JOBS_DIR = resolve23(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve23(WORKSPACE_DIR, "cron", "jobs"));
|
|
31335
31152
|
DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 60000;
|
|
31336
31153
|
DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;
|
|
31337
31154
|
CronScriptError = class CronScriptError extends Error {
|
|
@@ -32451,8 +32268,8 @@ var init_scheduler = __esm(async () => {
|
|
|
32451
32268
|
});
|
|
32452
32269
|
|
|
32453
32270
|
// ../../packages/module-cron/src/module.ts
|
|
32454
|
-
import { existsSync as
|
|
32455
|
-
import { fileURLToPath as
|
|
32271
|
+
import { existsSync as existsSync35 } from "fs";
|
|
32272
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
32456
32273
|
function envMs(name, fallback) {
|
|
32457
32274
|
const parsed = Number.parseInt(process.env[name] ?? "", 10);
|
|
32458
32275
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -32482,7 +32299,7 @@ function createCronModule(options = {}) {
|
|
|
32482
32299
|
if (event.type === "topic-deleted")
|
|
32483
32300
|
trackCleanup(resetCronTopicContext(event.topicId));
|
|
32484
32301
|
});
|
|
32485
|
-
const mcpServerFile = options.mcpServerFile ?? (
|
|
32302
|
+
const mcpServerFile = options.mcpServerFile ?? (existsSync35(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
|
|
32486
32303
|
const unregisterMcp = host2.registerRuntimeMcpServer("cron-manager", {
|
|
32487
32304
|
scopes: ["forum", "manager"],
|
|
32488
32305
|
forumRequired: true,
|
|
@@ -32575,8 +32392,8 @@ var init_module = __esm(async () => {
|
|
|
32575
32392
|
await init_scheduler();
|
|
32576
32393
|
await init_scripts();
|
|
32577
32394
|
await init_store();
|
|
32578
|
-
SOURCE_MCP_SERVER_FILE =
|
|
32579
|
-
PACKAGED_MCP_SERVER_FILE =
|
|
32395
|
+
SOURCE_MCP_SERVER_FILE = fileURLToPath3(new URL("./mcp-server.ts", import.meta.url));
|
|
32396
|
+
PACKAGED_MCP_SERVER_FILE = fileURLToPath3(new URL("./runtime/cron/mcp-server.ts", import.meta.url));
|
|
32580
32397
|
});
|
|
32581
32398
|
|
|
32582
32399
|
// ../../packages/module-cron/src/summarize.ts
|
|
@@ -32762,10 +32579,10 @@ __export(exports_src3, {
|
|
|
32762
32579
|
NODE_CONTROL_PROTOCOL_VERSION: () => NODE_CONTROL_PROTOCOL_VERSION,
|
|
32763
32580
|
NODE_CONTROL_BASE_PATH: () => NODE_CONTROL_BASE_PATH
|
|
32764
32581
|
});
|
|
32765
|
-
import { createServer as
|
|
32582
|
+
import { createServer as createServer5 } from "net";
|
|
32766
32583
|
async function availableLoopbackPort() {
|
|
32767
|
-
return new Promise((
|
|
32768
|
-
const probe =
|
|
32584
|
+
return new Promise((resolve24, reject) => {
|
|
32585
|
+
const probe = createServer5();
|
|
32769
32586
|
probe.unref();
|
|
32770
32587
|
probe.once("error", reject);
|
|
32771
32588
|
probe.listen(0, "127.0.0.1", () => {
|
|
@@ -32775,7 +32592,7 @@ async function availableLoopbackPort() {
|
|
|
32775
32592
|
if (error2)
|
|
32776
32593
|
reject(error2);
|
|
32777
32594
|
else if (port > 0)
|
|
32778
|
-
|
|
32595
|
+
resolve24(port);
|
|
32779
32596
|
else
|
|
32780
32597
|
reject(new Error("failed to allocate a loopback port"));
|
|
32781
32598
|
});
|
|
@@ -32914,8 +32731,8 @@ function startNode(opts = {}) {
|
|
|
32914
32731
|
const manifest = new McpManifest;
|
|
32915
32732
|
const stopSweeper = mcpHost.startSweeper();
|
|
32916
32733
|
let resolveCompleted;
|
|
32917
|
-
const completed = new Promise((
|
|
32918
|
-
resolveCompleted =
|
|
32734
|
+
const completed = new Promise((resolve24) => {
|
|
32735
|
+
resolveCompleted = resolve24;
|
|
32919
32736
|
});
|
|
32920
32737
|
let advertised = null;
|
|
32921
32738
|
try {
|
|
@@ -32994,7 +32811,7 @@ async function runNodeDaemon(opts = {}) {
|
|
|
32994
32811
|
singleton: true
|
|
32995
32812
|
});
|
|
32996
32813
|
await node.completed;
|
|
32997
|
-
await new Promise((
|
|
32814
|
+
await new Promise((resolve24) => setImmediate(resolve24));
|
|
32998
32815
|
process.exit(0);
|
|
32999
32816
|
}
|
|
33000
32817
|
var init_src5 = __esm(async () => {
|
|
@@ -34040,9 +33857,9 @@ var init_context_usage = __esm(async () => {
|
|
|
34040
33857
|
|
|
34041
33858
|
// ../../adapters/terminal/src/path-suggest.ts
|
|
34042
33859
|
import { execFile as execFile3 } from "child_process";
|
|
34043
|
-
import { existsSync as
|
|
33860
|
+
import { existsSync as existsSync36, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
|
|
34044
33861
|
import { homedir as homedir10 } from "os";
|
|
34045
|
-
import { basename as basename11, dirname as
|
|
33862
|
+
import { basename as basename11, dirname as dirname21, join as join42, sep as sep3 } from "path";
|
|
34046
33863
|
import { promisify as promisify3 } from "util";
|
|
34047
33864
|
function indexRecursivePaths(files) {
|
|
34048
33865
|
const paths = [];
|
|
@@ -34143,15 +33960,15 @@ function resolveFragment(frag) {
|
|
|
34143
33960
|
else if (frag === "~/")
|
|
34144
33961
|
path = home;
|
|
34145
33962
|
else if (frag.startsWith("~/"))
|
|
34146
|
-
path =
|
|
33963
|
+
path = join42(home, frag.slice(2));
|
|
34147
33964
|
else if (frag.startsWith("/"))
|
|
34148
33965
|
path = frag;
|
|
34149
33966
|
else
|
|
34150
|
-
path =
|
|
33967
|
+
path = join42(home, frag);
|
|
34151
33968
|
if (frag.endsWith("/") || frag === "" || frag === "~") {
|
|
34152
33969
|
return { dir: path, prefix: "" };
|
|
34153
33970
|
}
|
|
34154
|
-
return { dir:
|
|
33971
|
+
return { dir: dirname21(path), prefix: basename11(path) };
|
|
34155
33972
|
}
|
|
34156
33973
|
function toToken(fullPath, isDir) {
|
|
34157
33974
|
const home = homedir10();
|
|
@@ -34176,7 +33993,7 @@ function rankAndSlice(dir, candidates) {
|
|
|
34176
33993
|
});
|
|
34177
33994
|
return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
|
|
34178
33995
|
label: `${relPath}${isDir ? "/" : ""}`,
|
|
34179
|
-
value: toToken(
|
|
33996
|
+
value: toToken(join42(dir, relPath), isDir),
|
|
34180
33997
|
isDir
|
|
34181
33998
|
}));
|
|
34182
33999
|
}
|
|
@@ -34255,7 +34072,7 @@ function pathSuggestions(lineText, col) {
|
|
|
34255
34072
|
let isDir = entry.isDirectory();
|
|
34256
34073
|
if (!isDir && entry.isSymbolicLink()) {
|
|
34257
34074
|
try {
|
|
34258
|
-
isDir = statSync19(
|
|
34075
|
+
isDir = statSync19(join42(dir, entry.name)).isDirectory();
|
|
34259
34076
|
} catch {}
|
|
34260
34077
|
}
|
|
34261
34078
|
return {
|
|
@@ -34282,14 +34099,14 @@ function fragmentToAbsolutePath(frag) {
|
|
|
34282
34099
|
if (frag === "~" || frag === "~/")
|
|
34283
34100
|
return home;
|
|
34284
34101
|
if (frag.startsWith("~/"))
|
|
34285
|
-
return
|
|
34102
|
+
return join42(home, frag.slice(2));
|
|
34286
34103
|
if (frag.startsWith("/"))
|
|
34287
34104
|
return frag;
|
|
34288
|
-
return
|
|
34105
|
+
return join42(home, frag);
|
|
34289
34106
|
}
|
|
34290
34107
|
function fragmentResolves(frag) {
|
|
34291
34108
|
try {
|
|
34292
|
-
return
|
|
34109
|
+
return existsSync36(fragmentToAbsolutePath(frag));
|
|
34293
34110
|
} catch {
|
|
34294
34111
|
return false;
|
|
34295
34112
|
}
|
|
@@ -38193,8 +38010,8 @@ class TerminalApp {
|
|
|
38193
38010
|
this.#refreshBackgroundSessions();
|
|
38194
38011
|
}, 1000);
|
|
38195
38012
|
this.#backgroundRefreshTimer.unref?.();
|
|
38196
|
-
await new Promise((
|
|
38197
|
-
this.#finishRun =
|
|
38013
|
+
await new Promise((resolve24) => {
|
|
38014
|
+
this.#finishRun = resolve24;
|
|
38198
38015
|
});
|
|
38199
38016
|
} finally {
|
|
38200
38017
|
this.#running = false;
|
|
@@ -40791,7 +40608,7 @@ var init_commands2 = __esm(async () => {
|
|
|
40791
40608
|
// ../../adapters/telegram/src/mapping-store.ts
|
|
40792
40609
|
import { Database as Database2 } from "bun:sqlite";
|
|
40793
40610
|
import { mkdirSync as mkdirSync32 } from "fs";
|
|
40794
|
-
import { dirname as
|
|
40611
|
+
import { dirname as dirname22, join as join43 } from "path";
|
|
40795
40612
|
function outboxRowToEntry(row) {
|
|
40796
40613
|
return {
|
|
40797
40614
|
id: row.id,
|
|
@@ -40857,9 +40674,9 @@ function migrateOutboxSchema(db4) {
|
|
|
40857
40674
|
db4.run("CREATE INDEX IF NOT EXISTS idx_telegram_outbox_runtime_message ON outbox(runtime_message_id)");
|
|
40858
40675
|
}
|
|
40859
40676
|
function openMappingStore(path) {
|
|
40860
|
-
const dbPath = path ??
|
|
40677
|
+
const dbPath = path ?? join43(DATA_DIR, "adapter-telegram.db");
|
|
40861
40678
|
if (dbPath !== ":memory:")
|
|
40862
|
-
mkdirSync32(
|
|
40679
|
+
mkdirSync32(dirname22(dbPath), { recursive: true });
|
|
40863
40680
|
const db4 = new Database2(dbPath);
|
|
40864
40681
|
migrateLegacySchema(db4);
|
|
40865
40682
|
createTables(db4);
|
|
@@ -41094,8 +40911,8 @@ ${caption}` : voiceText : caption;
|
|
|
41094
40911
|
return;
|
|
41095
40912
|
}
|
|
41096
40913
|
let release;
|
|
41097
|
-
const ready = new Promise((
|
|
41098
|
-
release =
|
|
40914
|
+
const ready = new Promise((resolve24) => {
|
|
40915
|
+
release = resolve24;
|
|
41099
40916
|
});
|
|
41100
40917
|
const entry = {
|
|
41101
40918
|
messages: [msg],
|
|
@@ -41217,7 +41034,7 @@ function renderOutbound(text2, maxLen = 4096) {
|
|
|
41217
41034
|
}
|
|
41218
41035
|
|
|
41219
41036
|
// ../../adapters/telegram/src/adapter.ts
|
|
41220
|
-
import { existsSync as
|
|
41037
|
+
import { existsSync as existsSync37 } from "fs";
|
|
41221
41038
|
import { basename as basename12, extname as extname5 } from "path";
|
|
41222
41039
|
function startTelegramAdapter(opts) {
|
|
41223
41040
|
const { client, forumChatId } = opts;
|
|
@@ -41328,7 +41145,7 @@ function startTelegramAdapter(opts) {
|
|
|
41328
41145
|
attempt += 1;
|
|
41329
41146
|
const waitMs = Math.min(5000, 250 * 2 ** Math.min(attempt - 1, 5));
|
|
41330
41147
|
logger.warn({ err: err2, attempt, waitMs }, "telegram adapter: getMe failed while resolving onboarding identity; retrying");
|
|
41331
|
-
await new Promise((
|
|
41148
|
+
await new Promise((resolve24) => setTimeout(resolve24, waitMs));
|
|
41332
41149
|
}
|
|
41333
41150
|
}
|
|
41334
41151
|
return;
|
|
@@ -41715,7 +41532,7 @@ function startTelegramAdapter(opts) {
|
|
|
41715
41532
|
logger.warn({ path, chatId }, "telegram adapter: blocked sensitive file path");
|
|
41716
41533
|
return { ref: null, delivered: false, error: "path matches the sensitive-file blacklist" };
|
|
41717
41534
|
}
|
|
41718
|
-
if (!
|
|
41535
|
+
if (!existsSync37(path)) {
|
|
41719
41536
|
const sent = await client.sendMessage(chatId, `File: ${path}`, base).catch(() => null);
|
|
41720
41537
|
const messageId = sentMessageId(sent);
|
|
41721
41538
|
return {
|
|
@@ -41815,14 +41632,14 @@ function startTelegramAdapter(opts) {
|
|
|
41815
41632
|
const sendQueues = new Map;
|
|
41816
41633
|
function enqueueSend(topicId, task) {
|
|
41817
41634
|
const prev = sendQueues.get(topicId) ?? Promise.resolve();
|
|
41818
|
-
const next = prev.then(() => new Promise((
|
|
41635
|
+
const next = prev.then(() => new Promise((resolve24) => {
|
|
41819
41636
|
const timer = setTimeout(() => {
|
|
41820
41637
|
logger.warn({ topicId, sendTimeoutMs }, "telegram adapter: send timed out \u2014 abandoning it and continuing the queue");
|
|
41821
|
-
|
|
41638
|
+
resolve24();
|
|
41822
41639
|
}, sendTimeoutMs);
|
|
41823
41640
|
task().catch((err2) => logger.warn({ err: err2, topicId }, "telegram adapter: send task failed")).finally(() => {
|
|
41824
41641
|
clearTimeout(timer);
|
|
41825
|
-
|
|
41642
|
+
resolve24();
|
|
41826
41643
|
});
|
|
41827
41644
|
}));
|
|
41828
41645
|
sendQueues.set(topicId, next);
|
|
@@ -42068,7 +41885,7 @@ ${suffix}`;
|
|
|
42068
41885
|
continue;
|
|
42069
41886
|
}
|
|
42070
41887
|
if (retryDelayMs > 0) {
|
|
42071
|
-
await new Promise((
|
|
41888
|
+
await new Promise((resolve24) => setTimeout(resolve24, retryDelayMs));
|
|
42072
41889
|
}
|
|
42073
41890
|
try {
|
|
42074
41891
|
await client.editMessageText(text2, editOptions);
|
|
@@ -42709,8 +42526,8 @@ async function runTelegramCli(args = process.argv.slice(2)) {
|
|
|
42709
42526
|
onShutdown("telegram-channel", 100, () => channel.stop());
|
|
42710
42527
|
onShutdown("telegram-singleton", 90, () => singleton.stop());
|
|
42711
42528
|
let resolveCompleted;
|
|
42712
|
-
const completed = new Promise((
|
|
42713
|
-
resolveCompleted =
|
|
42529
|
+
const completed = new Promise((resolve24) => {
|
|
42530
|
+
resolveCompleted = resolve24;
|
|
42714
42531
|
});
|
|
42715
42532
|
onShutdown("telegram-completed", -100, resolveCompleted);
|
|
42716
42533
|
process.stdout.write(`negotium Telegram adapter connected to canonical node pid ${initialNode.info?.pid ?? "unknown"}
|
|
@@ -42735,11 +42552,11 @@ var exports_join_status = {};
|
|
|
42735
42552
|
__export(exports_join_status, {
|
|
42736
42553
|
hasConfiguredOtiumJoin: () => hasConfiguredOtiumJoin
|
|
42737
42554
|
});
|
|
42738
|
-
import { existsSync as
|
|
42739
|
-
import { resolve as
|
|
42555
|
+
import { existsSync as existsSync38 } from "fs";
|
|
42556
|
+
import { resolve as resolve24 } from "path";
|
|
42740
42557
|
function hasConfiguredOtiumJoin() {
|
|
42741
42558
|
const envJoin = Boolean(process.env.OTIUM_CENTRAL_URL?.trim() && process.env.OTIUM_CELL_ID?.trim() && process.env.OTIUM_CELL_SECRET?.trim());
|
|
42742
|
-
return envJoin ||
|
|
42559
|
+
return envJoin || existsSync38(resolve24(DATA_DIR, "otium-join.json"));
|
|
42743
42560
|
}
|
|
42744
42561
|
var init_join_status = __esm(() => {
|
|
42745
42562
|
init_config_public();
|
|
@@ -42757,9 +42574,9 @@ function firstCell() {
|
|
|
42757
42574
|
return cell;
|
|
42758
42575
|
return null;
|
|
42759
42576
|
}
|
|
42760
|
-
function attachOtiumCentralCell(
|
|
42761
|
-
cells.set(
|
|
42762
|
-
join:
|
|
42577
|
+
function attachOtiumCentralCell(join44) {
|
|
42578
|
+
cells.set(join44.cellId, {
|
|
42579
|
+
join: join44,
|
|
42763
42580
|
nodesCache: null,
|
|
42764
42581
|
verifyCache: new Map,
|
|
42765
42582
|
tokenCache: new Map
|
|
@@ -42771,10 +42588,10 @@ function detachOtiumCentralCell(cellId) {
|
|
|
42771
42588
|
function attachedOtiumCells() {
|
|
42772
42589
|
return [...cells.values()].map((cell) => cell.join);
|
|
42773
42590
|
}
|
|
42774
|
-
function configureOtiumCentral(
|
|
42591
|
+
function configureOtiumCentral(join44) {
|
|
42775
42592
|
cells.clear();
|
|
42776
|
-
if (
|
|
42777
|
-
attachOtiumCentralCell(
|
|
42593
|
+
if (join44)
|
|
42594
|
+
attachOtiumCentralCell(join44);
|
|
42778
42595
|
}
|
|
42779
42596
|
function isOtiumCentralConfigured() {
|
|
42780
42597
|
return cells.size > 0;
|
|
@@ -43188,7 +43005,7 @@ import { createHash as createHash11, randomUUID as randomUUID28 } from "crypto";
|
|
|
43188
43005
|
import {
|
|
43189
43006
|
chmodSync as chmodSync7,
|
|
43190
43007
|
closeSync as closeSync5,
|
|
43191
|
-
existsSync as
|
|
43008
|
+
existsSync as existsSync39,
|
|
43192
43009
|
fsyncSync as fsyncSync2,
|
|
43193
43010
|
linkSync,
|
|
43194
43011
|
lstatSync,
|
|
@@ -43201,9 +43018,9 @@ import {
|
|
|
43201
43018
|
unlinkSync as unlinkSync23,
|
|
43202
43019
|
writeFileSync as writeFileSync25
|
|
43203
43020
|
} from "fs";
|
|
43204
|
-
import { dirname as
|
|
43021
|
+
import { dirname as dirname23, resolve as resolve25 } from "path";
|
|
43205
43022
|
function joinFilePath() {
|
|
43206
|
-
return
|
|
43023
|
+
return resolve25(DATA_DIR, "otium-join.json");
|
|
43207
43024
|
}
|
|
43208
43025
|
function isHttpUrl(value) {
|
|
43209
43026
|
return /^https?:\/\//.test(value);
|
|
@@ -43258,7 +43075,7 @@ function parseInviteCode(code) {
|
|
|
43258
43075
|
return normalizeJoin(parsed);
|
|
43259
43076
|
}
|
|
43260
43077
|
function joinLockPath() {
|
|
43261
|
-
return
|
|
43078
|
+
return resolve25(DATA_DIR, ".otium-join.lock");
|
|
43262
43079
|
}
|
|
43263
43080
|
function processIsAlive(pid) {
|
|
43264
43081
|
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
@@ -43272,9 +43089,9 @@ function processIsAlive(pid) {
|
|
|
43272
43089
|
}
|
|
43273
43090
|
function withJoinCredentialLock(operation) {
|
|
43274
43091
|
const lockPath = joinLockPath();
|
|
43275
|
-
const ownerPath =
|
|
43092
|
+
const ownerPath = resolve25(lockPath, "owner.json");
|
|
43276
43093
|
const owner = { pid: process.pid, token: randomUUID28() };
|
|
43277
|
-
mkdirSync33(
|
|
43094
|
+
mkdirSync33(dirname23(lockPath), { recursive: true });
|
|
43278
43095
|
for (let attempt = 0;; attempt += 1) {
|
|
43279
43096
|
let created = false;
|
|
43280
43097
|
try {
|
|
@@ -43338,20 +43155,20 @@ function withJoinCredentialLock(operation) {
|
|
|
43338
43155
|
function joinsEqual(left, right) {
|
|
43339
43156
|
return left.central === right.central && left.relay === right.relay && left.cellId === right.cellId && left.secret === right.secret;
|
|
43340
43157
|
}
|
|
43341
|
-
function normalizedJoin(
|
|
43158
|
+
function normalizedJoin(join44) {
|
|
43342
43159
|
return normalizeJoin({
|
|
43343
|
-
v:
|
|
43344
|
-
central:
|
|
43345
|
-
relay:
|
|
43346
|
-
cellId:
|
|
43347
|
-
secret:
|
|
43160
|
+
v: join44.v,
|
|
43161
|
+
central: join44.central,
|
|
43162
|
+
relay: join44.relay,
|
|
43163
|
+
cellId: join44.cellId,
|
|
43164
|
+
secret: join44.secret
|
|
43348
43165
|
});
|
|
43349
43166
|
}
|
|
43350
|
-
function joinCredentialDigest(
|
|
43351
|
-
return createHash11("sha256").update(JSON.stringify(normalizedJoin(
|
|
43167
|
+
function joinCredentialDigest(join44) {
|
|
43168
|
+
return createHash11("sha256").update(JSON.stringify(normalizedJoin(join44))).digest("base64url");
|
|
43352
43169
|
}
|
|
43353
43170
|
function readPersistedJoins(path = joinFilePath()) {
|
|
43354
|
-
if (!
|
|
43171
|
+
if (!existsSync39(path))
|
|
43355
43172
|
return [];
|
|
43356
43173
|
const parsed = JSON.parse(readFileSync29(path, "utf-8"));
|
|
43357
43174
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -43367,9 +43184,9 @@ function readPersistedJoins(path = joinFilePath()) {
|
|
|
43367
43184
|
return normalizeJoin(entry);
|
|
43368
43185
|
});
|
|
43369
43186
|
}
|
|
43370
|
-
function isJoinPersisted(
|
|
43187
|
+
function isJoinPersisted(join44) {
|
|
43371
43188
|
try {
|
|
43372
|
-
const normalized = normalizedJoin(
|
|
43189
|
+
const normalized = normalizedJoin(join44);
|
|
43373
43190
|
return readPersistedJoins().some((persisted) => joinsEqual(persisted, normalized));
|
|
43374
43191
|
} catch {
|
|
43375
43192
|
return false;
|
|
@@ -43385,8 +43202,8 @@ function fsyncPath(target) {
|
|
|
43385
43202
|
}
|
|
43386
43203
|
function writeJoins(joins, allowOverwrite) {
|
|
43387
43204
|
const path = joinFilePath();
|
|
43388
|
-
const directory =
|
|
43389
|
-
const temporaryPath =
|
|
43205
|
+
const directory = dirname23(path);
|
|
43206
|
+
const temporaryPath = resolve25(directory, `.otium-join.json.${process.pid}.${randomUUID28()}.tmp`);
|
|
43390
43207
|
const payload = { v: JOIN_FILE_VERSION, joins };
|
|
43391
43208
|
let fd;
|
|
43392
43209
|
try {
|
|
@@ -43407,18 +43224,18 @@ function writeJoins(joins, allowOverwrite) {
|
|
|
43407
43224
|
} catch (error2) {
|
|
43408
43225
|
if (fd !== undefined)
|
|
43409
43226
|
closeSync5(fd);
|
|
43410
|
-
if (
|
|
43227
|
+
if (existsSync39(temporaryPath))
|
|
43411
43228
|
unlinkSync23(temporaryPath);
|
|
43412
43229
|
throw error2;
|
|
43413
43230
|
}
|
|
43414
43231
|
return path;
|
|
43415
43232
|
}
|
|
43416
|
-
function saveJoinWhileLocked(
|
|
43233
|
+
function saveJoinWhileLocked(join44, options = {}) {
|
|
43417
43234
|
const path = joinFilePath();
|
|
43418
|
-
const directory =
|
|
43419
|
-
const normalized = normalizedJoin(
|
|
43235
|
+
const directory = dirname23(path);
|
|
43236
|
+
const normalized = normalizedJoin(join44);
|
|
43420
43237
|
mkdirSync33(directory, { recursive: true });
|
|
43421
|
-
if (!
|
|
43238
|
+
if (!existsSync39(path))
|
|
43422
43239
|
return writeJoins([normalized], false);
|
|
43423
43240
|
if (lstatSync(path).isSymbolicLink()) {
|
|
43424
43241
|
throw new Error(`refusing to replace symlinked Otium join file at ${path}`);
|
|
@@ -43446,13 +43263,13 @@ function saveJoinWhileLocked(join43, options = {}) {
|
|
|
43446
43263
|
const next = conflict ? existing.map((persisted) => persisted.cellId === normalized.cellId ? normalized : persisted) : [...existing, normalized];
|
|
43447
43264
|
return writeJoins(next, true);
|
|
43448
43265
|
}
|
|
43449
|
-
function saveJoin(
|
|
43450
|
-
return withJoinCredentialLock(() => saveJoinWhileLocked(
|
|
43266
|
+
function saveJoin(join44, options = {}) {
|
|
43267
|
+
return withJoinCredentialLock(() => saveJoinWhileLocked(join44, options));
|
|
43451
43268
|
}
|
|
43452
43269
|
function removeJoin(cellId) {
|
|
43453
43270
|
return withJoinCredentialLock(() => {
|
|
43454
43271
|
const path = joinFilePath();
|
|
43455
|
-
if (!
|
|
43272
|
+
if (!existsSync39(path))
|
|
43456
43273
|
return false;
|
|
43457
43274
|
if (lstatSync(path).isSymbolicLink()) {
|
|
43458
43275
|
throw new Error(`refusing to remove symlinked Otium join file at ${path}`);
|
|
@@ -43460,7 +43277,7 @@ function removeJoin(cellId) {
|
|
|
43460
43277
|
if (cellId) {
|
|
43461
43278
|
let remaining;
|
|
43462
43279
|
try {
|
|
43463
|
-
remaining = readPersistedJoins(path).filter((
|
|
43280
|
+
remaining = readPersistedJoins(path).filter((join44) => join44.cellId !== cellId);
|
|
43464
43281
|
} catch {
|
|
43465
43282
|
return false;
|
|
43466
43283
|
}
|
|
@@ -43472,7 +43289,7 @@ function removeJoin(cellId) {
|
|
|
43472
43289
|
}
|
|
43473
43290
|
}
|
|
43474
43291
|
unlinkSync23(path);
|
|
43475
|
-
fsyncPath(
|
|
43292
|
+
fsyncPath(dirname23(path));
|
|
43476
43293
|
return true;
|
|
43477
43294
|
});
|
|
43478
43295
|
}
|
|
@@ -43493,7 +43310,7 @@ function loadJoins() {
|
|
|
43493
43310
|
logger.warn("otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together \u2014 ignoring partial env");
|
|
43494
43311
|
}
|
|
43495
43312
|
const path = joinFilePath();
|
|
43496
|
-
if (!
|
|
43313
|
+
if (!existsSync39(path))
|
|
43497
43314
|
return [];
|
|
43498
43315
|
try {
|
|
43499
43316
|
return readPersistedJoins(path);
|
|
@@ -43513,7 +43330,7 @@ var init_join = __esm(async () => {
|
|
|
43513
43330
|
// ../../adapters/otium/src/peer-files.ts
|
|
43514
43331
|
import { randomUUID as randomUUID29 } from "crypto";
|
|
43515
43332
|
import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync34, rmSync as rmSync11, statSync as statSync21 } from "fs";
|
|
43516
|
-
import { basename as basename13, extname as extname6, join as
|
|
43333
|
+
import { basename as basename13, extname as extname6, join as join44 } from "path";
|
|
43517
43334
|
function fileType(mimeType) {
|
|
43518
43335
|
if (mimeType.startsWith("image/"))
|
|
43519
43336
|
return "image";
|
|
@@ -43579,7 +43396,7 @@ function recordFile(args) {
|
|
|
43579
43396
|
function insertLocalFile(args) {
|
|
43580
43397
|
const id = randomUUID29();
|
|
43581
43398
|
mkdirSync34(PEER_FILES_DIR, { recursive: true });
|
|
43582
|
-
const path =
|
|
43399
|
+
const path = join44(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
|
|
43583
43400
|
copyFileSync5(args.sourcePath, path);
|
|
43584
43401
|
return recordFile({ id, path, sizeBytes: statSync21(path).size, ...args });
|
|
43585
43402
|
}
|
|
@@ -43625,7 +43442,7 @@ function installPeerFileHooks() {
|
|
|
43625
43442
|
var PEER_FILES_DIR;
|
|
43626
43443
|
var init_peer_files = __esm(async () => {
|
|
43627
43444
|
await init_src();
|
|
43628
|
-
PEER_FILES_DIR =
|
|
43445
|
+
PEER_FILES_DIR = join44(DATA_DIR, "otium-peer-files");
|
|
43629
43446
|
db.exec(`
|
|
43630
43447
|
CREATE TABLE IF NOT EXISTS otium_peer_files (
|
|
43631
43448
|
id TEXT PRIMARY KEY,
|
|
@@ -44018,14 +43835,14 @@ var init_store2 = __esm(async () => {
|
|
|
44018
43835
|
// ../../adapters/otium/src/workspace-scope.ts
|
|
44019
43836
|
import { createHash as createHash13 } from "crypto";
|
|
44020
43837
|
import { mkdirSync as mkdirSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
|
|
44021
|
-
import { dirname as
|
|
43838
|
+
import { dirname as dirname24, resolve as resolve26 } from "path";
|
|
44022
43839
|
function surfaceScopeFor(central, workspaceId) {
|
|
44023
43840
|
const digest = createHash13("sha256").update(`${central.trim().replace(/\/+$/, "")}
|
|
44024
43841
|
${workspaceId.trim()}`).digest("hex");
|
|
44025
43842
|
return `ws_${digest.slice(0, 24)}`;
|
|
44026
43843
|
}
|
|
44027
43844
|
function scopeCachePath() {
|
|
44028
|
-
return
|
|
43845
|
+
return resolve26(DATA_DIR, "otium-workspace.json");
|
|
44029
43846
|
}
|
|
44030
43847
|
function readCache() {
|
|
44031
43848
|
try {
|
|
@@ -44037,19 +43854,19 @@ function readCache() {
|
|
|
44037
43854
|
return {};
|
|
44038
43855
|
}
|
|
44039
43856
|
}
|
|
44040
|
-
function cachedSurfaceScope(
|
|
44041
|
-
const record = readCache()[
|
|
44042
|
-
if (!record || record.central !==
|
|
43857
|
+
function cachedSurfaceScope(join45) {
|
|
43858
|
+
const record = readCache()[join45.cellId];
|
|
43859
|
+
if (!record || record.central !== join45.central || !record.workspaceId)
|
|
44043
43860
|
return null;
|
|
44044
43861
|
return surfaceScopeFor(record.central, record.workspaceId);
|
|
44045
43862
|
}
|
|
44046
|
-
function cacheSurfaceScope(
|
|
44047
|
-
const scope = surfaceScopeFor(
|
|
43863
|
+
function cacheSurfaceScope(join45, workspaceId) {
|
|
43864
|
+
const scope = surfaceScopeFor(join45.central, workspaceId);
|
|
44048
43865
|
const path = scopeCachePath();
|
|
44049
43866
|
try {
|
|
44050
|
-
mkdirSync35(
|
|
43867
|
+
mkdirSync35(dirname24(path), { recursive: true });
|
|
44051
43868
|
const cache = readCache();
|
|
44052
|
-
cache[
|
|
43869
|
+
cache[join45.cellId] = { central: join45.central, workspaceId, scope };
|
|
44053
43870
|
writeFileSync26(path, `${JSON.stringify(cache, null, 2)}
|
|
44054
43871
|
`, { mode: 384 });
|
|
44055
43872
|
} catch (err2) {
|
|
@@ -44057,23 +43874,23 @@ function cacheSurfaceScope(join44, workspaceId) {
|
|
|
44057
43874
|
}
|
|
44058
43875
|
return scope;
|
|
44059
43876
|
}
|
|
44060
|
-
async function resolveSurfaceScope(
|
|
44061
|
-
const cached = cachedSurfaceScope(
|
|
43877
|
+
async function resolveSurfaceScope(join45) {
|
|
43878
|
+
const cached = cachedSurfaceScope(join45);
|
|
44062
43879
|
if (cached)
|
|
44063
43880
|
return cached;
|
|
44064
43881
|
try {
|
|
44065
|
-
const workspaceId = await peerWorkspaceIdForCell(
|
|
43882
|
+
const workspaceId = await peerWorkspaceIdForCell(join45.cellId);
|
|
44066
43883
|
if (!workspaceId)
|
|
44067
43884
|
return null;
|
|
44068
|
-
return cacheSurfaceScope(
|
|
43885
|
+
return cacheSurfaceScope(join45, workspaceId);
|
|
44069
43886
|
} catch (err2) {
|
|
44070
43887
|
logger.warn({ err: err2 }, "otium: workspace scope unresolved (will retry on the next contact)");
|
|
44071
43888
|
return null;
|
|
44072
43889
|
}
|
|
44073
43890
|
}
|
|
44074
43891
|
function surfaceScopeForCell(cellId) {
|
|
44075
|
-
const
|
|
44076
|
-
return
|
|
43892
|
+
const join45 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
|
|
43893
|
+
return join45 ? cachedSurfaceScope(join45) : null;
|
|
44077
43894
|
}
|
|
44078
43895
|
function unscopedRoomsAddressable() {
|
|
44079
43896
|
return attachedOtiumCells().length < 2;
|
|
@@ -45086,7 +44903,7 @@ import {
|
|
|
45086
44903
|
import {
|
|
45087
44904
|
chmodSync as chmodSync8,
|
|
45088
44905
|
closeSync as closeSync6,
|
|
45089
|
-
existsSync as
|
|
44906
|
+
existsSync as existsSync40,
|
|
45090
44907
|
fsyncSync as fsyncSync3,
|
|
45091
44908
|
linkSync as linkSync2,
|
|
45092
44909
|
mkdirSync as mkdirSync36,
|
|
@@ -45096,7 +44913,7 @@ import {
|
|
|
45096
44913
|
unlinkSync as unlinkSync24,
|
|
45097
44914
|
writeFileSync as writeFileSync27
|
|
45098
44915
|
} from "fs";
|
|
45099
|
-
import { dirname as
|
|
44916
|
+
import { dirname as dirname25, resolve as resolve27 } from "path";
|
|
45100
44917
|
function parseEnrollmentInvite(code) {
|
|
45101
44918
|
let parsed;
|
|
45102
44919
|
try {
|
|
@@ -45117,11 +44934,11 @@ function parseEnrollmentInvite(code) {
|
|
|
45117
44934
|
return { v: 2, central, token };
|
|
45118
44935
|
}
|
|
45119
44936
|
function pendingEnrollmentPath() {
|
|
45120
|
-
return
|
|
44937
|
+
return resolve27(DATA_DIR, "otium-enrollment-pending.json");
|
|
45121
44938
|
}
|
|
45122
44939
|
function isEnrollmentPending(invite) {
|
|
45123
44940
|
const path = pendingEnrollmentPath();
|
|
45124
|
-
if (!
|
|
44941
|
+
if (!existsSync40(path))
|
|
45125
44942
|
return false;
|
|
45126
44943
|
try {
|
|
45127
44944
|
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
@@ -45132,7 +44949,7 @@ function isEnrollmentPending(invite) {
|
|
|
45132
44949
|
}
|
|
45133
44950
|
function loadOrCreatePending(invite, nodeName) {
|
|
45134
44951
|
const path = pendingEnrollmentPath();
|
|
45135
|
-
if (
|
|
44952
|
+
if (existsSync40(path)) {
|
|
45136
44953
|
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
45137
44954
|
if (saved.central === invite.central && saved.token === invite.token)
|
|
45138
44955
|
return saved;
|
|
@@ -45146,8 +44963,8 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
45146
44963
|
publicKey: pair.publicKey.export({ format: "der", type: "spki" }).toString("base64url"),
|
|
45147
44964
|
...nodeName ? { nodeName } : {}
|
|
45148
44965
|
};
|
|
45149
|
-
mkdirSync36(
|
|
45150
|
-
const temporaryPath =
|
|
44966
|
+
mkdirSync36(dirname25(path), { recursive: true });
|
|
44967
|
+
const temporaryPath = resolve27(dirname25(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
45151
44968
|
let fd;
|
|
45152
44969
|
try {
|
|
45153
44970
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
@@ -45159,7 +44976,7 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
45159
44976
|
linkSync2(temporaryPath, path);
|
|
45160
44977
|
unlinkSync24(temporaryPath);
|
|
45161
44978
|
chmodSync8(path, 384);
|
|
45162
|
-
const directoryFd = openSync6(
|
|
44979
|
+
const directoryFd = openSync6(dirname25(path), "r");
|
|
45163
44980
|
try {
|
|
45164
44981
|
fsyncSync3(directoryFd);
|
|
45165
44982
|
} finally {
|
|
@@ -45168,9 +44985,9 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
45168
44985
|
} catch (error2) {
|
|
45169
44986
|
if (fd !== undefined)
|
|
45170
44987
|
closeSync6(fd);
|
|
45171
|
-
if (
|
|
44988
|
+
if (existsSync40(temporaryPath))
|
|
45172
44989
|
unlinkSync24(temporaryPath);
|
|
45173
|
-
if (error2.code === "EEXIST" &&
|
|
44990
|
+
if (error2.code === "EEXIST" && existsSync40(path)) {
|
|
45174
44991
|
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
45175
44992
|
if (saved.central === invite.central && saved.token === invite.token)
|
|
45176
44993
|
return saved;
|
|
@@ -45181,8 +44998,8 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
45181
44998
|
}
|
|
45182
44999
|
function replacePendingEnrollment(pending2) {
|
|
45183
45000
|
const path = pendingEnrollmentPath();
|
|
45184
|
-
const directory =
|
|
45185
|
-
const temporaryPath =
|
|
45001
|
+
const directory = dirname25(path);
|
|
45002
|
+
const temporaryPath = resolve27(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
45186
45003
|
let fd;
|
|
45187
45004
|
try {
|
|
45188
45005
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
@@ -45201,18 +45018,18 @@ function replacePendingEnrollment(pending2) {
|
|
|
45201
45018
|
} catch (error2) {
|
|
45202
45019
|
if (fd !== undefined)
|
|
45203
45020
|
closeSync6(fd);
|
|
45204
|
-
if (
|
|
45021
|
+
if (existsSync40(temporaryPath))
|
|
45205
45022
|
unlinkSync24(temporaryPath);
|
|
45206
45023
|
throw error2;
|
|
45207
45024
|
}
|
|
45208
45025
|
}
|
|
45209
|
-
function recordClaimedCredential(pending2,
|
|
45026
|
+
function recordClaimedCredential(pending2, join45) {
|
|
45210
45027
|
withJoinCredentialLock(() => {
|
|
45211
45028
|
const current3 = JSON.parse(readFileSync31(pendingEnrollmentPath(), "utf8"));
|
|
45212
45029
|
if (current3.central !== pending2.central || current3.token !== pending2.token || current3.idempotencyKey !== pending2.idempotencyKey || current3.publicKey !== pending2.publicKey) {
|
|
45213
45030
|
throw new Error("pending Otium enrollment changed while its claim was in flight");
|
|
45214
45031
|
}
|
|
45215
|
-
const claimed = { digest: joinCredentialDigest(
|
|
45032
|
+
const claimed = { digest: joinCredentialDigest(join45), cellId: join45.cellId };
|
|
45216
45033
|
if (current3.claimed && (current3.claimed.digest !== claimed.digest || current3.claimed.cellId !== claimed.cellId)) {
|
|
45217
45034
|
throw new Error("central returned different credentials for an idempotent enrollment claim");
|
|
45218
45035
|
}
|
|
@@ -45270,36 +45087,36 @@ async function claimEnrollment(invite, nodeName) {
|
|
|
45270
45087
|
const secret = openCredential(credential, pending2.privateKey);
|
|
45271
45088
|
if (!secret.startsWith("rcs_"))
|
|
45272
45089
|
throw new Error("central returned an invalid runtime credential");
|
|
45273
|
-
const
|
|
45090
|
+
const join45 = {
|
|
45274
45091
|
v: 2,
|
|
45275
45092
|
central: invite.central,
|
|
45276
45093
|
relay: String(response.relayUrl),
|
|
45277
45094
|
cellId: String(response.cell?.id),
|
|
45278
45095
|
secret
|
|
45279
45096
|
};
|
|
45280
|
-
if (!
|
|
45097
|
+
if (!join45.relay || !join45.cellId || join45.cellId === "undefined") {
|
|
45281
45098
|
throw new Error("central returned an incomplete enrollment response");
|
|
45282
45099
|
}
|
|
45283
|
-
assertSecureRelayUrl(
|
|
45284
|
-
recordClaimedCredential(pending2,
|
|
45285
|
-
return
|
|
45100
|
+
assertSecureRelayUrl(join45.relay);
|
|
45101
|
+
recordClaimedCredential(pending2, join45);
|
|
45102
|
+
return join45;
|
|
45286
45103
|
}
|
|
45287
|
-
function commitEnrollment(
|
|
45104
|
+
function commitEnrollment(join45, options = {}) {
|
|
45288
45105
|
return withJoinCredentialLock(() => {
|
|
45289
45106
|
const pendingPath = pendingEnrollmentPath();
|
|
45290
|
-
const pending2 =
|
|
45291
|
-
const digest = joinCredentialDigest(
|
|
45292
|
-
if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !==
|
|
45107
|
+
const pending2 = existsSync40(pendingPath) ? JSON.parse(readFileSync31(pendingPath, "utf8")) : null;
|
|
45108
|
+
const digest = joinCredentialDigest(join45);
|
|
45109
|
+
if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join45.cellId)) {
|
|
45293
45110
|
throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
|
|
45294
45111
|
}
|
|
45295
|
-
const path = saveJoinWhileLocked(
|
|
45296
|
-
if (!isJoinPersisted(
|
|
45112
|
+
const path = saveJoinWhileLocked(join45, options);
|
|
45113
|
+
if (!isJoinPersisted(join45)) {
|
|
45297
45114
|
throw new Error("Otium join credentials were not durably persisted");
|
|
45298
45115
|
}
|
|
45299
45116
|
if (!pending2)
|
|
45300
45117
|
return path;
|
|
45301
45118
|
unlinkSync24(pendingPath);
|
|
45302
|
-
const directoryFd = openSync6(
|
|
45119
|
+
const directoryFd = openSync6(dirname25(pendingPath), "r");
|
|
45303
45120
|
try {
|
|
45304
45121
|
fsyncSync3(directoryFd);
|
|
45305
45122
|
} finally {
|
|
@@ -45887,24 +45704,24 @@ function refreshDefaultSurfaceScope() {
|
|
|
45887
45704
|
setSurfaceScopeRequired(scopes.length > 1);
|
|
45888
45705
|
}
|
|
45889
45706
|
function startOtiumNodeRuntime(options) {
|
|
45890
|
-
const { join:
|
|
45891
|
-
attachOtiumCentralCell(
|
|
45707
|
+
const { join: join45 } = options;
|
|
45708
|
+
attachOtiumCentralCell(join45);
|
|
45892
45709
|
const releaseGlobals = acquireGlobalOtiumServices();
|
|
45893
45710
|
let stopped = false;
|
|
45894
|
-
logger.info({ central:
|
|
45895
|
-
mountedScopes.set(
|
|
45711
|
+
logger.info({ central: join45.central, cellId: join45.cellId }, "otium: worker mode enabled");
|
|
45712
|
+
mountedScopes.set(join45.cellId, cachedSurfaceScope(join45));
|
|
45896
45713
|
refreshDefaultSurfaceScope();
|
|
45897
|
-
selfPeerNodeForCell(
|
|
45714
|
+
selfPeerNodeForCell(join45.cellId).then((self) => {
|
|
45898
45715
|
if (self) {
|
|
45899
45716
|
logger.info({ nodeName: self.nodeName, baseUrl: self.baseUrl }, "otium: attached to workspace");
|
|
45900
45717
|
}
|
|
45901
45718
|
}).catch((err2) => {
|
|
45902
45719
|
logger.warn({ err: err2 }, "otium: self check against central failed (will retry per request)");
|
|
45903
45720
|
});
|
|
45904
|
-
resolveSurfaceScope(
|
|
45721
|
+
resolveSurfaceScope(join45).then((scope) => {
|
|
45905
45722
|
if (!scope || stopped)
|
|
45906
45723
|
return;
|
|
45907
|
-
mountedScopes.set(
|
|
45724
|
+
mountedScopes.set(join45.cellId, scope);
|
|
45908
45725
|
refreshDefaultSurfaceScope();
|
|
45909
45726
|
if (mountedScopes.size === 1)
|
|
45910
45727
|
stampUnscopedOtiumTopics(scope);
|
|
@@ -45913,14 +45730,14 @@ function startOtiumNodeRuntime(options) {
|
|
|
45913
45730
|
});
|
|
45914
45731
|
return {
|
|
45915
45732
|
name: "otium",
|
|
45916
|
-
join:
|
|
45733
|
+
join: join45,
|
|
45917
45734
|
stop: () => {
|
|
45918
45735
|
if (stopped)
|
|
45919
45736
|
return;
|
|
45920
45737
|
stopped = true;
|
|
45921
|
-
mountedScopes.delete(
|
|
45738
|
+
mountedScopes.delete(join45.cellId);
|
|
45922
45739
|
refreshDefaultSurfaceScope();
|
|
45923
|
-
detachOtiumCentralCell(
|
|
45740
|
+
detachOtiumCentralCell(join45.cellId);
|
|
45924
45741
|
releaseGlobals();
|
|
45925
45742
|
}
|
|
45926
45743
|
};
|
|
@@ -46015,14 +45832,14 @@ __export(exports_node_runtime, {
|
|
|
46015
45832
|
function sameCredentials(left, right) {
|
|
46016
45833
|
return left.central === right.central && left.relay === right.relay && left.secret === right.secret;
|
|
46017
45834
|
}
|
|
46018
|
-
function attachOtiumWorkspace(
|
|
46019
|
-
const current3 = mounted.get(
|
|
45835
|
+
function attachOtiumWorkspace(join45) {
|
|
45836
|
+
const current3 = mounted.get(join45.cellId);
|
|
46020
45837
|
if (current3) {
|
|
46021
|
-
if (sameCredentials(current3.join,
|
|
45838
|
+
if (sameCredentials(current3.join, join45))
|
|
46022
45839
|
return false;
|
|
46023
|
-
detachOtiumWorkspace(
|
|
45840
|
+
detachOtiumWorkspace(join45.cellId);
|
|
46024
45841
|
}
|
|
46025
|
-
mounted.set(
|
|
45842
|
+
mounted.set(join45.cellId, startOtiumNodeRuntime({ join: join45 }));
|
|
46026
45843
|
return true;
|
|
46027
45844
|
}
|
|
46028
45845
|
function detachOtiumWorkspace(cellId) {
|
|
@@ -46037,7 +45854,7 @@ function mountedOtiumWorkspaces() {
|
|
|
46037
45854
|
return [...mounted.values()].map((runtime2) => runtime2.join);
|
|
46038
45855
|
}
|
|
46039
45856
|
function reconcileOtiumWorkspaces(joins = loadJoins()) {
|
|
46040
|
-
const wanted = new Map(joins.map((
|
|
45857
|
+
const wanted = new Map(joins.map((join45) => [join45.cellId, join45]));
|
|
46041
45858
|
const detached = [];
|
|
46042
45859
|
for (const cellId of [...mounted.keys()]) {
|
|
46043
45860
|
if (wanted.has(cellId))
|
|
@@ -46046,8 +45863,8 @@ function reconcileOtiumWorkspaces(joins = loadJoins()) {
|
|
|
46046
45863
|
detached.push(cellId);
|
|
46047
45864
|
}
|
|
46048
45865
|
const attached = [];
|
|
46049
|
-
for (const [cellId,
|
|
46050
|
-
if (attachOtiumWorkspace(
|
|
45866
|
+
for (const [cellId, join45] of wanted) {
|
|
45867
|
+
if (attachOtiumWorkspace(join45))
|
|
46051
45868
|
attached.push(cellId);
|
|
46052
45869
|
}
|
|
46053
45870
|
if (attached.length > 0 || detached.length > 0) {
|
|
@@ -46070,9 +45887,9 @@ async function handleOtiumAdapterControlRequest(req) {
|
|
|
46070
45887
|
if (req.method === "GET") {
|
|
46071
45888
|
return Response.json({
|
|
46072
45889
|
ok: true,
|
|
46073
|
-
workspaces: mountedOtiumWorkspaces().map((
|
|
46074
|
-
cellId:
|
|
46075
|
-
central:
|
|
45890
|
+
workspaces: mountedOtiumWorkspaces().map((join45) => ({
|
|
45891
|
+
cellId: join45.cellId,
|
|
45892
|
+
central: join45.central
|
|
46076
45893
|
}))
|
|
46077
45894
|
});
|
|
46078
45895
|
}
|
|
@@ -46093,8 +45910,8 @@ function mountConfiguredOtiumNodeRuntime() {
|
|
|
46093
45910
|
const joins = loadJoins();
|
|
46094
45911
|
if (joins.length === 0)
|
|
46095
45912
|
return null;
|
|
46096
|
-
for (const
|
|
46097
|
-
attachOtiumWorkspace(
|
|
45913
|
+
for (const join45 of joins)
|
|
45914
|
+
attachOtiumWorkspace(join45);
|
|
46098
45915
|
registerNodeRequestHandler("otium-adapter-control", handleOtiumAdapterControlRequest);
|
|
46099
45916
|
let stopped = false;
|
|
46100
45917
|
return {
|
|
@@ -46206,11 +46023,11 @@ async function joinCommand(args) {
|
|
|
46206
46023
|
process.exitCode = 1;
|
|
46207
46024
|
return;
|
|
46208
46025
|
}
|
|
46209
|
-
let
|
|
46026
|
+
let join45;
|
|
46210
46027
|
let productionEnrollment = false;
|
|
46211
46028
|
if (args.includes("--legacy")) {
|
|
46212
46029
|
try {
|
|
46213
|
-
|
|
46030
|
+
join45 = parseInviteCode(code);
|
|
46214
46031
|
} catch (err2) {
|
|
46215
46032
|
console.error(`invalid legacy invite code: ${err2 instanceof Error ? err2.message : err2}`);
|
|
46216
46033
|
process.exitCode = 1;
|
|
@@ -46236,7 +46053,7 @@ async function joinCommand(args) {
|
|
|
46236
46053
|
return;
|
|
46237
46054
|
}
|
|
46238
46055
|
}
|
|
46239
|
-
|
|
46056
|
+
join45 = await claimEnrollment(invite, nodeName);
|
|
46240
46057
|
productionEnrollment = true;
|
|
46241
46058
|
} catch (err2) {
|
|
46242
46059
|
console.error(`enrollment failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
@@ -46247,16 +46064,16 @@ async function joinCommand(args) {
|
|
|
46247
46064
|
let path;
|
|
46248
46065
|
try {
|
|
46249
46066
|
const saveOptions = { replaceExisting: args.includes("--replace") };
|
|
46250
|
-
path = productionEnrollment ? commitEnrollment(
|
|
46067
|
+
path = productionEnrollment ? commitEnrollment(join45, saveOptions) : saveJoin(join45, saveOptions);
|
|
46251
46068
|
} catch (err2) {
|
|
46252
46069
|
console.error(`could not save join credentials: ${err2 instanceof Error ? err2.message : err2}`);
|
|
46253
46070
|
process.exitCode = 1;
|
|
46254
46071
|
return;
|
|
46255
46072
|
}
|
|
46256
46073
|
console.log(`otium join credentials saved to ${path}`);
|
|
46257
|
-
console.log(` central: ${
|
|
46258
|
-
console.log(` cellId: ${
|
|
46259
|
-
configureOtiumCentral(
|
|
46074
|
+
console.log(` central: ${join45.central}`);
|
|
46075
|
+
console.log(` cellId: ${join45.cellId}`);
|
|
46076
|
+
configureOtiumCentral(join45);
|
|
46260
46077
|
try {
|
|
46261
46078
|
const self = await selfPeerNode();
|
|
46262
46079
|
if (self) {
|
|
@@ -46298,19 +46115,19 @@ async function statusCommand() {
|
|
|
46298
46115
|
return;
|
|
46299
46116
|
}
|
|
46300
46117
|
configureOtiumCentral(joins[0] ?? null);
|
|
46301
|
-
for (const
|
|
46302
|
-
attachOtiumCentralCell(
|
|
46303
|
-
for (const [index,
|
|
46118
|
+
for (const join45 of joins.slice(1))
|
|
46119
|
+
attachOtiumCentralCell(join45);
|
|
46120
|
+
for (const [index, join45] of joins.entries()) {
|
|
46304
46121
|
if (index > 0)
|
|
46305
46122
|
console.log("");
|
|
46306
|
-
console.log(`cellId: ${
|
|
46307
|
-
console.log(`central: ${
|
|
46308
|
-
if (
|
|
46309
|
-
console.log(`relay: ${
|
|
46123
|
+
console.log(`cellId: ${join45.cellId}`);
|
|
46124
|
+
console.log(`central: ${join45.central}`);
|
|
46125
|
+
if (join45.relay)
|
|
46126
|
+
console.log(`relay: ${join45.relay}`);
|
|
46310
46127
|
try {
|
|
46311
|
-
const self = await selfPeerNodeForCell(
|
|
46128
|
+
const self = await selfPeerNodeForCell(join45.cellId);
|
|
46312
46129
|
if (self) {
|
|
46313
|
-
console.log(`node: ${self.nodeName ??
|
|
46130
|
+
console.log(`node: ${self.nodeName ?? join45.cellId}${self.isPrimary ? " (primary)" : ""}`);
|
|
46314
46131
|
console.log(`baseUrl: ${self.baseUrl}`);
|
|
46315
46132
|
} else {
|
|
46316
46133
|
console.warn(" warning: central answered but this cell has no visible assignment yet \u2014 check the workspace assignment");
|
|
@@ -46346,13 +46163,13 @@ function resolveTunnelTargets(joins, relayOverride) {
|
|
|
46346
46163
|
const targets = [];
|
|
46347
46164
|
const skippedNoRelay = [];
|
|
46348
46165
|
const envRelay = process.env.OTIUM_RELAY_URL?.trim();
|
|
46349
|
-
for (const
|
|
46350
|
-
const relayUrl = relayOverride?.trim() ||
|
|
46166
|
+
for (const join45 of joins) {
|
|
46167
|
+
const relayUrl = relayOverride?.trim() || join45.relay || envRelay;
|
|
46351
46168
|
if (!relayUrl) {
|
|
46352
|
-
skippedNoRelay.push(
|
|
46169
|
+
skippedNoRelay.push(join45.cellId);
|
|
46353
46170
|
continue;
|
|
46354
46171
|
}
|
|
46355
|
-
targets.push({ cellId:
|
|
46172
|
+
targets.push({ cellId: join45.cellId, relayUrl, secret: join45.secret });
|
|
46356
46173
|
}
|
|
46357
46174
|
return { targets, skippedNoRelay };
|
|
46358
46175
|
}
|
|
@@ -46439,8 +46256,8 @@ async function runOtiumSidecar(options) {
|
|
|
46439
46256
|
for (const tunnel of tunnels.values())
|
|
46440
46257
|
tunnel.start();
|
|
46441
46258
|
let resolveCompleted;
|
|
46442
|
-
const completed = new Promise((
|
|
46443
|
-
resolveCompleted =
|
|
46259
|
+
const completed = new Promise((resolve28) => {
|
|
46260
|
+
resolveCompleted = resolve28;
|
|
46444
46261
|
});
|
|
46445
46262
|
onShutdown("otium-sidecar-server", 130, () => server?.stop(true));
|
|
46446
46263
|
onShutdown("otium-sidecar-tunnel", 120, () => {
|
|
@@ -46589,9 +46406,9 @@ async function runOtiumCli(args = process.argv.slice(2)) {
|
|
|
46589
46406
|
if (joins.length === 0)
|
|
46590
46407
|
throw new Error("not joined to an Otium workspace");
|
|
46591
46408
|
if (!targetCellId && joins.length > 1) {
|
|
46592
|
-
throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((
|
|
46409
|
+
throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join45) => join45.cellId).join(", ")}`);
|
|
46593
46410
|
}
|
|
46594
|
-
if (targetCellId && !joins.some((
|
|
46411
|
+
if (targetCellId && !joins.some((join45) => join45.cellId === targetCellId)) {
|
|
46595
46412
|
throw new Error(`not joined as ${targetCellId}`);
|
|
46596
46413
|
}
|
|
46597
46414
|
removeJoin2(targetCellId);
|
|
@@ -47244,7 +47061,7 @@ async function runCanonicalNode(port) {
|
|
|
47244
47061
|
});
|
|
47245
47062
|
console.log(`negotium node listening on 127.0.0.1:${node.port} (ctrl-c to stop)`);
|
|
47246
47063
|
await node.completed;
|
|
47247
|
-
await new Promise((
|
|
47064
|
+
await new Promise((resolve28) => setImmediate(resolve28));
|
|
47248
47065
|
process.exit(0);
|
|
47249
47066
|
}
|
|
47250
47067
|
async function stopAdapter(name) {
|
|
@@ -47378,4 +47195,4 @@ switch (command) {
|
|
|
47378
47195
|
}
|
|
47379
47196
|
}
|
|
47380
47197
|
|
|
47381
|
-
//# debugId=
|
|
47198
|
+
//# debugId=A4A68C691DEC1BCD64756E2164756E21
|