@massa-ai/tools-api 1.52.0 → 1.53.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/dist/index.js +962 -109
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -7836,17 +7836,11 @@ function detectRoute(platform, host) {
|
|
|
7836
7836
|
if (route === "file")
|
|
7837
7837
|
return { kind: "proceed" };
|
|
7838
7838
|
if (route === "marketplace") {
|
|
7839
|
-
if (host === "claude")
|
|
7839
|
+
if (host === "claude" || host === "codex")
|
|
7840
7840
|
return { kind: "proceed" };
|
|
7841
|
-
if (host === "codex") {
|
|
7842
|
-
return {
|
|
7843
|
-
kind: "refuse",
|
|
7844
|
-
reason: "codex marketplace-route installs are refused (in-place bundle rewrite would dirty a checkout " + "and break the drift gate) \u2014 use the dev path: MASSA_AI_MODEL_PROFILE + regenerate."
|
|
7845
|
-
};
|
|
7846
|
-
}
|
|
7847
7841
|
return {
|
|
7848
7842
|
kind: "refuse",
|
|
7849
|
-
reason: "
|
|
7843
|
+
reason: "marketplace-route installs need an explicit, supported host before switching " + "(claude and codex proceed; no other host resolves a live write target yet) \u2014 " + "use the dev path: MASSA_AI_MODEL_PROFILE + regenerate."
|
|
7850
7844
|
};
|
|
7851
7845
|
}
|
|
7852
7846
|
return {
|
|
@@ -8045,6 +8039,61 @@ var init_lock = __esm(() => {
|
|
|
8045
8039
|
import fs7 from "fs";
|
|
8046
8040
|
import os5 from "os";
|
|
8047
8041
|
import path11 from "path";
|
|
8042
|
+
function splitPluginKey(pluginKey) {
|
|
8043
|
+
const at = pluginKey.indexOf("@");
|
|
8044
|
+
if (at === -1)
|
|
8045
|
+
return null;
|
|
8046
|
+
const pluginName = pluginKey.slice(0, at);
|
|
8047
|
+
const marketplaceName = pluginKey.slice(at + 1);
|
|
8048
|
+
if (!pluginName || !marketplaceName)
|
|
8049
|
+
return null;
|
|
8050
|
+
return { pluginName, marketplaceName };
|
|
8051
|
+
}
|
|
8052
|
+
function isContainedPath(root, candidate) {
|
|
8053
|
+
const rel = path11.relative(root, candidate);
|
|
8054
|
+
return rel === "" || !rel.startsWith("..") && !path11.isAbsolute(rel);
|
|
8055
|
+
}
|
|
8056
|
+
function resolveDirectorySourceRoot(targetHome, pluginKey) {
|
|
8057
|
+
const split = splitPluginKey(pluginKey);
|
|
8058
|
+
if (!split)
|
|
8059
|
+
return;
|
|
8060
|
+
const { pluginName, marketplaceName } = split;
|
|
8061
|
+
const knownMarketplacesPath = path11.join(targetHome, ".claude", "plugins", "known_marketplaces.json");
|
|
8062
|
+
let known;
|
|
8063
|
+
try {
|
|
8064
|
+
known = JSON.parse(fs7.readFileSync(knownMarketplacesPath, "utf8"));
|
|
8065
|
+
} catch {
|
|
8066
|
+
return;
|
|
8067
|
+
}
|
|
8068
|
+
const entry = known?.[marketplaceName];
|
|
8069
|
+
if (!entry || entry.source?.source !== "directory")
|
|
8070
|
+
return;
|
|
8071
|
+
const installLocation = entry.installLocation;
|
|
8072
|
+
if (!installLocation)
|
|
8073
|
+
return null;
|
|
8074
|
+
const manifestPath = path11.join(installLocation, ".claude-plugin", "marketplace.json");
|
|
8075
|
+
let manifest;
|
|
8076
|
+
try {
|
|
8077
|
+
manifest = JSON.parse(fs7.readFileSync(manifestPath, "utf8"));
|
|
8078
|
+
} catch {
|
|
8079
|
+
return null;
|
|
8080
|
+
}
|
|
8081
|
+
const plugin2 = manifest.plugins?.find((p) => p?.name === pluginName);
|
|
8082
|
+
const relSource = plugin2?.source;
|
|
8083
|
+
if (!relSource)
|
|
8084
|
+
return null;
|
|
8085
|
+
const resolvedInstallLocation = path11.resolve(installLocation);
|
|
8086
|
+
const composed = path11.resolve(resolvedInstallLocation, relSource);
|
|
8087
|
+
if (!isContainedPath(resolvedInstallLocation, composed))
|
|
8088
|
+
return null;
|
|
8089
|
+
try {
|
|
8090
|
+
if (!fs7.existsSync(composed))
|
|
8091
|
+
return null;
|
|
8092
|
+
} catch {
|
|
8093
|
+
return null;
|
|
8094
|
+
}
|
|
8095
|
+
return composed;
|
|
8096
|
+
}
|
|
8048
8097
|
function selectRecord(records) {
|
|
8049
8098
|
if (records.length === 0)
|
|
8050
8099
|
return;
|
|
@@ -8064,6 +8113,9 @@ function selectRecord(records) {
|
|
|
8064
8113
|
function resolveClaudeMarketplaceRoot(opts = {}) {
|
|
8065
8114
|
const targetHome = opts.targetHome ?? os5.homedir();
|
|
8066
8115
|
const pluginKey = opts.pluginKey ?? DEFAULT_PLUGIN_KEY;
|
|
8116
|
+
const directoryResult = resolveDirectorySourceRoot(targetHome, pluginKey);
|
|
8117
|
+
if (directoryResult !== undefined)
|
|
8118
|
+
return directoryResult;
|
|
8067
8119
|
const registryPath = path11.join(targetHome, ".claude", "plugins", "installed_plugins.json");
|
|
8068
8120
|
let records;
|
|
8069
8121
|
try {
|
|
@@ -8095,6 +8147,7 @@ import fs8 from "fs";
|
|
|
8095
8147
|
import path12 from "path";
|
|
8096
8148
|
import os6 from "os";
|
|
8097
8149
|
import crypto5 from "crypto";
|
|
8150
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
8098
8151
|
function namedError3(name, message) {
|
|
8099
8152
|
const err = new SwitchEngineError(message);
|
|
8100
8153
|
err.name = name;
|
|
@@ -8173,6 +8226,46 @@ function matchesGlob(filename, glob) {
|
|
|
8173
8226
|
const suffix = glob.slice(starIdx + 1);
|
|
8174
8227
|
return filename.length >= prefix.length + suffix.length && filename.startsWith(prefix) && filename.endsWith(suffix);
|
|
8175
8228
|
}
|
|
8229
|
+
function matchingFileNames(dir, glob) {
|
|
8230
|
+
return fs8.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && matchesGlob(e.name, glob)).map((e) => e.name);
|
|
8231
|
+
}
|
|
8232
|
+
function detectGitAvailability(dir) {
|
|
8233
|
+
try {
|
|
8234
|
+
const out = execFileSync2("git", ["-C", dir, "rev-parse", "--is-inside-work-tree"], {
|
|
8235
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
8236
|
+
});
|
|
8237
|
+
return out.toString().trim() === "true" ? "in-repo" : "not-a-repo";
|
|
8238
|
+
} catch (err) {
|
|
8239
|
+
return err.code === "ENOENT" ? "no-git" : "not-a-repo";
|
|
8240
|
+
}
|
|
8241
|
+
}
|
|
8242
|
+
function gitTrackedFileNames(dir, filenames) {
|
|
8243
|
+
if (filenames.length === 0)
|
|
8244
|
+
return new Set;
|
|
8245
|
+
try {
|
|
8246
|
+
const out = execFileSync2("git", ["-C", dir, "ls-files", "--", ...filenames], {
|
|
8247
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
8248
|
+
});
|
|
8249
|
+
return new Set(out.toString().split(`
|
|
8250
|
+
`).map((line) => line.trim()).filter(Boolean));
|
|
8251
|
+
} catch {
|
|
8252
|
+
return new Set;
|
|
8253
|
+
}
|
|
8254
|
+
}
|
|
8255
|
+
function checkTrackedPathGuard(activeDir, filenames) {
|
|
8256
|
+
if (filenames.length === 0 || !fs8.existsSync(activeDir))
|
|
8257
|
+
return GUARD_PASS;
|
|
8258
|
+
const availability = detectGitAvailability(activeDir);
|
|
8259
|
+
if (availability === "no-git")
|
|
8260
|
+
return GUARD_UNCHECKED;
|
|
8261
|
+
if (availability === "not-a-repo")
|
|
8262
|
+
return GUARD_PASS;
|
|
8263
|
+
const tracked = gitTrackedFileNames(activeDir, filenames);
|
|
8264
|
+
if (tracked.size === 0)
|
|
8265
|
+
return GUARD_PASS;
|
|
8266
|
+
const offending = filenames.find((name) => tracked.has(name));
|
|
8267
|
+
return { blocked: true, path: path12.join(activeDir, offending), unchecked: false };
|
|
8268
|
+
}
|
|
8176
8269
|
function assertStateWritable(stateFilePath) {
|
|
8177
8270
|
const dir = path12.dirname(stateFilePath);
|
|
8178
8271
|
try {
|
|
@@ -8302,12 +8395,27 @@ function switchProfile(opts) {
|
|
|
8302
8395
|
rows.push({ host: h.host, status: "switched" });
|
|
8303
8396
|
continue;
|
|
8304
8397
|
}
|
|
8398
|
+
const candidateNames = matchingFileNames(h.variantDir, h.layout.activeGlob);
|
|
8399
|
+
const guard2 = checkTrackedPathGuard(h.layout.activeDir, candidateNames);
|
|
8400
|
+
if (guard2.blocked) {
|
|
8401
|
+
rows.push({
|
|
8402
|
+
host: h.host,
|
|
8403
|
+
status: "failed",
|
|
8404
|
+
reason: `refusing to write ${guard2.path} \u2014 it is tracked by git and this switch would dirty the checkout`
|
|
8405
|
+
});
|
|
8406
|
+
continue;
|
|
8407
|
+
}
|
|
8305
8408
|
try {
|
|
8306
8409
|
const filesChanged = copyVariant(h.host, h.layout, h.variantDir);
|
|
8307
8410
|
updatePlatform(stateFilePath, h.host, {
|
|
8308
8411
|
modelProfile: { profile: opts.profile, switchedAt: new Date().toISOString() }
|
|
8309
8412
|
});
|
|
8310
|
-
rows.push({
|
|
8413
|
+
rows.push({
|
|
8414
|
+
host: h.host,
|
|
8415
|
+
status: "switched",
|
|
8416
|
+
filesChanged,
|
|
8417
|
+
...guard2.unchecked ? { reason: "tracked-path guard could not verify (git unavailable) \u2014 proceeded unchecked" } : {}
|
|
8418
|
+
});
|
|
8311
8419
|
} catch (err) {
|
|
8312
8420
|
rows.push({ host: h.host, status: "failed", reason: err.message });
|
|
8313
8421
|
}
|
|
@@ -8323,7 +8431,7 @@ function orderRows(universe, rows) {
|
|
|
8323
8431
|
const byHost = new Map(rows.map((r2) => [r2.host, r2]));
|
|
8324
8432
|
return HOSTS.filter((h) => universe.includes(h) && byHost.has(h)).map((h) => byHost.get(h));
|
|
8325
8433
|
}
|
|
8326
|
-
var SwitchEngineError, UnknownProfileError = (profile, known) => namedError3("UnknownProfileError", `unknown profile "${profile}" \u2014 installed: ${known.length > 0 ? known.join(", ") : "none"}`), NoHostsDetectedError = () => namedError3("NoHostsDetectedError", "no installed hosts found");
|
|
8434
|
+
var SwitchEngineError, UnknownProfileError = (profile, known) => namedError3("UnknownProfileError", `unknown profile "${profile}" \u2014 installed: ${known.length > 0 ? known.join(", ") : "none"}`), NoHostsDetectedError = () => namedError3("NoHostsDetectedError", "no installed hosts found"), GUARD_PASS, GUARD_UNCHECKED;
|
|
8327
8435
|
var init_engine = __esm(() => {
|
|
8328
8436
|
init_hosts();
|
|
8329
8437
|
init_state();
|
|
@@ -8335,6 +8443,8 @@ var init_engine = __esm(() => {
|
|
|
8335
8443
|
this.name = "SwitchEngineError";
|
|
8336
8444
|
}
|
|
8337
8445
|
};
|
|
8446
|
+
GUARD_PASS = { blocked: false, unchecked: false };
|
|
8447
|
+
GUARD_UNCHECKED = { blocked: false, unchecked: true };
|
|
8338
8448
|
});
|
|
8339
8449
|
|
|
8340
8450
|
// ../../packages/shared/dist/profile-switch/report.js
|
|
@@ -128577,7 +128687,7 @@ var init_git_ref_validation = __esm(() => {
|
|
|
128577
128687
|
});
|
|
128578
128688
|
|
|
128579
128689
|
// ../../packages/core/dist/services/symbol/impact-analysis.js
|
|
128580
|
-
import { execFileSync as
|
|
128690
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
128581
128691
|
function readBfsCteFlag() {
|
|
128582
128692
|
try {
|
|
128583
128693
|
return Boolean(config.get("impact")?.bfsCteEnabled);
|
|
@@ -128903,19 +129013,19 @@ function defaultDiffRunner(projectPath2, scope, baseBranch, since) {
|
|
|
128903
129013
|
let diffRange;
|
|
128904
129014
|
if (since) {
|
|
128905
129015
|
try {
|
|
128906
|
-
ref =
|
|
129016
|
+
ref = execFileSync3("git", ["-C", projectPath2, "rev-parse", "--verify", `${since}^{commit}`], {
|
|
128907
129017
|
cwd: projectPath2,
|
|
128908
129018
|
encoding: "utf-8",
|
|
128909
129019
|
stdio: ["ignore", "pipe", "pipe"]
|
|
128910
129020
|
}).trim();
|
|
128911
129021
|
} catch {
|
|
128912
|
-
ref =
|
|
129022
|
+
ref = execFileSync3("git", ["-C", projectPath2, "rev-list", "-1", `--before=${since}`, "HEAD"], {
|
|
128913
129023
|
cwd: projectPath2,
|
|
128914
129024
|
encoding: "utf-8",
|
|
128915
129025
|
stdio: ["ignore", "pipe", "pipe"]
|
|
128916
129026
|
}).trim();
|
|
128917
129027
|
if (!ref) {
|
|
128918
|
-
const emptyTree =
|
|
129028
|
+
const emptyTree = execFileSync3("git", ["hash-object", "-t", "tree", "--stdin"], {
|
|
128919
129029
|
cwd: projectPath2,
|
|
128920
129030
|
encoding: "utf-8",
|
|
128921
129031
|
input: "",
|
|
@@ -128927,7 +129037,7 @@ function defaultDiffRunner(projectPath2, scope, baseBranch, since) {
|
|
|
128927
129037
|
}
|
|
128928
129038
|
args.push(diffRange ?? `${ref}...HEAD`);
|
|
128929
129039
|
}
|
|
128930
|
-
const out =
|
|
129040
|
+
const out = execFileSync3("git", args, {
|
|
128931
129041
|
cwd: projectPath2,
|
|
128932
129042
|
encoding: "utf-8",
|
|
128933
129043
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -128939,7 +129049,7 @@ function defaultDiffRunner(projectPath2, scope, baseBranch, since) {
|
|
|
128939
129049
|
let untrackedFiltered = 0;
|
|
128940
129050
|
const merged = new Set(tracked);
|
|
128941
129051
|
if (includeUntracked) {
|
|
128942
|
-
const untracked =
|
|
129052
|
+
const untracked = execFileSync3("git", ["-C", projectPath2, "ls-files", "--others", "--exclude-standard"], {
|
|
128943
129053
|
cwd: projectPath2,
|
|
128944
129054
|
encoding: "utf-8",
|
|
128945
129055
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -128977,7 +129087,7 @@ var init_impact_analysis = __esm(() => {
|
|
|
128977
129087
|
});
|
|
128978
129088
|
|
|
128979
129089
|
// ../../packages/core/dist/services/executor/runtime.js
|
|
128980
|
-
import { execFileSync as
|
|
129090
|
+
import { execFileSync as execFileSync4, execSync } from "child_process";
|
|
128981
129091
|
function commandExists(cmd) {
|
|
128982
129092
|
try {
|
|
128983
129093
|
const check3 = isWindows ? `where ${cmd}` : `command -v ${cmd}`;
|
|
@@ -129010,7 +129120,7 @@ function getVersion(cmd, args = ["--version"], deps) {
|
|
|
129010
129120
|
timeout: 5000
|
|
129011
129121
|
}).trim().split(/\r?\n/)[0];
|
|
129012
129122
|
}
|
|
129013
|
-
return
|
|
129123
|
+
return execFileSync4(cmd, args, {
|
|
129014
129124
|
encoding: "utf-8",
|
|
129015
129125
|
stdio: ["pipe", "pipe", "pipe"],
|
|
129016
129126
|
timeout: 5000
|
|
@@ -129151,12 +129261,12 @@ var init_runtime = __esm(() => {
|
|
|
129151
129261
|
|
|
129152
129262
|
// ../../packages/core/dist/services/executor/sandbox.js
|
|
129153
129263
|
import { realpathSync as realpathSync2 } from "fs";
|
|
129154
|
-
import { execFileSync as
|
|
129264
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
129155
129265
|
function isDockerAvailable() {
|
|
129156
129266
|
if (_dockerAvailable !== null)
|
|
129157
129267
|
return _dockerAvailable;
|
|
129158
129268
|
try {
|
|
129159
|
-
|
|
129269
|
+
execFileSync5("docker", ["--version"], { stdio: "pipe", timeout: 5000 });
|
|
129160
129270
|
_dockerAvailable = true;
|
|
129161
129271
|
} catch {
|
|
129162
129272
|
_dockerAvailable = false;
|
|
@@ -129167,7 +129277,7 @@ function isSeatbeltAvailable() {
|
|
|
129167
129277
|
if (_seatbeltAvailable !== null)
|
|
129168
129278
|
return _seatbeltAvailable;
|
|
129169
129279
|
try {
|
|
129170
|
-
|
|
129280
|
+
execFileSync5("sandbox-exec", ["--version"], { stdio: "pipe", timeout: 5000 });
|
|
129171
129281
|
_seatbeltAvailable = true;
|
|
129172
129282
|
} catch {
|
|
129173
129283
|
_seatbeltAvailable = false;
|
|
@@ -129272,7 +129382,7 @@ var init_sandbox = __esm(() => {
|
|
|
129272
129382
|
});
|
|
129273
129383
|
|
|
129274
129384
|
// ../../packages/core/dist/services/executor/executor.js
|
|
129275
|
-
import { spawn, execSync as execSync2, execFileSync as
|
|
129385
|
+
import { spawn, execSync as execSync2, execFileSync as execFileSync6 } from "child_process";
|
|
129276
129386
|
import { mkdtempSync, writeFileSync as writeFileSync2, rmSync, realpathSync as realpathSync3 } from "fs";
|
|
129277
129387
|
import { join as join3, resolve as resolve5, isAbsolute, relative as relative2 } from "path";
|
|
129278
129388
|
import { tmpdir } from "os";
|
|
@@ -129475,7 +129585,7 @@ ${body}`;
|
|
|
129475
129585
|
const binPath = srcPath.replace(/\.rs$/, isWin ? ".exe" : "");
|
|
129476
129586
|
try {
|
|
129477
129587
|
try {
|
|
129478
|
-
|
|
129588
|
+
execFileSync6("rustc", [srcPath, "-o", binPath], {
|
|
129479
129589
|
cwd,
|
|
129480
129590
|
timeout: Math.min(timeout, 60000),
|
|
129481
129591
|
encoding: "utf-8",
|
|
@@ -132417,23 +132527,94 @@ var init_scheduler = __esm(() => {
|
|
|
132417
132527
|
};
|
|
132418
132528
|
});
|
|
132419
132529
|
|
|
132530
|
+
// ../../packages/core/dist/data/proposal/proposal-payload-validation.js
|
|
132531
|
+
function isRecord(value) {
|
|
132532
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
132533
|
+
}
|
|
132534
|
+
function isStringArray(value) {
|
|
132535
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
132536
|
+
}
|
|
132537
|
+
function isString(value) {
|
|
132538
|
+
return typeof value === "string";
|
|
132539
|
+
}
|
|
132540
|
+
function isNumber(value) {
|
|
132541
|
+
return typeof value === "number";
|
|
132542
|
+
}
|
|
132543
|
+
function isValidProposalPayload(kind, value) {
|
|
132544
|
+
const rule = PROPOSAL_PAYLOAD_RULES[kind];
|
|
132545
|
+
if (!Object.keys(value).every((key) => rule.allowedKeys.includes(key)))
|
|
132546
|
+
return false;
|
|
132547
|
+
if (rule.requireNonEmpty && Object.keys(value).length === 0)
|
|
132548
|
+
return false;
|
|
132549
|
+
for (const [field3, fieldRule] of Object.entries(rule.fields)) {
|
|
132550
|
+
const present = value[field3] !== undefined;
|
|
132551
|
+
if (!present) {
|
|
132552
|
+
if (!fieldRule.optional)
|
|
132553
|
+
return false;
|
|
132554
|
+
continue;
|
|
132555
|
+
}
|
|
132556
|
+
if (!fieldRule.validate(value[field3]))
|
|
132557
|
+
return false;
|
|
132558
|
+
}
|
|
132559
|
+
return true;
|
|
132560
|
+
}
|
|
132561
|
+
function assertValidProposalPayload(kind, value) {
|
|
132562
|
+
if (!isRecord(value)) {
|
|
132563
|
+
throw new ProposalPayloadValidationError(kind, `proposal payload must be an object for kind "${kind}"`);
|
|
132564
|
+
}
|
|
132565
|
+
if (!isValidProposalPayload(kind, value)) {
|
|
132566
|
+
throw new ProposalPayloadValidationError(kind, `proposal payload is invalid for kind "${kind}"`);
|
|
132567
|
+
}
|
|
132568
|
+
}
|
|
132569
|
+
var PROPOSAL_PAYLOAD_RULES, ProposalPayloadValidationError;
|
|
132570
|
+
var init_proposal_payload_validation = __esm(() => {
|
|
132571
|
+
PROPOSAL_PAYLOAD_RULES = {
|
|
132572
|
+
"memory.create": {
|
|
132573
|
+
allowedKeys: ["content", "type", "level", "importance", "tags"],
|
|
132574
|
+
fields: {
|
|
132575
|
+
content: { optional: false, validate: isString },
|
|
132576
|
+
type: { optional: true, validate: isString },
|
|
132577
|
+
level: { optional: true, validate: isNumber },
|
|
132578
|
+
importance: { optional: true, validate: isNumber },
|
|
132579
|
+
tags: { optional: true, validate: isStringArray }
|
|
132580
|
+
}
|
|
132581
|
+
},
|
|
132582
|
+
"memory.update": {
|
|
132583
|
+
allowedKeys: ["content", "importance", "tags"],
|
|
132584
|
+
requireNonEmpty: true,
|
|
132585
|
+
fields: {
|
|
132586
|
+
content: { optional: true, validate: isString },
|
|
132587
|
+
importance: { optional: true, validate: isNumber },
|
|
132588
|
+
tags: { optional: true, validate: isStringArray }
|
|
132589
|
+
}
|
|
132590
|
+
},
|
|
132591
|
+
"memory.tag": {
|
|
132592
|
+
allowedKeys: ["tags"],
|
|
132593
|
+
fields: {
|
|
132594
|
+
tags: { optional: false, validate: isStringArray }
|
|
132595
|
+
}
|
|
132596
|
+
}
|
|
132597
|
+
};
|
|
132598
|
+
ProposalPayloadValidationError = class ProposalPayloadValidationError extends Error {
|
|
132599
|
+
kind;
|
|
132600
|
+
statusCode = 400;
|
|
132601
|
+
constructor(kind, message) {
|
|
132602
|
+
super(message);
|
|
132603
|
+
this.kind = kind;
|
|
132604
|
+
this.name = "ProposalPayloadValidationError";
|
|
132605
|
+
}
|
|
132606
|
+
};
|
|
132607
|
+
});
|
|
132608
|
+
|
|
132420
132609
|
// ../../packages/core/dist/data/proposal/proposal-contract.js
|
|
132421
132610
|
var PROPOSAL_STATUSES, PROPOSAL_KINDS;
|
|
132422
132611
|
var init_proposal_contract = __esm(() => {
|
|
132612
|
+
init_proposal_payload_validation();
|
|
132423
132613
|
PROPOSAL_STATUSES = ["pending", "approved", "rejected"];
|
|
132424
132614
|
PROPOSAL_KINDS = ["memory.create", "memory.update", "memory.tag"];
|
|
132425
132615
|
});
|
|
132426
132616
|
|
|
132427
132617
|
// ../../packages/core/dist/data/proposal/proposal-repository-pg.js
|
|
132428
|
-
function isRecord(value) {
|
|
132429
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
132430
|
-
}
|
|
132431
|
-
function stringArray(value) {
|
|
132432
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
132433
|
-
}
|
|
132434
|
-
function validKeys(value, allowed) {
|
|
132435
|
-
return Object.keys(value).every((key) => allowed.includes(key));
|
|
132436
|
-
}
|
|
132437
132618
|
function parsePayload(raw2, kind) {
|
|
132438
132619
|
let value;
|
|
132439
132620
|
try {
|
|
@@ -132444,15 +132625,7 @@ function parsePayload(raw2, kind) {
|
|
|
132444
132625
|
if (!isRecord(value)) {
|
|
132445
132626
|
throw storeCorruption("proposal.payload_json", new TypeError("expected object"));
|
|
132446
132627
|
}
|
|
132447
|
-
|
|
132448
|
-
if (kind === "memory.create") {
|
|
132449
|
-
valid = validKeys(value, ["content", "type", "level", "importance", "tags"]) && typeof value.content === "string" && (value.type === undefined || typeof value.type === "string") && (value.level === undefined || typeof value.level === "number") && (value.importance === undefined || typeof value.importance === "number") && (value.tags === undefined || stringArray(value.tags));
|
|
132450
|
-
} else if (kind === "memory.update") {
|
|
132451
|
-
valid = validKeys(value, ["content", "importance", "tags"]) && Object.keys(value).length > 0 && (value.content === undefined || typeof value.content === "string") && (value.importance === undefined || typeof value.importance === "number") && (value.tags === undefined || stringArray(value.tags));
|
|
132452
|
-
} else {
|
|
132453
|
-
valid = validKeys(value, ["tags"]) && stringArray(value.tags);
|
|
132454
|
-
}
|
|
132455
|
-
if (!valid) {
|
|
132628
|
+
if (!isValidProposalPayload(kind, value)) {
|
|
132456
132629
|
throw storeCorruption("proposal.payload_json", new TypeError("invalid proposal payload"));
|
|
132457
132630
|
}
|
|
132458
132631
|
return value;
|
|
@@ -132586,6 +132759,65 @@ class PgProposalStore {
|
|
|
132586
132759
|
this.mirror.set(id, persisted);
|
|
132587
132760
|
return structuredClone(persisted);
|
|
132588
132761
|
}
|
|
132762
|
+
async create(input) {
|
|
132763
|
+
assertValidProposalPayload(input.kind, input.payload);
|
|
132764
|
+
const record2 = {
|
|
132765
|
+
id: input.id,
|
|
132766
|
+
projectId: input.projectId,
|
|
132767
|
+
kind: input.kind,
|
|
132768
|
+
targetMemoryId: input.targetMemoryId ?? null,
|
|
132769
|
+
payload: input.payload,
|
|
132770
|
+
rationale: input.rationale ?? "",
|
|
132771
|
+
status: "pending",
|
|
132772
|
+
createdAt: input.createdAt ?? Date.now(),
|
|
132773
|
+
decidedAt: null
|
|
132774
|
+
};
|
|
132775
|
+
await this.insert(record2);
|
|
132776
|
+
return record2;
|
|
132777
|
+
}
|
|
132778
|
+
async update(id, patch) {
|
|
132779
|
+
await this.ensureHydrated();
|
|
132780
|
+
const current = this.mirror.get(id);
|
|
132781
|
+
if (!current)
|
|
132782
|
+
return null;
|
|
132783
|
+
if (patch.payload !== undefined) {
|
|
132784
|
+
assertValidProposalPayload(current.kind, patch.payload);
|
|
132785
|
+
}
|
|
132786
|
+
const merged = {
|
|
132787
|
+
...current,
|
|
132788
|
+
...patch.rationale !== undefined ? { rationale: patch.rationale } : {},
|
|
132789
|
+
...patch.payload !== undefined ? { payload: patch.payload } : {}
|
|
132790
|
+
};
|
|
132791
|
+
let rows;
|
|
132792
|
+
try {
|
|
132793
|
+
rows = await this.getClient().$queryRaw`
|
|
132794
|
+
UPDATE proposals
|
|
132795
|
+
SET rationale = ${merged.rationale}, payload_json = ${JSON.stringify(merged.payload)}
|
|
132796
|
+
WHERE id = ${id}
|
|
132797
|
+
RETURNING id, project_id, kind, target_memory_id, payload_json,
|
|
132798
|
+
rationale, status, created_at, decided_at`;
|
|
132799
|
+
} catch (error51) {
|
|
132800
|
+
throw searchBackendUnavailable("proposal_store", error51);
|
|
132801
|
+
}
|
|
132802
|
+
if (!rows[0])
|
|
132803
|
+
return null;
|
|
132804
|
+
const persisted = toRecord(rows[0]);
|
|
132805
|
+
this.mirror.set(id, persisted);
|
|
132806
|
+
return structuredClone(persisted);
|
|
132807
|
+
}
|
|
132808
|
+
async delete(id) {
|
|
132809
|
+
await this.ensureHydrated();
|
|
132810
|
+
let affected;
|
|
132811
|
+
try {
|
|
132812
|
+
affected = await this.getClient().$executeRaw`DELETE FROM proposals WHERE id = ${id}`;
|
|
132813
|
+
} catch (error51) {
|
|
132814
|
+
throw searchBackendUnavailable("proposal_store", error51);
|
|
132815
|
+
}
|
|
132816
|
+
if (affected === 0)
|
|
132817
|
+
return null;
|
|
132818
|
+
this.mirror.delete(id);
|
|
132819
|
+
return id;
|
|
132820
|
+
}
|
|
132589
132821
|
async journalMode() {
|
|
132590
132822
|
await this.ensureHydrated();
|
|
132591
132823
|
return "postgres";
|
|
@@ -132602,6 +132834,7 @@ var init_proposal_repository_pg = __esm(() => {
|
|
|
132602
132834
|
init_alias_resolver();
|
|
132603
132835
|
init_search_diagnostics();
|
|
132604
132836
|
init_proposal_contract();
|
|
132837
|
+
init_proposal_payload_validation();
|
|
132605
132838
|
});
|
|
132606
132839
|
|
|
132607
132840
|
// ../../packages/core/dist/data/proposal/proposal-repository.js
|
|
@@ -179181,6 +179414,53 @@ class PgHandoffStore {
|
|
|
179181
179414
|
this.mirror.set(id, persisted);
|
|
179182
179415
|
return structuredClone(persisted);
|
|
179183
179416
|
}
|
|
179417
|
+
async update(id, patch) {
|
|
179418
|
+
await this.ensureHydrated();
|
|
179419
|
+
const current = this.mirror.get(id);
|
|
179420
|
+
if (!current)
|
|
179421
|
+
return null;
|
|
179422
|
+
const merged = {
|
|
179423
|
+
...current,
|
|
179424
|
+
...patch.targetAgent !== undefined ? { targetAgent: patch.targetAgent } : {},
|
|
179425
|
+
...patch.summary !== undefined ? { summary: patch.summary } : {},
|
|
179426
|
+
...patch.openQuestions !== undefined ? { openQuestions: patch.openQuestions } : {},
|
|
179427
|
+
...patch.nextSteps !== undefined ? { nextSteps: patch.nextSteps } : {},
|
|
179428
|
+
...patch.files !== undefined ? { files: patch.files } : {}
|
|
179429
|
+
};
|
|
179430
|
+
let rows;
|
|
179431
|
+
try {
|
|
179432
|
+
rows = await this.getClient().$queryRaw`
|
|
179433
|
+
UPDATE handoffs
|
|
179434
|
+
SET target_agent = ${merged.targetAgent}, summary = ${merged.summary},
|
|
179435
|
+
open_questions_json = ${JSON.stringify(merged.openQuestions ?? [])},
|
|
179436
|
+
next_steps_json = ${JSON.stringify(merged.nextSteps ?? [])},
|
|
179437
|
+
files_json = ${JSON.stringify(merged.files ?? [])}
|
|
179438
|
+
WHERE id = ${id}
|
|
179439
|
+
RETURNING id, project_id, source_session_id, target_agent, summary,
|
|
179440
|
+
open_questions_json, next_steps_json, files_json, status,
|
|
179441
|
+
created_at, accepted_at`;
|
|
179442
|
+
} catch (error51) {
|
|
179443
|
+
throw searchBackendUnavailable("handoff_store", error51);
|
|
179444
|
+
}
|
|
179445
|
+
if (!rows[0])
|
|
179446
|
+
return null;
|
|
179447
|
+
const persisted = toRecord2(rows[0]);
|
|
179448
|
+
this.mirror.set(id, persisted);
|
|
179449
|
+
return structuredClone(persisted);
|
|
179450
|
+
}
|
|
179451
|
+
async delete(id) {
|
|
179452
|
+
await this.ensureHydrated();
|
|
179453
|
+
let affected;
|
|
179454
|
+
try {
|
|
179455
|
+
affected = await this.getClient().$executeRaw`DELETE FROM handoffs WHERE id = ${id}`;
|
|
179456
|
+
} catch (error51) {
|
|
179457
|
+
throw searchBackendUnavailable("handoff_store", error51);
|
|
179458
|
+
}
|
|
179459
|
+
if (affected === 0)
|
|
179460
|
+
return null;
|
|
179461
|
+
this.mirror.delete(id);
|
|
179462
|
+
return id;
|
|
179463
|
+
}
|
|
179184
179464
|
async journalMode() {
|
|
179185
179465
|
await this.ensureHydrated();
|
|
179186
179466
|
return "postgres";
|
|
@@ -179205,7 +179485,6 @@ function newHandoffId() {
|
|
|
179205
179485
|
// ../../packages/core/dist/services/handoff/handoff-service.js
|
|
179206
179486
|
init_zod();
|
|
179207
179487
|
init_dist();
|
|
179208
|
-
import { randomUUID as randomUUID10 } from "crypto";
|
|
179209
179488
|
init_memory_repository_factory();
|
|
179210
179489
|
init_event_bus();
|
|
179211
179490
|
init_llm_client();
|
|
@@ -179226,7 +179505,8 @@ class HandoffService {
|
|
|
179226
179505
|
this.idFactory = deps.idFactory ?? (() => newHandoffId());
|
|
179227
179506
|
const injectedRepo = deps.memoryRepo;
|
|
179228
179507
|
this.memoryRepo = injectedRepo ?? {
|
|
179229
|
-
insert: (i) => getMemoryRepository().insert(i)
|
|
179508
|
+
insert: (i) => getMemoryRepository().insert(i),
|
|
179509
|
+
update: (id, p) => getMemoryRepository().update(id, p)
|
|
179230
179510
|
};
|
|
179231
179511
|
}
|
|
179232
179512
|
async begin(input) {
|
|
@@ -179268,7 +179548,7 @@ class HandoffService {
|
|
|
179268
179548
|
return { ok: true, id, status: "open", memoryId };
|
|
179269
179549
|
}
|
|
179270
179550
|
async dualWrite(record2) {
|
|
179271
|
-
const memId =
|
|
179551
|
+
const memId = dualWriteMemoryId(record2.id);
|
|
179272
179552
|
const input = buildHandoffMemoryInput(memId, record2);
|
|
179273
179553
|
await Promise.resolve(this.memoryRepo.insert(input));
|
|
179274
179554
|
return memId;
|
|
@@ -179312,6 +179592,50 @@ class HandoffService {
|
|
|
179312
179592
|
async listPending(projectId, targetAgent) {
|
|
179313
179593
|
return this.store.listPending(projectId, targetAgent ?? undefined);
|
|
179314
179594
|
}
|
|
179595
|
+
async update(params) {
|
|
179596
|
+
if (!params || !params.id) {
|
|
179597
|
+
return { ok: false, reason: "missing-id" };
|
|
179598
|
+
}
|
|
179599
|
+
const row = await this.store.getById(params.id);
|
|
179600
|
+
if (!row)
|
|
179601
|
+
return { ok: false, reason: "not-found" };
|
|
179602
|
+
if (params.projectId && row.projectId !== params.projectId) {
|
|
179603
|
+
return { ok: false, reason: "project-mismatch" };
|
|
179604
|
+
}
|
|
179605
|
+
const updated = await this.store.update(params.id, params.patch);
|
|
179606
|
+
if (!updated)
|
|
179607
|
+
return { ok: false, reason: "not-found" };
|
|
179608
|
+
const touchesMemoryContent = params.patch.summary !== undefined || params.patch.openQuestions !== undefined || params.patch.nextSteps !== undefined || params.patch.files !== undefined;
|
|
179609
|
+
if (touchesMemoryContent) {
|
|
179610
|
+
await this.refreshDualWriteMemory(updated);
|
|
179611
|
+
}
|
|
179612
|
+
return { ok: true, handoff: updated };
|
|
179613
|
+
}
|
|
179614
|
+
async refreshDualWriteMemory(record2) {
|
|
179615
|
+
try {
|
|
179616
|
+
const memId = dualWriteMemoryId(record2.id);
|
|
179617
|
+
const content = formatMemoryContent(record2);
|
|
179618
|
+
await Promise.resolve(this.memoryRepo.update(memId, { content }));
|
|
179619
|
+
} catch {}
|
|
179620
|
+
}
|
|
179621
|
+
async delete(params) {
|
|
179622
|
+
if (!params || !params.id) {
|
|
179623
|
+
return { ok: false, reason: "missing-id" };
|
|
179624
|
+
}
|
|
179625
|
+
const row = await this.store.getById(params.id);
|
|
179626
|
+
if (!row)
|
|
179627
|
+
return { ok: false, reason: "not-found" };
|
|
179628
|
+
if (params.projectId && row.projectId !== params.projectId) {
|
|
179629
|
+
return { ok: false, reason: "project-mismatch" };
|
|
179630
|
+
}
|
|
179631
|
+
const deletedId = await this.store.delete(params.id);
|
|
179632
|
+
if (!deletedId)
|
|
179633
|
+
return { ok: false, reason: "not-found" };
|
|
179634
|
+
return { ok: true, id: deletedId };
|
|
179635
|
+
}
|
|
179636
|
+
}
|
|
179637
|
+
function dualWriteMemoryId(handoffId) {
|
|
179638
|
+
return `handoff-mem-${handoffId}`;
|
|
179315
179639
|
}
|
|
179316
179640
|
function buildHandoffMemoryInput(memId, record2) {
|
|
179317
179641
|
const content = formatMemoryContent(record2);
|
|
@@ -179406,6 +179730,7 @@ init_event_bus();
|
|
|
179406
179730
|
|
|
179407
179731
|
// ../../packages/core/dist/index.js
|
|
179408
179732
|
init_proposal_repository();
|
|
179733
|
+
init_proposal_payload_validation();
|
|
179409
179734
|
init_auto_improve_job();
|
|
179410
179735
|
|
|
179411
179736
|
// src/routes/search.ts
|
|
@@ -180400,12 +180725,35 @@ var systemRoutes = new Elysia({ prefix: "/api/v1/system" }).get("/info", async (
|
|
|
180400
180725
|
}
|
|
180401
180726
|
});
|
|
180402
180727
|
|
|
180728
|
+
// src/routes/sse-keepalive.ts
|
|
180729
|
+
var SSE_HEARTBEAT_MS_DEFAULT = 5000;
|
|
180730
|
+
var SSE_MAX_DURATION_MS_DEFAULT = 10 * 60 * 1000;
|
|
180731
|
+
var SSE_REQUEST_TIMEOUT_SECONDS = 120;
|
|
180732
|
+
function resolveHeartbeatMs() {
|
|
180733
|
+
return Number(process.env.MASSA_AI_SSE_HEARTBEAT_MS) || SSE_HEARTBEAT_MS_DEFAULT;
|
|
180734
|
+
}
|
|
180735
|
+
function resolveMaxDurationMs() {
|
|
180736
|
+
return Number(process.env.MASSA_AI_SSE_MAX_DURATION_MS) || SSE_MAX_DURATION_MS_DEFAULT;
|
|
180737
|
+
}
|
|
180738
|
+
var requestTimeoutSource;
|
|
180739
|
+
var applyCount = 0;
|
|
180740
|
+
function setSseRequestTimeoutSource(source) {
|
|
180741
|
+
requestTimeoutSource = source;
|
|
180742
|
+
}
|
|
180743
|
+
function applySseRequestTimeout(request) {
|
|
180744
|
+
const source = requestTimeoutSource;
|
|
180745
|
+
if (!source || typeof source.timeout !== "function")
|
|
180746
|
+
return;
|
|
180747
|
+
try {
|
|
180748
|
+
source.timeout(request, SSE_REQUEST_TIMEOUT_SECONDS);
|
|
180749
|
+
applyCount++;
|
|
180750
|
+
} catch {}
|
|
180751
|
+
}
|
|
180752
|
+
|
|
180403
180753
|
// src/routes/events.ts
|
|
180404
|
-
var
|
|
180405
|
-
|
|
180406
|
-
|
|
180407
|
-
const HEARTBEAT_MS = Number(process.env.MASSA_AI_SSE_HEARTBEAT_MS) || HEARTBEAT_MS_DEFAULT;
|
|
180408
|
-
const MAX_DURATION_MS = Number(process.env.MASSA_AI_SSE_MAX_DURATION_MS) || MAX_DURATION_MS_DEFAULT;
|
|
180754
|
+
var eventsRoutes = new Elysia({ prefix: "/api/v1" }).get("/events", async ({ query, set: set3, request }) => {
|
|
180755
|
+
const HEARTBEAT_MS = resolveHeartbeatMs();
|
|
180756
|
+
const MAX_DURATION_MS = resolveMaxDurationMs();
|
|
180409
180757
|
const projectIdFilter = query.projectId;
|
|
180410
180758
|
const jobIdFilter = query.jobId;
|
|
180411
180759
|
set3.headers["Content-Type"] = "text/event-stream";
|
|
@@ -180419,6 +180767,18 @@ var eventsRoutes = new Elysia({ prefix: "/api/v1" }).get("/events", async ({ que
|
|
|
180419
180767
|
let closeTimer;
|
|
180420
180768
|
const stream2 = new ReadableStream({
|
|
180421
180769
|
start(controller) {
|
|
180770
|
+
applySseRequestTimeout(request);
|
|
180771
|
+
const teardown = () => {
|
|
180772
|
+
closed = true;
|
|
180773
|
+
unsubscribers.forEach((u) => u());
|
|
180774
|
+
if (heartbeatTimer)
|
|
180775
|
+
clearInterval(heartbeatTimer);
|
|
180776
|
+
if (closeTimer)
|
|
180777
|
+
clearTimeout(closeTimer);
|
|
180778
|
+
try {
|
|
180779
|
+
controller.close();
|
|
180780
|
+
} catch {}
|
|
180781
|
+
};
|
|
180422
180782
|
const enqueue = (data) => {
|
|
180423
180783
|
if (closed)
|
|
180424
180784
|
return;
|
|
@@ -180427,7 +180787,7 @@ var eventsRoutes = new Elysia({ prefix: "/api/v1" }).get("/events", async ({ que
|
|
|
180427
180787
|
|
|
180428
180788
|
`));
|
|
180429
180789
|
} catch {
|
|
180430
|
-
|
|
180790
|
+
teardown();
|
|
180431
180791
|
}
|
|
180432
180792
|
};
|
|
180433
180793
|
const events = [
|
|
@@ -180455,18 +180815,10 @@ var eventsRoutes = new Elysia({ prefix: "/api/v1" }).get("/events", async ({ que
|
|
|
180455
180815
|
|
|
180456
180816
|
`));
|
|
180457
180817
|
} catch {
|
|
180458
|
-
|
|
180459
|
-
clearInterval(heartbeatTimer);
|
|
180818
|
+
teardown();
|
|
180460
180819
|
}
|
|
180461
180820
|
}, HEARTBEAT_MS);
|
|
180462
|
-
closeTimer = setTimeout(
|
|
180463
|
-
closed = true;
|
|
180464
|
-
unsubscribers.forEach((u) => u());
|
|
180465
|
-
clearInterval(heartbeatTimer);
|
|
180466
|
-
try {
|
|
180467
|
-
controller.close();
|
|
180468
|
-
} catch {}
|
|
180469
|
-
}, MAX_DURATION_MS);
|
|
180821
|
+
closeTimer = setTimeout(teardown, MAX_DURATION_MS);
|
|
180470
180822
|
enqueue({
|
|
180471
180823
|
event: "connected",
|
|
180472
180824
|
payload: {
|
|
@@ -181568,6 +181920,16 @@ function rethrowCanonicalHandoffError(error51) {
|
|
|
181568
181920
|
var EVENT_DETAIL3 = {
|
|
181569
181921
|
tags: ["handoffs"]
|
|
181570
181922
|
};
|
|
181923
|
+
var HANDOFF_PATCH_ALLOWED_FIELDS = [
|
|
181924
|
+
"targetAgent",
|
|
181925
|
+
"summary",
|
|
181926
|
+
"openQuestions",
|
|
181927
|
+
"nextSteps",
|
|
181928
|
+
"files"
|
|
181929
|
+
];
|
|
181930
|
+
function isStringArray2(value) {
|
|
181931
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
181932
|
+
}
|
|
181571
181933
|
var handoffRoutes = new Elysia({ prefix: "/api/v1/handoff" }).post("/begin", async ({ body, set: set3 }) => {
|
|
181572
181934
|
if (handoffsDisabled()) {
|
|
181573
181935
|
set3.status = 423;
|
|
@@ -181708,6 +182070,121 @@ var handoffRoutes = new Elysia({ prefix: "/api/v1/handoff" }).post("/begin", asy
|
|
|
181708
182070
|
summary: "List pending (open) handoffs",
|
|
181709
182071
|
description: "Lists open handoffs for a project, optionally filtered by target agent, ordered oldest-first."
|
|
181710
182072
|
}
|
|
182073
|
+
}).patch("/:id", async ({ params, query, body, set: set3 }) => {
|
|
182074
|
+
if (handoffsDisabled()) {
|
|
182075
|
+
set3.status = 423;
|
|
182076
|
+
return { status: 423, error: "handoffs disabled" };
|
|
182077
|
+
}
|
|
182078
|
+
const raw2 = body ?? {};
|
|
182079
|
+
const keys = Object.keys(raw2);
|
|
182080
|
+
const rejected = keys.filter((k2) => !HANDOFF_PATCH_ALLOWED_FIELDS.includes(k2));
|
|
182081
|
+
if (rejected.length > 0) {
|
|
182082
|
+
set3.status = 400;
|
|
182083
|
+
return { status: 400, error: `field not allowed: ${rejected.join(", ")}` };
|
|
182084
|
+
}
|
|
182085
|
+
const patch = {};
|
|
182086
|
+
if ("targetAgent" in raw2) {
|
|
182087
|
+
const v = raw2.targetAgent;
|
|
182088
|
+
if (v !== null && typeof v !== "string") {
|
|
182089
|
+
set3.status = 400;
|
|
182090
|
+
return { status: 400, error: "targetAgent must be a string or null" };
|
|
182091
|
+
}
|
|
182092
|
+
patch.targetAgent = v;
|
|
182093
|
+
}
|
|
182094
|
+
if ("summary" in raw2) {
|
|
182095
|
+
if (typeof raw2.summary !== "string") {
|
|
182096
|
+
set3.status = 400;
|
|
182097
|
+
return { status: 400, error: "summary must be a string" };
|
|
182098
|
+
}
|
|
182099
|
+
patch.summary = raw2.summary;
|
|
182100
|
+
}
|
|
182101
|
+
if ("openQuestions" in raw2) {
|
|
182102
|
+
if (!isStringArray2(raw2.openQuestions)) {
|
|
182103
|
+
set3.status = 400;
|
|
182104
|
+
return { status: 400, error: "openQuestions must be an array of strings" };
|
|
182105
|
+
}
|
|
182106
|
+
patch.openQuestions = raw2.openQuestions;
|
|
182107
|
+
}
|
|
182108
|
+
if ("nextSteps" in raw2) {
|
|
182109
|
+
if (!isStringArray2(raw2.nextSteps)) {
|
|
182110
|
+
set3.status = 400;
|
|
182111
|
+
return { status: 400, error: "nextSteps must be an array of strings" };
|
|
182112
|
+
}
|
|
182113
|
+
patch.nextSteps = raw2.nextSteps;
|
|
182114
|
+
}
|
|
182115
|
+
if ("files" in raw2) {
|
|
182116
|
+
if (!isStringArray2(raw2.files)) {
|
|
182117
|
+
set3.status = 400;
|
|
182118
|
+
return { status: 400, error: "files must be an array of strings" };
|
|
182119
|
+
}
|
|
182120
|
+
patch.files = raw2.files;
|
|
182121
|
+
}
|
|
182122
|
+
if (Object.keys(patch).length === 0) {
|
|
182123
|
+
set3.status = 400;
|
|
182124
|
+
return { status: 400, error: "no editable field provided" };
|
|
182125
|
+
}
|
|
182126
|
+
try {
|
|
182127
|
+
const result = await service3().update({
|
|
182128
|
+
id: params.id,
|
|
182129
|
+
projectId: query.projectId,
|
|
182130
|
+
patch
|
|
182131
|
+
});
|
|
182132
|
+
if (!result.ok) {
|
|
182133
|
+
set3.status = result.reason === "not-found" || result.reason === "project-mismatch" ? 404 : 400;
|
|
182134
|
+
return { status: set3.status, error: result.reason };
|
|
182135
|
+
}
|
|
182136
|
+
set3.status = 200;
|
|
182137
|
+
return { success: true, data: result.handoff };
|
|
182138
|
+
} catch (e) {
|
|
182139
|
+
rethrowCanonicalHandoffError(e);
|
|
182140
|
+
const err = e;
|
|
182141
|
+
logger.error("handoff update failed", err);
|
|
182142
|
+
set3.status = 500;
|
|
182143
|
+
return { success: false, error: `handoff update failed: ${err.message}` };
|
|
182144
|
+
}
|
|
182145
|
+
}, {
|
|
182146
|
+
params: t.Object({ id: t.String({ description: "Handoff id" }) }),
|
|
182147
|
+
query: t.Object({
|
|
182148
|
+
projectId: t.Optional(t.String({ description: "If supplied, must match the row's projectId or the request 404s" }))
|
|
182149
|
+
}),
|
|
182150
|
+
body: t.Record(t.String(), t.Unknown(), {
|
|
182151
|
+
description: "Only targetAgent/summary/openQuestions/nextSteps/files are accepted; any other key is a 400 naming it."
|
|
182152
|
+
}),
|
|
182153
|
+
detail: {
|
|
182154
|
+
...EVENT_DETAIL3,
|
|
182155
|
+
summary: "Edit a handoff (targetAgent/summary/openQuestions/nextSteps/files only)",
|
|
182156
|
+
description: "Allowlist PATCH: status/acceptedAt/id/projectId/createdAt/sourceSessionId and any unknown key are rejected " + "by name with 400. Refreshes the dual-written memory's content when a content-feeding field changes."
|
|
182157
|
+
}
|
|
182158
|
+
}).delete("/:id", async ({ params, query, set: set3 }) => {
|
|
182159
|
+
if (handoffsDisabled()) {
|
|
182160
|
+
set3.status = 423;
|
|
182161
|
+
return { status: 423, error: "handoffs disabled" };
|
|
182162
|
+
}
|
|
182163
|
+
try {
|
|
182164
|
+
const result = await service3().delete({ id: params.id, projectId: query.projectId });
|
|
182165
|
+
if (!result.ok) {
|
|
182166
|
+
set3.status = 404;
|
|
182167
|
+
return { status: 404, error: result.reason };
|
|
182168
|
+
}
|
|
182169
|
+
set3.status = 200;
|
|
182170
|
+
return { success: true, data: { id: result.id } };
|
|
182171
|
+
} catch (e) {
|
|
182172
|
+
rethrowCanonicalHandoffError(e);
|
|
182173
|
+
const err = e;
|
|
182174
|
+
logger.error("handoff delete failed", err);
|
|
182175
|
+
set3.status = 500;
|
|
182176
|
+
return { success: false, error: `handoff delete failed: ${err.message}` };
|
|
182177
|
+
}
|
|
182178
|
+
}, {
|
|
182179
|
+
params: t.Object({ id: t.String({ description: "Handoff id" }) }),
|
|
182180
|
+
query: t.Object({
|
|
182181
|
+
projectId: t.Optional(t.String({ description: "If supplied, must match the row's projectId or the request 404s" }))
|
|
182182
|
+
}),
|
|
182183
|
+
detail: {
|
|
182184
|
+
...EVENT_DETAIL3,
|
|
182185
|
+
summary: "Hard-delete a handoff",
|
|
182186
|
+
description: "Permitted in any status, including open. The dual-written memory row is left intact (dangling metadata.handoffId is accepted)."
|
|
182187
|
+
}
|
|
181711
182188
|
});
|
|
181712
182189
|
|
|
181713
182190
|
// src/routes/proposals.ts
|
|
@@ -181718,6 +182195,12 @@ function job() {
|
|
|
181718
182195
|
cachedJob2 = getAutoImproveJob();
|
|
181719
182196
|
return cachedJob2;
|
|
181720
182197
|
}
|
|
182198
|
+
var cachedStore4 = null;
|
|
182199
|
+
function store6() {
|
|
182200
|
+
if (!cachedStore4)
|
|
182201
|
+
cachedStore4 = getProposalStore();
|
|
182202
|
+
return cachedStore4;
|
|
182203
|
+
}
|
|
181721
182204
|
function autoImproveDisabled() {
|
|
181722
182205
|
try {
|
|
181723
182206
|
return config.get("memory")?.autoImprove?.enabled === false;
|
|
@@ -181728,6 +182211,16 @@ function autoImproveDisabled() {
|
|
|
181728
182211
|
var EVENT_DETAIL4 = {
|
|
181729
182212
|
tags: ["proposals"]
|
|
181730
182213
|
};
|
|
182214
|
+
var PROPOSAL_PATCH_ALLOWED_FIELDS = ["rationale", "payload"];
|
|
182215
|
+
function isRecord2(value) {
|
|
182216
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
182217
|
+
}
|
|
182218
|
+
function isProposalKind(value) {
|
|
182219
|
+
return typeof value === "string" && PROPOSAL_KINDS.includes(value);
|
|
182220
|
+
}
|
|
182221
|
+
function proposalPayloadValidationStatus(error51) {
|
|
182222
|
+
return error51 instanceof ProposalPayloadValidationError ? error51.statusCode : null;
|
|
182223
|
+
}
|
|
181731
182224
|
var proposalRoutes = new Elysia({ prefix: "/api/v1/proposal" }).post("/list", async ({ body, set: set3 }) => {
|
|
181732
182225
|
if (autoImproveDisabled()) {
|
|
181733
182226
|
set3.status = 423;
|
|
@@ -181828,6 +182321,191 @@ var proposalRoutes = new Elysia({ prefix: "/api/v1/proposal" }).post("/list", as
|
|
|
181828
182321
|
summary: "Reject a pending proposal",
|
|
181829
182322
|
description: "Flips status pending\u2192rejected (no apply, no event). Same failure semantics as approve on missing/non-pending/project-mismatch."
|
|
181830
182323
|
}
|
|
182324
|
+
}).post("/create", async ({ body, set: set3 }) => {
|
|
182325
|
+
if (autoImproveDisabled()) {
|
|
182326
|
+
set3.status = 423;
|
|
182327
|
+
return { status: 423, error: "auto-improve disabled" };
|
|
182328
|
+
}
|
|
182329
|
+
const raw2 = body ?? {};
|
|
182330
|
+
if (typeof raw2.projectId !== "string" || !raw2.projectId.trim()) {
|
|
182331
|
+
set3.status = 400;
|
|
182332
|
+
return { status: 400, error: "projectId required" };
|
|
182333
|
+
}
|
|
182334
|
+
if (!isProposalKind(raw2.kind)) {
|
|
182335
|
+
set3.status = 400;
|
|
182336
|
+
return { status: 400, error: `kind must be one of ${PROPOSAL_KINDS.join(", ")}` };
|
|
182337
|
+
}
|
|
182338
|
+
if (!isRecord2(raw2.payload)) {
|
|
182339
|
+
set3.status = 400;
|
|
182340
|
+
return { status: 400, error: "payload must be an object" };
|
|
182341
|
+
}
|
|
182342
|
+
if (raw2.rationale !== undefined && typeof raw2.rationale !== "string") {
|
|
182343
|
+
set3.status = 400;
|
|
182344
|
+
return { status: 400, error: "rationale must be a string" };
|
|
182345
|
+
}
|
|
182346
|
+
if (raw2.targetMemoryId !== undefined && raw2.targetMemoryId !== null && typeof raw2.targetMemoryId !== "string") {
|
|
182347
|
+
set3.status = 400;
|
|
182348
|
+
return { status: 400, error: "targetMemoryId must be a string or null" };
|
|
182349
|
+
}
|
|
182350
|
+
const projectId = raw2.projectId;
|
|
182351
|
+
const kind = raw2.kind;
|
|
182352
|
+
const payload = raw2.payload;
|
|
182353
|
+
try {
|
|
182354
|
+
const record2 = await store6().create({
|
|
182355
|
+
id: newProposalId(),
|
|
182356
|
+
projectId,
|
|
182357
|
+
kind,
|
|
182358
|
+
payload,
|
|
182359
|
+
rationale: raw2.rationale,
|
|
182360
|
+
targetMemoryId: raw2.targetMemoryId
|
|
182361
|
+
});
|
|
182362
|
+
set3.status = 200;
|
|
182363
|
+
return { success: true, data: record2 };
|
|
182364
|
+
} catch (e) {
|
|
182365
|
+
if (e instanceof SearchServiceError)
|
|
182366
|
+
throw e;
|
|
182367
|
+
const validationStatus = proposalPayloadValidationStatus(e);
|
|
182368
|
+
if (validationStatus !== null) {
|
|
182369
|
+
set3.status = validationStatus;
|
|
182370
|
+
return { status: validationStatus, error: e.message };
|
|
182371
|
+
}
|
|
182372
|
+
const err = e;
|
|
182373
|
+
logger.error("proposal create failed", err);
|
|
182374
|
+
set3.status = 500;
|
|
182375
|
+
return { success: false, error: `proposal create failed: ${err.message}` };
|
|
182376
|
+
}
|
|
182377
|
+
}, {
|
|
182378
|
+
body: t.Record(t.String(), t.Unknown(), {
|
|
182379
|
+
description: "projectId, kind (memory.create|memory.update|memory.tag), payload, rationale?, targetMemoryId?"
|
|
182380
|
+
}),
|
|
182381
|
+
detail: {
|
|
182382
|
+
...EVENT_DETAIL4,
|
|
182383
|
+
summary: "Create a pending proposal",
|
|
182384
|
+
description: "kind must be memory.create/memory.update/memory.tag; payload is validated against the same per-kind " + "rules the store enforces on read (AC-02.2), so a write the reader would reject is refused here instead."
|
|
182385
|
+
}
|
|
182386
|
+
}).patch("/:id", async ({ params, query, body, set: set3 }) => {
|
|
182387
|
+
if (autoImproveDisabled()) {
|
|
182388
|
+
set3.status = 423;
|
|
182389
|
+
return { status: 423, error: "auto-improve disabled" };
|
|
182390
|
+
}
|
|
182391
|
+
const raw2 = body ?? {};
|
|
182392
|
+
const keys = Object.keys(raw2);
|
|
182393
|
+
const rejected = keys.filter((k2) => !PROPOSAL_PATCH_ALLOWED_FIELDS.includes(k2));
|
|
182394
|
+
if (rejected.length > 0) {
|
|
182395
|
+
set3.status = 400;
|
|
182396
|
+
return { status: 400, error: `field not allowed: ${rejected.join(", ")}` };
|
|
182397
|
+
}
|
|
182398
|
+
const patch = {};
|
|
182399
|
+
if ("rationale" in raw2) {
|
|
182400
|
+
if (typeof raw2.rationale !== "string") {
|
|
182401
|
+
set3.status = 400;
|
|
182402
|
+
return { status: 400, error: "rationale must be a string" };
|
|
182403
|
+
}
|
|
182404
|
+
patch.rationale = raw2.rationale;
|
|
182405
|
+
}
|
|
182406
|
+
if ("payload" in raw2) {
|
|
182407
|
+
if (!isRecord2(raw2.payload)) {
|
|
182408
|
+
set3.status = 400;
|
|
182409
|
+
return { status: 400, error: "payload must be an object" };
|
|
182410
|
+
}
|
|
182411
|
+
patch.payload = raw2.payload;
|
|
182412
|
+
}
|
|
182413
|
+
if (Object.keys(patch).length === 0) {
|
|
182414
|
+
set3.status = 400;
|
|
182415
|
+
return { status: 400, error: "no editable field provided" };
|
|
182416
|
+
}
|
|
182417
|
+
try {
|
|
182418
|
+
const current = await store6().getById(params.id);
|
|
182419
|
+
if (!current) {
|
|
182420
|
+
set3.status = 404;
|
|
182421
|
+
return { status: 404, error: "not-found" };
|
|
182422
|
+
}
|
|
182423
|
+
if (query.projectId && current.projectId !== query.projectId) {
|
|
182424
|
+
set3.status = 404;
|
|
182425
|
+
return { status: 404, error: "project-mismatch" };
|
|
182426
|
+
}
|
|
182427
|
+
const updated = await store6().update(params.id, {
|
|
182428
|
+
...patch.rationale !== undefined ? { rationale: patch.rationale } : {},
|
|
182429
|
+
...patch.payload !== undefined ? { payload: patch.payload } : {}
|
|
182430
|
+
});
|
|
182431
|
+
if (!updated) {
|
|
182432
|
+
set3.status = 404;
|
|
182433
|
+
return { status: 404, error: "not-found" };
|
|
182434
|
+
}
|
|
182435
|
+
set3.status = 200;
|
|
182436
|
+
return { success: true, data: updated };
|
|
182437
|
+
} catch (e) {
|
|
182438
|
+
if (e instanceof SearchServiceError)
|
|
182439
|
+
throw e;
|
|
182440
|
+
const validationStatus = proposalPayloadValidationStatus(e);
|
|
182441
|
+
if (validationStatus !== null) {
|
|
182442
|
+
set3.status = validationStatus;
|
|
182443
|
+
return { status: validationStatus, error: e.message };
|
|
182444
|
+
}
|
|
182445
|
+
const err = e;
|
|
182446
|
+
logger.error("proposal update failed", err);
|
|
182447
|
+
set3.status = 500;
|
|
182448
|
+
return { success: false, error: `proposal update failed: ${err.message}` };
|
|
182449
|
+
}
|
|
182450
|
+
}, {
|
|
182451
|
+
params: t.Object({ id: t.String({ description: "Proposal id" }) }),
|
|
182452
|
+
query: t.Object({
|
|
182453
|
+
projectId: t.Optional(t.String({ description: "If supplied, must match the row's projectId or the request 404s" }))
|
|
182454
|
+
}),
|
|
182455
|
+
body: t.Record(t.String(), t.Unknown(), {
|
|
182456
|
+
description: "Only rationale/payload are accepted; any other key is a 400 naming it."
|
|
182457
|
+
}),
|
|
182458
|
+
detail: {
|
|
182459
|
+
...EVENT_DETAIL4,
|
|
182460
|
+
summary: "Edit a proposal (rationale/payload only)",
|
|
182461
|
+
description: "Allowlist PATCH: kind/targetMemoryId/status/decidedAt/id/projectId/createdAt and any unknown key are " + "rejected by name with 400. A payload edit re-validates against the row's existing kind (AC-02.3/AC-02.4)."
|
|
182462
|
+
}
|
|
182463
|
+
}).delete("/:id", async ({ params, query, set: set3 }) => {
|
|
182464
|
+
if (autoImproveDisabled()) {
|
|
182465
|
+
set3.status = 423;
|
|
182466
|
+
return { status: 423, error: "auto-improve disabled" };
|
|
182467
|
+
}
|
|
182468
|
+
try {
|
|
182469
|
+
const current = await store6().getById(params.id);
|
|
182470
|
+
if (!current) {
|
|
182471
|
+
set3.status = 404;
|
|
182472
|
+
return { status: 404, error: "not-found" };
|
|
182473
|
+
}
|
|
182474
|
+
if (query.projectId && current.projectId !== query.projectId) {
|
|
182475
|
+
set3.status = 404;
|
|
182476
|
+
return { status: 404, error: "project-mismatch" };
|
|
182477
|
+
}
|
|
182478
|
+
const deletedId = await store6().delete(params.id);
|
|
182479
|
+
if (!deletedId) {
|
|
182480
|
+
set3.status = 404;
|
|
182481
|
+
return { status: 404, error: "not-found" };
|
|
182482
|
+
}
|
|
182483
|
+
set3.status = 200;
|
|
182484
|
+
return {
|
|
182485
|
+
success: true,
|
|
182486
|
+
data: {
|
|
182487
|
+
id: deletedId,
|
|
182488
|
+
...current.status === "approved" ? { note: "approved proposal's applied memory edit was not reversed" } : {}
|
|
182489
|
+
}
|
|
182490
|
+
};
|
|
182491
|
+
} catch (e) {
|
|
182492
|
+
if (e instanceof SearchServiceError)
|
|
182493
|
+
throw e;
|
|
182494
|
+
const err = e;
|
|
182495
|
+
logger.error("proposal delete failed", err);
|
|
182496
|
+
set3.status = 500;
|
|
182497
|
+
return { success: false, error: `proposal delete failed: ${err.message}` };
|
|
182498
|
+
}
|
|
182499
|
+
}, {
|
|
182500
|
+
params: t.Object({ id: t.String({ description: "Proposal id" }) }),
|
|
182501
|
+
query: t.Object({
|
|
182502
|
+
projectId: t.Optional(t.String({ description: "If supplied, must match the row's projectId or the request 404s" }))
|
|
182503
|
+
}),
|
|
182504
|
+
detail: {
|
|
182505
|
+
...EVENT_DETAIL4,
|
|
182506
|
+
summary: "Hard-delete a proposal",
|
|
182507
|
+
description: "Permitted in any status. Deleting an already-approved proposal does not reverse the memory edit it " + "already applied (AC-02.5) \u2014 the response says so explicitly via a `note` field."
|
|
182508
|
+
}
|
|
181831
182509
|
});
|
|
181832
182510
|
|
|
181833
182511
|
// src/routes/executor.ts
|
|
@@ -182710,6 +183388,7 @@ var configRoutes = new Elysia({ prefix: "/api/v1/config" }).get("/", ({ set: set
|
|
|
182710
183388
|
// src/routes/model-registry-stream.ts
|
|
182711
183389
|
init_config();
|
|
182712
183390
|
init_dist();
|
|
183391
|
+
import fs28 from "fs";
|
|
182713
183392
|
import path42 from "path";
|
|
182714
183393
|
import { spawn as spawn3 } from "child_process";
|
|
182715
183394
|
var encoder3 = new TextEncoder;
|
|
@@ -182718,6 +183397,58 @@ function sseFrame(data) {
|
|
|
182718
183397
|
|
|
182719
183398
|
`);
|
|
182720
183399
|
}
|
|
183400
|
+
var KNOWN_GENERATOR_FILENAMES = ["generate-skill-artifacts.ts", "generate-subagent-artifacts.ts"];
|
|
183401
|
+
function deriveGeneratorScripts(root2) {
|
|
183402
|
+
const pkgPath = path42.join(root2, "package.json");
|
|
183403
|
+
let raw2;
|
|
183404
|
+
try {
|
|
183405
|
+
raw2 = fs28.readFileSync(pkgPath, "utf-8");
|
|
183406
|
+
} catch (e) {
|
|
183407
|
+
throw new Error(`cannot read ${pkgPath}: ${e.message}`);
|
|
183408
|
+
}
|
|
183409
|
+
let pkg;
|
|
183410
|
+
try {
|
|
183411
|
+
pkg = JSON.parse(raw2);
|
|
183412
|
+
} catch (e) {
|
|
183413
|
+
throw new Error(`${pkgPath} is not valid JSON: ${e.message}`);
|
|
183414
|
+
}
|
|
183415
|
+
const scriptsField = pkg?.scripts;
|
|
183416
|
+
const command = scriptsField?.["generate:artifacts"];
|
|
183417
|
+
if (typeof command !== "string" || command.trim().length === 0) {
|
|
183418
|
+
throw new Error(`${pkgPath}'s scripts."generate:artifacts" is missing or not a string`);
|
|
183419
|
+
}
|
|
183420
|
+
const segments = command.split("&&").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
183421
|
+
if (segments.length === 0) {
|
|
183422
|
+
throw new Error(`"generate:artifacts" parsed to zero commands: ${JSON.stringify(command)}`);
|
|
183423
|
+
}
|
|
183424
|
+
return segments.map((segment) => {
|
|
183425
|
+
const match2 = /^bun\s+(\S+\.ts)$/.exec(segment);
|
|
183426
|
+
if (!match2) {
|
|
183427
|
+
throw new Error(`"generate:artifacts" segment does not match the expected "bun <script.ts>" shape: ${JSON.stringify(segment)}`);
|
|
183428
|
+
}
|
|
183429
|
+
const relPath = match2[1];
|
|
183430
|
+
return { relPath, name: path42.basename(relPath) };
|
|
183431
|
+
});
|
|
183432
|
+
}
|
|
183433
|
+
function assertGeneratorBackstop(scripts) {
|
|
183434
|
+
const names = scripts.map((s) => s.name);
|
|
183435
|
+
const missing = KNOWN_GENERATOR_FILENAMES.filter((f) => !names.includes(f));
|
|
183436
|
+
if (scripts.length < 2 || missing.length > 0) {
|
|
183437
|
+
throw new Error(`generator list ${JSON.stringify(names)} does not contain the known generators ` + `${KNOWN_GENERATOR_FILENAMES.join(", ")} \u2014 refusing to spawn an implausibly short list`);
|
|
183438
|
+
}
|
|
183439
|
+
}
|
|
183440
|
+
function emitSkillsFrames(controller2, closedRef) {
|
|
183441
|
+
for (const host of HOSTS) {
|
|
183442
|
+
if (closedRef.closed)
|
|
183443
|
+
return;
|
|
183444
|
+
try {
|
|
183445
|
+
controller2.enqueue(sseFrame({ type: "skills", host, status: "generated" }));
|
|
183446
|
+
} catch {
|
|
183447
|
+
closedRef.closed = true;
|
|
183448
|
+
return;
|
|
183449
|
+
}
|
|
183450
|
+
}
|
|
183451
|
+
}
|
|
182721
183452
|
function deriveInstallStatus(report) {
|
|
182722
183453
|
if (report.hosts.some((h) => h.status === "switched"))
|
|
182723
183454
|
return "switched";
|
|
@@ -182792,17 +183523,15 @@ function createRegenerateStreamHandler() {
|
|
|
182792
183523
|
closedRef.closed = true;
|
|
182793
183524
|
return;
|
|
182794
183525
|
}
|
|
182795
|
-
|
|
183526
|
+
let generatorScripts;
|
|
182796
183527
|
try {
|
|
182797
|
-
|
|
182798
|
-
|
|
182799
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
182800
|
-
});
|
|
183528
|
+
generatorScripts = deriveGeneratorScripts(root2);
|
|
183529
|
+
assertGeneratorBackstop(generatorScripts);
|
|
182801
183530
|
} catch (e) {
|
|
182802
183531
|
controller2.enqueue(sseFrame({
|
|
182803
183532
|
type: "done",
|
|
182804
183533
|
exitCode: null,
|
|
182805
|
-
error: `
|
|
183534
|
+
error: `could not derive the generator list: ${e.message}`
|
|
182806
183535
|
}));
|
|
182807
183536
|
controller2.close();
|
|
182808
183537
|
closedRef.closed = true;
|
|
@@ -182825,35 +183554,57 @@ function createRegenerateStreamHandler() {
|
|
|
182825
183554
|
}
|
|
182826
183555
|
}
|
|
182827
183556
|
};
|
|
182828
|
-
|
|
182829
|
-
child.stderr?.on("data", (chunk) => emitLine("stderr", chunk));
|
|
182830
|
-
child.on("error", (e) => {
|
|
183557
|
+
const finish = (exitCode, error51) => {
|
|
182831
183558
|
if (closedRef.closed)
|
|
182832
183559
|
return;
|
|
182833
183560
|
closedRef.closed = true;
|
|
182834
183561
|
try {
|
|
182835
|
-
controller2.enqueue(sseFrame({ type: "done", exitCode
|
|
183562
|
+
controller2.enqueue(sseFrame(error51 !== undefined ? { type: "done", exitCode, error: error51 } : { type: "done", exitCode }));
|
|
182836
183563
|
controller2.close();
|
|
182837
183564
|
} catch {}
|
|
182838
|
-
}
|
|
182839
|
-
|
|
183565
|
+
};
|
|
183566
|
+
const runGenerator = (index) => {
|
|
182840
183567
|
if (closedRef.closed)
|
|
182841
183568
|
return;
|
|
182842
|
-
if (
|
|
183569
|
+
if (index >= generatorScripts.length) {
|
|
182843
183570
|
try {
|
|
182844
183571
|
controller2.enqueue(sseFrame({ type: "line", stream: "stdout", text: "Installing regenerated agents to active directories..." }));
|
|
182845
183572
|
} catch {
|
|
182846
183573
|
closedRef.closed = true;
|
|
182847
183574
|
return;
|
|
182848
183575
|
}
|
|
183576
|
+
emitSkillsFrames(controller2, closedRef);
|
|
182849
183577
|
installActiveProfiles(controller2, closedRef);
|
|
183578
|
+
finish(0);
|
|
183579
|
+
return;
|
|
182850
183580
|
}
|
|
182851
|
-
|
|
183581
|
+
const generator = generatorScripts[index];
|
|
183582
|
+
const scriptPath = path42.join(root2, generator.relPath);
|
|
182852
183583
|
try {
|
|
182853
|
-
|
|
182854
|
-
|
|
182855
|
-
|
|
182856
|
-
|
|
183584
|
+
child = spawn3("bun", [scriptPath], {
|
|
183585
|
+
env: { ...process.env },
|
|
183586
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
183587
|
+
});
|
|
183588
|
+
} catch (e) {
|
|
183589
|
+
finish(null, `spawn failed (${generator.name}): ${e.message}`);
|
|
183590
|
+
return;
|
|
183591
|
+
}
|
|
183592
|
+
child.stdout?.on("data", (chunk) => emitLine("stdout", chunk));
|
|
183593
|
+
child.stderr?.on("data", (chunk) => emitLine("stderr", chunk));
|
|
183594
|
+
child.on("error", (e) => {
|
|
183595
|
+
finish(null, `spawn error (${generator.name}): ${e.message}`);
|
|
183596
|
+
});
|
|
183597
|
+
child.on("close", (code) => {
|
|
183598
|
+
if (closedRef.closed)
|
|
183599
|
+
return;
|
|
183600
|
+
if (code !== 0) {
|
|
183601
|
+
finish(code, `${generator.name} failed with exit code ${code}`);
|
|
183602
|
+
return;
|
|
183603
|
+
}
|
|
183604
|
+
runGenerator(index + 1);
|
|
183605
|
+
});
|
|
183606
|
+
};
|
|
183607
|
+
runGenerator(0);
|
|
182857
183608
|
},
|
|
182858
183609
|
cancel() {
|
|
182859
183610
|
closedRef.closed = true;
|
|
@@ -182875,8 +183626,8 @@ function createRegenerateStreamHandler() {
|
|
|
182875
183626
|
var modelRegistryStreamRoutes = new Elysia({ prefix: "/api/v1/model-registry" }).post("/regenerate-and-install-stream", createRegenerateStreamHandler(), {
|
|
182876
183627
|
detail: {
|
|
182877
183628
|
tags: ["model-registry"],
|
|
182878
|
-
summary: "Regenerate subagent artifacts
|
|
182879
|
-
description: "Spawns `
|
|
183629
|
+
summary: "Regenerate skill + subagent artifacts, then auto-install to active dirs (streaming SSE)",
|
|
183630
|
+
description: "Spawns every generator named by package.json's `generate:artifacts` script (today `generate-skill-artifacts.ts` then `generate-subagent-artifacts.ts`), one at a time in that order, with child_process.spawn (non-blocking). Pipes stdout/stderr line-by-line as SSE; the first generator to exit non-zero stops the chain and names itself in the terminal `done` frame. Once every generator exits 0, emits one `skills` event per host, bridges the freshly generated agent-profiles trees into each host's installed variant root (`variant-sync` events per host), then calls switchProfile for every detected host to reinstall the active profile's agents from those synced variant dirs. Emits `install` events per host, then a terminal `done` event with the exit code."
|
|
182880
183631
|
}
|
|
182881
183632
|
}).post("/regenerate-stream", createRegenerateStreamHandler(), {
|
|
182882
183633
|
detail: {
|
|
@@ -182986,7 +183737,7 @@ var restartRoutes = new Elysia({ prefix: "/api/v1/system" }).onAfterResponse(()
|
|
|
182986
183737
|
|
|
182987
183738
|
// src/routes/logs.ts
|
|
182988
183739
|
init_dist();
|
|
182989
|
-
import
|
|
183740
|
+
import fs29 from "fs";
|
|
182990
183741
|
var LOGS_DETAIL = { tags: ["logs"] };
|
|
182991
183742
|
var MAX_SCAN_BYTES = 64 * 1024 * 1024;
|
|
182992
183743
|
var MAX_LIMIT = 1000;
|
|
@@ -183066,7 +183817,7 @@ function parseLine(line, prevTs) {
|
|
|
183066
183817
|
function realReadTail(filePath, maxBytes) {
|
|
183067
183818
|
let size;
|
|
183068
183819
|
try {
|
|
183069
|
-
size =
|
|
183820
|
+
size = fs29.statSync(filePath).size;
|
|
183070
183821
|
} catch {
|
|
183071
183822
|
return { content: "", truncated: false };
|
|
183072
183823
|
}
|
|
@@ -183074,24 +183825,24 @@ function realReadTail(filePath, maxBytes) {
|
|
|
183074
183825
|
return { content: "", truncated: false };
|
|
183075
183826
|
if (size <= maxBytes) {
|
|
183076
183827
|
try {
|
|
183077
|
-
return { content:
|
|
183828
|
+
return { content: fs29.readFileSync(filePath, "utf8"), truncated: false };
|
|
183078
183829
|
} catch {
|
|
183079
183830
|
return { content: "", truncated: false };
|
|
183080
183831
|
}
|
|
183081
183832
|
}
|
|
183082
183833
|
try {
|
|
183083
|
-
const fd =
|
|
183834
|
+
const fd = fs29.openSync(filePath, "r");
|
|
183084
183835
|
try {
|
|
183085
183836
|
const start = size - maxBytes;
|
|
183086
183837
|
const buf = Buffer.alloc(maxBytes);
|
|
183087
|
-
|
|
183838
|
+
fs29.readSync(fd, buf, 0, maxBytes, start);
|
|
183088
183839
|
let text3 = buf.toString("utf8");
|
|
183089
183840
|
const firstNewline = text3.indexOf(`
|
|
183090
183841
|
`);
|
|
183091
183842
|
text3 = firstNewline !== -1 ? text3.slice(firstNewline + 1) : "";
|
|
183092
183843
|
return { content: text3, truncated: true };
|
|
183093
183844
|
} finally {
|
|
183094
|
-
|
|
183845
|
+
fs29.closeSync(fd);
|
|
183095
183846
|
}
|
|
183096
183847
|
} catch {
|
|
183097
183848
|
return { content: "", truncated: true };
|
|
@@ -183101,7 +183852,7 @@ var realReader = {
|
|
|
183101
183852
|
listFiles(filePath, maxFiles) {
|
|
183102
183853
|
return sinkFiles(filePath, maxFiles).filter((f) => {
|
|
183103
183854
|
try {
|
|
183104
|
-
|
|
183855
|
+
fs29.accessSync(f, fs29.constants.R_OK);
|
|
183105
183856
|
return true;
|
|
183106
183857
|
} catch {
|
|
183107
183858
|
return false;
|
|
@@ -183177,8 +183928,6 @@ function renderTxtLine(entry2) {
|
|
|
183177
183928
|
const metaStr = entry2.meta ? ` ${JSON.stringify(entry2.meta)}` : "";
|
|
183178
183929
|
return `[${entry2.ts}] [${entry2.level.toUpperCase()}] ${entry2.message}${metaStr}`;
|
|
183179
183930
|
}
|
|
183180
|
-
var SSE_HEARTBEAT_MS_DEFAULT = 15000;
|
|
183181
|
-
var SSE_MAX_DURATION_MS_DEFAULT = 10 * 60 * 1000;
|
|
183182
183931
|
var SINK_POLL_MS_DEFAULT = 1000;
|
|
183183
183932
|
var SINK_POLL_MAX_BYTES = 1024 * 1024;
|
|
183184
183933
|
function startSinkTail(enqueue) {
|
|
@@ -183192,7 +183941,7 @@ function startSinkTail(enqueue) {
|
|
|
183192
183941
|
let currentFile = initial[0];
|
|
183193
183942
|
let offset;
|
|
183194
183943
|
try {
|
|
183195
|
-
offset =
|
|
183944
|
+
offset = fs29.statSync(currentFile).size;
|
|
183196
183945
|
} catch {
|
|
183197
183946
|
return;
|
|
183198
183947
|
}
|
|
@@ -183208,7 +183957,7 @@ function startSinkTail(enqueue) {
|
|
|
183208
183957
|
offset = 0;
|
|
183209
183958
|
carry = "";
|
|
183210
183959
|
}
|
|
183211
|
-
const size =
|
|
183960
|
+
const size = fs29.statSync(currentFile).size;
|
|
183212
183961
|
if (size < offset) {
|
|
183213
183962
|
offset = 0;
|
|
183214
183963
|
carry = "";
|
|
@@ -183217,11 +183966,11 @@ function startSinkTail(enqueue) {
|
|
|
183217
183966
|
return;
|
|
183218
183967
|
const length = Math.min(size - offset, SINK_POLL_MAX_BYTES);
|
|
183219
183968
|
const buf = Buffer.alloc(length);
|
|
183220
|
-
const fd =
|
|
183969
|
+
const fd = fs29.openSync(currentFile, "r");
|
|
183221
183970
|
try {
|
|
183222
|
-
|
|
183971
|
+
fs29.readSync(fd, buf, 0, length, offset);
|
|
183223
183972
|
} finally {
|
|
183224
|
-
|
|
183973
|
+
fs29.closeSync(fd);
|
|
183225
183974
|
}
|
|
183226
183975
|
offset += length;
|
|
183227
183976
|
const text3 = carry + buf.toString("utf8");
|
|
@@ -183284,9 +184033,9 @@ var logsRoutes = new Elysia({ prefix: "/api/v1/logs" }).get("/", ({ query, set:
|
|
|
183284
184033
|
summary: "Download the queried log range as jsonl or txt",
|
|
183285
184034
|
description: "Same query surface as GET /api/v1/logs (minus limit/offset \u2014 the full matching range is returned) plus format=jsonl|txt. Responds with Content-Disposition: attachment and a filename carrying the range. A range matching zero entries still returns an empty (200) download."
|
|
183286
184035
|
}
|
|
183287
|
-
}).get("/stream", () => {
|
|
183288
|
-
const HEARTBEAT_MS =
|
|
183289
|
-
const MAX_DURATION_MS =
|
|
184036
|
+
}).get("/stream", ({ request }) => {
|
|
184037
|
+
const HEARTBEAT_MS = resolveHeartbeatMs();
|
|
184038
|
+
const MAX_DURATION_MS = resolveMaxDurationMs();
|
|
183290
184039
|
const encoder4 = new TextEncoder;
|
|
183291
184040
|
let closed = false;
|
|
183292
184041
|
let unsubscribe;
|
|
@@ -183294,6 +184043,18 @@ var logsRoutes = new Elysia({ prefix: "/api/v1/logs" }).get("/", ({ query, set:
|
|
|
183294
184043
|
let closeTimer;
|
|
183295
184044
|
const stream2 = new ReadableStream({
|
|
183296
184045
|
start(controller2) {
|
|
184046
|
+
applySseRequestTimeout(request);
|
|
184047
|
+
const teardown = () => {
|
|
184048
|
+
closed = true;
|
|
184049
|
+
unsubscribe?.();
|
|
184050
|
+
if (heartbeatTimer)
|
|
184051
|
+
clearInterval(heartbeatTimer);
|
|
184052
|
+
if (closeTimer)
|
|
184053
|
+
clearTimeout(closeTimer);
|
|
184054
|
+
try {
|
|
184055
|
+
controller2.close();
|
|
184056
|
+
} catch {}
|
|
184057
|
+
};
|
|
183297
184058
|
const enqueue = (data) => {
|
|
183298
184059
|
if (closed)
|
|
183299
184060
|
return;
|
|
@@ -183302,7 +184063,7 @@ var logsRoutes = new Elysia({ prefix: "/api/v1/logs" }).get("/", ({ query, set:
|
|
|
183302
184063
|
|
|
183303
184064
|
`));
|
|
183304
184065
|
} catch {
|
|
183305
|
-
|
|
184066
|
+
teardown();
|
|
183306
184067
|
}
|
|
183307
184068
|
};
|
|
183308
184069
|
const tail = startSinkTail(enqueue);
|
|
@@ -183323,18 +184084,10 @@ var logsRoutes = new Elysia({ prefix: "/api/v1/logs" }).get("/", ({ query, set:
|
|
|
183323
184084
|
|
|
183324
184085
|
`));
|
|
183325
184086
|
} catch {
|
|
183326
|
-
|
|
183327
|
-
clearInterval(heartbeatTimer);
|
|
184087
|
+
teardown();
|
|
183328
184088
|
}
|
|
183329
184089
|
}, HEARTBEAT_MS);
|
|
183330
|
-
closeTimer = setTimeout(
|
|
183331
|
-
closed = true;
|
|
183332
|
-
unsubscribe?.();
|
|
183333
|
-
clearInterval(heartbeatTimer);
|
|
183334
|
-
try {
|
|
183335
|
-
controller2.close();
|
|
183336
|
-
} catch {}
|
|
183337
|
-
}, MAX_DURATION_MS);
|
|
184090
|
+
closeTimer = setTimeout(teardown, MAX_DURATION_MS);
|
|
183338
184091
|
},
|
|
183339
184092
|
cancel() {
|
|
183340
184093
|
closed = true;
|
|
@@ -183361,6 +184114,104 @@ var logsRoutes = new Elysia({ prefix: "/api/v1/logs" }).get("/", ({ query, set:
|
|
|
183361
184114
|
}
|
|
183362
184115
|
});
|
|
183363
184116
|
|
|
184117
|
+
// src/middleware/write-mode-classification.ts
|
|
184118
|
+
var READ_ONLY_ROUTES = [
|
|
184119
|
+
{
|
|
184120
|
+
method: "POST",
|
|
184121
|
+
path: "/api/v1/search/project",
|
|
184122
|
+
justification: "WRITES when autoReindex+projectPath are both set. " + "apps/tools-api/src/routes/search.ts:48-106 passes the raw request body to " + "SearchProjectTool.handle(), which forwards it unchanged to " + "SearchController.searchProject " + "(packages/core/src/services/search/search-controller.ts:132-179): autoReindex " + "defaults to false at :140, and at :177-179 `if (autoReindex && projectPath) " + "this.handleAutoReindex(projectId, projectPath)` calls the private helper at " + ":364-389, which calls `this.contextualSearch.ensureFreshIndex(...)` \u2014 a real " + "index write. Kept on the allowlist with a sanitizer (AC-03.3a) instead of being " + "dropped, so read-only mode does not 403 project search entirely.",
|
|
184123
|
+
sanitizeBody: (body) => {
|
|
184124
|
+
body.autoReindex = false;
|
|
184125
|
+
}
|
|
184126
|
+
},
|
|
184127
|
+
{
|
|
184128
|
+
method: "POST",
|
|
184129
|
+
path: "/api/v1/search/code",
|
|
184130
|
+
justification: "Clean. apps/tools-api/src/routes/search.ts:108-126's request schema exposes only " + "{query, projectId, limit} \u2014 no `autoReindex` field exists on this route to flip. " + "packages/core/src/tools/search_code.ts:56-62 delegates to SearchProjectTool.handle " + "with `autoReindex: false` hardcoded at line 61 regardless of caller input, so the " + "write path proven above for /search/project is structurally unreachable from here."
|
|
184131
|
+
},
|
|
184132
|
+
{
|
|
184133
|
+
method: "POST",
|
|
184134
|
+
path: "/api/v1/memory/search",
|
|
184135
|
+
justification: "Clean. apps/tools-api/src/routes/memory.ts:126-138 (`POST /search`) delegates the " + "raw body to SearchMemoriesTool.handle(), which " + "(packages/core/src/tools/search_memories.ts:93-121) only calls " + "`MemoryController.search(...)` and formats the result for the response \u2014 no " + "repository mutation on any code path this handler can reach."
|
|
184136
|
+
},
|
|
184137
|
+
{
|
|
184138
|
+
method: "POST",
|
|
184139
|
+
path: "/api/v1/memory/list",
|
|
184140
|
+
justification: "Clean. apps/tools-api/src/routes/memory.ts:243-268 calls only " + "`getMemoryRepository().search(...)`, then filters/paginates the result in the " + "route handler itself \u2014 no repository write method is reachable from this handler."
|
|
184141
|
+
},
|
|
184142
|
+
{
|
|
184143
|
+
method: "POST",
|
|
184144
|
+
path: "/api/v1/checkpoints/list",
|
|
184145
|
+
justification: "Clean. apps/tools-api/src/routes/checkpoints.ts:56-68 delegates to " + "ListCheckpointsTool.handle(), which " + "(packages/core/src/tools/list_checkpoints.ts:90-99) calls only " + "`checkpointManager.listCheckpoints(...)` and `checkpointManager.getStats()` \u2014 both " + "reads over existing state, never a checkpoint create/delete/restore call."
|
|
184146
|
+
},
|
|
184147
|
+
{
|
|
184148
|
+
method: "POST",
|
|
184149
|
+
path: "/api/v1/handoff/list",
|
|
184150
|
+
justification: "Clean. apps/tools-api/src/routes/handoff.ts:174-213 calls only " + "`service().listPending(...)`, which " + "(packages/core/src/services/handoff/handoff-service.ts:228-229) is a direct " + "pass-through to `this.store.listPending(...)` \u2014 never `begin` (:115), `accept` " + "(:173) or `cancel` (:182), the service's three write entry points."
|
|
184151
|
+
},
|
|
184152
|
+
{
|
|
184153
|
+
method: "POST",
|
|
184154
|
+
path: "/api/v1/proposal/list",
|
|
184155
|
+
justification: "Clean. apps/tools-api/src/routes/proposals.ts:36-73 calls only " + "`job().listPending(...)`, which " + "(packages/core/src/services/jobs/auto-improve-job.ts:113) is a direct pass-through " + "to `this.proposalStore.listPending(...)` \u2014 never `approve` (:111) or `reject` " + "(:112), the job's two write entry points."
|
|
184156
|
+
},
|
|
184157
|
+
{
|
|
184158
|
+
method: "POST",
|
|
184159
|
+
path: "/api/v1/context/compress",
|
|
184160
|
+
justification: "Clean. apps/tools-api/src/routes/context.ts:29-68 delegates to " + "CompressContextTool.handle(), which " + "(packages/core/src/tools/compress_context.ts:67-118) runs `compressWithMetrics` " + "over the request's own `content` string in memory and returns the result \u2014 no " + "repository, index, or filesystem write on any code path."
|
|
184161
|
+
},
|
|
184162
|
+
{
|
|
184163
|
+
method: "POST",
|
|
184164
|
+
path: "/api/v1/context/optimized",
|
|
184165
|
+
justification: "Clean. apps/tools-api/src/routes/context.ts:70-101 delegates to " + "GetOptimizedContextTool.handle(), which " + "(packages/core/src/tools/get_optimized_context.ts:105-110) calls " + "`ContextController.getOptimizedContext(p)`. That controller " + "(packages/core/src/services/context/context-controller.ts:161) hardcodes " + "`autoReindex: false` on its own internal search call, so the write path proven " + "for /search/project is structurally unreachable here even though this route's " + "schema also carries `projectPath`."
|
|
184166
|
+
},
|
|
184167
|
+
{
|
|
184168
|
+
method: "POST",
|
|
184169
|
+
path: "/api/v1/symbol/impact",
|
|
184170
|
+
justification: "Clean. apps/tools-api/src/routes/workspace.ts:542-673 calls " + "`getGraphController().analyzeImpact(...)`, which " + "(packages/core/src/services/symbol/graph-controller.ts:189-232) delegates to " + "`impactAnalysisService.analyze(...)` " + "(packages/core/src/services/symbol/impact-analysis.ts). Every git invocation there " + "runs through `execFileSync` for `rev-parse`/`diff`/`status`-class read commands, " + "plus one `hash-object -t tree --stdin` with no `-w` flag (so nothing is written to " + "the git object database) \u2014 no `git commit`, `git add`, or `-w` flag anywhere in " + "the module."
|
|
184171
|
+
}
|
|
184172
|
+
];
|
|
184173
|
+
function normalizeRoutePath(path43) {
|
|
184174
|
+
return path43.length > 1 && path43.endsWith("/") ? path43.slice(0, -1) : path43;
|
|
184175
|
+
}
|
|
184176
|
+
var READ_ONLY_INDEX = new Map(READ_ONLY_ROUTES.map((entry2) => [`${entry2.method} ${entry2.path}`, entry2]));
|
|
184177
|
+
function findReadOnlyRoute(method, path43) {
|
|
184178
|
+
return READ_ONLY_INDEX.get(`${method.toUpperCase()} ${normalizeRoutePath(path43)}`);
|
|
184179
|
+
}
|
|
184180
|
+
|
|
184181
|
+
// src/middleware/write-mode.ts
|
|
184182
|
+
function defaultReadOnlyModeAccessor() {
|
|
184183
|
+
return process.env.MASSA_AI_READ_ONLY_MODE === "true";
|
|
184184
|
+
}
|
|
184185
|
+
var readOnlyModeAccessor = defaultReadOnlyModeAccessor;
|
|
184186
|
+
function isReadOnlyModeActive() {
|
|
184187
|
+
try {
|
|
184188
|
+
return readOnlyModeAccessor();
|
|
184189
|
+
} catch {
|
|
184190
|
+
return true;
|
|
184191
|
+
}
|
|
184192
|
+
}
|
|
184193
|
+
var WRITE_REFUSED = {
|
|
184194
|
+
success: false,
|
|
184195
|
+
error: "Write refused: read-only mode is active"
|
|
184196
|
+
};
|
|
184197
|
+
var writeModeMiddleware = new Elysia({ name: "write-mode" }).onBeforeHandle({ as: "global" }, ({ request, path: path43, set: set3, body }) => {
|
|
184198
|
+
if (request.method.toUpperCase() === "GET")
|
|
184199
|
+
return;
|
|
184200
|
+
if (isPublicPath(path43))
|
|
184201
|
+
return;
|
|
184202
|
+
if (!isReadOnlyModeActive())
|
|
184203
|
+
return;
|
|
184204
|
+
const entry2 = findReadOnlyRoute(request.method, path43);
|
|
184205
|
+
if (entry2) {
|
|
184206
|
+
if (entry2.sanitizeBody && body && typeof body === "object") {
|
|
184207
|
+
entry2.sanitizeBody(body);
|
|
184208
|
+
}
|
|
184209
|
+
return;
|
|
184210
|
+
}
|
|
184211
|
+
set3.status = 403;
|
|
184212
|
+
return { ...WRITE_REFUSED };
|
|
184213
|
+
});
|
|
184214
|
+
|
|
183364
184215
|
// src/middleware/error.ts
|
|
183365
184216
|
init_dist();
|
|
183366
184217
|
var errorHandler = new Elysia({ name: "error-handler" }).onError(({ code, error: error51, set: set3, path: path43, request }) => {
|
|
@@ -183484,7 +184335,7 @@ var app = new Elysia({ adapter: node() }).use(cors(buildCorsOptions(config.get("
|
|
|
183484
184335
|
},
|
|
183485
184336
|
security: [{ ApiKeyAuth: [] }]
|
|
183486
184337
|
}
|
|
183487
|
-
})).use(errorHandler).use(authMiddleware).use(searchRoutes).use(memoryRoutes).use(checkpointRoutes).use(projectRoutes).use(contextRoutes).use(analyticsRoutes).use(systemRoutes).use(eventsRoutes).use(workspaceRoutes).use(fileRoutes).use(synapseRoutes).use(hookRoutes).use(bootstrapRoutes).use(handoffRoutes).use(proposalRoutes).use(executorRoutes).use(webRoutes).use(webUiRoutes).use(architectureRoutes).use(dashboardRoutes).use(profileRoutes).use(configRoutes).use(modelRegistryRoutes).use(modelRegistryStreamRoutes).use(restartRoutes).use(logsRoutes).get("/health", () => buildHealthResponse(getParserReadiness()));
|
|
184338
|
+
})).use(errorHandler).use(authMiddleware).use(writeModeMiddleware).use(searchRoutes).use(memoryRoutes).use(checkpointRoutes).use(projectRoutes).use(contextRoutes).use(analyticsRoutes).use(systemRoutes).use(eventsRoutes).use(workspaceRoutes).use(fileRoutes).use(synapseRoutes).use(hookRoutes).use(bootstrapRoutes).use(handoffRoutes).use(proposalRoutes).use(executorRoutes).use(webRoutes).use(webUiRoutes).use(architectureRoutes).use(dashboardRoutes).use(profileRoutes).use(configRoutes).use(modelRegistryRoutes).use(modelRegistryStreamRoutes).use(restartRoutes).use(logsRoutes).get("/health", () => buildHealthResponse(getParserReadiness()));
|
|
183488
184339
|
initAuthOrExit();
|
|
183489
184340
|
warnIfTrustOverrideEnabled();
|
|
183490
184341
|
await listenAfterParserValidation({
|
|
@@ -183493,6 +184344,8 @@ await listenAfterParserValidation({
|
|
|
183493
184344
|
try {
|
|
183494
184345
|
app.listen({ port: Number(PORT), reusePort: false }, (server) => {
|
|
183495
184346
|
setServerStopper(() => void server.stop());
|
|
184347
|
+
const bunServer = server.bun?.server;
|
|
184348
|
+
setSseRequestTimeoutSource(bunServer);
|
|
183496
184349
|
});
|
|
183497
184350
|
} catch (error51) {
|
|
183498
184351
|
if (error51?.code === "EADDRINUSE") {
|