@devness/useai 0.4.4 → 0.4.5
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/LICENSE +21 -0
- package/dist/index.js +380 -92
- package/package.json +11 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 useai.dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
CHANGED
|
@@ -83,7 +83,7 @@ var init_types = __esm({
|
|
|
83
83
|
// ../shared/dist/constants/paths.js
|
|
84
84
|
import { join } from "path";
|
|
85
85
|
import { homedir } from "os";
|
|
86
|
-
var USEAI_DIR, DATA_DIR, ACTIVE_DIR, SEALED_DIR, KEYSTORE_FILE, CONFIG_FILE, SESSIONS_FILE, MILESTONES_FILE, DAEMON_PID_FILE, DAEMON_PORT, DAEMON_LOG_FILE, DAEMON_MCP_URL, DAEMON_HEALTH_URL, LAUNCHD_PLIST_PATH, SYSTEMD_SERVICE_PATH, WINDOWS_STARTUP_SCRIPT_PATH;
|
|
86
|
+
var USEAI_DIR, DATA_DIR, ACTIVE_DIR, SEALED_DIR, KEYSTORE_FILE, CONFIG_FILE, SESSIONS_FILE, MILESTONES_FILE, DAEMON_PID_FILE, USEAI_HOOKS_DIR, DAEMON_PORT, DAEMON_LOG_FILE, DAEMON_MCP_URL, DAEMON_HEALTH_URL, LAUNCHD_PLIST_PATH, SYSTEMD_SERVICE_PATH, WINDOWS_STARTUP_SCRIPT_PATH;
|
|
87
87
|
var init_paths = __esm({
|
|
88
88
|
"../shared/dist/constants/paths.js"() {
|
|
89
89
|
"use strict";
|
|
@@ -96,6 +96,7 @@ var init_paths = __esm({
|
|
|
96
96
|
SESSIONS_FILE = join(DATA_DIR, "sessions.json");
|
|
97
97
|
MILESTONES_FILE = join(DATA_DIR, "milestones.json");
|
|
98
98
|
DAEMON_PID_FILE = join(USEAI_DIR, "daemon.pid");
|
|
99
|
+
USEAI_HOOKS_DIR = join(USEAI_DIR, "hooks");
|
|
99
100
|
DAEMON_PORT = 19200;
|
|
100
101
|
DAEMON_LOG_FILE = join(USEAI_DIR, "daemon.log");
|
|
101
102
|
DAEMON_MCP_URL = `http://127.0.0.1:${DAEMON_PORT}/mcp`;
|
|
@@ -111,7 +112,7 @@ var VERSION;
|
|
|
111
112
|
var init_version = __esm({
|
|
112
113
|
"../shared/dist/constants/version.js"() {
|
|
113
114
|
"use strict";
|
|
114
|
-
VERSION = "0.4.
|
|
115
|
+
VERSION = "0.4.5";
|
|
115
116
|
}
|
|
116
117
|
});
|
|
117
118
|
|
|
@@ -836,6 +837,136 @@ var init_daemon = __esm({
|
|
|
836
837
|
}
|
|
837
838
|
});
|
|
838
839
|
|
|
840
|
+
// ../shared/dist/hooks/claude-code.js
|
|
841
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, chmodSync } from "fs";
|
|
842
|
+
import { join as join3 } from "path";
|
|
843
|
+
import { homedir as homedir3 } from "os";
|
|
844
|
+
function readSettings() {
|
|
845
|
+
if (!existsSync5(CLAUDE_SETTINGS_PATH))
|
|
846
|
+
return {};
|
|
847
|
+
try {
|
|
848
|
+
return JSON.parse(readFileSync3(CLAUDE_SETTINGS_PATH, "utf-8"));
|
|
849
|
+
} catch {
|
|
850
|
+
return {};
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
function writeSettings(settings) {
|
|
854
|
+
mkdirSync3(join3(homedir3(), ".claude"), { recursive: true });
|
|
855
|
+
writeFileSync3(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n");
|
|
856
|
+
}
|
|
857
|
+
function installClaudeCodeHooks() {
|
|
858
|
+
mkdirSync3(USEAI_HOOKS_DIR, { recursive: true });
|
|
859
|
+
writeFileSync3(STOP_GUARD_PATH, STOP_GUARD_SCRIPT);
|
|
860
|
+
try {
|
|
861
|
+
chmodSync(STOP_GUARD_PATH, "755");
|
|
862
|
+
} catch {
|
|
863
|
+
}
|
|
864
|
+
const settings = readSettings();
|
|
865
|
+
const hooks = settings["hooks"] ?? {};
|
|
866
|
+
const stopCmd = `node "${STOP_GUARD_PATH}"`;
|
|
867
|
+
const sealCmd = `curl -sf -X POST http://127.0.0.1:${DAEMON_PORT}/api/seal-active --max-time 3 2>/dev/null || true`;
|
|
868
|
+
let changed = false;
|
|
869
|
+
if (!hooks["Stop"])
|
|
870
|
+
hooks["Stop"] = [];
|
|
871
|
+
const stopArr = hooks["Stop"];
|
|
872
|
+
const hasStop = stopArr.some((g) => {
|
|
873
|
+
const inner = g["hooks"];
|
|
874
|
+
return inner?.some((h) => h["command"]?.includes("stop-guard"));
|
|
875
|
+
});
|
|
876
|
+
if (!hasStop) {
|
|
877
|
+
stopArr.push({ hooks: [{ type: "command", command: stopCmd, timeout: 10 }] });
|
|
878
|
+
changed = true;
|
|
879
|
+
}
|
|
880
|
+
if (!hooks["SessionEnd"])
|
|
881
|
+
hooks["SessionEnd"] = [];
|
|
882
|
+
const endArr = hooks["SessionEnd"];
|
|
883
|
+
const hasEnd = endArr.some((g) => {
|
|
884
|
+
const inner = g["hooks"];
|
|
885
|
+
return inner?.some((h) => h["command"]?.includes("seal-active"));
|
|
886
|
+
});
|
|
887
|
+
if (!hasEnd) {
|
|
888
|
+
endArr.push({ hooks: [{ type: "command", command: sealCmd, timeout: 5 }] });
|
|
889
|
+
changed = true;
|
|
890
|
+
}
|
|
891
|
+
settings["hooks"] = hooks;
|
|
892
|
+
writeSettings(settings);
|
|
893
|
+
return changed;
|
|
894
|
+
}
|
|
895
|
+
function removeClaudeCodeHooks() {
|
|
896
|
+
if (existsSync5(CLAUDE_SETTINGS_PATH)) {
|
|
897
|
+
try {
|
|
898
|
+
const settings = readSettings();
|
|
899
|
+
const hooks = settings["hooks"];
|
|
900
|
+
if (hooks) {
|
|
901
|
+
if (hooks["Stop"]) {
|
|
902
|
+
hooks["Stop"] = hooks["Stop"].filter((g) => {
|
|
903
|
+
const inner = g["hooks"];
|
|
904
|
+
return !inner?.some((h) => h["command"]?.includes("stop-guard"));
|
|
905
|
+
});
|
|
906
|
+
if (hooks["Stop"].length === 0)
|
|
907
|
+
delete hooks["Stop"];
|
|
908
|
+
}
|
|
909
|
+
if (hooks["SessionEnd"]) {
|
|
910
|
+
hooks["SessionEnd"] = hooks["SessionEnd"].filter((g) => {
|
|
911
|
+
const inner = g["hooks"];
|
|
912
|
+
return !inner?.some((h) => h["command"]?.includes("seal-active"));
|
|
913
|
+
});
|
|
914
|
+
if (hooks["SessionEnd"].length === 0)
|
|
915
|
+
delete hooks["SessionEnd"];
|
|
916
|
+
}
|
|
917
|
+
if (Object.keys(hooks).length === 0)
|
|
918
|
+
delete settings["hooks"];
|
|
919
|
+
}
|
|
920
|
+
writeSettings(settings);
|
|
921
|
+
} catch {
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
try {
|
|
925
|
+
if (existsSync5(STOP_GUARD_PATH))
|
|
926
|
+
unlinkSync3(STOP_GUARD_PATH);
|
|
927
|
+
} catch {
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
var STOP_GUARD_PATH, CLAUDE_SETTINGS_PATH, STOP_GUARD_SCRIPT;
|
|
931
|
+
var init_claude_code = __esm({
|
|
932
|
+
"../shared/dist/hooks/claude-code.js"() {
|
|
933
|
+
"use strict";
|
|
934
|
+
init_paths();
|
|
935
|
+
STOP_GUARD_PATH = join3(USEAI_HOOKS_DIR, "stop-guard.js");
|
|
936
|
+
CLAUDE_SETTINGS_PATH = join3(homedir3(), ".claude", "settings.json");
|
|
937
|
+
STOP_GUARD_SCRIPT = `#!/usr/bin/env node
|
|
938
|
+
'use strict';
|
|
939
|
+
const { readFileSync } = require('node:fs');
|
|
940
|
+
let input = '';
|
|
941
|
+
process.stdin.setEncoding('utf-8');
|
|
942
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
943
|
+
process.stdin.on('end', () => {
|
|
944
|
+
try {
|
|
945
|
+
const hook = JSON.parse(input);
|
|
946
|
+
if (hook.stop_hook_active) process.exit(0);
|
|
947
|
+
if (!hook.transcript_path) process.exit(0);
|
|
948
|
+
let transcript;
|
|
949
|
+
try { transcript = readFileSync(hook.transcript_path, 'utf-8'); } catch { process.exit(0); }
|
|
950
|
+
if (!transcript.includes('useai_session_start')) process.exit(0);
|
|
951
|
+
if (transcript.includes('useai_session_end')) process.exit(0);
|
|
952
|
+
process.stdout.write(JSON.stringify({
|
|
953
|
+
decision: 'block',
|
|
954
|
+
reason: 'Before ending, call useai_session_end with milestones (generic descriptions of accomplishments), languages used, and files_touched_count.'
|
|
955
|
+
}));
|
|
956
|
+
} catch { process.exit(0); }
|
|
957
|
+
});
|
|
958
|
+
`;
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
// ../shared/dist/hooks/index.js
|
|
963
|
+
var init_hooks = __esm({
|
|
964
|
+
"../shared/dist/hooks/index.js"() {
|
|
965
|
+
"use strict";
|
|
966
|
+
init_claude_code();
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
|
|
839
970
|
// ../shared/dist/index.js
|
|
840
971
|
var init_dist = __esm({
|
|
841
972
|
"../shared/dist/index.js"() {
|
|
@@ -846,12 +977,13 @@ var init_dist = __esm({
|
|
|
846
977
|
init_validation();
|
|
847
978
|
init_utils();
|
|
848
979
|
init_daemon();
|
|
980
|
+
init_hooks();
|
|
849
981
|
}
|
|
850
982
|
});
|
|
851
983
|
|
|
852
984
|
// src/session-state.ts
|
|
853
|
-
import { appendFileSync, existsSync as
|
|
854
|
-
import { join as
|
|
985
|
+
import { appendFileSync, existsSync as existsSync6 } from "fs";
|
|
986
|
+
import { join as join4 } from "path";
|
|
855
987
|
var SessionState;
|
|
856
988
|
var init_session_state = __esm({
|
|
857
989
|
"src/session-state.ts"() {
|
|
@@ -901,7 +1033,7 @@ var init_session_state = __esm({
|
|
|
901
1033
|
}
|
|
902
1034
|
initializeKeystore() {
|
|
903
1035
|
ensureDir();
|
|
904
|
-
if (
|
|
1036
|
+
if (existsSync6(KEYSTORE_FILE)) {
|
|
905
1037
|
const ks = readJson(KEYSTORE_FILE, null);
|
|
906
1038
|
if (ks) {
|
|
907
1039
|
try {
|
|
@@ -919,7 +1051,7 @@ var init_session_state = __esm({
|
|
|
919
1051
|
}
|
|
920
1052
|
/** Path to this session's chain file in the active directory */
|
|
921
1053
|
sessionChainPath() {
|
|
922
|
-
return
|
|
1054
|
+
return join4(ACTIVE_DIR, `${this.sessionId}.jsonl`);
|
|
923
1055
|
}
|
|
924
1056
|
appendToChain(type, data) {
|
|
925
1057
|
const record = buildChainRecord(type, this.sessionId, data, this.chainTipHash, this.signingKey);
|
|
@@ -936,8 +1068,8 @@ var init_session_state = __esm({
|
|
|
936
1068
|
// src/register-tools.ts
|
|
937
1069
|
import { z as z2 } from "zod";
|
|
938
1070
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
939
|
-
import { existsSync as
|
|
940
|
-
import { join as
|
|
1071
|
+
import { existsSync as existsSync7, renameSync as renameSync2 } from "fs";
|
|
1072
|
+
import { join as join5 } from "path";
|
|
941
1073
|
function getConfig() {
|
|
942
1074
|
return readJson(CONFIG_FILE, {
|
|
943
1075
|
milestone_tracking: true,
|
|
@@ -1043,10 +1175,10 @@ Session: ${session2.sessionId.slice(0, 8)} \xB7 Chain: ${record.hash.slice(0, 12
|
|
|
1043
1175
|
seal: sealData,
|
|
1044
1176
|
seal_signature: sealSignature
|
|
1045
1177
|
});
|
|
1046
|
-
const activePath =
|
|
1047
|
-
const sealedPath =
|
|
1178
|
+
const activePath = join5(ACTIVE_DIR, `${session2.sessionId}.jsonl`);
|
|
1179
|
+
const sealedPath = join5(SEALED_DIR, `${session2.sessionId}.jsonl`);
|
|
1048
1180
|
try {
|
|
1049
|
-
if (
|
|
1181
|
+
if (existsSync7(activePath)) {
|
|
1050
1182
|
renameSync2(activePath, sealedPath);
|
|
1051
1183
|
}
|
|
1052
1184
|
} catch {
|
|
@@ -1126,9 +1258,9 @@ var init_register_tools = __esm({
|
|
|
1126
1258
|
|
|
1127
1259
|
// src/tools.ts
|
|
1128
1260
|
import { execSync as execSync3 } from "child_process";
|
|
1129
|
-
import { existsSync as
|
|
1130
|
-
import { dirname as dirname2, join as
|
|
1131
|
-
import { homedir as
|
|
1261
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
1262
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
1263
|
+
import { homedir as homedir4 } from "os";
|
|
1132
1264
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
1133
1265
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
1134
1266
|
function installStandardHttp(configPath) {
|
|
@@ -1156,9 +1288,9 @@ function hasBinary(name) {
|
|
|
1156
1288
|
}
|
|
1157
1289
|
}
|
|
1158
1290
|
function readJsonFile(path) {
|
|
1159
|
-
if (!
|
|
1291
|
+
if (!existsSync8(path)) return {};
|
|
1160
1292
|
try {
|
|
1161
|
-
const raw =
|
|
1293
|
+
const raw = readFileSync4(path, "utf-8").trim();
|
|
1162
1294
|
if (!raw) return {};
|
|
1163
1295
|
return JSON.parse(raw);
|
|
1164
1296
|
} catch {
|
|
@@ -1166,8 +1298,8 @@ function readJsonFile(path) {
|
|
|
1166
1298
|
}
|
|
1167
1299
|
}
|
|
1168
1300
|
function writeJsonFile(path, data) {
|
|
1169
|
-
|
|
1170
|
-
|
|
1301
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
1302
|
+
writeFileSync4(path, JSON.stringify(data, null, 2) + "\n");
|
|
1171
1303
|
}
|
|
1172
1304
|
function isConfiguredStandard(configPath) {
|
|
1173
1305
|
const config = readJsonFile(configPath);
|
|
@@ -1248,9 +1380,9 @@ function removeZed(configPath) {
|
|
|
1248
1380
|
}
|
|
1249
1381
|
}
|
|
1250
1382
|
function readTomlFile(path) {
|
|
1251
|
-
if (!
|
|
1383
|
+
if (!existsSync8(path)) return {};
|
|
1252
1384
|
try {
|
|
1253
|
-
const raw =
|
|
1385
|
+
const raw = readFileSync4(path, "utf-8").trim();
|
|
1254
1386
|
if (!raw) return {};
|
|
1255
1387
|
return parseToml(raw);
|
|
1256
1388
|
} catch {
|
|
@@ -1258,8 +1390,8 @@ function readTomlFile(path) {
|
|
|
1258
1390
|
}
|
|
1259
1391
|
}
|
|
1260
1392
|
function writeTomlFile(path, data) {
|
|
1261
|
-
|
|
1262
|
-
|
|
1393
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
1394
|
+
writeFileSync4(path, stringifyToml(data) + "\n");
|
|
1263
1395
|
}
|
|
1264
1396
|
function isConfiguredToml(configPath) {
|
|
1265
1397
|
const config = readTomlFile(configPath);
|
|
@@ -1287,9 +1419,9 @@ function removeToml(configPath) {
|
|
|
1287
1419
|
}
|
|
1288
1420
|
}
|
|
1289
1421
|
function readYamlFile(path) {
|
|
1290
|
-
if (!
|
|
1422
|
+
if (!existsSync8(path)) return {};
|
|
1291
1423
|
try {
|
|
1292
|
-
const raw =
|
|
1424
|
+
const raw = readFileSync4(path, "utf-8").trim();
|
|
1293
1425
|
if (!raw) return {};
|
|
1294
1426
|
return parseYaml(raw) ?? {};
|
|
1295
1427
|
} catch {
|
|
@@ -1297,8 +1429,8 @@ function readYamlFile(path) {
|
|
|
1297
1429
|
}
|
|
1298
1430
|
}
|
|
1299
1431
|
function writeYamlFile(path, data) {
|
|
1300
|
-
|
|
1301
|
-
|
|
1432
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
1433
|
+
writeFileSync4(path, stringifyYaml(data));
|
|
1302
1434
|
}
|
|
1303
1435
|
function isConfiguredYaml(configPath) {
|
|
1304
1436
|
const config = readYamlFile(configPath);
|
|
@@ -1332,49 +1464,49 @@ function removeYaml(configPath) {
|
|
|
1332
1464
|
}
|
|
1333
1465
|
}
|
|
1334
1466
|
function hasInstructionsBlock(filePath) {
|
|
1335
|
-
if (!
|
|
1467
|
+
if (!existsSync8(filePath)) return false;
|
|
1336
1468
|
try {
|
|
1337
|
-
return
|
|
1469
|
+
return readFileSync4(filePath, "utf-8").includes(INSTRUCTIONS_START);
|
|
1338
1470
|
} catch {
|
|
1339
1471
|
return false;
|
|
1340
1472
|
}
|
|
1341
1473
|
}
|
|
1342
1474
|
function injectInstructions(config) {
|
|
1343
|
-
|
|
1475
|
+
mkdirSync4(dirname2(config.path), { recursive: true });
|
|
1344
1476
|
if (config.method === "create") {
|
|
1345
|
-
|
|
1477
|
+
writeFileSync4(config.path, USEAI_INSTRUCTIONS + "\n");
|
|
1346
1478
|
return;
|
|
1347
1479
|
}
|
|
1348
1480
|
if (hasInstructionsBlock(config.path)) return;
|
|
1349
1481
|
let existing = "";
|
|
1350
|
-
if (
|
|
1351
|
-
existing =
|
|
1482
|
+
if (existsSync8(config.path)) {
|
|
1483
|
+
existing = readFileSync4(config.path, "utf-8");
|
|
1352
1484
|
}
|
|
1353
1485
|
const separator = existing && !existing.endsWith("\n") ? "\n\n" : existing ? "\n" : "";
|
|
1354
|
-
|
|
1486
|
+
writeFileSync4(config.path, existing + separator + USEAI_INSTRUCTIONS_BLOCK + "\n");
|
|
1355
1487
|
}
|
|
1356
1488
|
function removeInstructions(config) {
|
|
1357
1489
|
if (config.method === "create") {
|
|
1358
|
-
if (
|
|
1490
|
+
if (existsSync8(config.path)) {
|
|
1359
1491
|
try {
|
|
1360
|
-
|
|
1492
|
+
unlinkSync4(config.path);
|
|
1361
1493
|
} catch {
|
|
1362
1494
|
}
|
|
1363
1495
|
}
|
|
1364
1496
|
return;
|
|
1365
1497
|
}
|
|
1366
|
-
if (!
|
|
1498
|
+
if (!existsSync8(config.path)) return;
|
|
1367
1499
|
try {
|
|
1368
|
-
const content =
|
|
1500
|
+
const content = readFileSync4(config.path, "utf-8");
|
|
1369
1501
|
const escaped = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1370
1502
|
const regex = new RegExp(
|
|
1371
1503
|
`\\n?${escaped(INSTRUCTIONS_START)}[\\s\\S]*?${escaped(INSTRUCTIONS_END)}\\n?`
|
|
1372
1504
|
);
|
|
1373
1505
|
const cleaned = content.replace(regex, "").trim();
|
|
1374
1506
|
if (cleaned) {
|
|
1375
|
-
|
|
1507
|
+
writeFileSync4(config.path, cleaned + "\n");
|
|
1376
1508
|
} else {
|
|
1377
|
-
|
|
1509
|
+
unlinkSync4(config.path);
|
|
1378
1510
|
}
|
|
1379
1511
|
} catch {
|
|
1380
1512
|
}
|
|
@@ -1450,7 +1582,7 @@ var init_tools = __esm({
|
|
|
1450
1582
|
};
|
|
1451
1583
|
MCP_HTTP_URL = DAEMON_MCP_URL;
|
|
1452
1584
|
MCP_HTTP_ENTRY = { type: "http", url: MCP_HTTP_URL };
|
|
1453
|
-
home =
|
|
1585
|
+
home = homedir4();
|
|
1454
1586
|
INSTRUCTIONS_START = "<!-- useai:start -->";
|
|
1455
1587
|
INSTRUCTIONS_END = "<!-- useai:end -->";
|
|
1456
1588
|
USEAI_INSTRUCTIONS = [
|
|
@@ -1470,23 +1602,23 @@ ${INSTRUCTIONS_END}`;
|
|
|
1470
1602
|
toml: { isConfigured: isConfiguredToml, install: installToml, remove: removeToml },
|
|
1471
1603
|
yaml: { isConfigured: isConfiguredYaml, install: installYaml, remove: removeYaml }
|
|
1472
1604
|
};
|
|
1473
|
-
appSupport =
|
|
1605
|
+
appSupport = join6(home, "Library", "Application Support");
|
|
1474
1606
|
AI_TOOLS = [
|
|
1475
1607
|
createTool({
|
|
1476
1608
|
id: "claude-code",
|
|
1477
1609
|
name: "Claude Code",
|
|
1478
1610
|
configFormat: "standard",
|
|
1479
|
-
configPath:
|
|
1480
|
-
detect: () => hasBinary("claude") ||
|
|
1481
|
-
instructions: { method: "append", path:
|
|
1611
|
+
configPath: join6(home, ".claude.json"),
|
|
1612
|
+
detect: () => hasBinary("claude") || existsSync8(join6(home, ".claude.json")),
|
|
1613
|
+
instructions: { method: "append", path: join6(home, ".claude", "CLAUDE.md") },
|
|
1482
1614
|
supportsUrl: true
|
|
1483
1615
|
}),
|
|
1484
1616
|
createTool({
|
|
1485
1617
|
id: "cursor",
|
|
1486
1618
|
name: "Cursor",
|
|
1487
1619
|
configFormat: "standard",
|
|
1488
|
-
configPath:
|
|
1489
|
-
detect: () =>
|
|
1620
|
+
configPath: join6(home, ".cursor", "mcp.json"),
|
|
1621
|
+
detect: () => existsSync8(join6(home, ".cursor")),
|
|
1490
1622
|
manualHint: "Open Cursor Settings \u2192 Rules \u2192 User Rules and paste the instructions below.",
|
|
1491
1623
|
supportsUrl: true
|
|
1492
1624
|
}),
|
|
@@ -1494,51 +1626,51 @@ ${INSTRUCTIONS_END}`;
|
|
|
1494
1626
|
id: "windsurf",
|
|
1495
1627
|
name: "Windsurf",
|
|
1496
1628
|
configFormat: "standard",
|
|
1497
|
-
configPath:
|
|
1498
|
-
detect: () =>
|
|
1499
|
-
instructions: { method: "append", path:
|
|
1629
|
+
configPath: join6(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
1630
|
+
detect: () => existsSync8(join6(home, ".codeium", "windsurf")),
|
|
1631
|
+
instructions: { method: "append", path: join6(home, ".codeium", "windsurf", "memories", "global_rules.md") },
|
|
1500
1632
|
supportsUrl: true
|
|
1501
1633
|
}),
|
|
1502
1634
|
createTool({
|
|
1503
1635
|
id: "vscode",
|
|
1504
1636
|
name: "VS Code",
|
|
1505
1637
|
configFormat: "vscode",
|
|
1506
|
-
configPath:
|
|
1507
|
-
detect: () =>
|
|
1508
|
-
instructions: { method: "create", path:
|
|
1638
|
+
configPath: join6(appSupport, "Code", "User", "mcp.json"),
|
|
1639
|
+
detect: () => existsSync8(join6(appSupport, "Code")),
|
|
1640
|
+
instructions: { method: "create", path: join6(appSupport, "Code", "User", "prompts", "useai.instructions.md") },
|
|
1509
1641
|
supportsUrl: true
|
|
1510
1642
|
}),
|
|
1511
1643
|
createTool({
|
|
1512
1644
|
id: "vscode-insiders",
|
|
1513
1645
|
name: "VS Code Insiders",
|
|
1514
1646
|
configFormat: "vscode",
|
|
1515
|
-
configPath:
|
|
1516
|
-
detect: () =>
|
|
1517
|
-
instructions: { method: "create", path:
|
|
1647
|
+
configPath: join6(appSupport, "Code - Insiders", "User", "mcp.json"),
|
|
1648
|
+
detect: () => existsSync8(join6(appSupport, "Code - Insiders")),
|
|
1649
|
+
instructions: { method: "create", path: join6(appSupport, "Code - Insiders", "User", "prompts", "useai.instructions.md") },
|
|
1518
1650
|
supportsUrl: true
|
|
1519
1651
|
}),
|
|
1520
1652
|
createTool({
|
|
1521
1653
|
id: "gemini-cli",
|
|
1522
1654
|
name: "Gemini CLI",
|
|
1523
1655
|
configFormat: "standard",
|
|
1524
|
-
configPath:
|
|
1656
|
+
configPath: join6(home, ".gemini", "settings.json"),
|
|
1525
1657
|
detect: () => hasBinary("gemini"),
|
|
1526
|
-
instructions: { method: "append", path:
|
|
1658
|
+
instructions: { method: "append", path: join6(home, ".gemini", "GEMINI.md") },
|
|
1527
1659
|
supportsUrl: true
|
|
1528
1660
|
}),
|
|
1529
1661
|
createTool({
|
|
1530
1662
|
id: "zed",
|
|
1531
1663
|
name: "Zed",
|
|
1532
1664
|
configFormat: "zed",
|
|
1533
|
-
configPath:
|
|
1534
|
-
detect: () =>
|
|
1665
|
+
configPath: join6(appSupport, "Zed", "settings.json"),
|
|
1666
|
+
detect: () => existsSync8(join6(appSupport, "Zed")),
|
|
1535
1667
|
manualHint: "Open Rules Library (\u2318\u2325L) \u2192 click + \u2192 paste the instructions below."
|
|
1536
1668
|
}),
|
|
1537
1669
|
createTool({
|
|
1538
1670
|
id: "cline",
|
|
1539
1671
|
name: "Cline",
|
|
1540
1672
|
configFormat: "standard",
|
|
1541
|
-
configPath:
|
|
1673
|
+
configPath: join6(
|
|
1542
1674
|
appSupport,
|
|
1543
1675
|
"Code",
|
|
1544
1676
|
"User",
|
|
@@ -1547,17 +1679,17 @@ ${INSTRUCTIONS_END}`;
|
|
|
1547
1679
|
"settings",
|
|
1548
1680
|
"cline_mcp_settings.json"
|
|
1549
1681
|
),
|
|
1550
|
-
detect: () =>
|
|
1551
|
-
|
|
1682
|
+
detect: () => existsSync8(
|
|
1683
|
+
join6(appSupport, "Code", "User", "globalStorage", "saoudrizwan.claude-dev")
|
|
1552
1684
|
),
|
|
1553
|
-
instructions: { method: "create", path:
|
|
1685
|
+
instructions: { method: "create", path: join6(home, "Documents", "Cline", "Rules", "useai.md") },
|
|
1554
1686
|
supportsUrl: true
|
|
1555
1687
|
}),
|
|
1556
1688
|
createTool({
|
|
1557
1689
|
id: "roo-code",
|
|
1558
1690
|
name: "Roo Code",
|
|
1559
1691
|
configFormat: "standard",
|
|
1560
|
-
configPath:
|
|
1692
|
+
configPath: join6(
|
|
1561
1693
|
appSupport,
|
|
1562
1694
|
"Code",
|
|
1563
1695
|
"User",
|
|
@@ -1566,59 +1698,59 @@ ${INSTRUCTIONS_END}`;
|
|
|
1566
1698
|
"settings",
|
|
1567
1699
|
"cline_mcp_settings.json"
|
|
1568
1700
|
),
|
|
1569
|
-
detect: () =>
|
|
1570
|
-
|
|
1701
|
+
detect: () => existsSync8(
|
|
1702
|
+
join6(appSupport, "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline")
|
|
1571
1703
|
),
|
|
1572
|
-
instructions: { method: "create", path:
|
|
1704
|
+
instructions: { method: "create", path: join6(home, ".roo", "rules", "useai.md") },
|
|
1573
1705
|
supportsUrl: true
|
|
1574
1706
|
}),
|
|
1575
1707
|
createTool({
|
|
1576
1708
|
id: "amazon-q-cli",
|
|
1577
1709
|
name: "Amazon Q CLI",
|
|
1578
1710
|
configFormat: "standard",
|
|
1579
|
-
configPath:
|
|
1580
|
-
detect: () => hasBinary("q") ||
|
|
1711
|
+
configPath: join6(home, ".aws", "amazonq", "mcp.json"),
|
|
1712
|
+
detect: () => hasBinary("q") || existsSync8(join6(home, ".aws", "amazonq")),
|
|
1581
1713
|
manualHint: "Create .amazonq/rules/useai.md in your project root with the instructions below."
|
|
1582
1714
|
}),
|
|
1583
1715
|
createTool({
|
|
1584
1716
|
id: "amazon-q-ide",
|
|
1585
1717
|
name: "Amazon Q IDE",
|
|
1586
1718
|
configFormat: "standard",
|
|
1587
|
-
configPath:
|
|
1588
|
-
detect: () =>
|
|
1719
|
+
configPath: join6(home, ".aws", "amazonq", "default.json"),
|
|
1720
|
+
detect: () => existsSync8(join6(home, ".amazonq")) || existsSync8(join6(home, ".aws", "amazonq")),
|
|
1589
1721
|
manualHint: "Create .amazonq/rules/useai.md in your project root with the instructions below."
|
|
1590
1722
|
}),
|
|
1591
1723
|
createTool({
|
|
1592
1724
|
id: "codex",
|
|
1593
1725
|
name: "Codex",
|
|
1594
1726
|
configFormat: "toml",
|
|
1595
|
-
configPath:
|
|
1596
|
-
detect: () => hasBinary("codex") ||
|
|
1597
|
-
instructions: { method: "append", path:
|
|
1727
|
+
configPath: join6(home, ".codex", "config.toml"),
|
|
1728
|
+
detect: () => hasBinary("codex") || existsSync8(join6(home, ".codex")) || existsSync8("/Applications/Codex.app"),
|
|
1729
|
+
instructions: { method: "append", path: join6(home, ".codex", "AGENTS.md") }
|
|
1598
1730
|
}),
|
|
1599
1731
|
createTool({
|
|
1600
1732
|
id: "goose",
|
|
1601
1733
|
name: "Goose",
|
|
1602
1734
|
configFormat: "yaml",
|
|
1603
|
-
configPath:
|
|
1604
|
-
detect: () =>
|
|
1605
|
-
instructions: { method: "append", path:
|
|
1735
|
+
configPath: join6(home, ".config", "goose", "config.yaml"),
|
|
1736
|
+
detect: () => existsSync8(join6(home, ".config", "goose")),
|
|
1737
|
+
instructions: { method: "append", path: join6(home, ".config", "goose", ".goosehints") }
|
|
1606
1738
|
}),
|
|
1607
1739
|
createTool({
|
|
1608
1740
|
id: "opencode",
|
|
1609
1741
|
name: "OpenCode",
|
|
1610
1742
|
configFormat: "standard",
|
|
1611
|
-
configPath:
|
|
1612
|
-
detect: () => hasBinary("opencode") ||
|
|
1613
|
-
instructions: { method: "append", path:
|
|
1743
|
+
configPath: join6(home, ".config", "opencode", "opencode.json"),
|
|
1744
|
+
detect: () => hasBinary("opencode") || existsSync8(join6(home, ".config", "opencode")),
|
|
1745
|
+
instructions: { method: "append", path: join6(home, ".config", "opencode", "AGENTS.md") },
|
|
1614
1746
|
supportsUrl: true
|
|
1615
1747
|
}),
|
|
1616
1748
|
createTool({
|
|
1617
1749
|
id: "junie",
|
|
1618
1750
|
name: "Junie",
|
|
1619
1751
|
configFormat: "standard",
|
|
1620
|
-
configPath:
|
|
1621
|
-
detect: () =>
|
|
1752
|
+
configPath: join6(home, ".junie", "mcp", "mcp.json"),
|
|
1753
|
+
detect: () => existsSync8(join6(home, ".junie")),
|
|
1622
1754
|
manualHint: "Add the instructions below to .junie/guidelines.md in your project root."
|
|
1623
1755
|
})
|
|
1624
1756
|
];
|
|
@@ -1734,6 +1866,14 @@ async function daemonInstallFlow(tools, explicit) {
|
|
|
1734
1866
|
console.log(err(`\u2717 ${tool.name.padEnd(18)} \u2014 ${e.message}`));
|
|
1735
1867
|
}
|
|
1736
1868
|
}
|
|
1869
|
+
try {
|
|
1870
|
+
const hooksInstalled = installClaudeCodeHooks();
|
|
1871
|
+
if (hooksInstalled) {
|
|
1872
|
+
console.log(ok("\u2713 Claude Code hooks installed (Stop + SessionEnd)"));
|
|
1873
|
+
}
|
|
1874
|
+
} catch {
|
|
1875
|
+
console.log(chalk.yellow(" \u26A0 Could not install Claude Code hooks"));
|
|
1876
|
+
}
|
|
1737
1877
|
showManualHints(targetTools);
|
|
1738
1878
|
const mode = useDaemon ? "daemon mode" : "stdio mode";
|
|
1739
1879
|
console.log(`
|
|
@@ -1908,6 +2048,11 @@ async function fullRemoveFlow(tools, autoYes, explicit) {
|
|
|
1908
2048
|
}
|
|
1909
2049
|
}
|
|
1910
2050
|
}
|
|
2051
|
+
try {
|
|
2052
|
+
removeClaudeCodeHooks();
|
|
2053
|
+
console.log(ok("\u2713 Claude Code hooks removed"));
|
|
2054
|
+
} catch {
|
|
2055
|
+
}
|
|
1911
2056
|
console.log();
|
|
1912
2057
|
try {
|
|
1913
2058
|
await killDaemon();
|
|
@@ -2594,11 +2739,125 @@ __export(daemon_exports, {
|
|
|
2594
2739
|
});
|
|
2595
2740
|
import { createServer } from "http";
|
|
2596
2741
|
import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
|
|
2597
|
-
import { existsSync as
|
|
2598
|
-
import { join as
|
|
2742
|
+
import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync5, appendFileSync as appendFileSync2, renameSync as renameSync3, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
2743
|
+
import { join as join7 } from "path";
|
|
2599
2744
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2600
2745
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
2601
2746
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
2747
|
+
function getActiveUseaiSessionIds() {
|
|
2748
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2749
|
+
for (const [, active] of sessions) {
|
|
2750
|
+
ids.add(active.session.sessionId);
|
|
2751
|
+
}
|
|
2752
|
+
return ids;
|
|
2753
|
+
}
|
|
2754
|
+
function sealOrphanFile(sessionId) {
|
|
2755
|
+
const filePath = join7(ACTIVE_DIR, `${sessionId}.jsonl`);
|
|
2756
|
+
if (!existsSync9(filePath)) return;
|
|
2757
|
+
try {
|
|
2758
|
+
const content = readFileSync5(filePath, "utf-8").trim();
|
|
2759
|
+
if (!content) return;
|
|
2760
|
+
const lines = content.split("\n").filter(Boolean);
|
|
2761
|
+
if (lines.length === 0) return;
|
|
2762
|
+
const firstRecord = JSON.parse(lines[0]);
|
|
2763
|
+
const lastRecord = JSON.parse(lines[lines.length - 1]);
|
|
2764
|
+
if (lastRecord.type === "session_end" || lastRecord.type === "session_seal") {
|
|
2765
|
+
try {
|
|
2766
|
+
renameSync3(filePath, join7(SEALED_DIR, `${sessionId}.jsonl`));
|
|
2767
|
+
} catch {
|
|
2768
|
+
}
|
|
2769
|
+
return;
|
|
2770
|
+
}
|
|
2771
|
+
const startData = firstRecord.data;
|
|
2772
|
+
const client = startData["client"] ?? "unknown";
|
|
2773
|
+
const taskType = startData["task_type"] ?? "coding";
|
|
2774
|
+
const startTime = firstRecord.timestamp;
|
|
2775
|
+
let heartbeatCount = 0;
|
|
2776
|
+
for (const line of lines) {
|
|
2777
|
+
try {
|
|
2778
|
+
if (JSON.parse(line).type === "heartbeat") heartbeatCount++;
|
|
2779
|
+
} catch {
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
const chainTip = lastRecord.hash;
|
|
2783
|
+
const duration = Math.round((Date.now() - new Date(startTime).getTime()) / 1e3);
|
|
2784
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2785
|
+
const endRecord = buildChainRecord("session_end", sessionId, {
|
|
2786
|
+
duration_seconds: duration,
|
|
2787
|
+
task_type: taskType,
|
|
2788
|
+
languages: [],
|
|
2789
|
+
files_touched: 0,
|
|
2790
|
+
heartbeat_count: heartbeatCount,
|
|
2791
|
+
auto_sealed: true
|
|
2792
|
+
}, chainTip, daemonSigningKey);
|
|
2793
|
+
appendFileSync2(filePath, JSON.stringify(endRecord) + "\n");
|
|
2794
|
+
const sealData = JSON.stringify({
|
|
2795
|
+
session_id: sessionId,
|
|
2796
|
+
client,
|
|
2797
|
+
task_type: taskType,
|
|
2798
|
+
languages: [],
|
|
2799
|
+
files_touched: 0,
|
|
2800
|
+
started_at: startTime,
|
|
2801
|
+
ended_at: now,
|
|
2802
|
+
duration_seconds: duration,
|
|
2803
|
+
heartbeat_count: heartbeatCount,
|
|
2804
|
+
record_count: lines.length + 2,
|
|
2805
|
+
chain_end_hash: endRecord.hash
|
|
2806
|
+
});
|
|
2807
|
+
const sealSignature = signHash(
|
|
2808
|
+
createHash4("sha256").update(sealData).digest("hex"),
|
|
2809
|
+
daemonSigningKey
|
|
2810
|
+
);
|
|
2811
|
+
appendFileSync2(filePath, JSON.stringify(
|
|
2812
|
+
buildChainRecord("session_seal", sessionId, {
|
|
2813
|
+
seal: sealData,
|
|
2814
|
+
seal_signature: sealSignature,
|
|
2815
|
+
auto_sealed: true
|
|
2816
|
+
}, endRecord.hash, daemonSigningKey)
|
|
2817
|
+
) + "\n");
|
|
2818
|
+
try {
|
|
2819
|
+
renameSync3(filePath, join7(SEALED_DIR, `${sessionId}.jsonl`));
|
|
2820
|
+
} catch {
|
|
2821
|
+
}
|
|
2822
|
+
const seal = {
|
|
2823
|
+
session_id: sessionId,
|
|
2824
|
+
client,
|
|
2825
|
+
task_type: taskType,
|
|
2826
|
+
languages: [],
|
|
2827
|
+
files_touched: 0,
|
|
2828
|
+
started_at: startTime,
|
|
2829
|
+
ended_at: now,
|
|
2830
|
+
duration_seconds: duration,
|
|
2831
|
+
heartbeat_count: heartbeatCount,
|
|
2832
|
+
record_count: lines.length + 2,
|
|
2833
|
+
chain_start_hash: firstRecord.prev_hash,
|
|
2834
|
+
chain_end_hash: endRecord.hash,
|
|
2835
|
+
seal_signature: sealSignature
|
|
2836
|
+
};
|
|
2837
|
+
const allSessions = readJson(SESSIONS_FILE, []);
|
|
2838
|
+
allSessions.push(seal);
|
|
2839
|
+
writeJson(SESSIONS_FILE, allSessions);
|
|
2840
|
+
} catch {
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
function sealOrphanedSessions() {
|
|
2844
|
+
if (!existsSync9(ACTIVE_DIR)) return;
|
|
2845
|
+
const activeIds = getActiveUseaiSessionIds();
|
|
2846
|
+
let sealed = 0;
|
|
2847
|
+
try {
|
|
2848
|
+
const files = readdirSync(ACTIVE_DIR).filter((f) => f.endsWith(".jsonl"));
|
|
2849
|
+
for (const file of files) {
|
|
2850
|
+
const sessionId = file.replace(".jsonl", "");
|
|
2851
|
+
if (activeIds.has(sessionId)) continue;
|
|
2852
|
+
sealOrphanFile(sessionId);
|
|
2853
|
+
sealed++;
|
|
2854
|
+
}
|
|
2855
|
+
} catch {
|
|
2856
|
+
}
|
|
2857
|
+
if (sealed > 0) {
|
|
2858
|
+
console.log(`Sealed ${sealed} orphaned session${sealed === 1 ? "" : "s"}`);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2602
2861
|
function autoSealSession(active) {
|
|
2603
2862
|
const { session: session2 } = active;
|
|
2604
2863
|
if (session2.sessionRecordCount === 0) return;
|
|
@@ -2634,10 +2893,10 @@ function autoSealSession(active) {
|
|
|
2634
2893
|
seal_signature: sealSignature,
|
|
2635
2894
|
auto_sealed: true
|
|
2636
2895
|
});
|
|
2637
|
-
const activePath =
|
|
2638
|
-
const sealedPath =
|
|
2896
|
+
const activePath = join7(ACTIVE_DIR, `${session2.sessionId}.jsonl`);
|
|
2897
|
+
const sealedPath = join7(SEALED_DIR, `${session2.sessionId}.jsonl`);
|
|
2639
2898
|
try {
|
|
2640
|
-
if (
|
|
2899
|
+
if (existsSync9(activePath)) {
|
|
2641
2900
|
renameSync3(activePath, sealedPath);
|
|
2642
2901
|
}
|
|
2643
2902
|
} catch {
|
|
@@ -2714,6 +2973,21 @@ function parseBody(req) {
|
|
|
2714
2973
|
async function startDaemon(port) {
|
|
2715
2974
|
const listenPort = port ?? DAEMON_PORT;
|
|
2716
2975
|
ensureDir();
|
|
2976
|
+
try {
|
|
2977
|
+
if (existsSync9(KEYSTORE_FILE)) {
|
|
2978
|
+
const ks = readJson(KEYSTORE_FILE, null);
|
|
2979
|
+
if (ks) daemonSigningKey = decryptKeystore(ks);
|
|
2980
|
+
}
|
|
2981
|
+
if (!daemonSigningKey) {
|
|
2982
|
+
const result = generateKeystore();
|
|
2983
|
+
writeJson(KEYSTORE_FILE, result.keystore);
|
|
2984
|
+
daemonSigningKey = result.signingKey;
|
|
2985
|
+
}
|
|
2986
|
+
} catch {
|
|
2987
|
+
}
|
|
2988
|
+
sealOrphanedSessions();
|
|
2989
|
+
const sweepInterval = setInterval(sealOrphanedSessions, ORPHAN_SWEEP_INTERVAL_MS);
|
|
2990
|
+
sweepInterval.unref();
|
|
2717
2991
|
const server2 = createServer(async (req, res) => {
|
|
2718
2992
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
2719
2993
|
if (url.pathname === "/health" && req.method === "GET") {
|
|
@@ -2743,7 +3017,19 @@ async function startDaemon(port) {
|
|
|
2743
3017
|
await handleLocalSync(req, res);
|
|
2744
3018
|
return;
|
|
2745
3019
|
}
|
|
2746
|
-
if (url.pathname
|
|
3020
|
+
if (url.pathname === "/api/seal-active" && req.method === "POST") {
|
|
3021
|
+
const sids = [...sessions.keys()];
|
|
3022
|
+
for (const sid of sids) {
|
|
3023
|
+
await cleanupSession(sid);
|
|
3024
|
+
}
|
|
3025
|
+
res.writeHead(200, {
|
|
3026
|
+
"Content-Type": "application/json",
|
|
3027
|
+
"Access-Control-Allow-Origin": "*"
|
|
3028
|
+
});
|
|
3029
|
+
res.end(JSON.stringify({ sealed: sids.length }));
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
if ((url.pathname.startsWith("/api/local/") || url.pathname === "/api/seal-active") && req.method === "OPTIONS") {
|
|
2747
3033
|
res.writeHead(204, {
|
|
2748
3034
|
"Access-Control-Allow-Origin": "*",
|
|
2749
3035
|
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
@@ -2856,14 +3142,14 @@ async function startDaemon(port) {
|
|
|
2856
3142
|
port: listenPort,
|
|
2857
3143
|
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2858
3144
|
});
|
|
2859
|
-
|
|
3145
|
+
writeFileSync5(DAEMON_PID_FILE, pidData + "\n");
|
|
2860
3146
|
const shutdown = async (signal) => {
|
|
2861
3147
|
for (const [sid] of sessions) {
|
|
2862
3148
|
await cleanupSession(sid);
|
|
2863
3149
|
}
|
|
2864
3150
|
try {
|
|
2865
|
-
if (
|
|
2866
|
-
|
|
3151
|
+
if (existsSync9(DAEMON_PID_FILE)) {
|
|
3152
|
+
unlinkSync5(DAEMON_PID_FILE);
|
|
2867
3153
|
}
|
|
2868
3154
|
} catch {
|
|
2869
3155
|
}
|
|
@@ -2879,7 +3165,7 @@ async function startDaemon(port) {
|
|
|
2879
3165
|
console.log(`PID: ${process.pid}`);
|
|
2880
3166
|
});
|
|
2881
3167
|
}
|
|
2882
|
-
var IDLE_TIMEOUT_MS, sessions, startedAt;
|
|
3168
|
+
var IDLE_TIMEOUT_MS, sessions, daemonSigningKey, ORPHAN_SWEEP_INTERVAL_MS, startedAt;
|
|
2883
3169
|
var init_daemon2 = __esm({
|
|
2884
3170
|
"src/daemon.ts"() {
|
|
2885
3171
|
"use strict";
|
|
@@ -2890,6 +3176,8 @@ var init_daemon2 = __esm({
|
|
|
2890
3176
|
init_local_api();
|
|
2891
3177
|
IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
2892
3178
|
sessions = /* @__PURE__ */ new Map();
|
|
3179
|
+
daemonSigningKey = null;
|
|
3180
|
+
ORPHAN_SWEEP_INTERVAL_MS = 15 * 60 * 1e3;
|
|
2893
3181
|
startedAt = Date.now();
|
|
2894
3182
|
}
|
|
2895
3183
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devness/useai",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "Track your AI-assisted development workflow. MCP server that records usage metrics across all your AI tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -28,14 +28,6 @@
|
|
|
28
28
|
"README.md",
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
|
-
"scripts": {
|
|
32
|
-
"build": "tsc -p tsconfig.build.json",
|
|
33
|
-
"dev": "tsc --watch",
|
|
34
|
-
"bundle": "tsup src/index.ts --format esm --target node18 --no-splitting",
|
|
35
|
-
"prepublishOnly": "pnpm run bundle",
|
|
36
|
-
"typecheck": "tsc --noEmit",
|
|
37
|
-
"clean": "rm -rf dist"
|
|
38
|
-
},
|
|
39
31
|
"dependencies": {
|
|
40
32
|
"@inquirer/prompts": "^8.2.1",
|
|
41
33
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
@@ -46,9 +38,9 @@
|
|
|
46
38
|
},
|
|
47
39
|
"devDependencies": {
|
|
48
40
|
"@types/node": "^22.13.4",
|
|
49
|
-
"@useai/shared": "workspace:*",
|
|
50
41
|
"tsup": "^8.0.0",
|
|
51
|
-
"typescript": "^5.7.3"
|
|
42
|
+
"typescript": "^5.7.3",
|
|
43
|
+
"@useai/shared": "0.3.0"
|
|
52
44
|
},
|
|
53
45
|
"repository": {
|
|
54
46
|
"type": "git",
|
|
@@ -57,5 +49,12 @@
|
|
|
57
49
|
"homepage": "https://useai.dev",
|
|
58
50
|
"engines": {
|
|
59
51
|
"node": ">=18"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsc -p tsconfig.build.json",
|
|
55
|
+
"dev": "tsc --watch",
|
|
56
|
+
"bundle": "tsup src/index.ts --format esm --target node18 --no-splitting",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"clean": "rm -rf dist"
|
|
60
59
|
}
|
|
61
|
-
}
|
|
60
|
+
}
|