@askexenow/exe-os 0.9.60 → 0.9.62
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/deploy/stack-manifests/v0.9.json +62 -13
- package/dist/bin/backfill-conversations.js +282 -7
- package/dist/bin/backfill-responses.js +282 -7
- package/dist/bin/backfill-vectors.js +119 -7
- package/dist/bin/cc-doctor.js +376 -0
- package/dist/bin/cleanup-stale-review-tasks.js +282 -7
- package/dist/bin/cli.js +455 -77
- package/dist/bin/customer-readiness.js +33 -0
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +3 -3
- package/dist/bin/exe-assign.js +282 -7
- package/dist/bin/exe-boot.js +125 -13
- package/dist/bin/exe-call.js +3 -3
- package/dist/bin/exe-cloud.js +2 -2
- package/dist/bin/exe-dispatch.js +282 -7
- package/dist/bin/exe-doctor.js +119 -7
- package/dist/bin/exe-export-behaviors.js +282 -7
- package/dist/bin/exe-forget.js +336 -7
- package/dist/bin/exe-gateway.js +282 -7
- package/dist/bin/exe-heartbeat.js +284 -9
- package/dist/bin/exe-kill.js +282 -7
- package/dist/bin/exe-launch-agent.js +282 -7
- package/dist/bin/exe-link.js +121 -9
- package/dist/bin/exe-new-employee.js +50 -21
- package/dist/bin/exe-pending-messages.js +282 -7
- package/dist/bin/exe-pending-notifications.js +282 -7
- package/dist/bin/exe-pending-reviews.js +282 -7
- package/dist/bin/exe-rename.js +282 -7
- package/dist/bin/exe-review.js +282 -7
- package/dist/bin/exe-search.js +306 -8
- package/dist/bin/exe-session-cleanup.js +282 -7
- package/dist/bin/exe-settings.js +2 -2
- package/dist/bin/exe-start-codex.js +326 -21
- package/dist/bin/exe-start-opencode.js +304 -10
- package/dist/bin/exe-status.js +282 -7
- package/dist/bin/exe-team.js +282 -7
- package/dist/bin/git-sweep.js +282 -7
- package/dist/bin/graph-backfill.js +282 -7
- package/dist/bin/graph-export.js +282 -7
- package/dist/bin/install.js +58 -33
- package/dist/bin/intercom-check.js +282 -7
- package/dist/bin/pre-build-guard.js +98 -0
- package/dist/bin/scan-tasks.js +282 -7
- package/dist/bin/setup.js +122 -10
- package/dist/bin/shard-migrate.js +282 -7
- package/dist/bin/stack-update.js +79 -11
- package/dist/bin/update.js +2 -2
- package/dist/gateway/index.js +288 -13
- package/dist/hooks/bug-report-worker.js +282 -7
- package/dist/hooks/codex-stop-task-finalizer.js +282 -7
- package/dist/hooks/commit-complete.js +282 -7
- package/dist/hooks/error-recall.js +306 -8
- package/dist/hooks/exe-heartbeat-hook.js +2 -2
- package/dist/hooks/ingest-worker.js +2 -2
- package/dist/hooks/ingest.js +282 -7
- package/dist/hooks/instructions-loaded.js +282 -7
- package/dist/hooks/notification.js +282 -7
- package/dist/hooks/post-compact.js +282 -7
- package/dist/hooks/post-tool-combined.js +306 -8
- package/dist/hooks/pre-compact.js +282 -7
- package/dist/hooks/pre-tool-use.js +282 -7
- package/dist/hooks/prompt-submit.js +306 -8
- package/dist/hooks/session-end.js +282 -7
- package/dist/hooks/session-start.js +308 -10
- package/dist/hooks/stop.js +282 -7
- package/dist/hooks/subagent-stop.js +282 -7
- package/dist/hooks/summary-worker.js +125 -13
- package/dist/index.js +288 -13
- package/dist/lib/agent-config.js +2 -2
- package/dist/lib/cloud-sync.js +121 -9
- package/dist/lib/config.js +2 -2
- package/dist/lib/consolidation.js +2 -2
- package/dist/lib/database.js +115 -3
- package/dist/lib/db-daemon-client.js +2 -2
- package/dist/lib/db.js +115 -3
- package/dist/lib/device-registry.js +115 -3
- package/dist/lib/embedder.js +2 -2
- package/dist/lib/employee-templates.js +3 -3
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon-client.js +2 -2
- package/dist/lib/exe-daemon.js +339 -31
- package/dist/lib/hybrid-search.js +306 -8
- package/dist/lib/identity.js +2 -2
- package/dist/lib/license.js +2 -2
- package/dist/lib/messaging.js +2 -2
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +119 -7
- package/dist/lib/skill-learning.js +2 -2
- package/dist/lib/store.js +282 -7
- package/dist/lib/task-router.js +2 -2
- package/dist/lib/tasks.js +2 -2
- package/dist/lib/tmux-routing.js +2 -2
- package/dist/lib/token-spend.js +2 -2
- package/dist/mcp/server.js +339 -31
- package/dist/mcp/tools/complete-reminder.js +2 -2
- package/dist/mcp/tools/create-reminder.js +2 -2
- package/dist/mcp/tools/create-task.js +2 -2
- package/dist/mcp/tools/deactivate-behavior.js +2 -2
- package/dist/mcp/tools/list-reminders.js +2 -2
- package/dist/mcp/tools/list-tasks.js +2 -2
- package/dist/mcp/tools/send-message.js +2 -2
- package/dist/mcp/tools/update-task.js +2 -2
- package/dist/runtime/index.js +282 -7
- package/dist/tui/App.js +290 -15
- package/package.json +3 -2
- package/stack.release.json +23 -7
package/dist/bin/cli.js
CHANGED
|
@@ -258,8 +258,8 @@ var init_config = __esm({
|
|
|
258
258
|
rerankerAutoTrigger: {
|
|
259
259
|
enabled: true,
|
|
260
260
|
broadQueryMinCardinality: 5e4,
|
|
261
|
-
fetchTopK:
|
|
262
|
-
returnTopK:
|
|
261
|
+
fetchTopK: 200,
|
|
262
|
+
returnTopK: 20
|
|
263
263
|
}
|
|
264
264
|
},
|
|
265
265
|
graphRagEnabled: true,
|
|
@@ -810,6 +810,48 @@ var init_preferences = __esm({
|
|
|
810
810
|
}
|
|
811
811
|
});
|
|
812
812
|
|
|
813
|
+
// src/adapters/runtime-hook-manifest.ts
|
|
814
|
+
function commandHasAnyMarker(command, markers) {
|
|
815
|
+
return markers.some((marker) => command.includes(marker));
|
|
816
|
+
}
|
|
817
|
+
function isLegacySplitPostToolCommand(command) {
|
|
818
|
+
return commandHasAnyMarker(command, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS);
|
|
819
|
+
}
|
|
820
|
+
function textHasLegacySplitPostToolHook(text) {
|
|
821
|
+
return [EXE_HOOK_FILES.ingest, EXE_HOOK_FILES.errorRecall, EXE_HOOK_FILES.ingestWorker].some((file) => text.includes(file));
|
|
822
|
+
}
|
|
823
|
+
var EXE_HOOK_FILES, EXE_HOOKS, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
|
|
824
|
+
var init_runtime_hook_manifest = __esm({
|
|
825
|
+
"src/adapters/runtime-hook-manifest.ts"() {
|
|
826
|
+
"use strict";
|
|
827
|
+
EXE_HOOK_FILES = {
|
|
828
|
+
postToolCombined: "post-tool-combined.js",
|
|
829
|
+
ingest: "ingest.js",
|
|
830
|
+
errorRecall: "error-recall.js",
|
|
831
|
+
ingestWorker: "ingest-worker.js"
|
|
832
|
+
};
|
|
833
|
+
EXE_HOOKS = {
|
|
834
|
+
postToolCombined: "dist/hooks/post-tool-combined.js",
|
|
835
|
+
sessionStart: "dist/hooks/session-start.js",
|
|
836
|
+
promptSubmit: "dist/hooks/prompt-submit.js",
|
|
837
|
+
heartbeat: "dist/hooks/exe-heartbeat-hook.js",
|
|
838
|
+
stop: "dist/hooks/stop.js",
|
|
839
|
+
preToolUse: "dist/hooks/pre-tool-use.js",
|
|
840
|
+
subagentStop: "dist/hooks/subagent-stop.js",
|
|
841
|
+
preCompact: "dist/hooks/pre-compact.js",
|
|
842
|
+
postCompact: "dist/hooks/post-compact.js",
|
|
843
|
+
sessionEnd: "dist/hooks/session-end.js",
|
|
844
|
+
notification: "dist/hooks/notification.js",
|
|
845
|
+
instructionsLoaded: "dist/hooks/instructions-loaded.js"
|
|
846
|
+
};
|
|
847
|
+
LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS = [
|
|
848
|
+
"dist/hooks/ingest.js",
|
|
849
|
+
"dist/hooks/error-recall.js",
|
|
850
|
+
"dist/hooks/ingest-worker.js"
|
|
851
|
+
];
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
|
|
813
855
|
// src/adapters/claude/installer.ts
|
|
814
856
|
var installer_exports = {};
|
|
815
857
|
__export(installer_exports, {
|
|
@@ -1132,7 +1174,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1132
1174
|
}
|
|
1133
1175
|
]
|
|
1134
1176
|
},
|
|
1135
|
-
marker:
|
|
1177
|
+
marker: EXE_HOOKS.postToolCombined
|
|
1136
1178
|
},
|
|
1137
1179
|
{
|
|
1138
1180
|
event: "SessionStart",
|
|
@@ -1145,7 +1187,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1145
1187
|
}
|
|
1146
1188
|
]
|
|
1147
1189
|
},
|
|
1148
|
-
marker:
|
|
1190
|
+
marker: EXE_HOOKS.sessionStart
|
|
1149
1191
|
},
|
|
1150
1192
|
{
|
|
1151
1193
|
event: "UserPromptSubmit",
|
|
@@ -1157,7 +1199,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1157
1199
|
}
|
|
1158
1200
|
]
|
|
1159
1201
|
},
|
|
1160
|
-
marker:
|
|
1202
|
+
marker: EXE_HOOKS.promptSubmit
|
|
1161
1203
|
},
|
|
1162
1204
|
{
|
|
1163
1205
|
event: "UserPromptSubmit",
|
|
@@ -1170,7 +1212,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1170
1212
|
}
|
|
1171
1213
|
]
|
|
1172
1214
|
},
|
|
1173
|
-
marker:
|
|
1215
|
+
marker: EXE_HOOKS.heartbeat
|
|
1174
1216
|
},
|
|
1175
1217
|
{
|
|
1176
1218
|
event: "Stop",
|
|
@@ -1182,7 +1224,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1182
1224
|
}
|
|
1183
1225
|
]
|
|
1184
1226
|
},
|
|
1185
|
-
marker:
|
|
1227
|
+
marker: EXE_HOOKS.stop
|
|
1186
1228
|
},
|
|
1187
1229
|
{
|
|
1188
1230
|
event: "PreToolUse",
|
|
@@ -1195,7 +1237,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1195
1237
|
}
|
|
1196
1238
|
]
|
|
1197
1239
|
},
|
|
1198
|
-
marker:
|
|
1240
|
+
marker: EXE_HOOKS.preToolUse
|
|
1199
1241
|
},
|
|
1200
1242
|
{
|
|
1201
1243
|
event: "SubagentStop",
|
|
@@ -1207,7 +1249,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1207
1249
|
}
|
|
1208
1250
|
]
|
|
1209
1251
|
},
|
|
1210
|
-
marker:
|
|
1252
|
+
marker: EXE_HOOKS.subagentStop
|
|
1211
1253
|
},
|
|
1212
1254
|
{
|
|
1213
1255
|
event: "PreCompact",
|
|
@@ -1220,7 +1262,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1220
1262
|
}
|
|
1221
1263
|
]
|
|
1222
1264
|
},
|
|
1223
|
-
marker:
|
|
1265
|
+
marker: EXE_HOOKS.preCompact
|
|
1224
1266
|
},
|
|
1225
1267
|
{
|
|
1226
1268
|
event: "SessionEnd",
|
|
@@ -1232,7 +1274,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1232
1274
|
}
|
|
1233
1275
|
]
|
|
1234
1276
|
},
|
|
1235
|
-
marker:
|
|
1277
|
+
marker: EXE_HOOKS.sessionEnd
|
|
1236
1278
|
},
|
|
1237
1279
|
{
|
|
1238
1280
|
event: "Notification",
|
|
@@ -1244,7 +1286,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1244
1286
|
}
|
|
1245
1287
|
]
|
|
1246
1288
|
},
|
|
1247
|
-
marker:
|
|
1289
|
+
marker: EXE_HOOKS.notification
|
|
1248
1290
|
},
|
|
1249
1291
|
{
|
|
1250
1292
|
event: "PostCompact",
|
|
@@ -1257,7 +1299,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1257
1299
|
}
|
|
1258
1300
|
]
|
|
1259
1301
|
},
|
|
1260
|
-
marker:
|
|
1302
|
+
marker: EXE_HOOKS.postCompact
|
|
1261
1303
|
},
|
|
1262
1304
|
{
|
|
1263
1305
|
event: "InstructionsLoaded",
|
|
@@ -1269,21 +1311,16 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1269
1311
|
}
|
|
1270
1312
|
]
|
|
1271
1313
|
},
|
|
1272
|
-
marker:
|
|
1314
|
+
marker: EXE_HOOKS.instructionsLoaded
|
|
1273
1315
|
}
|
|
1274
1316
|
];
|
|
1275
1317
|
let added = 0;
|
|
1276
1318
|
let skipped = 0;
|
|
1277
|
-
const legacyPostToolMarkers = [
|
|
1278
|
-
"dist/hooks/ingest.js",
|
|
1279
|
-
"dist/hooks/error-recall.js",
|
|
1280
|
-
"dist/hooks/ingest-worker.js"
|
|
1281
|
-
];
|
|
1282
1319
|
const postToolGroups = settings.hooks["PostToolUse"];
|
|
1283
1320
|
if (Array.isArray(postToolGroups)) {
|
|
1284
1321
|
settings.hooks["PostToolUse"] = postToolGroups.map((g) => ({
|
|
1285
1322
|
...g,
|
|
1286
|
-
hooks: g.hooks.filter((h) => !
|
|
1323
|
+
hooks: g.hooks.filter((h) => !isLegacySplitPostToolCommand(h.command))
|
|
1287
1324
|
})).filter((g) => g.hooks.length > 0);
|
|
1288
1325
|
}
|
|
1289
1326
|
for (const { event, group, marker } of hooksToRegister) {
|
|
@@ -1760,6 +1797,7 @@ var init_installer = __esm({
|
|
|
1760
1797
|
init_agent_symlinks();
|
|
1761
1798
|
init_mcp_prefix();
|
|
1762
1799
|
init_preferences();
|
|
1800
|
+
init_runtime_hook_manifest();
|
|
1763
1801
|
EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
|
|
1764
1802
|
EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
|
|
1765
1803
|
ORCHESTRATION_RULES = `${EXE_SECTION_START}
|
|
@@ -4306,7 +4344,7 @@ async function ensureSchema() {
|
|
|
4306
4344
|
ON session_kills(agent_id);
|
|
4307
4345
|
`);
|
|
4308
4346
|
await client.execute(`
|
|
4309
|
-
CREATE TABLE IF NOT EXISTS
|
|
4347
|
+
CREATE TABLE IF NOT EXISTS company_procedures (
|
|
4310
4348
|
id TEXT PRIMARY KEY,
|
|
4311
4349
|
title TEXT NOT NULL,
|
|
4312
4350
|
content TEXT NOT NULL,
|
|
@@ -4317,6 +4355,73 @@ async function ensureSchema() {
|
|
|
4317
4355
|
updated_at TEXT NOT NULL
|
|
4318
4356
|
)
|
|
4319
4357
|
`);
|
|
4358
|
+
const legacyProcedureObject = await client.execute({
|
|
4359
|
+
sql: "SELECT type FROM sqlite_master WHERE name = 'global_procedures'",
|
|
4360
|
+
args: []
|
|
4361
|
+
});
|
|
4362
|
+
const legacyProcedureType = legacyProcedureObject.rows[0]?.type == null ? null : String(legacyProcedureObject.rows[0].type);
|
|
4363
|
+
if (legacyProcedureType === "table") {
|
|
4364
|
+
await client.execute(`
|
|
4365
|
+
INSERT OR IGNORE INTO company_procedures
|
|
4366
|
+
(id, title, content, priority, domain, active, created_at, updated_at)
|
|
4367
|
+
SELECT id, title, content, priority, domain, active, created_at, updated_at
|
|
4368
|
+
FROM global_procedures
|
|
4369
|
+
`);
|
|
4370
|
+
await client.executeMultiple(`
|
|
4371
|
+
CREATE TRIGGER IF NOT EXISTS global_procedures_mirror_insert
|
|
4372
|
+
AFTER INSERT ON global_procedures
|
|
4373
|
+
BEGIN
|
|
4374
|
+
INSERT OR IGNORE INTO company_procedures
|
|
4375
|
+
(id, title, content, priority, domain, active, created_at, updated_at)
|
|
4376
|
+
VALUES
|
|
4377
|
+
(NEW.id, NEW.title, NEW.content, NEW.priority, NEW.domain, NEW.active, NEW.created_at, NEW.updated_at);
|
|
4378
|
+
END;
|
|
4379
|
+
|
|
4380
|
+
CREATE TRIGGER IF NOT EXISTS global_procedures_mirror_update
|
|
4381
|
+
AFTER UPDATE ON global_procedures
|
|
4382
|
+
BEGIN
|
|
4383
|
+
UPDATE company_procedures
|
|
4384
|
+
SET title = NEW.title,
|
|
4385
|
+
content = NEW.content,
|
|
4386
|
+
priority = NEW.priority,
|
|
4387
|
+
domain = NEW.domain,
|
|
4388
|
+
active = NEW.active,
|
|
4389
|
+
created_at = NEW.created_at,
|
|
4390
|
+
updated_at = NEW.updated_at
|
|
4391
|
+
WHERE id = OLD.id;
|
|
4392
|
+
END;
|
|
4393
|
+
`);
|
|
4394
|
+
} else {
|
|
4395
|
+
await client.execute(`
|
|
4396
|
+
CREATE VIEW IF NOT EXISTS global_procedures AS
|
|
4397
|
+
SELECT id, title, content, priority, domain, active, created_at, updated_at
|
|
4398
|
+
FROM company_procedures
|
|
4399
|
+
`);
|
|
4400
|
+
await client.executeMultiple(`
|
|
4401
|
+
CREATE TRIGGER IF NOT EXISTS global_procedures_insert
|
|
4402
|
+
INSTEAD OF INSERT ON global_procedures
|
|
4403
|
+
BEGIN
|
|
4404
|
+
INSERT INTO company_procedures
|
|
4405
|
+
(id, title, content, priority, domain, active, created_at, updated_at)
|
|
4406
|
+
VALUES
|
|
4407
|
+
(NEW.id, NEW.title, NEW.content, NEW.priority, NEW.domain, NEW.active, NEW.created_at, NEW.updated_at);
|
|
4408
|
+
END;
|
|
4409
|
+
|
|
4410
|
+
CREATE TRIGGER IF NOT EXISTS global_procedures_update
|
|
4411
|
+
INSTEAD OF UPDATE ON global_procedures
|
|
4412
|
+
BEGIN
|
|
4413
|
+
UPDATE company_procedures
|
|
4414
|
+
SET title = NEW.title,
|
|
4415
|
+
content = NEW.content,
|
|
4416
|
+
priority = NEW.priority,
|
|
4417
|
+
domain = NEW.domain,
|
|
4418
|
+
active = NEW.active,
|
|
4419
|
+
created_at = NEW.created_at,
|
|
4420
|
+
updated_at = NEW.updated_at
|
|
4421
|
+
WHERE id = OLD.id;
|
|
4422
|
+
END;
|
|
4423
|
+
`);
|
|
4424
|
+
}
|
|
4320
4425
|
await client.executeMultiple(`
|
|
4321
4426
|
CREATE TABLE IF NOT EXISTS conversations (
|
|
4322
4427
|
id TEXT PRIMARY KEY,
|
|
@@ -4456,6 +4561,51 @@ async function ensureSchema() {
|
|
|
4456
4561
|
VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
|
|
4457
4562
|
END;
|
|
4458
4563
|
`);
|
|
4564
|
+
await client.executeMultiple(`
|
|
4565
|
+
CREATE TABLE IF NOT EXISTS memory_cards (
|
|
4566
|
+
id TEXT PRIMARY KEY,
|
|
4567
|
+
memory_id TEXT NOT NULL,
|
|
4568
|
+
agent_id TEXT NOT NULL,
|
|
4569
|
+
session_id TEXT NOT NULL,
|
|
4570
|
+
project_name TEXT,
|
|
4571
|
+
timestamp TEXT NOT NULL,
|
|
4572
|
+
card_type TEXT NOT NULL,
|
|
4573
|
+
subject TEXT,
|
|
4574
|
+
predicate TEXT,
|
|
4575
|
+
object TEXT,
|
|
4576
|
+
content TEXT NOT NULL,
|
|
4577
|
+
source_ref TEXT,
|
|
4578
|
+
confidence REAL DEFAULT 0.6,
|
|
4579
|
+
active INTEGER DEFAULT 1,
|
|
4580
|
+
created_at TEXT NOT NULL
|
|
4581
|
+
);
|
|
4582
|
+
|
|
4583
|
+
CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
|
|
4584
|
+
ON memory_cards(agent_id, active, timestamp);
|
|
4585
|
+
|
|
4586
|
+
CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
|
|
4587
|
+
ON memory_cards(memory_id);
|
|
4588
|
+
|
|
4589
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
|
|
4590
|
+
USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
|
|
4591
|
+
|
|
4592
|
+
CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
|
|
4593
|
+
INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
|
|
4594
|
+
VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
|
|
4595
|
+
END;
|
|
4596
|
+
|
|
4597
|
+
CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
|
|
4598
|
+
INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
|
|
4599
|
+
VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
|
|
4600
|
+
END;
|
|
4601
|
+
|
|
4602
|
+
CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
|
|
4603
|
+
INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
|
|
4604
|
+
VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
|
|
4605
|
+
INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
|
|
4606
|
+
VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
|
|
4607
|
+
END;
|
|
4608
|
+
`);
|
|
4459
4609
|
try {
|
|
4460
4610
|
await client.execute({
|
|
4461
4611
|
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
@@ -5904,12 +6054,12 @@ async function cloudSync(config) {
|
|
|
5904
6054
|
try {
|
|
5905
6055
|
await cloudPushGlobalProcedures(config);
|
|
5906
6056
|
} catch (err) {
|
|
5907
|
-
logError(`[cloud-sync]
|
|
6057
|
+
logError(`[cloud-sync] Company procedures push: ${err instanceof Error ? err.message : String(err)}`);
|
|
5908
6058
|
}
|
|
5909
6059
|
try {
|
|
5910
6060
|
await cloudPullGlobalProcedures(config);
|
|
5911
6061
|
} catch (err) {
|
|
5912
|
-
logError(`[cloud-sync]
|
|
6062
|
+
logError(`[cloud-sync] Company procedures pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
5913
6063
|
}
|
|
5914
6064
|
const countRows = async (sql) => {
|
|
5915
6065
|
try {
|
|
@@ -6318,12 +6468,12 @@ async function cloudPullBlob(route, config) {
|
|
|
6318
6468
|
}
|
|
6319
6469
|
async function cloudPushGlobalProcedures(config) {
|
|
6320
6470
|
const client = getClient();
|
|
6321
|
-
const result = await client.execute("SELECT * FROM
|
|
6471
|
+
const result = await client.execute("SELECT * FROM company_procedures LIMIT 1000");
|
|
6322
6472
|
const rows = result.rows;
|
|
6323
6473
|
const { ok } = await cloudPushBlob(
|
|
6324
6474
|
"/sync/push-global-procedures",
|
|
6325
6475
|
rows,
|
|
6326
|
-
"
|
|
6476
|
+
"last_company_procedures_push_version",
|
|
6327
6477
|
config
|
|
6328
6478
|
);
|
|
6329
6479
|
return ok;
|
|
@@ -6336,7 +6486,7 @@ async function cloudPullGlobalProcedures(config) {
|
|
|
6336
6486
|
if (!remoteProcs || remoteProcs.length === 0) return { pulled: 0 };
|
|
6337
6487
|
const client = getClient();
|
|
6338
6488
|
const stmts = remoteProcs.map((p) => ({
|
|
6339
|
-
sql: `INSERT INTO
|
|
6489
|
+
sql: `INSERT INTO company_procedures
|
|
6340
6490
|
(id, title, content, priority, domain, active, created_at, updated_at)
|
|
6341
6491
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
6342
6492
|
ON CONFLICT(id) DO UPDATE SET
|
|
@@ -6346,7 +6496,7 @@ async function cloudPullGlobalProcedures(config) {
|
|
|
6346
6496
|
domain = excluded.domain,
|
|
6347
6497
|
active = excluded.active,
|
|
6348
6498
|
updated_at = excluded.updated_at
|
|
6349
|
-
WHERE excluded.updated_at >
|
|
6499
|
+
WHERE excluded.updated_at > company_procedures.updated_at`,
|
|
6350
6500
|
args: [
|
|
6351
6501
|
sqlSafe(p.id),
|
|
6352
6502
|
sqlSafe(p.title),
|
|
@@ -7756,7 +7906,7 @@ var init_platform_procedures = __esm({
|
|
|
7756
7906
|
title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
|
|
7757
7907
|
domain: "tool-use",
|
|
7758
7908
|
priority: "p1",
|
|
7759
|
-
content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done.
|
|
7909
|
+
content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. company_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: global_procedure, store_global_procedure, list_global_procedures, deactivate_global_procedure."
|
|
7760
7910
|
}
|
|
7761
7911
|
];
|
|
7762
7912
|
PLATFORM_PROCEDURE_TITLES = new Set(
|
|
@@ -7777,7 +7927,7 @@ import { randomUUID as randomUUID3 } from "crypto";
|
|
|
7777
7927
|
async function loadGlobalProcedures() {
|
|
7778
7928
|
const client = getClient();
|
|
7779
7929
|
const result = await client.execute({
|
|
7780
|
-
sql: "SELECT * FROM
|
|
7930
|
+
sql: "SELECT * FROM company_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
|
|
7781
7931
|
args: []
|
|
7782
7932
|
});
|
|
7783
7933
|
const allRows = result.rows;
|
|
@@ -7806,7 +7956,7 @@ async function storeGlobalProcedure(input) {
|
|
|
7806
7956
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7807
7957
|
const client = getClient();
|
|
7808
7958
|
await client.execute({
|
|
7809
|
-
sql: `INSERT INTO
|
|
7959
|
+
sql: `INSERT INTO company_procedures (id, title, content, priority, domain, active, created_at, updated_at)
|
|
7810
7960
|
VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
7811
7961
|
args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
|
|
7812
7962
|
});
|
|
@@ -7817,7 +7967,7 @@ async function deactivateGlobalProcedure(id) {
|
|
|
7817
7967
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7818
7968
|
const client = getClient();
|
|
7819
7969
|
const result = await client.execute({
|
|
7820
|
-
sql: "UPDATE
|
|
7970
|
+
sql: "UPDATE company_procedures SET active = 0, updated_at = ? WHERE id = ?",
|
|
7821
7971
|
args: [now, id]
|
|
7822
7972
|
});
|
|
7823
7973
|
await loadGlobalProcedures();
|
|
@@ -7836,6 +7986,164 @@ ${p.content}`).join("\n\n");
|
|
|
7836
7986
|
}
|
|
7837
7987
|
});
|
|
7838
7988
|
|
|
7989
|
+
// src/lib/memory-cards.ts
|
|
7990
|
+
var memory_cards_exports = {};
|
|
7991
|
+
__export(memory_cards_exports, {
|
|
7992
|
+
extractMemoryCards: () => extractMemoryCards,
|
|
7993
|
+
insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
|
|
7994
|
+
searchMemoryCards: () => searchMemoryCards
|
|
7995
|
+
});
|
|
7996
|
+
import { createHash as createHash3 } from "crypto";
|
|
7997
|
+
function stableId(memoryId, type, content) {
|
|
7998
|
+
return createHash3("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
7999
|
+
}
|
|
8000
|
+
function cleanText(text) {
|
|
8001
|
+
return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
8002
|
+
}
|
|
8003
|
+
function splitSentences(text) {
|
|
8004
|
+
return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
|
|
8005
|
+
}
|
|
8006
|
+
function inferCardType(sentence, toolName) {
|
|
8007
|
+
const lower = sentence.toLowerCase();
|
|
8008
|
+
if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
|
|
8009
|
+
if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
|
|
8010
|
+
if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
|
|
8011
|
+
if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
|
|
8012
|
+
if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
|
|
8013
|
+
return "fact";
|
|
8014
|
+
}
|
|
8015
|
+
function extractSubject(sentence, agentId) {
|
|
8016
|
+
const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
|
|
8017
|
+
return explicit?.[1] ?? agentId;
|
|
8018
|
+
}
|
|
8019
|
+
function predicateFor(type) {
|
|
8020
|
+
switch (type) {
|
|
8021
|
+
case "preference":
|
|
8022
|
+
return "prefers";
|
|
8023
|
+
case "belief_update":
|
|
8024
|
+
return "updated";
|
|
8025
|
+
case "decision":
|
|
8026
|
+
return "decided";
|
|
8027
|
+
case "event":
|
|
8028
|
+
return "happened";
|
|
8029
|
+
case "code":
|
|
8030
|
+
return "implemented";
|
|
8031
|
+
default:
|
|
8032
|
+
return "states";
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
8035
|
+
function extractMemoryCards(row) {
|
|
8036
|
+
const sentences = splitSentences(row.raw_text);
|
|
8037
|
+
const cards = [];
|
|
8038
|
+
for (const sentence of sentences) {
|
|
8039
|
+
const type = inferCardType(sentence, row.tool_name);
|
|
8040
|
+
const subject = extractSubject(sentence, row.agent_id);
|
|
8041
|
+
const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
|
|
8042
|
+
cards.push({
|
|
8043
|
+
id: stableId(row.id, type, content),
|
|
8044
|
+
memory_id: row.id,
|
|
8045
|
+
agent_id: row.agent_id,
|
|
8046
|
+
session_id: row.session_id,
|
|
8047
|
+
project_name: row.project_name ?? null,
|
|
8048
|
+
timestamp: row.timestamp,
|
|
8049
|
+
card_type: type,
|
|
8050
|
+
subject,
|
|
8051
|
+
predicate: predicateFor(type),
|
|
8052
|
+
object: content,
|
|
8053
|
+
content,
|
|
8054
|
+
source_ref: row.id,
|
|
8055
|
+
confidence: type === "fact" ? 0.55 : 0.65
|
|
8056
|
+
});
|
|
8057
|
+
if (cards.length >= MAX_CARDS_PER_MEMORY) break;
|
|
8058
|
+
}
|
|
8059
|
+
return cards;
|
|
8060
|
+
}
|
|
8061
|
+
async function insertMemoryCardsForBatch(rows) {
|
|
8062
|
+
const cards = rows.flatMap(extractMemoryCards);
|
|
8063
|
+
if (cards.length === 0) return 0;
|
|
8064
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8065
|
+
const client = getClient();
|
|
8066
|
+
const stmts = cards.map((card) => ({
|
|
8067
|
+
sql: `INSERT OR IGNORE INTO memory_cards
|
|
8068
|
+
(id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
|
|
8069
|
+
subject, predicate, object, content, source_ref, confidence, active, created_at)
|
|
8070
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
|
|
8071
|
+
args: [
|
|
8072
|
+
card.id,
|
|
8073
|
+
card.memory_id,
|
|
8074
|
+
card.agent_id,
|
|
8075
|
+
card.session_id,
|
|
8076
|
+
card.project_name,
|
|
8077
|
+
card.timestamp,
|
|
8078
|
+
card.card_type,
|
|
8079
|
+
card.subject,
|
|
8080
|
+
card.predicate,
|
|
8081
|
+
card.object,
|
|
8082
|
+
card.content,
|
|
8083
|
+
card.source_ref,
|
|
8084
|
+
card.confidence,
|
|
8085
|
+
now
|
|
8086
|
+
]
|
|
8087
|
+
}));
|
|
8088
|
+
await client.batch(stmts, "write");
|
|
8089
|
+
return cards.length;
|
|
8090
|
+
}
|
|
8091
|
+
function buildMatchExpr(queryText) {
|
|
8092
|
+
const terms = queryText.toLowerCase().split(/\s+/).filter((t) => t.length >= 3).map((t) => t.replace(/[^a-z0-9_]/g, "")).filter((t) => t.length >= 3).slice(0, 12);
|
|
8093
|
+
if (terms.length === 0) return null;
|
|
8094
|
+
return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
|
|
8095
|
+
}
|
|
8096
|
+
async function searchMemoryCards(queryText, agentId, options) {
|
|
8097
|
+
const limit = options?.limit ?? 10;
|
|
8098
|
+
const matchExpr = buildMatchExpr(queryText);
|
|
8099
|
+
if (!matchExpr) return [];
|
|
8100
|
+
let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
|
|
8101
|
+
c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
|
|
8102
|
+
FROM memory_cards c
|
|
8103
|
+
JOIN memory_cards_fts fts ON c.rowid = fts.rowid
|
|
8104
|
+
WHERE memory_cards_fts MATCH ?
|
|
8105
|
+
AND c.agent_id = ?
|
|
8106
|
+
AND COALESCE(c.active, 1) = 1`;
|
|
8107
|
+
const args2 = [matchExpr, agentId];
|
|
8108
|
+
if (options?.projectName) {
|
|
8109
|
+
sql += ` AND c.project_name = ?`;
|
|
8110
|
+
args2.push(options.projectName);
|
|
8111
|
+
}
|
|
8112
|
+
if (options?.since) {
|
|
8113
|
+
sql += ` AND c.timestamp >= ?`;
|
|
8114
|
+
args2.push(options.since);
|
|
8115
|
+
}
|
|
8116
|
+
sql += ` ORDER BY rank LIMIT ?`;
|
|
8117
|
+
args2.push(limit);
|
|
8118
|
+
const result = await getClient().execute({ sql, args: args2 });
|
|
8119
|
+
return result.rows.map((row) => ({
|
|
8120
|
+
id: `card:${String(row.id)}`,
|
|
8121
|
+
agent_id: String(row.agent_id),
|
|
8122
|
+
agent_role: "memory_card",
|
|
8123
|
+
session_id: String(row.session_id),
|
|
8124
|
+
timestamp: String(row.timestamp),
|
|
8125
|
+
tool_name: `memory_card:${String(row.card_type)}`,
|
|
8126
|
+
project_name: row.project_name == null ? "" : String(row.project_name),
|
|
8127
|
+
has_error: false,
|
|
8128
|
+
raw_text: `[${String(row.card_type)}] ${String(row.content)}
|
|
8129
|
+
Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
8130
|
+
vector: [],
|
|
8131
|
+
importance: 6,
|
|
8132
|
+
status: "active",
|
|
8133
|
+
confidence: Number(row.confidence ?? 0.6),
|
|
8134
|
+
last_accessed: String(row.timestamp)
|
|
8135
|
+
}));
|
|
8136
|
+
}
|
|
8137
|
+
var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
|
|
8138
|
+
var init_memory_cards = __esm({
|
|
8139
|
+
"src/lib/memory-cards.ts"() {
|
|
8140
|
+
"use strict";
|
|
8141
|
+
init_database();
|
|
8142
|
+
MAX_CARDS_PER_MEMORY = 6;
|
|
8143
|
+
MAX_SENTENCE_CHARS = 360;
|
|
8144
|
+
}
|
|
8145
|
+
});
|
|
8146
|
+
|
|
7839
8147
|
// src/lib/store.ts
|
|
7840
8148
|
var store_exports = {};
|
|
7841
8149
|
__export(store_exports, {
|
|
@@ -8174,6 +8482,11 @@ async function flushBatch() {
|
|
|
8174
8482
|
const globalClient = getClient();
|
|
8175
8483
|
const globalStmts = batch.map(buildStmt);
|
|
8176
8484
|
await globalClient.batch(globalStmts, "write");
|
|
8485
|
+
try {
|
|
8486
|
+
const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
|
|
8487
|
+
await insertMemoryCardsForBatch2(batch);
|
|
8488
|
+
} catch {
|
|
8489
|
+
}
|
|
8177
8490
|
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
8178
8491
|
_pendingRecords.splice(0, batch.length);
|
|
8179
8492
|
try {
|
|
@@ -9432,7 +9745,7 @@ __export(identity_exports, {
|
|
|
9432
9745
|
import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
9433
9746
|
import { readdirSync as readdirSync4 } from "fs";
|
|
9434
9747
|
import path17 from "path";
|
|
9435
|
-
import { createHash as
|
|
9748
|
+
import { createHash as createHash4 } from "crypto";
|
|
9436
9749
|
function ensureDir() {
|
|
9437
9750
|
if (!existsSync16(IDENTITY_DIR2)) {
|
|
9438
9751
|
mkdirSync9(IDENTITY_DIR2, { recursive: true });
|
|
@@ -9479,7 +9792,7 @@ function parseFrontmatter(raw) {
|
|
|
9479
9792
|
};
|
|
9480
9793
|
}
|
|
9481
9794
|
function contentHash(content) {
|
|
9482
|
-
return
|
|
9795
|
+
return createHash4("sha256").update(content).digest("hex").slice(0, 16);
|
|
9483
9796
|
}
|
|
9484
9797
|
function getIdentity(agentId) {
|
|
9485
9798
|
const filePath = identityPath(agentId);
|
|
@@ -9695,7 +10008,7 @@ async function insertProcedures(procedures, timestamp, options) {
|
|
|
9695
10008
|
continue;
|
|
9696
10009
|
}
|
|
9697
10010
|
await client.execute({
|
|
9698
|
-
sql: `INSERT INTO
|
|
10011
|
+
sql: `INSERT INTO company_procedures (id, title, content, priority, domain, active, created_at, updated_at)
|
|
9699
10012
|
VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
9700
10013
|
args: [
|
|
9701
10014
|
randomUUID4(),
|
|
@@ -9777,7 +10090,7 @@ async function importIdentities(identities, updatedBy) {
|
|
|
9777
10090
|
async function getActiveProcedureTitles() {
|
|
9778
10091
|
const client = getClient();
|
|
9779
10092
|
const result = await client.execute({
|
|
9780
|
-
sql: "SELECT title FROM
|
|
10093
|
+
sql: "SELECT title FROM company_procedures WHERE active = 1",
|
|
9781
10094
|
args: []
|
|
9782
10095
|
});
|
|
9783
10096
|
return new Set(result.rows.map((row) => String(row.title)));
|
|
@@ -9800,7 +10113,7 @@ async function exportOrchestration(createdBy) {
|
|
|
9800
10113
|
args: []
|
|
9801
10114
|
});
|
|
9802
10115
|
const procedureResult = await client.execute({
|
|
9803
|
-
sql: "SELECT title, content, priority, domain FROM
|
|
10116
|
+
sql: "SELECT title, content, priority, domain FROM company_procedures WHERE active = 1",
|
|
9804
10117
|
args: []
|
|
9805
10118
|
});
|
|
9806
10119
|
const behaviors = behaviorResult.rows.map((row) => ({
|
|
@@ -14899,7 +15212,7 @@ var init_exe_rename = __esm({
|
|
|
14899
15212
|
// src/lib/model-downloader.ts
|
|
14900
15213
|
import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync26, unlinkSync as unlinkSync12, renameSync as renameSync6 } from "fs";
|
|
14901
15214
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
14902
|
-
import { createHash as
|
|
15215
|
+
import { createHash as createHash5 } from "crypto";
|
|
14903
15216
|
import path32 from "path";
|
|
14904
15217
|
async function downloadModel(opts) {
|
|
14905
15218
|
const { destDir, onProgress, fetchFn = globalThis.fetch } = opts;
|
|
@@ -14927,7 +15240,7 @@ async function downloadModel(opts) {
|
|
|
14927
15240
|
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
14928
15241
|
}
|
|
14929
15242
|
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
14930
|
-
const hash =
|
|
15243
|
+
const hash = createHash5("sha256");
|
|
14931
15244
|
const fileStream = createWriteStream(tmpPath);
|
|
14932
15245
|
const reader = response.body.getReader();
|
|
14933
15246
|
try {
|
|
@@ -14971,7 +15284,7 @@ Download attempt ${attempt} failed, retrying...
|
|
|
14971
15284
|
}
|
|
14972
15285
|
async function fileHash(filePath) {
|
|
14973
15286
|
return new Promise((resolve, reject) => {
|
|
14974
|
-
const hash =
|
|
15287
|
+
const hash = createHash5("sha256");
|
|
14975
15288
|
const stream = createReadStream2(filePath);
|
|
14976
15289
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
14977
15290
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
@@ -16793,12 +17106,64 @@ var init_update = __esm({
|
|
|
16793
17106
|
|
|
16794
17107
|
// src/lib/stack-update.ts
|
|
16795
17108
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
16796
|
-
import {
|
|
17109
|
+
import { createVerify, verify as verifySignature } from "crypto";
|
|
17110
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readdirSync as readdirSync10, readFileSync as readFileSync25, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "fs";
|
|
16797
17111
|
import http from "http";
|
|
16798
17112
|
import https from "https";
|
|
16799
17113
|
import path37 from "path";
|
|
16800
|
-
function
|
|
16801
|
-
|
|
17114
|
+
function isSignedEnvelope(value) {
|
|
17115
|
+
return !!value && typeof value === "object" && "manifest" in value && "signature" in value;
|
|
17116
|
+
}
|
|
17117
|
+
function canonicalizeStackManifest(manifest) {
|
|
17118
|
+
const clone = JSON.parse(JSON.stringify(manifest));
|
|
17119
|
+
delete clone.signature;
|
|
17120
|
+
return stableJson(clone);
|
|
17121
|
+
}
|
|
17122
|
+
function verifyStackManifestSignature(manifest, publicKeyPem) {
|
|
17123
|
+
const signature = manifest.signature;
|
|
17124
|
+
if (!signature) throw new Error("Stack manifest signature required but missing");
|
|
17125
|
+
const payload = Buffer.from(canonicalizeStackManifest(manifest));
|
|
17126
|
+
const sig = Buffer.from(signature.signature, "base64");
|
|
17127
|
+
let ok = false;
|
|
17128
|
+
if (signature.alg === "ed25519") {
|
|
17129
|
+
ok = verifySignature(null, payload, publicKeyPem, sig);
|
|
17130
|
+
} else if (signature.alg === "rsa-sha256") {
|
|
17131
|
+
const verifier = createVerify("RSA-SHA256");
|
|
17132
|
+
verifier.update(payload);
|
|
17133
|
+
verifier.end();
|
|
17134
|
+
ok = verifier.verify(publicKeyPem, sig);
|
|
17135
|
+
} else {
|
|
17136
|
+
throw new Error(`Unsupported stack manifest signature alg: ${signature.alg}`);
|
|
17137
|
+
}
|
|
17138
|
+
if (!ok) throw new Error("Stack manifest signature verification failed");
|
|
17139
|
+
}
|
|
17140
|
+
function stableJson(value) {
|
|
17141
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
17142
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
17143
|
+
const obj = value;
|
|
17144
|
+
return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${stableJson(obj[key])}`).join(",")}}`;
|
|
17145
|
+
}
|
|
17146
|
+
function findLatestBackupEnvFile(envFile) {
|
|
17147
|
+
const backupDir = path37.join(path37.dirname(envFile), ".exe-stack-backups");
|
|
17148
|
+
if (!existsSync30(backupDir)) return null;
|
|
17149
|
+
const backups = readdirSync10(backupDir).filter((name) => name.startsWith("env-") && name.endsWith(".bak")).sort();
|
|
17150
|
+
const latest = backups.at(-1);
|
|
17151
|
+
return latest ? path37.join(backupDir, latest) : null;
|
|
17152
|
+
}
|
|
17153
|
+
async function rollbackStackUpdate(options) {
|
|
17154
|
+
const exec2 = options.exec ?? defaultExec;
|
|
17155
|
+
const backupEnvFile = options.lockFile && existsSync30(options.lockFile) ? JSON.parse(readFileSync25(options.lockFile, "utf8")).backupEnvFile : void 0;
|
|
17156
|
+
const rollbackEnv = backupEnvFile && existsSync30(backupEnvFile) ? backupEnvFile : findLatestBackupEnvFile(options.envFile);
|
|
17157
|
+
if (!rollbackEnv) throw new Error(`No stack backup env found beside ${options.envFile}`);
|
|
17158
|
+
writeFileSync20(options.envFile, readFileSync25(rollbackEnv), { mode: 384 });
|
|
17159
|
+
const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
|
|
17160
|
+
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
17161
|
+
return { status: "rolled_back", targetVersion: "previous", changes: [], backupEnvFile: rollbackEnv, lockFile: options.lockFile ?? path37.join(path37.dirname(options.envFile), ".exe-stack-lock.json") };
|
|
17162
|
+
}
|
|
17163
|
+
function parseStackManifest(raw, publicKey) {
|
|
17164
|
+
const parsedRaw = JSON.parse(raw);
|
|
17165
|
+
const parsed = isSignedEnvelope(parsedRaw) ? { ...parsedRaw.manifest, signature: parsedRaw.signature } : parsedRaw;
|
|
17166
|
+
if (publicKey) verifyStackManifestSignature(parsed, publicKey);
|
|
16802
17167
|
if (parsed.schemaVersion !== 1) throw new Error("Unsupported stack manifest schemaVersion");
|
|
16803
17168
|
if (!parsed.latest || !parsed.stacks || typeof parsed.stacks !== "object") {
|
|
16804
17169
|
throw new Error("Invalid stack manifest: latest and stacks are required");
|
|
@@ -16816,9 +17181,9 @@ function parseStackManifest(raw) {
|
|
|
16816
17181
|
}
|
|
16817
17182
|
return parsed;
|
|
16818
17183
|
}
|
|
16819
|
-
async function loadStackManifest(ref, fetchText = defaultFetchText) {
|
|
16820
|
-
if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref));
|
|
16821
|
-
return parseStackManifest(readFileSync25(ref, "utf8"));
|
|
17184
|
+
async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey) {
|
|
17185
|
+
if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref), publicKey);
|
|
17186
|
+
return parseStackManifest(readFileSync25(ref, "utf8"), publicKey);
|
|
16822
17187
|
}
|
|
16823
17188
|
function parseEnv(raw) {
|
|
16824
17189
|
const env = /* @__PURE__ */ new Map();
|
|
@@ -16884,7 +17249,8 @@ Re-run with --allow-breaking ${missing.map((c) => c.id).join(",")}`
|
|
|
16884
17249
|
async function runStackUpdate(options) {
|
|
16885
17250
|
const exec2 = options.exec ?? defaultExec;
|
|
16886
17251
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16887
|
-
|
|
17252
|
+
if (options.rollback) return rollbackStackUpdate(options);
|
|
17253
|
+
const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey);
|
|
16888
17254
|
const envRaw = readFileSync25(options.envFile, "utf8");
|
|
16889
17255
|
const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
|
|
16890
17256
|
assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
|
|
@@ -16907,7 +17273,7 @@ async function runStackUpdate(options) {
|
|
|
16907
17273
|
exec2("docker", [...composeArgs, "pull"]);
|
|
16908
17274
|
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
16909
17275
|
await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
|
|
16910
|
-
writeFileSync20(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), services: plan.release.services }, null, 2) + "\n");
|
|
17276
|
+
writeFileSync20(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), backupEnvFile, services: plan.release.services }, null, 2) + "\n");
|
|
16911
17277
|
return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
|
|
16912
17278
|
} catch (err) {
|
|
16913
17279
|
writeFileSync20(options.envFile, envRaw, { mode: 384 });
|
|
@@ -16966,7 +17332,7 @@ function defaultStackPaths() {
|
|
|
16966
17332
|
return {
|
|
16967
17333
|
composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync30(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
|
|
16968
17334
|
envFile: process.env.EXE_STACK_ENV_FILE || (existsSync30(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
|
|
16969
|
-
manifestRef: process.env.EXE_STACK_MANIFEST || "https://
|
|
17335
|
+
manifestRef: process.env.EXE_STACK_MANIFEST || "https://update.askexe.com/stack-manifest.json"
|
|
16970
17336
|
};
|
|
16971
17337
|
}
|
|
16972
17338
|
var init_stack_update = __esm({
|
|
@@ -16989,6 +17355,7 @@ function parseArgs4(args2) {
|
|
|
16989
17355
|
envFile: defaults.envFile,
|
|
16990
17356
|
dryRun: false,
|
|
16991
17357
|
check: false,
|
|
17358
|
+
rollback: false,
|
|
16992
17359
|
yes: false,
|
|
16993
17360
|
allowedBreakingChangeIds: []
|
|
16994
17361
|
};
|
|
@@ -17004,6 +17371,9 @@ function parseArgs4(args2) {
|
|
|
17004
17371
|
else if (arg === "--env-file") opts.envFile = next();
|
|
17005
17372
|
else if (arg.startsWith("--env-file=")) opts.envFile = arg.split("=").slice(1).join("=");
|
|
17006
17373
|
else if (arg === "--lock-file") opts.lockFile = next();
|
|
17374
|
+
else if (arg === "--public-key") opts.manifestPublicKey = readFileSync26(next(), "utf8");
|
|
17375
|
+
else if (arg.startsWith("--public-key=")) opts.manifestPublicKey = readFileSync26(arg.split("=").slice(1).join("="), "utf8");
|
|
17376
|
+
else if (arg === "--rollback") opts.rollback = true;
|
|
17007
17377
|
else if (arg === "--dry-run") opts.dryRun = true;
|
|
17008
17378
|
else if (arg === "--check") opts.check = true;
|
|
17009
17379
|
else if (arg === "--yes" || arg === "-y") opts.yes = true;
|
|
@@ -17025,13 +17395,15 @@ Usage:
|
|
|
17025
17395
|
exe-os stack-update [--manifest <path-or-url>] [--target <version>] [--yes]
|
|
17026
17396
|
|
|
17027
17397
|
Options:
|
|
17028
|
-
--manifest <ref> Stack manifest JSON path or URL (default:
|
|
17398
|
+
--manifest <ref> Stack manifest JSON path or URL (default: update.askexe.com)
|
|
17029
17399
|
--target <version> Stack version to install (default: manifest.latest)
|
|
17030
17400
|
--compose-file <path> docker-compose.yml path (default: ./docker-compose.yml or /opt/exe-stack/docker-compose.yml)
|
|
17031
17401
|
--env-file <path> .env path (default: ./.env or /opt/exe-stack/.env)
|
|
17032
17402
|
--lock-file <path> Lock file path (default: beside .env)
|
|
17033
17403
|
--check Print available changes only
|
|
17034
17404
|
--dry-run Plan only; do not run Docker
|
|
17405
|
+
--public-key <path> PEM public key for signed manifest verification
|
|
17406
|
+
--rollback Restore latest backed-up .env and restart stack
|
|
17035
17407
|
--allow-breaking <ids> Confirm breaking changes, comma-separated
|
|
17036
17408
|
-y, --yes Non-interactive confirmation
|
|
17037
17409
|
`);
|
|
@@ -17059,7 +17431,16 @@ function printBreaking(changes) {
|
|
|
17059
17431
|
}
|
|
17060
17432
|
async function main3() {
|
|
17061
17433
|
const opts = parseArgs4(process.argv.slice(2));
|
|
17062
|
-
|
|
17434
|
+
if (opts.rollback) {
|
|
17435
|
+
if (!opts.yes) {
|
|
17436
|
+
console.error("Refusing to rollback without --yes.");
|
|
17437
|
+
process.exit(2);
|
|
17438
|
+
}
|
|
17439
|
+
const result2 = await runStackUpdate(opts);
|
|
17440
|
+
console.log(`\u2705 Stack rollback attempted using backup: ${result2.backupEnvFile ?? "latest backup"}`);
|
|
17441
|
+
return;
|
|
17442
|
+
}
|
|
17443
|
+
const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey);
|
|
17063
17444
|
const envRaw = readFileSync26(opts.envFile, "utf8");
|
|
17064
17445
|
const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
|
|
17065
17446
|
console.log(`Exe OS stack target: ${plan.targetVersion}`);
|
|
@@ -25494,16 +25875,16 @@ var init_useAgentLoop = __esm({
|
|
|
25494
25875
|
}
|
|
25495
25876
|
});
|
|
25496
25877
|
|
|
25497
|
-
// src/
|
|
25878
|
+
// src/lib/providers/anthropic.ts
|
|
25498
25879
|
import Anthropic from "@anthropic-ai/sdk";
|
|
25499
25880
|
var AnthropicProvider;
|
|
25500
25881
|
var init_anthropic = __esm({
|
|
25501
|
-
"src/
|
|
25882
|
+
"src/lib/providers/anthropic.ts"() {
|
|
25502
25883
|
"use strict";
|
|
25503
25884
|
AnthropicProvider = class {
|
|
25504
25885
|
name;
|
|
25505
|
-
client;
|
|
25506
25886
|
defaultModel;
|
|
25887
|
+
client;
|
|
25507
25888
|
constructor(name, config) {
|
|
25508
25889
|
this.name = name;
|
|
25509
25890
|
this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
|
|
@@ -25596,17 +25977,17 @@ var init_anthropic = __esm({
|
|
|
25596
25977
|
}
|
|
25597
25978
|
});
|
|
25598
25979
|
|
|
25599
|
-
// src/
|
|
25980
|
+
// src/lib/providers/openai-compat.ts
|
|
25600
25981
|
import OpenAI from "openai";
|
|
25601
25982
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
25602
25983
|
var OpenAICompatProvider;
|
|
25603
25984
|
var init_openai_compat = __esm({
|
|
25604
|
-
"src/
|
|
25985
|
+
"src/lib/providers/openai-compat.ts"() {
|
|
25605
25986
|
"use strict";
|
|
25606
25987
|
OpenAICompatProvider = class {
|
|
25607
25988
|
name;
|
|
25608
|
-
client;
|
|
25609
25989
|
defaultModel;
|
|
25990
|
+
client;
|
|
25610
25991
|
constructor(name, config) {
|
|
25611
25992
|
this.name = name;
|
|
25612
25993
|
this.defaultModel = config.defaultModel ?? "gpt-4o";
|
|
@@ -25738,7 +26119,7 @@ var init_openai_compat = __esm({
|
|
|
25738
26119
|
}
|
|
25739
26120
|
});
|
|
25740
26121
|
|
|
25741
|
-
// src/
|
|
26122
|
+
// src/lib/providers/factory.ts
|
|
25742
26123
|
var factory_exports = {};
|
|
25743
26124
|
__export(factory_exports, {
|
|
25744
26125
|
OPENCODE_ANTHROPIC_MODELS: () => OPENCODE_ANTHROPIC_MODELS,
|
|
@@ -25789,7 +26170,7 @@ function createProvider(opts) {
|
|
|
25789
26170
|
}
|
|
25790
26171
|
var OPENCODE_BASE_URL, OPENCODE_ANTHROPIC_MODELS, OPENCODE_OPENAI_MODELS, OPENAI_BASE_URL;
|
|
25791
26172
|
var init_factory = __esm({
|
|
25792
|
-
"src/
|
|
26173
|
+
"src/lib/providers/factory.ts"() {
|
|
25793
26174
|
"use strict";
|
|
25794
26175
|
init_anthropic();
|
|
25795
26176
|
init_openai_compat();
|
|
@@ -31434,8 +31815,8 @@ function verifyOpenCodeHooks(homeDir = os19.homedir()) {
|
|
|
31434
31815
|
if (!existsSync32(pluginPath)) return false;
|
|
31435
31816
|
try {
|
|
31436
31817
|
const plugin = readFileSync28(pluginPath, "utf-8");
|
|
31437
|
-
if (!plugin.includes(
|
|
31438
|
-
if (
|
|
31818
|
+
if (!plugin.includes(EXE_HOOK_FILES.postToolCombined)) return false;
|
|
31819
|
+
if (textHasLegacySplitPostToolHook(plugin)) return false;
|
|
31439
31820
|
} catch {
|
|
31440
31821
|
return false;
|
|
31441
31822
|
}
|
|
@@ -31459,6 +31840,7 @@ var init_installer2 = __esm({
|
|
|
31459
31840
|
"use strict";
|
|
31460
31841
|
init_installer();
|
|
31461
31842
|
init_plugin_template();
|
|
31843
|
+
init_runtime_hook_manifest();
|
|
31462
31844
|
}
|
|
31463
31845
|
});
|
|
31464
31846
|
|
|
@@ -31507,7 +31889,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
31507
31889
|
}
|
|
31508
31890
|
]
|
|
31509
31891
|
},
|
|
31510
|
-
marker:
|
|
31892
|
+
marker: EXE_HOOKS.sessionStart
|
|
31511
31893
|
},
|
|
31512
31894
|
{
|
|
31513
31895
|
event: "PostToolUse",
|
|
@@ -31522,7 +31904,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
31522
31904
|
}
|
|
31523
31905
|
]
|
|
31524
31906
|
},
|
|
31525
|
-
marker:
|
|
31907
|
+
marker: EXE_HOOKS.postToolCombined
|
|
31526
31908
|
},
|
|
31527
31909
|
{
|
|
31528
31910
|
event: "UserPromptSubmit",
|
|
@@ -31536,7 +31918,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
31536
31918
|
}
|
|
31537
31919
|
]
|
|
31538
31920
|
},
|
|
31539
|
-
marker:
|
|
31921
|
+
marker: EXE_HOOKS.promptSubmit
|
|
31540
31922
|
},
|
|
31541
31923
|
{
|
|
31542
31924
|
event: "Stop",
|
|
@@ -31548,7 +31930,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
31548
31930
|
}
|
|
31549
31931
|
]
|
|
31550
31932
|
},
|
|
31551
|
-
marker:
|
|
31933
|
+
marker: EXE_HOOKS.stop
|
|
31552
31934
|
},
|
|
31553
31935
|
{
|
|
31554
31936
|
event: "PreToolUse",
|
|
@@ -31561,21 +31943,16 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
31561
31943
|
}
|
|
31562
31944
|
]
|
|
31563
31945
|
},
|
|
31564
|
-
marker:
|
|
31946
|
+
marker: EXE_HOOKS.preToolUse
|
|
31565
31947
|
}
|
|
31566
31948
|
];
|
|
31567
31949
|
let added = 0;
|
|
31568
31950
|
let skipped = 0;
|
|
31569
|
-
const legacyPostToolMarkers = [
|
|
31570
|
-
"dist/hooks/ingest.js",
|
|
31571
|
-
"dist/hooks/error-recall.js",
|
|
31572
|
-
"dist/hooks/ingest-worker.js"
|
|
31573
|
-
];
|
|
31574
31951
|
const postToolGroups = hooksJson.hooks["PostToolUse"];
|
|
31575
31952
|
if (Array.isArray(postToolGroups)) {
|
|
31576
31953
|
hooksJson.hooks["PostToolUse"] = postToolGroups.map((g) => ({
|
|
31577
31954
|
...g,
|
|
31578
|
-
hooks: g.hooks.filter((h) => !
|
|
31955
|
+
hooks: g.hooks.filter((h) => !isLegacySplitPostToolCommand(h.command))
|
|
31579
31956
|
})).filter((g) => g.hooks.length > 0);
|
|
31580
31957
|
}
|
|
31581
31958
|
for (const { event, group, marker } of hooksToRegister) {
|
|
@@ -31618,11 +31995,11 @@ function verifyCodexHooks(homeDir = os20.homedir()) {
|
|
|
31618
31995
|
}
|
|
31619
31996
|
}
|
|
31620
31997
|
const postToolCommands = (hooksJson.hooks.PostToolUse ?? []).flatMap((g) => g.hooks.map((h) => h.command));
|
|
31621
|
-
if (!postToolCommands.some((cmd) => cmd.includes(
|
|
31998
|
+
if (!postToolCommands.some((cmd) => cmd.includes(EXE_HOOKS.postToolCombined))) {
|
|
31622
31999
|
return false;
|
|
31623
32000
|
}
|
|
31624
32001
|
if (postToolCommands.some(
|
|
31625
|
-
(cmd) =>
|
|
32002
|
+
(cmd) => isLegacySplitPostToolCommand(cmd)
|
|
31626
32003
|
)) {
|
|
31627
32004
|
return false;
|
|
31628
32005
|
}
|
|
@@ -31783,6 +32160,7 @@ var init_installer3 = __esm({
|
|
|
31783
32160
|
"use strict";
|
|
31784
32161
|
init_installer();
|
|
31785
32162
|
init_preferences();
|
|
32163
|
+
init_runtime_hook_manifest();
|
|
31786
32164
|
DEFAULT_CODEX_STATUS_LINE = [
|
|
31787
32165
|
"model-with-reasoning",
|
|
31788
32166
|
"current-dir",
|
|
@@ -32002,7 +32380,7 @@ var init_mcp_diagnostics = __esm({
|
|
|
32002
32380
|
});
|
|
32003
32381
|
|
|
32004
32382
|
// src/bin/cli.ts
|
|
32005
|
-
import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync21, readdirSync as
|
|
32383
|
+
import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync21, readdirSync as readdirSync11, rmSync } from "fs";
|
|
32006
32384
|
import path49 from "path";
|
|
32007
32385
|
import os22 from "os";
|
|
32008
32386
|
var args = process.argv.slice(2);
|
|
@@ -32451,7 +32829,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
32451
32829
|
if (existsSync35(skillsDir)) {
|
|
32452
32830
|
let skillCount = 0;
|
|
32453
32831
|
try {
|
|
32454
|
-
const entries =
|
|
32832
|
+
const entries = readdirSync11(skillsDir);
|
|
32455
32833
|
for (const entry of entries) {
|
|
32456
32834
|
if (entry.startsWith("exe")) {
|
|
32457
32835
|
const fullPath = path49.join(skillsDir, entry);
|
|
@@ -32484,7 +32862,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
32484
32862
|
if (existsSync35(agentsDir)) {
|
|
32485
32863
|
let agentCount = 0;
|
|
32486
32864
|
try {
|
|
32487
|
-
const entries =
|
|
32865
|
+
const entries = readdirSync11(agentsDir).filter((f) => f.endsWith(".md"));
|
|
32488
32866
|
let knownNames = /* @__PURE__ */ new Set();
|
|
32489
32867
|
const rosterPath = path49.join(exeOsDir, "exe-employees.json");
|
|
32490
32868
|
if (existsSync35(rosterPath)) {
|
|
@@ -32512,7 +32890,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
32512
32890
|
if (existsSync35(projectsDir)) {
|
|
32513
32891
|
let projectCount = 0;
|
|
32514
32892
|
try {
|
|
32515
|
-
const projects =
|
|
32893
|
+
const projects = readdirSync11(projectsDir);
|
|
32516
32894
|
for (const proj of projects) {
|
|
32517
32895
|
const projSettings = path49.join(projectsDir, proj, "settings.json");
|
|
32518
32896
|
if (!existsSync35(projSettings)) continue;
|