negotium 0.3.13 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -6
- package/dist/agent-helpers.js +328 -185
- 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 +207 -64
- package/dist/hosted-agent.js.map +8 -7
- package/dist/main.js +753 -886
- package/dist/main.js.map +13 -13
- package/dist/mcp-factories.js +300 -468
- 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 +33 -8
- package/dist/runtime/src/agents/codex-vault-hook-bridge.ts +192 -0
- package/dist/runtime/src/agents/codex-vault-hook.mjs +48 -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 +27 -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 +1 -1
- 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.0";
|
|
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) {
|
|
5287
|
+
return [
|
|
5288
|
+
"#!/bin/sh",
|
|
5289
|
+
'if [ "$1" = "exec" ]; then',
|
|
5290
|
+
" shift",
|
|
5291
|
+
` exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} exec --dangerously-bypass-hook-trust "$@"`,
|
|
5292
|
+
"fi",
|
|
5293
|
+
`exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} "$@"`,
|
|
5294
|
+
""
|
|
5295
|
+
].join(`
|
|
5296
|
+
`);
|
|
5297
|
+
}
|
|
5298
|
+
async function createCodexVaultHookBridge(userId) {
|
|
5299
|
+
const root = await mkdtemp(join13(tmpdir2(), "negotium-codex-vault-"));
|
|
5300
|
+
await chmod(root, 448);
|
|
5301
|
+
const socketPath = join13(root, "hook.sock");
|
|
5302
|
+
const wrapperPath = join13(root, "codex-with-hooks");
|
|
5303
|
+
const token = randomBytes5(32).toString("hex");
|
|
5304
|
+
const connections = new Set;
|
|
5305
|
+
const server = createServer((socket) => {
|
|
5306
|
+
connections.add(socket);
|
|
5307
|
+
socket.once("close", () => connections.delete(socket));
|
|
5308
|
+
socket.setEncoding("utf8");
|
|
5309
|
+
let request = "";
|
|
5310
|
+
let handled = false;
|
|
5311
|
+
const handleRequest = () => {
|
|
5312
|
+
if (handled)
|
|
5313
|
+
return;
|
|
5314
|
+
handled = true;
|
|
5315
|
+
try {
|
|
5316
|
+
const parsed = JSON.parse(request.trimEnd());
|
|
5317
|
+
if (parsed.token !== token)
|
|
5318
|
+
throw new Error("invalid hook capability");
|
|
5319
|
+
if (!parsed.input || typeof parsed.input !== "object" || typeof parsed.input.tool_name !== "string") {
|
|
5320
|
+
throw new Error("invalid PreToolUse payload");
|
|
5321
|
+
}
|
|
5322
|
+
const output = evaluateCodexVaultPreToolUse(parsed.input, userId, {
|
|
5323
|
+
referencesSensitiveStorage: referencesHostedSecretStorage,
|
|
5324
|
+
substitute: substituteHostedSecrets
|
|
5325
|
+
});
|
|
5326
|
+
socket.end(JSON.stringify({ ok: true, output }));
|
|
5327
|
+
} catch (error) {
|
|
5328
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5329
|
+
socket.end(JSON.stringify({ ok: false, error: message }));
|
|
5330
|
+
}
|
|
5331
|
+
};
|
|
5332
|
+
socket.on("data", (chunk) => {
|
|
5333
|
+
request += chunk;
|
|
5334
|
+
if (Buffer.byteLength(request) > MAX_HOOK_REQUEST_BYTES)
|
|
5335
|
+
socket.destroy();
|
|
5336
|
+
else if (request.endsWith(`
|
|
5337
|
+
`))
|
|
5338
|
+
handleRequest();
|
|
5339
|
+
});
|
|
5340
|
+
socket.on("end", handleRequest);
|
|
5341
|
+
});
|
|
5342
|
+
try {
|
|
5343
|
+
await new Promise((resolveListen, reject) => {
|
|
5344
|
+
server.once("error", reject);
|
|
5345
|
+
server.listen(socketPath, () => {
|
|
5346
|
+
server.off("error", reject);
|
|
5347
|
+
resolveListen();
|
|
5348
|
+
});
|
|
5349
|
+
});
|
|
5350
|
+
await chmod(socketPath, 384);
|
|
5351
|
+
await writeFile(wrapperPath, privateCodexWrapper(codexCliScriptPath()), { mode: 448 });
|
|
5352
|
+
const command = [
|
|
5353
|
+
shellQuote(process.execPath),
|
|
5354
|
+
shellQuote(hookClientPath()),
|
|
5355
|
+
shellQuote(socketPath),
|
|
5356
|
+
shellQuote(token)
|
|
5357
|
+
].join(" ");
|
|
5358
|
+
return {
|
|
5359
|
+
codexPathOverride: wrapperPath,
|
|
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";
|
|
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") {
|
|
@@ -6440,12 +6587,14 @@ async function* codexProvider(opts) {
|
|
|
6440
6587
|
abortSignal?.removeEventListener("abort", onAbortKill);
|
|
6441
6588
|
if (trackedPids.pids.length > 0)
|
|
6442
6589
|
unregisterOwnedCodexPids(trackedPids.pids);
|
|
6590
|
+
await vaultHook.close();
|
|
6443
6591
|
}
|
|
6444
6592
|
}
|
|
6445
6593
|
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;
|
|
6446
6594
|
var init_codex_provider = __esm(async () => {
|
|
6447
6595
|
init_codex_native_multi_agent();
|
|
6448
6596
|
init_codex_tree_kill();
|
|
6597
|
+
await init_codex_vault_hook_bridge();
|
|
6449
6598
|
await init_execution_host();
|
|
6450
6599
|
await init_codex();
|
|
6451
6600
|
init_tool_format();
|
|
@@ -6468,7 +6617,7 @@ __export(exports_maestro_provider, {
|
|
|
6468
6617
|
buildMaestroToolHooks: () => buildMaestroToolHooks,
|
|
6469
6618
|
buildMaestroDisallowedTools: () => buildMaestroDisallowedTools
|
|
6470
6619
|
});
|
|
6471
|
-
import { resolve as
|
|
6620
|
+
import { resolve as resolve10 } from "path";
|
|
6472
6621
|
import { maestroProvider as sdkMaestroProvider, setMcpResolver } from "maestro-agent-sdk";
|
|
6473
6622
|
function buildMaestroDisallowedTools(callerDisallowedTools = [], toolPolicy) {
|
|
6474
6623
|
return [
|
|
@@ -6602,13 +6751,13 @@ var init_maestro_provider = __esm(async () => {
|
|
|
6602
6751
|
"ToolSearch",
|
|
6603
6752
|
...DEFAULT_MAESTRO_DISALLOWED_TOOLS
|
|
6604
6753
|
];
|
|
6605
|
-
MAESTRO_TOOL_OUTPUT_DIR =
|
|
6754
|
+
MAESTRO_TOOL_OUTPUT_DIR = resolve10(RUN_DIR, "maestro-tool-outputs");
|
|
6606
6755
|
});
|
|
6607
6756
|
|
|
6608
6757
|
// ../../packages/core/src/agents/index.ts
|
|
6609
|
-
import { existsSync as
|
|
6758
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6610
6759
|
import { homedir as homedir8 } from "os";
|
|
6611
|
-
import { join as
|
|
6760
|
+
import { join as join15 } from "path";
|
|
6612
6761
|
async function* dispatchAgent(opts) {
|
|
6613
6762
|
switch (opts.agent) {
|
|
6614
6763
|
case "claude": {
|
|
@@ -6642,11 +6791,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
|
|
|
6642
6791
|
switch (agent) {
|
|
6643
6792
|
case "claude": {
|
|
6644
6793
|
const encodedCwd = encodeClaudeCwd(cwd);
|
|
6645
|
-
const path =
|
|
6646
|
-
return !
|
|
6794
|
+
const path = join15(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
|
|
6795
|
+
return !existsSync15(path);
|
|
6647
6796
|
}
|
|
6648
6797
|
case "codex": {
|
|
6649
|
-
const sessionsDir =
|
|
6798
|
+
const sessionsDir = join15(hostedCodexHomePath(), "sessions");
|
|
6650
6799
|
const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
|
|
6651
6800
|
for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
|
|
6652
6801
|
return false;
|
|
@@ -6655,7 +6804,7 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
|
|
|
6655
6804
|
}
|
|
6656
6805
|
case "maestro": {
|
|
6657
6806
|
const { hasActiveMaestroSession, maestroSessionPath: maestroSessionPath2 } = await import("maestro-agent-sdk");
|
|
6658
|
-
return !
|
|
6807
|
+
return !existsSync15(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
|
|
6659
6808
|
}
|
|
6660
6809
|
default: {
|
|
6661
6810
|
const _exhaustive = agent;
|
|
@@ -8874,7 +9023,7 @@ async function waitForRuntimeProcessLease(role, options = {}) {
|
|
|
8874
9023
|
const remaining = deadline - Date.now();
|
|
8875
9024
|
if (remaining <= 0)
|
|
8876
9025
|
return null;
|
|
8877
|
-
await new Promise((
|
|
9026
|
+
await new Promise((resolve11) => setTimeout(resolve11, Math.min(interval, remaining)));
|
|
8878
9027
|
}
|
|
8879
9028
|
}
|
|
8880
9029
|
async function waitForRequiredRuntimeProcessLease(role, options = {}) {
|
|
@@ -9151,8 +9300,8 @@ function createAskUserRuntime(host) {
|
|
|
9151
9300
|
return errorResult(`Error: failed to persist ask_user_question: ${error instanceof Error ? error.message : String(error)}`);
|
|
9152
9301
|
}
|
|
9153
9302
|
let resolveAnswer;
|
|
9154
|
-
const promise = new Promise((
|
|
9155
|
-
resolveAnswer =
|
|
9303
|
+
const promise = new Promise((resolve11) => {
|
|
9304
|
+
resolveAnswer = resolve11;
|
|
9156
9305
|
});
|
|
9157
9306
|
pendingAsks.set(message.id, {
|
|
9158
9307
|
topicId: ctx.topicId,
|
|
@@ -9808,11 +9957,11 @@ var init_manager_utils = () => {};
|
|
|
9808
9957
|
// ../../packages/core/src/platform/playwright/browser-processes.ts
|
|
9809
9958
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
9810
9959
|
import { readdirSync as readdirSync2, unlinkSync as unlinkSync10 } from "fs";
|
|
9811
|
-
import { createServer } from "net";
|
|
9812
|
-
import { resolve as
|
|
9960
|
+
import { createServer as createServer2 } from "net";
|
|
9961
|
+
import { resolve as resolve11, sep } from "path";
|
|
9813
9962
|
function isPortInUse(port) {
|
|
9814
9963
|
return new Promise((resolveProbe) => {
|
|
9815
|
-
const server =
|
|
9964
|
+
const server = createServer2();
|
|
9816
9965
|
let settled = false;
|
|
9817
9966
|
const finish = (occupied) => {
|
|
9818
9967
|
if (settled)
|
|
@@ -9842,8 +9991,8 @@ async function reserveAvailableLoopbackPort(minPort, maxPort, reservedPorts, pro
|
|
|
9842
9991
|
return null;
|
|
9843
9992
|
}
|
|
9844
9993
|
function killBrowserProcsForUserDataDir(userDataDir) {
|
|
9845
|
-
const target =
|
|
9846
|
-
const profileRoot =
|
|
9994
|
+
const target = resolve11(userDataDir);
|
|
9995
|
+
const profileRoot = resolve11(BROWSER_PROFILES_DIR);
|
|
9847
9996
|
if (target !== profileRoot && !target.startsWith(`${profileRoot}${sep}`))
|
|
9848
9997
|
return;
|
|
9849
9998
|
let pids;
|
|
@@ -9864,7 +10013,7 @@ function killBrowserProcsForUserDataDir(userDataDir) {
|
|
|
9864
10013
|
stdio: "pipe"
|
|
9865
10014
|
}).toString().trim();
|
|
9866
10015
|
const argDir = extractUserDataDirArg(cmdline);
|
|
9867
|
-
if (!argDir ||
|
|
10016
|
+
if (!argDir || resolve11(argDir) !== target)
|
|
9868
10017
|
continue;
|
|
9869
10018
|
killProcessTreeChildren(pidNum);
|
|
9870
10019
|
process.kill(pidNum, "SIGKILL");
|
|
@@ -9875,13 +10024,13 @@ function killBrowserProcsForUserDataDir(userDataDir) {
|
|
|
9875
10024
|
}
|
|
9876
10025
|
}
|
|
9877
10026
|
function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid) {
|
|
9878
|
-
const root =
|
|
9879
|
-
const live = new Set([...liveUserDataDirs].map((d) =>
|
|
10027
|
+
const root = resolve11(profileRoot);
|
|
10028
|
+
const live = new Set([...liveUserDataDirs].map((d) => resolve11(d)));
|
|
9880
10029
|
const out = [];
|
|
9881
10030
|
for (const { pid, userDataDir } of procs) {
|
|
9882
10031
|
if (pid === selfPid || !userDataDir)
|
|
9883
10032
|
continue;
|
|
9884
|
-
const dir =
|
|
10033
|
+
const dir = resolve11(userDataDir);
|
|
9885
10034
|
if (dir !== root && !dir.startsWith(`${root}${sep}`))
|
|
9886
10035
|
continue;
|
|
9887
10036
|
if (live.has(dir))
|
|
@@ -9897,7 +10046,7 @@ function reapOrphanBrowsers(liveUserDataDirs) {
|
|
|
9897
10046
|
const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
|
|
9898
10047
|
if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
|
|
9899
10048
|
return;
|
|
9900
|
-
const profileRoot =
|
|
10049
|
+
const profileRoot = resolve11(BROWSER_PROFILES_DIR);
|
|
9901
10050
|
let pids;
|
|
9902
10051
|
try {
|
|
9903
10052
|
pids = execFileSync6("pgrep", ["-f", "--", profileRoot], { stdio: "pipe" }).toString().trim();
|
|
@@ -9936,7 +10085,7 @@ function cleanSingletonFiles(userDataDir) {
|
|
|
9936
10085
|
for (const f of files) {
|
|
9937
10086
|
if (f.startsWith("Singleton")) {
|
|
9938
10087
|
try {
|
|
9939
|
-
unlinkSync10(
|
|
10088
|
+
unlinkSync10(resolve11(userDataDir, f));
|
|
9940
10089
|
logger.info({ file: f, userDataDir }, "Removed stale Singleton file");
|
|
9941
10090
|
} catch (e) {
|
|
9942
10091
|
logger.warn({ err: e, file: f }, "Failed to remove stale Chrome Singleton file");
|
|
@@ -9976,9 +10125,9 @@ var init_browser_processes = __esm(async () => {
|
|
|
9976
10125
|
|
|
9977
10126
|
// ../../packages/core/src/platform/playwright/headed-launch.ts
|
|
9978
10127
|
import { accessSync as accessSync2, constants as constants2 } from "fs";
|
|
9979
|
-
import { delimiter, isAbsolute as isAbsolute3, resolve as
|
|
10128
|
+
import { delimiter, isAbsolute as isAbsolute3, resolve as resolve12 } from "path";
|
|
9980
10129
|
function findExecutableOnPath(command, environment = process.env) {
|
|
9981
|
-
const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) =>
|
|
10130
|
+
const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve12(directory, command));
|
|
9982
10131
|
for (const candidate of candidates) {
|
|
9983
10132
|
try {
|
|
9984
10133
|
accessSync2(candidate, constants2.X_OK);
|
|
@@ -10099,11 +10248,11 @@ var init_vault_public = __esm(async () => {
|
|
|
10099
10248
|
});
|
|
10100
10249
|
|
|
10101
10250
|
// ../../packages/core/src/platform/playwright/vault-broker.ts
|
|
10102
|
-
import { randomBytes as
|
|
10251
|
+
import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
10103
10252
|
import { chmodSync as chmodSync4 } from "fs";
|
|
10104
|
-
import { createServer as
|
|
10105
|
-
import { tmpdir as
|
|
10106
|
-
import { join as
|
|
10253
|
+
import { createServer as createServer3 } from "net";
|
|
10254
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
10255
|
+
import { join as join16 } from "path";
|
|
10107
10256
|
function deepMapStrings2(value, transform) {
|
|
10108
10257
|
if (typeof value === "string")
|
|
10109
10258
|
return transform(value);
|
|
@@ -10189,8 +10338,8 @@ function authorized(actual, expected) {
|
|
|
10189
10338
|
return left.length === right.length && timingSafeEqual2(left, right);
|
|
10190
10339
|
}
|
|
10191
10340
|
async function createBrowserVaultBroker(userId) {
|
|
10192
|
-
const token =
|
|
10193
|
-
const socketPath =
|
|
10341
|
+
const token = randomBytes6(32).toString("hex");
|
|
10342
|
+
const socketPath = join16(process.platform === "win32" ? tmpdir3() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes6(8).toString("hex")}.sock`);
|
|
10194
10343
|
const retainedForms = new Map;
|
|
10195
10344
|
const leases = new Map;
|
|
10196
10345
|
const sockets = new Set;
|
|
@@ -10229,7 +10378,7 @@ async function createBrowserVaultBroker(userId) {
|
|
|
10229
10378
|
return result.text;
|
|
10230
10379
|
});
|
|
10231
10380
|
const prepared = prepareInput(request.tool, substituted);
|
|
10232
|
-
const lease =
|
|
10381
|
+
const lease = randomBytes6(24).toString("hex");
|
|
10233
10382
|
leases.set(lease, { boundary: prepared.boundary, createdAt: Date.now() });
|
|
10234
10383
|
return {
|
|
10235
10384
|
id,
|
|
@@ -10261,7 +10410,7 @@ async function createBrowserVaultBroker(userId) {
|
|
|
10261
10410
|
}
|
|
10262
10411
|
return { id, ok: false, error: "unknown operation" };
|
|
10263
10412
|
};
|
|
10264
|
-
const server =
|
|
10413
|
+
const server = createServer3((socket) => {
|
|
10265
10414
|
sockets.add(socket);
|
|
10266
10415
|
let pending = "";
|
|
10267
10416
|
socket.setEncoding("utf8");
|
|
@@ -10446,24 +10595,24 @@ var init_browser_profiles = __esm(async () => {
|
|
|
10446
10595
|
|
|
10447
10596
|
// ../../packages/core/src/platform/playwright/manager.ts
|
|
10448
10597
|
import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
|
|
10449
|
-
import { createHash as createHash4, randomBytes as
|
|
10598
|
+
import { createHash as createHash4, randomBytes as randomBytes7, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
10450
10599
|
import {
|
|
10451
10600
|
cpSync,
|
|
10452
|
-
existsSync as
|
|
10601
|
+
existsSync as existsSync16,
|
|
10453
10602
|
mkdirSync as mkdirSync10,
|
|
10454
10603
|
readFileSync as readFileSync12,
|
|
10455
10604
|
rmSync as rmSync2,
|
|
10456
10605
|
unlinkSync as unlinkSync11,
|
|
10457
10606
|
writeFileSync as writeFileSync8
|
|
10458
10607
|
} from "fs";
|
|
10459
|
-
import { dirname as
|
|
10608
|
+
import { dirname as dirname11, join as join17, resolve as resolve13 } from "path";
|
|
10460
10609
|
function removeDefaultProfileDataDir(userDataDir) {
|
|
10461
|
-
const root =
|
|
10462
|
-
const target =
|
|
10463
|
-
if (
|
|
10610
|
+
const root = resolve13(BROWSER_PROFILES_DIR);
|
|
10611
|
+
const target = resolve13(userDataDir);
|
|
10612
|
+
if (dirname11(target) !== root) {
|
|
10464
10613
|
throw new Error(`Refusing to delete browser profile outside managed root: ${target}`);
|
|
10465
10614
|
}
|
|
10466
|
-
const existed =
|
|
10615
|
+
const existed = existsSync16(target);
|
|
10467
10616
|
rmSync2(target, { recursive: true, force: true });
|
|
10468
10617
|
return existed;
|
|
10469
10618
|
}
|
|
@@ -10546,14 +10695,14 @@ function portFileName(instanceKey) {
|
|
|
10546
10695
|
function writePortFile(instanceKey, port) {
|
|
10547
10696
|
try {
|
|
10548
10697
|
mkdirSync10(managerHost.portsDir, { recursive: true });
|
|
10549
|
-
writeFileSync8(
|
|
10698
|
+
writeFileSync8(join17(managerHost.portsDir, portFileName(instanceKey)), String(port));
|
|
10550
10699
|
} catch (e) {
|
|
10551
10700
|
logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
|
|
10552
10701
|
}
|
|
10553
10702
|
}
|
|
10554
10703
|
function deletePortFile(instanceKey) {
|
|
10555
10704
|
try {
|
|
10556
|
-
unlinkSync11(
|
|
10705
|
+
unlinkSync11(join17(managerHost.portsDir, portFileName(instanceKey)));
|
|
10557
10706
|
} catch (e) {
|
|
10558
10707
|
if (e.code === "ENOENT")
|
|
10559
10708
|
return;
|
|
@@ -10700,7 +10849,7 @@ function releasePort(port) {
|
|
|
10700
10849
|
usedPorts.delete(port);
|
|
10701
10850
|
}
|
|
10702
10851
|
function defaultProfileDir(_ownerId, profile) {
|
|
10703
|
-
return
|
|
10852
|
+
return resolve13(BROWSER_PROFILES_DIR, profile);
|
|
10704
10853
|
}
|
|
10705
10854
|
function resolveUserDataDir(instanceKey) {
|
|
10706
10855
|
return managerHost.resolveInstanceDataDir(instanceKey);
|
|
@@ -10792,8 +10941,8 @@ async function spawnPlaywright(instanceKey, ownerId, reservedPort, browserBin =
|
|
|
10792
10941
|
releasePort(port);
|
|
10793
10942
|
throw new Error("Browser.rs managed mode does not support authenticated egress proxies");
|
|
10794
10943
|
}
|
|
10795
|
-
const capability =
|
|
10796
|
-
const spawnNonce =
|
|
10944
|
+
const capability = randomBytes7(32).toString("hex");
|
|
10945
|
+
const spawnNonce = randomBytes7(32).toString("hex");
|
|
10797
10946
|
let vaultBroker;
|
|
10798
10947
|
try {
|
|
10799
10948
|
vaultBroker = await createBrowserVaultBroker(ownerId);
|
|
@@ -11061,14 +11210,14 @@ async function cloneProfileForChild(opts) {
|
|
|
11061
11210
|
killInstance(dstKey);
|
|
11062
11211
|
await delay(500);
|
|
11063
11212
|
}
|
|
11064
|
-
if (!
|
|
11213
|
+
if (!existsSync16(srcDir)) {
|
|
11065
11214
|
return { copied: false, srcDir, dstDir, reason: "src-missing" };
|
|
11066
11215
|
}
|
|
11067
11216
|
try {
|
|
11068
|
-
if (
|
|
11217
|
+
if (existsSync16(dstDir)) {
|
|
11069
11218
|
rmSync2(dstDir, { recursive: true, force: true });
|
|
11070
11219
|
}
|
|
11071
|
-
mkdirSync10(
|
|
11220
|
+
mkdirSync10(dirname11(dstDir), { recursive: true });
|
|
11072
11221
|
if (process.platform === "darwin") {
|
|
11073
11222
|
try {
|
|
11074
11223
|
execFileSync7("cp", ["-cR", srcDir, dstDir], { stdio: "pipe" });
|
|
@@ -11086,7 +11235,7 @@ async function cloneProfileForChild(opts) {
|
|
|
11086
11235
|
cleanSingletonFiles(dstDir);
|
|
11087
11236
|
for (const f of ["DevToolsActivePort", "LOCK"]) {
|
|
11088
11237
|
try {
|
|
11089
|
-
unlinkSync11(
|
|
11238
|
+
unlinkSync11(resolve13(dstDir, f));
|
|
11090
11239
|
} catch {}
|
|
11091
11240
|
}
|
|
11092
11241
|
logger.info({ srcKey, dstKey, srcDir, dstDir }, "Cloned Playwright profile for child topic");
|
|
@@ -11589,9 +11738,9 @@ var init_personal_general = __esm(async () => {
|
|
|
11589
11738
|
|
|
11590
11739
|
// ../../packages/core/src/prompts/builders.ts
|
|
11591
11740
|
import { readFileSync as readFileSync13 } from "fs";
|
|
11592
|
-
import { resolve as
|
|
11741
|
+
import { resolve as resolve14 } from "path";
|
|
11593
11742
|
function loadPrompt(filename, dir = SESSIONS_DIR) {
|
|
11594
|
-
const raw = readFileSync13(
|
|
11743
|
+
const raw = readFileSync13(resolve14(dir, filename), "utf-8");
|
|
11595
11744
|
return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
|
|
11596
11745
|
}
|
|
11597
11746
|
function replaceVars(template, vars) {
|
|
@@ -11640,7 +11789,7 @@ function visualDesignGuide() {
|
|
|
11640
11789
|
return _visualDesignGuide;
|
|
11641
11790
|
}
|
|
11642
11791
|
function loadAgentPrompt(filename) {
|
|
11643
|
-
const raw = readFileSync13(
|
|
11792
|
+
const raw = readFileSync13(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
|
|
11644
11793
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
11645
11794
|
if (!match)
|
|
11646
11795
|
throw new Error(`Agent prompt ${filename} is missing frontmatter`);
|
|
@@ -11955,8 +12104,8 @@ var init_builders = __esm(() => {
|
|
|
11955
12104
|
init_model_catalog();
|
|
11956
12105
|
init_config();
|
|
11957
12106
|
init_logger();
|
|
11958
|
-
PROMPTS_DIR =
|
|
11959
|
-
SESSIONS_DIR =
|
|
12107
|
+
PROMPTS_DIR = resolve14(PROJECT_ROOT, "src/prompts");
|
|
12108
|
+
SESSIONS_DIR = resolve14(PROMPTS_DIR, "sessions");
|
|
11960
12109
|
defaultPromptBuilders = createPromptBuilders();
|
|
11961
12110
|
buildTopicSystemPrompt = defaultPromptBuilders.buildTopicSystemPrompt;
|
|
11962
12111
|
buildChannelSystemPrompt = defaultPromptBuilders.buildChannelSystemPrompt;
|
|
@@ -12098,50 +12247,50 @@ __export(exports_wiki, {
|
|
|
12098
12247
|
getTopicMemoryFilePaths: () => getTopicMemoryFilePaths,
|
|
12099
12248
|
getSharedWikiDir: () => getSharedWikiDir
|
|
12100
12249
|
});
|
|
12101
|
-
import { existsSync as
|
|
12102
|
-
import { basename as basename2, dirname as
|
|
12250
|
+
import { existsSync as existsSync17, readdirSync as readdirSync3, statSync as statSync6 } from "fs";
|
|
12251
|
+
import { basename as basename2, dirname as dirname12, join as join18 } from "path";
|
|
12103
12252
|
function getWikiDir(_userId, workspaceDir = resolveStorageWorkspaceDir()) {
|
|
12104
|
-
return
|
|
12253
|
+
return join18(workspaceDir, "wiki");
|
|
12105
12254
|
}
|
|
12106
12255
|
function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
|
|
12107
|
-
return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() :
|
|
12256
|
+
return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join18(workspaceDir, "wiki");
|
|
12108
12257
|
}
|
|
12109
12258
|
function findLatestSummaryFile(wikiDir, safeTopic) {
|
|
12110
|
-
const summariesDir =
|
|
12111
|
-
if (!
|
|
12259
|
+
const summariesDir = join18(wikiDir, "summaries");
|
|
12260
|
+
if (!existsSync17(summariesDir))
|
|
12112
12261
|
return null;
|
|
12113
12262
|
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(
|
|
12263
|
+
const mtimeDelta = statSync6(join18(summariesDir, right)).mtimeMs - statSync6(join18(summariesDir, left)).mtimeMs;
|
|
12115
12264
|
return mtimeDelta || right.localeCompare(left, undefined, { numeric: true });
|
|
12116
12265
|
});
|
|
12117
|
-
return files.length > 0 ?
|
|
12266
|
+
return files.length > 0 ? join18(summariesDir, files[0]) : null;
|
|
12118
12267
|
}
|
|
12119
12268
|
function getTopicMemoryFilePaths(_userId, topicName, forkOrigin, workspaceDir = resolveStorageWorkspaceDir()) {
|
|
12120
12269
|
const wikiDir = getSharedWikiDir(workspaceDir);
|
|
12121
12270
|
const resolveBrief = (name) => {
|
|
12122
12271
|
const safe = sanitizeTopicName(name, true);
|
|
12123
|
-
const brief =
|
|
12272
|
+
const brief = join18(wikiDir, "topic", `${safe}.md`);
|
|
12124
12273
|
const latestSummary = findLatestSummaryFile(wikiDir, safe);
|
|
12125
12274
|
return { brief, latestSummary };
|
|
12126
12275
|
};
|
|
12127
|
-
const archiveDir =
|
|
12276
|
+
const archiveDir = join18(wikiDir, "archive");
|
|
12128
12277
|
const archiveExistsFor = (name) => {
|
|
12129
12278
|
const safe = sanitizeTopicName(name);
|
|
12130
|
-
return
|
|
12279
|
+
return existsSync17(archiveDir) && readdirSync3(archiveDir).some((f) => f.endsWith(".jsonl") && f.startsWith(`${safe}_`));
|
|
12131
12280
|
};
|
|
12132
12281
|
const sourceTopic = forkOrigin ?? topicName;
|
|
12133
12282
|
const hasArchive = archiveExistsFor(sourceTopic) || (forkOrigin ? archiveExistsFor(topicName) : false);
|
|
12134
12283
|
const target = resolveBrief(sourceTopic);
|
|
12135
|
-
if (
|
|
12284
|
+
if (existsSync17(target.brief)) {
|
|
12136
12285
|
return {
|
|
12137
|
-
memoryDir:
|
|
12286
|
+
memoryDir: dirname12(target.brief),
|
|
12138
12287
|
memoryFiles: [basename2(target.brief)],
|
|
12139
12288
|
...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
|
|
12140
12289
|
...hasArchive ? { hasArchive: true } : {}
|
|
12141
12290
|
};
|
|
12142
12291
|
}
|
|
12143
12292
|
return {
|
|
12144
|
-
memoryDir:
|
|
12293
|
+
memoryDir: join18(wikiDir, "topic"),
|
|
12145
12294
|
memoryFiles: [],
|
|
12146
12295
|
...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
|
|
12147
12296
|
...hasArchive ? { hasArchive: true } : {}
|
|
@@ -12153,8 +12302,8 @@ var init_wiki = __esm(async () => {
|
|
|
12153
12302
|
|
|
12154
12303
|
// ../../packages/core/src/agents/archiver.ts
|
|
12155
12304
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
12156
|
-
import { existsSync as
|
|
12157
|
-
import { join as
|
|
12305
|
+
import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
|
|
12306
|
+
import { join as join19 } from "path";
|
|
12158
12307
|
function resolveMemoryLanguage() {
|
|
12159
12308
|
const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
|
|
12160
12309
|
return override && override.length > 0 ? override : resolveOutputLanguage();
|
|
@@ -12405,10 +12554,10 @@ function distillOneLine(summaryMd) {
|
|
|
12405
12554
|
return "";
|
|
12406
12555
|
}
|
|
12407
12556
|
function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
|
|
12408
|
-
const dir =
|
|
12557
|
+
const dir = join19(storage.getWikiDir(), "summaries");
|
|
12409
12558
|
if (!storage.fileExists(dir))
|
|
12410
12559
|
return null;
|
|
12411
|
-
const predicted =
|
|
12560
|
+
const predicted = join19(dir, wikiSummaryFilename(date, topicTitle, topicId));
|
|
12412
12561
|
if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
|
|
12413
12562
|
return predicted;
|
|
12414
12563
|
let best = null;
|
|
@@ -12416,7 +12565,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
|
|
|
12416
12565
|
if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
|
|
12417
12566
|
continue;
|
|
12418
12567
|
}
|
|
12419
|
-
const p =
|
|
12568
|
+
const p = join19(dir, f);
|
|
12420
12569
|
try {
|
|
12421
12570
|
const m = storage.fileModifiedAt(p);
|
|
12422
12571
|
if (m >= sinceMs && (!best || m > best.mtime))
|
|
@@ -12500,7 +12649,7 @@ var init_archiver = __esm(async () => {
|
|
|
12500
12649
|
defaultArchiverRuntime = createArchiverRuntime({
|
|
12501
12650
|
storage: {
|
|
12502
12651
|
getWikiDir: getSharedWikiDir,
|
|
12503
|
-
fileExists:
|
|
12652
|
+
fileExists: existsSync18,
|
|
12504
12653
|
listDirectory: readdirSync4,
|
|
12505
12654
|
readTextFile: (path) => readFileSync14(path, "utf-8"),
|
|
12506
12655
|
fileSize: (path) => statSync7(path).size,
|
|
@@ -12632,13 +12781,13 @@ __export(exports_topic_archive, {
|
|
|
12632
12781
|
archiveConversationEvents: () => archiveConversationEvents
|
|
12633
12782
|
});
|
|
12634
12783
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
12635
|
-
import { join as
|
|
12784
|
+
import { join as join20 } from "path";
|
|
12636
12785
|
function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
12637
12786
|
const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
|
|
12638
12787
|
if (rows.length === 0)
|
|
12639
12788
|
return null;
|
|
12640
12789
|
const safeTopic = sanitizeTopicName(topicTitle, true);
|
|
12641
|
-
const archiveDir =
|
|
12790
|
+
const archiveDir = join20(getSharedWikiDir(), "archive");
|
|
12642
12791
|
mkdirSync11(archiveDir, { recursive: true });
|
|
12643
12792
|
const date = new Date().toISOString().slice(0, 10);
|
|
12644
12793
|
const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
|
|
@@ -12651,7 +12800,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
|
12651
12800
|
let path;
|
|
12652
12801
|
while (true) {
|
|
12653
12802
|
filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
|
|
12654
|
-
path =
|
|
12803
|
+
path = join20(archiveDir, filename);
|
|
12655
12804
|
try {
|
|
12656
12805
|
writeFileSync9(path, body, { flag: "wx" });
|
|
12657
12806
|
break;
|
|
@@ -12696,7 +12845,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
|
|
|
12696
12845
|
const entries = readRawConversation(userId, topicTitle);
|
|
12697
12846
|
if (entries.length === 0)
|
|
12698
12847
|
return null;
|
|
12699
|
-
const archiveDir =
|
|
12848
|
+
const archiveDir = join20(getSharedWikiDir(), "archive");
|
|
12700
12849
|
mkdirSync11(archiveDir, { recursive: true });
|
|
12701
12850
|
const safeTopic = sanitizeTopicName(topicTitle, true);
|
|
12702
12851
|
const date = new Date().toISOString().slice(0, 10);
|
|
@@ -12724,7 +12873,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
|
|
|
12724
12873
|
let counter = 1;
|
|
12725
12874
|
while (true) {
|
|
12726
12875
|
const suffix = counter === 1 ? "" : `_${counter}`;
|
|
12727
|
-
const path =
|
|
12876
|
+
const path = join20(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
|
|
12728
12877
|
try {
|
|
12729
12878
|
writeFileSync9(path, body, { flag: "wx" });
|
|
12730
12879
|
logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
|
|
@@ -13051,7 +13200,7 @@ var init_idle_archiver = __esm(async () => {
|
|
|
13051
13200
|
|
|
13052
13201
|
// ../../packages/core/src/agents/topic-cleanup.ts
|
|
13053
13202
|
import { mkdirSync as mkdirSync12, renameSync as renameSync6, unlinkSync as unlinkSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
13054
|
-
import { dirname as
|
|
13203
|
+
import { dirname as dirname13 } from "path";
|
|
13055
13204
|
function collectSessionIdsByAgent(entries, extraSessions = []) {
|
|
13056
13205
|
const out = new Map;
|
|
13057
13206
|
for (const e of entries) {
|
|
@@ -13117,7 +13266,7 @@ function createTopicLogMaintenance(host) {
|
|
|
13117
13266
|
const path = runtimeHost.activeConversationPath(opts.userId, opts.topicName);
|
|
13118
13267
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
13119
13268
|
try {
|
|
13120
|
-
mkdirSync12(
|
|
13269
|
+
mkdirSync12(dirname13(path), { recursive: true });
|
|
13121
13270
|
writeFileSync10(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
|
|
13122
13271
|
`)}
|
|
13123
13272
|
` : "", { flag: "wx" });
|
|
@@ -13734,15 +13883,15 @@ var init_runtime_turn_requests = __esm(async () => {
|
|
|
13734
13883
|
// ../../packages/core/src/topics/session.ts
|
|
13735
13884
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
13736
13885
|
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
|
|
13737
|
-
import { tmpdir as
|
|
13738
|
-
import { join as
|
|
13886
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
13887
|
+
import { join as join21 } from "path";
|
|
13739
13888
|
async function waitForMemoryArchive(settled, timeoutMs) {
|
|
13740
13889
|
let timer;
|
|
13741
13890
|
try {
|
|
13742
13891
|
return await Promise.race([
|
|
13743
13892
|
settled.then(() => true),
|
|
13744
|
-
new Promise((
|
|
13745
|
-
timer = setTimeout(() =>
|
|
13893
|
+
new Promise((resolve15) => {
|
|
13894
|
+
timer = setTimeout(() => resolve15(false), timeoutMs);
|
|
13746
13895
|
timer.unref?.();
|
|
13747
13896
|
})
|
|
13748
13897
|
]);
|
|
@@ -13806,8 +13955,8 @@ async function restartTopicSession(topicId, userId, reason = "topic-session-rest
|
|
|
13806
13955
|
};
|
|
13807
13956
|
}
|
|
13808
13957
|
let settleMemoryArchive;
|
|
13809
|
-
const memoryArchiveSettled = new Promise((
|
|
13810
|
-
settleMemoryArchive =
|
|
13958
|
+
const memoryArchiveSettled = new Promise((resolve15) => {
|
|
13959
|
+
settleMemoryArchive = resolve15;
|
|
13811
13960
|
});
|
|
13812
13961
|
const archiveStatus = (options.archiveMemory ?? archiveActiveTopicForMemory)(topicId, userId, {
|
|
13813
13962
|
reason: "reset",
|
|
@@ -13988,7 +14137,7 @@ function formatCompactElapsed(startedAt) {
|
|
|
13988
14137
|
async function summarizeTopicContext(request) {
|
|
13989
14138
|
const startedAt = Date.now();
|
|
13990
14139
|
const sessionIds = [];
|
|
13991
|
-
const compactCwd = mkdtempSync2(
|
|
14140
|
+
const compactCwd = mkdtempSync2(join21(tmpdir4(), "negotium-compact-"));
|
|
13992
14141
|
const abortController = new AbortController;
|
|
13993
14142
|
const relayAbort = () => abortController.abort(request.signal?.reason);
|
|
13994
14143
|
if (request.signal?.aborted)
|
|
@@ -14015,7 +14164,7 @@ async function summarizeTopicContext(request) {
|
|
|
14015
14164
|
let error = "";
|
|
14016
14165
|
let toolViolation = false;
|
|
14017
14166
|
let compactionLogCalls = 0;
|
|
14018
|
-
const compactionLogPath =
|
|
14167
|
+
const compactionLogPath = join21(compactCwd, "conversation.log");
|
|
14019
14168
|
try {
|
|
14020
14169
|
const compactionMcp = useCompactionLog ? {
|
|
14021
14170
|
compact_log: {
|
|
@@ -15336,14 +15485,14 @@ var init_self_config = __esm(async () => {
|
|
|
15336
15485
|
});
|
|
15337
15486
|
|
|
15338
15487
|
// ../../packages/core/src/query/session-inbox-path.ts
|
|
15339
|
-
import { join as
|
|
15488
|
+
import { join as join22 } from "path";
|
|
15340
15489
|
function sessionInboxPath(userId, topicId) {
|
|
15341
15490
|
const key = Buffer.from(topicId, "utf8").toString("base64url");
|
|
15342
|
-
return
|
|
15491
|
+
return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
|
|
15343
15492
|
}
|
|
15344
15493
|
function scheduledSessionInboxPath(userId, topicId) {
|
|
15345
15494
|
const key = Buffer.from(topicId, "utf8").toString("base64url");
|
|
15346
|
-
return
|
|
15495
|
+
return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
|
|
15347
15496
|
}
|
|
15348
15497
|
function decodeTopicIdFileName(fileName, suffix) {
|
|
15349
15498
|
if (!fileName.startsWith(TOPIC_ID_FILE_PREFIX) || !fileName.endsWith(suffix))
|
|
@@ -15371,13 +15520,13 @@ var init_session_inbox_path = __esm(() => {
|
|
|
15371
15520
|
|
|
15372
15521
|
// ../../packages/core/src/query/session-inbox-cleanup.ts
|
|
15373
15522
|
import { unlinkSync as unlinkSync14 } from "fs";
|
|
15374
|
-
import { basename as basename3, join as
|
|
15523
|
+
import { basename as basename3, join as join23 } from "path";
|
|
15375
15524
|
function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
|
|
15376
15525
|
const live = sessionInboxPath(userId, topicId);
|
|
15377
15526
|
const scheduled = scheduledSessionInboxPath(userId, topicId);
|
|
15378
15527
|
const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
|
|
15379
15528
|
if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename3(legacyTopicTitle) === legacyTopicTitle) {
|
|
15380
|
-
const legacyBase =
|
|
15529
|
+
const legacyBase = join23(SESSION_INBOX_DIR, userId, legacyTopicTitle);
|
|
15381
15530
|
for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
|
|
15382
15531
|
candidates.add(`${legacyBase}${suffix}`);
|
|
15383
15532
|
}
|
|
@@ -15403,16 +15552,16 @@ var init_session_inbox_cleanup = __esm(() => {
|
|
|
15403
15552
|
|
|
15404
15553
|
// ../../packages/core/src/query/state.ts
|
|
15405
15554
|
import { mkdirSync as mkdirSync14, renameSync as renameSync7, unlinkSync as unlinkSync15, writeFileSync as writeFileSync12 } from "fs";
|
|
15406
|
-
import { basename as basename4, join as
|
|
15555
|
+
import { basename as basename4, join as join24 } from "path";
|
|
15407
15556
|
function createQueryStateStore(options) {
|
|
15408
15557
|
const sanitize = options.sanitizeTopicId ?? sanitizeId;
|
|
15409
|
-
const queryStateDirPath = (userId) =>
|
|
15410
|
-
const queryStateFile = (userId, topicId) =>
|
|
15558
|
+
const queryStateDirPath = (userId) => join24(options.usersLogDir, String(userId), "active-queries");
|
|
15559
|
+
const queryStateFile = (userId, topicId) => join24(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
|
|
15411
15560
|
const legacyQueryStateFile = (userId, topicName) => {
|
|
15412
15561
|
if (!topicName || topicName === "." || topicName === ".." || basename4(topicName) !== topicName) {
|
|
15413
15562
|
return null;
|
|
15414
15563
|
}
|
|
15415
|
-
return
|
|
15564
|
+
return join24(queryStateDirPath(userId), `${topicName}.json`);
|
|
15416
15565
|
};
|
|
15417
15566
|
return {
|
|
15418
15567
|
write(userId, topicId, topicName, task) {
|
|
@@ -15573,30 +15722,30 @@ import {
|
|
|
15573
15722
|
unlinkSync as unlinkSync16,
|
|
15574
15723
|
writeFileSync as writeFileSync13
|
|
15575
15724
|
} from "fs";
|
|
15576
|
-
import { dirname as
|
|
15725
|
+
import { dirname as dirname14, join as join25 } from "path";
|
|
15577
15726
|
function pendingAskDir(userId) {
|
|
15578
15727
|
const rawUserId = String(userId);
|
|
15579
15728
|
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
|
|
15729
|
+
return join25(resolveStorageSessionAsksDir(), safeUserId);
|
|
15581
15730
|
}
|
|
15582
15731
|
function encodeAskKey(key) {
|
|
15583
15732
|
return JSON.stringify([key.from, key.to]);
|
|
15584
15733
|
}
|
|
15585
15734
|
function pendingAskPath(key) {
|
|
15586
15735
|
const digest = createHash6("sha256").update(encodeAskKey(key)).digest("hex");
|
|
15587
|
-
return
|
|
15736
|
+
return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
|
|
15588
15737
|
}
|
|
15589
15738
|
function v2PendingAskPath(key) {
|
|
15590
15739
|
const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
|
|
15591
|
-
return
|
|
15740
|
+
return join25(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
|
|
15592
15741
|
}
|
|
15593
15742
|
function legacyPendingAskPath(key) {
|
|
15594
15743
|
if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
|
|
15595
15744
|
return null;
|
|
15596
15745
|
}
|
|
15597
15746
|
const dir = pendingAskDir(key.userId);
|
|
15598
|
-
const candidate =
|
|
15599
|
-
return
|
|
15747
|
+
const candidate = join25(dir, `${key.from}___${key.to}.pending`);
|
|
15748
|
+
return dirname14(candidate) === dir ? candidate : null;
|
|
15600
15749
|
}
|
|
15601
15750
|
function parsePendingAskFilename(fileName) {
|
|
15602
15751
|
if (!fileName.endsWith(".pending"))
|
|
@@ -15836,7 +15985,7 @@ function listPendingAsksForCaller(args) {
|
|
|
15836
15985
|
const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
|
|
15837
15986
|
if (!parsed)
|
|
15838
15987
|
continue;
|
|
15839
|
-
const path =
|
|
15988
|
+
const path = join25(dir, fileName);
|
|
15840
15989
|
const record = readPendingAskFile(path, {
|
|
15841
15990
|
userId: args.userId,
|
|
15842
15991
|
from: parsed.from,
|
|
@@ -15878,7 +16027,7 @@ function deletePendingAsksForTopic(args) {
|
|
|
15878
16027
|
}
|
|
15879
16028
|
let deleted = 0;
|
|
15880
16029
|
for (const fileName of files) {
|
|
15881
|
-
const path =
|
|
16030
|
+
const path = join25(dir, fileName);
|
|
15882
16031
|
const parsed = parsePendingAskFilename(fileName);
|
|
15883
16032
|
const record = readPendingAskFile(path, {
|
|
15884
16033
|
userId: args.userId,
|
|
@@ -16296,7 +16445,7 @@ __export(exports_token_stats, {
|
|
|
16296
16445
|
});
|
|
16297
16446
|
import { createHash as createHash7 } from "crypto";
|
|
16298
16447
|
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
16299
|
-
import { join as
|
|
16448
|
+
import { join as join26 } from "path";
|
|
16300
16449
|
function emptyBucket() {
|
|
16301
16450
|
return {
|
|
16302
16451
|
inputTokens: 0,
|
|
@@ -16315,7 +16464,7 @@ function queriesPath(userId) {
|
|
|
16315
16464
|
const fileId = tokenStatsFileId(userId);
|
|
16316
16465
|
const logDir = resolveStorageLogDir();
|
|
16317
16466
|
mkdirSync16(logDir, { recursive: true });
|
|
16318
|
-
return
|
|
16467
|
+
return join26(logDir, `token-queries-${fileId}.jsonl`);
|
|
16319
16468
|
}
|
|
16320
16469
|
function loadRecords(userId) {
|
|
16321
16470
|
try {
|
|
@@ -16744,7 +16893,7 @@ var init_lifecycle = __esm(async () => {
|
|
|
16744
16893
|
// ../../packages/core/src/runtime/attachments.ts
|
|
16745
16894
|
import { randomUUID as randomUUID14 } from "crypto";
|
|
16746
16895
|
import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
|
|
16747
|
-
import { basename as basename5, join as
|
|
16896
|
+
import { basename as basename5, join as join27 } from "path";
|
|
16748
16897
|
function workspaceCwdFor(topicId) {
|
|
16749
16898
|
return resolveTopicWorkspaceDir(topicId);
|
|
16750
16899
|
}
|
|
@@ -16757,7 +16906,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
|
|
|
16757
16906
|
if (!attachmentIds?.length)
|
|
16758
16907
|
return [];
|
|
16759
16908
|
const out = [];
|
|
16760
|
-
const destDir =
|
|
16909
|
+
const destDir = join27(workspaceCwdFor(topicId), "attachments", queryId);
|
|
16761
16910
|
for (const rawId of attachmentIds) {
|
|
16762
16911
|
if (typeof rawId !== "string")
|
|
16763
16912
|
continue;
|
|
@@ -16774,7 +16923,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
|
|
|
16774
16923
|
mkdirSync17(destDir, { recursive: true });
|
|
16775
16924
|
const index = String(out.length + 1).padStart(2, "0");
|
|
16776
16925
|
const safeName = safeAttachmentFilename(attachment.filename, fileId);
|
|
16777
|
-
const destPath =
|
|
16926
|
+
const destPath = join27(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
|
|
16778
16927
|
copyFileSync2(sourcePath, destPath);
|
|
16779
16928
|
out.push({
|
|
16780
16929
|
id: attachment.id,
|
|
@@ -16804,10 +16953,10 @@ function promptWithAttachments(prompt, attachments) {
|
|
|
16804
16953
|
return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
|
|
16805
16954
|
}
|
|
16806
16955
|
function ingestAttachment(args) {
|
|
16807
|
-
const destDir =
|
|
16956
|
+
const destDir = join27(UPLOADS_DIR, args.topicId);
|
|
16808
16957
|
mkdirSync17(destDir, { recursive: true });
|
|
16809
16958
|
const safeName = safeAttachmentFilename(args.filename, "upload");
|
|
16810
|
-
const destPath =
|
|
16959
|
+
const destPath = join27(destDir, `${Date.now()}-${randomUUID14().slice(0, 8)}-${safeName}`);
|
|
16811
16960
|
if (args.sourcePath !== undefined) {
|
|
16812
16961
|
copyFileSync2(args.sourcePath, destPath);
|
|
16813
16962
|
} else if (args.bytes !== undefined) {
|
|
@@ -16982,12 +17131,12 @@ var init_errors = __esm(() => {
|
|
|
16982
17131
|
|
|
16983
17132
|
// ../../packages/core/src/runtime/event-heartbeat.ts
|
|
16984
17133
|
function nextOrHeartbeat(pending, intervalMs) {
|
|
16985
|
-
return new Promise((
|
|
16986
|
-
const timer = setTimeout(() =>
|
|
17134
|
+
return new Promise((resolve15, reject) => {
|
|
17135
|
+
const timer = setTimeout(() => resolve15({ kind: "heartbeat" }), intervalMs);
|
|
16987
17136
|
timer.unref?.();
|
|
16988
17137
|
pending.then((result) => {
|
|
16989
17138
|
clearTimeout(timer);
|
|
16990
|
-
|
|
17139
|
+
resolve15({ kind: "event", result });
|
|
16991
17140
|
}, (error) => {
|
|
16992
17141
|
clearTimeout(timer);
|
|
16993
17142
|
reject(error);
|
|
@@ -17324,7 +17473,7 @@ var init_visual_html = __esm(() => {
|
|
|
17324
17473
|
|
|
17325
17474
|
// ../../packages/core/src/runtime/visuals.ts
|
|
17326
17475
|
import { realpathSync as realpathSync4 } from "fs";
|
|
17327
|
-
import { isAbsolute as isAbsolute4, resolve as
|
|
17476
|
+
import { isAbsolute as isAbsolute4, resolve as resolve15 } from "path";
|
|
17328
17477
|
function activeVisualHtmlForPrompt(html) {
|
|
17329
17478
|
if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
|
|
17330
17479
|
return { html, omittedChars: 0 };
|
|
@@ -17375,8 +17524,8 @@ function topicAllowsVisualFileId(topicId, fileId) {
|
|
|
17375
17524
|
return topicHasAttachmentFileId(topicId, fileId) || topicHasVisualFileId(topicId, fileId);
|
|
17376
17525
|
}
|
|
17377
17526
|
function isPathInside(baseDir, filePath) {
|
|
17378
|
-
const base =
|
|
17379
|
-
const normalized =
|
|
17527
|
+
const base = resolve15(baseDir);
|
|
17528
|
+
const normalized = resolve15(filePath);
|
|
17380
17529
|
try {
|
|
17381
17530
|
const realBase = realpathSync4(base);
|
|
17382
17531
|
const real = realpathSync4(normalized);
|
|
@@ -17438,7 +17587,7 @@ function resolveVisualMediaInput(topicId, input) {
|
|
|
17438
17587
|
}
|
|
17439
17588
|
const rawPath = input.file_path.trim();
|
|
17440
17589
|
const cwd = workspaceCwdFor(topicId);
|
|
17441
|
-
const candidate = isAbsolute4(rawPath) ? rawPath :
|
|
17590
|
+
const candidate = isAbsolute4(rawPath) ? rawPath : resolve15(cwd, rawPath);
|
|
17442
17591
|
if (!isPathInside(cwd, candidate)) {
|
|
17443
17592
|
return { error: "file_path must be inside the topic workspace" };
|
|
17444
17593
|
}
|
|
@@ -17476,7 +17625,7 @@ var init_visuals = __esm(async () => {
|
|
|
17476
17625
|
// ../../packages/core/src/runtime/turn-event-stream.ts
|
|
17477
17626
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
17478
17627
|
import { realpathSync as realpathSync5, statSync as statSync9 } from "fs";
|
|
17479
|
-
import { isAbsolute as isAbsolute5, resolve as
|
|
17628
|
+
import { isAbsolute as isAbsolute5, resolve as resolve16 } from "path";
|
|
17480
17629
|
function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
|
|
17481
17630
|
if (getRoomQuery(topicId)?.queryId !== queryId)
|
|
17482
17631
|
return false;
|
|
@@ -17829,7 +17978,7 @@ ${JSON.stringify(event.input ?? {})}`);
|
|
|
17829
17978
|
case "file":
|
|
17830
17979
|
if (!silent && peerBridge) {
|
|
17831
17980
|
const cwd = workspaceCwdFor(topicId);
|
|
17832
|
-
const path = isAbsolute5(event.path) ? event.path :
|
|
17981
|
+
const path = isAbsolute5(event.path) ? event.path : resolve16(cwd, event.path);
|
|
17833
17982
|
if (!isPathInside(cwd, path)) {
|
|
17834
17983
|
logger.warn({ topicId, path }, "peer output file is outside the topic workspace");
|
|
17835
17984
|
break;
|
|
@@ -18117,15 +18266,15 @@ __export(exports_app_settings, {
|
|
|
18117
18266
|
getGlobalAiName: () => getGlobalAiName,
|
|
18118
18267
|
DEFAULT_AI_NAME: () => DEFAULT_AI_NAME
|
|
18119
18268
|
});
|
|
18120
|
-
import { existsSync as
|
|
18121
|
-
import { dirname as
|
|
18269
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync16 } from "fs";
|
|
18270
|
+
import { dirname as dirname15, join as join28 } from "path";
|
|
18122
18271
|
function settingsFile() {
|
|
18123
|
-
return
|
|
18272
|
+
return join28(resolveStorageDataDir(), "otium-settings.json");
|
|
18124
18273
|
}
|
|
18125
18274
|
function getGlobalAiName() {
|
|
18126
18275
|
const path = settingsFile();
|
|
18127
18276
|
try {
|
|
18128
|
-
if (
|
|
18277
|
+
if (existsSync19(path)) {
|
|
18129
18278
|
const data = JSON.parse(readFileSync16(path, "utf8"));
|
|
18130
18279
|
if (typeof data.aiName === "string" && data.aiName.trim()) {
|
|
18131
18280
|
return data.aiName.trim();
|
|
@@ -18138,7 +18287,7 @@ function setGlobalAiName(name) {
|
|
|
18138
18287
|
const path = settingsFile();
|
|
18139
18288
|
const aiName = name.trim() || DEFAULT_AI_NAME;
|
|
18140
18289
|
try {
|
|
18141
|
-
mkdirSync18(
|
|
18290
|
+
mkdirSync18(dirname15(path), { recursive: true });
|
|
18142
18291
|
writeFileSync16(path, JSON.stringify({ aiName }, null, 2));
|
|
18143
18292
|
} catch {}
|
|
18144
18293
|
return aiName;
|
|
@@ -18223,8 +18372,8 @@ __export(exports_turn_runner, {
|
|
|
18223
18372
|
AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
|
|
18224
18373
|
});
|
|
18225
18374
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
18226
|
-
import { existsSync as
|
|
18227
|
-
import { join as
|
|
18375
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync19, readdirSync as readdirSync6, statSync as statSync10 } from "fs";
|
|
18376
|
+
import { join as join29 } from "path";
|
|
18228
18377
|
function withDefaultPlaywright(configuredMcp, isManager) {
|
|
18229
18378
|
if (isManager)
|
|
18230
18379
|
return configuredMcp;
|
|
@@ -18279,8 +18428,8 @@ function appendAskReplyMessage(topicId, text2, agentType) {
|
|
|
18279
18428
|
return message;
|
|
18280
18429
|
}
|
|
18281
18430
|
function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
|
|
18282
|
-
const preferred =
|
|
18283
|
-
if (preferExact &&
|
|
18431
|
+
const preferred = join29(directory, preferredFilename);
|
|
18432
|
+
if (preferExact && existsSync20(preferred))
|
|
18284
18433
|
return preferred;
|
|
18285
18434
|
let newestLegacyId = null;
|
|
18286
18435
|
let newestTitle = null;
|
|
@@ -18290,7 +18439,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
|
|
|
18290
18439
|
const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
|
|
18291
18440
|
if (!titleMatch && !legacyIdMatch)
|
|
18292
18441
|
continue;
|
|
18293
|
-
const path =
|
|
18442
|
+
const path = join29(directory, filename);
|
|
18294
18443
|
const mtimeMs = statSync10(path).mtimeMs;
|
|
18295
18444
|
if (titleMatch) {
|
|
18296
18445
|
if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
|
|
@@ -18304,10 +18453,10 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
|
|
|
18304
18453
|
return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
|
|
18305
18454
|
}
|
|
18306
18455
|
function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
|
|
18307
|
-
const briefFile = resolveWikiMirrorPath(
|
|
18308
|
-
const hasBriefFile =
|
|
18309
|
-
const latestSummaryCandidate = resolveWikiMirrorPath(
|
|
18310
|
-
const latestSummaryFile =
|
|
18456
|
+
const briefFile = resolveWikiMirrorPath(join29(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
|
|
18457
|
+
const hasBriefFile = existsSync20(briefFile) && statSync10(briefFile).isFile();
|
|
18458
|
+
const latestSummaryCandidate = resolveWikiMirrorPath(join29(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
|
|
18459
|
+
const latestSummaryFile = existsSync20(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
|
|
18311
18460
|
return { briefFile, hasBriefFile, latestSummaryFile };
|
|
18312
18461
|
}
|
|
18313
18462
|
async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
|
|
@@ -21054,9 +21203,9 @@ var init_vault_command = __esm(async () => {
|
|
|
21054
21203
|
|
|
21055
21204
|
// ../../packages/core/src/media/text-extractor.ts
|
|
21056
21205
|
import { execFile } from "child_process";
|
|
21057
|
-
import { existsSync as
|
|
21206
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync21, readFileSync as readFileSync17, rmSync as rmSync7 } from "fs";
|
|
21058
21207
|
import { readFile } from "fs/promises";
|
|
21059
|
-
import { extname, join as
|
|
21208
|
+
import { extname, join as join30 } from "path";
|
|
21060
21209
|
import { promisify } from "util";
|
|
21061
21210
|
async function extractText(filePath) {
|
|
21062
21211
|
const ext = extname(filePath).toLowerCase();
|
|
@@ -21161,7 +21310,7 @@ async function extractFromImage(filePath) {
|
|
|
21161
21310
|
function isTranscriptionConfigured(opts = {}) {
|
|
21162
21311
|
const ffmpeg = opts.ffmpegBin ?? FFMPEG_BIN;
|
|
21163
21312
|
const wrapper = opts.wrapperPath ?? FASTER_WHISPER_WRAPPER;
|
|
21164
|
-
return Boolean(ffmpeg) &&
|
|
21313
|
+
return Boolean(ffmpeg) && existsSync21(wrapper);
|
|
21165
21314
|
}
|
|
21166
21315
|
async function transcribeAudio(filePath, opts = {}) {
|
|
21167
21316
|
if (!isTranscriptionConfigured(opts))
|
|
@@ -21173,7 +21322,7 @@ async function extractFromAudio(filePath, opts = {}) {
|
|
|
21173
21322
|
const tmpDir = `${filePath}_whisper_tmp`;
|
|
21174
21323
|
try {
|
|
21175
21324
|
mkdirSync21(tmpDir, { recursive: true });
|
|
21176
|
-
const mp3Path =
|
|
21325
|
+
const mp3Path = join30(tmpDir, "audio.mp3");
|
|
21177
21326
|
await execFileAsync(opts.ffmpegBin ?? FFMPEG_BIN2, [
|
|
21178
21327
|
"-y",
|
|
21179
21328
|
"-i",
|
|
@@ -21200,8 +21349,8 @@ async function extractFromAudio(filePath, opts = {}) {
|
|
|
21200
21349
|
"--output-format",
|
|
21201
21350
|
"txt"
|
|
21202
21351
|
], { timeout: 120000 });
|
|
21203
|
-
const txtPath =
|
|
21204
|
-
const text2 =
|
|
21352
|
+
const txtPath = join30(tmpDir, "audio.txt");
|
|
21353
|
+
const text2 = existsSync21(txtPath) ? readFileSync17(txtPath, "utf-8").trim() : null;
|
|
21205
21354
|
if (!text2) {
|
|
21206
21355
|
return { text: null, method: "whisper", error: "Whisper produced no text" };
|
|
21207
21356
|
}
|
|
@@ -21372,19 +21521,19 @@ var init_lifecycle2 = __esm(() => {
|
|
|
21372
21521
|
});
|
|
21373
21522
|
|
|
21374
21523
|
// ../../packages/core/src/platform/log-rotation.ts
|
|
21375
|
-
import { existsSync as
|
|
21376
|
-
import { join as
|
|
21524
|
+
import { existsSync as existsSync22, renameSync as renameSync8, statSync as statSync11 } from "fs";
|
|
21525
|
+
import { join as join31 } from "path";
|
|
21377
21526
|
function rotateOversizedLog(logPath, maxBytes = DEFAULT_DAEMON_LOG_ROTATE_BYTES) {
|
|
21378
21527
|
try {
|
|
21379
|
-
if (!
|
|
21528
|
+
if (!existsSync22(logPath))
|
|
21380
21529
|
return;
|
|
21381
21530
|
const { size } = statSync11(logPath);
|
|
21382
21531
|
if (size < maxBytes)
|
|
21383
21532
|
return;
|
|
21384
21533
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
21385
|
-
const dir =
|
|
21534
|
+
const dir = join31(logPath, "..");
|
|
21386
21535
|
const base = logPath.slice(dir.length + 1);
|
|
21387
|
-
renameSync8(logPath,
|
|
21536
|
+
renameSync8(logPath, join31(dir, `${base}.${stamp}`));
|
|
21388
21537
|
} catch {}
|
|
21389
21538
|
}
|
|
21390
21539
|
var DEFAULT_DAEMON_LOG_ROTATE_BYTES;
|
|
@@ -21577,7 +21726,7 @@ import {
|
|
|
21577
21726
|
rmSync as rmSync8,
|
|
21578
21727
|
statSync as statSync12
|
|
21579
21728
|
} from "fs";
|
|
21580
|
-
import { join as
|
|
21729
|
+
import { join as join32 } from "path";
|
|
21581
21730
|
function setBashrsCompletionSink(sink) {
|
|
21582
21731
|
completionSink = sink ?? defaultSink;
|
|
21583
21732
|
}
|
|
@@ -21604,15 +21753,15 @@ function readTail(filePath) {
|
|
|
21604
21753
|
}
|
|
21605
21754
|
}
|
|
21606
21755
|
function buildMessage(dir, result) {
|
|
21607
|
-
const stdout = readTail(
|
|
21608
|
-
const stderr = readTail(
|
|
21756
|
+
const stdout = readTail(join32(dir, "stdout.log"));
|
|
21757
|
+
const stderr = readTail(join32(dir, "stderr.log"));
|
|
21609
21758
|
const parts = [];
|
|
21610
21759
|
if (stdout.text.trim() || stdout.truncated) {
|
|
21611
|
-
parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${
|
|
21760
|
+
parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${join32(dir, "stdout.log")})` : ""}:
|
|
21612
21761
|
${stdout.text.trim()}`);
|
|
21613
21762
|
}
|
|
21614
21763
|
if (stderr.text.trim() || stderr.truncated) {
|
|
21615
|
-
parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${
|
|
21764
|
+
parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${join32(dir, "stderr.log")})` : ""}:
|
|
21616
21765
|
${stderr.text.trim()}`);
|
|
21617
21766
|
}
|
|
21618
21767
|
const header = watchHeader(result) ?? `[background_bash ${result.bash_id} finished]`;
|
|
@@ -21646,7 +21795,7 @@ function parseOwner(owner) {
|
|
|
21646
21795
|
return { userId, topicId };
|
|
21647
21796
|
}
|
|
21648
21797
|
function injectedMarker(dir) {
|
|
21649
|
-
return
|
|
21798
|
+
return join32(dir, "result.json.injected");
|
|
21650
21799
|
}
|
|
21651
21800
|
async function flushBashrsCompletions() {
|
|
21652
21801
|
let entries;
|
|
@@ -21657,8 +21806,8 @@ async function flushBashrsCompletions() {
|
|
|
21657
21806
|
}
|
|
21658
21807
|
const now = Date.now();
|
|
21659
21808
|
for (const bashId of entries) {
|
|
21660
|
-
const dir =
|
|
21661
|
-
const resultPath =
|
|
21809
|
+
const dir = join32(BASHRS_SPILL_ROOT, bashId);
|
|
21810
|
+
const resultPath = join32(dir, "result.json");
|
|
21662
21811
|
const marker = injectedMarker(dir);
|
|
21663
21812
|
try {
|
|
21664
21813
|
const markerStat = statSync12(marker);
|
|
@@ -21794,8 +21943,8 @@ function prepareDeliveryAck(messageId, claimTimeoutMs, deliveryTimeoutMs) {
|
|
|
21794
21943
|
let finishPromise = () => {};
|
|
21795
21944
|
let timer;
|
|
21796
21945
|
let unsubscribe = () => {};
|
|
21797
|
-
const promise = new Promise((
|
|
21798
|
-
finishPromise =
|
|
21946
|
+
const promise = new Promise((resolve17) => {
|
|
21947
|
+
finishPromise = resolve17;
|
|
21799
21948
|
});
|
|
21800
21949
|
unsubscribe = runtimeBus().subscribe((event) => {
|
|
21801
21950
|
if (event.type !== "ai-status")
|
|
@@ -21864,7 +22013,7 @@ function renderTurnFooter(msg) {
|
|
|
21864
22013
|
|
|
21865
22014
|
// ../../packages/core/src/outbox/file-ops.ts
|
|
21866
22015
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
21867
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
22016
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync23, readFileSync as readFileSync19, renameSync as renameSync10, unlinkSync as unlinkSync17 } from "fs";
|
|
21868
22017
|
function host() {
|
|
21869
22018
|
return hostContext.getStore() ?? defaultHost2;
|
|
21870
22019
|
}
|
|
@@ -21881,8 +22030,8 @@ function tryUnlink(path, label, reason) {
|
|
|
21881
22030
|
}
|
|
21882
22031
|
function drainOutboxFile(filePath, label) {
|
|
21883
22032
|
const processingPath = filePath + PROCESSING_SUFFIX;
|
|
21884
|
-
const hasPending =
|
|
21885
|
-
const hasLeftover =
|
|
22033
|
+
const hasPending = existsSync23(filePath);
|
|
22034
|
+
const hasLeftover = existsSync23(processingPath);
|
|
21886
22035
|
if (hasLeftover) {
|
|
21887
22036
|
const logMsg = `${label}: merging leftover .processing before claim`;
|
|
21888
22037
|
const logCtx = { filePath, processingPath, hadPending: hasPending };
|
|
@@ -21902,7 +22051,7 @@ function drainOutboxFile(filePath, label) {
|
|
|
21902
22051
|
tryUnlink(processingPath, label, "failed to clean up leftover after merge error");
|
|
21903
22052
|
}
|
|
21904
22053
|
}
|
|
21905
|
-
if (!
|
|
22054
|
+
if (!existsSync23(filePath))
|
|
21906
22055
|
return null;
|
|
21907
22056
|
try {
|
|
21908
22057
|
renameSync10(filePath, processingPath);
|
|
@@ -21956,7 +22105,7 @@ var init_file_ops = __esm(() => {
|
|
|
21956
22105
|
// ../../packages/core/src/runtime/inbox.ts
|
|
21957
22106
|
import { createHash as createHash9, randomUUID as randomUUID20 } from "crypto";
|
|
21958
22107
|
import { readdirSync as readdirSync8, statSync as statSync13, writeFileSync as writeFileSync17 } from "fs";
|
|
21959
|
-
import { join as
|
|
22108
|
+
import { join as join33 } from "path";
|
|
21960
22109
|
async function createAskForkPlan(options) {
|
|
21961
22110
|
const snapshot = structuredClone(options.entries);
|
|
21962
22111
|
const prepareSession = async () => options.synthesize(structuredClone(snapshot));
|
|
@@ -22096,7 +22245,7 @@ async function flushSessionInbox() {
|
|
|
22096
22245
|
return;
|
|
22097
22246
|
}
|
|
22098
22247
|
for (const uid of userDirs) {
|
|
22099
|
-
const userInboxDir =
|
|
22248
|
+
const userInboxDir = join33(SESSION_INBOX_DIR, uid);
|
|
22100
22249
|
let entries;
|
|
22101
22250
|
try {
|
|
22102
22251
|
entries = readdirSync8(userInboxDir);
|
|
@@ -22104,7 +22253,7 @@ async function flushSessionInbox() {
|
|
|
22104
22253
|
continue;
|
|
22105
22254
|
}
|
|
22106
22255
|
for (const entry of entries) {
|
|
22107
|
-
const entryPath =
|
|
22256
|
+
const entryPath = join33(userInboxDir, entry);
|
|
22108
22257
|
let isDir = false;
|
|
22109
22258
|
try {
|
|
22110
22259
|
isDir = statSync13(entryPath).isDirectory();
|
|
@@ -22194,7 +22343,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
|
|
|
22194
22343
|
return;
|
|
22195
22344
|
}
|
|
22196
22345
|
for (const userId of userDirs) {
|
|
22197
|
-
const userInboxDir =
|
|
22346
|
+
const userInboxDir = join33(SESSION_INBOX_DIR, userId);
|
|
22198
22347
|
let files;
|
|
22199
22348
|
try {
|
|
22200
22349
|
files = readdirSync8(userInboxDir);
|
|
@@ -22206,7 +22355,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
|
|
|
22206
22355
|
const topicId = topicIdFromScheduledSessionInboxFileName(file);
|
|
22207
22356
|
if (!topicId)
|
|
22208
22357
|
continue;
|
|
22209
|
-
const schedulePath =
|
|
22358
|
+
const schedulePath = join33(userInboxDir, file);
|
|
22210
22359
|
const hasProcessingClaim = files.includes(`${file}.processing`);
|
|
22211
22360
|
if (!hasProcessingClaim && !scheduledFileNeedsClaim(schedulePath, nowMs))
|
|
22212
22361
|
continue;
|
|
@@ -23042,8 +23191,8 @@ var init_src = __esm(async () => {
|
|
|
23042
23191
|
});
|
|
23043
23192
|
|
|
23044
23193
|
// ../../packages/core/src/storage/conversation-migration.ts
|
|
23045
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
23046
|
-
import { dirname as
|
|
23194
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync24, mkdirSync as mkdirSync23, readFileSync as readFileSync20 } from "fs";
|
|
23195
|
+
import { dirname as dirname16, join as join34 } from "path";
|
|
23047
23196
|
function readEntries(path) {
|
|
23048
23197
|
const entries = [];
|
|
23049
23198
|
for (const [index, line] of readFileSync20(path, "utf8").split(`
|
|
@@ -23106,20 +23255,20 @@ function migrateLegacyCompactedConversations() {
|
|
|
23106
23255
|
const userId = participant.userId;
|
|
23107
23256
|
const rawPath = getConversationPath(userId, topic.title);
|
|
23108
23257
|
const activePath = getActiveConversationPath(userId, topic.title);
|
|
23109
|
-
if (!
|
|
23258
|
+
if (!existsSync24(rawPath) || existsSync24(activePath)) {
|
|
23110
23259
|
result.skipped++;
|
|
23111
23260
|
continue;
|
|
23112
23261
|
}
|
|
23113
|
-
const backupPath =
|
|
23114
|
-
const sourcePath =
|
|
23262
|
+
const backupPath = join34(resolveStorageDataDir(), "conversation-migration-backups", MIGRATION_NAME, userId, `${sanitizeTopicName(topic.title, true)}.jsonl`);
|
|
23263
|
+
const sourcePath = existsSync24(backupPath) ? backupPath : rawPath;
|
|
23115
23264
|
const legacyEntries = readEntries(sourcePath);
|
|
23116
23265
|
const compactIndex = compactionEntryIndex(legacyEntries);
|
|
23117
23266
|
if (compactIndex < 0) {
|
|
23118
23267
|
result.skipped++;
|
|
23119
23268
|
continue;
|
|
23120
23269
|
}
|
|
23121
|
-
if (!
|
|
23122
|
-
mkdirSync23(
|
|
23270
|
+
if (!existsSync24(backupPath)) {
|
|
23271
|
+
mkdirSync23(dirname16(backupPath), { recursive: true });
|
|
23123
23272
|
copyFileSync3(rawPath, backupPath);
|
|
23124
23273
|
}
|
|
23125
23274
|
const compactedAt = legacyEntries[compactIndex]?.ts;
|
|
@@ -23169,8 +23318,8 @@ __export(exports_decisions, {
|
|
|
23169
23318
|
createDecisions: () => createDecisions,
|
|
23170
23319
|
DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES
|
|
23171
23320
|
});
|
|
23172
|
-
import { existsSync as
|
|
23173
|
-
import { dirname as
|
|
23321
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync24, readFileSync as readFileSync21, renameSync as renameSync11, writeFileSync as writeFileSync18 } from "fs";
|
|
23322
|
+
import { dirname as dirname17, join as join35 } from "path";
|
|
23174
23323
|
function safeDecisionScopeKey(scopeKey) {
|
|
23175
23324
|
const safe = sanitizeFileName(scopeKey);
|
|
23176
23325
|
if (!safe || safe === "." || safe === "..") {
|
|
@@ -23182,14 +23331,14 @@ function decisionScopeKey(opts) {
|
|
|
23182
23331
|
return opts.topicId?.trim() || opts.session || "default";
|
|
23183
23332
|
}
|
|
23184
23333
|
function getDecisionFilePath(userId, scopeKey) {
|
|
23185
|
-
return
|
|
23334
|
+
return join35(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
|
|
23186
23335
|
}
|
|
23187
23336
|
function getDecisionGraphSvgPath(userId, scopeKey) {
|
|
23188
|
-
return
|
|
23337
|
+
return join35(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
|
|
23189
23338
|
}
|
|
23190
23339
|
function writeDecisionGraphSvg(userId, scopeKey, svg) {
|
|
23191
23340
|
const path = getDecisionGraphSvgPath(userId, scopeKey);
|
|
23192
|
-
mkdirSync24(
|
|
23341
|
+
mkdirSync24(dirname17(path), { recursive: true });
|
|
23193
23342
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
23194
23343
|
writeFileSync18(tmp, svg, "utf-8");
|
|
23195
23344
|
renameSync11(tmp, path);
|
|
@@ -23197,7 +23346,7 @@ function writeDecisionGraphSvg(userId, scopeKey, svg) {
|
|
|
23197
23346
|
}
|
|
23198
23347
|
function readDecisions(userId, scopeKey) {
|
|
23199
23348
|
const path = getDecisionFilePath(userId, scopeKey);
|
|
23200
|
-
if (!
|
|
23349
|
+
if (!existsSync25(path))
|
|
23201
23350
|
return [];
|
|
23202
23351
|
try {
|
|
23203
23352
|
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
@@ -23209,7 +23358,7 @@ function readDecisions(userId, scopeKey) {
|
|
|
23209
23358
|
}
|
|
23210
23359
|
function writeDecisions(userId, scopeKey, decisions) {
|
|
23211
23360
|
const path = getDecisionFilePath(userId, scopeKey);
|
|
23212
|
-
mkdirSync24(
|
|
23361
|
+
mkdirSync24(dirname17(path), { recursive: true });
|
|
23213
23362
|
const payload = { version: 1, decisions };
|
|
23214
23363
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
23215
23364
|
writeFileSync18(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
@@ -23468,8 +23617,8 @@ var init_node_host = __esm(async () => {
|
|
|
23468
23617
|
});
|
|
23469
23618
|
|
|
23470
23619
|
// ../../packages/core/src/agents/mcp-tools/inline-assets.ts
|
|
23471
|
-
import { existsSync as
|
|
23472
|
-
import { extname as extname2, isAbsolute as isAbsolute6, resolve as
|
|
23620
|
+
import { existsSync as existsSync26, readFileSync as readFileSync22, statSync as statSync14 } from "fs";
|
|
23621
|
+
import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve17 } from "path";
|
|
23473
23622
|
function isNonLocal(ref) {
|
|
23474
23623
|
return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
|
|
23475
23624
|
}
|
|
@@ -23493,10 +23642,10 @@ function inlineLocalAssets(html, options) {
|
|
|
23493
23642
|
const relPath = stripSuffix(ref);
|
|
23494
23643
|
if (!relPath)
|
|
23495
23644
|
return null;
|
|
23496
|
-
const abs = isAbsolute6(relPath) ? relPath :
|
|
23645
|
+
const abs = isAbsolute6(relPath) ? relPath : resolve17(baseDir, relPath);
|
|
23497
23646
|
let bytes;
|
|
23498
23647
|
try {
|
|
23499
|
-
if (!
|
|
23648
|
+
if (!existsSync26(abs) || !statSync14(abs).isFile()) {
|
|
23500
23649
|
unresolved.push(ref);
|
|
23501
23650
|
cache.set(ref, null);
|
|
23502
23651
|
return null;
|
|
@@ -24444,8 +24593,8 @@ __export(exports_agent_health, {
|
|
|
24444
24593
|
createAgentHealthMcpServer: () => createAgentHealthMcpServer
|
|
24445
24594
|
});
|
|
24446
24595
|
import { spawn as spawn6 } from "child_process";
|
|
24447
|
-
import { existsSync as
|
|
24448
|
-
import { join as
|
|
24596
|
+
import { existsSync as existsSync27, readdirSync as readdirSync9 } from "fs";
|
|
24597
|
+
import { join as join36 } from "path";
|
|
24449
24598
|
import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24450
24599
|
import { z as z10 } from "zod";
|
|
24451
24600
|
function signalProcessTree2(child, signal) {
|
|
@@ -24461,7 +24610,7 @@ function signalProcessTree2(child, signal) {
|
|
|
24461
24610
|
}
|
|
24462
24611
|
}
|
|
24463
24612
|
function spawnCapture(cmd, args, timeoutMs, signal, env) {
|
|
24464
|
-
return new Promise((
|
|
24613
|
+
return new Promise((resolve18, reject) => {
|
|
24465
24614
|
const child = spawn6(cmd, args, {
|
|
24466
24615
|
env: env ?? process.env,
|
|
24467
24616
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -24519,7 +24668,7 @@ function spawnCapture(cmd, args, timeoutMs, signal, env) {
|
|
|
24519
24668
|
if (killTimer)
|
|
24520
24669
|
clearTimeout(killTimer);
|
|
24521
24670
|
if (code === 0)
|
|
24522
|
-
|
|
24671
|
+
resolve18({ stdout, stderr });
|
|
24523
24672
|
else
|
|
24524
24673
|
reject(new Error(stderr.trim() || `${cmd} exited with ${code ?? exitSignal}`));
|
|
24525
24674
|
});
|
|
@@ -24597,11 +24746,11 @@ async function checkMaestro(model, timeoutMs, signal) {
|
|
|
24597
24746
|
const remaining = deadline - Date.now();
|
|
24598
24747
|
if (remaining <= 0)
|
|
24599
24748
|
throw new Error("timeout");
|
|
24600
|
-
const eventResult = await new Promise((
|
|
24749
|
+
const eventResult = await new Promise((resolve18, reject) => {
|
|
24601
24750
|
const timeout = setTimeout(() => reject(new Error("timeout")), remaining);
|
|
24602
24751
|
const onAbort = () => reject(new Error("aborted"));
|
|
24603
24752
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
24604
|
-
activeIterator.next().then(
|
|
24753
|
+
activeIterator.next().then(resolve18, reject).finally(() => {
|
|
24605
24754
|
clearTimeout(timeout);
|
|
24606
24755
|
signal.removeEventListener("abort", onAbort);
|
|
24607
24756
|
});
|
|
@@ -24631,7 +24780,7 @@ function getDefaultModel(registry) {
|
|
|
24631
24780
|
}
|
|
24632
24781
|
async function withCheckSlot(operation) {
|
|
24633
24782
|
if (activeChecks >= MAX_CONCURRENT_CHECKS) {
|
|
24634
|
-
await new Promise((
|
|
24783
|
+
await new Promise((resolve18) => checkWaiters.push(resolve18));
|
|
24635
24784
|
}
|
|
24636
24785
|
activeChecks += 1;
|
|
24637
24786
|
try {
|
|
@@ -24689,8 +24838,8 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
|
|
|
24689
24838
|
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
24839
|
if (!context.userId)
|
|
24691
24840
|
return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
|
|
24692
|
-
const stateDir =
|
|
24693
|
-
if (!
|
|
24841
|
+
const stateDir = join36(USERS_LOG_DIR, context.userId, "active-queries");
|
|
24842
|
+
if (!existsSync27(stateDir))
|
|
24694
24843
|
return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
|
|
24695
24844
|
let files;
|
|
24696
24845
|
try {
|
|
@@ -24702,7 +24851,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
|
|
|
24702
24851
|
const entries = files.flatMap((file) => {
|
|
24703
24852
|
if (!file.endsWith(".json"))
|
|
24704
24853
|
return [];
|
|
24705
|
-
const state = readJsonFile(
|
|
24854
|
+
const state = readJsonFile(join36(stateDir, file));
|
|
24706
24855
|
if (!state)
|
|
24707
24856
|
return [];
|
|
24708
24857
|
const sinceMs = new Date(state.since).getTime();
|
|
@@ -24935,7 +25084,7 @@ var exports_default_host = {};
|
|
|
24935
25084
|
__export(exports_default_host, {
|
|
24936
25085
|
createDefaultSessionCommMcpHost: () => createDefaultSessionCommMcpHost
|
|
24937
25086
|
});
|
|
24938
|
-
import { basename as basename6, join as
|
|
25087
|
+
import { basename as basename6, join as join37 } from "path";
|
|
24939
25088
|
function ok2(text2) {
|
|
24940
25089
|
return { content: [{ type: "text", text: text2 }] };
|
|
24941
25090
|
}
|
|
@@ -24982,10 +25131,10 @@ function remoteTarget(context, to) {
|
|
|
24982
25131
|
return { node: to.slice(0, slash), topic: to.slice(slash + 1) };
|
|
24983
25132
|
}
|
|
24984
25133
|
function activeQuery(context, topicId, title) {
|
|
24985
|
-
const dir =
|
|
24986
|
-
const candidates = [
|
|
25134
|
+
const dir = join37(USERS_LOG_DIR, context.userId, "active-queries");
|
|
25135
|
+
const candidates = [join37(dir, `${sanitizeId(topicId)}.json`)];
|
|
24987
25136
|
if (title && basename6(title) === title && title !== "." && title !== "..") {
|
|
24988
|
-
candidates.push(
|
|
25137
|
+
candidates.push(join37(dir, `${title}.json`));
|
|
24989
25138
|
}
|
|
24990
25139
|
for (const path of candidates) {
|
|
24991
25140
|
const state = readJsonFile(path);
|
|
@@ -25276,298 +25425,15 @@ var init_default_host = __esm(async () => {
|
|
|
25276
25425
|
init_types();
|
|
25277
25426
|
});
|
|
25278
25427
|
|
|
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
25428
|
// ../../packages/core/src/mcp/factories/vault.ts
|
|
25560
25429
|
var exports_vault = {};
|
|
25561
25430
|
__export(exports_vault, {
|
|
25562
25431
|
createVaultMcpServer: () => createVaultMcpServer
|
|
25563
25432
|
});
|
|
25564
25433
|
import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
25565
|
-
|
|
25566
|
-
function createVaultMcpServer(context, host2, executors = {}) {
|
|
25434
|
+
function createVaultMcpServer(context, host2) {
|
|
25567
25435
|
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.", {}, () => {
|
|
25436
|
+
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
25437
|
if (!context.userId)
|
|
25572
25438
|
return mcpOk("(vault unavailable: no user context)");
|
|
25573
25439
|
const entries = host2.list(context.userId);
|
|
@@ -25578,54 +25444,14 @@ function createVaultMcpServer(context, host2, executors = {}) {
|
|
|
25578
25444
|
${lines.join(`
|
|
25579
25445
|
`)}`);
|
|
25580
25446
|
});
|
|
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
25447
|
return server;
|
|
25620
25448
|
}
|
|
25621
25449
|
var init_vault2 = __esm(() => {
|
|
25622
|
-
init_vault_http();
|
|
25623
|
-
init_vault_run();
|
|
25624
25450
|
init_mcp_helpers();
|
|
25625
25451
|
});
|
|
25626
25452
|
|
|
25627
25453
|
// ../../packages/core/src/mcp/wiki-search-index.ts
|
|
25628
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
25454
|
+
import { chmodSync as chmodSync5, existsSync as existsSync28, readFileSync as readFileSync23, unlinkSync as unlinkSync18 } from "fs";
|
|
25629
25455
|
function documentId(source) {
|
|
25630
25456
|
return `${source.kind}:${source.key}`;
|
|
25631
25457
|
}
|
|
@@ -25827,7 +25653,7 @@ class WikiSearchIndex {
|
|
|
25827
25653
|
}
|
|
25828
25654
|
function resetCorruptWikiSearchIndex(path) {
|
|
25829
25655
|
for (const candidate of [path, `${path}-journal`, `${path}-wal`, `${path}-shm`]) {
|
|
25830
|
-
if (!
|
|
25656
|
+
if (!existsSync28(candidate))
|
|
25831
25657
|
continue;
|
|
25832
25658
|
unlinkSync18(candidate);
|
|
25833
25659
|
}
|
|
@@ -25847,7 +25673,7 @@ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
|
25847
25673
|
import { randomUUID as randomUUID21 } from "crypto";
|
|
25848
25674
|
import {
|
|
25849
25675
|
closeSync as closeSync4,
|
|
25850
|
-
existsSync as
|
|
25676
|
+
existsSync as existsSync29,
|
|
25851
25677
|
mkdirSync as mkdirSync25,
|
|
25852
25678
|
openSync as openSync4,
|
|
25853
25679
|
readdirSync as readdirSync10,
|
|
@@ -25857,7 +25683,7 @@ import {
|
|
|
25857
25683
|
unlinkSync as unlinkSync19,
|
|
25858
25684
|
writeFileSync as writeFileSync19
|
|
25859
25685
|
} from "fs";
|
|
25860
|
-
import { basename as basename7, dirname as
|
|
25686
|
+
import { basename as basename7, dirname as dirname18, join as join38, relative as relative2, resolve as resolve18 } from "path";
|
|
25861
25687
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
25862
25688
|
import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
25863
25689
|
import {
|
|
@@ -26092,7 +25918,7 @@ function trigramSimilarity(left, right) {
|
|
|
26092
25918
|
return 2 * overlap / (a.size + b.size);
|
|
26093
25919
|
}
|
|
26094
25920
|
function openWikiSearchIndex() {
|
|
26095
|
-
const path =
|
|
25921
|
+
const path = resolve18(runtime().wikiDir, WIKI_SEARCH_INDEX_FILENAME);
|
|
26096
25922
|
try {
|
|
26097
25923
|
return new WikiSearchIndex(path);
|
|
26098
25924
|
} catch (error2) {
|
|
@@ -26119,7 +25945,7 @@ function prepareWikiSearchDocument(kind, key, text2) {
|
|
|
26119
25945
|
}
|
|
26120
25946
|
function indexWikiSearchDocument(kind, key, text2) {
|
|
26121
25947
|
const root = kind === "article" ? runtime().articlesDir : runtime().summariesDir;
|
|
26122
|
-
const path =
|
|
25948
|
+
const path = resolve18(root, `${key}.md`);
|
|
26123
25949
|
const index = openWikiSearchIndex();
|
|
26124
25950
|
if (!index)
|
|
26125
25951
|
return false;
|
|
@@ -26134,7 +25960,7 @@ function indexWikiSearchDocument(kind, key, text2) {
|
|
|
26134
25960
|
}
|
|
26135
25961
|
}
|
|
26136
25962
|
function wikiIndexPath(kind) {
|
|
26137
|
-
return
|
|
25963
|
+
return resolve18(runtime().wikiDir, WIKI_INDEX_FILENAMES[kind]);
|
|
26138
25964
|
}
|
|
26139
25965
|
function normalizeIndexDescription(raw) {
|
|
26140
25966
|
const description = raw.replaceAll(/\s+/g, " ").trim();
|
|
@@ -26190,7 +26016,7 @@ function indexCandidateExists(candidate) {
|
|
|
26190
26016
|
if (candidate.kind === "topic")
|
|
26191
26017
|
return true;
|
|
26192
26018
|
const root = candidate.kind === "article" ? runtime().articlesDir : runtime().summariesDir;
|
|
26193
|
-
const filePath =
|
|
26019
|
+
const filePath = resolve18(root, `${candidate.key}.md`);
|
|
26194
26020
|
const relativePath = relative2(root, filePath);
|
|
26195
26021
|
if (relativePath.startsWith("..") || relativePath.startsWith("/"))
|
|
26196
26022
|
return false;
|
|
@@ -26272,8 +26098,8 @@ function wikiQuery(args) {
|
|
|
26272
26098
|
const canReadTopicMemory = runtime().host.canReadTopicMemory;
|
|
26273
26099
|
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
26100
|
const visibleIndexCandidates = [
|
|
26275
|
-
...parseWikiIndex(
|
|
26276
|
-
...parseWikiIndex(
|
|
26101
|
+
...parseWikiIndex(resolve18(runtime().wikiDir, "topic-index.md")),
|
|
26102
|
+
...parseWikiIndex(resolve18(runtime().wikiDir, "article-index.md"))
|
|
26277
26103
|
].filter((candidate) => kind === "all" || candidate.kind === kind).filter((candidate) => candidate.kind !== "topic" || !canReadTopicMemory || canReadTopicMemory(candidate.key, runtime().userId));
|
|
26278
26104
|
const hasStaleExactIndexMatch = visibleIndexCandidates.some((candidate) => !indexCandidateExists(candidate) && scoreIndexKey(question, candidate.key) >= 100);
|
|
26279
26105
|
const indexCandidates = visibleIndexCandidates.filter(indexCandidateExists).map((candidate) => ({
|
|
@@ -26307,13 +26133,13 @@ function wikiQuery(args) {
|
|
|
26307
26133
|
try {
|
|
26308
26134
|
for (const entry of readdirSync10(dir, { withFileTypes: true })) {
|
|
26309
26135
|
if (entry.isDirectory()) {
|
|
26310
|
-
const sub =
|
|
26136
|
+
const sub = join38(dir, entry.name);
|
|
26311
26137
|
if (label === "articles" || label === "skills") {
|
|
26312
26138
|
try {
|
|
26313
26139
|
for (const f of readdirSync10(sub, { withFileTypes: true })) {
|
|
26314
26140
|
if (!f.isFile() || !f.name.endsWith(".md"))
|
|
26315
26141
|
continue;
|
|
26316
|
-
const fp =
|
|
26142
|
+
const fp = join38(sub, f.name);
|
|
26317
26143
|
try {
|
|
26318
26144
|
const text2 = readFileSync24(fp, "utf-8");
|
|
26319
26145
|
const key = `${entry.name}/${f.name.replace(/\.md$/i, "")}`;
|
|
@@ -26334,7 +26160,7 @@ function wikiQuery(args) {
|
|
|
26334
26160
|
if (label === "topic" && canReadTopicMemory && !canReadTopicMemory(entry.name.replace(/\.md$/i, ""), runtime().userId)) {
|
|
26335
26161
|
continue;
|
|
26336
26162
|
}
|
|
26337
|
-
const fp =
|
|
26163
|
+
const fp = join38(dir, entry.name);
|
|
26338
26164
|
try {
|
|
26339
26165
|
const text2 = readFileSync24(fp, "utf-8");
|
|
26340
26166
|
const key = entry.name.replace(/\.md$/i, "");
|
|
@@ -26618,7 +26444,7 @@ function wikiTopicBrief(args) {
|
|
|
26618
26444
|
};
|
|
26619
26445
|
}
|
|
26620
26446
|
const topicKey = wikiSummarySlug(rawTopic.replace(/^topic\//, "").replace(/\.md$/i, ""));
|
|
26621
|
-
const filePath =
|
|
26447
|
+
const filePath = resolve18(runtime().topicsDir, `${topicKey}.md`);
|
|
26622
26448
|
try {
|
|
26623
26449
|
const content = readFileSync24(filePath, "utf-8");
|
|
26624
26450
|
return {
|
|
@@ -26666,7 +26492,7 @@ Adopted topic memory: ${memoryKey}` }]
|
|
|
26666
26492
|
}
|
|
26667
26493
|
const root = kind === "summary" ? runtime().summariesDir : runtime().articlesDir;
|
|
26668
26494
|
const key = rawKey.replace(new RegExp(`^(?:${kind === "summary" ? "summaries" : "articles"})/`), "").replace(/\.md$/i, "");
|
|
26669
|
-
const filePath =
|
|
26495
|
+
const filePath = resolve18(root, `${key}.md`);
|
|
26670
26496
|
const relativePath = relative2(root, filePath);
|
|
26671
26497
|
if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
|
|
26672
26498
|
return { content: [{ type: "text", text: "Invalid wiki document key." }], isError: true };
|
|
@@ -26690,18 +26516,18 @@ function wikiLastConversation(args) {
|
|
|
26690
26516
|
return { content: [{ type: "text", text: "No topic specified." }] };
|
|
26691
26517
|
}
|
|
26692
26518
|
const name = slugify(topicNameFrom(rawTopic));
|
|
26693
|
-
const archiveDir =
|
|
26519
|
+
const archiveDir = resolve18(runtime().archiveDir, name);
|
|
26694
26520
|
try {
|
|
26695
|
-
let files =
|
|
26521
|
+
let files = existsSync29(archiveDir) ? readdirSync10(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
|
|
26696
26522
|
if (files.length === 0) {
|
|
26697
|
-
files =
|
|
26523
|
+
files = existsSync29(runtime().archiveDir) ? readdirSync10(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
|
|
26698
26524
|
}
|
|
26699
26525
|
if (files.length === 0) {
|
|
26700
26526
|
return {
|
|
26701
26527
|
content: [{ type: "text", text: `No archive found for topic: ${rawTopic}` }]
|
|
26702
26528
|
};
|
|
26703
26529
|
}
|
|
26704
|
-
const actualPath = files[0].includes("/") ? files[0] :
|
|
26530
|
+
const actualPath = files[0].includes("/") ? files[0] : resolve18(existsSync29(archiveDir) && readdirSync10(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
|
|
26705
26531
|
try {
|
|
26706
26532
|
const raw = readFileSync24(actualPath, "utf-8");
|
|
26707
26533
|
const lines = raw.trim().split(`
|
|
@@ -26774,7 +26600,7 @@ function skillQuery(args) {
|
|
|
26774
26600
|
})) {
|
|
26775
26601
|
if (!entry.isDirectory())
|
|
26776
26602
|
continue;
|
|
26777
|
-
const skillFile =
|
|
26603
|
+
const skillFile = resolve18(runtime().skillsDir, entry.name, "skill.md");
|
|
26778
26604
|
try {
|
|
26779
26605
|
const text2 = readFileSync24(skillFile, "utf-8");
|
|
26780
26606
|
const lower = text2.toLowerCase();
|
|
@@ -26842,9 +26668,9 @@ ${fresh.join(`
|
|
|
26842
26668
|
`)}
|
|
26843
26669
|
`;
|
|
26844
26670
|
}
|
|
26845
|
-
const skillDir =
|
|
26671
|
+
const skillDir = resolve18(runtime().skillsDir, name);
|
|
26846
26672
|
ensureDir(skillDir);
|
|
26847
|
-
const skillPath =
|
|
26673
|
+
const skillPath = resolve18(skillDir, "skill.md");
|
|
26848
26674
|
let finalContent = content;
|
|
26849
26675
|
try {
|
|
26850
26676
|
const existing = readFileSync24(skillPath, "utf-8");
|
|
@@ -26869,7 +26695,7 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26869
26695
|
let counter = 1;
|
|
26870
26696
|
while (true) {
|
|
26871
26697
|
try {
|
|
26872
|
-
writeFileSync19(
|
|
26698
|
+
writeFileSync19(resolve18(runtime().summariesDir, summaryName), content, {
|
|
26873
26699
|
encoding: "utf-8",
|
|
26874
26700
|
flag: "wx"
|
|
26875
26701
|
});
|
|
@@ -26886,8 +26712,8 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26886
26712
|
let sqliteUpdated = false;
|
|
26887
26713
|
if (topicId && setTopicBrief2) {
|
|
26888
26714
|
try {
|
|
26889
|
-
const briefPath =
|
|
26890
|
-
let briefMd =
|
|
26715
|
+
const briefPath = resolve18(runtime().topicsDir, `${fileSlug}.md`);
|
|
26716
|
+
let briefMd = existsSync29(briefPath) ? readFileSync24(briefPath, "utf-8") : undefined;
|
|
26891
26717
|
if (briefMd === undefined && getTopicBrief2 && !getTopicBrief2(fileSlug)?.briefMd) {
|
|
26892
26718
|
const legacy = getTopicBrief2(topicId)?.briefMd;
|
|
26893
26719
|
if (legacy)
|
|
@@ -26906,9 +26732,9 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
|
|
|
26906
26732
|
function writeTopicDocument(rawTopic, content) {
|
|
26907
26733
|
const topicId = runtime().topicId;
|
|
26908
26734
|
const fileSlug = wikiBriefStorageKey(rawTopic, topicId);
|
|
26909
|
-
const briefPath =
|
|
26910
|
-
ensureDir(
|
|
26911
|
-
const existed =
|
|
26735
|
+
const briefPath = resolve18(runtime().topicsDir, `${fileSlug}.md`);
|
|
26736
|
+
ensureDir(dirname18(briefPath));
|
|
26737
|
+
const existed = existsSync29(briefPath);
|
|
26912
26738
|
writeFileSync19(briefPath, content, "utf-8");
|
|
26913
26739
|
const setTopicBrief2 = runtime().host.setTopicBrief;
|
|
26914
26740
|
let sqliteUpdated = false;
|
|
@@ -26921,9 +26747,9 @@ function writeTopicDocument(rawTopic, content) {
|
|
|
26921
26747
|
return { slug: fileSlug, existed, sqliteUpdated };
|
|
26922
26748
|
}
|
|
26923
26749
|
function writeArticleDocument(slug, content) {
|
|
26924
|
-
const articlePath =
|
|
26925
|
-
ensureDir(
|
|
26926
|
-
const existed =
|
|
26750
|
+
const articlePath = resolve18(runtime().articlesDir, `${slug}.md`);
|
|
26751
|
+
ensureDir(dirname18(articlePath));
|
|
26752
|
+
const existed = existsSync29(articlePath);
|
|
26927
26753
|
atomicWriteFile(articlePath, content);
|
|
26928
26754
|
return { existed };
|
|
26929
26755
|
}
|
|
@@ -27055,21 +26881,21 @@ function collectIndexableDocuments() {
|
|
|
27055
26881
|
}
|
|
27056
26882
|
for (const entry of entries) {
|
|
27057
26883
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
27058
|
-
add(kind, entry.name.replace(/\.md$/i, ""),
|
|
26884
|
+
add(kind, entry.name.replace(/\.md$/i, ""), join38(root, entry.name));
|
|
27059
26885
|
continue;
|
|
27060
26886
|
}
|
|
27061
26887
|
if (!entry.isDirectory() || !allowSubdirectories)
|
|
27062
26888
|
continue;
|
|
27063
26889
|
let nested;
|
|
27064
26890
|
try {
|
|
27065
|
-
nested = readdirSync10(
|
|
26891
|
+
nested = readdirSync10(join38(root, entry.name), { withFileTypes: true });
|
|
27066
26892
|
} catch {
|
|
27067
26893
|
continue;
|
|
27068
26894
|
}
|
|
27069
26895
|
for (const file of nested) {
|
|
27070
26896
|
if (!file.isFile() || !file.name.endsWith(".md"))
|
|
27071
26897
|
continue;
|
|
27072
|
-
add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`,
|
|
26898
|
+
add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join38(root, entry.name, file.name));
|
|
27073
26899
|
}
|
|
27074
26900
|
}
|
|
27075
26901
|
}
|
|
@@ -27126,7 +26952,7 @@ function indexRowOrPartialWrite(label, options) {
|
|
|
27126
26952
|
function upsertIndexRow(options) {
|
|
27127
26953
|
const { kind, slug, description, date, section } = options;
|
|
27128
26954
|
const indexPath = wikiIndexPath(kind);
|
|
27129
|
-
ensureDir(
|
|
26955
|
+
ensureDir(dirname18(indexPath));
|
|
27130
26956
|
const releaseLock = acquireFileLock(indexPath);
|
|
27131
26957
|
try {
|
|
27132
26958
|
let index;
|
|
@@ -27183,7 +27009,7 @@ function upsertIndexRow(options) {
|
|
|
27183
27009
|
}
|
|
27184
27010
|
function wikiDocumentExists(kind, slug) {
|
|
27185
27011
|
const root = kind === "article" ? runtime().articlesDir : kind === "summary" ? runtime().summariesDir : kind === "topic" ? runtime().topicsDir : runtime().skillsDir;
|
|
27186
|
-
const filePath =
|
|
27012
|
+
const filePath = resolve18(root, `${slug}.md`);
|
|
27187
27013
|
if (relative2(root, filePath).startsWith(".."))
|
|
27188
27014
|
return false;
|
|
27189
27015
|
try {
|
|
@@ -27235,7 +27061,7 @@ function indexUpsert(args) {
|
|
|
27235
27061
|
}
|
|
27236
27062
|
function createWikiMcpServer(context, host2) {
|
|
27237
27063
|
const surface = context.surface ?? "all";
|
|
27238
|
-
const wikiDir =
|
|
27064
|
+
const wikiDir = resolve18(host2.wikiRoot);
|
|
27239
27065
|
const current3 = {
|
|
27240
27066
|
userId: context.userId,
|
|
27241
27067
|
currentTopicId: context.currentTopicId ?? context.topicId,
|
|
@@ -27243,11 +27069,11 @@ function createWikiMcpServer(context, host2) {
|
|
|
27243
27069
|
surface,
|
|
27244
27070
|
host: host2,
|
|
27245
27071
|
wikiDir,
|
|
27246
|
-
skillsDir:
|
|
27247
|
-
topicsDir:
|
|
27248
|
-
summariesDir:
|
|
27249
|
-
articlesDir:
|
|
27250
|
-
archiveDir:
|
|
27072
|
+
skillsDir: resolve18(wikiDir, "skills"),
|
|
27073
|
+
topicsDir: resolve18(wikiDir, "topic"),
|
|
27074
|
+
summariesDir: resolve18(wikiDir, "summaries"),
|
|
27075
|
+
articlesDir: resolve18(wikiDir, "articles"),
|
|
27076
|
+
archiveDir: resolve18(wikiDir, "archive")
|
|
27251
27077
|
};
|
|
27252
27078
|
const tools = surface === "wiki" ? WIKI_TOOLS : surface === "skills" ? SKILL_TOOLS : [...WIKI_TOOLS, ...SKILL_TOOLS];
|
|
27253
27079
|
for (const directory of [
|
|
@@ -27570,7 +27396,7 @@ __export(exports_activity_log, {
|
|
|
27570
27396
|
readSentFilesForTopic: () => readSentFilesForTopic
|
|
27571
27397
|
});
|
|
27572
27398
|
import {
|
|
27573
|
-
existsSync as
|
|
27399
|
+
existsSync as existsSync30,
|
|
27574
27400
|
mkdirSync as mkdirSync26,
|
|
27575
27401
|
readdirSync as readdirSync11,
|
|
27576
27402
|
renameSync as renameSync13,
|
|
@@ -27578,7 +27404,7 @@ import {
|
|
|
27578
27404
|
unlinkSync as unlinkSync20,
|
|
27579
27405
|
writeFileSync as writeFileSync20
|
|
27580
27406
|
} from "fs";
|
|
27581
|
-
import { basename as basename8, join as
|
|
27407
|
+
import { basename as basename8, join as join39 } from "path";
|
|
27582
27408
|
function startLogRotation() {
|
|
27583
27409
|
if (rotationTimer)
|
|
27584
27410
|
return;
|
|
@@ -27592,7 +27418,7 @@ function writeLog(entry) {
|
|
|
27592
27418
|
mkdirSync26(logDir, { recursive: true });
|
|
27593
27419
|
const safeSession = sanitizeTopicName(entry.session);
|
|
27594
27420
|
const sidShort = entry.sessionId ? entry.sessionId.slice(0, 8) : "new";
|
|
27595
|
-
const file =
|
|
27421
|
+
const file = join39(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
|
|
27596
27422
|
try {
|
|
27597
27423
|
const stat = statSync16(file);
|
|
27598
27424
|
if (stat.size >= MAX_FILE_SIZE) {
|
|
@@ -27612,7 +27438,7 @@ function rotateOldLogs() {
|
|
|
27612
27438
|
try {
|
|
27613
27439
|
const files = readdirSync11(logDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
27614
27440
|
try {
|
|
27615
|
-
const stat = statSync16(
|
|
27441
|
+
const stat = statSync16(join39(logDir, f));
|
|
27616
27442
|
return { name: f, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
27617
27443
|
} catch {
|
|
27618
27444
|
return null;
|
|
@@ -27628,7 +27454,7 @@ function rotateOldLogs() {
|
|
|
27628
27454
|
if (freed >= toFree)
|
|
27629
27455
|
break;
|
|
27630
27456
|
try {
|
|
27631
|
-
unlinkSync20(
|
|
27457
|
+
unlinkSync20(join39(logDir, file.name));
|
|
27632
27458
|
freed += file.size;
|
|
27633
27459
|
logger.info({ file: file.name, sizeKB: (file.size / 1024).toFixed(0) }, "Rotated out old log");
|
|
27634
27460
|
} catch (e) {
|
|
@@ -27640,9 +27466,9 @@ function rotateOldLogs() {
|
|
|
27640
27466
|
}
|
|
27641
27467
|
}
|
|
27642
27468
|
function writeSentFileLog(entry) {
|
|
27643
|
-
const dir =
|
|
27469
|
+
const dir = join39(resolveStorageUsersLogDir(), String(entry.userId));
|
|
27644
27470
|
mkdirSync26(dir, { recursive: true });
|
|
27645
|
-
const file =
|
|
27471
|
+
const file = join39(dir, "sent-files.jsonl");
|
|
27646
27472
|
try {
|
|
27647
27473
|
appendJsonlEntry(file, { ...entry, fileName: basename8(entry.filePath) });
|
|
27648
27474
|
} catch (e) {
|
|
@@ -27653,8 +27479,8 @@ function entryMatchesTopic(e, topicName) {
|
|
|
27653
27479
|
return e.topicName === topicName;
|
|
27654
27480
|
}
|
|
27655
27481
|
function readSentFilesForTopic(userId, topicName) {
|
|
27656
|
-
const file =
|
|
27657
|
-
if (!
|
|
27482
|
+
const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
|
|
27483
|
+
if (!existsSync30(file))
|
|
27658
27484
|
return [];
|
|
27659
27485
|
try {
|
|
27660
27486
|
return readJsonlLines(file).map((line) => JSON.parse(line)).filter((e) => entryMatchesTopic(e, topicName));
|
|
@@ -27664,8 +27490,8 @@ function readSentFilesForTopic(userId, topicName) {
|
|
|
27664
27490
|
}
|
|
27665
27491
|
}
|
|
27666
27492
|
function removeSentFilesForTopic(userId, topicName) {
|
|
27667
|
-
const file =
|
|
27668
|
-
if (!
|
|
27493
|
+
const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
|
|
27494
|
+
if (!existsSync30(file))
|
|
27669
27495
|
return;
|
|
27670
27496
|
try {
|
|
27671
27497
|
const remaining = readJsonlLines(file).filter((line) => {
|
|
@@ -28429,7 +28255,7 @@ var init_storage_public = __esm(async () => {
|
|
|
28429
28255
|
});
|
|
28430
28256
|
|
|
28431
28257
|
// ../../packages/mcp/src/hosted-surfaces.ts
|
|
28432
|
-
import { resolve as
|
|
28258
|
+
import { resolve as resolve19 } from "path";
|
|
28433
28259
|
function isActiveHostedMcpSurface(surface) {
|
|
28434
28260
|
return ACTIVE_HOSTED_MCP_SURFACES.includes(surface);
|
|
28435
28261
|
}
|
|
@@ -28486,23 +28312,11 @@ async function buildHostedSurfaceServer(surface, context) {
|
|
|
28486
28312
|
});
|
|
28487
28313
|
}
|
|
28488
28314
|
case "vault": {
|
|
28489
|
-
const [{ createVaultMcpServer: createVaultMcpServer2 }, vault
|
|
28315
|
+
const [{ createVaultMcpServer: createVaultMcpServer2 }, vault] = await Promise.all([
|
|
28490
28316
|
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)
|
|
28317
|
+
init_vault_public().then(() => exports_vault_public)
|
|
28493
28318
|
]);
|
|
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
|
-
});
|
|
28319
|
+
return createVaultMcpServer2({ userId: context.userId }, { list: vault.vaultList });
|
|
28506
28320
|
}
|
|
28507
28321
|
case "wiki":
|
|
28508
28322
|
case "skills": {
|
|
@@ -28525,7 +28339,7 @@ async function buildHostedSurfaceServer(surface, context) {
|
|
|
28525
28339
|
...topicId ? { topicId } : {},
|
|
28526
28340
|
surface
|
|
28527
28341
|
}, {
|
|
28528
|
-
wikiRoot:
|
|
28342
|
+
wikiRoot: resolve19(WORKSPACE_DIR2, "wiki"),
|
|
28529
28343
|
getTopicBrief: storage.getTopicBrief,
|
|
28530
28344
|
resolveTopicBrief: accessibleTopicBrief,
|
|
28531
28345
|
setTopicBrief: storage.setTopicBrief
|
|
@@ -28656,10 +28470,10 @@ var init_sse_transport = () => {};
|
|
|
28656
28470
|
// ../../packages/mcp/src/server.ts
|
|
28657
28471
|
import { createHash as createHash10, randomUUID as randomUUID23 } from "crypto";
|
|
28658
28472
|
import { realpathSync as realpathSync6, statSync as statSync17 } from "fs";
|
|
28659
|
-
import { basename as basename9, extname as extname3, resolve as
|
|
28473
|
+
import { basename as basename9, extname as extname3, resolve as resolve20 } from "path";
|
|
28660
28474
|
import { McpServer as McpServer8 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28661
28475
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
28662
|
-
import { z as
|
|
28476
|
+
import { z as z12 } from "zod";
|
|
28663
28477
|
function requireTopicAccess(ctx) {
|
|
28664
28478
|
const topic = getTopic(ctx.topicId);
|
|
28665
28479
|
if (!topic)
|
|
@@ -28670,8 +28484,8 @@ function requireTopicAccess(ctx) {
|
|
|
28670
28484
|
return { topic };
|
|
28671
28485
|
}
|
|
28672
28486
|
function isPathInside2(baseDir, filePath) {
|
|
28673
|
-
const cwd =
|
|
28674
|
-
const normalized =
|
|
28487
|
+
const cwd = resolve20(baseDir);
|
|
28488
|
+
const normalized = resolve20(filePath);
|
|
28675
28489
|
try {
|
|
28676
28490
|
const realCwd = realpathSync6(cwd);
|
|
28677
28491
|
const real = realpathSync6(normalized);
|
|
@@ -28687,7 +28501,7 @@ function localFileInfo(ctx, filePath) {
|
|
|
28687
28501
|
if (!isPathInside2(ctx.cwd, filePath)) {
|
|
28688
28502
|
return { error: "Access denied. File must be within the topic workspace." };
|
|
28689
28503
|
}
|
|
28690
|
-
const normalizedPath =
|
|
28504
|
+
const normalizedPath = resolve20(filePath);
|
|
28691
28505
|
if (isSensitivePath(normalizedPath)) {
|
|
28692
28506
|
return { error: "Access denied. Path matches the sensitive-file blacklist." };
|
|
28693
28507
|
}
|
|
@@ -28879,8 +28693,8 @@ function buildNegotiumMcpServer(ctx) {
|
|
|
28879
28693
|
}
|
|
28880
28694
|
}
|
|
28881
28695
|
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:
|
|
28696
|
+
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));
|
|
28697
|
+
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
28698
|
const results = await Promise.all(file_paths.map((filePath) => deliverFile(ctx, filePath)));
|
|
28885
28699
|
const hasError = results.some((result) => ("isError" in result) && result.isError);
|
|
28886
28700
|
return {
|
|
@@ -29164,37 +28978,37 @@ var init_src2 = __esm(async () => {
|
|
|
29164
28978
|
|
|
29165
28979
|
// ../../packages/mcp-host/src/paths.ts
|
|
29166
28980
|
import { homedir as homedir9 } from "os";
|
|
29167
|
-
import { resolve as
|
|
28981
|
+
import { resolve as resolve21 } from "path";
|
|
29168
28982
|
function stateDir() {
|
|
29169
28983
|
const env = process.env.NEGOTIUM_STATE_DIR?.trim();
|
|
29170
|
-
return env ?
|
|
28984
|
+
return env ? resolve21(env) : resolve21(homedir9(), ".negotium");
|
|
29171
28985
|
}
|
|
29172
28986
|
function defaultPortsDir() {
|
|
29173
|
-
return
|
|
28987
|
+
return resolve21(stateDir(), "run", "mcp-ports");
|
|
29174
28988
|
}
|
|
29175
28989
|
function defaultManifestFile() {
|
|
29176
|
-
return
|
|
28990
|
+
return resolve21(stateDir(), "data", "mcp-manifest.json");
|
|
29177
28991
|
}
|
|
29178
28992
|
var init_paths = () => {};
|
|
29179
28993
|
|
|
29180
28994
|
// ../../packages/mcp-host/src/spec.ts
|
|
29181
|
-
import { z as
|
|
28995
|
+
import { z as z13 } from "zod";
|
|
29182
28996
|
var KEY_PATTERN, portSchema, mcpServerSpecSchema;
|
|
29183
28997
|
var init_spec = __esm(() => {
|
|
29184
28998
|
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:
|
|
28999
|
+
portSchema = z13.number().int().min(1).max(65535);
|
|
29000
|
+
mcpServerSpecSchema = z13.object({
|
|
29001
|
+
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"),
|
|
29002
|
+
transport: z13.enum(["stdio", "http"]),
|
|
29003
|
+
command: z13.string().min(1),
|
|
29004
|
+
args: z13.array(z13.string()).optional(),
|
|
29005
|
+
env: z13.record(z13.string(), z13.string()).optional(),
|
|
29006
|
+
cwd: z13.string().optional(),
|
|
29007
|
+
portRange: z13.object({ base: portSchema, max: portSchema }).optional(),
|
|
29008
|
+
scope: z13.enum(["node", "instance"]),
|
|
29009
|
+
readyTimeoutMs: z13.number().int().positive().optional(),
|
|
29010
|
+
idleEvictMs: z13.number().int().positive().optional(),
|
|
29011
|
+
healthIntervalMs: z13.number().int().positive().optional()
|
|
29198
29012
|
}).superRefine((spec, ctx) => {
|
|
29199
29013
|
if (spec.transport === "http") {
|
|
29200
29014
|
if (!spec.portRange) {
|
|
@@ -29215,9 +29029,9 @@ var init_spec = __esm(() => {
|
|
|
29215
29029
|
});
|
|
29216
29030
|
|
|
29217
29031
|
// ../../packages/mcp-host/src/manifest.ts
|
|
29218
|
-
import { existsSync as
|
|
29219
|
-
import { dirname as
|
|
29220
|
-
import { z as
|
|
29032
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync27, readFileSync as readFileSync25, renameSync as renameSync14, writeFileSync as writeFileSync21 } from "fs";
|
|
29033
|
+
import { dirname as dirname19 } from "path";
|
|
29034
|
+
import { z as z14 } from "zod";
|
|
29221
29035
|
|
|
29222
29036
|
class McpManifest {
|
|
29223
29037
|
file;
|
|
@@ -29260,7 +29074,7 @@ class McpManifest {
|
|
|
29260
29074
|
return this.entries.get(key)?.enabled ?? false;
|
|
29261
29075
|
}
|
|
29262
29076
|
load() {
|
|
29263
|
-
if (!
|
|
29077
|
+
if (!existsSync31(this.file))
|
|
29264
29078
|
return;
|
|
29265
29079
|
let parsed;
|
|
29266
29080
|
try {
|
|
@@ -29276,7 +29090,7 @@ class McpManifest {
|
|
|
29276
29090
|
}
|
|
29277
29091
|
}
|
|
29278
29092
|
save() {
|
|
29279
|
-
mkdirSync27(
|
|
29093
|
+
mkdirSync27(dirname19(this.file), { recursive: true });
|
|
29280
29094
|
const payload = {
|
|
29281
29095
|
version: 1,
|
|
29282
29096
|
servers: [...this.entries.values()]
|
|
@@ -29291,13 +29105,13 @@ var manifestEntrySchema, manifestFileSchema;
|
|
|
29291
29105
|
var init_manifest = __esm(() => {
|
|
29292
29106
|
init_paths();
|
|
29293
29107
|
init_spec();
|
|
29294
|
-
manifestEntrySchema =
|
|
29108
|
+
manifestEntrySchema = z14.object({
|
|
29295
29109
|
spec: mcpServerSpecSchema,
|
|
29296
|
-
enabled:
|
|
29110
|
+
enabled: z14.boolean()
|
|
29297
29111
|
});
|
|
29298
|
-
manifestFileSchema =
|
|
29299
|
-
version:
|
|
29300
|
-
servers:
|
|
29112
|
+
manifestFileSchema = z14.object({
|
|
29113
|
+
version: z14.literal(1),
|
|
29114
|
+
servers: z14.array(manifestEntrySchema)
|
|
29301
29115
|
});
|
|
29302
29116
|
});
|
|
29303
29117
|
|
|
@@ -29310,7 +29124,7 @@ function sanitizePathComponent(name) {
|
|
|
29310
29124
|
}
|
|
29311
29125
|
|
|
29312
29126
|
// ../../packages/mcp-host/src/manager.ts
|
|
29313
|
-
import { execFileSync as execFileSync8, spawn as
|
|
29127
|
+
import { execFileSync as execFileSync8, spawn as spawn7 } from "child_process";
|
|
29314
29128
|
import {
|
|
29315
29129
|
mkdirSync as mkdirSync28,
|
|
29316
29130
|
readdirSync as readdirSync12,
|
|
@@ -29319,8 +29133,8 @@ import {
|
|
|
29319
29133
|
unlinkSync as unlinkSync21,
|
|
29320
29134
|
writeFileSync as writeFileSync22
|
|
29321
29135
|
} from "fs";
|
|
29322
|
-
import { connect, createServer as
|
|
29323
|
-
import { join as
|
|
29136
|
+
import { connect, createServer as createServer4 } from "net";
|
|
29137
|
+
import { join as join40 } from "path";
|
|
29324
29138
|
function regKey(key, instanceKey) {
|
|
29325
29139
|
return `${key}\x00${instanceKey}`;
|
|
29326
29140
|
}
|
|
@@ -29336,16 +29150,16 @@ function resolveInstanceKey(spec, instanceKey) {
|
|
|
29336
29150
|
return instanceKey;
|
|
29337
29151
|
}
|
|
29338
29152
|
function canBind(port) {
|
|
29339
|
-
return new Promise((
|
|
29340
|
-
const srv =
|
|
29341
|
-
srv.once("error", () =>
|
|
29153
|
+
return new Promise((resolve22) => {
|
|
29154
|
+
const srv = createServer4();
|
|
29155
|
+
srv.once("error", () => resolve22(false));
|
|
29342
29156
|
srv.listen({ port, host: "127.0.0.1", exclusive: true }, () => {
|
|
29343
|
-
srv.close(() =>
|
|
29157
|
+
srv.close(() => resolve22(true));
|
|
29344
29158
|
});
|
|
29345
29159
|
});
|
|
29346
29160
|
}
|
|
29347
29161
|
function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
|
|
29348
|
-
return new Promise((
|
|
29162
|
+
return new Promise((resolve22) => {
|
|
29349
29163
|
const sock = connect({ port, host: "127.0.0.1" });
|
|
29350
29164
|
let settled = false;
|
|
29351
29165
|
const done = (ok3) => {
|
|
@@ -29353,7 +29167,7 @@ function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
|
|
|
29353
29167
|
return;
|
|
29354
29168
|
settled = true;
|
|
29355
29169
|
sock.destroy();
|
|
29356
|
-
|
|
29170
|
+
resolve22(ok3);
|
|
29357
29171
|
};
|
|
29358
29172
|
sock.once("connect", () => done(true));
|
|
29359
29173
|
sock.once("error", () => done(false));
|
|
@@ -29381,12 +29195,12 @@ function killProcessTree(pid) {
|
|
|
29381
29195
|
function waitExit(proc, timeoutMs) {
|
|
29382
29196
|
if (proc.exitCode !== null || proc.signalCode !== null)
|
|
29383
29197
|
return Promise.resolve(true);
|
|
29384
|
-
return new Promise((
|
|
29385
|
-
const timer = setTimeout(() =>
|
|
29198
|
+
return new Promise((resolve22) => {
|
|
29199
|
+
const timer = setTimeout(() => resolve22(false), timeoutMs);
|
|
29386
29200
|
timer.unref?.();
|
|
29387
29201
|
const settle = () => {
|
|
29388
29202
|
clearTimeout(timer);
|
|
29389
|
-
|
|
29203
|
+
resolve22(true);
|
|
29390
29204
|
};
|
|
29391
29205
|
proc.once("exit", settle);
|
|
29392
29206
|
proc.once("error", settle);
|
|
@@ -29520,7 +29334,7 @@ class McpHost {
|
|
|
29520
29334
|
let spawnFailed = false;
|
|
29521
29335
|
let proc;
|
|
29522
29336
|
try {
|
|
29523
|
-
proc =
|
|
29337
|
+
proc = spawn7(spec.command, args, {
|
|
29524
29338
|
stdio: "ignore",
|
|
29525
29339
|
detached: false,
|
|
29526
29340
|
cwd: spec.cwd,
|
|
@@ -29622,7 +29436,7 @@ class McpHost {
|
|
|
29622
29436
|
if (file.includes(".tmp-"))
|
|
29623
29437
|
continue;
|
|
29624
29438
|
try {
|
|
29625
|
-
const port = Number.parseInt(readFileSync26(
|
|
29439
|
+
const port = Number.parseInt(readFileSync26(join40(this.portsDir, file), "utf8").trim(), 10);
|
|
29626
29440
|
if (!Number.isNaN(port))
|
|
29627
29441
|
claims.set(file, port);
|
|
29628
29442
|
} catch {}
|
|
@@ -29632,7 +29446,7 @@ class McpHost {
|
|
|
29632
29446
|
writePortFile(fileName, port) {
|
|
29633
29447
|
try {
|
|
29634
29448
|
mkdirSync28(this.portsDir, { recursive: true });
|
|
29635
|
-
const file =
|
|
29449
|
+
const file = join40(this.portsDir, fileName);
|
|
29636
29450
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
29637
29451
|
writeFileSync22(tmp, String(port));
|
|
29638
29452
|
renameSync15(tmp, file);
|
|
@@ -29642,7 +29456,7 @@ class McpHost {
|
|
|
29642
29456
|
}
|
|
29643
29457
|
deletePortFile(fileName) {
|
|
29644
29458
|
try {
|
|
29645
|
-
unlinkSync21(
|
|
29459
|
+
unlinkSync21(join40(this.portsDir, fileName));
|
|
29646
29460
|
} catch {}
|
|
29647
29461
|
}
|
|
29648
29462
|
cleanupRunning(rkey, inst) {
|
|
@@ -29729,7 +29543,7 @@ var DEFAULT_READY_TIMEOUT_MS = 20000, READY_POLL_MS = 150, KILL_GRACE_MS = 5000,
|
|
|
29729
29543
|
fn(`[mcp-host] ${msg}`, ctx);
|
|
29730
29544
|
else
|
|
29731
29545
|
fn(`[mcp-host] ${msg}`);
|
|
29732
|
-
}, delay2 = (ms) => new Promise((
|
|
29546
|
+
}, delay2 = (ms) => new Promise((resolve22) => setTimeout(resolve22, ms));
|
|
29733
29547
|
var init_manager3 = __esm(() => {
|
|
29734
29548
|
init_manifest();
|
|
29735
29549
|
init_paths();
|
|
@@ -29747,14 +29561,14 @@ var init_src3 = __esm(() => {
|
|
|
29747
29561
|
import { randomUUID as randomUUID24 } from "crypto";
|
|
29748
29562
|
import {
|
|
29749
29563
|
copyFileSync as copyFileSync4,
|
|
29750
|
-
existsSync as
|
|
29564
|
+
existsSync as existsSync32,
|
|
29751
29565
|
mkdirSync as mkdirSync29,
|
|
29752
29566
|
readFileSync as readFileSync27,
|
|
29753
29567
|
rmSync as rmSync9,
|
|
29754
29568
|
statSync as statSync18,
|
|
29755
29569
|
writeFileSync as writeFileSync23
|
|
29756
29570
|
} from "fs";
|
|
29757
|
-
import { basename as basename10, extname as extname4, join as
|
|
29571
|
+
import { basename as basename10, extname as extname4, join as join41 } from "path";
|
|
29758
29572
|
function safeExtension(filename) {
|
|
29759
29573
|
const extension = extname4(basename10(filename));
|
|
29760
29574
|
return /^\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : "";
|
|
@@ -29772,14 +29586,14 @@ function contentDisposition(filename) {
|
|
|
29772
29586
|
|
|
29773
29587
|
class NodeFileStore {
|
|
29774
29588
|
uploadDir;
|
|
29775
|
-
constructor(uploadDir =
|
|
29589
|
+
constructor(uploadDir = join41(DATA_DIR, "uploads")) {
|
|
29776
29590
|
this.uploadDir = uploadDir;
|
|
29777
29591
|
}
|
|
29778
29592
|
#ensureDir() {
|
|
29779
29593
|
mkdirSync29(this.uploadDir, { recursive: true });
|
|
29780
29594
|
}
|
|
29781
29595
|
#metadataPath(fileId) {
|
|
29782
|
-
return
|
|
29596
|
+
return join41(this.uploadDir, `${fileId}.meta.json`);
|
|
29783
29597
|
}
|
|
29784
29598
|
#metadata(fileId) {
|
|
29785
29599
|
if (!FILE_ID_RE.test(fileId))
|
|
@@ -29808,7 +29622,7 @@ class NodeFileStore {
|
|
|
29808
29622
|
hooks = {
|
|
29809
29623
|
resolveAttachmentByFileId: (fileId) => {
|
|
29810
29624
|
const metadata = this.#metadata(fileId);
|
|
29811
|
-
if (!metadata || !
|
|
29625
|
+
if (!metadata || !existsSync32(join41(this.uploadDir, metadata.savedName)))
|
|
29812
29626
|
return null;
|
|
29813
29627
|
return this.#attachment(fileId, metadata);
|
|
29814
29628
|
},
|
|
@@ -29816,8 +29630,8 @@ class NodeFileStore {
|
|
|
29816
29630
|
const metadata = this.#metadata(fileId);
|
|
29817
29631
|
if (!metadata)
|
|
29818
29632
|
return null;
|
|
29819
|
-
const path =
|
|
29820
|
-
return
|
|
29633
|
+
const path = join41(this.uploadDir, metadata.savedName);
|
|
29634
|
+
return existsSync32(path) ? path : null;
|
|
29821
29635
|
},
|
|
29822
29636
|
storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),
|
|
29823
29637
|
deleteFilesForTopic: (topicId) => this.deleteForTopic(topicId)
|
|
@@ -29831,11 +29645,11 @@ class NodeFileStore {
|
|
|
29831
29645
|
const mimeType = file.type || MIME_BY_EXT2[extension] || "application/octet-stream";
|
|
29832
29646
|
const existing = this.#metadata(fileId);
|
|
29833
29647
|
if (existing) {
|
|
29834
|
-
const matches = existing.filename === filename && existing.mimeType === mimeType && existing.sizeBytes === file.size && existing.ownerUserId === access.ownerUserId && existing.topicId === access.topicId &&
|
|
29648
|
+
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
29649
|
return matches ? this.#attachment(fileId, existing) : null;
|
|
29836
29650
|
}
|
|
29837
29651
|
const savedName = `${fileId}${extension}`;
|
|
29838
|
-
const savedPath =
|
|
29652
|
+
const savedPath = join41(this.uploadDir, savedName);
|
|
29839
29653
|
try {
|
|
29840
29654
|
const sizeBytes = await Bun.write(savedPath, file);
|
|
29841
29655
|
if (sizeBytes !== file.size)
|
|
@@ -29860,14 +29674,14 @@ class NodeFileStore {
|
|
|
29860
29674
|
}
|
|
29861
29675
|
allows(fileId, access) {
|
|
29862
29676
|
const metadata = this.#metadata(fileId);
|
|
29863
|
-
return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId &&
|
|
29677
|
+
return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId && existsSync32(join41(this.uploadDir, metadata.savedName)));
|
|
29864
29678
|
}
|
|
29865
29679
|
store(absPath, access = {}) {
|
|
29866
29680
|
this.#ensureDir();
|
|
29867
29681
|
const fileId = randomUUID24();
|
|
29868
29682
|
const extension = safeExtension(absPath);
|
|
29869
29683
|
const savedName = `${fileId}${extension}`;
|
|
29870
|
-
const savedPath =
|
|
29684
|
+
const savedPath = join41(this.uploadDir, savedName);
|
|
29871
29685
|
try {
|
|
29872
29686
|
const stats = statSync18(absPath);
|
|
29873
29687
|
if (!stats.isFile() || stats.size > MAX_NODE_UPLOAD_BYTES)
|
|
@@ -29899,8 +29713,8 @@ class NodeFileStore {
|
|
|
29899
29713
|
const allowed = metadata.visibility === "workspace" || metadata.ownerUserId === userId || Boolean(topic && isParticipant(topic, userId));
|
|
29900
29714
|
if (!allowed)
|
|
29901
29715
|
return null;
|
|
29902
|
-
const path =
|
|
29903
|
-
if (!
|
|
29716
|
+
const path = join41(this.uploadDir, metadata.savedName);
|
|
29717
|
+
if (!existsSync32(path))
|
|
29904
29718
|
return null;
|
|
29905
29719
|
return new Response(Bun.file(path), {
|
|
29906
29720
|
headers: {
|
|
@@ -29918,7 +29732,7 @@ class NodeFileStore {
|
|
|
29918
29732
|
const metadata = this.#metadata(fileId);
|
|
29919
29733
|
if (metadata?.topicId !== topicId)
|
|
29920
29734
|
continue;
|
|
29921
|
-
rmSync9(
|
|
29735
|
+
rmSync9(join41(this.uploadDir, metadata.savedName), { force: true });
|
|
29922
29736
|
rmSync9(this.#metadataPath(fileId), { force: true });
|
|
29923
29737
|
}
|
|
29924
29738
|
}
|
|
@@ -30070,14 +29884,14 @@ function createPollingSseStream(req, options) {
|
|
|
30070
29884
|
import { randomUUID as randomUUID25, timingSafeEqual as timingSafeEqual4 } from "crypto";
|
|
30071
29885
|
import {
|
|
30072
29886
|
chmodSync as chmodSync6,
|
|
30073
|
-
existsSync as
|
|
29887
|
+
existsSync as existsSync33,
|
|
30074
29888
|
mkdirSync as mkdirSync30,
|
|
30075
29889
|
readFileSync as readFileSync28,
|
|
30076
29890
|
renameSync as renameSync16,
|
|
30077
29891
|
unlinkSync as unlinkSync22,
|
|
30078
29892
|
writeFileSync as writeFileSync24
|
|
30079
29893
|
} from "fs";
|
|
30080
|
-
import { dirname as
|
|
29894
|
+
import { dirname as dirname20, resolve as resolve22 } from "path";
|
|
30081
29895
|
function jsonError(status, error2) {
|
|
30082
29896
|
return Response.json({ ok: false, error: error2 }, { status });
|
|
30083
29897
|
}
|
|
@@ -30906,7 +30720,7 @@ function writeNodeDaemonInfo(port, startedAt) {
|
|
|
30906
30720
|
stateDir: STATE_DIR,
|
|
30907
30721
|
startedAt
|
|
30908
30722
|
};
|
|
30909
|
-
mkdirSync30(
|
|
30723
|
+
mkdirSync30(dirname20(NODE_DAEMON_INFO_PATH), { recursive: true });
|
|
30910
30724
|
const temporary = `${NODE_DAEMON_INFO_PATH}.${process.pid}.${randomUUID25()}.tmp`;
|
|
30911
30725
|
writeFileSync24(temporary, `${JSON.stringify(info, null, 2)}
|
|
30912
30726
|
`, { mode: 384 });
|
|
@@ -30916,7 +30730,7 @@ function writeNodeDaemonInfo(port, startedAt) {
|
|
|
30916
30730
|
return info;
|
|
30917
30731
|
}
|
|
30918
30732
|
function readNodeDaemonInfo() {
|
|
30919
|
-
if (!
|
|
30733
|
+
if (!existsSync33(NODE_DAEMON_INFO_PATH))
|
|
30920
30734
|
return null;
|
|
30921
30735
|
try {
|
|
30922
30736
|
const value = JSON.parse(readFileSync28(NODE_DAEMON_INFO_PATH, "utf8"));
|
|
@@ -31001,7 +30815,7 @@ var init_control = __esm(async () => {
|
|
|
31001
30815
|
await init_node_host();
|
|
31002
30816
|
await init_files();
|
|
31003
30817
|
NODE_RUNTIME_CONTRACT_BASE_PATH = `${NODE_CONTROL_BASE_PATH}/runtime/v1`;
|
|
31004
|
-
NODE_DAEMON_INFO_PATH =
|
|
30818
|
+
NODE_DAEMON_INFO_PATH = resolve22(RUN_DIR, "node-daemon.json");
|
|
31005
30819
|
NODE_VERSION = NEGOTIUM_VERSION;
|
|
31006
30820
|
ControlRequestError = class ControlRequestError extends Error {
|
|
31007
30821
|
constructor(message) {
|
|
@@ -31202,9 +31016,9 @@ var init_schedule = __esm(() => {
|
|
|
31202
31016
|
});
|
|
31203
31017
|
|
|
31204
31018
|
// ../../packages/module-cron/src/scripts.ts
|
|
31205
|
-
import { spawn as
|
|
31206
|
-
import { existsSync as
|
|
31207
|
-
import { resolve as
|
|
31019
|
+
import { spawn as spawn8 } from "child_process";
|
|
31020
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync31, readdirSync as readdirSync13 } from "fs";
|
|
31021
|
+
import { resolve as resolve23, sep as sep2 } from "path";
|
|
31208
31022
|
function validateCronScriptName(script) {
|
|
31209
31023
|
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*\.py$/.test(script) || script.includes("..") || script.includes("/") || script.includes("\\")) {
|
|
31210
31024
|
return { ok: false, error: `script must be a plain .py filename under ${CRON_JOBS_DIR}` };
|
|
@@ -31215,7 +31029,7 @@ function resolveCronScriptPath(script) {
|
|
|
31215
31029
|
const valid = validateCronScriptName(script);
|
|
31216
31030
|
if (!valid.ok)
|
|
31217
31031
|
throw new Error(valid.error);
|
|
31218
|
-
const path =
|
|
31032
|
+
const path = resolve23(CRON_JOBS_DIR, script);
|
|
31219
31033
|
if (!path.startsWith(`${CRON_JOBS_DIR}${sep2}`))
|
|
31220
31034
|
throw new Error("script path escaped jobs directory");
|
|
31221
31035
|
return path;
|
|
@@ -31226,7 +31040,7 @@ function listCronScripts() {
|
|
|
31226
31040
|
}
|
|
31227
31041
|
function cronScriptExists(script) {
|
|
31228
31042
|
try {
|
|
31229
|
-
return
|
|
31043
|
+
return existsSync34(resolveCronScriptPath(script));
|
|
31230
31044
|
} catch {
|
|
31231
31045
|
return false;
|
|
31232
31046
|
}
|
|
@@ -31258,14 +31072,14 @@ async function runCronPromptScript(options) {
|
|
|
31258
31072
|
if (options.signal?.aborted)
|
|
31259
31073
|
throw new CronScriptError("script run aborted");
|
|
31260
31074
|
const scriptPath = resolveCronScriptPath(options.script);
|
|
31261
|
-
if (!
|
|
31075
|
+
if (!existsSync34(scriptPath))
|
|
31262
31076
|
throw new CronScriptError(`cron script not found: ${options.script}`);
|
|
31263
31077
|
const timeoutMs = Math.max(5000, options.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS);
|
|
31264
31078
|
const outputLimitBytes = Math.max(1024, options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES);
|
|
31265
31079
|
const command = commandForScript(scriptPath);
|
|
31266
31080
|
let child;
|
|
31267
31081
|
try {
|
|
31268
|
-
child =
|
|
31082
|
+
child = spawn8(command.executable, command.args, {
|
|
31269
31083
|
cwd: options.cwd,
|
|
31270
31084
|
detached: true,
|
|
31271
31085
|
env: {
|
|
@@ -31331,7 +31145,7 @@ async function runCronPromptScript(options) {
|
|
|
31331
31145
|
var CRON_JOBS_DIR, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_OUTPUT_LIMIT_BYTES, KILL_GRACE_MS2 = 2500, CronScriptError;
|
|
31332
31146
|
var init_scripts = __esm(async () => {
|
|
31333
31147
|
await init_cron_host();
|
|
31334
|
-
CRON_JOBS_DIR =
|
|
31148
|
+
CRON_JOBS_DIR = resolve23(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve23(WORKSPACE_DIR, "cron", "jobs"));
|
|
31335
31149
|
DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 60000;
|
|
31336
31150
|
DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;
|
|
31337
31151
|
CronScriptError = class CronScriptError extends Error {
|
|
@@ -32451,8 +32265,8 @@ var init_scheduler = __esm(async () => {
|
|
|
32451
32265
|
});
|
|
32452
32266
|
|
|
32453
32267
|
// ../../packages/module-cron/src/module.ts
|
|
32454
|
-
import { existsSync as
|
|
32455
|
-
import { fileURLToPath as
|
|
32268
|
+
import { existsSync as existsSync35 } from "fs";
|
|
32269
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
32456
32270
|
function envMs(name, fallback) {
|
|
32457
32271
|
const parsed = Number.parseInt(process.env[name] ?? "", 10);
|
|
32458
32272
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -32482,7 +32296,7 @@ function createCronModule(options = {}) {
|
|
|
32482
32296
|
if (event.type === "topic-deleted")
|
|
32483
32297
|
trackCleanup(resetCronTopicContext(event.topicId));
|
|
32484
32298
|
});
|
|
32485
|
-
const mcpServerFile = options.mcpServerFile ?? (
|
|
32299
|
+
const mcpServerFile = options.mcpServerFile ?? (existsSync35(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
|
|
32486
32300
|
const unregisterMcp = host2.registerRuntimeMcpServer("cron-manager", {
|
|
32487
32301
|
scopes: ["forum", "manager"],
|
|
32488
32302
|
forumRequired: true,
|
|
@@ -32575,8 +32389,8 @@ var init_module = __esm(async () => {
|
|
|
32575
32389
|
await init_scheduler();
|
|
32576
32390
|
await init_scripts();
|
|
32577
32391
|
await init_store();
|
|
32578
|
-
SOURCE_MCP_SERVER_FILE =
|
|
32579
|
-
PACKAGED_MCP_SERVER_FILE =
|
|
32392
|
+
SOURCE_MCP_SERVER_FILE = fileURLToPath3(new URL("./mcp-server.ts", import.meta.url));
|
|
32393
|
+
PACKAGED_MCP_SERVER_FILE = fileURLToPath3(new URL("./runtime/cron/mcp-server.ts", import.meta.url));
|
|
32580
32394
|
});
|
|
32581
32395
|
|
|
32582
32396
|
// ../../packages/module-cron/src/summarize.ts
|
|
@@ -32762,10 +32576,10 @@ __export(exports_src3, {
|
|
|
32762
32576
|
NODE_CONTROL_PROTOCOL_VERSION: () => NODE_CONTROL_PROTOCOL_VERSION,
|
|
32763
32577
|
NODE_CONTROL_BASE_PATH: () => NODE_CONTROL_BASE_PATH
|
|
32764
32578
|
});
|
|
32765
|
-
import { createServer as
|
|
32579
|
+
import { createServer as createServer5 } from "net";
|
|
32766
32580
|
async function availableLoopbackPort() {
|
|
32767
|
-
return new Promise((
|
|
32768
|
-
const probe =
|
|
32581
|
+
return new Promise((resolve24, reject) => {
|
|
32582
|
+
const probe = createServer5();
|
|
32769
32583
|
probe.unref();
|
|
32770
32584
|
probe.once("error", reject);
|
|
32771
32585
|
probe.listen(0, "127.0.0.1", () => {
|
|
@@ -32775,7 +32589,7 @@ async function availableLoopbackPort() {
|
|
|
32775
32589
|
if (error2)
|
|
32776
32590
|
reject(error2);
|
|
32777
32591
|
else if (port > 0)
|
|
32778
|
-
|
|
32592
|
+
resolve24(port);
|
|
32779
32593
|
else
|
|
32780
32594
|
reject(new Error("failed to allocate a loopback port"));
|
|
32781
32595
|
});
|
|
@@ -32914,8 +32728,8 @@ function startNode(opts = {}) {
|
|
|
32914
32728
|
const manifest = new McpManifest;
|
|
32915
32729
|
const stopSweeper = mcpHost.startSweeper();
|
|
32916
32730
|
let resolveCompleted;
|
|
32917
|
-
const completed = new Promise((
|
|
32918
|
-
resolveCompleted =
|
|
32731
|
+
const completed = new Promise((resolve24) => {
|
|
32732
|
+
resolveCompleted = resolve24;
|
|
32919
32733
|
});
|
|
32920
32734
|
let advertised = null;
|
|
32921
32735
|
try {
|
|
@@ -32994,7 +32808,7 @@ async function runNodeDaemon(opts = {}) {
|
|
|
32994
32808
|
singleton: true
|
|
32995
32809
|
});
|
|
32996
32810
|
await node.completed;
|
|
32997
|
-
await new Promise((
|
|
32811
|
+
await new Promise((resolve24) => setImmediate(resolve24));
|
|
32998
32812
|
process.exit(0);
|
|
32999
32813
|
}
|
|
33000
32814
|
var init_src5 = __esm(async () => {
|
|
@@ -34040,9 +33854,9 @@ var init_context_usage = __esm(async () => {
|
|
|
34040
33854
|
|
|
34041
33855
|
// ../../adapters/terminal/src/path-suggest.ts
|
|
34042
33856
|
import { execFile as execFile3 } from "child_process";
|
|
34043
|
-
import { existsSync as
|
|
33857
|
+
import { existsSync as existsSync36, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
|
|
34044
33858
|
import { homedir as homedir10 } from "os";
|
|
34045
|
-
import { basename as basename11, dirname as
|
|
33859
|
+
import { basename as basename11, dirname as dirname21, join as join42, sep as sep3 } from "path";
|
|
34046
33860
|
import { promisify as promisify3 } from "util";
|
|
34047
33861
|
function indexRecursivePaths(files) {
|
|
34048
33862
|
const paths = [];
|
|
@@ -34143,15 +33957,15 @@ function resolveFragment(frag) {
|
|
|
34143
33957
|
else if (frag === "~/")
|
|
34144
33958
|
path = home;
|
|
34145
33959
|
else if (frag.startsWith("~/"))
|
|
34146
|
-
path =
|
|
33960
|
+
path = join42(home, frag.slice(2));
|
|
34147
33961
|
else if (frag.startsWith("/"))
|
|
34148
33962
|
path = frag;
|
|
34149
33963
|
else
|
|
34150
|
-
path =
|
|
33964
|
+
path = join42(home, frag);
|
|
34151
33965
|
if (frag.endsWith("/") || frag === "" || frag === "~") {
|
|
34152
33966
|
return { dir: path, prefix: "" };
|
|
34153
33967
|
}
|
|
34154
|
-
return { dir:
|
|
33968
|
+
return { dir: dirname21(path), prefix: basename11(path) };
|
|
34155
33969
|
}
|
|
34156
33970
|
function toToken(fullPath, isDir) {
|
|
34157
33971
|
const home = homedir10();
|
|
@@ -34176,7 +33990,7 @@ function rankAndSlice(dir, candidates) {
|
|
|
34176
33990
|
});
|
|
34177
33991
|
return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
|
|
34178
33992
|
label: `${relPath}${isDir ? "/" : ""}`,
|
|
34179
|
-
value: toToken(
|
|
33993
|
+
value: toToken(join42(dir, relPath), isDir),
|
|
34180
33994
|
isDir
|
|
34181
33995
|
}));
|
|
34182
33996
|
}
|
|
@@ -34255,7 +34069,7 @@ function pathSuggestions(lineText, col) {
|
|
|
34255
34069
|
let isDir = entry.isDirectory();
|
|
34256
34070
|
if (!isDir && entry.isSymbolicLink()) {
|
|
34257
34071
|
try {
|
|
34258
|
-
isDir = statSync19(
|
|
34072
|
+
isDir = statSync19(join42(dir, entry.name)).isDirectory();
|
|
34259
34073
|
} catch {}
|
|
34260
34074
|
}
|
|
34261
34075
|
return {
|
|
@@ -34282,14 +34096,14 @@ function fragmentToAbsolutePath(frag) {
|
|
|
34282
34096
|
if (frag === "~" || frag === "~/")
|
|
34283
34097
|
return home;
|
|
34284
34098
|
if (frag.startsWith("~/"))
|
|
34285
|
-
return
|
|
34099
|
+
return join42(home, frag.slice(2));
|
|
34286
34100
|
if (frag.startsWith("/"))
|
|
34287
34101
|
return frag;
|
|
34288
|
-
return
|
|
34102
|
+
return join42(home, frag);
|
|
34289
34103
|
}
|
|
34290
34104
|
function fragmentResolves(frag) {
|
|
34291
34105
|
try {
|
|
34292
|
-
return
|
|
34106
|
+
return existsSync36(fragmentToAbsolutePath(frag));
|
|
34293
34107
|
} catch {
|
|
34294
34108
|
return false;
|
|
34295
34109
|
}
|
|
@@ -38193,8 +38007,8 @@ class TerminalApp {
|
|
|
38193
38007
|
this.#refreshBackgroundSessions();
|
|
38194
38008
|
}, 1000);
|
|
38195
38009
|
this.#backgroundRefreshTimer.unref?.();
|
|
38196
|
-
await new Promise((
|
|
38197
|
-
this.#finishRun =
|
|
38010
|
+
await new Promise((resolve24) => {
|
|
38011
|
+
this.#finishRun = resolve24;
|
|
38198
38012
|
});
|
|
38199
38013
|
} finally {
|
|
38200
38014
|
this.#running = false;
|
|
@@ -40791,7 +40605,7 @@ var init_commands2 = __esm(async () => {
|
|
|
40791
40605
|
// ../../adapters/telegram/src/mapping-store.ts
|
|
40792
40606
|
import { Database as Database2 } from "bun:sqlite";
|
|
40793
40607
|
import { mkdirSync as mkdirSync32 } from "fs";
|
|
40794
|
-
import { dirname as
|
|
40608
|
+
import { dirname as dirname22, join as join43 } from "path";
|
|
40795
40609
|
function outboxRowToEntry(row) {
|
|
40796
40610
|
return {
|
|
40797
40611
|
id: row.id,
|
|
@@ -40857,9 +40671,9 @@ function migrateOutboxSchema(db4) {
|
|
|
40857
40671
|
db4.run("CREATE INDEX IF NOT EXISTS idx_telegram_outbox_runtime_message ON outbox(runtime_message_id)");
|
|
40858
40672
|
}
|
|
40859
40673
|
function openMappingStore(path) {
|
|
40860
|
-
const dbPath = path ??
|
|
40674
|
+
const dbPath = path ?? join43(DATA_DIR, "adapter-telegram.db");
|
|
40861
40675
|
if (dbPath !== ":memory:")
|
|
40862
|
-
mkdirSync32(
|
|
40676
|
+
mkdirSync32(dirname22(dbPath), { recursive: true });
|
|
40863
40677
|
const db4 = new Database2(dbPath);
|
|
40864
40678
|
migrateLegacySchema(db4);
|
|
40865
40679
|
createTables(db4);
|
|
@@ -41094,8 +40908,8 @@ ${caption}` : voiceText : caption;
|
|
|
41094
40908
|
return;
|
|
41095
40909
|
}
|
|
41096
40910
|
let release;
|
|
41097
|
-
const ready = new Promise((
|
|
41098
|
-
release =
|
|
40911
|
+
const ready = new Promise((resolve24) => {
|
|
40912
|
+
release = resolve24;
|
|
41099
40913
|
});
|
|
41100
40914
|
const entry = {
|
|
41101
40915
|
messages: [msg],
|
|
@@ -41217,7 +41031,7 @@ function renderOutbound(text2, maxLen = 4096) {
|
|
|
41217
41031
|
}
|
|
41218
41032
|
|
|
41219
41033
|
// ../../adapters/telegram/src/adapter.ts
|
|
41220
|
-
import { existsSync as
|
|
41034
|
+
import { existsSync as existsSync37 } from "fs";
|
|
41221
41035
|
import { basename as basename12, extname as extname5 } from "path";
|
|
41222
41036
|
function startTelegramAdapter(opts) {
|
|
41223
41037
|
const { client, forumChatId } = opts;
|
|
@@ -41328,7 +41142,7 @@ function startTelegramAdapter(opts) {
|
|
|
41328
41142
|
attempt += 1;
|
|
41329
41143
|
const waitMs = Math.min(5000, 250 * 2 ** Math.min(attempt - 1, 5));
|
|
41330
41144
|
logger.warn({ err: err2, attempt, waitMs }, "telegram adapter: getMe failed while resolving onboarding identity; retrying");
|
|
41331
|
-
await new Promise((
|
|
41145
|
+
await new Promise((resolve24) => setTimeout(resolve24, waitMs));
|
|
41332
41146
|
}
|
|
41333
41147
|
}
|
|
41334
41148
|
return;
|
|
@@ -41715,7 +41529,7 @@ function startTelegramAdapter(opts) {
|
|
|
41715
41529
|
logger.warn({ path, chatId }, "telegram adapter: blocked sensitive file path");
|
|
41716
41530
|
return { ref: null, delivered: false, error: "path matches the sensitive-file blacklist" };
|
|
41717
41531
|
}
|
|
41718
|
-
if (!
|
|
41532
|
+
if (!existsSync37(path)) {
|
|
41719
41533
|
const sent = await client.sendMessage(chatId, `File: ${path}`, base).catch(() => null);
|
|
41720
41534
|
const messageId = sentMessageId(sent);
|
|
41721
41535
|
return {
|
|
@@ -41815,14 +41629,14 @@ function startTelegramAdapter(opts) {
|
|
|
41815
41629
|
const sendQueues = new Map;
|
|
41816
41630
|
function enqueueSend(topicId, task) {
|
|
41817
41631
|
const prev = sendQueues.get(topicId) ?? Promise.resolve();
|
|
41818
|
-
const next = prev.then(() => new Promise((
|
|
41632
|
+
const next = prev.then(() => new Promise((resolve24) => {
|
|
41819
41633
|
const timer = setTimeout(() => {
|
|
41820
41634
|
logger.warn({ topicId, sendTimeoutMs }, "telegram adapter: send timed out \u2014 abandoning it and continuing the queue");
|
|
41821
|
-
|
|
41635
|
+
resolve24();
|
|
41822
41636
|
}, sendTimeoutMs);
|
|
41823
41637
|
task().catch((err2) => logger.warn({ err: err2, topicId }, "telegram adapter: send task failed")).finally(() => {
|
|
41824
41638
|
clearTimeout(timer);
|
|
41825
|
-
|
|
41639
|
+
resolve24();
|
|
41826
41640
|
});
|
|
41827
41641
|
}));
|
|
41828
41642
|
sendQueues.set(topicId, next);
|
|
@@ -42068,7 +41882,7 @@ ${suffix}`;
|
|
|
42068
41882
|
continue;
|
|
42069
41883
|
}
|
|
42070
41884
|
if (retryDelayMs > 0) {
|
|
42071
|
-
await new Promise((
|
|
41885
|
+
await new Promise((resolve24) => setTimeout(resolve24, retryDelayMs));
|
|
42072
41886
|
}
|
|
42073
41887
|
try {
|
|
42074
41888
|
await client.editMessageText(text2, editOptions);
|
|
@@ -42709,8 +42523,8 @@ async function runTelegramCli(args = process.argv.slice(2)) {
|
|
|
42709
42523
|
onShutdown("telegram-channel", 100, () => channel.stop());
|
|
42710
42524
|
onShutdown("telegram-singleton", 90, () => singleton.stop());
|
|
42711
42525
|
let resolveCompleted;
|
|
42712
|
-
const completed = new Promise((
|
|
42713
|
-
resolveCompleted =
|
|
42526
|
+
const completed = new Promise((resolve24) => {
|
|
42527
|
+
resolveCompleted = resolve24;
|
|
42714
42528
|
});
|
|
42715
42529
|
onShutdown("telegram-completed", -100, resolveCompleted);
|
|
42716
42530
|
process.stdout.write(`negotium Telegram adapter connected to canonical node pid ${initialNode.info?.pid ?? "unknown"}
|
|
@@ -42735,11 +42549,11 @@ var exports_join_status = {};
|
|
|
42735
42549
|
__export(exports_join_status, {
|
|
42736
42550
|
hasConfiguredOtiumJoin: () => hasConfiguredOtiumJoin
|
|
42737
42551
|
});
|
|
42738
|
-
import { existsSync as
|
|
42739
|
-
import { resolve as
|
|
42552
|
+
import { existsSync as existsSync38 } from "fs";
|
|
42553
|
+
import { resolve as resolve24 } from "path";
|
|
42740
42554
|
function hasConfiguredOtiumJoin() {
|
|
42741
42555
|
const envJoin = Boolean(process.env.OTIUM_CENTRAL_URL?.trim() && process.env.OTIUM_CELL_ID?.trim() && process.env.OTIUM_CELL_SECRET?.trim());
|
|
42742
|
-
return envJoin ||
|
|
42556
|
+
return envJoin || existsSync38(resolve24(DATA_DIR, "otium-join.json"));
|
|
42743
42557
|
}
|
|
42744
42558
|
var init_join_status = __esm(() => {
|
|
42745
42559
|
init_config_public();
|
|
@@ -42757,9 +42571,9 @@ function firstCell() {
|
|
|
42757
42571
|
return cell;
|
|
42758
42572
|
return null;
|
|
42759
42573
|
}
|
|
42760
|
-
function attachOtiumCentralCell(
|
|
42761
|
-
cells.set(
|
|
42762
|
-
join:
|
|
42574
|
+
function attachOtiumCentralCell(join44) {
|
|
42575
|
+
cells.set(join44.cellId, {
|
|
42576
|
+
join: join44,
|
|
42763
42577
|
nodesCache: null,
|
|
42764
42578
|
verifyCache: new Map,
|
|
42765
42579
|
tokenCache: new Map
|
|
@@ -42771,10 +42585,10 @@ function detachOtiumCentralCell(cellId) {
|
|
|
42771
42585
|
function attachedOtiumCells() {
|
|
42772
42586
|
return [...cells.values()].map((cell) => cell.join);
|
|
42773
42587
|
}
|
|
42774
|
-
function configureOtiumCentral(
|
|
42588
|
+
function configureOtiumCentral(join44) {
|
|
42775
42589
|
cells.clear();
|
|
42776
|
-
if (
|
|
42777
|
-
attachOtiumCentralCell(
|
|
42590
|
+
if (join44)
|
|
42591
|
+
attachOtiumCentralCell(join44);
|
|
42778
42592
|
}
|
|
42779
42593
|
function isOtiumCentralConfigured() {
|
|
42780
42594
|
return cells.size > 0;
|
|
@@ -43188,7 +43002,7 @@ import { createHash as createHash11, randomUUID as randomUUID28 } from "crypto";
|
|
|
43188
43002
|
import {
|
|
43189
43003
|
chmodSync as chmodSync7,
|
|
43190
43004
|
closeSync as closeSync5,
|
|
43191
|
-
existsSync as
|
|
43005
|
+
existsSync as existsSync39,
|
|
43192
43006
|
fsyncSync as fsyncSync2,
|
|
43193
43007
|
linkSync,
|
|
43194
43008
|
lstatSync,
|
|
@@ -43201,9 +43015,9 @@ import {
|
|
|
43201
43015
|
unlinkSync as unlinkSync23,
|
|
43202
43016
|
writeFileSync as writeFileSync25
|
|
43203
43017
|
} from "fs";
|
|
43204
|
-
import { dirname as
|
|
43018
|
+
import { dirname as dirname23, resolve as resolve25 } from "path";
|
|
43205
43019
|
function joinFilePath() {
|
|
43206
|
-
return
|
|
43020
|
+
return resolve25(DATA_DIR, "otium-join.json");
|
|
43207
43021
|
}
|
|
43208
43022
|
function isHttpUrl(value) {
|
|
43209
43023
|
return /^https?:\/\//.test(value);
|
|
@@ -43258,7 +43072,7 @@ function parseInviteCode(code) {
|
|
|
43258
43072
|
return normalizeJoin(parsed);
|
|
43259
43073
|
}
|
|
43260
43074
|
function joinLockPath() {
|
|
43261
|
-
return
|
|
43075
|
+
return resolve25(DATA_DIR, ".otium-join.lock");
|
|
43262
43076
|
}
|
|
43263
43077
|
function processIsAlive(pid) {
|
|
43264
43078
|
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
@@ -43272,9 +43086,9 @@ function processIsAlive(pid) {
|
|
|
43272
43086
|
}
|
|
43273
43087
|
function withJoinCredentialLock(operation) {
|
|
43274
43088
|
const lockPath = joinLockPath();
|
|
43275
|
-
const ownerPath =
|
|
43089
|
+
const ownerPath = resolve25(lockPath, "owner.json");
|
|
43276
43090
|
const owner = { pid: process.pid, token: randomUUID28() };
|
|
43277
|
-
mkdirSync33(
|
|
43091
|
+
mkdirSync33(dirname23(lockPath), { recursive: true });
|
|
43278
43092
|
for (let attempt = 0;; attempt += 1) {
|
|
43279
43093
|
let created = false;
|
|
43280
43094
|
try {
|
|
@@ -43338,20 +43152,20 @@ function withJoinCredentialLock(operation) {
|
|
|
43338
43152
|
function joinsEqual(left, right) {
|
|
43339
43153
|
return left.central === right.central && left.relay === right.relay && left.cellId === right.cellId && left.secret === right.secret;
|
|
43340
43154
|
}
|
|
43341
|
-
function normalizedJoin(
|
|
43155
|
+
function normalizedJoin(join44) {
|
|
43342
43156
|
return normalizeJoin({
|
|
43343
|
-
v:
|
|
43344
|
-
central:
|
|
43345
|
-
relay:
|
|
43346
|
-
cellId:
|
|
43347
|
-
secret:
|
|
43157
|
+
v: join44.v,
|
|
43158
|
+
central: join44.central,
|
|
43159
|
+
relay: join44.relay,
|
|
43160
|
+
cellId: join44.cellId,
|
|
43161
|
+
secret: join44.secret
|
|
43348
43162
|
});
|
|
43349
43163
|
}
|
|
43350
|
-
function joinCredentialDigest(
|
|
43351
|
-
return createHash11("sha256").update(JSON.stringify(normalizedJoin(
|
|
43164
|
+
function joinCredentialDigest(join44) {
|
|
43165
|
+
return createHash11("sha256").update(JSON.stringify(normalizedJoin(join44))).digest("base64url");
|
|
43352
43166
|
}
|
|
43353
43167
|
function readPersistedJoins(path = joinFilePath()) {
|
|
43354
|
-
if (!
|
|
43168
|
+
if (!existsSync39(path))
|
|
43355
43169
|
return [];
|
|
43356
43170
|
const parsed = JSON.parse(readFileSync29(path, "utf-8"));
|
|
43357
43171
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -43367,9 +43181,9 @@ function readPersistedJoins(path = joinFilePath()) {
|
|
|
43367
43181
|
return normalizeJoin(entry);
|
|
43368
43182
|
});
|
|
43369
43183
|
}
|
|
43370
|
-
function isJoinPersisted(
|
|
43184
|
+
function isJoinPersisted(join44) {
|
|
43371
43185
|
try {
|
|
43372
|
-
const normalized = normalizedJoin(
|
|
43186
|
+
const normalized = normalizedJoin(join44);
|
|
43373
43187
|
return readPersistedJoins().some((persisted) => joinsEqual(persisted, normalized));
|
|
43374
43188
|
} catch {
|
|
43375
43189
|
return false;
|
|
@@ -43385,8 +43199,8 @@ function fsyncPath(target) {
|
|
|
43385
43199
|
}
|
|
43386
43200
|
function writeJoins(joins, allowOverwrite) {
|
|
43387
43201
|
const path = joinFilePath();
|
|
43388
|
-
const directory =
|
|
43389
|
-
const temporaryPath =
|
|
43202
|
+
const directory = dirname23(path);
|
|
43203
|
+
const temporaryPath = resolve25(directory, `.otium-join.json.${process.pid}.${randomUUID28()}.tmp`);
|
|
43390
43204
|
const payload = { v: JOIN_FILE_VERSION, joins };
|
|
43391
43205
|
let fd;
|
|
43392
43206
|
try {
|
|
@@ -43407,18 +43221,18 @@ function writeJoins(joins, allowOverwrite) {
|
|
|
43407
43221
|
} catch (error2) {
|
|
43408
43222
|
if (fd !== undefined)
|
|
43409
43223
|
closeSync5(fd);
|
|
43410
|
-
if (
|
|
43224
|
+
if (existsSync39(temporaryPath))
|
|
43411
43225
|
unlinkSync23(temporaryPath);
|
|
43412
43226
|
throw error2;
|
|
43413
43227
|
}
|
|
43414
43228
|
return path;
|
|
43415
43229
|
}
|
|
43416
|
-
function saveJoinWhileLocked(
|
|
43230
|
+
function saveJoinWhileLocked(join44, options = {}) {
|
|
43417
43231
|
const path = joinFilePath();
|
|
43418
|
-
const directory =
|
|
43419
|
-
const normalized = normalizedJoin(
|
|
43232
|
+
const directory = dirname23(path);
|
|
43233
|
+
const normalized = normalizedJoin(join44);
|
|
43420
43234
|
mkdirSync33(directory, { recursive: true });
|
|
43421
|
-
if (!
|
|
43235
|
+
if (!existsSync39(path))
|
|
43422
43236
|
return writeJoins([normalized], false);
|
|
43423
43237
|
if (lstatSync(path).isSymbolicLink()) {
|
|
43424
43238
|
throw new Error(`refusing to replace symlinked Otium join file at ${path}`);
|
|
@@ -43446,13 +43260,13 @@ function saveJoinWhileLocked(join43, options = {}) {
|
|
|
43446
43260
|
const next = conflict ? existing.map((persisted) => persisted.cellId === normalized.cellId ? normalized : persisted) : [...existing, normalized];
|
|
43447
43261
|
return writeJoins(next, true);
|
|
43448
43262
|
}
|
|
43449
|
-
function saveJoin(
|
|
43450
|
-
return withJoinCredentialLock(() => saveJoinWhileLocked(
|
|
43263
|
+
function saveJoin(join44, options = {}) {
|
|
43264
|
+
return withJoinCredentialLock(() => saveJoinWhileLocked(join44, options));
|
|
43451
43265
|
}
|
|
43452
43266
|
function removeJoin(cellId) {
|
|
43453
43267
|
return withJoinCredentialLock(() => {
|
|
43454
43268
|
const path = joinFilePath();
|
|
43455
|
-
if (!
|
|
43269
|
+
if (!existsSync39(path))
|
|
43456
43270
|
return false;
|
|
43457
43271
|
if (lstatSync(path).isSymbolicLink()) {
|
|
43458
43272
|
throw new Error(`refusing to remove symlinked Otium join file at ${path}`);
|
|
@@ -43460,7 +43274,7 @@ function removeJoin(cellId) {
|
|
|
43460
43274
|
if (cellId) {
|
|
43461
43275
|
let remaining;
|
|
43462
43276
|
try {
|
|
43463
|
-
remaining = readPersistedJoins(path).filter((
|
|
43277
|
+
remaining = readPersistedJoins(path).filter((join44) => join44.cellId !== cellId);
|
|
43464
43278
|
} catch {
|
|
43465
43279
|
return false;
|
|
43466
43280
|
}
|
|
@@ -43472,7 +43286,7 @@ function removeJoin(cellId) {
|
|
|
43472
43286
|
}
|
|
43473
43287
|
}
|
|
43474
43288
|
unlinkSync23(path);
|
|
43475
|
-
fsyncPath(
|
|
43289
|
+
fsyncPath(dirname23(path));
|
|
43476
43290
|
return true;
|
|
43477
43291
|
});
|
|
43478
43292
|
}
|
|
@@ -43493,7 +43307,7 @@ function loadJoins() {
|
|
|
43493
43307
|
logger.warn("otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together \u2014 ignoring partial env");
|
|
43494
43308
|
}
|
|
43495
43309
|
const path = joinFilePath();
|
|
43496
|
-
if (!
|
|
43310
|
+
if (!existsSync39(path))
|
|
43497
43311
|
return [];
|
|
43498
43312
|
try {
|
|
43499
43313
|
return readPersistedJoins(path);
|
|
@@ -43513,7 +43327,7 @@ var init_join = __esm(async () => {
|
|
|
43513
43327
|
// ../../adapters/otium/src/peer-files.ts
|
|
43514
43328
|
import { randomUUID as randomUUID29 } from "crypto";
|
|
43515
43329
|
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
|
|
43330
|
+
import { basename as basename13, extname as extname6, join as join44 } from "path";
|
|
43517
43331
|
function fileType(mimeType) {
|
|
43518
43332
|
if (mimeType.startsWith("image/"))
|
|
43519
43333
|
return "image";
|
|
@@ -43579,7 +43393,7 @@ function recordFile(args) {
|
|
|
43579
43393
|
function insertLocalFile(args) {
|
|
43580
43394
|
const id = randomUUID29();
|
|
43581
43395
|
mkdirSync34(PEER_FILES_DIR, { recursive: true });
|
|
43582
|
-
const path =
|
|
43396
|
+
const path = join44(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
|
|
43583
43397
|
copyFileSync5(args.sourcePath, path);
|
|
43584
43398
|
return recordFile({ id, path, sizeBytes: statSync21(path).size, ...args });
|
|
43585
43399
|
}
|
|
@@ -43625,7 +43439,7 @@ function installPeerFileHooks() {
|
|
|
43625
43439
|
var PEER_FILES_DIR;
|
|
43626
43440
|
var init_peer_files = __esm(async () => {
|
|
43627
43441
|
await init_src();
|
|
43628
|
-
PEER_FILES_DIR =
|
|
43442
|
+
PEER_FILES_DIR = join44(DATA_DIR, "otium-peer-files");
|
|
43629
43443
|
db.exec(`
|
|
43630
43444
|
CREATE TABLE IF NOT EXISTS otium_peer_files (
|
|
43631
43445
|
id TEXT PRIMARY KEY,
|
|
@@ -44018,14 +43832,14 @@ var init_store2 = __esm(async () => {
|
|
|
44018
43832
|
// ../../adapters/otium/src/workspace-scope.ts
|
|
44019
43833
|
import { createHash as createHash13 } from "crypto";
|
|
44020
43834
|
import { mkdirSync as mkdirSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
|
|
44021
|
-
import { dirname as
|
|
43835
|
+
import { dirname as dirname24, resolve as resolve26 } from "path";
|
|
44022
43836
|
function surfaceScopeFor(central, workspaceId) {
|
|
44023
43837
|
const digest = createHash13("sha256").update(`${central.trim().replace(/\/+$/, "")}
|
|
44024
43838
|
${workspaceId.trim()}`).digest("hex");
|
|
44025
43839
|
return `ws_${digest.slice(0, 24)}`;
|
|
44026
43840
|
}
|
|
44027
43841
|
function scopeCachePath() {
|
|
44028
|
-
return
|
|
43842
|
+
return resolve26(DATA_DIR, "otium-workspace.json");
|
|
44029
43843
|
}
|
|
44030
43844
|
function readCache() {
|
|
44031
43845
|
try {
|
|
@@ -44037,19 +43851,19 @@ function readCache() {
|
|
|
44037
43851
|
return {};
|
|
44038
43852
|
}
|
|
44039
43853
|
}
|
|
44040
|
-
function cachedSurfaceScope(
|
|
44041
|
-
const record = readCache()[
|
|
44042
|
-
if (!record || record.central !==
|
|
43854
|
+
function cachedSurfaceScope(join45) {
|
|
43855
|
+
const record = readCache()[join45.cellId];
|
|
43856
|
+
if (!record || record.central !== join45.central || !record.workspaceId)
|
|
44043
43857
|
return null;
|
|
44044
43858
|
return surfaceScopeFor(record.central, record.workspaceId);
|
|
44045
43859
|
}
|
|
44046
|
-
function cacheSurfaceScope(
|
|
44047
|
-
const scope = surfaceScopeFor(
|
|
43860
|
+
function cacheSurfaceScope(join45, workspaceId) {
|
|
43861
|
+
const scope = surfaceScopeFor(join45.central, workspaceId);
|
|
44048
43862
|
const path = scopeCachePath();
|
|
44049
43863
|
try {
|
|
44050
|
-
mkdirSync35(
|
|
43864
|
+
mkdirSync35(dirname24(path), { recursive: true });
|
|
44051
43865
|
const cache = readCache();
|
|
44052
|
-
cache[
|
|
43866
|
+
cache[join45.cellId] = { central: join45.central, workspaceId, scope };
|
|
44053
43867
|
writeFileSync26(path, `${JSON.stringify(cache, null, 2)}
|
|
44054
43868
|
`, { mode: 384 });
|
|
44055
43869
|
} catch (err2) {
|
|
@@ -44057,23 +43871,23 @@ function cacheSurfaceScope(join44, workspaceId) {
|
|
|
44057
43871
|
}
|
|
44058
43872
|
return scope;
|
|
44059
43873
|
}
|
|
44060
|
-
async function resolveSurfaceScope(
|
|
44061
|
-
const cached = cachedSurfaceScope(
|
|
43874
|
+
async function resolveSurfaceScope(join45) {
|
|
43875
|
+
const cached = cachedSurfaceScope(join45);
|
|
44062
43876
|
if (cached)
|
|
44063
43877
|
return cached;
|
|
44064
43878
|
try {
|
|
44065
|
-
const workspaceId = await peerWorkspaceIdForCell(
|
|
43879
|
+
const workspaceId = await peerWorkspaceIdForCell(join45.cellId);
|
|
44066
43880
|
if (!workspaceId)
|
|
44067
43881
|
return null;
|
|
44068
|
-
return cacheSurfaceScope(
|
|
43882
|
+
return cacheSurfaceScope(join45, workspaceId);
|
|
44069
43883
|
} catch (err2) {
|
|
44070
43884
|
logger.warn({ err: err2 }, "otium: workspace scope unresolved (will retry on the next contact)");
|
|
44071
43885
|
return null;
|
|
44072
43886
|
}
|
|
44073
43887
|
}
|
|
44074
43888
|
function surfaceScopeForCell(cellId) {
|
|
44075
|
-
const
|
|
44076
|
-
return
|
|
43889
|
+
const join45 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
|
|
43890
|
+
return join45 ? cachedSurfaceScope(join45) : null;
|
|
44077
43891
|
}
|
|
44078
43892
|
function unscopedRoomsAddressable() {
|
|
44079
43893
|
return attachedOtiumCells().length < 2;
|
|
@@ -45086,7 +44900,7 @@ import {
|
|
|
45086
44900
|
import {
|
|
45087
44901
|
chmodSync as chmodSync8,
|
|
45088
44902
|
closeSync as closeSync6,
|
|
45089
|
-
existsSync as
|
|
44903
|
+
existsSync as existsSync40,
|
|
45090
44904
|
fsyncSync as fsyncSync3,
|
|
45091
44905
|
linkSync as linkSync2,
|
|
45092
44906
|
mkdirSync as mkdirSync36,
|
|
@@ -45096,7 +44910,7 @@ import {
|
|
|
45096
44910
|
unlinkSync as unlinkSync24,
|
|
45097
44911
|
writeFileSync as writeFileSync27
|
|
45098
44912
|
} from "fs";
|
|
45099
|
-
import { dirname as
|
|
44913
|
+
import { dirname as dirname25, resolve as resolve27 } from "path";
|
|
45100
44914
|
function parseEnrollmentInvite(code) {
|
|
45101
44915
|
let parsed;
|
|
45102
44916
|
try {
|
|
@@ -45117,11 +44931,11 @@ function parseEnrollmentInvite(code) {
|
|
|
45117
44931
|
return { v: 2, central, token };
|
|
45118
44932
|
}
|
|
45119
44933
|
function pendingEnrollmentPath() {
|
|
45120
|
-
return
|
|
44934
|
+
return resolve27(DATA_DIR, "otium-enrollment-pending.json");
|
|
45121
44935
|
}
|
|
45122
44936
|
function isEnrollmentPending(invite) {
|
|
45123
44937
|
const path = pendingEnrollmentPath();
|
|
45124
|
-
if (!
|
|
44938
|
+
if (!existsSync40(path))
|
|
45125
44939
|
return false;
|
|
45126
44940
|
try {
|
|
45127
44941
|
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
@@ -45132,7 +44946,7 @@ function isEnrollmentPending(invite) {
|
|
|
45132
44946
|
}
|
|
45133
44947
|
function loadOrCreatePending(invite, nodeName) {
|
|
45134
44948
|
const path = pendingEnrollmentPath();
|
|
45135
|
-
if (
|
|
44949
|
+
if (existsSync40(path)) {
|
|
45136
44950
|
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
45137
44951
|
if (saved.central === invite.central && saved.token === invite.token)
|
|
45138
44952
|
return saved;
|
|
@@ -45146,8 +44960,8 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
45146
44960
|
publicKey: pair.publicKey.export({ format: "der", type: "spki" }).toString("base64url"),
|
|
45147
44961
|
...nodeName ? { nodeName } : {}
|
|
45148
44962
|
};
|
|
45149
|
-
mkdirSync36(
|
|
45150
|
-
const temporaryPath =
|
|
44963
|
+
mkdirSync36(dirname25(path), { recursive: true });
|
|
44964
|
+
const temporaryPath = resolve27(dirname25(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
45151
44965
|
let fd;
|
|
45152
44966
|
try {
|
|
45153
44967
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
@@ -45159,7 +44973,7 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
45159
44973
|
linkSync2(temporaryPath, path);
|
|
45160
44974
|
unlinkSync24(temporaryPath);
|
|
45161
44975
|
chmodSync8(path, 384);
|
|
45162
|
-
const directoryFd = openSync6(
|
|
44976
|
+
const directoryFd = openSync6(dirname25(path), "r");
|
|
45163
44977
|
try {
|
|
45164
44978
|
fsyncSync3(directoryFd);
|
|
45165
44979
|
} finally {
|
|
@@ -45168,9 +44982,9 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
45168
44982
|
} catch (error2) {
|
|
45169
44983
|
if (fd !== undefined)
|
|
45170
44984
|
closeSync6(fd);
|
|
45171
|
-
if (
|
|
44985
|
+
if (existsSync40(temporaryPath))
|
|
45172
44986
|
unlinkSync24(temporaryPath);
|
|
45173
|
-
if (error2.code === "EEXIST" &&
|
|
44987
|
+
if (error2.code === "EEXIST" && existsSync40(path)) {
|
|
45174
44988
|
const saved = JSON.parse(readFileSync31(path, "utf8"));
|
|
45175
44989
|
if (saved.central === invite.central && saved.token === invite.token)
|
|
45176
44990
|
return saved;
|
|
@@ -45181,8 +44995,8 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
45181
44995
|
}
|
|
45182
44996
|
function replacePendingEnrollment(pending2) {
|
|
45183
44997
|
const path = pendingEnrollmentPath();
|
|
45184
|
-
const directory =
|
|
45185
|
-
const temporaryPath =
|
|
44998
|
+
const directory = dirname25(path);
|
|
44999
|
+
const temporaryPath = resolve27(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
45186
45000
|
let fd;
|
|
45187
45001
|
try {
|
|
45188
45002
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
@@ -45201,18 +45015,18 @@ function replacePendingEnrollment(pending2) {
|
|
|
45201
45015
|
} catch (error2) {
|
|
45202
45016
|
if (fd !== undefined)
|
|
45203
45017
|
closeSync6(fd);
|
|
45204
|
-
if (
|
|
45018
|
+
if (existsSync40(temporaryPath))
|
|
45205
45019
|
unlinkSync24(temporaryPath);
|
|
45206
45020
|
throw error2;
|
|
45207
45021
|
}
|
|
45208
45022
|
}
|
|
45209
|
-
function recordClaimedCredential(pending2,
|
|
45023
|
+
function recordClaimedCredential(pending2, join45) {
|
|
45210
45024
|
withJoinCredentialLock(() => {
|
|
45211
45025
|
const current3 = JSON.parse(readFileSync31(pendingEnrollmentPath(), "utf8"));
|
|
45212
45026
|
if (current3.central !== pending2.central || current3.token !== pending2.token || current3.idempotencyKey !== pending2.idempotencyKey || current3.publicKey !== pending2.publicKey) {
|
|
45213
45027
|
throw new Error("pending Otium enrollment changed while its claim was in flight");
|
|
45214
45028
|
}
|
|
45215
|
-
const claimed = { digest: joinCredentialDigest(
|
|
45029
|
+
const claimed = { digest: joinCredentialDigest(join45), cellId: join45.cellId };
|
|
45216
45030
|
if (current3.claimed && (current3.claimed.digest !== claimed.digest || current3.claimed.cellId !== claimed.cellId)) {
|
|
45217
45031
|
throw new Error("central returned different credentials for an idempotent enrollment claim");
|
|
45218
45032
|
}
|
|
@@ -45270,36 +45084,36 @@ async function claimEnrollment(invite, nodeName) {
|
|
|
45270
45084
|
const secret = openCredential(credential, pending2.privateKey);
|
|
45271
45085
|
if (!secret.startsWith("rcs_"))
|
|
45272
45086
|
throw new Error("central returned an invalid runtime credential");
|
|
45273
|
-
const
|
|
45087
|
+
const join45 = {
|
|
45274
45088
|
v: 2,
|
|
45275
45089
|
central: invite.central,
|
|
45276
45090
|
relay: String(response.relayUrl),
|
|
45277
45091
|
cellId: String(response.cell?.id),
|
|
45278
45092
|
secret
|
|
45279
45093
|
};
|
|
45280
|
-
if (!
|
|
45094
|
+
if (!join45.relay || !join45.cellId || join45.cellId === "undefined") {
|
|
45281
45095
|
throw new Error("central returned an incomplete enrollment response");
|
|
45282
45096
|
}
|
|
45283
|
-
assertSecureRelayUrl(
|
|
45284
|
-
recordClaimedCredential(pending2,
|
|
45285
|
-
return
|
|
45097
|
+
assertSecureRelayUrl(join45.relay);
|
|
45098
|
+
recordClaimedCredential(pending2, join45);
|
|
45099
|
+
return join45;
|
|
45286
45100
|
}
|
|
45287
|
-
function commitEnrollment(
|
|
45101
|
+
function commitEnrollment(join45, options = {}) {
|
|
45288
45102
|
return withJoinCredentialLock(() => {
|
|
45289
45103
|
const pendingPath = pendingEnrollmentPath();
|
|
45290
|
-
const pending2 =
|
|
45291
|
-
const digest = joinCredentialDigest(
|
|
45292
|
-
if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !==
|
|
45104
|
+
const pending2 = existsSync40(pendingPath) ? JSON.parse(readFileSync31(pendingPath, "utf8")) : null;
|
|
45105
|
+
const digest = joinCredentialDigest(join45);
|
|
45106
|
+
if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join45.cellId)) {
|
|
45293
45107
|
throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
|
|
45294
45108
|
}
|
|
45295
|
-
const path = saveJoinWhileLocked(
|
|
45296
|
-
if (!isJoinPersisted(
|
|
45109
|
+
const path = saveJoinWhileLocked(join45, options);
|
|
45110
|
+
if (!isJoinPersisted(join45)) {
|
|
45297
45111
|
throw new Error("Otium join credentials were not durably persisted");
|
|
45298
45112
|
}
|
|
45299
45113
|
if (!pending2)
|
|
45300
45114
|
return path;
|
|
45301
45115
|
unlinkSync24(pendingPath);
|
|
45302
|
-
const directoryFd = openSync6(
|
|
45116
|
+
const directoryFd = openSync6(dirname25(pendingPath), "r");
|
|
45303
45117
|
try {
|
|
45304
45118
|
fsyncSync3(directoryFd);
|
|
45305
45119
|
} finally {
|
|
@@ -45887,24 +45701,24 @@ function refreshDefaultSurfaceScope() {
|
|
|
45887
45701
|
setSurfaceScopeRequired(scopes.length > 1);
|
|
45888
45702
|
}
|
|
45889
45703
|
function startOtiumNodeRuntime(options) {
|
|
45890
|
-
const { join:
|
|
45891
|
-
attachOtiumCentralCell(
|
|
45704
|
+
const { join: join45 } = options;
|
|
45705
|
+
attachOtiumCentralCell(join45);
|
|
45892
45706
|
const releaseGlobals = acquireGlobalOtiumServices();
|
|
45893
45707
|
let stopped = false;
|
|
45894
|
-
logger.info({ central:
|
|
45895
|
-
mountedScopes.set(
|
|
45708
|
+
logger.info({ central: join45.central, cellId: join45.cellId }, "otium: worker mode enabled");
|
|
45709
|
+
mountedScopes.set(join45.cellId, cachedSurfaceScope(join45));
|
|
45896
45710
|
refreshDefaultSurfaceScope();
|
|
45897
|
-
selfPeerNodeForCell(
|
|
45711
|
+
selfPeerNodeForCell(join45.cellId).then((self) => {
|
|
45898
45712
|
if (self) {
|
|
45899
45713
|
logger.info({ nodeName: self.nodeName, baseUrl: self.baseUrl }, "otium: attached to workspace");
|
|
45900
45714
|
}
|
|
45901
45715
|
}).catch((err2) => {
|
|
45902
45716
|
logger.warn({ err: err2 }, "otium: self check against central failed (will retry per request)");
|
|
45903
45717
|
});
|
|
45904
|
-
resolveSurfaceScope(
|
|
45718
|
+
resolveSurfaceScope(join45).then((scope) => {
|
|
45905
45719
|
if (!scope || stopped)
|
|
45906
45720
|
return;
|
|
45907
|
-
mountedScopes.set(
|
|
45721
|
+
mountedScopes.set(join45.cellId, scope);
|
|
45908
45722
|
refreshDefaultSurfaceScope();
|
|
45909
45723
|
if (mountedScopes.size === 1)
|
|
45910
45724
|
stampUnscopedOtiumTopics(scope);
|
|
@@ -45913,14 +45727,14 @@ function startOtiumNodeRuntime(options) {
|
|
|
45913
45727
|
});
|
|
45914
45728
|
return {
|
|
45915
45729
|
name: "otium",
|
|
45916
|
-
join:
|
|
45730
|
+
join: join45,
|
|
45917
45731
|
stop: () => {
|
|
45918
45732
|
if (stopped)
|
|
45919
45733
|
return;
|
|
45920
45734
|
stopped = true;
|
|
45921
|
-
mountedScopes.delete(
|
|
45735
|
+
mountedScopes.delete(join45.cellId);
|
|
45922
45736
|
refreshDefaultSurfaceScope();
|
|
45923
|
-
detachOtiumCentralCell(
|
|
45737
|
+
detachOtiumCentralCell(join45.cellId);
|
|
45924
45738
|
releaseGlobals();
|
|
45925
45739
|
}
|
|
45926
45740
|
};
|
|
@@ -46015,14 +45829,14 @@ __export(exports_node_runtime, {
|
|
|
46015
45829
|
function sameCredentials(left, right) {
|
|
46016
45830
|
return left.central === right.central && left.relay === right.relay && left.secret === right.secret;
|
|
46017
45831
|
}
|
|
46018
|
-
function attachOtiumWorkspace(
|
|
46019
|
-
const current3 = mounted.get(
|
|
45832
|
+
function attachOtiumWorkspace(join45) {
|
|
45833
|
+
const current3 = mounted.get(join45.cellId);
|
|
46020
45834
|
if (current3) {
|
|
46021
|
-
if (sameCredentials(current3.join,
|
|
45835
|
+
if (sameCredentials(current3.join, join45))
|
|
46022
45836
|
return false;
|
|
46023
|
-
detachOtiumWorkspace(
|
|
45837
|
+
detachOtiumWorkspace(join45.cellId);
|
|
46024
45838
|
}
|
|
46025
|
-
mounted.set(
|
|
45839
|
+
mounted.set(join45.cellId, startOtiumNodeRuntime({ join: join45 }));
|
|
46026
45840
|
return true;
|
|
46027
45841
|
}
|
|
46028
45842
|
function detachOtiumWorkspace(cellId) {
|
|
@@ -46037,7 +45851,7 @@ function mountedOtiumWorkspaces() {
|
|
|
46037
45851
|
return [...mounted.values()].map((runtime2) => runtime2.join);
|
|
46038
45852
|
}
|
|
46039
45853
|
function reconcileOtiumWorkspaces(joins = loadJoins()) {
|
|
46040
|
-
const wanted = new Map(joins.map((
|
|
45854
|
+
const wanted = new Map(joins.map((join45) => [join45.cellId, join45]));
|
|
46041
45855
|
const detached = [];
|
|
46042
45856
|
for (const cellId of [...mounted.keys()]) {
|
|
46043
45857
|
if (wanted.has(cellId))
|
|
@@ -46046,8 +45860,8 @@ function reconcileOtiumWorkspaces(joins = loadJoins()) {
|
|
|
46046
45860
|
detached.push(cellId);
|
|
46047
45861
|
}
|
|
46048
45862
|
const attached = [];
|
|
46049
|
-
for (const [cellId,
|
|
46050
|
-
if (attachOtiumWorkspace(
|
|
45863
|
+
for (const [cellId, join45] of wanted) {
|
|
45864
|
+
if (attachOtiumWorkspace(join45))
|
|
46051
45865
|
attached.push(cellId);
|
|
46052
45866
|
}
|
|
46053
45867
|
if (attached.length > 0 || detached.length > 0) {
|
|
@@ -46070,9 +45884,9 @@ async function handleOtiumAdapterControlRequest(req) {
|
|
|
46070
45884
|
if (req.method === "GET") {
|
|
46071
45885
|
return Response.json({
|
|
46072
45886
|
ok: true,
|
|
46073
|
-
workspaces: mountedOtiumWorkspaces().map((
|
|
46074
|
-
cellId:
|
|
46075
|
-
central:
|
|
45887
|
+
workspaces: mountedOtiumWorkspaces().map((join45) => ({
|
|
45888
|
+
cellId: join45.cellId,
|
|
45889
|
+
central: join45.central
|
|
46076
45890
|
}))
|
|
46077
45891
|
});
|
|
46078
45892
|
}
|
|
@@ -46093,8 +45907,8 @@ function mountConfiguredOtiumNodeRuntime() {
|
|
|
46093
45907
|
const joins = loadJoins();
|
|
46094
45908
|
if (joins.length === 0)
|
|
46095
45909
|
return null;
|
|
46096
|
-
for (const
|
|
46097
|
-
attachOtiumWorkspace(
|
|
45910
|
+
for (const join45 of joins)
|
|
45911
|
+
attachOtiumWorkspace(join45);
|
|
46098
45912
|
registerNodeRequestHandler("otium-adapter-control", handleOtiumAdapterControlRequest);
|
|
46099
45913
|
let stopped = false;
|
|
46100
45914
|
return {
|
|
@@ -46206,11 +46020,11 @@ async function joinCommand(args) {
|
|
|
46206
46020
|
process.exitCode = 1;
|
|
46207
46021
|
return;
|
|
46208
46022
|
}
|
|
46209
|
-
let
|
|
46023
|
+
let join45;
|
|
46210
46024
|
let productionEnrollment = false;
|
|
46211
46025
|
if (args.includes("--legacy")) {
|
|
46212
46026
|
try {
|
|
46213
|
-
|
|
46027
|
+
join45 = parseInviteCode(code);
|
|
46214
46028
|
} catch (err2) {
|
|
46215
46029
|
console.error(`invalid legacy invite code: ${err2 instanceof Error ? err2.message : err2}`);
|
|
46216
46030
|
process.exitCode = 1;
|
|
@@ -46236,7 +46050,7 @@ async function joinCommand(args) {
|
|
|
46236
46050
|
return;
|
|
46237
46051
|
}
|
|
46238
46052
|
}
|
|
46239
|
-
|
|
46053
|
+
join45 = await claimEnrollment(invite, nodeName);
|
|
46240
46054
|
productionEnrollment = true;
|
|
46241
46055
|
} catch (err2) {
|
|
46242
46056
|
console.error(`enrollment failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
@@ -46247,16 +46061,16 @@ async function joinCommand(args) {
|
|
|
46247
46061
|
let path;
|
|
46248
46062
|
try {
|
|
46249
46063
|
const saveOptions = { replaceExisting: args.includes("--replace") };
|
|
46250
|
-
path = productionEnrollment ? commitEnrollment(
|
|
46064
|
+
path = productionEnrollment ? commitEnrollment(join45, saveOptions) : saveJoin(join45, saveOptions);
|
|
46251
46065
|
} catch (err2) {
|
|
46252
46066
|
console.error(`could not save join credentials: ${err2 instanceof Error ? err2.message : err2}`);
|
|
46253
46067
|
process.exitCode = 1;
|
|
46254
46068
|
return;
|
|
46255
46069
|
}
|
|
46256
46070
|
console.log(`otium join credentials saved to ${path}`);
|
|
46257
|
-
console.log(` central: ${
|
|
46258
|
-
console.log(` cellId: ${
|
|
46259
|
-
configureOtiumCentral(
|
|
46071
|
+
console.log(` central: ${join45.central}`);
|
|
46072
|
+
console.log(` cellId: ${join45.cellId}`);
|
|
46073
|
+
configureOtiumCentral(join45);
|
|
46260
46074
|
try {
|
|
46261
46075
|
const self = await selfPeerNode();
|
|
46262
46076
|
if (self) {
|
|
@@ -46284,6 +46098,50 @@ var init_join_cli = __esm(async () => {
|
|
|
46284
46098
|
await init_join();
|
|
46285
46099
|
});
|
|
46286
46100
|
|
|
46101
|
+
// ../../adapters/otium/src/status-cli.ts
|
|
46102
|
+
var exports_status_cli = {};
|
|
46103
|
+
__export(exports_status_cli, {
|
|
46104
|
+
statusCommand: () => statusCommand
|
|
46105
|
+
});
|
|
46106
|
+
async function statusCommand() {
|
|
46107
|
+
try {
|
|
46108
|
+
const joins = loadJoins();
|
|
46109
|
+
if (joins.length === 0) {
|
|
46110
|
+
console.log("not joined to any Otium workspace");
|
|
46111
|
+
process.exitCode = 1;
|
|
46112
|
+
return;
|
|
46113
|
+
}
|
|
46114
|
+
configureOtiumCentral(joins[0] ?? null);
|
|
46115
|
+
for (const join45 of joins.slice(1))
|
|
46116
|
+
attachOtiumCentralCell(join45);
|
|
46117
|
+
for (const [index, join45] of joins.entries()) {
|
|
46118
|
+
if (index > 0)
|
|
46119
|
+
console.log("");
|
|
46120
|
+
console.log(`cellId: ${join45.cellId}`);
|
|
46121
|
+
console.log(`central: ${join45.central}`);
|
|
46122
|
+
if (join45.relay)
|
|
46123
|
+
console.log(`relay: ${join45.relay}`);
|
|
46124
|
+
try {
|
|
46125
|
+
const self = await selfPeerNodeForCell(join45.cellId);
|
|
46126
|
+
if (self) {
|
|
46127
|
+
console.log(`node: ${self.nodeName ?? join45.cellId}${self.isPrimary ? " (primary)" : ""}`);
|
|
46128
|
+
console.log(`baseUrl: ${self.baseUrl}`);
|
|
46129
|
+
} else {
|
|
46130
|
+
console.warn(" warning: central answered but this cell has no visible assignment yet \u2014 check the workspace assignment");
|
|
46131
|
+
}
|
|
46132
|
+
} catch (err2) {
|
|
46133
|
+
console.warn(` warning: could not verify against central (${err2 instanceof Error ? err2.message : err2}) \u2014 showing locally known info only`);
|
|
46134
|
+
}
|
|
46135
|
+
}
|
|
46136
|
+
} finally {
|
|
46137
|
+
configureOtiumCentral(null);
|
|
46138
|
+
}
|
|
46139
|
+
}
|
|
46140
|
+
var init_status_cli = __esm(async () => {
|
|
46141
|
+
await init_central();
|
|
46142
|
+
await init_join();
|
|
46143
|
+
});
|
|
46144
|
+
|
|
46287
46145
|
// ../../adapters/otium/src/sidecar.ts
|
|
46288
46146
|
var exports_sidecar = {};
|
|
46289
46147
|
__export(exports_sidecar, {
|
|
@@ -46302,13 +46160,13 @@ function resolveTunnelTargets(joins, relayOverride) {
|
|
|
46302
46160
|
const targets = [];
|
|
46303
46161
|
const skippedNoRelay = [];
|
|
46304
46162
|
const envRelay = process.env.OTIUM_RELAY_URL?.trim();
|
|
46305
|
-
for (const
|
|
46306
|
-
const relayUrl = relayOverride?.trim() ||
|
|
46163
|
+
for (const join45 of joins) {
|
|
46164
|
+
const relayUrl = relayOverride?.trim() || join45.relay || envRelay;
|
|
46307
46165
|
if (!relayUrl) {
|
|
46308
|
-
skippedNoRelay.push(
|
|
46166
|
+
skippedNoRelay.push(join45.cellId);
|
|
46309
46167
|
continue;
|
|
46310
46168
|
}
|
|
46311
|
-
targets.push({ cellId:
|
|
46169
|
+
targets.push({ cellId: join45.cellId, relayUrl, secret: join45.secret });
|
|
46312
46170
|
}
|
|
46313
46171
|
return { targets, skippedNoRelay };
|
|
46314
46172
|
}
|
|
@@ -46395,8 +46253,8 @@ async function runOtiumSidecar(options) {
|
|
|
46395
46253
|
for (const tunnel of tunnels.values())
|
|
46396
46254
|
tunnel.start();
|
|
46397
46255
|
let resolveCompleted;
|
|
46398
|
-
const completed = new Promise((
|
|
46399
|
-
resolveCompleted =
|
|
46256
|
+
const completed = new Promise((resolve28) => {
|
|
46257
|
+
resolveCompleted = resolve28;
|
|
46400
46258
|
});
|
|
46401
46259
|
onShutdown("otium-sidecar-server", 130, () => server?.stop(true));
|
|
46402
46260
|
onShutdown("otium-sidecar-tunnel", 120, () => {
|
|
@@ -46524,6 +46382,14 @@ async function runOtiumCli(args = process.argv.slice(2)) {
|
|
|
46524
46382
|
await joinCommand2(commandArgs);
|
|
46525
46383
|
break;
|
|
46526
46384
|
}
|
|
46385
|
+
case "status": {
|
|
46386
|
+
if (commandArgs.length > 0) {
|
|
46387
|
+
throw new Error("usage: negotium otium status");
|
|
46388
|
+
}
|
|
46389
|
+
const { statusCommand: statusCommand2 } = await init_status_cli().then(() => exports_status_cli);
|
|
46390
|
+
await statusCommand2();
|
|
46391
|
+
break;
|
|
46392
|
+
}
|
|
46527
46393
|
case "leave": {
|
|
46528
46394
|
const targetCellId = commandArgs[0]?.trim();
|
|
46529
46395
|
if (commandArgs.length > 1 || targetCellId?.startsWith("-")) {
|
|
@@ -46537,9 +46403,9 @@ async function runOtiumCli(args = process.argv.slice(2)) {
|
|
|
46537
46403
|
if (joins.length === 0)
|
|
46538
46404
|
throw new Error("not joined to an Otium workspace");
|
|
46539
46405
|
if (!targetCellId && joins.length > 1) {
|
|
46540
|
-
throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((
|
|
46406
|
+
throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join45) => join45.cellId).join(", ")}`);
|
|
46541
46407
|
}
|
|
46542
|
-
if (targetCellId && !joins.some((
|
|
46408
|
+
if (targetCellId && !joins.some((join45) => join45.cellId === targetCellId)) {
|
|
46543
46409
|
throw new Error(`not joined as ${targetCellId}`);
|
|
46544
46410
|
}
|
|
46545
46411
|
removeJoin2(targetCellId);
|
|
@@ -46562,10 +46428,11 @@ async function runOtiumCli(args = process.argv.slice(2)) {
|
|
|
46562
46428
|
console.log([
|
|
46563
46429
|
"negotium otium \u2014 attach a Negotium node to an Otium workspace",
|
|
46564
46430
|
"",
|
|
46565
|
-
"usage: negotium otium <join|leave|serve> [args]",
|
|
46431
|
+
"usage: negotium otium <join|leave|status|serve> [args]",
|
|
46566
46432
|
"",
|
|
46567
46433
|
" join <code> store credentials from an Otium invite code",
|
|
46568
46434
|
" leave remove the stored workspace credentials",
|
|
46435
|
+
" status show which workspace(s)/central this node is joined to",
|
|
46569
46436
|
" serve [--port <port>] [--relay <url>]",
|
|
46570
46437
|
" run peer routes and an outbound relay tunnel",
|
|
46571
46438
|
"",
|
|
@@ -47126,7 +46993,7 @@ var CLI_COMMANDS = [
|
|
|
47126
46993
|
},
|
|
47127
46994
|
{
|
|
47128
46995
|
name: "otium",
|
|
47129
|
-
usage: "otium join|leave|serve",
|
|
46996
|
+
usage: "otium join|leave|status|serve",
|
|
47130
46997
|
description: "manage the Otium workspace connection",
|
|
47131
46998
|
group: "Channels"
|
|
47132
46999
|
}
|
|
@@ -47191,7 +47058,7 @@ async function runCanonicalNode(port) {
|
|
|
47191
47058
|
});
|
|
47192
47059
|
console.log(`negotium node listening on 127.0.0.1:${node.port} (ctrl-c to stop)`);
|
|
47193
47060
|
await node.completed;
|
|
47194
|
-
await new Promise((
|
|
47061
|
+
await new Promise((resolve28) => setImmediate(resolve28));
|
|
47195
47062
|
process.exit(0);
|
|
47196
47063
|
}
|
|
47197
47064
|
async function stopAdapter(name) {
|
|
@@ -47325,4 +47192,4 @@ switch (command) {
|
|
|
47325
47192
|
}
|
|
47326
47193
|
}
|
|
47327
47194
|
|
|
47328
|
-
//# debugId=
|
|
47195
|
+
//# debugId=8E2AD65C6830FCEF64756E2164756E21
|