@mytegroupinc/myte-core 0.0.41 → 0.0.43
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 +3 -1
- package/lib/mytecody-splash.js +3 -3
- package/mytecody-cli.js +468 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,7 +31,9 @@ This package exists so the public wrapper can stay small and versioned cleanly.
|
|
|
31
31
|
run, it installs the branded MyteCody engine and signed client assets from
|
|
32
32
|
the Myte release manifest into a user-local Myte cache after signature/hash
|
|
33
33
|
checks. Coding execution requires those assets, a reachable Myte AI Cody
|
|
34
|
-
gateway, and `MYTEAI_API_KEY`.
|
|
34
|
+
gateway, and `MYTEAI_API_KEY`. Normal coding runs use the branded Codex
|
|
35
|
+
harness with Myte context hardening; the experimental controller is not the
|
|
36
|
+
default product path.
|
|
35
37
|
- `mytecody doctor` reports both the signed MyteCody engine state and this npm
|
|
36
38
|
package version. `mytecody update` updates the engine only. Use
|
|
37
39
|
`npm install -g myte@latest` to update the launcher and the Myte API CLI
|
package/lib/mytecody-splash.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const FRAME_MS = 48;
|
|
4
|
-
const MIN_MS =
|
|
4
|
+
const MIN_MS = 3000;
|
|
5
5
|
const FRAMES = 32;
|
|
6
6
|
const WIDTH = 84;
|
|
7
7
|
const HEIGHT = 64;
|
|
@@ -225,12 +225,12 @@ function createMyteSplash() {
|
|
|
225
225
|
|
|
226
226
|
async function stop() {
|
|
227
227
|
if (!enabled) return;
|
|
228
|
+
const remaining = Math.max(0, MIN_MS - (Date.now() - started));
|
|
229
|
+
if (remaining) await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
228
230
|
if (timer) {
|
|
229
231
|
clearInterval(timer);
|
|
230
232
|
timer = null;
|
|
231
233
|
}
|
|
232
|
-
const remaining = Math.max(0, MIN_MS - (Date.now() - started));
|
|
233
|
-
if (remaining) await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
234
234
|
if (rendered) process.stdout.write(`\x1b[${lineCount}F\x1b[J`);
|
|
235
235
|
process.stdout.write("\x1b[?25h");
|
|
236
236
|
rendered = false;
|
package/mytecody-cli.js
CHANGED
|
@@ -7,6 +7,7 @@ const path = require("path");
|
|
|
7
7
|
const crypto = require("crypto");
|
|
8
8
|
const zlib = require("zlib");
|
|
9
9
|
const { spawn } = require("child_process");
|
|
10
|
+
const readline = require("readline");
|
|
10
11
|
const {
|
|
11
12
|
DEFAULT_MYTEAI_BASE,
|
|
12
13
|
normalizeMyteAiBase,
|
|
@@ -229,6 +230,55 @@ function loadSignedController() {
|
|
|
229
230
|
return controller;
|
|
230
231
|
}
|
|
231
232
|
|
|
233
|
+
function controllerRunsRoot() {
|
|
234
|
+
return path.join(installRoot(), "controller-runs");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function readJsonFileIfPresent(filePath) {
|
|
238
|
+
try {
|
|
239
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function listControllerRunRecords() {
|
|
246
|
+
const root = controllerRunsRoot();
|
|
247
|
+
if (!fs.existsSync(root)) return [];
|
|
248
|
+
const records = [];
|
|
249
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
250
|
+
if (!entry.isDirectory()) continue;
|
|
251
|
+
const runPath = path.join(root, entry.name, "run.json");
|
|
252
|
+
const run = readJsonFileIfPresent(runPath);
|
|
253
|
+
if (!run || !run.run_id) continue;
|
|
254
|
+
let mtimeMs = 0;
|
|
255
|
+
try {
|
|
256
|
+
mtimeMs = fs.statSync(runPath).mtimeMs;
|
|
257
|
+
} catch {}
|
|
258
|
+
records.push({
|
|
259
|
+
run_id: run.run_id,
|
|
260
|
+
workspace_root: run.workspace_root || process.cwd(),
|
|
261
|
+
status: run.status || "unknown",
|
|
262
|
+
path: runPath,
|
|
263
|
+
mtimeMs,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return records.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function resolveControllerRunTarget(target = "latest") {
|
|
270
|
+
const records = listControllerRunRecords();
|
|
271
|
+
if (!records.length) return null;
|
|
272
|
+
const wanted = String(target || "latest");
|
|
273
|
+
if (wanted === "latest") return records[0];
|
|
274
|
+
const exact = records.find((record) => record.run_id === wanted);
|
|
275
|
+
if (exact) return exact;
|
|
276
|
+
const prefixed = records.filter((record) => record.run_id.startsWith(wanted));
|
|
277
|
+
if (prefixed.length === 1) return prefixed[0];
|
|
278
|
+
if (prefixed.length > 1) throw new Error(`CodyRun prefix is ambiguous: ${wanted}`);
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
232
282
|
function releaseAssetsForPlatform(manifest, artifact) {
|
|
233
283
|
const assets = [];
|
|
234
284
|
const collect = (value, source) => {
|
|
@@ -337,12 +387,23 @@ function printHelp() {
|
|
|
337
387
|
|
|
338
388
|
Usage:
|
|
339
389
|
mytecody
|
|
390
|
+
mytecody [prompt...]
|
|
340
391
|
mytecody doctor [--json] [--base-url <url>] [--probe-gateway]
|
|
341
392
|
mytecody exec [prompt or agent exec args...]
|
|
393
|
+
mytecody resume [codex-session-id|latest]
|
|
394
|
+
mytecody controller-resume [controller-run-id|latest]
|
|
395
|
+
mytecody codex [raw engine args...]
|
|
342
396
|
mytecody update --dry-run [--json] [--manifest <url-or-file>] [--fetch-manifest]
|
|
343
397
|
mytecody update [--json] [--manifest <url-or-file>]
|
|
344
398
|
mytecody help
|
|
345
399
|
|
|
400
|
+
Defaults:
|
|
401
|
+
mytecody opens the branded Codex harness with Myte context hardening.
|
|
402
|
+
Non-command prompt args are forwarded to the branded engine, not the experimental controller.
|
|
403
|
+
Use --controller on exec or controller-shell for temporary controller diagnostics.
|
|
404
|
+
Use mytecody controller-shell only for temporary controller-shell diagnostics.
|
|
405
|
+
Use mytecody codex only for raw engine diagnostics.
|
|
406
|
+
|
|
346
407
|
Updates:
|
|
347
408
|
mytecody update updates the signed MyteCody engine only
|
|
348
409
|
npm install -g myte@latest updates this npm launcher and Myte API tools
|
|
@@ -747,6 +808,63 @@ function installArtifactBytes(bytes, manifest, artifact) {
|
|
|
747
808
|
return installedManifest;
|
|
748
809
|
}
|
|
749
810
|
|
|
811
|
+
function reusableInstalledArtifact(artifact) {
|
|
812
|
+
const enginePath = currentEnginePath();
|
|
813
|
+
const current = readCurrentClientManifest();
|
|
814
|
+
if (!current || !fs.existsSync(enginePath)) return null;
|
|
815
|
+
|
|
816
|
+
const currentArtifact = current.artifact || {};
|
|
817
|
+
if (artifact && artifact.sha256) {
|
|
818
|
+
if (String(currentArtifact.sha256 || "").toLowerCase() !== String(artifact.sha256 || "").toLowerCase()) {
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const engineBytes = fs.readFileSync(enginePath);
|
|
824
|
+
const installedSha = sha256Hex(engineBytes);
|
|
825
|
+
const expectedInstalledSha =
|
|
826
|
+
artifact && (artifact.installed_sha256 || artifact.executable_sha256 || artifact.uncompressed_sha256);
|
|
827
|
+
if (expectedInstalledSha && installedSha.toLowerCase() !== String(expectedInstalledSha).toLowerCase()) {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
if (
|
|
831
|
+
currentArtifact.installed_sha256 &&
|
|
832
|
+
installedSha.toLowerCase() !== String(currentArtifact.installed_sha256).toLowerCase()
|
|
833
|
+
) {
|
|
834
|
+
return null;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return {
|
|
838
|
+
enginePath,
|
|
839
|
+
engineBytes,
|
|
840
|
+
artifactSizeBytes: Number(currentArtifact.size_bytes || artifact?.size_bytes || 0),
|
|
841
|
+
installedSha,
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function installManifestForReusableArtifact(reusable, manifest, artifact) {
|
|
846
|
+
const installedManifest = {
|
|
847
|
+
schema_version: manifest.schema_version || 1,
|
|
848
|
+
channel: manifest.channel || DEFAULT_CHANNEL,
|
|
849
|
+
version: manifest.version || "unknown",
|
|
850
|
+
installed_at: new Date().toISOString(),
|
|
851
|
+
launcher_version: PACKAGE_VERSION,
|
|
852
|
+
platform: platformKey(),
|
|
853
|
+
executable: reusable.enginePath,
|
|
854
|
+
artifact: {
|
|
855
|
+
url: artifact.url,
|
|
856
|
+
sha256: artifact.sha256,
|
|
857
|
+
format: artifactFormat(artifact),
|
|
858
|
+
size_bytes: reusable.artifactSizeBytes,
|
|
859
|
+
installed_sha256: reusable.installedSha,
|
|
860
|
+
installed_size_bytes: reusable.engineBytes.length,
|
|
861
|
+
},
|
|
862
|
+
};
|
|
863
|
+
fs.mkdirSync(path.dirname(currentClientManifestPath()), { recursive: true });
|
|
864
|
+
fs.writeFileSync(currentClientManifestPath(), JSON.stringify(installedManifest, null, 2), "utf8");
|
|
865
|
+
return installedManifest;
|
|
866
|
+
}
|
|
867
|
+
|
|
750
868
|
async function installReleaseAssets(manifest, artifact, { progress } = {}) {
|
|
751
869
|
const assets = releaseAssetsForPlatform(manifest, artifact);
|
|
752
870
|
const installed = [];
|
|
@@ -1082,12 +1200,13 @@ function execArgsAndStdin(rawArgs) {
|
|
|
1082
1200
|
|
|
1083
1201
|
function stripControllerArgs(rawArgs) {
|
|
1084
1202
|
const kept = [];
|
|
1085
|
-
let enabled = process.env.MYTE_CODY_CONTROLLER
|
|
1203
|
+
let enabled = process.env.MYTE_CODY_CONTROLLER === "1";
|
|
1086
1204
|
for (let i = 0; i < rawArgs.length; i += 1) {
|
|
1087
1205
|
const arg = rawArgs[i];
|
|
1088
1206
|
if (arg === "--controller") {
|
|
1089
1207
|
const next = rawArgs[i + 1];
|
|
1090
|
-
|
|
1208
|
+
const normalizedNext = String(next || "").toLowerCase();
|
|
1209
|
+
if (next !== undefined && !next.startsWith("-") && ["0", "1", "false", "true", "off", "on", "raw"].includes(normalizedNext)) {
|
|
1091
1210
|
enabled = !["0", "false", "off", "raw"].includes(String(next).toLowerCase());
|
|
1092
1211
|
i += 1;
|
|
1093
1212
|
} else {
|
|
@@ -1119,6 +1238,24 @@ function controllerPromptFromExecArgs(rawArgs) {
|
|
|
1119
1238
|
return { prompt, forwardedArgs: execInput.args.slice(0, -1) };
|
|
1120
1239
|
}
|
|
1121
1240
|
|
|
1241
|
+
function classifyMyteCodyInvocation(rawArgs = []) {
|
|
1242
|
+
const args = Array.isArray(rawArgs) ? rawArgs : [];
|
|
1243
|
+
if (!args.length) return { mode: "raw-codex", args: [], controller: false };
|
|
1244
|
+
|
|
1245
|
+
const command = args[0];
|
|
1246
|
+
if (command === "codex") return { mode: "raw-codex", args: args.slice(1), controller: false };
|
|
1247
|
+
if (command === "controller-shell") return { mode: "controller-shell", args: args.slice(1) };
|
|
1248
|
+
if (command === "controller-resume") return { mode: "controller-resume", args: args.slice(1) };
|
|
1249
|
+
if (command === "resume") return { mode: "raw-codex", args, controller: false };
|
|
1250
|
+
if (command === "exec") {
|
|
1251
|
+
const controller = stripControllerArgs(args.slice(1));
|
|
1252
|
+
if (controller.enabled) return { mode: "controller-exec", args: controller.args };
|
|
1253
|
+
return { mode: "raw-codex", args: ["exec", ...controller.args] };
|
|
1254
|
+
}
|
|
1255
|
+
if (String(command || "").startsWith("-")) return { mode: "raw-codex", args };
|
|
1256
|
+
return { mode: "raw-codex", args, controller: false };
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1122
1259
|
function runEngineExecWorker({ command, args, providerBaseUrl, token, prompt, timeoutMs }) {
|
|
1123
1260
|
return new Promise((resolve) => {
|
|
1124
1261
|
const started = Date.now();
|
|
@@ -1179,6 +1316,242 @@ function runEngineExecWorker({ command, args, providerBaseUrl, token, prompt, ti
|
|
|
1179
1316
|
});
|
|
1180
1317
|
}
|
|
1181
1318
|
|
|
1319
|
+
async function runSignedControllerPrompt({ signedController, command, args, bridge, token, promptInfo }) {
|
|
1320
|
+
if (!promptInfo.prompt.trim()) {
|
|
1321
|
+
console.error("MyteCody controller requires a prompt.");
|
|
1322
|
+
return 1;
|
|
1323
|
+
}
|
|
1324
|
+
const summary = await signedController.runMyteCodyController({
|
|
1325
|
+
prompt: promptInfo.prompt,
|
|
1326
|
+
workspace: process.cwd(),
|
|
1327
|
+
artifactRoot: path.join(installRoot(), "controller-runs"),
|
|
1328
|
+
runWorker: (workerPrompt, workerOptions = {}) =>
|
|
1329
|
+
runEngineExecWorker({
|
|
1330
|
+
command,
|
|
1331
|
+
args,
|
|
1332
|
+
providerBaseUrl: bridge.baseUrl,
|
|
1333
|
+
token,
|
|
1334
|
+
prompt: workerPrompt,
|
|
1335
|
+
timeoutMs: workerOptions.timeoutMs || 180000,
|
|
1336
|
+
}),
|
|
1337
|
+
});
|
|
1338
|
+
console.error(`[MYTE CODY] controller run: ${summary.artifact_dir}`);
|
|
1339
|
+
console.error(`[MYTE CODY] controller status: ${summary.status}`);
|
|
1340
|
+
if (summary.status === "paused") {
|
|
1341
|
+
console.error(`[MYTE CODY] resume: mytecody resume ${summary.run_id}`);
|
|
1342
|
+
}
|
|
1343
|
+
return ["pass", "paused", "completed"].includes(summary.status) ? 0 : 1;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
async function runSignedControllerResume({ signedController, command, args, bridge, token, target }) {
|
|
1347
|
+
if (!signedController || typeof signedController.resumeMyteCodyControllerRun !== "function") {
|
|
1348
|
+
console.error("Installed MyteCody controller does not support resume; run `mytecody update`.");
|
|
1349
|
+
return 1;
|
|
1350
|
+
}
|
|
1351
|
+
let record;
|
|
1352
|
+
try {
|
|
1353
|
+
record = resolveControllerRunTarget(target || "latest");
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
console.error(`[MYTE CODY] ${error && error.message ? error.message : error}`);
|
|
1356
|
+
return 1;
|
|
1357
|
+
}
|
|
1358
|
+
if (!record) {
|
|
1359
|
+
console.error(`[MYTE CODY] CodyRun not found: ${target || "latest"}`);
|
|
1360
|
+
return 1;
|
|
1361
|
+
}
|
|
1362
|
+
const summary = await signedController.resumeMyteCodyControllerRun({
|
|
1363
|
+
runId: record.run_id,
|
|
1364
|
+
workspace: record.workspace_root || process.cwd(),
|
|
1365
|
+
artifactRoot: controllerRunsRoot(),
|
|
1366
|
+
runWorker: (workerPrompt, workerOptions = {}) =>
|
|
1367
|
+
runEngineExecWorker({
|
|
1368
|
+
command,
|
|
1369
|
+
args,
|
|
1370
|
+
providerBaseUrl: bridge.baseUrl,
|
|
1371
|
+
token,
|
|
1372
|
+
prompt: workerPrompt,
|
|
1373
|
+
timeoutMs: workerOptions.timeoutMs || 180000,
|
|
1374
|
+
}),
|
|
1375
|
+
});
|
|
1376
|
+
console.error(`[MYTE CODY] resumed CodyRun: ${summary.run_id}`);
|
|
1377
|
+
console.error(`[MYTE CODY] controller status: ${summary.status}`);
|
|
1378
|
+
if (summary.status === "paused") {
|
|
1379
|
+
console.error(`[MYTE CODY] resume: mytecody resume ${summary.run_id}`);
|
|
1380
|
+
}
|
|
1381
|
+
return ["pass", "paused", "completed"].includes(summary.status) ? 0 : 1;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
function isControllerShellExit(value) {
|
|
1385
|
+
return ["/exit", "/quit", "exit", "quit"].includes(String(value || "").trim().toLowerCase());
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
function compactWorkspacePath(workspace = process.cwd()) {
|
|
1389
|
+
const home = os.homedir();
|
|
1390
|
+
const resolved = path.resolve(workspace);
|
|
1391
|
+
if (resolved.toLowerCase().startsWith(home.toLowerCase())) {
|
|
1392
|
+
return `~${resolved.slice(home.length)}`;
|
|
1393
|
+
}
|
|
1394
|
+
return resolved;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function fitCell(value, width) {
|
|
1398
|
+
const text = String(value || "");
|
|
1399
|
+
if (text.length <= width) return `${text}${" ".repeat(width - text.length)}`;
|
|
1400
|
+
return `${text.slice(0, Math.max(0, width - 1))}…`;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
function printControllerShellBanner() {
|
|
1404
|
+
const width = 70;
|
|
1405
|
+
const lines = [
|
|
1406
|
+
"MYTE CODY - Your Tech Your Way",
|
|
1407
|
+
`workspace: ${compactWorkspacePath()}`,
|
|
1408
|
+
"mode: sovereign coding agent - Myte AI gateway",
|
|
1409
|
+
"enter a prompt, /resume latest, /diagnostics, /help, or /exit",
|
|
1410
|
+
];
|
|
1411
|
+
console.log(`╭${"─".repeat(width)}╮`);
|
|
1412
|
+
for (const line of lines) console.log(`│ ${fitCell(line, width - 2)} │`);
|
|
1413
|
+
console.log(`╰${"─".repeat(width)}╯`);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
function printControllerShellHelp() {
|
|
1417
|
+
console.log("");
|
|
1418
|
+
console.log("MYTE CODY commands");
|
|
1419
|
+
console.log("/resume [run_id|latest] Continue a paused CodyRun.");
|
|
1420
|
+
console.log("/diagnostics Open the raw engine diagnostics view.");
|
|
1421
|
+
console.log("/exit Quit.");
|
|
1422
|
+
console.log("");
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
async function runControllerShell({ signedController, command, args, bridge, token }) {
|
|
1426
|
+
printControllerShellBanner();
|
|
1427
|
+
const rl = readline.createInterface({
|
|
1428
|
+
input: process.stdin,
|
|
1429
|
+
output: process.stdout,
|
|
1430
|
+
prompt: "\n› ",
|
|
1431
|
+
});
|
|
1432
|
+
rl.prompt();
|
|
1433
|
+
for await (const line of rl) {
|
|
1434
|
+
const prompt = String(line || "").trim();
|
|
1435
|
+
if (!prompt) {
|
|
1436
|
+
rl.prompt();
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
if (isControllerShellExit(prompt)) break;
|
|
1440
|
+
if (prompt === "/help") {
|
|
1441
|
+
printControllerShellHelp();
|
|
1442
|
+
rl.prompt();
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (prompt === "/diagnostics" || prompt === "/codex") {
|
|
1446
|
+
rl.close();
|
|
1447
|
+
return { rawCodexRequested: true };
|
|
1448
|
+
}
|
|
1449
|
+
if (prompt.startsWith("/resume")) {
|
|
1450
|
+
const target = prompt.split(/\s+/).slice(1)[0] || "latest";
|
|
1451
|
+
await runSignedControllerResume({
|
|
1452
|
+
signedController,
|
|
1453
|
+
command,
|
|
1454
|
+
args,
|
|
1455
|
+
bridge,
|
|
1456
|
+
token,
|
|
1457
|
+
target,
|
|
1458
|
+
});
|
|
1459
|
+
rl.prompt();
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
await runSignedControllerPrompt({
|
|
1463
|
+
signedController,
|
|
1464
|
+
command,
|
|
1465
|
+
args,
|
|
1466
|
+
bridge,
|
|
1467
|
+
token,
|
|
1468
|
+
promptInfo: { prompt, forwardedArgs: [] },
|
|
1469
|
+
});
|
|
1470
|
+
rl.prompt();
|
|
1471
|
+
}
|
|
1472
|
+
return { rawCodexRequested: false };
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
function emitSidecarEvent(event) {
|
|
1476
|
+
process.stdout.write(`${JSON.stringify(event)}\n`);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
async function runControllerSidecar() {
|
|
1480
|
+
let request;
|
|
1481
|
+
try {
|
|
1482
|
+
const raw = fs.readFileSync(0, "utf8");
|
|
1483
|
+
request = raw.trim() ? JSON.parse(raw) : {};
|
|
1484
|
+
} catch (error) {
|
|
1485
|
+
emitSidecarEvent({
|
|
1486
|
+
type: "failed",
|
|
1487
|
+
message: `Invalid MyteCody sidecar request: ${error && error.message ? error.message : error}`,
|
|
1488
|
+
});
|
|
1489
|
+
return 1;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
const prompt = String(request.prompt || "").trim();
|
|
1493
|
+
const workspace = path.resolve(request.workspace || process.cwd());
|
|
1494
|
+
if (!prompt) {
|
|
1495
|
+
emitSidecarEvent({ type: "failed", message: "MyteCody sidecar request did not include a prompt." });
|
|
1496
|
+
return 1;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
const token = process.env.MYTE_CODY_AUTH_TOKEN || getAuthToken();
|
|
1500
|
+
const providerBaseUrl = process.env.MYTE_CODY_BRIDGE_BASE_URL;
|
|
1501
|
+
const command = resolveCodexCommand();
|
|
1502
|
+
if (!token || !providerBaseUrl || !command) {
|
|
1503
|
+
emitSidecarEvent({
|
|
1504
|
+
type: "failed",
|
|
1505
|
+
message: "MyteCody sidecar is missing auth, bridge URL, or installed engine command.",
|
|
1506
|
+
});
|
|
1507
|
+
return 1;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
emitSidecarEvent({
|
|
1511
|
+
type: "started",
|
|
1512
|
+
message: "MyteCody controller started.",
|
|
1513
|
+
workspace,
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
try {
|
|
1517
|
+
const signedController = loadSignedController();
|
|
1518
|
+
const summary = await signedController.runMyteCodyController({
|
|
1519
|
+
prompt,
|
|
1520
|
+
workspace,
|
|
1521
|
+
artifactRoot: process.env.MYTE_CODY_CONTROLLER_RUNS_DIR || controllerRunsRoot(),
|
|
1522
|
+
mode: process.env.MYTE_CODY_CONTROLLER_MODE || "gateway",
|
|
1523
|
+
runWorker: (workerPrompt, workerOptions = {}) =>
|
|
1524
|
+
runEngineExecWorker({
|
|
1525
|
+
command,
|
|
1526
|
+
args: {},
|
|
1527
|
+
providerBaseUrl,
|
|
1528
|
+
token,
|
|
1529
|
+
prompt: workerPrompt,
|
|
1530
|
+
timeoutMs: workerOptions.timeoutMs || 180000,
|
|
1531
|
+
}),
|
|
1532
|
+
});
|
|
1533
|
+
const resumeCommand = `mytecody resume ${summary.run_id}`;
|
|
1534
|
+
emitSidecarEvent({
|
|
1535
|
+
type: summary.status === "paused" ? "paused" : "completed",
|
|
1536
|
+
run_id: summary.run_id,
|
|
1537
|
+
status: summary.status,
|
|
1538
|
+
artifact_dir: summary.artifact_dir,
|
|
1539
|
+
resume: summary.status === "paused" ? resumeCommand : null,
|
|
1540
|
+
message:
|
|
1541
|
+
summary.status === "paused"
|
|
1542
|
+
? `MyteCody paused with a durable run state. Resume with: ${resumeCommand}`
|
|
1543
|
+
: `MyteCody controller completed with status: ${summary.status}`,
|
|
1544
|
+
});
|
|
1545
|
+
return ["pass", "paused", "completed"].includes(summary.status) ? 0 : 1;
|
|
1546
|
+
} catch (error) {
|
|
1547
|
+
emitSidecarEvent({
|
|
1548
|
+
type: "failed",
|
|
1549
|
+
message: error && error.stack ? error.stack : error && error.message ? error.message : String(error),
|
|
1550
|
+
});
|
|
1551
|
+
return 1;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1182
1555
|
async function runCodex(rawArgs, args = {}, envPath = null) {
|
|
1183
1556
|
const token = getAuthToken();
|
|
1184
1557
|
if (!token) {
|
|
@@ -1237,39 +1610,82 @@ async function runCodex(rawArgs, args = {}, envPath = null) {
|
|
|
1237
1610
|
return 1;
|
|
1238
1611
|
}
|
|
1239
1612
|
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1242
|
-
if (rawArgs[0] === "exec" && controllerState.enabled) {
|
|
1613
|
+
const invocation = classifyMyteCodyInvocation(rawArgs);
|
|
1614
|
+
if (["controller-exec", "controller-prompt", "controller-shell", "controller-resume"].includes(invocation.mode)) {
|
|
1243
1615
|
try {
|
|
1244
1616
|
progress("opening MyteCody controller");
|
|
1245
1617
|
const signedController = loadSignedController();
|
|
1246
|
-
|
|
1618
|
+
await splash.stop();
|
|
1619
|
+
if (packageNotice) statusLine(packageNotice);
|
|
1620
|
+
|
|
1621
|
+
if (invocation.mode === "controller-resume") {
|
|
1622
|
+
const code = await runSignedControllerResume({
|
|
1623
|
+
signedController,
|
|
1624
|
+
command,
|
|
1625
|
+
args,
|
|
1626
|
+
bridge,
|
|
1627
|
+
token,
|
|
1628
|
+
target: invocation.args[0] || "latest",
|
|
1629
|
+
});
|
|
1630
|
+
await bridge.close();
|
|
1631
|
+
return code;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
if (invocation.mode === "controller-shell") {
|
|
1635
|
+
const shellResult = await runControllerShell({
|
|
1636
|
+
signedController,
|
|
1637
|
+
command,
|
|
1638
|
+
args,
|
|
1639
|
+
bridge,
|
|
1640
|
+
token,
|
|
1641
|
+
});
|
|
1642
|
+
if (shellResult.rawCodexRequested) {
|
|
1643
|
+
const rawArgs = [...command.args, ...codexLaunchArgs([], args, bridge.baseUrl)];
|
|
1644
|
+
const env = {
|
|
1645
|
+
...process.env,
|
|
1646
|
+
CODEX_HOME: codexHome(),
|
|
1647
|
+
MYTE_CODY_AUTH_TOKEN: token,
|
|
1648
|
+
MYTE_CODY_BRAND: "1",
|
|
1649
|
+
MYTE_CODY_CONTROLLER: "0",
|
|
1650
|
+
MYTE_CODY_BRIDGE_BASE_URL: bridge.baseUrl,
|
|
1651
|
+
};
|
|
1652
|
+
return await new Promise((resolve) => {
|
|
1653
|
+
const child = spawn(command.cmd, rawArgs, {
|
|
1654
|
+
cwd: process.cwd(),
|
|
1655
|
+
env,
|
|
1656
|
+
stdio: "inherit",
|
|
1657
|
+
shell: process.platform === "win32" && command.cmd === "codex",
|
|
1658
|
+
});
|
|
1659
|
+
child.on("error", (error) => {
|
|
1660
|
+
console.error(`Unable to launch MyteCody engine: ${error.message || error}`);
|
|
1661
|
+
bridge.close().finally(() => resolve(1));
|
|
1662
|
+
});
|
|
1663
|
+
child.on("close", (code) => {
|
|
1664
|
+
bridge.close().finally(() => resolve(Number.isInteger(code) ? code : 1));
|
|
1665
|
+
});
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
await bridge.close();
|
|
1669
|
+
return 0;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
const promptInfo = controllerPromptFromExecArgs(invocation.args);
|
|
1247
1673
|
if (!promptInfo.prompt.trim()) {
|
|
1248
1674
|
await splash.stop();
|
|
1249
1675
|
await bridge.close();
|
|
1250
|
-
console.error("MyteCody controller
|
|
1676
|
+
console.error("MyteCody controller requires a prompt.");
|
|
1251
1677
|
return 1;
|
|
1252
1678
|
}
|
|
1253
|
-
await
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
runEngineExecWorker({
|
|
1261
|
-
command,
|
|
1262
|
-
args,
|
|
1263
|
-
providerBaseUrl: bridge.baseUrl,
|
|
1264
|
-
token,
|
|
1265
|
-
prompt: workerPrompt,
|
|
1266
|
-
timeoutMs: workerOptions.timeoutMs || 180000,
|
|
1267
|
-
}),
|
|
1679
|
+
const code = await runSignedControllerPrompt({
|
|
1680
|
+
signedController,
|
|
1681
|
+
command,
|
|
1682
|
+
args,
|
|
1683
|
+
bridge,
|
|
1684
|
+
token,
|
|
1685
|
+
promptInfo,
|
|
1268
1686
|
});
|
|
1269
|
-
console.error(`[MYTE CODY] controller run: ${summary.artifact_dir}`);
|
|
1270
|
-
console.error(`[MYTE CODY] controller status: ${summary.status}`);
|
|
1271
1687
|
await bridge.close();
|
|
1272
|
-
return
|
|
1688
|
+
return code;
|
|
1273
1689
|
} catch (error) {
|
|
1274
1690
|
await splash.stop();
|
|
1275
1691
|
await bridge.close();
|
|
@@ -1278,12 +1694,17 @@ async function runCodex(rawArgs, args = {}, envPath = null) {
|
|
|
1278
1694
|
}
|
|
1279
1695
|
}
|
|
1280
1696
|
|
|
1281
|
-
const launchArgs = [...command.args, ...codexLaunchArgs(
|
|
1697
|
+
const launchArgs = [...command.args, ...codexLaunchArgs(invocation.args, args, bridge.baseUrl)];
|
|
1282
1698
|
const env = {
|
|
1283
1699
|
...process.env,
|
|
1284
1700
|
CODEX_HOME: codexHome(),
|
|
1285
1701
|
MYTE_CODY_AUTH_TOKEN: token,
|
|
1286
1702
|
MYTE_CODY_BRAND: "1",
|
|
1703
|
+
MYTE_CODY_CONTROLLER: invocation.controller === true ? "1" : "0",
|
|
1704
|
+
MYTE_CODY_CONTROLLER_MODE: "gateway",
|
|
1705
|
+
MYTE_CODY_CONTROLLER_NODE: process.execPath,
|
|
1706
|
+
MYTE_CODY_CONTROLLER_ENTRY: __filename,
|
|
1707
|
+
MYTE_CODY_CONTROLLER_RUNS_DIR: controllerRunsRoot(),
|
|
1287
1708
|
MYTE_CODY_BRIDGE_BASE_URL: bridge.baseUrl,
|
|
1288
1709
|
};
|
|
1289
1710
|
progress("opening MyteCody workspace");
|
|
@@ -1319,8 +1740,7 @@ async function runDoctor(args, envPath) {
|
|
|
1319
1740
|
payload.ready_for_coding =
|
|
1320
1741
|
payload.auth.present &&
|
|
1321
1742
|
payload.release.client_installed &&
|
|
1322
|
-
payload.release.bridge_installed
|
|
1323
|
-
payload.release.controller_installed;
|
|
1743
|
+
payload.release.bridge_installed;
|
|
1324
1744
|
if (payload.gateway.probe) {
|
|
1325
1745
|
payload.ready_for_coding = Boolean(payload.ready_for_coding && payload.gateway.probe.ok);
|
|
1326
1746
|
}
|
|
@@ -1400,12 +1820,23 @@ async function buildUpdatePayload(args, envPath, { dryRun = false, progress = nu
|
|
|
1400
1820
|
if (!artifactMetadata.ok) {
|
|
1401
1821
|
throw new Error(`MyteCody release artifact metadata is ${artifactMetadata.status}.`);
|
|
1402
1822
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1823
|
+
let installed;
|
|
1824
|
+
let artifactDigest = String(artifact.sha256 || "");
|
|
1825
|
+
let artifactSizeBytes = Number(artifact.size_bytes || 0);
|
|
1826
|
+
const reusable = reusableInstalledArtifact(artifact);
|
|
1827
|
+
if (reusable) {
|
|
1828
|
+
if (progress) progress("reusing installed MyteCody engine");
|
|
1829
|
+
installed = installManifestForReusableArtifact(reusable, manifest, artifact);
|
|
1830
|
+
artifactSizeBytes = reusable.artifactSizeBytes;
|
|
1831
|
+
} else {
|
|
1832
|
+
const bytes = await readArtifactBytes(artifact, { progress });
|
|
1833
|
+
artifactDigest = sha256Hex(bytes);
|
|
1834
|
+
artifactSizeBytes = bytes.length;
|
|
1835
|
+
if (artifactDigest.toLowerCase() !== String(artifact.sha256 || "").toLowerCase()) {
|
|
1836
|
+
throw new Error(`Artifact SHA-256 mismatch: expected ${artifact.sha256}, got ${artifactDigest}`);
|
|
1837
|
+
}
|
|
1838
|
+
installed = installArtifactBytes(bytes, manifest, artifact);
|
|
1407
1839
|
}
|
|
1408
|
-
const installed = installArtifactBytes(bytes, manifest, artifact);
|
|
1409
1840
|
const installedAssets = await installReleaseAssets(manifest, artifact, { progress });
|
|
1410
1841
|
if (installedAssets.length) {
|
|
1411
1842
|
installed.assets = installedAssets;
|
|
@@ -1415,8 +1846,8 @@ async function buildUpdatePayload(args, envPath, { dryRun = false, progress = nu
|
|
|
1415
1846
|
ok: true,
|
|
1416
1847
|
version: installed.version,
|
|
1417
1848
|
executable: installed.executable,
|
|
1418
|
-
sha256:
|
|
1419
|
-
size_bytes:
|
|
1849
|
+
sha256: artifactDigest,
|
|
1850
|
+
size_bytes: artifactSizeBytes,
|
|
1420
1851
|
installed_sha256: installed.artifact.installed_sha256,
|
|
1421
1852
|
installed_size_bytes: installed.artifact.installed_size_bytes,
|
|
1422
1853
|
format: installed.artifact.format,
|
|
@@ -1545,6 +1976,7 @@ async function run(argv = process.argv.slice(2)) {
|
|
|
1545
1976
|
}
|
|
1546
1977
|
if (command === "doctor") return runDoctor(restArgs, envPath);
|
|
1547
1978
|
if (command === "update") return runUpdate(restArgs, envPath);
|
|
1979
|
+
if (command === "controller-sidecar") return runControllerSidecar();
|
|
1548
1980
|
if (command === "version" || command === "--version" || command === "-v") {
|
|
1549
1981
|
console.log(PACKAGE_VERSION);
|
|
1550
1982
|
return 0;
|
|
@@ -1569,6 +2001,7 @@ if (require.main === module) {
|
|
|
1569
2001
|
|
|
1570
2002
|
module.exports = {
|
|
1571
2003
|
checkPackageUpdate,
|
|
2004
|
+
classifyMyteCodyInvocation,
|
|
1572
2005
|
codexLaunchArgs,
|
|
1573
2006
|
codexProviderArgs,
|
|
1574
2007
|
codyInferenceBase,
|