@textcortex/zenocode 0.1.2 → 0.1.3
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/README.md +31 -0
- package/package.json +8 -8
- package/scripts/branding-patch.mjs +112 -3
- package/scripts/branding-patch.test.mjs +17 -0
- package/scripts/build-branded-opencode.mjs +520 -78
- package/scripts/build-branded-opencode.test.mjs +25 -0
- package/scripts/{run-codecortex.mjs → run-zenocode.mjs} +323 -80
- package/scripts/run-zenocode.test.mjs +224 -0
- package/scripts/run-codecortex.test.mjs +0 -59
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import {
|
|
4
|
+
buildWrapperBinMap,
|
|
5
|
+
mapBrandedBinaryPackageName,
|
|
6
|
+
} from "./build-branded-opencode.mjs";
|
|
7
|
+
|
|
8
|
+
test("mapBrandedBinaryPackageName scopes runtime binaries under zenocode", () => {
|
|
9
|
+
assert.equal(
|
|
10
|
+
mapBrandedBinaryPackageName("opencode-darwin-arm64", "@textcortex/zenocode-ai"),
|
|
11
|
+
"@textcortex/zenocode-darwin-arm64",
|
|
12
|
+
);
|
|
13
|
+
assert.equal(
|
|
14
|
+
mapBrandedBinaryPackageName("opencode-linux-x64-baseline-musl", "@textcortex/zenocode-ai"),
|
|
15
|
+
"@textcortex/zenocode-linux-x64-baseline-musl",
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("buildWrapperBinMap includes package, opencode, and zenocode entrypoints", () => {
|
|
20
|
+
assert.deepEqual(buildWrapperBinMap("@textcortex/zenocode-ai", "zenocode"), {
|
|
21
|
+
"zenocode-ai": "./bin/opencode",
|
|
22
|
+
opencode: "./bin/opencode",
|
|
23
|
+
zenocode: "./bin/opencode",
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -7,7 +7,10 @@ import path from "node:path";
|
|
|
7
7
|
import process from "node:process";
|
|
8
8
|
import readline from "node:readline/promises";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
patchZenocodeBinaryText,
|
|
12
|
+
zenocodeLogo,
|
|
13
|
+
} from "./branding-patch.mjs";
|
|
11
14
|
|
|
12
15
|
const currentFilePath = realpathSync(fileURLToPath(import.meta.url));
|
|
13
16
|
const __dirname = path.dirname(currentFilePath);
|
|
@@ -66,19 +69,22 @@ const credentialFiles = [
|
|
|
66
69
|
path.join(os.homedir(), ".credentials.json"),
|
|
67
70
|
...devCredentialFiles,
|
|
68
71
|
].filter((value, index, values) => values.indexOf(value) === index);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
|
|
73
|
+
function _decodeEscapedLogoLine(line) {
|
|
74
|
+
return line.replace(/\\u([0-9a-fA-F]{4})/g, (_, codePoint) =>
|
|
75
|
+
String.fromCharCode(Number.parseInt(codePoint, 16)),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function buildZenocodeBanner() {
|
|
80
|
+
const bannerLines = zenocodeLogo.left
|
|
81
|
+
.map((leftLine, index) => {
|
|
82
|
+
const rightLine = zenocodeLogo.right[index] || "";
|
|
83
|
+
return `${_decodeEscapedLogoLine(leftLine)} ${_decodeEscapedLogoLine(rightLine)}`.trimEnd();
|
|
84
|
+
})
|
|
85
|
+
.filter((line) => line.trim());
|
|
86
|
+
return `\n${bannerLines.join("\n")}\n`;
|
|
87
|
+
}
|
|
82
88
|
const privateDirectoryMode = 0o700;
|
|
83
89
|
const privateFileMode = 0o600;
|
|
84
90
|
|
|
@@ -503,16 +509,28 @@ async function runLoginCommand(baseUrl, args) {
|
|
|
503
509
|
}
|
|
504
510
|
|
|
505
511
|
async function runLogoutCommand() {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
512
|
+
let removedAnyCredentials = false;
|
|
513
|
+
for (const credentialsPath of [
|
|
514
|
+
runtimeCredentialsPath,
|
|
515
|
+
legacyRuntimeCredentialsPath,
|
|
516
|
+
].filter((value, index, values) => values.indexOf(value) === index)) {
|
|
517
|
+
try {
|
|
518
|
+
await fs.unlink(credentialsPath);
|
|
519
|
+
removedAnyCredentials = true;
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (error?.code === "ENOENT") {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
throw error;
|
|
513
525
|
}
|
|
514
|
-
throw error;
|
|
515
526
|
}
|
|
527
|
+
|
|
528
|
+
if (removedAnyCredentials) {
|
|
529
|
+
console.log("Zenocode credentials removed.");
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
console.log("No local Zenocode credentials found.");
|
|
516
534
|
}
|
|
517
535
|
|
|
518
536
|
async function runChild(command, args, options) {
|
|
@@ -527,8 +545,130 @@ async function runChild(command, args, options) {
|
|
|
527
545
|
});
|
|
528
546
|
}
|
|
529
547
|
|
|
530
|
-
|
|
531
|
-
|
|
548
|
+
function _buildScriptCommand(runtimeBinaryPath, args, transcriptPath) {
|
|
549
|
+
if (process.platform === "darwin") {
|
|
550
|
+
return {
|
|
551
|
+
command: "script",
|
|
552
|
+
args: ["-q", "-F", "-t", "0", transcriptPath, runtimeBinaryPath, ...args],
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
if (process.platform === "linux") {
|
|
556
|
+
const shellCommand = [runtimeBinaryPath, ...args]
|
|
557
|
+
.map((value) => `'${String(value).replaceAll("'", `'\\''`)}'`)
|
|
558
|
+
.join(" ");
|
|
559
|
+
return {
|
|
560
|
+
command: "script",
|
|
561
|
+
args: ["-q", "-f", "-c", shellCommand, transcriptPath],
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async function _readTranscriptUpdate(transcriptPath, offset) {
|
|
568
|
+
let handle;
|
|
569
|
+
try {
|
|
570
|
+
handle = await fs.open(transcriptPath, "r");
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if (error?.code === "ENOENT") {
|
|
573
|
+
return { offset, text: "" };
|
|
574
|
+
}
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
try {
|
|
579
|
+
const stats = await handle.stat();
|
|
580
|
+
if (stats.size <= offset) {
|
|
581
|
+
return { offset, text: "" };
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const length = stats.size - offset;
|
|
585
|
+
const buffer = Buffer.alloc(length);
|
|
586
|
+
await handle.read(buffer, 0, length, offset);
|
|
587
|
+
return { offset: stats.size, text: buffer.toString("utf-8") };
|
|
588
|
+
} finally {
|
|
589
|
+
await handle.close();
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
async function _terminateChildProcess(child, exitPromise) {
|
|
594
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
595
|
+
return exitPromise;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
child.kill("SIGTERM");
|
|
599
|
+
const exited = await Promise.race([exitPromise, sleep(1_500).then(() => null)]);
|
|
600
|
+
if (exited) {
|
|
601
|
+
return exited;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
child.kill("SIGKILL");
|
|
605
|
+
return exitPromise;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async function _runRuntimeWithTranscriptMonitor(runtimeBinaryPath, args, options) {
|
|
609
|
+
const transcriptDir = await fs.mkdtemp(path.join(os.tmpdir(), "zenocode-runtime-"));
|
|
610
|
+
const transcriptPath = path.join(transcriptDir, "session.log");
|
|
611
|
+
const command = _buildScriptCommand(runtimeBinaryPath, args, transcriptPath);
|
|
612
|
+
if (!command) {
|
|
613
|
+
const result = await runChild(runtimeBinaryPath, args, { ...options, stdio: "inherit" });
|
|
614
|
+
return { ...result, expiredSession: false };
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
try {
|
|
618
|
+
const child = spawn(command.command, command.args, {
|
|
619
|
+
...options,
|
|
620
|
+
stdio: "inherit",
|
|
621
|
+
});
|
|
622
|
+
const exitPromise = new Promise((resolve, reject) => {
|
|
623
|
+
child.on("error", reject);
|
|
624
|
+
child.on("exit", (code, signal) => resolve({ code, signal, expiredSession: false }));
|
|
625
|
+
});
|
|
626
|
+
let transcriptOffset = 0;
|
|
627
|
+
let transcriptTail = "";
|
|
628
|
+
|
|
629
|
+
while (true) {
|
|
630
|
+
const outcome = await Promise.race([
|
|
631
|
+
exitPromise.then((result) => ({ type: "exit", result })),
|
|
632
|
+
sleep(250).then(() => ({ type: "poll" })),
|
|
633
|
+
]);
|
|
634
|
+
|
|
635
|
+
const update = await _readTranscriptUpdate(transcriptPath, transcriptOffset);
|
|
636
|
+
transcriptOffset = update.offset;
|
|
637
|
+
if (update.text) {
|
|
638
|
+
transcriptTail = _appendTranscriptTail(transcriptTail, update.text);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (canRecoverRuntimeSessionFromTranscript(transcriptTail)) {
|
|
642
|
+
await _terminateChildProcess(child, exitPromise);
|
|
643
|
+
return { expiredSession: true };
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (outcome.type === "exit") {
|
|
647
|
+
return canRecoverRuntimeSessionFromTranscript(transcriptTail)
|
|
648
|
+
? { expiredSession: true }
|
|
649
|
+
: outcome.result;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
} catch (error) {
|
|
653
|
+
if (error?.code === "ENOENT") {
|
|
654
|
+
const result = await runChild(runtimeBinaryPath, args, { ...options, stdio: "inherit" });
|
|
655
|
+
return { ...result, expiredSession: false };
|
|
656
|
+
}
|
|
657
|
+
throw error;
|
|
658
|
+
} finally {
|
|
659
|
+
await fs.rm(transcriptDir, { recursive: true, force: true });
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
async function runRuntimeBinary(runtimeBinaryPath, args, options, monitorRuntimeSession) {
|
|
664
|
+
if (!monitorRuntimeSession) {
|
|
665
|
+
const result = await runChild(runtimeBinaryPath, args, { ...options, stdio: "inherit" });
|
|
666
|
+
return { ...result, expiredSession: false };
|
|
667
|
+
}
|
|
668
|
+
return _runRuntimeWithTranscriptMonitor(runtimeBinaryPath, args, options);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function exitWithChildResult(result) {
|
|
532
672
|
if (result.signal) {
|
|
533
673
|
process.kill(process.pid, result.signal);
|
|
534
674
|
return;
|
|
@@ -553,14 +693,6 @@ function shouldPatchOpencodeRuntimePackage(packageName) {
|
|
|
553
693
|
);
|
|
554
694
|
}
|
|
555
695
|
|
|
556
|
-
function _buildPaddedLogoSnippet(coreSnippet) {
|
|
557
|
-
return padBinaryReplacement(opencodeLogoSnippet, coreSnippet);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
function _buildZenocodeLogoSnippet() {
|
|
561
|
-
return _buildPaddedLogoSnippet(zenocodeTextLogoSnippetCore);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
696
|
async function _pathExists(pathToCheck) {
|
|
565
697
|
try {
|
|
566
698
|
await fs.access(pathToCheck);
|
|
@@ -688,26 +820,6 @@ async function _adHocSignBinary(binaryPath) {
|
|
|
688
820
|
}
|
|
689
821
|
}
|
|
690
822
|
|
|
691
|
-
function _patchLogoSnippetText(text) {
|
|
692
|
-
const replacement = _buildZenocodeLogoSnippet();
|
|
693
|
-
if (!replacement || text.includes(replacement)) {
|
|
694
|
-
return { patched: false, text };
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
const patchTargets = [opencodeLogoSnippet].filter(Boolean);
|
|
698
|
-
let logoOffset = -1;
|
|
699
|
-
for (const target of patchTargets) {
|
|
700
|
-
logoOffset = text.indexOf(target);
|
|
701
|
-
if (logoOffset !== -1) break;
|
|
702
|
-
}
|
|
703
|
-
if (logoOffset === -1) {
|
|
704
|
-
return { patched: false, text };
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const nextText = `${text.slice(0, logoOffset)}${replacement}${text.slice(logoOffset + replacement.length)}`;
|
|
708
|
-
return { patched: true, text: nextText };
|
|
709
|
-
}
|
|
710
|
-
|
|
711
823
|
async function _patchOpencodeBinaryBranding(binaryPath) {
|
|
712
824
|
let buffer;
|
|
713
825
|
try {
|
|
@@ -718,19 +830,9 @@ async function _patchOpencodeBinaryBranding(binaryPath) {
|
|
|
718
830
|
|
|
719
831
|
const originalLength = buffer.length;
|
|
720
832
|
let text = buffer.toString("latin1");
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
const
|
|
724
|
-
if (logoPatch.patched) {
|
|
725
|
-
text = logoPatch.text;
|
|
726
|
-
patched = true;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
const footerPatch = patchOpenCodeVersionFooterText(text);
|
|
730
|
-
if (footerPatch.patched) {
|
|
731
|
-
text = footerPatch.text;
|
|
732
|
-
patched = true;
|
|
733
|
-
}
|
|
833
|
+
const patch = patchZenocodeBinaryText(text);
|
|
834
|
+
text = patch.text;
|
|
835
|
+
const patched = patch.patched;
|
|
734
836
|
|
|
735
837
|
if (!patched) {
|
|
736
838
|
return false;
|
|
@@ -859,6 +961,88 @@ async function runPackageLauncher(packageName, args, options) {
|
|
|
859
961
|
);
|
|
860
962
|
}
|
|
861
963
|
|
|
964
|
+
async function resolvePinnedRuntimeBinary(packageName, options) {
|
|
965
|
+
const runners = [
|
|
966
|
+
{ command: _runnerCommand("pnpm"), args: ["dlx", packageName] },
|
|
967
|
+
{ command: _runnerCommand("npx"), args: ["--yes", packageName] },
|
|
968
|
+
];
|
|
969
|
+
const missingRunners = [];
|
|
970
|
+
|
|
971
|
+
for (const runner of runners) {
|
|
972
|
+
try {
|
|
973
|
+
let pinnedRuntimePath = await _ensurePatchedOpencodeDlxBinaries(packageName, runner, options);
|
|
974
|
+
if (!pinnedRuntimePath && shouldPatchOpencodeRuntimePackage(packageName)) {
|
|
975
|
+
const existingPinnedPath = _runtimeBrandedBinaryPath();
|
|
976
|
+
if (await _pathExists(existingPinnedPath)) {
|
|
977
|
+
pinnedRuntimePath = existingPinnedPath;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
if (pinnedRuntimePath) {
|
|
982
|
+
return pinnedRuntimePath;
|
|
983
|
+
}
|
|
984
|
+
} catch (error) {
|
|
985
|
+
if (error?.code === "ENOENT") {
|
|
986
|
+
missingRunners.push(runner.command);
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
throw error;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
if (missingRunners.length === runners.length) {
|
|
994
|
+
throw new Error(
|
|
995
|
+
`No package runner found (${missingRunners.join(", ")}). Install pnpm or npm, or set ZENOCODE_OPENCODE_BIN_PATH/CODECORTEX_OPENCODE_BIN_PATH.`,
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
export async function runRuntimeWithSessionRecovery({
|
|
1003
|
+
args,
|
|
1004
|
+
baseUrl,
|
|
1005
|
+
token,
|
|
1006
|
+
childOptions,
|
|
1007
|
+
canAutoLoginRuntime,
|
|
1008
|
+
runLogin = runLoginCommand,
|
|
1009
|
+
resolveTokenFn = resolveToken,
|
|
1010
|
+
resolveStoredBaseUrlFn = resolveStoredBaseUrl,
|
|
1011
|
+
prepareRuntimeFn = prepareRuntime,
|
|
1012
|
+
launchRuntimeFn,
|
|
1013
|
+
}) {
|
|
1014
|
+
let activeBaseUrl = baseUrl;
|
|
1015
|
+
let activeToken = token;
|
|
1016
|
+
|
|
1017
|
+
while (true) {
|
|
1018
|
+
const result = await launchRuntimeFn({
|
|
1019
|
+
args,
|
|
1020
|
+
childOptions: {
|
|
1021
|
+
...childOptions,
|
|
1022
|
+
env: {
|
|
1023
|
+
...(childOptions.env || {}),
|
|
1024
|
+
TEXTCORTEX_API_KEY: activeToken,
|
|
1025
|
+
},
|
|
1026
|
+
},
|
|
1027
|
+
token: activeToken,
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
if (!result.expiredSession) {
|
|
1031
|
+
return result;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
if (!canAutoLoginRuntime) {
|
|
1035
|
+
return result;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
console.log("Zenocode session expired. Starting login flow...\n");
|
|
1039
|
+
await runLogin(activeBaseUrl, buildAutoLoginArgs());
|
|
1040
|
+
activeToken = await resolveTokenFn();
|
|
1041
|
+
activeBaseUrl = process.env.TEXTCORTEX_BASE_URL || (await resolveStoredBaseUrlFn()) || activeBaseUrl;
|
|
1042
|
+
await prepareRuntimeFn(activeBaseUrl, activeToken);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
862
1046
|
async function packageExistsOnNpm(packageName) {
|
|
863
1047
|
const controller = new AbortController();
|
|
864
1048
|
const timeout = setTimeout(() => controller.abort(), 4_000);
|
|
@@ -925,7 +1109,14 @@ function maybeRenderBanner(args) {
|
|
|
925
1109
|
if (!shouldRenderBanner(args)) {
|
|
926
1110
|
return;
|
|
927
1111
|
}
|
|
928
|
-
console.log(`${
|
|
1112
|
+
console.log(`${buildZenocodeBanner()}\n`);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
function buildAutoLoginArgs() {
|
|
1116
|
+
return process.env.ZENOCODE_AUTO_LOGIN_NO_BROWSER === "1" ||
|
|
1117
|
+
process.env.CODECORTEX_AUTO_LOGIN_NO_BROWSER === "1"
|
|
1118
|
+
? ["--no-launch-browser"]
|
|
1119
|
+
: [];
|
|
929
1120
|
}
|
|
930
1121
|
|
|
931
1122
|
function isMissingTokenError(error) {
|
|
@@ -954,6 +1145,45 @@ function canAutoLogin(args) {
|
|
|
954
1145
|
return true;
|
|
955
1146
|
}
|
|
956
1147
|
|
|
1148
|
+
const ansiSequencePattern =
|
|
1149
|
+
/\u001B(?:\[[0-?]*[ -/]*[@-~]|\][^\u0007]*(?:\u0007|\u001B\\)|[@-Z\\-_])/g;
|
|
1150
|
+
|
|
1151
|
+
function _stripTerminalControlCharacters(text) {
|
|
1152
|
+
let normalized = "";
|
|
1153
|
+
for (const char of text) {
|
|
1154
|
+
if (char === "\b" || char === "\u007f") {
|
|
1155
|
+
normalized = normalized.slice(0, -1);
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
if (char === "\r") {
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
if (char < " " && char !== "\n" && char !== "\t") {
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
normalized += char;
|
|
1165
|
+
}
|
|
1166
|
+
return normalized;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
export function normalizeRuntimeTranscript(text) {
|
|
1170
|
+
return _stripTerminalControlCharacters(text.replaceAll(ansiSequencePattern, ""));
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
export function canRecoverRuntimeSessionFromTranscript(text) {
|
|
1174
|
+
const normalized = normalizeRuntimeTranscript(text);
|
|
1175
|
+
return (
|
|
1176
|
+
/Unauthorized:\s*\{\s*"detail"\s*:\s*"Token has expired"\s*\}/i.test(normalized) ||
|
|
1177
|
+
/authentication token may be missing or expired/i.test(normalized) ||
|
|
1178
|
+
/server returned 401 after successful authentication/i.test(normalized)
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function _appendTranscriptTail(tail, nextChunk, maxLength = 32_768) {
|
|
1183
|
+
const combined = `${tail}${normalizeRuntimeTranscript(nextChunk)}`;
|
|
1184
|
+
return combined.length > maxLength ? combined.slice(-maxLength) : combined;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
957
1187
|
function shouldAttemptAutoLogin(error, args) {
|
|
958
1188
|
if (!isMissingTokenError(error)) return false;
|
|
959
1189
|
return canAutoLogin(args);
|
|
@@ -969,12 +1199,7 @@ async function resolveTokenWithAutoLogin(baseUrl, args) {
|
|
|
969
1199
|
}
|
|
970
1200
|
|
|
971
1201
|
console.log("No local Zenocode credentials found. Starting login flow...\n");
|
|
972
|
-
|
|
973
|
-
process.env.ZENOCODE_AUTO_LOGIN_NO_BROWSER === "1" ||
|
|
974
|
-
process.env.CODECORTEX_AUTO_LOGIN_NO_BROWSER === "1"
|
|
975
|
-
? ["--no-launch-browser"]
|
|
976
|
-
: [];
|
|
977
|
-
await runLoginCommand(baseUrl, loginArgs);
|
|
1202
|
+
await runLoginCommand(baseUrl, buildAutoLoginArgs());
|
|
978
1203
|
const token = await resolveToken();
|
|
979
1204
|
const persistedBaseUrl = process.env.TEXTCORTEX_BASE_URL || (await resolveStoredBaseUrl()) || baseUrl;
|
|
980
1205
|
return { token, baseUrl: persistedBaseUrl };
|
|
@@ -994,12 +1219,7 @@ async function prepareRuntimeWithAutoLogin(baseUrl, token, args) {
|
|
|
994
1219
|
}
|
|
995
1220
|
|
|
996
1221
|
console.log("Zenocode session expired. Starting login flow...\n");
|
|
997
|
-
|
|
998
|
-
process.env.ZENOCODE_AUTO_LOGIN_NO_BROWSER === "1" ||
|
|
999
|
-
process.env.CODECORTEX_AUTO_LOGIN_NO_BROWSER === "1"
|
|
1000
|
-
? ["--no-launch-browser"]
|
|
1001
|
-
: [];
|
|
1002
|
-
await runLoginCommand(baseUrl, loginArgs);
|
|
1222
|
+
await runLoginCommand(baseUrl, buildAutoLoginArgs());
|
|
1003
1223
|
const refreshedToken = await resolveToken();
|
|
1004
1224
|
const refreshedBaseUrl = process.env.TEXTCORTEX_BASE_URL || (await resolveStoredBaseUrl()) || baseUrl;
|
|
1005
1225
|
const model = await prepareRuntime(refreshedBaseUrl, refreshedToken);
|
|
@@ -1047,7 +1267,6 @@ async function main() {
|
|
|
1047
1267
|
|
|
1048
1268
|
const childOptions = {
|
|
1049
1269
|
cwd: process.cwd(),
|
|
1050
|
-
stdio: "inherit",
|
|
1051
1270
|
env: {
|
|
1052
1271
|
...process.env,
|
|
1053
1272
|
OPENCODE_MODELS_PATH: modelsPath,
|
|
@@ -1055,13 +1274,37 @@ async function main() {
|
|
|
1055
1274
|
TEXTCORTEX_API_KEY: token,
|
|
1056
1275
|
},
|
|
1057
1276
|
};
|
|
1277
|
+
const monitorRuntimeSession = canAutoLogin(passthrough);
|
|
1058
1278
|
|
|
1059
1279
|
if (opencodeBinaryPath) {
|
|
1060
|
-
await
|
|
1280
|
+
const result = await runRuntimeWithSessionRecovery({
|
|
1281
|
+
args: passthrough,
|
|
1282
|
+
baseUrl: runtime.baseUrl,
|
|
1283
|
+
token,
|
|
1284
|
+
childOptions,
|
|
1285
|
+
canAutoLoginRuntime: monitorRuntimeSession,
|
|
1286
|
+
launchRuntimeFn: ({ args, childOptions }) =>
|
|
1287
|
+
runRuntimeBinary(opencodeBinaryPath, args, childOptions, monitorRuntimeSession),
|
|
1288
|
+
});
|
|
1289
|
+
exitWithChildResult(result);
|
|
1061
1290
|
return;
|
|
1062
1291
|
}
|
|
1063
1292
|
|
|
1064
1293
|
const launchPackage = await resolveLaunchPackage();
|
|
1294
|
+
const pinnedRuntimePath = await resolvePinnedRuntimeBinary(launchPackage, childOptions);
|
|
1295
|
+
if (pinnedRuntimePath) {
|
|
1296
|
+
const result = await runRuntimeWithSessionRecovery({
|
|
1297
|
+
args: passthrough,
|
|
1298
|
+
baseUrl: runtime.baseUrl,
|
|
1299
|
+
token,
|
|
1300
|
+
childOptions,
|
|
1301
|
+
canAutoLoginRuntime: monitorRuntimeSession,
|
|
1302
|
+
launchRuntimeFn: ({ args, childOptions }) =>
|
|
1303
|
+
runRuntimeBinary(pinnedRuntimePath, args, childOptions, monitorRuntimeSession),
|
|
1304
|
+
});
|
|
1305
|
+
exitWithChildResult(result);
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1065
1308
|
await runPackageLauncher(launchPackage, passthrough, childOptions);
|
|
1066
1309
|
}
|
|
1067
1310
|
|