@jentrix/cli 0.5.5 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +523 -51
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -6418,7 +6418,7 @@ var require_compile = __commonJS({
|
|
|
6418
6418
|
const schOrFunc = root.refs[ref];
|
|
6419
6419
|
if (schOrFunc)
|
|
6420
6420
|
return schOrFunc;
|
|
6421
|
-
let _sch =
|
|
6421
|
+
let _sch = resolve5.call(this, root, ref);
|
|
6422
6422
|
if (_sch === void 0) {
|
|
6423
6423
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
6424
6424
|
const { schemaId } = this.opts;
|
|
@@ -6445,7 +6445,7 @@ var require_compile = __commonJS({
|
|
|
6445
6445
|
function sameSchemaEnv(s1, s2) {
|
|
6446
6446
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
6447
6447
|
}
|
|
6448
|
-
function
|
|
6448
|
+
function resolve5(root, ref) {
|
|
6449
6449
|
let sch;
|
|
6450
6450
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
6451
6451
|
ref = sch;
|
|
@@ -7076,7 +7076,7 @@ var require_fast_uri = __commonJS({
|
|
|
7076
7076
|
}
|
|
7077
7077
|
return uri;
|
|
7078
7078
|
}
|
|
7079
|
-
function
|
|
7079
|
+
function resolve5(baseURI, relativeURI, options) {
|
|
7080
7080
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
7081
7081
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
7082
7082
|
schemelessOptions.skipEscape = true;
|
|
@@ -7334,7 +7334,7 @@ var require_fast_uri = __commonJS({
|
|
|
7334
7334
|
var fastUri = {
|
|
7335
7335
|
SCHEMES,
|
|
7336
7336
|
normalize,
|
|
7337
|
-
resolve:
|
|
7337
|
+
resolve: resolve5,
|
|
7338
7338
|
resolveComponent,
|
|
7339
7339
|
equal,
|
|
7340
7340
|
serialize,
|
|
@@ -10327,14 +10327,17 @@ var require_dist = __commonJS({
|
|
|
10327
10327
|
import { spawn as spawn3 } from "node:child_process";
|
|
10328
10328
|
import { createInterface } from "node:readline";
|
|
10329
10329
|
import {
|
|
10330
|
+
appendFileSync,
|
|
10330
10331
|
closeSync as closeSync3,
|
|
10332
|
+
copyFileSync,
|
|
10331
10333
|
existsSync as existsSync6,
|
|
10334
|
+
mkdirSync as mkdirSync4,
|
|
10332
10335
|
openSync as openSync3,
|
|
10333
10336
|
readFileSync as readFileSync7,
|
|
10334
10337
|
statSync as statSync3
|
|
10335
10338
|
} from "node:fs";
|
|
10336
10339
|
import { homedir } from "node:os";
|
|
10337
|
-
import { basename as basename2, join as join6 } from "node:path";
|
|
10340
|
+
import { basename as basename2, delimiter as delimiter2, dirname as dirname4, join as join6, resolve as resolve4 } from "node:path";
|
|
10338
10341
|
import { fileURLToPath } from "node:url";
|
|
10339
10342
|
|
|
10340
10343
|
// node_modules/.pnpm/commander@14.0.3/node_modules/commander/esm.mjs
|
|
@@ -17870,7 +17873,7 @@ var Protocol = class {
|
|
|
17870
17873
|
return;
|
|
17871
17874
|
}
|
|
17872
17875
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
17873
|
-
await new Promise((
|
|
17876
|
+
await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
|
|
17874
17877
|
options?.signal?.throwIfAborted();
|
|
17875
17878
|
}
|
|
17876
17879
|
} catch (error2) {
|
|
@@ -17887,7 +17890,7 @@ var Protocol = class {
|
|
|
17887
17890
|
*/
|
|
17888
17891
|
request(request, resultSchema, options) {
|
|
17889
17892
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
17890
|
-
return new Promise((
|
|
17893
|
+
return new Promise((resolve5, reject) => {
|
|
17891
17894
|
const earlyReject = (error2) => {
|
|
17892
17895
|
reject(error2);
|
|
17893
17896
|
};
|
|
@@ -17965,7 +17968,7 @@ var Protocol = class {
|
|
|
17965
17968
|
if (!parseResult.success) {
|
|
17966
17969
|
reject(parseResult.error);
|
|
17967
17970
|
} else {
|
|
17968
|
-
|
|
17971
|
+
resolve5(parseResult.data);
|
|
17969
17972
|
}
|
|
17970
17973
|
} catch (error2) {
|
|
17971
17974
|
reject(error2);
|
|
@@ -18226,12 +18229,12 @@ var Protocol = class {
|
|
|
18226
18229
|
}
|
|
18227
18230
|
} catch {
|
|
18228
18231
|
}
|
|
18229
|
-
return new Promise((
|
|
18232
|
+
return new Promise((resolve5, reject) => {
|
|
18230
18233
|
if (signal.aborted) {
|
|
18231
18234
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
18232
18235
|
return;
|
|
18233
18236
|
}
|
|
18234
|
-
const timeoutId = setTimeout(
|
|
18237
|
+
const timeoutId = setTimeout(resolve5, interval);
|
|
18235
18238
|
signal.addEventListener("abort", () => {
|
|
18236
18239
|
clearTimeout(timeoutId);
|
|
18237
18240
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -20959,7 +20962,7 @@ function refreshAccessToken(input) {
|
|
|
20959
20962
|
|
|
20960
20963
|
// src/client.ts
|
|
20961
20964
|
var CLI_NAME = "stacks-cli";
|
|
20962
|
-
var CLI_VERSION = "0.5.
|
|
20965
|
+
var CLI_VERSION = "0.5.7";
|
|
20963
20966
|
var DEAD_TOKEN_MESSAGE = "authentication failed (HTTP 401): the token is dead or expired \u2014 mint a new PAT at /account/tokens on your Jentrix server and update STACKS_TOKEN (or --token / the config file).";
|
|
20964
20967
|
var RELOGIN_MESSAGE = "OAuth session expired and could not be refreshed \u2014 run `jentrix login` to sign in again.";
|
|
20965
20968
|
function originOf2(url2) {
|
|
@@ -21095,6 +21098,26 @@ function readOAuthRecord(configPath, reader) {
|
|
|
21095
21098
|
}
|
|
21096
21099
|
}
|
|
21097
21100
|
|
|
21101
|
+
// src/exec-target.ts
|
|
21102
|
+
var INTERPRETED = /\.(cmd|bat)$/i;
|
|
21103
|
+
function quoteForCmd(token) {
|
|
21104
|
+
return `"${token.replace(/"/g, '""')}"`;
|
|
21105
|
+
}
|
|
21106
|
+
function execTarget(file, args, platform2 = process.platform, comSpec = process.env.ComSpec) {
|
|
21107
|
+
if (platform2 !== "win32" || !INTERPRETED.test(file)) {
|
|
21108
|
+
return { file, args: [...args], options: {} };
|
|
21109
|
+
}
|
|
21110
|
+
const line = [file, ...args].map(quoteForCmd).join(" ");
|
|
21111
|
+
return {
|
|
21112
|
+
file: comSpec || "cmd.exe",
|
|
21113
|
+
// `/d` skips any AutoRun command the machine has registered; `/s` makes cmd
|
|
21114
|
+
// strip exactly the outer quote pair and take the rest verbatim, which is
|
|
21115
|
+
// the only form that survives a quoted program path.
|
|
21116
|
+
args: ["/d", "/s", "/c", `"${line}"`],
|
|
21117
|
+
options: { windowsVerbatimArguments: true }
|
|
21118
|
+
};
|
|
21119
|
+
}
|
|
21120
|
+
|
|
21098
21121
|
// src/loopback.ts
|
|
21099
21122
|
import { createServer } from "node:http";
|
|
21100
21123
|
import { spawn } from "node:child_process";
|
|
@@ -21107,7 +21130,7 @@ function escapeHtml(s) {
|
|
|
21107
21130
|
);
|
|
21108
21131
|
}
|
|
21109
21132
|
function tryBindPort(port, timeoutMs = CALLBACK_TIMEOUT_MS) {
|
|
21110
|
-
return new Promise((
|
|
21133
|
+
return new Promise((resolve5) => {
|
|
21111
21134
|
let settleCode = null;
|
|
21112
21135
|
let rejectCode = null;
|
|
21113
21136
|
let timer = null;
|
|
@@ -21132,9 +21155,9 @@ function tryBindPort(port, timeoutMs = CALLBACK_TIMEOUT_MS) {
|
|
|
21132
21155
|
rejectCode?.(new Error("callback had no authorization code"));
|
|
21133
21156
|
else settleCode?.({ code, state: url2.searchParams.get("state") });
|
|
21134
21157
|
});
|
|
21135
|
-
server.once("error", () =>
|
|
21158
|
+
server.once("error", () => resolve5(null));
|
|
21136
21159
|
server.listen(port, LOOPBACK_HOST, () => {
|
|
21137
|
-
|
|
21160
|
+
resolve5({
|
|
21138
21161
|
port,
|
|
21139
21162
|
waitForCode: () => new Promise((res, rej) => {
|
|
21140
21163
|
settleCode = res;
|
|
@@ -21161,17 +21184,17 @@ async function bindLoopback(ports, timeoutMs = CALLBACK_TIMEOUT_MS) {
|
|
|
21161
21184
|
return null;
|
|
21162
21185
|
}
|
|
21163
21186
|
function openBrowser(url2) {
|
|
21164
|
-
return new Promise((
|
|
21187
|
+
return new Promise((resolve5) => {
|
|
21165
21188
|
const os = platform();
|
|
21166
21189
|
const cmd = os === "darwin" ? "open" : os === "win32" ? "cmd" : "xdg-open";
|
|
21167
21190
|
const args = os === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
21168
21191
|
try {
|
|
21169
21192
|
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
21170
|
-
child.on("error", () =>
|
|
21193
|
+
child.on("error", () => resolve5());
|
|
21171
21194
|
child.unref();
|
|
21172
21195
|
} catch {
|
|
21173
21196
|
}
|
|
21174
|
-
|
|
21197
|
+
resolve5();
|
|
21175
21198
|
});
|
|
21176
21199
|
}
|
|
21177
21200
|
|
|
@@ -21724,13 +21747,13 @@ function normalizeRemoteRepo(url2) {
|
|
|
21724
21747
|
if (trimmed.length < 2) return null;
|
|
21725
21748
|
return `${trimmed[0]}/${trimmed[1]}`.toLowerCase();
|
|
21726
21749
|
}
|
|
21727
|
-
var defaultGitRunner = (args, cwd) => new Promise((
|
|
21750
|
+
var defaultGitRunner = (args, cwd) => new Promise((resolve5) => {
|
|
21728
21751
|
execFile(
|
|
21729
21752
|
"git",
|
|
21730
21753
|
args,
|
|
21731
21754
|
{ cwd, timeout: 1e4, maxBuffer: 4 * 1024 * 1024, windowsHide: true },
|
|
21732
21755
|
(error2, stdout) => {
|
|
21733
|
-
|
|
21756
|
+
resolve5({
|
|
21734
21757
|
code: error2 === null ? 0 : typeof error2.code === "number" ? Number(error2.code) : 1,
|
|
21735
21758
|
stdout: String(stdout)
|
|
21736
21759
|
});
|
|
@@ -27191,6 +27214,11 @@ function relayFailure(deps2, step, result) {
|
|
|
27191
27214
|
);
|
|
27192
27215
|
return EXIT_CODES.INTERNAL;
|
|
27193
27216
|
}
|
|
27217
|
+
function persistentPluginRoot(globalNodeModules, ownRoot, exists) {
|
|
27218
|
+
if (!globalNodeModules) return ownRoot;
|
|
27219
|
+
const global = join4(globalNodeModules, "@jentrix", "cli");
|
|
27220
|
+
return exists(global) ? global : ownRoot;
|
|
27221
|
+
}
|
|
27194
27222
|
function isPluginMarketplaceDir(dir) {
|
|
27195
27223
|
return existsSync4(join4(dir, ".claude-plugin", "marketplace.json"));
|
|
27196
27224
|
}
|
|
@@ -27468,22 +27496,33 @@ function invokeRunnerProcess(file, args, stdin, timeoutMs = RUNNER_TIMEOUT_MS) {
|
|
|
27468
27496
|
if (!isAbsolute(file)) {
|
|
27469
27497
|
return Promise.reject(new Error("runner executable path must be absolute"));
|
|
27470
27498
|
}
|
|
27499
|
+
const target = execTarget(file, args);
|
|
27471
27500
|
return new Promise((resolveInvocation, reject) => {
|
|
27472
27501
|
const child = execFile2(
|
|
27473
|
-
file,
|
|
27474
|
-
args,
|
|
27502
|
+
target.file,
|
|
27503
|
+
target.args,
|
|
27475
27504
|
{
|
|
27476
27505
|
timeout: timeoutMs,
|
|
27477
27506
|
maxBuffer: RUNNER_OUTPUT_CAP,
|
|
27478
|
-
windowsHide: true
|
|
27507
|
+
windowsHide: true,
|
|
27508
|
+
...target.options
|
|
27479
27509
|
},
|
|
27480
27510
|
(error2, stdout, stderr) => {
|
|
27481
27511
|
if (error2 && error2.killed) {
|
|
27482
27512
|
reject(new Error("runner subprocess timed out"));
|
|
27483
27513
|
return;
|
|
27484
27514
|
}
|
|
27515
|
+
const raw = error2?.code;
|
|
27516
|
+
if (error2 && typeof raw === "string") {
|
|
27517
|
+
resolveInvocation({
|
|
27518
|
+
code: 1,
|
|
27519
|
+
stdout: String(stdout),
|
|
27520
|
+
stderr: `${String(stderr)}${raw}: ${error2.message}`
|
|
27521
|
+
});
|
|
27522
|
+
return;
|
|
27523
|
+
}
|
|
27485
27524
|
resolveInvocation({
|
|
27486
|
-
code: typeof
|
|
27525
|
+
code: typeof raw === "number" ? Number(raw) : 0,
|
|
27487
27526
|
stdout: String(stdout),
|
|
27488
27527
|
stderr: String(stderr)
|
|
27489
27528
|
});
|
|
@@ -27804,8 +27843,12 @@ function runRunnerForeground(file, args) {
|
|
|
27804
27843
|
if (!isAbsolute(file)) {
|
|
27805
27844
|
return Promise.reject(new Error("runner executable path must be absolute"));
|
|
27806
27845
|
}
|
|
27846
|
+
const target = execTarget(file, args);
|
|
27807
27847
|
return new Promise((resolveRun, reject) => {
|
|
27808
|
-
const child = spawn2(file, args, {
|
|
27848
|
+
const child = spawn2(target.file, target.args, {
|
|
27849
|
+
stdio: "inherit",
|
|
27850
|
+
...target.options
|
|
27851
|
+
});
|
|
27809
27852
|
child.once("error", reject);
|
|
27810
27853
|
child.once("exit", (code, signal) => resolveRun(code ?? (signal ? 1 : 0)));
|
|
27811
27854
|
});
|
|
@@ -27852,6 +27895,344 @@ function registerRunnerCommand(program3, deps2, onExit2) {
|
|
|
27852
27895
|
);
|
|
27853
27896
|
}
|
|
27854
27897
|
|
|
27898
|
+
// src/commands/setup.ts
|
|
27899
|
+
var NPM_INSTALL_TIMEOUT_MS2 = 3e5;
|
|
27900
|
+
var SUPERSEDED = ["@jentrix/stacks-cli", "@jentrix/stacks-runner"];
|
|
27901
|
+
var CLI_SPEC = `@jentrix/cli@${CLI_VERSION}`;
|
|
27902
|
+
var RUNNER_SPEC2 = `@jentrix/runner@${CLI_VERSION}`;
|
|
27903
|
+
function say(deps2, text = "") {
|
|
27904
|
+
deps2.writeOut(text);
|
|
27905
|
+
}
|
|
27906
|
+
async function installToolchain(deps2) {
|
|
27907
|
+
const npm = await deps2.resolveNpm();
|
|
27908
|
+
let global = await deps2.globalInstall();
|
|
27909
|
+
if (global.cliDir) {
|
|
27910
|
+
say(
|
|
27911
|
+
deps2,
|
|
27912
|
+
`jentrix ${CLI_VERSION} is already installed (${global.cliDir}).`
|
|
27913
|
+
);
|
|
27914
|
+
say(deps2, " Upgrade it any time with: npm update -g @jentrix/cli");
|
|
27915
|
+
} else {
|
|
27916
|
+
if (!npm) {
|
|
27917
|
+
deps2.writeErr(
|
|
27918
|
+
"NPM_NOT_FOUND: the toolchain installs from npm and `npm` is not on PATH \u2014 install Node.js >= 20 (https://nodejs.org), then retry."
|
|
27919
|
+
);
|
|
27920
|
+
return EXIT_CODES.INVALID_INPUT;
|
|
27921
|
+
}
|
|
27922
|
+
await deps2.invoke(npm, ["rm", "-g", ...SUPERSEDED], void 0, 6e4);
|
|
27923
|
+
say(deps2, `Installing ${CLI_SPEC} + ${RUNNER_SPEC2} (npm -g)\u2026`);
|
|
27924
|
+
const installed = await deps2.invoke(
|
|
27925
|
+
npm,
|
|
27926
|
+
["install", "-g", CLI_SPEC, RUNNER_SPEC2],
|
|
27927
|
+
void 0,
|
|
27928
|
+
NPM_INSTALL_TIMEOUT_MS2
|
|
27929
|
+
);
|
|
27930
|
+
if (installed.code !== 0) {
|
|
27931
|
+
const detail = (installed.stderr || installed.stdout).trim();
|
|
27932
|
+
deps2.writeErr(
|
|
27933
|
+
`TOOLCHAIN_INSTALL_FAILED: \`npm install -g\` exited ${installed.code}${detail ? `: ${detail}` : ""}`
|
|
27934
|
+
);
|
|
27935
|
+
deps2.writeErr(
|
|
27936
|
+
" On EACCES, fix npm's global prefix (https://docs.npmjs.com/resolving-eacces-permissions-errors); on EEXIST, delete the bin npm names and retry."
|
|
27937
|
+
);
|
|
27938
|
+
return EXIT_CODES.INTERNAL;
|
|
27939
|
+
}
|
|
27940
|
+
global = await deps2.globalInstall();
|
|
27941
|
+
if (!global.cliDir) {
|
|
27942
|
+
deps2.writeErr(
|
|
27943
|
+
"TOOLCHAIN_INSTALL_FAILED: npm reported success but @jentrix/cli is not under `npm root -g` \u2014 check npm's global prefix, then retry."
|
|
27944
|
+
);
|
|
27945
|
+
return EXIT_CODES.INTERNAL;
|
|
27946
|
+
}
|
|
27947
|
+
say(deps2, "Installed the CLI and the session runner.");
|
|
27948
|
+
}
|
|
27949
|
+
if (!global.runnerDir) {
|
|
27950
|
+
if (!npm) {
|
|
27951
|
+
deps2.writeErr(
|
|
27952
|
+
`RUNNER_NOT_INSTALLED: connected sessions need the runner and npm was not found to install it \u2014 npm install -g ${RUNNER_SPEC2}, then retry.`
|
|
27953
|
+
);
|
|
27954
|
+
return EXIT_CODES.INVALID_INPUT;
|
|
27955
|
+
}
|
|
27956
|
+
say(deps2, `Installing ${RUNNER_SPEC2} (connected sessions need it)\u2026`);
|
|
27957
|
+
const runner = await deps2.invoke(
|
|
27958
|
+
npm,
|
|
27959
|
+
["install", "-g", RUNNER_SPEC2],
|
|
27960
|
+
void 0,
|
|
27961
|
+
NPM_INSTALL_TIMEOUT_MS2
|
|
27962
|
+
);
|
|
27963
|
+
global = await deps2.globalInstall();
|
|
27964
|
+
if (runner.code !== 0 || !global.runnerDir) {
|
|
27965
|
+
deps2.writeErr(
|
|
27966
|
+
`RUNNER_NOT_INSTALLED: \`npm install -g ${RUNNER_SPEC2}\` did not leave the runner under \`npm root -g\` \u2014 install it by hand, then retry.`
|
|
27967
|
+
);
|
|
27968
|
+
return EXIT_CODES.INTERNAL;
|
|
27969
|
+
}
|
|
27970
|
+
}
|
|
27971
|
+
if (global.binDir && !global.binDirOnPath) {
|
|
27972
|
+
say(deps2, "");
|
|
27973
|
+
say(deps2, "note: npm's global bin directory is NOT on your PATH \u2014");
|
|
27974
|
+
say(deps2, ` ${global.binDir}`);
|
|
27975
|
+
say(
|
|
27976
|
+
deps2,
|
|
27977
|
+
" The toolchain is installed, but `jentrix` will not be found in a new"
|
|
27978
|
+
);
|
|
27979
|
+
say(
|
|
27980
|
+
deps2,
|
|
27981
|
+
" terminal until you add that directory to PATH and reopen your shell."
|
|
27982
|
+
);
|
|
27983
|
+
}
|
|
27984
|
+
return EXIT_CODES.OK;
|
|
27985
|
+
}
|
|
27986
|
+
var RUNTIMES = [
|
|
27987
|
+
{
|
|
27988
|
+
provider: "claude",
|
|
27989
|
+
resolve: (deps2) => deps2.resolveClaude(),
|
|
27990
|
+
absent: [
|
|
27991
|
+
"note: the `claude` CLI is not installed \u2014 skipped its plugin.",
|
|
27992
|
+
" Install Claude Code (https://claude.com/claude-code), then run: jentrix plugin install"
|
|
27993
|
+
]
|
|
27994
|
+
},
|
|
27995
|
+
{
|
|
27996
|
+
provider: "codex",
|
|
27997
|
+
resolve: (deps2) => deps2.resolveCodex(),
|
|
27998
|
+
absent: [
|
|
27999
|
+
"note: the `codex` CLI is not installed \u2014 skipped its plugin and MCP server.",
|
|
28000
|
+
" Install Codex (https://developers.openai.com/codex/cli), then re-run this command."
|
|
28001
|
+
]
|
|
28002
|
+
}
|
|
28003
|
+
];
|
|
28004
|
+
async function installRuntimes(deps2, pending) {
|
|
28005
|
+
for (const runtime of RUNTIMES) {
|
|
28006
|
+
if (!await runtime.resolve(deps2)) {
|
|
28007
|
+
for (const line of runtime.absent) say(deps2, line);
|
|
28008
|
+
continue;
|
|
28009
|
+
}
|
|
28010
|
+
if (await deps2.installPlugin(runtime.provider) !== EXIT_CODES.OK) {
|
|
28011
|
+
pending.failedPlugins.push(runtime.provider);
|
|
28012
|
+
}
|
|
28013
|
+
}
|
|
28014
|
+
}
|
|
28015
|
+
async function registerCodexMcp(deps2, codex, pending) {
|
|
28016
|
+
const configPath = deps2.codexConfigPath();
|
|
28017
|
+
const config2 = deps2.readTextFile(configPath);
|
|
28018
|
+
const existingKey = config2 ? /^\[mcp_servers\.jentrix\]/m.test(config2) ? "jentrix" : /^\[mcp_servers\.stacks\]/m.test(config2) ? "stacks" : null : null;
|
|
28019
|
+
if (existingKey) {
|
|
28020
|
+
say(
|
|
28021
|
+
deps2,
|
|
28022
|
+
`note: Codex already has an [mcp_servers.${existingKey}] entry \u2014 left untouched.`
|
|
28023
|
+
);
|
|
28024
|
+
say(
|
|
28025
|
+
deps2,
|
|
28026
|
+
` It may point at a different deployment; check ${configPath}.`
|
|
28027
|
+
);
|
|
28028
|
+
say(
|
|
28029
|
+
deps2,
|
|
28030
|
+
` To move it off an exported STACKS_CODEX_TOKEN and onto sign-in:`
|
|
28031
|
+
);
|
|
28032
|
+
say(
|
|
28033
|
+
deps2,
|
|
28034
|
+
` \`codex mcp remove ${existingKey}\`, then re-run this command.`
|
|
28035
|
+
);
|
|
28036
|
+
return;
|
|
28037
|
+
}
|
|
28038
|
+
const url2 = deps2.signedInUrl();
|
|
28039
|
+
if (!url2) {
|
|
28040
|
+
say(
|
|
28041
|
+
deps2,
|
|
28042
|
+
"note: skipped the Codex MCP entry \u2014 this machine is not signed in yet, so"
|
|
28043
|
+
);
|
|
28044
|
+
say(
|
|
28045
|
+
deps2,
|
|
28046
|
+
" there is no endpoint to register. Run `jentrix login`, then re-run this command."
|
|
28047
|
+
);
|
|
28048
|
+
return;
|
|
28049
|
+
}
|
|
28050
|
+
const oauth = (await deps2.invoke(codex, ["mcp", "login", "--help"])).code === 0;
|
|
28051
|
+
if (config2 !== null) {
|
|
28052
|
+
const stamp = deps2.now().toISOString().replace(/[-:T]/g, "").slice(0, 14);
|
|
28053
|
+
deps2.copyFile(configPath, `${configPath}.bak-jentrix-${stamp}`);
|
|
28054
|
+
}
|
|
28055
|
+
const lines = ["", "# Added by the Jentrix installer."];
|
|
28056
|
+
lines.push(
|
|
28057
|
+
oauth ? "# Authenticated by `codex mcp login jentrix` \u2014 no token to export." : "# Needs STACKS_CODEX_TOKEN in the environment (a personal access token, scopes read + write)."
|
|
28058
|
+
);
|
|
28059
|
+
lines.push("[mcp_servers.jentrix]", `url = "${url2}"`);
|
|
28060
|
+
if (!oauth) lines.push('bearer_token_env_var = "STACKS_CODEX_TOKEN"');
|
|
28061
|
+
deps2.appendTextFile(configPath, `${lines.join("\n")}
|
|
28062
|
+
`);
|
|
28063
|
+
say(deps2, `Codex: registered [mcp_servers.jentrix] \u2192 ${url2}`);
|
|
28064
|
+
if (!oauth) {
|
|
28065
|
+
say(
|
|
28066
|
+
deps2,
|
|
28067
|
+
" (this Codex build has no `codex mcp login`, so the entry reads a token"
|
|
28068
|
+
);
|
|
28069
|
+
say(deps2, " from the environment)");
|
|
28070
|
+
pending.codexTokenUrl = `${url2.replace(/\/api\/mcp$/, "")}/account/tokens`;
|
|
28071
|
+
return;
|
|
28072
|
+
}
|
|
28073
|
+
if (!deps2.isInteractive) {
|
|
28074
|
+
pending.codexLoginUrl = url2;
|
|
28075
|
+
return;
|
|
28076
|
+
}
|
|
28077
|
+
say(
|
|
28078
|
+
deps2,
|
|
28079
|
+
"Signing Codex in \u2014 approve the Jentrix consent screen in your browser\u2026"
|
|
28080
|
+
);
|
|
28081
|
+
if ((await deps2.invoke(codex, ["mcp", "login", "jentrix"])).code !== 0) {
|
|
28082
|
+
pending.codexLoginUrl = url2;
|
|
28083
|
+
}
|
|
28084
|
+
}
|
|
28085
|
+
async function bootstrapRepository(deps2) {
|
|
28086
|
+
const here = deps2.cwd();
|
|
28087
|
+
if (!await deps2.resolveGit()) {
|
|
28088
|
+
say(deps2, "note: `git` is not installed \u2014 skipped the repository step.");
|
|
28089
|
+
say(
|
|
28090
|
+
deps2,
|
|
28091
|
+
" Jentrix aligns sessions by repository identity, so install git (https://git-scm.com/downloads) and re-run."
|
|
28092
|
+
);
|
|
28093
|
+
return;
|
|
28094
|
+
}
|
|
28095
|
+
const inRepo = async () => (await deps2.git(["rev-parse", "--is-inside-work-tree"], here)).code === 0;
|
|
28096
|
+
if (!await inRepo()) {
|
|
28097
|
+
const reply = await deps2.readLine(
|
|
28098
|
+
`Initialize a git repository in ${here} so agent sessions can align here? [y/N] `
|
|
28099
|
+
);
|
|
28100
|
+
if (!/^y/i.test(reply.trim())) return;
|
|
28101
|
+
if ((await deps2.git(["init"], here)).code !== 0) {
|
|
28102
|
+
say(deps2, "note: `git init` failed \u2014 skipped the repository step.");
|
|
28103
|
+
return;
|
|
28104
|
+
}
|
|
28105
|
+
}
|
|
28106
|
+
if ((await deps2.git(["remote", "get-url", "origin"], here)).code === 0)
|
|
28107
|
+
return;
|
|
28108
|
+
const fallback = here.split(/[/\\]/).filter(Boolean).pop().toLowerCase().replace(/ /g, "-");
|
|
28109
|
+
say(
|
|
28110
|
+
deps2,
|
|
28111
|
+
"Jentrix aligns sessions by repository identity (owner/name); the remote doesn't have to exist yet."
|
|
28112
|
+
);
|
|
28113
|
+
say(
|
|
28114
|
+
deps2,
|
|
28115
|
+
`Optional \u2014 leave it blank and this checkout aligns as local/${fallback}.`
|
|
28116
|
+
);
|
|
28117
|
+
const answer = (await deps2.readLine(
|
|
28118
|
+
"GitHub repo for origin (owner/name or full URL, blank to skip): "
|
|
28119
|
+
)).trim();
|
|
28120
|
+
if (!answer) return;
|
|
28121
|
+
const url2 = /:\/\/|^[^/]+@[^:]+:/.test(answer) ? answer : /^[^/]+\/[^/]+$/.test(answer) ? `https://github.com/${answer}.git` : null;
|
|
28122
|
+
if (!url2) {
|
|
28123
|
+
say(deps2, `skipped \u2014 "${answer}" is neither owner/name nor a URL`);
|
|
28124
|
+
return;
|
|
28125
|
+
}
|
|
28126
|
+
if ((await deps2.git(["remote", "add", "origin", url2], here)).code === 0) {
|
|
28127
|
+
say(deps2, `origin \u2192 ${url2}`);
|
|
28128
|
+
}
|
|
28129
|
+
}
|
|
28130
|
+
async function runSetupCommand(flags, deps2) {
|
|
28131
|
+
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
28132
|
+
if (Number.isFinite(nodeMajor) && nodeMajor < 20) {
|
|
28133
|
+
deps2.writeErr(
|
|
28134
|
+
`NODE_TOO_OLD: Jentrix requires Node.js >= 20 (running v${process.versions.node}) \u2014 upgrade from https://nodejs.org, then retry.`
|
|
28135
|
+
);
|
|
28136
|
+
return EXIT_CODES.INVALID_INPUT;
|
|
28137
|
+
}
|
|
28138
|
+
const toolchain = await installToolchain(deps2);
|
|
28139
|
+
if (toolchain !== EXIT_CODES.OK) return toolchain;
|
|
28140
|
+
const pending = { failedPlugins: [] };
|
|
28141
|
+
await installRuntimes(deps2, pending);
|
|
28142
|
+
if (!deps2.hasCredential()) {
|
|
28143
|
+
if (deps2.isInteractive) {
|
|
28144
|
+
say(deps2, "");
|
|
28145
|
+
say(deps2, "Connecting this machine to a Jentrix server\u2026");
|
|
28146
|
+
if (await deps2.login({ url: flags.url, local: flags.local }) !== 0) {
|
|
28147
|
+
say(
|
|
28148
|
+
deps2,
|
|
28149
|
+
"note: sign-in did not complete \u2014 run `jentrix login` when you're ready."
|
|
28150
|
+
);
|
|
28151
|
+
}
|
|
28152
|
+
} else {
|
|
28153
|
+
say(
|
|
28154
|
+
deps2,
|
|
28155
|
+
"note: no credential is configured \u2014 run `jentrix login` (interactive) to connect."
|
|
28156
|
+
);
|
|
28157
|
+
}
|
|
28158
|
+
}
|
|
28159
|
+
const codex = await deps2.resolveCodex();
|
|
28160
|
+
if (codex) await registerCodexMcp(deps2, codex, pending);
|
|
28161
|
+
if (flags.git !== false && deps2.isInteractive && deps2.cwd() !== deps2.homeDir()) {
|
|
28162
|
+
await bootstrapRepository(deps2);
|
|
28163
|
+
}
|
|
28164
|
+
say(deps2, "");
|
|
28165
|
+
if (pending.failedPlugins.length) {
|
|
28166
|
+
for (const provider of pending.failedPlugins) {
|
|
28167
|
+
say(
|
|
28168
|
+
deps2,
|
|
28169
|
+
`note: the ${provider} plugin did NOT install \u2014 its commands will be missing.`
|
|
28170
|
+
);
|
|
28171
|
+
say(
|
|
28172
|
+
deps2,
|
|
28173
|
+
` Run \`jentrix plugin install ${provider}\` on its own to see why.`
|
|
28174
|
+
);
|
|
28175
|
+
}
|
|
28176
|
+
say(deps2, "");
|
|
28177
|
+
}
|
|
28178
|
+
say(deps2, "Done. Verify with: jentrix whoami");
|
|
28179
|
+
if (pending.codexLoginUrl) {
|
|
28180
|
+
say(deps2, "");
|
|
28181
|
+
say(
|
|
28182
|
+
deps2,
|
|
28183
|
+
`One step left for Codex \u2014 approve its access to ${pending.codexLoginUrl}:`
|
|
28184
|
+
);
|
|
28185
|
+
say(
|
|
28186
|
+
deps2,
|
|
28187
|
+
" codex mcp login jentrix (opens the browser; no token to copy)"
|
|
28188
|
+
);
|
|
28189
|
+
}
|
|
28190
|
+
if (pending.codexTokenUrl) {
|
|
28191
|
+
say(deps2, "");
|
|
28192
|
+
say(
|
|
28193
|
+
deps2,
|
|
28194
|
+
"One step left for Codex \u2014 its MCP client carries its own token:"
|
|
28195
|
+
);
|
|
28196
|
+
say(
|
|
28197
|
+
deps2,
|
|
28198
|
+
` 1. mint a personal access token (scopes read + write) at ${pending.codexTokenUrl}`
|
|
28199
|
+
);
|
|
28200
|
+
say(
|
|
28201
|
+
deps2,
|
|
28202
|
+
" 2. export STACKS_CODEX_TOKEN in the shell you start Codex from"
|
|
28203
|
+
);
|
|
28204
|
+
say(deps2, " 3. restart Codex");
|
|
28205
|
+
say(
|
|
28206
|
+
deps2,
|
|
28207
|
+
" (deliberately not STACKS_TOKEN \u2014 that would also override the CLI's own"
|
|
28208
|
+
);
|
|
28209
|
+
say(deps2, " rotating login, which refreshes itself and needs no export)");
|
|
28210
|
+
}
|
|
28211
|
+
say(deps2, "");
|
|
28212
|
+
say(deps2, "Next: open your agent in the project folder and align \u2014");
|
|
28213
|
+
say(deps2, " Claude Code: /jentrix-align");
|
|
28214
|
+
say(deps2, " Codex: $jentrix-align");
|
|
28215
|
+
say(deps2, " any terminal: jentrix align");
|
|
28216
|
+
say(
|
|
28217
|
+
deps2,
|
|
28218
|
+
"(Bind a folder to a different server any time with: jentrix login --local)"
|
|
28219
|
+
);
|
|
28220
|
+
return pending.failedPlugins.length ? EXIT_CODES.INTERNAL : EXIT_CODES.OK;
|
|
28221
|
+
}
|
|
28222
|
+
function registerSetupCommand(program3, deps2, onExit2) {
|
|
28223
|
+
return program3.command("setup").description(
|
|
28224
|
+
"Install and connect the whole local toolchain \u2014 CLI, session runner, the plugins for whichever agent runtimes are present, browser sign-in, the Codex MCP server, and this folder's repository. The same command on Windows, macOS and Linux. Safe to re-run."
|
|
28225
|
+
).option(
|
|
28226
|
+
"--url <url>",
|
|
28227
|
+
"MCP endpoint to sign in against (default: the interactive server picker)"
|
|
28228
|
+
).option(
|
|
28229
|
+
"--local",
|
|
28230
|
+
"bind THIS folder to that server (./.stacks/config.json) instead of the machine-wide config"
|
|
28231
|
+
).option("--no-git", "never touch the current folder's git repository").action(async (options) => {
|
|
28232
|
+
onExit2(await runSetupCommand(options, deps2));
|
|
28233
|
+
});
|
|
28234
|
+
}
|
|
28235
|
+
|
|
27855
28236
|
// src/commands/snapshot.ts
|
|
27856
28237
|
import {
|
|
27857
28238
|
closeSync as closeSync2,
|
|
@@ -28190,12 +28571,12 @@ function surfaceFileMtime() {
|
|
|
28190
28571
|
}
|
|
28191
28572
|
}
|
|
28192
28573
|
function readStdin() {
|
|
28193
|
-
return new Promise((
|
|
28574
|
+
return new Promise((resolve5, reject) => {
|
|
28194
28575
|
const chunks = [];
|
|
28195
28576
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
28196
28577
|
process.stdin.on(
|
|
28197
28578
|
"end",
|
|
28198
|
-
() =>
|
|
28579
|
+
() => resolve5(Buffer.concat(chunks).toString("utf8"))
|
|
28199
28580
|
);
|
|
28200
28581
|
process.stdin.on("error", reject);
|
|
28201
28582
|
});
|
|
@@ -28206,14 +28587,14 @@ function configFile() {
|
|
|
28206
28587
|
return readConfigFile(CONFIG_PATH);
|
|
28207
28588
|
}
|
|
28208
28589
|
function readLine(prompt) {
|
|
28209
|
-
return new Promise((
|
|
28590
|
+
return new Promise((resolve5) => {
|
|
28210
28591
|
const rl = createInterface({
|
|
28211
28592
|
input: process.stdin,
|
|
28212
28593
|
output: process.stdout
|
|
28213
28594
|
});
|
|
28214
28595
|
rl.question(prompt, (answer) => {
|
|
28215
28596
|
rl.close();
|
|
28216
|
-
|
|
28597
|
+
resolve5(answer);
|
|
28217
28598
|
});
|
|
28218
28599
|
});
|
|
28219
28600
|
}
|
|
@@ -28271,7 +28652,7 @@ var deps = {
|
|
|
28271
28652
|
`),
|
|
28272
28653
|
writeErr: (text) => process.stderr.write(`${text}
|
|
28273
28654
|
`),
|
|
28274
|
-
sleep: (ms) => new Promise((
|
|
28655
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
28275
28656
|
now: () => Date.now()
|
|
28276
28657
|
};
|
|
28277
28658
|
var loginDeps = {
|
|
@@ -28403,10 +28784,16 @@ var sessionDeps = {
|
|
|
28403
28784
|
runSessionHost: (runnerBin, planPath) => runRunnerForeground(runnerBin, ["session-run", "--plan-file", planPath]),
|
|
28404
28785
|
spawnSessionHostDetached: (runnerBin, planPath, logPath) => {
|
|
28405
28786
|
const fd = openSync3(logPath, "a", 384);
|
|
28787
|
+
const target = execTarget(runnerBin, [
|
|
28788
|
+
"session-run",
|
|
28789
|
+
"--plan-file",
|
|
28790
|
+
planPath
|
|
28791
|
+
]);
|
|
28406
28792
|
try {
|
|
28407
|
-
const child = spawn3(
|
|
28793
|
+
const child = spawn3(target.file, target.args, {
|
|
28408
28794
|
detached: true,
|
|
28409
|
-
stdio: ["ignore", fd, fd]
|
|
28795
|
+
stdio: ["ignore", fd, fd],
|
|
28796
|
+
...target.options
|
|
28410
28797
|
});
|
|
28411
28798
|
child.unref();
|
|
28412
28799
|
return child.pid ?? -1;
|
|
@@ -28420,36 +28807,121 @@ var sessionCommand = registerSessionCommand(program2, sessionDeps, onExit);
|
|
|
28420
28807
|
registerSnapshotCommand(sessionCommand, sessionDeps, onExit);
|
|
28421
28808
|
registerAlignCommand(program2, sessionDeps, onExit);
|
|
28422
28809
|
registerPushCommand(program2, sessionDeps, onExit);
|
|
28810
|
+
var PACKAGE_ROOT = fileURLToPath(new URL("..", import.meta.url));
|
|
28423
28811
|
var PLUGIN_DIR = fileURLToPath(new URL("../claude-plugin", import.meta.url));
|
|
28424
28812
|
var CODEX_PLUGIN_DIR = fileURLToPath(
|
|
28425
28813
|
new URL("../codex-plugin", import.meta.url)
|
|
28426
28814
|
);
|
|
28427
|
-
|
|
28428
|
-
|
|
28429
|
-
|
|
28430
|
-
|
|
28431
|
-
|
|
28432
|
-
|
|
28433
|
-
|
|
28434
|
-
|
|
28435
|
-
|
|
28436
|
-
invoke: invokeRunnerProcess,
|
|
28437
|
-
writeOut: (text) => process.stdout.write(`${text}
|
|
28815
|
+
var pluginDeps = {
|
|
28816
|
+
resolvePluginDir: () => existsSync6(PLUGIN_DIR) && isPluginMarketplaceDir(PLUGIN_DIR) ? PLUGIN_DIR : null,
|
|
28817
|
+
resolveCodexPluginDir: () => existsSync6(CODEX_PLUGIN_DIR) && isCodexPluginMarketplaceDir(CODEX_PLUGIN_DIR) ? CODEX_PLUGIN_DIR : null,
|
|
28818
|
+
resolveClaude: () => resolveExecutableOnPath(platformExecutableNames("claude"), process.env),
|
|
28819
|
+
resolveCodex: () => resolveExecutableOnPath(platformExecutableNames("codex"), process.env),
|
|
28820
|
+
resolveRunner: () => resolveRunnerExecutable(process.env),
|
|
28821
|
+
resolveNpm: () => resolveExecutableOnPath(platformExecutableNames("npm"), process.env),
|
|
28822
|
+
invoke: invokeRunnerProcess,
|
|
28823
|
+
writeOut: (text) => process.stdout.write(`${text}
|
|
28438
28824
|
`),
|
|
28439
|
-
|
|
28825
|
+
writeErr: (text) => process.stderr.write(`${text}
|
|
28440
28826
|
`),
|
|
28441
|
-
|
|
28442
|
-
|
|
28443
|
-
|
|
28827
|
+
// AGE-952: the one-liner install ends connected — same in-process login
|
|
28828
|
+
// reuse as runner setup (never a `jentrix` subprocess).
|
|
28829
|
+
hasCredential: () => {
|
|
28830
|
+
try {
|
|
28831
|
+
resolveConfig({ env: process.env, file: configFile() });
|
|
28832
|
+
return true;
|
|
28833
|
+
} catch {
|
|
28834
|
+
return false;
|
|
28835
|
+
}
|
|
28836
|
+
},
|
|
28837
|
+
login: () => runLoginCommand({}, loginDeps),
|
|
28838
|
+
isInteractive: Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
28839
|
+
};
|
|
28840
|
+
registerPluginCommand(program2, pluginDeps, onExit);
|
|
28841
|
+
registerSetupCommand(
|
|
28842
|
+
program2,
|
|
28843
|
+
{
|
|
28844
|
+
...pluginDeps,
|
|
28845
|
+
// npm's global prefix, read from npm itself — never a PATH probe. Under
|
|
28846
|
+
// `npx @jentrix/cli setup` the _npx cache is prepended to PATH and holds a
|
|
28847
|
+
// `jentrix` shim, so a PATH answer is always yes and always meaningless.
|
|
28848
|
+
globalInstall: async () => {
|
|
28849
|
+
const npm = await resolveExecutableOnPath(
|
|
28850
|
+
platformExecutableNames("npm"),
|
|
28851
|
+
process.env
|
|
28852
|
+
);
|
|
28853
|
+
const ask = async (args) => {
|
|
28854
|
+
if (!npm) return null;
|
|
28855
|
+
const out = (await invokeRunnerProcess(npm, args)).stdout.trim();
|
|
28856
|
+
return out || null;
|
|
28857
|
+
};
|
|
28858
|
+
const root = await ask(["root", "-g"]);
|
|
28859
|
+
const prefix = await ask(["prefix", "-g"]);
|
|
28860
|
+
const binDir = prefix ? process.platform === "win32" ? prefix : join6(prefix, "bin") : null;
|
|
28861
|
+
const dir = (name) => {
|
|
28862
|
+
if (!root) return null;
|
|
28863
|
+
const candidate = join6(root, "@jentrix", name);
|
|
28864
|
+
return existsSync6(join6(candidate, "package.json")) ? candidate : null;
|
|
28865
|
+
};
|
|
28866
|
+
return {
|
|
28867
|
+
cliDir: dir("cli"),
|
|
28868
|
+
runnerDir: dir("runner"),
|
|
28869
|
+
binDir,
|
|
28870
|
+
binDirOnPath: Boolean(
|
|
28871
|
+
binDir && (process.env.PATH ?? "").split(delimiter2).some((entry) => entry && resolve4(entry) === resolve4(binDir))
|
|
28872
|
+
)
|
|
28873
|
+
};
|
|
28874
|
+
},
|
|
28875
|
+
resolveGit: () => resolveExecutableOnPath(platformExecutableNames("git"), process.env),
|
|
28876
|
+
// Register the plugin from whichever copy of the package PERSISTS. Under
|
|
28877
|
+
// `npx @jentrix/cli setup` the running module lives in npm's _npx cache,
|
|
28878
|
+
// and a marketplace registered from there dangles the moment npm prunes
|
|
28879
|
+
// it. `npm root -g` names the copy that stays.
|
|
28880
|
+
installPlugin: async (provider) => {
|
|
28881
|
+
const npm = await resolveExecutableOnPath(
|
|
28882
|
+
platformExecutableNames("npm"),
|
|
28883
|
+
process.env
|
|
28884
|
+
);
|
|
28885
|
+
const root = npm ? (await invokeRunnerProcess(npm, ["root", "-g"])).stdout.trim() || null : null;
|
|
28886
|
+
const packageRoot = persistentPluginRoot(
|
|
28887
|
+
root,
|
|
28888
|
+
PACKAGE_ROOT,
|
|
28889
|
+
(dir) => isPluginMarketplaceDir(join6(dir, "claude-plugin"))
|
|
28890
|
+
);
|
|
28891
|
+
return runPluginInstall(
|
|
28892
|
+
packageRoot === PACKAGE_ROOT ? pluginDeps : {
|
|
28893
|
+
...pluginDeps,
|
|
28894
|
+
resolvePluginDir: () => join6(packageRoot, "claude-plugin"),
|
|
28895
|
+
resolveCodexPluginDir: () => {
|
|
28896
|
+
const dir = join6(packageRoot, "codex-plugin");
|
|
28897
|
+
return isCodexPluginMarketplaceDir(dir) ? dir : null;
|
|
28898
|
+
}
|
|
28899
|
+
},
|
|
28900
|
+
provider
|
|
28901
|
+
);
|
|
28902
|
+
},
|
|
28903
|
+
// The endpoint a token actually resolves against — the in-process answer
|
|
28904
|
+
// to what install.sh had to ask for with `jentrix whoami --json`.
|
|
28905
|
+
signedInUrl: () => {
|
|
28444
28906
|
try {
|
|
28445
|
-
resolveConfig({ env: process.env, file: configFile() });
|
|
28446
|
-
return true;
|
|
28907
|
+
return resolveConfig({ env: process.env, file: configFile() }).url;
|
|
28447
28908
|
} catch {
|
|
28448
|
-
return
|
|
28909
|
+
return null;
|
|
28449
28910
|
}
|
|
28450
28911
|
},
|
|
28451
|
-
login: () => runLoginCommand(
|
|
28452
|
-
|
|
28912
|
+
login: (flags) => runLoginCommand(flags, loginDeps),
|
|
28913
|
+
codexConfigPath: () => join6(process.env.CODEX_HOME ?? join6(homedir(), ".codex"), "config.toml"),
|
|
28914
|
+
readTextFile: (path) => existsSync6(path) ? readFileSync7(path, "utf8") : null,
|
|
28915
|
+
appendTextFile: (path, text) => {
|
|
28916
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
28917
|
+
appendFileSync(path, text);
|
|
28918
|
+
},
|
|
28919
|
+
copyFile: (from, to) => copyFileSync(from, to),
|
|
28920
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
28921
|
+
cwd: () => process.cwd(),
|
|
28922
|
+
homeDir: () => homedir(),
|
|
28923
|
+
git: defaultGitRunner,
|
|
28924
|
+
readLine
|
|
28453
28925
|
},
|
|
28454
28926
|
onExit
|
|
28455
28927
|
);
|