clay-server 2.33.1 → 2.34.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/config.js +9 -13
- package/lib/daemon.js +19 -17
- package/lib/mate-datastore.js +337 -0
- package/lib/os-users.js +57 -37
- package/lib/project-http.js +3 -4
- package/lib/project-image.js +3 -2
- package/lib/project-mate-datastore.js +218 -0
- package/lib/project.js +29 -0
- package/lib/public/app.js +2 -0
- package/lib/public/css/mates.css +175 -0
- package/lib/public/css/messages.css +23 -0
- package/lib/public/css/notifications-center.css +76 -0
- package/lib/public/index.html +28 -0
- package/lib/public/modules/app-messages.js +25 -0
- package/lib/public/modules/mate-datastore-ui.js +270 -0
- package/lib/public/modules/mate-sidebar.js +3 -0
- package/lib/public/modules/tools.js +57 -5
- package/lib/sdk-bridge.js +20 -13
- package/lib/sdk-message-processor.js +7 -1
- package/lib/updater.js +2 -2
- package/lib/users.js +2 -2
- package/lib/ws-schema.js +14 -0
- package/lib/yoke/adapters/codex.js +1 -0
- package/lib/yoke/index.js +20 -13
- package/package.json +1 -1
|
@@ -20,6 +20,14 @@ var todoItems = [];
|
|
|
20
20
|
var todoWidgetEl = null;
|
|
21
21
|
var todoWidgetVisible = true; // whether in-chat widget is in viewport
|
|
22
22
|
var todoObserver = null;
|
|
23
|
+
var todoMeta = {
|
|
24
|
+
variant: "tasks",
|
|
25
|
+
title: "Tasks",
|
|
26
|
+
icon: "list-checks",
|
|
27
|
+
showProgress: true,
|
|
28
|
+
showCompletedCount: true,
|
|
29
|
+
stickyEnabled: true,
|
|
30
|
+
};
|
|
23
31
|
|
|
24
32
|
// --- Tool tracking ---
|
|
25
33
|
var tools = {};
|
|
@@ -1262,6 +1270,7 @@ function todoStatusIcon(status) {
|
|
|
1262
1270
|
|
|
1263
1271
|
export function handleTodoWrite(input) {
|
|
1264
1272
|
if (!input || !Array.isArray(input.todos)) return;
|
|
1273
|
+
todoMeta = normalizeTodoMeta(input.meta);
|
|
1265
1274
|
todoItems = input.todos.map(function (t, i) {
|
|
1266
1275
|
return {
|
|
1267
1276
|
id: t.id || String(i + 1),
|
|
@@ -1275,6 +1284,7 @@ export function handleTodoWrite(input) {
|
|
|
1275
1284
|
|
|
1276
1285
|
export function handleTaskCreate(input) {
|
|
1277
1286
|
if (!input) return;
|
|
1287
|
+
todoMeta = normalizeTodoMeta();
|
|
1278
1288
|
var id = String(todoItems.length + 1);
|
|
1279
1289
|
todoItems.push({
|
|
1280
1290
|
id: id,
|
|
@@ -1287,6 +1297,7 @@ export function handleTaskCreate(input) {
|
|
|
1287
1297
|
|
|
1288
1298
|
export function handleTaskUpdate(input) {
|
|
1289
1299
|
if (!input || !input.taskId) return;
|
|
1300
|
+
todoMeta = normalizeTodoMeta();
|
|
1290
1301
|
for (var i = 0; i < todoItems.length; i++) {
|
|
1291
1302
|
if (todoItems[i].id === input.taskId) {
|
|
1292
1303
|
if (input.status === "deleted") {
|
|
@@ -1302,11 +1313,33 @@ export function handleTaskUpdate(input) {
|
|
|
1302
1313
|
renderTodoWidget();
|
|
1303
1314
|
}
|
|
1304
1315
|
|
|
1316
|
+
function normalizeTodoMeta(meta) {
|
|
1317
|
+
if (meta && meta.variant === "plan") {
|
|
1318
|
+
return {
|
|
1319
|
+
variant: "plan",
|
|
1320
|
+
title: "Plan",
|
|
1321
|
+
icon: "map",
|
|
1322
|
+
showProgress: false,
|
|
1323
|
+
showCompletedCount: false,
|
|
1324
|
+
stickyEnabled: false,
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
return {
|
|
1328
|
+
variant: "tasks",
|
|
1329
|
+
title: "Tasks",
|
|
1330
|
+
icon: "list-checks",
|
|
1331
|
+
showProgress: true,
|
|
1332
|
+
showCompletedCount: true,
|
|
1333
|
+
stickyEnabled: true,
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1305
1337
|
function renderTodoWidget() {
|
|
1306
1338
|
if (todoItems.length === 0) {
|
|
1307
1339
|
if (todoWidgetEl) { todoWidgetEl.remove(); todoWidgetEl = null; }
|
|
1308
1340
|
if (todoObserver) { todoObserver.disconnect(); todoObserver = null; }
|
|
1309
1341
|
todoWidgetVisible = true;
|
|
1342
|
+
todoMeta = normalizeTodoMeta();
|
|
1310
1343
|
updateTodoSticky();
|
|
1311
1344
|
return;
|
|
1312
1345
|
}
|
|
@@ -1316,19 +1349,26 @@ function renderTodoWidget() {
|
|
|
1316
1349
|
todoWidgetEl = document.createElement("div");
|
|
1317
1350
|
todoWidgetEl.className = "todo-widget";
|
|
1318
1351
|
}
|
|
1352
|
+
todoWidgetEl.className = "todo-widget" + (todoMeta.variant === "plan" ? " todo-widget-plan" : "");
|
|
1319
1353
|
|
|
1320
1354
|
var completed = 0;
|
|
1321
1355
|
for (var i = 0; i < todoItems.length; i++) {
|
|
1322
1356
|
if (todoItems[i].status === "completed") completed++;
|
|
1323
1357
|
}
|
|
1324
1358
|
|
|
1359
|
+
var countText = todoMeta.showCompletedCount
|
|
1360
|
+
? (completed + "/" + todoItems.length)
|
|
1361
|
+
: (todoItems.length + " " + (todoItems.length === 1 ? "step" : "steps"));
|
|
1362
|
+
|
|
1325
1363
|
var html = '<div class="todo-header">' +
|
|
1326
|
-
'<span class="todo-header-icon">' + iconHtml(
|
|
1327
|
-
'<span class="todo-header-title">
|
|
1328
|
-
'<span class="todo-header-count">' +
|
|
1364
|
+
'<span class="todo-header-icon">' + iconHtml(todoMeta.icon) + '</span>' +
|
|
1365
|
+
'<span class="todo-header-title">' + todoMeta.title + '</span>' +
|
|
1366
|
+
'<span class="todo-header-count">' + countText + '</span>' +
|
|
1329
1367
|
'</div>';
|
|
1330
|
-
|
|
1331
|
-
|
|
1368
|
+
if (todoMeta.showProgress) {
|
|
1369
|
+
html += '<div class="todo-progress"><div class="todo-progress-bar" style="width:' +
|
|
1370
|
+
(todoItems.length > 0 ? Math.round(completed / todoItems.length * 100) : 0) + '%"></div></div>';
|
|
1371
|
+
}
|
|
1332
1372
|
html += '<div class="todo-items">';
|
|
1333
1373
|
for (var i = 0; i < todoItems.length; i++) {
|
|
1334
1374
|
var t = todoItems[i];
|
|
@@ -1369,6 +1409,10 @@ function setupTodoObserver() {
|
|
|
1369
1409
|
function updateTodoStickyVisibility() {
|
|
1370
1410
|
var stickyEl = document.getElementById("todo-sticky");
|
|
1371
1411
|
if (!stickyEl) return;
|
|
1412
|
+
if (!todoMeta.stickyEnabled) {
|
|
1413
|
+
stickyEl.classList.add("hidden");
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1372
1416
|
|
|
1373
1417
|
if (todoWidgetVisible) {
|
|
1374
1418
|
stickyEl.classList.add("hidden");
|
|
@@ -1387,6 +1431,11 @@ function updateTodoStickyVisibility() {
|
|
|
1387
1431
|
function updateTodoSticky() {
|
|
1388
1432
|
var stickyEl = document.getElementById("todo-sticky");
|
|
1389
1433
|
if (!stickyEl) return;
|
|
1434
|
+
if (!todoMeta.stickyEnabled) {
|
|
1435
|
+
stickyEl.classList.add("hidden");
|
|
1436
|
+
stickyEl.innerHTML = "";
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1390
1439
|
|
|
1391
1440
|
// Hide if no active tasks (all completed or empty)
|
|
1392
1441
|
var hasActive = false;
|
|
@@ -2195,6 +2244,7 @@ export function saveToolState() {
|
|
|
2195
2244
|
tools: tools,
|
|
2196
2245
|
currentThinking: currentThinking,
|
|
2197
2246
|
todoWidgetEl: todoWidgetEl,
|
|
2247
|
+
todoMeta: todoMeta,
|
|
2198
2248
|
inPlanMode: inPlanMode,
|
|
2199
2249
|
planContent: planContent,
|
|
2200
2250
|
currentPlanCardEl: currentPlanCardEl,
|
|
@@ -2209,6 +2259,7 @@ export function restoreToolState(saved) {
|
|
|
2209
2259
|
tools = saved.tools;
|
|
2210
2260
|
currentThinking = saved.currentThinking;
|
|
2211
2261
|
todoWidgetEl = saved.todoWidgetEl;
|
|
2262
|
+
todoMeta = saved.todoMeta || normalizeTodoMeta();
|
|
2212
2263
|
inPlanMode = saved.inPlanMode;
|
|
2213
2264
|
planContent = saved.planContent;
|
|
2214
2265
|
currentPlanCardEl = saved.currentPlanCardEl || null;
|
|
@@ -2229,6 +2280,7 @@ export function resetToolState() {
|
|
|
2229
2280
|
planContent = null;
|
|
2230
2281
|
currentPlanCardEl = null;
|
|
2231
2282
|
todoItems = [];
|
|
2283
|
+
todoMeta = normalizeTodoMeta();
|
|
2232
2284
|
todoWidgetEl = null;
|
|
2233
2285
|
todoWidgetVisible = true;
|
|
2234
2286
|
if (todoObserver) { todoObserver.disconnect(); todoObserver = null; }
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const crypto = require("crypto");
|
|
2
2
|
var fs = require("fs");
|
|
3
3
|
var path = require("path");
|
|
4
|
-
var
|
|
4
|
+
var execSync = require("child_process").execSync;
|
|
5
|
+
var execFileSync = require("child_process").execFileSync;
|
|
5
6
|
var usersModule = require("./users");
|
|
6
7
|
var { getCodexConfig } = require("./codex-defaults");
|
|
7
8
|
var { splitShellSegments, attachSkillDiscovery } = require("./sdk-skill-discovery");
|
|
@@ -373,12 +374,12 @@ function createSDKBridge(opts) {
|
|
|
373
374
|
// Create and chown the project directory once
|
|
374
375
|
if (!fs.existsSync(dstDir)) {
|
|
375
376
|
fs.mkdirSync(dstDir, { recursive: true });
|
|
376
|
-
try {
|
|
377
|
+
try { execFileSync("chown", ["-R", String(uid), path.join(linuxUserHome, ".claude")]); } catch (e2) {}
|
|
377
378
|
} else {
|
|
378
379
|
try {
|
|
379
380
|
var dirStat = fs.statSync(dstDir);
|
|
380
381
|
if (dirStat.uid !== uid) {
|
|
381
|
-
|
|
382
|
+
execFileSync("chown", [String(uid), dstDir]);
|
|
382
383
|
}
|
|
383
384
|
} catch (e2) {}
|
|
384
385
|
}
|
|
@@ -389,7 +390,7 @@ function createSDKBridge(opts) {
|
|
|
389
390
|
var dstFile = path.join(dstDir, sessionFileName);
|
|
390
391
|
if (fs.existsSync(srcFile) && !fs.existsSync(dstFile)) {
|
|
391
392
|
fs.copyFileSync(srcFile, dstFile);
|
|
392
|
-
try {
|
|
393
|
+
try { execFileSync("chown", [String(uid), dstFile]); } catch (e2) {}
|
|
393
394
|
console.log("[sdk-bridge] Pre-copied CLI session " + session.cliSessionId + " to " + linuxUser);
|
|
394
395
|
}
|
|
395
396
|
}
|
|
@@ -447,6 +448,12 @@ function createSDKBridge(opts) {
|
|
|
447
448
|
}
|
|
448
449
|
}
|
|
449
450
|
|
|
451
|
+
// Auto-approve Mate datastore tools. These are scoped to the active Mate
|
|
452
|
+
// project and already enforce SQL policy server-side.
|
|
453
|
+
if (toolName.indexOf("mcp__clay-datastore__") === 0) {
|
|
454
|
+
return { behavior: "allow", updatedInput: input };
|
|
455
|
+
}
|
|
456
|
+
|
|
450
457
|
// Auto-approve remote MCP tools that the user explicitly enabled in project settings.
|
|
451
458
|
// These are user-owned local MCP servers, so no additional permission prompt needed.
|
|
452
459
|
if (toolName.indexOf("mcp__") === 0 && getRemoteMcpServers) {
|
|
@@ -646,7 +653,7 @@ function createSDKBridge(opts) {
|
|
|
646
653
|
*/
|
|
647
654
|
function findConflictingClaude() {
|
|
648
655
|
try {
|
|
649
|
-
var output =
|
|
656
|
+
var output = execFileSync("ps", ["ax", "-o", "pid,command"], { encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] });
|
|
650
657
|
var lines = output.trim().split("\n");
|
|
651
658
|
var candidates = [];
|
|
652
659
|
for (var i = 1; i < lines.length; i++) { // skip header
|
|
@@ -691,7 +698,7 @@ function createSDKBridge(opts) {
|
|
|
691
698
|
*/
|
|
692
699
|
function isClaudeProcess(pid) {
|
|
693
700
|
try {
|
|
694
|
-
var output =
|
|
701
|
+
var output = execFileSync("ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
695
702
|
return /\/claude(\s|$)/.test(output) || /^claude(\s|$)/.test(output);
|
|
696
703
|
} catch (e) {
|
|
697
704
|
return false;
|
|
@@ -1349,17 +1356,17 @@ function createSDKBridge(opts) {
|
|
|
1349
1356
|
// Detect which vendor binaries are installed for this user.
|
|
1350
1357
|
// In multi-user mode, runs checks as the specific Linux user.
|
|
1351
1358
|
function detectInstalledVendors(linuxUser) {
|
|
1352
|
-
var
|
|
1359
|
+
var execFileSync = require("child_process").execFileSync;
|
|
1353
1360
|
var fs = require("fs");
|
|
1354
|
-
var path = require("path");
|
|
1355
1361
|
var result = [];
|
|
1356
1362
|
|
|
1357
|
-
function
|
|
1363
|
+
function tryLookup(name) {
|
|
1358
1364
|
try {
|
|
1359
1365
|
if (linuxUser) {
|
|
1360
|
-
|
|
1366
|
+
execFileSync("su", ["-", linuxUser, "-c", "which " + name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
1361
1367
|
} else {
|
|
1362
|
-
|
|
1368
|
+
if (process.platform === "win32") execFileSync("where", [name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
1369
|
+
else execFileSync("which", [name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
1363
1370
|
}
|
|
1364
1371
|
return true;
|
|
1365
1372
|
} catch (e) {
|
|
@@ -1368,14 +1375,14 @@ function createSDKBridge(opts) {
|
|
|
1368
1375
|
}
|
|
1369
1376
|
|
|
1370
1377
|
// Claude: check if binary is in PATH
|
|
1371
|
-
if (
|
|
1378
|
+
if (tryLookup("claude")) result.push("claude");
|
|
1372
1379
|
|
|
1373
1380
|
// Codex: check bundled binary or PATH
|
|
1374
1381
|
var codexBin = null;
|
|
1375
1382
|
try {
|
|
1376
1383
|
codexBin = require("./yoke/codex-app-server").findCodexPath();
|
|
1377
1384
|
} catch (e) {}
|
|
1378
|
-
if ((codexBin && fs.existsSync(codexBin)) ||
|
|
1385
|
+
if ((codexBin && fs.existsSync(codexBin)) || tryLookup("codex")) result.push("codex");
|
|
1379
1386
|
|
|
1380
1387
|
return result;
|
|
1381
1388
|
}
|
|
@@ -234,7 +234,13 @@ function attachMessageProcessor(ctx) {
|
|
|
234
234
|
type: "tool_executing",
|
|
235
235
|
id: parsed.turnId || "codex-plan",
|
|
236
236
|
name: "TodoWrite",
|
|
237
|
-
input: {
|
|
237
|
+
input: {
|
|
238
|
+
todos: todos,
|
|
239
|
+
meta: {
|
|
240
|
+
variant: "plan",
|
|
241
|
+
title: parsed.title || "Plan",
|
|
242
|
+
},
|
|
243
|
+
},
|
|
238
244
|
});
|
|
239
245
|
|
|
240
246
|
} else if (parsed.yokeType === "plan_content") {
|
package/lib/updater.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const https = require("https");
|
|
2
|
-
const {
|
|
2
|
+
const { execFileSync, spawn } = require("child_process");
|
|
3
3
|
|
|
4
4
|
// ANSI helpers (mirrors cli.js)
|
|
5
5
|
var isBasicTerm = process.env.TERM_PROGRAM === "Apple_Terminal";
|
|
@@ -84,7 +84,7 @@ function isNewer(latest, current) {
|
|
|
84
84
|
function performUpdate(channel) {
|
|
85
85
|
var tag = channel === "beta" ? "beta" : "latest";
|
|
86
86
|
try {
|
|
87
|
-
|
|
87
|
+
execFileSync("npm", ["install", "-g", "clay-server@" + tag], { stdio: "pipe" });
|
|
88
88
|
return true;
|
|
89
89
|
} catch (e) {
|
|
90
90
|
return false;
|
package/lib/users.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var fs = require("fs");
|
|
2
2
|
var path = require("path");
|
|
3
3
|
var crypto = require("crypto");
|
|
4
|
-
var
|
|
4
|
+
var execFileSync = require("child_process").execFileSync;
|
|
5
5
|
var { CONFIG_DIR } = require("./config");
|
|
6
6
|
var { attachAuth } = require("./users-auth");
|
|
7
7
|
var { DEFAULT_PERMISSIONS, ALL_PERMISSIONS, attachPermissions } = require("./users-permissions");
|
|
@@ -243,7 +243,7 @@ function updateLinuxUser(userId, linuxUsername) {
|
|
|
243
243
|
|
|
244
244
|
// Validate Linux user exists
|
|
245
245
|
try {
|
|
246
|
-
|
|
246
|
+
execFileSync("id", [linuxUsername], { encoding: "utf8", timeout: 5000, stdio: "pipe" });
|
|
247
247
|
} catch (e) {
|
|
248
248
|
return { error: "Linux user '" + linuxUsername + "' does not exist" };
|
|
249
249
|
}
|
package/lib/ws-schema.js
CHANGED
|
@@ -454,6 +454,20 @@ var schema = {
|
|
|
454
454
|
"memory_delete": { direction: "c2s", handler: "lib/project.js", description: "Delete a memory entry by index" },
|
|
455
455
|
"memory_deleted": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Memory entry was deleted" },
|
|
456
456
|
|
|
457
|
+
// -----------------------------------------------------------------------
|
|
458
|
+
// Mate datastore
|
|
459
|
+
// -----------------------------------------------------------------------
|
|
460
|
+
"mate_db_tables": { direction: "c2s", handler: "lib/project-mate-datastore.js", description: "List schema objects in the current Mate datastore" },
|
|
461
|
+
"mate_db_describe": { direction: "c2s", handler: "lib/project-mate-datastore.js", description: "Describe a table or view in the current Mate datastore" },
|
|
462
|
+
"mate_db_query": { direction: "c2s", handler: "lib/project-mate-datastore.js", description: "Run read-only SQL against the current Mate datastore" },
|
|
463
|
+
"mate_db_exec": { direction: "c2s", handler: "lib/project-mate-datastore.js", description: "Run schema or write SQL against the current Mate datastore" },
|
|
464
|
+
"mate_db_tables_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Schema objects returned from a Mate datastore" },
|
|
465
|
+
"mate_db_describe_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Table description returned from a Mate datastore" },
|
|
466
|
+
"mate_db_query_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Query results from a Mate datastore" },
|
|
467
|
+
"mate_db_exec_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Execution summary from a Mate datastore" },
|
|
468
|
+
"mate_db_error": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Mate datastore error" },
|
|
469
|
+
"mate_db_change": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Mate datastore changed" },
|
|
470
|
+
|
|
457
471
|
// -----------------------------------------------------------------------
|
|
458
472
|
// Loop (automated task runner)
|
|
459
473
|
// -----------------------------------------------------------------------
|
|
@@ -164,6 +164,7 @@ function flattenEvent(notification, state) {
|
|
|
164
164
|
yokeType: "plan_updated",
|
|
165
165
|
turnId: params.turnId || null,
|
|
166
166
|
explanation: params.explanation || "",
|
|
167
|
+
title: "Plan",
|
|
167
168
|
plan: Array.isArray(params.plan) ? params.plan.map(function(step) {
|
|
168
169
|
return {
|
|
169
170
|
step: step && step.step ? step.step : "",
|
package/lib/yoke/index.js
CHANGED
|
@@ -82,6 +82,18 @@ function checkAuth() {
|
|
|
82
82
|
if (_authCache) return _authCache;
|
|
83
83
|
|
|
84
84
|
var execSync = require("child_process").execSync;
|
|
85
|
+
var execFileSync = require("child_process").execFileSync;
|
|
86
|
+
|
|
87
|
+
function lookupBinary(name) {
|
|
88
|
+
try {
|
|
89
|
+
if (process.platform === "win32") {
|
|
90
|
+
return execFileSync("where", [name], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0] || null;
|
|
91
|
+
}
|
|
92
|
+
return execFileSync("which", [name], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0] || null;
|
|
93
|
+
} catch (e) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
85
97
|
|
|
86
98
|
function parseClaudeAuthStatusJson(out) {
|
|
87
99
|
if (!out) return null;
|
|
@@ -127,27 +139,20 @@ function checkAuth() {
|
|
|
127
139
|
function resolveCodexBinary() {
|
|
128
140
|
var fs = require("fs");
|
|
129
141
|
var findCodexPath = require("./codex-app-server").findCodexPath;
|
|
130
|
-
var pathProbeCmd = process.platform === "win32" ? "where codex" : "which codex";
|
|
131
142
|
|
|
132
143
|
try {
|
|
133
144
|
var codexBin = findCodexPath();
|
|
134
145
|
if (codexBin && fs.existsSync(codexBin)) return codexBin;
|
|
135
146
|
} catch (e) {}
|
|
136
147
|
|
|
137
|
-
|
|
138
|
-
var whichOut = execSync(pathProbeCmd, { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
139
|
-
var codexPath = whichOut.trim().split(/\r?\n/)[0];
|
|
140
|
-
if (codexPath) return codexPath;
|
|
141
|
-
} catch (e) {}
|
|
142
|
-
|
|
143
|
-
return null;
|
|
148
|
+
return lookupBinary("codex");
|
|
144
149
|
}
|
|
145
150
|
|
|
146
151
|
function checkCodex() {
|
|
147
152
|
try {
|
|
148
153
|
var codexBin = resolveCodexBinary();
|
|
149
154
|
if (!codexBin) return false;
|
|
150
|
-
|
|
155
|
+
execFileSync(codexBin, ["login", "status"], { timeout: 5000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
151
156
|
return true;
|
|
152
157
|
} catch (e) {
|
|
153
158
|
return false;
|
|
@@ -164,10 +169,11 @@ function checkAuth() {
|
|
|
164
169
|
*/
|
|
165
170
|
function checkInstalled() {
|
|
166
171
|
var fs = require("fs");
|
|
167
|
-
var
|
|
172
|
+
var execFileSync = require("child_process").execFileSync;
|
|
168
173
|
var result = { claude: false, codex: false };
|
|
169
174
|
try {
|
|
170
|
-
|
|
175
|
+
if (process.platform === "win32") execFileSync("where", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
176
|
+
else execFileSync("which", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
171
177
|
result.claude = true;
|
|
172
178
|
} catch (e) {}
|
|
173
179
|
try {
|
|
@@ -181,8 +187,9 @@ function checkInstalled() {
|
|
|
181
187
|
}
|
|
182
188
|
} catch (e) {}
|
|
183
189
|
|
|
184
|
-
var
|
|
185
|
-
|
|
190
|
+
var whichOut = process.platform === "win32"
|
|
191
|
+
? execFileSync("where", ["codex"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
|
|
192
|
+
: execFileSync("which", ["codex"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
186
193
|
if (whichOut.trim()) result.codex = true;
|
|
187
194
|
} catch (e) {}
|
|
188
195
|
return result;
|