@mytegroupinc/myte-core 0.0.44 → 0.0.45

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/mytecody-cli.js CHANGED
@@ -1,2025 +1,2025 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
-
4
- const fs = require("fs");
5
- const os = require("os");
6
- const path = require("path");
7
- const crypto = require("crypto");
8
- const zlib = require("zlib");
9
- const { spawn } = require("child_process");
10
- const readline = require("readline");
11
- const {
12
- DEFAULT_MYTEAI_BASE,
13
- normalizeMyteAiBase,
14
- } = require("./lib/ai-gateway");
15
- const { createMyteSplash } = require("./lib/mytecody-splash");
16
-
17
- const PACKAGE_NAME = "myte";
18
- const PACKAGE_VERSION = require("./package.json").version;
19
- const DEFAULT_PACKAGE_LATEST_URL = "https://registry.npmjs.org/myte/latest";
20
- const DEFAULT_CHANNEL = "alpha";
21
- const DEFAULT_MODEL_ALIAS = "myte";
22
- const DEFAULT_CONTEXT_WINDOW = Number(process.env.MYTE_CODY_CONTEXT_WINDOW || 49152);
23
- const DEFAULT_AUTO_COMPACT_TOKENS = Number(process.env.MYTE_CODY_AUTO_COMPACT_TOKENS || 40960);
24
- const DEFAULT_TOOL_OUTPUT_TOKENS = Number(process.env.MYTE_CODY_TOOL_OUTPUT_TOKENS || 10000);
25
- const DEFAULT_AGENT_THREADS = Number(process.env.MYTE_CODY_AGENT_THREADS || 4);
26
- const CLIENT_BASE_INSTRUCTIONS =
27
- "You are MyteCody, a coding agent running through the Myte coding gateway.";
28
-
29
- function findEnvPath(startDir) {
30
- let cur = startDir;
31
- for (let i = 0; i < 8; i += 1) {
32
- const candidate = path.join(cur, ".env");
33
- if (fs.existsSync(candidate)) return candidate;
34
- const parent = path.dirname(cur);
35
- if (parent === cur) break;
36
- cur = parent;
37
- }
38
- return null;
39
- }
40
-
41
- function loadEnv() {
42
- const envPath = findEnvPath(process.cwd());
43
- if (!envPath || !fs.existsSync(envPath)) return null;
44
- const content = fs.readFileSync(envPath, "utf8");
45
- content.split(/\r?\n/).forEach((line) => {
46
- const trimmed = String(line || "").trim();
47
- if (!trimmed || trimmed.startsWith("#")) return;
48
- const idx = trimmed.indexOf("=");
49
- if (idx === -1) return;
50
- const key = trimmed.slice(0, idx).trim();
51
- let value = trimmed.slice(idx + 1).trim();
52
- if (
53
- (value.startsWith('"') && value.endsWith('"')) ||
54
- (value.startsWith("'") && value.endsWith("'"))
55
- ) {
56
- value = value.slice(1, -1);
57
- }
58
- if (key && !(key in process.env)) process.env[key] = value;
59
- });
60
- return envPath;
61
- }
62
-
63
- function parseArgs(argv) {
64
- const parsed = { _: [] };
65
- for (let i = 0; i < argv.length; i += 1) {
66
- const token = argv[i];
67
- if (token === "--") {
68
- parsed._.push(...argv.slice(i + 1));
69
- break;
70
- }
71
- if (token.startsWith("--no-")) {
72
- parsed[token.slice(5)] = false;
73
- continue;
74
- }
75
- if (token.startsWith("--")) {
76
- const eqIdx = token.indexOf("=");
77
- if (eqIdx !== -1) {
78
- parsed[token.slice(2, eqIdx)] = token.slice(eqIdx + 1);
79
- continue;
80
- }
81
- const key = token.slice(2);
82
- const next = argv[i + 1];
83
- if (next !== undefined && !next.startsWith("-")) {
84
- parsed[key] = next;
85
- i += 1;
86
- } else {
87
- parsed[key] = true;
88
- }
89
- continue;
90
- }
91
- parsed._.push(token);
92
- }
93
- return parsed;
94
- }
95
-
96
- function getKeyInfo(env = process.env) {
97
- if (String(env.MYTEAI_API_KEY || "").trim()) {
98
- return { present: true, source: "MYTEAI_API_KEY" };
99
- }
100
- if (String(env.MYTE_AI_API_KEY || "").trim()) {
101
- return { present: true, source: "MYTE_AI_API_KEY" };
102
- }
103
- return { present: false, source: null };
104
- }
105
-
106
- function getAuthToken(env = process.env) {
107
- return String(env.MYTEAI_API_KEY || env.MYTE_AI_API_KEY || "").trim();
108
- }
109
-
110
- function gatewayBase(args = {}) {
111
- const raw =
112
- args["base-url"] ||
113
- process.env.MYTE_CODY_API_BASE ||
114
- process.env.MYTEAI_API_BASE ||
115
- process.env.MYTE_AI_API_BASE ||
116
- DEFAULT_MYTEAI_BASE;
117
- return normalizeMyteAiBase(raw);
118
- }
119
-
120
- function gatewayRoot(args = {}) {
121
- return gatewayBase(args).replace(/\/v1$/i, "");
122
- }
123
-
124
- function codyGatewayUrl(args = {}, suffix = "") {
125
- const root = gatewayRoot(args).replace(/\/+$/, "");
126
- const tail = String(suffix || "").startsWith("/") ? String(suffix) : `/${suffix}`;
127
- return `${root}${tail}`;
128
- }
129
-
130
- function codyInferenceBase(args = {}) {
131
- return codyGatewayUrl(args, "/cody/v1");
132
- }
133
-
134
- function manifestUrl(args = {}) {
135
- return String(
136
- args.manifest ||
137
- process.env.MYTE_CODY_RELEASE_MANIFEST ||
138
- `${gatewayRoot(args)}/cody/releases/manifest.json`,
139
- );
140
- }
141
-
142
- function platformKey() {
143
- return `${process.platform}-${process.arch}`;
144
- }
145
-
146
- function installRoot() {
147
- if (process.env.MYTE_CODY_HOME) return path.resolve(process.env.MYTE_CODY_HOME);
148
- if (process.platform === "win32") {
149
- const base = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
150
- return path.join(base, "Myte", "Cody");
151
- }
152
- return path.join(os.homedir(), ".myte", "cody");
153
- }
154
-
155
- function currentInstallRoot() {
156
- return path.join(installRoot(), "current");
157
- }
158
-
159
- function currentClientManifestPath() {
160
- return path.join(currentInstallRoot(), "manifest.json");
161
- }
162
-
163
- function currentEnginePath() {
164
- const executable = process.platform === "win32" ? "mytecody-engine.exe" : "mytecody-engine";
165
- return path.join(currentInstallRoot(), "bin", executable);
166
- }
167
-
168
- function currentBridgePath() {
169
- return path.join(currentInstallRoot(), "lib", "mytecody-async-responses-bridge.js");
170
- }
171
-
172
- function currentControllerPath() {
173
- return path.join(currentInstallRoot(), "lib", "mytecody-controller.js");
174
- }
175
-
176
- function codexHome() {
177
- return path.join(installRoot(), "engine-home");
178
- }
179
-
180
- function codexModelCatalogPath() {
181
- return path.join(codexHome(), "mytecody-models.json");
182
- }
183
-
184
- function readCurrentClientManifest() {
185
- const filePath = currentClientManifestPath();
186
- if (!fs.existsSync(filePath)) return null;
187
- try {
188
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
189
- } catch {
190
- return null;
191
- }
192
- }
193
-
194
- function installedClientCommand() {
195
- const enginePath = currentEnginePath();
196
- if (!fs.existsSync(enginePath)) return null;
197
- return { cmd: enginePath, args: [], source: "myte-installed-engine" };
198
- }
199
-
200
- function installedClientUsable() {
201
- return Boolean(
202
- installedClientCommand() &&
203
- fs.existsSync(currentBridgePath()) &&
204
- fs.existsSync(currentControllerPath()) &&
205
- readCurrentClientManifest(),
206
- );
207
- }
208
-
209
- function loadSignedBridge() {
210
- const bridgePath = currentBridgePath();
211
- if (!fs.existsSync(bridgePath)) {
212
- throw new Error("signed MyteCody inference bridge asset is missing; run `mytecody update`.");
213
- }
214
- const bridge = require(bridgePath);
215
- if (!bridge || typeof bridge.startMyteCodyAsyncResponsesBridge !== "function") {
216
- throw new Error("signed MyteCody inference bridge asset is invalid.");
217
- }
218
- return bridge;
219
- }
220
-
221
- function loadSignedController() {
222
- const controllerPath = currentControllerPath();
223
- if (!fs.existsSync(controllerPath)) {
224
- throw new Error("signed MyteCody controller asset is missing; run `mytecody update`.");
225
- }
226
- const controller = require(controllerPath);
227
- if (!controller || typeof controller.runMyteCodyController !== "function") {
228
- throw new Error("signed MyteCody controller asset is invalid.");
229
- }
230
- return controller;
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
-
282
- function releaseAssetsForPlatform(manifest, artifact) {
283
- const assets = [];
284
- const collect = (value, source) => {
285
- if (!value) return;
286
- if (Array.isArray(value)) {
287
- value.forEach((item, index) => {
288
- if (item && typeof item === "object") {
289
- assets.push({ ...item, source, index });
290
- }
291
- });
292
- return;
293
- }
294
- if (typeof value === "object") {
295
- Object.entries(value).forEach(([name, item], index) => {
296
- if (item && typeof item === "object") {
297
- assets.push({ name: item.name || name, ...item, source, index });
298
- }
299
- });
300
- }
301
- };
302
-
303
- collect(manifest && manifest.client_assets, "manifest.client_assets");
304
- collect(artifact && artifact.assets, "artifact.assets");
305
-
306
- return assets
307
- .map((asset) => ({
308
- ...asset,
309
- install_path: asset.install_path || asset.path || "",
310
- }))
311
- .filter((asset) => asset && asset.url && asset.sha256 && asset.install_path);
312
- }
313
-
314
- function assertSafeReleaseAssetInstallPath(installPath) {
315
- const raw = String(installPath || "").replace(/\\/g, "/").trim();
316
- if (!raw) throw new Error("release asset install_path is required");
317
- if (path.isAbsolute(raw) || /^[A-Za-z]:/.test(raw)) {
318
- throw new Error(`release asset install_path must be relative: ${installPath}`);
319
- }
320
- const parts = raw.split("/").filter(Boolean);
321
- if (!parts.length || parts.includes("..")) {
322
- throw new Error(`release asset install_path is unsafe: ${installPath}`);
323
- }
324
- return path.join(currentInstallRoot(), ...parts);
325
- }
326
-
327
- function currentReleaseAssetRecordByPath(current) {
328
- const map = new Map();
329
- for (const asset of Array.isArray(current && current.assets) ? current.assets : []) {
330
- if (asset && asset.install_path) {
331
- map.set(String(asset.install_path).replace(/\\/g, "/"), asset);
332
- }
333
- }
334
- return map;
335
- }
336
-
337
- function installedReleaseAssetsMatchManifest(manifest, artifact) {
338
- const assets = releaseAssetsForPlatform(manifest, artifact);
339
- if (!assets.length) return true;
340
- const current = readCurrentClientManifest();
341
- const currentAssets = currentReleaseAssetRecordByPath(current);
342
-
343
- for (const asset of assets) {
344
- const installPath = String(asset.install_path || "").replace(/\\/g, "/");
345
- const currentAsset = currentAssets.get(installPath);
346
- if (!currentAsset) return false;
347
- if (String(currentAsset.sha256 || "").toLowerCase() !== String(asset.sha256 || "").toLowerCase()) {
348
- return false;
349
- }
350
- const targetPath = assertSafeReleaseAssetInstallPath(asset.install_path);
351
- if (!fs.existsSync(targetPath)) return false;
352
- const installedSha = sha256Hex(fs.readFileSync(targetPath));
353
- if (installedSha.toLowerCase() !== String(asset.installed_sha256 || asset.sha256).toLowerCase()) {
354
- return false;
355
- }
356
- }
357
- return true;
358
- }
359
-
360
- function installedClientMatchesManifest(manifest, artifact) {
361
- const current = readCurrentClientManifest();
362
- if (!current || !installedClientCommand()) return false;
363
- if (manifest && manifest.version && current.version !== manifest.version) return false;
364
-
365
- const currentArtifact = current.artifact || {};
366
- if (artifact && artifact.sha256) {
367
- if (String(currentArtifact.sha256 || "").toLowerCase() !== String(artifact.sha256 || "").toLowerCase()) {
368
- return false;
369
- }
370
- }
371
-
372
- const expectedInstalledSha =
373
- artifact && (artifact.installed_sha256 || artifact.executable_sha256 || artifact.uncompressed_sha256);
374
- if (expectedInstalledSha) {
375
- if (String(currentArtifact.installed_sha256 || "").toLowerCase() !== String(expectedInstalledSha).toLowerCase()) {
376
- return false;
377
- }
378
- }
379
-
380
- if (!installedReleaseAssetsMatchManifest(manifest, artifact)) return false;
381
-
382
- return true;
383
- }
384
-
385
- function printHelp() {
386
- console.log(`MYTE CODY - Your Tech Your Way
387
-
388
- Usage:
389
- mytecody
390
- mytecody [prompt...]
391
- mytecody doctor [--json] [--base-url <url>] [--probe-gateway]
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...]
396
- mytecody update --dry-run [--json] [--manifest <url-or-file>] [--fetch-manifest]
397
- mytecody update [--json] [--manifest <url-or-file>]
398
- mytecody help
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
-
407
- Updates:
408
- mytecody update updates the signed MyteCody engine only
409
- npm install -g myte@latest updates this npm launcher and Myte API tools
410
-
411
- Network:
412
- The distributed MyteCody client uses the Myte AI gateway for coding
413
- inference. It can inspect local config without network, but coding requires
414
- gateway access and a Myte AI key.
415
-
416
- Environment:
417
- MYTEAI_API_KEY Myte AI inference key
418
- MYTE_CODY_API_BASE Myte AI gateway base URL
419
- MYTE_CODY_HOME local MyteCody client cache/install directory
420
- `);
421
- }
422
-
423
- function commonStatus(args, envPath) {
424
- const keyInfo = getKeyInfo();
425
- const current = readCurrentClientManifest();
426
- return {
427
- product: "MyteCody",
428
- command: "mytecody",
429
- package_version: PACKAGE_VERSION,
430
- package: packageStatusBase(args),
431
- mode: "team-gateway",
432
- workspace: process.cwd(),
433
- env_file: envPath,
434
- auth: keyInfo,
435
- gateway: {
436
- base_url: gatewayBase(args),
437
- inference_base_url: codyInferenceBase(args),
438
- responses_transport: "async-job-bridge",
439
- network_required_for_coding: true,
440
- },
441
- instruction_pack: {
442
- owner: "myte-cody-gateway",
443
- client_embedded: false,
444
- },
445
- runtime: {
446
- context_window: DEFAULT_CONTEXT_WINDOW,
447
- auto_compact_tokens: DEFAULT_AUTO_COMPACT_TOKENS,
448
- tool_output_tokens: DEFAULT_TOOL_OUTPUT_TOKENS,
449
- max_concurrent_agent_threads: DEFAULT_AGENT_THREADS,
450
- },
451
- release: {
452
- channel: String(args.channel || process.env.MYTE_CODY_RELEASE_CHANNEL || DEFAULT_CHANNEL),
453
- manifest_url: manifestUrl(args),
454
- platform: platformKey(),
455
- install_root: installRoot(),
456
- engine_path: currentEnginePath(),
457
- bridge_path: currentBridgePath(),
458
- controller_path: currentControllerPath(),
459
- client_manifest: currentClientManifestPath(),
460
- client_installed: Boolean(current && installedClientCommand()),
461
- bridge_installed: fs.existsSync(currentBridgePath()),
462
- controller_installed: fs.existsSync(currentControllerPath()),
463
- client_version: current && current.version ? current.version : null,
464
- },
465
- };
466
- }
467
-
468
- function packageLatestUrl(args = {}) {
469
- return String(
470
- args["package-latest-url"] ||
471
- process.env.MYTE_CODY_PACKAGE_LATEST_URL ||
472
- process.env.MYTE_PACKAGE_LATEST_URL ||
473
- DEFAULT_PACKAGE_LATEST_URL,
474
- );
475
- }
476
-
477
- function packageUpdateCheckEnabled(args = {}) {
478
- if (args["package-update-check"] === false) return false;
479
- return process.env.MYTE_CODY_PACKAGE_UPDATE_CHECK !== "0";
480
- }
481
-
482
- function packageStatusBase(args = {}) {
483
- return {
484
- name: PACKAGE_NAME,
485
- installed_version: PACKAGE_VERSION,
486
- latest_version: null,
487
- update_available: null,
488
- check_status: "not-checked",
489
- latest_url: packageLatestUrl(args),
490
- update_command: `npm install -g ${PACKAGE_NAME}@latest`,
491
- engine_update_command: "mytecody update",
492
- };
493
- }
494
-
495
- function parseSemverish(version) {
496
- const main = String(version || "")
497
- .trim()
498
- .replace(/^v/i, "")
499
- .split(/[+-]/)[0];
500
- const match = main.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
501
- if (!match) return null;
502
- return [match[1], match[2] || "0", match[3] || "0"].map((part) => Number(part));
503
- }
504
-
505
- function compareSemverish(left, right) {
506
- const leftParts = parseSemverish(left);
507
- const rightParts = parseSemverish(right);
508
- if (!leftParts || !rightParts) return String(left || "").localeCompare(String(right || ""));
509
- for (let i = 0; i < 3; i += 1) {
510
- if (leftParts[i] > rightParts[i]) return 1;
511
- if (leftParts[i] < rightParts[i]) return -1;
512
- }
513
- return 0;
514
- }
515
-
516
- function isVersionNewer(latest, installed = PACKAGE_VERSION) {
517
- return compareSemverish(latest, installed) > 0;
518
- }
519
-
520
- function packageUpdateTimeoutMs() {
521
- const value = Number(process.env.MYTE_CODY_PACKAGE_UPDATE_TIMEOUT_MS || 1500);
522
- return Number.isFinite(value) && value > 0 ? value : 1500;
523
- }
524
-
525
- async function checkPackageUpdate(args = {}) {
526
- const status = packageStatusBase(args);
527
- if (!packageUpdateCheckEnabled(args)) {
528
- return { ...status, check_status: "skipped" };
529
- }
530
- try {
531
- const response = await fetchJson(status.latest_url, {
532
- timeoutMs: packageUpdateTimeoutMs(),
533
- });
534
- if (!response.ok) {
535
- return {
536
- ...status,
537
- check_status: "unavailable",
538
- registry_status: response.status,
539
- };
540
- }
541
- const latestVersion = response.body && response.body.version ? String(response.body.version) : "";
542
- if (!latestVersion) {
543
- return { ...status, check_status: "invalid-response" };
544
- }
545
- return {
546
- ...status,
547
- latest_version: latestVersion,
548
- update_available: isVersionNewer(latestVersion, PACKAGE_VERSION),
549
- check_status: "ok",
550
- };
551
- } catch (error) {
552
- return {
553
- ...status,
554
- check_status: "unavailable",
555
- error: error && error.message ? error.message : String(error),
556
- };
557
- }
558
- }
559
-
560
- function packageUpdateNotice(packageStatus) {
561
- if (!packageStatus || packageStatus.update_available !== true) return "";
562
- return `myte package update available: ${packageStatus.latest_version} (installed ${packageStatus.installed_version}). Run ${packageStatus.update_command}`;
563
- }
564
-
565
- function printJson(payload) {
566
- console.log(JSON.stringify(payload, null, 2));
567
- }
568
-
569
- function isUrl(value) {
570
- return /^https?:\/\//i.test(String(value || ""));
571
- }
572
-
573
- function statusLine(message) {
574
- if (process.env.MYTE_CODY_QUIET_SETUP === "1") return;
575
- console.error(`[MYTE CODY] ${message}`);
576
- }
577
-
578
- function setupProgress(splash) {
579
- return (message) => {
580
- if (splash && splash.enabled) {
581
- splash.setStatus(message);
582
- return;
583
- }
584
- statusLine(message);
585
- };
586
- }
587
-
588
- function formatBytes(bytes) {
589
- const value = Number(bytes || 0);
590
- if (!Number.isFinite(value) || value <= 0) return "unknown size";
591
- const units = ["B", "KB", "MB", "GB"];
592
- let size = value;
593
- let unit = 0;
594
- while (size >= 1024 && unit < units.length - 1) {
595
- size /= 1024;
596
- unit += 1;
597
- }
598
- return `${size.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
599
- }
600
-
601
- async function readManifest(source, { fetchManifest, progress } = {}) {
602
- if (!source) return { status: "missing", manifest: null };
603
- if (isUrl(source)) {
604
- if (!fetchManifest) {
605
- return { status: "skipped", manifest: null };
606
- }
607
- if (progress) progress("checking signed release manifest");
608
- const response = await fetch(source);
609
- const text = await response.text();
610
- if (!response.ok) {
611
- throw new Error(`Manifest fetch failed (${response.status}): ${text.slice(0, 300)}`);
612
- }
613
- return { status: "fetched", manifest: JSON.parse(text) };
614
- }
615
- const filePath = path.resolve(source);
616
- const text = fs.readFileSync(filePath, "utf8");
617
- return { status: "read", manifest: JSON.parse(text), file_path: filePath };
618
- }
619
-
620
- function stableJson(value) {
621
- if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
622
- if (value && typeof value === "object") {
623
- return `{${Object.keys(value)
624
- .sort()
625
- .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
626
- .join(",")}}`;
627
- }
628
- return JSON.stringify(value);
629
- }
630
-
631
- function manifestWithoutSignature(manifest) {
632
- const clone = JSON.parse(JSON.stringify(manifest || {}));
633
- delete clone.signature;
634
- return clone;
635
- }
636
-
637
- function verifyManifestSignature(manifest) {
638
- const signature = manifest && manifest.signature;
639
- if (!signature || !signature.value) {
640
- return { status: "missing", verified: false };
641
- }
642
- const publicKey = builtInPublicKeyPem();
643
- if (!publicKey) {
644
- return {
645
- status: "public-key-missing",
646
- verified: false,
647
- key_id: signature.key_id || null,
648
- };
649
- }
650
- const verifier = crypto.createVerify("SHA256");
651
- verifier.update(stableJson(manifestWithoutSignature(manifest)));
652
- verifier.end();
653
- return {
654
- status: "checked",
655
- verified: verifier.verify(publicKey, String(signature.value), "base64"),
656
- key_id: signature.key_id || null,
657
- };
658
- }
659
-
660
- function builtInPublicKeyPem() {
661
- const publicKeyPath = path.join(__dirname, "lib", "mytecody-release-public-key.pem");
662
- if (!fs.existsSync(publicKeyPath)) return "";
663
- return fs.readFileSync(publicKeyPath, "utf8");
664
- }
665
-
666
- function artifactForPlatform(manifest) {
667
- const artifacts = manifest && manifest.artifacts;
668
- const platform = platformKey();
669
- if (Array.isArray(artifacts)) {
670
- return (
671
- artifacts.find((artifact) => {
672
- if (!artifact || typeof artifact !== "object") return false;
673
- const combined = `${artifact.platform || process.platform}-${artifact.arch || process.arch}`;
674
- return artifact.platform_key === platform || combined === platform;
675
- }) || null
676
- );
677
- }
678
- if (!artifacts || typeof artifacts !== "object") return null;
679
- return artifacts[platform] || null;
680
- }
681
-
682
- function validateArtifactMetadata(artifact) {
683
- if (!artifact) return { status: "missing", ok: false };
684
- const missing = [];
685
- if (!artifact.url) missing.push("url");
686
- if (!artifact.sha256) missing.push("sha256");
687
- return {
688
- status: missing.length ? "invalid" : "present",
689
- ok: missing.length === 0,
690
- missing,
691
- url: artifact.url || null,
692
- format: artifact.format || null,
693
- sha256_present: Boolean(artifact.sha256),
694
- };
695
- }
696
-
697
- function localPathFromArtifactUrl(urlValue) {
698
- const raw = String(urlValue || "").trim();
699
- if (/^file:\/\//i.test(raw)) {
700
- const url = new URL(raw);
701
- return decodeURIComponent(url.pathname.replace(/^\/([A-Za-z]:)/, "$1"));
702
- }
703
- if (!isUrl(raw)) return path.resolve(raw);
704
- return null;
705
- }
706
-
707
- async function readArtifactBytes(artifact, { progress, label = "MyteCody engine" } = {}) {
708
- const urlValue = artifact && artifact.url ? String(artifact.url) : "";
709
- const localPath = localPathFromArtifactUrl(urlValue);
710
- if (localPath) {
711
- if (progress) progress(`reading local ${label} artifact`);
712
- return fs.readFileSync(localPath);
713
- }
714
- const headers = {};
715
- const token = getAuthToken();
716
- if (token) headers.Authorization = `Bearer ${token}`;
717
- if (progress) {
718
- const expectedSize = Number(artifact && artifact.size_bytes ? artifact.size_bytes : 0);
719
- progress(`downloading ${label} (${formatBytes(expectedSize)})`);
720
- }
721
- const response = await fetch(urlValue, { method: "GET", headers });
722
- if (!response.ok) {
723
- const bytes = Buffer.from(await response.arrayBuffer());
724
- throw new Error(`Artifact fetch failed (${response.status}): ${bytes.toString("utf8", 0, Math.min(bytes.length, 300))}`);
725
- }
726
- if (!response.body || typeof response.body.getReader !== "function") {
727
- const bytes = Buffer.from(await response.arrayBuffer());
728
- if (progress) progress(`downloaded ${label} (${formatBytes(bytes.length)})`);
729
- return bytes;
730
- }
731
-
732
- const total = Number(response.headers.get("content-length") || artifact?.size_bytes || 0);
733
- const reader = response.body.getReader();
734
- const chunks = [];
735
- let received = 0;
736
- let lastPct = -1;
737
- while (true) {
738
- const { done, value } = await reader.read();
739
- if (done) break;
740
- const chunk = Buffer.from(value);
741
- chunks.push(chunk);
742
- received += chunk.length;
743
- if (progress && total > 0) {
744
- const pct = Math.min(100, Math.floor((received / total) * 100));
745
- if (pct >= lastPct + 10 || pct === 100) {
746
- progress(`downloading ${label} ${pct}% (${formatBytes(received)} / ${formatBytes(total)})`);
747
- lastPct = pct;
748
- }
749
- }
750
- }
751
- const bytes = Buffer.concat(chunks);
752
- if (progress) progress(`downloaded ${label} (${formatBytes(bytes.length)})`);
753
- return bytes;
754
- }
755
-
756
- function sha256Hex(bytes) {
757
- return crypto.createHash("sha256").update(bytes).digest("hex");
758
- }
759
-
760
- function artifactFormat(artifact) {
761
- const explicit = String((artifact && artifact.format) || "").trim().toLowerCase();
762
- if (explicit) return explicit;
763
- const urlValue = String((artifact && artifact.url) || "").trim().toLowerCase();
764
- if (urlValue.endsWith(".gz")) return "gzip";
765
- return "exe";
766
- }
767
-
768
- function artifactBytesForInstall(bytes, artifact) {
769
- const format = artifactFormat(artifact);
770
- if (format === "exe" || format === "binary" || format === "raw") return bytes;
771
- if (format === "gzip" || format === "gz") return zlib.gunzipSync(bytes);
772
- throw new Error(`Unsupported MyteCody release artifact format: ${format}`);
773
- }
774
-
775
- function signatureAccepted(manifest, args = {}) {
776
- const signature = verifyManifestSignature(manifest);
777
- if (signature.verified) return { ok: true, signature, trusted_unsigned: false };
778
- return { ok: false, signature, trusted_unsigned: false };
779
- }
780
-
781
- function installArtifactBytes(bytes, manifest, artifact) {
782
- const installBytes = artifactBytesForInstall(bytes, artifact);
783
- const enginePath = currentEnginePath();
784
- fs.rmSync(path.dirname(path.dirname(enginePath)), { recursive: true, force: true });
785
- fs.mkdirSync(path.dirname(enginePath), { recursive: true });
786
- fs.writeFileSync(enginePath, installBytes);
787
- if (process.platform !== "win32") {
788
- fs.chmodSync(enginePath, 0o755);
789
- }
790
- const installedManifest = {
791
- schema_version: manifest.schema_version || 1,
792
- channel: manifest.channel || DEFAULT_CHANNEL,
793
- version: manifest.version || "unknown",
794
- installed_at: new Date().toISOString(),
795
- launcher_version: PACKAGE_VERSION,
796
- platform: platformKey(),
797
- executable: enginePath,
798
- artifact: {
799
- url: artifact.url,
800
- sha256: artifact.sha256,
801
- format: artifactFormat(artifact),
802
- size_bytes: bytes.length,
803
- installed_sha256: sha256Hex(installBytes),
804
- installed_size_bytes: installBytes.length,
805
- },
806
- };
807
- fs.writeFileSync(currentClientManifestPath(), JSON.stringify(installedManifest, null, 2), "utf8");
808
- return installedManifest;
809
- }
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
-
868
- async function installReleaseAssets(manifest, artifact, { progress } = {}) {
869
- const assets = releaseAssetsForPlatform(manifest, artifact);
870
- const installed = [];
871
- for (const asset of assets) {
872
- const name = String(asset.name || path.basename(String(asset.install_path || "")) || "client asset");
873
- const bytes = await readArtifactBytes(asset, { progress, label: `MyteCody ${name}` });
874
- const digest = sha256Hex(bytes);
875
- if (digest.toLowerCase() !== String(asset.sha256 || "").toLowerCase()) {
876
- throw new Error(`Release asset SHA-256 mismatch for ${name}: expected ${asset.sha256}, got ${digest}`);
877
- }
878
- const installBytes = artifactBytesForInstall(bytes, asset);
879
- const targetPath = assertSafeReleaseAssetInstallPath(asset.install_path);
880
- fs.mkdirSync(path.dirname(targetPath), { recursive: true });
881
- fs.writeFileSync(targetPath, installBytes);
882
- installed.push({
883
- name,
884
- install_path: String(asset.install_path || "").replace(/\\/g, "/"),
885
- url: asset.url,
886
- sha256: asset.sha256,
887
- format: artifactFormat(asset),
888
- size_bytes: bytes.length,
889
- installed_sha256: sha256Hex(installBytes),
890
- installed_size_bytes: installBytes.length,
891
- });
892
- }
893
- return installed;
894
- }
895
-
896
- async function fetchJson(url, { headers = {}, timeoutMs = 8000 } = {}) {
897
- const controller = typeof AbortController !== "undefined" ? new AbortController() : undefined;
898
- const timeoutId =
899
- controller && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
900
- try {
901
- const response = await fetch(url, {
902
- method: "GET",
903
- headers: {
904
- Accept: "application/json",
905
- ...headers,
906
- },
907
- signal: controller?.signal,
908
- });
909
- const text = await response.text();
910
- let body = {};
911
- if (text.trim()) {
912
- try {
913
- body = JSON.parse(text);
914
- } catch {
915
- body = { raw_text: text.slice(0, 500) };
916
- }
917
- }
918
- return {
919
- ok: Boolean(response.ok),
920
- status: response.status,
921
- body,
922
- };
923
- } finally {
924
- if (timeoutId) clearTimeout(timeoutId);
925
- }
926
- }
927
-
928
- async function probeGateway(args = {}) {
929
- const token = getAuthToken();
930
- const healthUrl = codyGatewayUrl(args, "/cody/health");
931
- const modelsUrl = codyGatewayUrl(args, "/cody/v1/models");
932
- const result = {
933
- ok: false,
934
- health: {
935
- url: healthUrl,
936
- ok: false,
937
- status: null,
938
- },
939
- models: {
940
- url: modelsUrl,
941
- ok: false,
942
- status: null,
943
- skipped: !token,
944
- },
945
- };
946
- try {
947
- const health = await fetchJson(healthUrl);
948
- result.health.ok = Boolean(health.ok && health.body && health.body.ok === true);
949
- result.health.status = health.status;
950
- result.health.service = health.body && health.body.service ? String(health.body.service) : null;
951
- result.health.model = health.body && health.body.model ? health.body.model : null;
952
- } catch (error) {
953
- result.health.error = error && error.message ? error.message : String(error);
954
- }
955
-
956
- if (token) {
957
- try {
958
- const models = await fetchJson(modelsUrl, {
959
- headers: { Authorization: `Bearer ${token}` },
960
- });
961
- const ids = Array.isArray(models.body?.data)
962
- ? models.body.data.map((item) => String(item && item.id ? item.id : "")).filter(Boolean)
963
- : [];
964
- result.models.ok = Boolean(models.ok && ids.includes("myte"));
965
- result.models.status = models.status;
966
- result.models.ids = ids;
967
- result.models.skipped = false;
968
- } catch (error) {
969
- result.models.error = error && error.message ? error.message : String(error);
970
- result.models.skipped = false;
971
- }
972
- }
973
-
974
- result.ok = Boolean(result.health.ok && result.models.ok);
975
- return result;
976
- }
977
-
978
- function tomlString(value) {
979
- return JSON.stringify(String(value || ""));
980
- }
981
-
982
- function tomlLiteralString(value) {
983
- const text = String(value || "");
984
- if (!text.includes("'") && !text.includes("\n") && !text.includes("\r")) {
985
- return `'${text}'`;
986
- }
987
- return tomlString(text);
988
- }
989
-
990
- function pathForToml(value) {
991
- return String(value || "");
992
- }
993
-
994
- function writeCodexModelCatalog() {
995
- fs.mkdirSync(codexHome(), { recursive: true });
996
- const catalog = {
997
- models: [
998
- {
999
- slug: DEFAULT_MODEL_ALIAS,
1000
- display_name: "Myte",
1001
- description: "Myte AI coding model.",
1002
- base_instructions: CLIENT_BASE_INSTRUCTIONS,
1003
- default_reasoning_level: "medium",
1004
- supported_reasoning_levels: [
1005
- { effort: "low", description: "Fast local coding pass." },
1006
- { effort: "medium", description: "Balanced coding pass." },
1007
- { effort: "high", description: "Deeper coding pass." },
1008
- ],
1009
- shell_type: "shell_command",
1010
- visibility: "list",
1011
- supported_in_api: true,
1012
- priority: 0,
1013
- availability_nux: null,
1014
- upgrade: null,
1015
- supports_reasoning_summaries: false,
1016
- default_reasoning_summary: "none",
1017
- support_verbosity: false,
1018
- default_verbosity: null,
1019
- apply_patch_tool_type: "freeform",
1020
- web_search_tool_type: "text",
1021
- truncation_policy: {
1022
- mode: "tokens",
1023
- limit: 10000,
1024
- },
1025
- supports_parallel_tool_calls: true,
1026
- supports_image_detail_original: false,
1027
- context_window: DEFAULT_CONTEXT_WINDOW,
1028
- max_context_window: DEFAULT_CONTEXT_WINDOW,
1029
- auto_compact_token_limit: DEFAULT_AUTO_COMPACT_TOKENS,
1030
- effective_context_window_percent: 90,
1031
- experimental_supported_tools: [],
1032
- input_modalities: ["text"],
1033
- supports_search_tool: false,
1034
- use_responses_lite: false,
1035
- auto_review_model_override: null,
1036
- },
1037
- ],
1038
- };
1039
- const catalogPath = codexModelCatalogPath();
1040
- fs.writeFileSync(catalogPath, JSON.stringify(catalog, null, 2), "utf8");
1041
- return catalogPath;
1042
- }
1043
-
1044
- function writeCodexConfig(args = {}, providerBaseUrl = codyInferenceBase(args)) {
1045
- fs.mkdirSync(codexHome(), { recursive: true });
1046
- const catalogPath = writeCodexModelCatalog();
1047
- const config = `model = ${tomlString(DEFAULT_MODEL_ALIAS)}
1048
- model_provider = "myte_ai"
1049
- model_catalog_json = ${tomlString(catalogPath)}
1050
- model_context_window = ${DEFAULT_CONTEXT_WINDOW}
1051
- model_auto_compact_token_limit = ${DEFAULT_AUTO_COMPACT_TOKENS}
1052
- tool_output_token_limit = ${DEFAULT_TOOL_OUTPUT_TOKENS}
1053
- web_search = "disabled"
1054
- suppress_unstable_features_warning = true
1055
- check_for_update_on_startup = false
1056
-
1057
- [tui]
1058
- show_tooltips = false
1059
- status_line = ["run-state", "current-dir"]
1060
- status_line_use_colors = true
1061
- terminal_title = ["project"]
1062
-
1063
- [model_providers.myte_ai]
1064
- name = "Myte AI"
1065
- base_url = ${tomlString(providerBaseUrl)}
1066
- env_key = "MYTE_CODY_AUTH_TOKEN"
1067
- wire_api = "responses"
1068
- requires_openai_auth = false
1069
-
1070
- [features]
1071
- apps = false
1072
- multi_agent = false
1073
- hooks = false
1074
- memories = false
1075
- plugins = false
1076
- tool_suggest = false
1077
-
1078
- [features.multi_agent_v2]
1079
- enabled = true
1080
- max_concurrent_threads_per_session = ${DEFAULT_AGENT_THREADS}
1081
-
1082
- [skills]
1083
- include_instructions = true
1084
-
1085
- [skills.bundled]
1086
- enabled = false
1087
-
1088
- [projects.${tomlLiteralString(process.cwd())}]
1089
- trust_level = "trusted"
1090
-
1091
- [windows]
1092
- sandbox = "unelevated"
1093
- `;
1094
- const configPath = path.join(codexHome(), "config.toml");
1095
- fs.writeFileSync(configPath, config, "utf8");
1096
- return { configPath, catalogPath };
1097
- }
1098
-
1099
- function resolveCodexCommand() {
1100
- const installed = installedClientCommand();
1101
- if (installed) return installed;
1102
- return null;
1103
- }
1104
-
1105
- function codexProviderArgs(args = {}, providerBaseUrl = codyInferenceBase(args)) {
1106
- return [
1107
- "-c",
1108
- 'model_provider="myte_ai"',
1109
- "-c",
1110
- 'model_providers.myte_ai.name="Myte AI"',
1111
- "-c",
1112
- `model_providers.myte_ai.base_url="${providerBaseUrl}"`,
1113
- "-c",
1114
- 'model_providers.myte_ai.env_key="MYTE_CODY_AUTH_TOKEN"',
1115
- "-c",
1116
- 'model_providers.myte_ai.wire_api="responses"',
1117
- "-c",
1118
- `model_catalog_json=${tomlString(codexModelCatalogPath())}`,
1119
- "-c",
1120
- `model_context_window=${DEFAULT_CONTEXT_WINDOW}`,
1121
- "-c",
1122
- `model_auto_compact_token_limit=${DEFAULT_AUTO_COMPACT_TOKENS}`,
1123
- "-c",
1124
- `tool_output_token_limit=${DEFAULT_TOOL_OUTPUT_TOKENS}`,
1125
- "-c",
1126
- "web_search=\"disabled\"",
1127
- "-c",
1128
- "suppress_unstable_features_warning=true",
1129
- "-c",
1130
- "features.multi_agent_v2.enabled=true",
1131
- "-c",
1132
- `features.multi_agent_v2.max_concurrent_threads_per_session=${DEFAULT_AGENT_THREADS}`,
1133
- "--sandbox",
1134
- "danger-full-access",
1135
- "--ask-for-approval",
1136
- "never",
1137
- "-m",
1138
- DEFAULT_MODEL_ALIAS,
1139
- ];
1140
- }
1141
-
1142
- function codexLaunchArgs(rawArgs, args = {}, providerBaseUrl = codyInferenceBase(args)) {
1143
- const providerArgs = codexProviderArgs(args, providerBaseUrl);
1144
- if (!rawArgs.length) return providerArgs;
1145
- if (rawArgs[0] === "exec") return [...providerArgs, "exec", "--skip-git-repo-check", ...rawArgs.slice(1)];
1146
- return [...providerArgs, ...rawArgs];
1147
- }
1148
-
1149
- function execArgsAndStdin(rawArgs) {
1150
- if (!rawArgs.length) return { args: [], stdin: null };
1151
- const valueOptions = new Set([
1152
- "-a",
1153
- "--ask-for-approval",
1154
- "-C",
1155
- "--cd",
1156
- "-c",
1157
- "--config",
1158
- "-i",
1159
- "--image",
1160
- "-m",
1161
- "--model",
1162
- "-o",
1163
- "--output-last-message",
1164
- "-p",
1165
- "--profile",
1166
- "-s",
1167
- "--sandbox",
1168
- "--color",
1169
- "--local-provider",
1170
- "--output-schema",
1171
- ]);
1172
- const forwarded = [];
1173
- let index = 0;
1174
- while (index < rawArgs.length) {
1175
- const arg = rawArgs[index];
1176
- if (arg === "--") {
1177
- index += 1;
1178
- break;
1179
- }
1180
- if (arg === "-") break;
1181
- if (!arg.startsWith("-")) break;
1182
- forwarded.push(arg);
1183
- index += 1;
1184
- if (valueOptions.has(arg) && index < rawArgs.length) {
1185
- forwarded.push(rawArgs[index]);
1186
- index += 1;
1187
- }
1188
- }
1189
- const promptParts = rawArgs.slice(index);
1190
- if (!promptParts.length) return { args: forwarded, stdin: null };
1191
- if (promptParts.length === 1 && promptParts[0] === "-") {
1192
- return { args: [...forwarded, "-"], stdin: fs.readFileSync(0, "utf8") };
1193
- }
1194
- const prompt = promptParts.join(" ");
1195
- if (prompt.includes("\n") || prompt.includes("\r") || promptParts.length > 1) {
1196
- return { args: [...forwarded, "-"], stdin: prompt };
1197
- }
1198
- return { args: [...forwarded, prompt], stdin: null };
1199
- }
1200
-
1201
- function stripControllerArgs(rawArgs) {
1202
- const kept = [];
1203
- let enabled = process.env.MYTE_CODY_CONTROLLER === "1";
1204
- for (let i = 0; i < rawArgs.length; i += 1) {
1205
- const arg = rawArgs[i];
1206
- if (arg === "--controller") {
1207
- const next = rawArgs[i + 1];
1208
- const normalizedNext = String(next || "").toLowerCase();
1209
- if (next !== undefined && !next.startsWith("-") && ["0", "1", "false", "true", "off", "on", "raw"].includes(normalizedNext)) {
1210
- enabled = !["0", "false", "off", "raw"].includes(String(next).toLowerCase());
1211
- i += 1;
1212
- } else {
1213
- enabled = true;
1214
- }
1215
- continue;
1216
- }
1217
- if (arg.startsWith("--controller=")) {
1218
- const value = arg.slice("--controller=".length);
1219
- enabled = !["0", "false", "off", "raw"].includes(String(value).toLowerCase());
1220
- continue;
1221
- }
1222
- if (arg === "--no-controller") {
1223
- enabled = false;
1224
- continue;
1225
- }
1226
- kept.push(arg);
1227
- }
1228
- return { enabled, args: kept };
1229
- }
1230
-
1231
- function controllerPromptFromExecArgs(rawArgs) {
1232
- const execInput = execArgsAndStdin(rawArgs);
1233
- if (execInput.stdin != null) return { prompt: execInput.stdin, forwardedArgs: [] };
1234
- const prompt = execInput.args.length ? execInput.args[execInput.args.length - 1] : "";
1235
- if (!prompt || prompt.startsWith("-")) {
1236
- return { prompt: "", forwardedArgs: execInput.args };
1237
- }
1238
- return { prompt, forwardedArgs: execInput.args.slice(0, -1) };
1239
- }
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
-
1259
- function runEngineExecWorker({ command, args, providerBaseUrl, token, prompt, timeoutMs }) {
1260
- return new Promise((resolve) => {
1261
- const started = Date.now();
1262
- const env = {
1263
- ...process.env,
1264
- CODEX_HOME: codexHome(),
1265
- MYTE_CODY_AUTH_TOKEN: token,
1266
- MYTE_CODY_BRAND: "1",
1267
- MYTE_CODY_CONTROLLER: "0",
1268
- MYTE_CODY_BRIDGE_BASE_URL: providerBaseUrl,
1269
- };
1270
- const launchArgs = [...command.args, ...codexLaunchArgs(["exec", "--json", "-"], args, providerBaseUrl)];
1271
- const child = spawn(command.cmd, launchArgs, {
1272
- cwd: process.cwd(),
1273
- env,
1274
- stdio: ["pipe", "pipe", "pipe"],
1275
- shell: process.platform === "win32" && command.cmd === "codex",
1276
- });
1277
- let stdout = "";
1278
- let stderr = "";
1279
- let settled = false;
1280
- const timer = setTimeout(() => {
1281
- if (settled) return;
1282
- try {
1283
- child.kill();
1284
- } catch {}
1285
- }, timeoutMs || 180000);
1286
- child.stdout.on("data", (chunk) => {
1287
- stdout += chunk.toString();
1288
- });
1289
- child.stderr.on("data", (chunk) => {
1290
- stderr += chunk.toString();
1291
- });
1292
- child.on("error", (error) => {
1293
- settled = true;
1294
- clearTimeout(timer);
1295
- resolve({
1296
- status: 1,
1297
- stdout,
1298
- stderr,
1299
- error: error.message || String(error),
1300
- durationMs: Date.now() - started,
1301
- });
1302
- });
1303
- child.on("exit", (code, signal) => {
1304
- settled = true;
1305
- clearTimeout(timer);
1306
- resolve({
1307
- status: code == null ? 1 : code,
1308
- signal: signal || null,
1309
- stdout,
1310
- stderr,
1311
- error: null,
1312
- durationMs: Date.now() - started,
1313
- });
1314
- });
1315
- child.stdin.end(prompt);
1316
- });
1317
- }
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
-
1555
- async function runCodex(rawArgs, args = {}, envPath = null) {
1556
- const token = getAuthToken();
1557
- if (!token) {
1558
- console.error("MyteCody requires MYTEAI_API_KEY for coding.");
1559
- return 1;
1560
- }
1561
- const splash = createMyteSplash();
1562
- const progress = setupProgress(splash);
1563
- splash.start("preparing trusted workspace");
1564
- progress("preparing trusted workspace");
1565
- try {
1566
- const install = await ensureBrandedEngineInstalled(args, envPath, { progress });
1567
- if (install.ok && install.installed) {
1568
- progress(`engine installed: ${install.payload.installed.version}`);
1569
- } else if (install.ok) {
1570
- progress("engine ready");
1571
- } else if (!install.ok) {
1572
- await splash.stop();
1573
- console.error(`MyteCody branded engine could not be verified: ${install.reason || "unknown"}.`);
1574
- console.error("Run `mytecody update` with access to the Myte release manifest.");
1575
- return 1;
1576
- }
1577
- } catch (error) {
1578
- await splash.stop();
1579
- console.error(`MyteCody engine verification failed: ${error && error.message ? error.message : error}`);
1580
- return 1;
1581
- }
1582
-
1583
- let packageNotice = "";
1584
- try {
1585
- progress("checking myte package version");
1586
- packageNotice = packageUpdateNotice(await checkPackageUpdate(args));
1587
- } catch {
1588
- packageNotice = "";
1589
- }
1590
-
1591
- const command = resolveCodexCommand();
1592
- if (!command) {
1593
- await splash.stop();
1594
- console.error("MyteCody branded engine is not installed.");
1595
- console.error("Run `mytecody update` with access to the Myte release manifest.");
1596
- return 1;
1597
- }
1598
- let bridge = null;
1599
- try {
1600
- progress("opening Myte inference bridge");
1601
- const signedBridge = loadSignedBridge();
1602
- bridge = await signedBridge.startMyteCodyAsyncResponsesBridge({
1603
- gatewayRoot: gatewayRoot(args),
1604
- token,
1605
- });
1606
- writeCodexConfig(args, bridge.baseUrl);
1607
- } catch (error) {
1608
- await splash.stop();
1609
- console.error(`MyteCody inference bridge failed to start: ${error && error.message ? error.message : error}`);
1610
- return 1;
1611
- }
1612
-
1613
- const invocation = classifyMyteCodyInvocation(rawArgs);
1614
- if (["controller-exec", "controller-prompt", "controller-shell", "controller-resume"].includes(invocation.mode)) {
1615
- try {
1616
- progress("opening MyteCody controller");
1617
- const signedController = loadSignedController();
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);
1673
- if (!promptInfo.prompt.trim()) {
1674
- await splash.stop();
1675
- await bridge.close();
1676
- console.error("MyteCody controller requires a prompt.");
1677
- return 1;
1678
- }
1679
- const code = await runSignedControllerPrompt({
1680
- signedController,
1681
- command,
1682
- args,
1683
- bridge,
1684
- token,
1685
- promptInfo,
1686
- });
1687
- await bridge.close();
1688
- return code;
1689
- } catch (error) {
1690
- await splash.stop();
1691
- await bridge.close();
1692
- console.error(`MyteCody controller failed: ${error && error.message ? error.message : error}`);
1693
- return 1;
1694
- }
1695
- }
1696
-
1697
- const launchArgs = [...command.args, ...codexLaunchArgs(invocation.args, args, bridge.baseUrl)];
1698
- const env = {
1699
- ...process.env,
1700
- CODEX_HOME: codexHome(),
1701
- MYTE_CODY_AUTH_TOKEN: token,
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(),
1708
- MYTE_CODY_BRIDGE_BASE_URL: bridge.baseUrl,
1709
- };
1710
- progress("opening MyteCody workspace");
1711
- await splash.stop();
1712
- if (packageNotice) statusLine(packageNotice);
1713
- return new Promise((resolve) => {
1714
- const child = spawn(command.cmd, launchArgs, {
1715
- cwd: process.cwd(),
1716
- env,
1717
- stdio: "inherit",
1718
- shell: process.platform === "win32" && command.cmd === "codex",
1719
- });
1720
- child.on("error", (error) => {
1721
- console.error(`Unable to launch MyteCody engine: ${error.message || error}`);
1722
- bridge.close().finally(() => resolve(1));
1723
- });
1724
- child.on("close", (code) => {
1725
- bridge.close().finally(() => resolve(Number.isInteger(code) ? code : 1));
1726
- });
1727
- });
1728
- }
1729
-
1730
- async function runDoctor(args, envPath) {
1731
- const payload = {
1732
- ok: true,
1733
- ready_for_coding: false,
1734
- ...commonStatus(args, envPath),
1735
- };
1736
- payload.package = await checkPackageUpdate(args);
1737
- if (args["probe-gateway"]) {
1738
- payload.gateway.probe = await probeGateway(args);
1739
- }
1740
- payload.ready_for_coding =
1741
- payload.auth.present &&
1742
- payload.release.client_installed &&
1743
- payload.release.bridge_installed;
1744
- if (payload.gateway.probe) {
1745
- payload.ready_for_coding = Boolean(payload.ready_for_coding && payload.gateway.probe.ok);
1746
- }
1747
- if (args.json) {
1748
- printJson(payload);
1749
- return 0;
1750
- }
1751
- console.log("MYTE CODY - Your Tech Your Way");
1752
- console.log("");
1753
- console.log(`mode: ${payload.mode}`);
1754
- console.log(`workspace: ${payload.workspace}`);
1755
- console.log(`auth: ${payload.auth.present ? `present (${payload.auth.source})` : "missing"}`);
1756
- console.log(`gateway: ${payload.gateway.base_url}`);
1757
- if (payload.gateway.probe) {
1758
- console.log(`gateway probe: ${payload.gateway.probe.ok ? "ok" : "failed"}`);
1759
- }
1760
- console.log(`package: ${payload.package.installed_version}`);
1761
- if (payload.package.check_status === "ok" && payload.package.update_available) {
1762
- console.log(`package update: ${payload.package.latest_version} available`);
1763
- console.log(`package command: ${payload.package.update_command}`);
1764
- } else if (payload.package.check_status === "ok") {
1765
- console.log("package update: current");
1766
- } else if (payload.package.check_status === "skipped") {
1767
- console.log("package update: skipped");
1768
- } else {
1769
- console.log(`package update: ${payload.package.check_status}`);
1770
- }
1771
- console.log(`client: ${payload.release.client_installed ? payload.release.client_version : "not installed"}`);
1772
- console.log(`bridge: ${payload.release.bridge_installed ? "installed" : "not installed"}`);
1773
- console.log(`install: ${payload.release.install_root}`);
1774
- console.log("");
1775
- console.log("Coding requires the Myte AI gateway and a Myte AI key.");
1776
- return 0;
1777
- }
1778
-
1779
- async function buildUpdatePayload(args, envPath, { dryRun = false, progress = null } = {}) {
1780
- const isDryRun = Boolean(dryRun);
1781
- const source = manifestUrl(args);
1782
- const manifestResult = await readManifest(source, {
1783
- fetchManifest: Boolean(args["fetch-manifest"]) || !isDryRun,
1784
- progress,
1785
- });
1786
- const manifest = manifestResult.manifest;
1787
- const artifact = manifest ? artifactForPlatform(manifest) : null;
1788
- const signature = manifest ? signatureAccepted(manifest, args) : { ok: false, signature: { status: "not-checked", verified: false } };
1789
- const artifactMetadata = manifest ? validateArtifactMetadata(artifact) : { status: "not-checked", ok: false };
1790
- const releaseAssets = manifest ? releaseAssetsForPlatform(manifest, artifact) : [];
1791
- const payload = {
1792
- ok: true,
1793
- dry_run: isDryRun,
1794
- would_write: !isDryRun,
1795
- ...commonStatus(args, envPath),
1796
- manifest: {
1797
- source,
1798
- read_status: manifestResult.status,
1799
- version: manifest && manifest.version ? manifest.version : null,
1800
- signature: signature.signature,
1801
- trusted_unsigned: Boolean(signature.trusted_unsigned),
1802
- },
1803
- artifact: artifactMetadata,
1804
- release_assets: releaseAssets.map((asset) => ({
1805
- name: asset.name || null,
1806
- install_path: asset.install_path,
1807
- url: asset.url || null,
1808
- format: asset.format || null,
1809
- sha256_present: Boolean(asset.sha256),
1810
- })),
1811
- };
1812
-
1813
- if (!isDryRun) {
1814
- if (!manifest) {
1815
- throw new Error("Cannot install MyteCody engine without a readable release manifest.");
1816
- }
1817
- if (!signature.ok) {
1818
- throw new Error("MyteCody release manifest signature is not trusted.");
1819
- }
1820
- if (!artifactMetadata.ok) {
1821
- throw new Error(`MyteCody release artifact metadata is ${artifactMetadata.status}.`);
1822
- }
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);
1839
- }
1840
- const installedAssets = await installReleaseAssets(manifest, artifact, { progress });
1841
- if (installedAssets.length) {
1842
- installed.assets = installedAssets;
1843
- fs.writeFileSync(currentClientManifestPath(), JSON.stringify(installed, null, 2), "utf8");
1844
- }
1845
- payload.installed = {
1846
- ok: true,
1847
- version: installed.version,
1848
- executable: installed.executable,
1849
- sha256: artifactDigest,
1850
- size_bytes: artifactSizeBytes,
1851
- installed_sha256: installed.artifact.installed_sha256,
1852
- installed_size_bytes: installed.artifact.installed_size_bytes,
1853
- format: installed.artifact.format,
1854
- assets: installedAssets,
1855
- };
1856
- payload.release = {
1857
- ...payload.release,
1858
- client_installed: true,
1859
- client_version: installed.version,
1860
- engine_path: installed.executable,
1861
- };
1862
- }
1863
-
1864
- return payload;
1865
- }
1866
-
1867
- function autoInstallEnabled(args = {}) {
1868
- if (args["auto-update"] === false) return false;
1869
- if (process.env.MYTE_CODY_AUTO_UPDATE === "0") return false;
1870
- return true;
1871
- }
1872
-
1873
- async function ensureBrandedEngineInstalled(args = {}, envPath = null, { progress = null } = {}) {
1874
- const updateArgs = {
1875
- ...args,
1876
- "fetch-manifest": true,
1877
- };
1878
- delete updateArgs["dry-run"];
1879
- delete updateArgs.json;
1880
-
1881
- const source = manifestUrl(updateArgs);
1882
- let manifestResult;
1883
- try {
1884
- manifestResult = await readManifest(source, { fetchManifest: true, progress });
1885
- } catch (error) {
1886
- if (isUrl(source) && installedClientUsable()) {
1887
- return {
1888
- ok: true,
1889
- installed: false,
1890
- reason: "cached-engine-manifest-unavailable",
1891
- manifest_status: "fetch-failed",
1892
- error: error && error.message ? error.message : String(error),
1893
- };
1894
- }
1895
- throw error;
1896
- }
1897
- const manifest = manifestResult.manifest;
1898
- if (!manifest) {
1899
- if (isUrl(source) && installedClientUsable()) {
1900
- return {
1901
- ok: true,
1902
- installed: false,
1903
- reason: "cached-engine-manifest-unavailable",
1904
- manifest_status: manifestResult.status,
1905
- };
1906
- }
1907
- return { ok: false, installed: false, reason: "manifest-unavailable", manifest_status: manifestResult.status };
1908
- }
1909
- const signature = signatureAccepted(manifest, updateArgs);
1910
- if (!signature.ok) {
1911
- return { ok: false, installed: false, reason: "manifest-untrusted", signature: signature.signature };
1912
- }
1913
- const artifact = artifactForPlatform(manifest);
1914
- const artifactMetadata = validateArtifactMetadata(artifact);
1915
- if (!artifactMetadata.ok) {
1916
- return { ok: false, installed: false, reason: "artifact-metadata-invalid", artifact: artifactMetadata };
1917
- }
1918
- if (installedClientMatchesManifest(manifest, artifact)) {
1919
- return { ok: true, installed: false, reason: "already-current" };
1920
- }
1921
-
1922
- if (!autoInstallEnabled(args)) {
1923
- return { ok: false, installed: false, reason: "update-required-auto-install-disabled" };
1924
- }
1925
-
1926
- const payload = await buildUpdatePayload(updateArgs, envPath, { dryRun: false, progress });
1927
- return {
1928
- ok: Boolean(payload.installed && payload.installed.ok),
1929
- installed: Boolean(payload.installed && payload.installed.ok),
1930
- reason: "installed",
1931
- payload,
1932
- };
1933
- }
1934
-
1935
- async function runUpdate(args, envPath) {
1936
- const dryRun = Boolean(args["dry-run"]);
1937
- const payload = await buildUpdatePayload(args, envPath, {
1938
- dryRun,
1939
- progress: args.json ? null : statusLine,
1940
- });
1941
-
1942
- if (args.json) {
1943
- printJson(payload);
1944
- return 0;
1945
- }
1946
- console.log(dryRun ? "MYTE CODY update dry-run" : "MYTE CODY update");
1947
- console.log("scope: MyteCody engine only");
1948
- console.log(`manifest: ${payload.manifest.source}`);
1949
- console.log(`manifest read: ${payload.manifest.read_status}`);
1950
- console.log(`platform: ${payload.release.platform}`);
1951
- console.log(`install: ${payload.release.install_root}`);
1952
- console.log(`would write: ${payload.would_write}`);
1953
- if (payload.manifest.read_status !== "skipped") {
1954
- console.log(`version: ${payload.manifest.version || "unknown"}`);
1955
- console.log(`signature: ${payload.manifest.signature.status}`);
1956
- console.log(`artifact: ${payload.artifact.status}`);
1957
- } else {
1958
- console.log("manifest fetch skipped; pass --fetch-manifest to test the configured endpoint.");
1959
- }
1960
- if (payload.installed) {
1961
- console.log(`installed: ${payload.installed.executable}`);
1962
- }
1963
- console.log(`npm launcher/API tools: ${payload.package.update_command}`);
1964
- return 0;
1965
- }
1966
-
1967
- async function run(argv = process.argv.slice(2)) {
1968
- const envPath = loadEnv();
1969
- const parsed = parseArgs(argv);
1970
- const command = argv[0] || "codex";
1971
- const restArgs = parseArgs(argv.slice(1));
1972
-
1973
- if (command === "help" || command === "--help" || command === "-h") {
1974
- printHelp();
1975
- return 0;
1976
- }
1977
- if (command === "doctor") return runDoctor(restArgs, envPath);
1978
- if (command === "update") return runUpdate(restArgs, envPath);
1979
- if (command === "controller-sidecar") return runControllerSidecar();
1980
- if (command === "version" || command === "--version" || command === "-v") {
1981
- console.log(PACKAGE_VERSION);
1982
- return 0;
1983
- }
1984
-
1985
- return runCodex(argv, parsed, envPath);
1986
- }
1987
-
1988
- async function main() {
1989
- try {
1990
- const code = await run();
1991
- process.exitCode = code;
1992
- } catch (error) {
1993
- console.error(error && error.message ? error.message : error);
1994
- process.exitCode = 1;
1995
- }
1996
- }
1997
-
1998
- if (require.main === module) {
1999
- main();
2000
- }
2001
-
2002
- module.exports = {
2003
- checkPackageUpdate,
2004
- classifyMyteCodyInvocation,
2005
- codexLaunchArgs,
2006
- codexProviderArgs,
2007
- codyInferenceBase,
2008
- codyGatewayUrl,
2009
- currentBridgePath,
2010
- currentControllerPath,
2011
- currentClientManifestPath,
2012
- currentEnginePath,
2013
- ensureBrandedEngineInstalled,
2014
- gatewayRoot,
2015
- installedClientCommand,
2016
- isVersionNewer,
2017
- packageUpdateNotice,
2018
- resolveCodexCommand,
2019
- run,
2020
- sha256Hex,
2021
- stableJson,
2022
- tomlLiteralString,
2023
- verifyManifestSignature,
2024
- writeCodexConfig,
2025
- };
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const os = require("os");
6
+ const path = require("path");
7
+ const crypto = require("crypto");
8
+ const zlib = require("zlib");
9
+ const { spawn } = require("child_process");
10
+ const readline = require("readline");
11
+ const {
12
+ DEFAULT_MYTEAI_BASE,
13
+ normalizeMyteAiBase,
14
+ } = require("./lib/ai-gateway");
15
+ const { createMyteSplash } = require("./lib/mytecody-splash");
16
+
17
+ const PACKAGE_NAME = "myte";
18
+ const PACKAGE_VERSION = require("./package.json").version;
19
+ const DEFAULT_PACKAGE_LATEST_URL = "https://registry.npmjs.org/myte/latest";
20
+ const DEFAULT_CHANNEL = "alpha";
21
+ const DEFAULT_MODEL_ALIAS = "myte";
22
+ const DEFAULT_CONTEXT_WINDOW = Number(process.env.MYTE_CODY_CONTEXT_WINDOW || 49152);
23
+ const DEFAULT_AUTO_COMPACT_TOKENS = Number(process.env.MYTE_CODY_AUTO_COMPACT_TOKENS || 40960);
24
+ const DEFAULT_TOOL_OUTPUT_TOKENS = Number(process.env.MYTE_CODY_TOOL_OUTPUT_TOKENS || 10000);
25
+ const DEFAULT_AGENT_THREADS = Number(process.env.MYTE_CODY_AGENT_THREADS || 4);
26
+ const CLIENT_BASE_INSTRUCTIONS =
27
+ "You are MyteCody, a coding agent running through the Myte coding gateway.";
28
+
29
+ function findEnvPath(startDir) {
30
+ let cur = startDir;
31
+ for (let i = 0; i < 8; i += 1) {
32
+ const candidate = path.join(cur, ".env");
33
+ if (fs.existsSync(candidate)) return candidate;
34
+ const parent = path.dirname(cur);
35
+ if (parent === cur) break;
36
+ cur = parent;
37
+ }
38
+ return null;
39
+ }
40
+
41
+ function loadEnv() {
42
+ const envPath = findEnvPath(process.cwd());
43
+ if (!envPath || !fs.existsSync(envPath)) return null;
44
+ const content = fs.readFileSync(envPath, "utf8");
45
+ content.split(/\r?\n/).forEach((line) => {
46
+ const trimmed = String(line || "").trim();
47
+ if (!trimmed || trimmed.startsWith("#")) return;
48
+ const idx = trimmed.indexOf("=");
49
+ if (idx === -1) return;
50
+ const key = trimmed.slice(0, idx).trim();
51
+ let value = trimmed.slice(idx + 1).trim();
52
+ if (
53
+ (value.startsWith('"') && value.endsWith('"')) ||
54
+ (value.startsWith("'") && value.endsWith("'"))
55
+ ) {
56
+ value = value.slice(1, -1);
57
+ }
58
+ if (key && !(key in process.env)) process.env[key] = value;
59
+ });
60
+ return envPath;
61
+ }
62
+
63
+ function parseArgs(argv) {
64
+ const parsed = { _: [] };
65
+ for (let i = 0; i < argv.length; i += 1) {
66
+ const token = argv[i];
67
+ if (token === "--") {
68
+ parsed._.push(...argv.slice(i + 1));
69
+ break;
70
+ }
71
+ if (token.startsWith("--no-")) {
72
+ parsed[token.slice(5)] = false;
73
+ continue;
74
+ }
75
+ if (token.startsWith("--")) {
76
+ const eqIdx = token.indexOf("=");
77
+ if (eqIdx !== -1) {
78
+ parsed[token.slice(2, eqIdx)] = token.slice(eqIdx + 1);
79
+ continue;
80
+ }
81
+ const key = token.slice(2);
82
+ const next = argv[i + 1];
83
+ if (next !== undefined && !next.startsWith("-")) {
84
+ parsed[key] = next;
85
+ i += 1;
86
+ } else {
87
+ parsed[key] = true;
88
+ }
89
+ continue;
90
+ }
91
+ parsed._.push(token);
92
+ }
93
+ return parsed;
94
+ }
95
+
96
+ function getKeyInfo(env = process.env) {
97
+ if (String(env.MYTEAI_API_KEY || "").trim()) {
98
+ return { present: true, source: "MYTEAI_API_KEY" };
99
+ }
100
+ if (String(env.MYTE_AI_API_KEY || "").trim()) {
101
+ return { present: true, source: "MYTE_AI_API_KEY" };
102
+ }
103
+ return { present: false, source: null };
104
+ }
105
+
106
+ function getAuthToken(env = process.env) {
107
+ return String(env.MYTEAI_API_KEY || env.MYTE_AI_API_KEY || "").trim();
108
+ }
109
+
110
+ function gatewayBase(args = {}) {
111
+ const raw =
112
+ args["base-url"] ||
113
+ process.env.MYTE_CODY_API_BASE ||
114
+ process.env.MYTEAI_API_BASE ||
115
+ process.env.MYTE_AI_API_BASE ||
116
+ DEFAULT_MYTEAI_BASE;
117
+ return normalizeMyteAiBase(raw);
118
+ }
119
+
120
+ function gatewayRoot(args = {}) {
121
+ return gatewayBase(args).replace(/\/v1$/i, "");
122
+ }
123
+
124
+ function codyGatewayUrl(args = {}, suffix = "") {
125
+ const root = gatewayRoot(args).replace(/\/+$/, "");
126
+ const tail = String(suffix || "").startsWith("/") ? String(suffix) : `/${suffix}`;
127
+ return `${root}${tail}`;
128
+ }
129
+
130
+ function codyInferenceBase(args = {}) {
131
+ return codyGatewayUrl(args, "/cody/v1");
132
+ }
133
+
134
+ function manifestUrl(args = {}) {
135
+ return String(
136
+ args.manifest ||
137
+ process.env.MYTE_CODY_RELEASE_MANIFEST ||
138
+ `${gatewayRoot(args)}/cody/releases/manifest.json`,
139
+ );
140
+ }
141
+
142
+ function platformKey() {
143
+ return `${process.platform}-${process.arch}`;
144
+ }
145
+
146
+ function installRoot() {
147
+ if (process.env.MYTE_CODY_HOME) return path.resolve(process.env.MYTE_CODY_HOME);
148
+ if (process.platform === "win32") {
149
+ const base = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
150
+ return path.join(base, "Myte", "Cody");
151
+ }
152
+ return path.join(os.homedir(), ".myte", "cody");
153
+ }
154
+
155
+ function currentInstallRoot() {
156
+ return path.join(installRoot(), "current");
157
+ }
158
+
159
+ function currentClientManifestPath() {
160
+ return path.join(currentInstallRoot(), "manifest.json");
161
+ }
162
+
163
+ function currentEnginePath() {
164
+ const executable = process.platform === "win32" ? "mytecody-engine.exe" : "mytecody-engine";
165
+ return path.join(currentInstallRoot(), "bin", executable);
166
+ }
167
+
168
+ function currentBridgePath() {
169
+ return path.join(currentInstallRoot(), "lib", "mytecody-async-responses-bridge.js");
170
+ }
171
+
172
+ function currentControllerPath() {
173
+ return path.join(currentInstallRoot(), "lib", "mytecody-controller.js");
174
+ }
175
+
176
+ function codexHome() {
177
+ return path.join(installRoot(), "engine-home");
178
+ }
179
+
180
+ function codexModelCatalogPath() {
181
+ return path.join(codexHome(), "mytecody-models.json");
182
+ }
183
+
184
+ function readCurrentClientManifest() {
185
+ const filePath = currentClientManifestPath();
186
+ if (!fs.existsSync(filePath)) return null;
187
+ try {
188
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
189
+ } catch {
190
+ return null;
191
+ }
192
+ }
193
+
194
+ function installedClientCommand() {
195
+ const enginePath = currentEnginePath();
196
+ if (!fs.existsSync(enginePath)) return null;
197
+ return { cmd: enginePath, args: [], source: "myte-installed-engine" };
198
+ }
199
+
200
+ function installedClientUsable() {
201
+ return Boolean(
202
+ installedClientCommand() &&
203
+ fs.existsSync(currentBridgePath()) &&
204
+ fs.existsSync(currentControllerPath()) &&
205
+ readCurrentClientManifest(),
206
+ );
207
+ }
208
+
209
+ function loadSignedBridge() {
210
+ const bridgePath = currentBridgePath();
211
+ if (!fs.existsSync(bridgePath)) {
212
+ throw new Error("signed MyteCody inference bridge asset is missing; run `mytecody update`.");
213
+ }
214
+ const bridge = require(bridgePath);
215
+ if (!bridge || typeof bridge.startMyteCodyAsyncResponsesBridge !== "function") {
216
+ throw new Error("signed MyteCody inference bridge asset is invalid.");
217
+ }
218
+ return bridge;
219
+ }
220
+
221
+ function loadSignedController() {
222
+ const controllerPath = currentControllerPath();
223
+ if (!fs.existsSync(controllerPath)) {
224
+ throw new Error("signed MyteCody controller asset is missing; run `mytecody update`.");
225
+ }
226
+ const controller = require(controllerPath);
227
+ if (!controller || typeof controller.runMyteCodyController !== "function") {
228
+ throw new Error("signed MyteCody controller asset is invalid.");
229
+ }
230
+ return controller;
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
+
282
+ function releaseAssetsForPlatform(manifest, artifact) {
283
+ const assets = [];
284
+ const collect = (value, source) => {
285
+ if (!value) return;
286
+ if (Array.isArray(value)) {
287
+ value.forEach((item, index) => {
288
+ if (item && typeof item === "object") {
289
+ assets.push({ ...item, source, index });
290
+ }
291
+ });
292
+ return;
293
+ }
294
+ if (typeof value === "object") {
295
+ Object.entries(value).forEach(([name, item], index) => {
296
+ if (item && typeof item === "object") {
297
+ assets.push({ name: item.name || name, ...item, source, index });
298
+ }
299
+ });
300
+ }
301
+ };
302
+
303
+ collect(manifest && manifest.client_assets, "manifest.client_assets");
304
+ collect(artifact && artifact.assets, "artifact.assets");
305
+
306
+ return assets
307
+ .map((asset) => ({
308
+ ...asset,
309
+ install_path: asset.install_path || asset.path || "",
310
+ }))
311
+ .filter((asset) => asset && asset.url && asset.sha256 && asset.install_path);
312
+ }
313
+
314
+ function assertSafeReleaseAssetInstallPath(installPath) {
315
+ const raw = String(installPath || "").replace(/\\/g, "/").trim();
316
+ if (!raw) throw new Error("release asset install_path is required");
317
+ if (path.isAbsolute(raw) || /^[A-Za-z]:/.test(raw)) {
318
+ throw new Error(`release asset install_path must be relative: ${installPath}`);
319
+ }
320
+ const parts = raw.split("/").filter(Boolean);
321
+ if (!parts.length || parts.includes("..")) {
322
+ throw new Error(`release asset install_path is unsafe: ${installPath}`);
323
+ }
324
+ return path.join(currentInstallRoot(), ...parts);
325
+ }
326
+
327
+ function currentReleaseAssetRecordByPath(current) {
328
+ const map = new Map();
329
+ for (const asset of Array.isArray(current && current.assets) ? current.assets : []) {
330
+ if (asset && asset.install_path) {
331
+ map.set(String(asset.install_path).replace(/\\/g, "/"), asset);
332
+ }
333
+ }
334
+ return map;
335
+ }
336
+
337
+ function installedReleaseAssetsMatchManifest(manifest, artifact) {
338
+ const assets = releaseAssetsForPlatform(manifest, artifact);
339
+ if (!assets.length) return true;
340
+ const current = readCurrentClientManifest();
341
+ const currentAssets = currentReleaseAssetRecordByPath(current);
342
+
343
+ for (const asset of assets) {
344
+ const installPath = String(asset.install_path || "").replace(/\\/g, "/");
345
+ const currentAsset = currentAssets.get(installPath);
346
+ if (!currentAsset) return false;
347
+ if (String(currentAsset.sha256 || "").toLowerCase() !== String(asset.sha256 || "").toLowerCase()) {
348
+ return false;
349
+ }
350
+ const targetPath = assertSafeReleaseAssetInstallPath(asset.install_path);
351
+ if (!fs.existsSync(targetPath)) return false;
352
+ const installedSha = sha256Hex(fs.readFileSync(targetPath));
353
+ if (installedSha.toLowerCase() !== String(asset.installed_sha256 || asset.sha256).toLowerCase()) {
354
+ return false;
355
+ }
356
+ }
357
+ return true;
358
+ }
359
+
360
+ function installedClientMatchesManifest(manifest, artifact) {
361
+ const current = readCurrentClientManifest();
362
+ if (!current || !installedClientCommand()) return false;
363
+ if (manifest && manifest.version && current.version !== manifest.version) return false;
364
+
365
+ const currentArtifact = current.artifact || {};
366
+ if (artifact && artifact.sha256) {
367
+ if (String(currentArtifact.sha256 || "").toLowerCase() !== String(artifact.sha256 || "").toLowerCase()) {
368
+ return false;
369
+ }
370
+ }
371
+
372
+ const expectedInstalledSha =
373
+ artifact && (artifact.installed_sha256 || artifact.executable_sha256 || artifact.uncompressed_sha256);
374
+ if (expectedInstalledSha) {
375
+ if (String(currentArtifact.installed_sha256 || "").toLowerCase() !== String(expectedInstalledSha).toLowerCase()) {
376
+ return false;
377
+ }
378
+ }
379
+
380
+ if (!installedReleaseAssetsMatchManifest(manifest, artifact)) return false;
381
+
382
+ return true;
383
+ }
384
+
385
+ function printHelp() {
386
+ console.log(`MYTE CODY - Your Tech Your Way
387
+
388
+ Usage:
389
+ mytecody
390
+ mytecody [prompt...]
391
+ mytecody doctor [--json] [--base-url <url>] [--probe-gateway]
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...]
396
+ mytecody update --dry-run [--json] [--manifest <url-or-file>] [--fetch-manifest]
397
+ mytecody update [--json] [--manifest <url-or-file>]
398
+ mytecody help
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
+
407
+ Updates:
408
+ mytecody update updates the signed MyteCody engine only
409
+ npm install -g myte@latest updates this npm launcher and Myte API tools
410
+
411
+ Network:
412
+ The distributed MyteCody client uses the Myte AI gateway for coding
413
+ inference. It can inspect local config without network, but coding requires
414
+ gateway access and a Myte AI key.
415
+
416
+ Environment:
417
+ MYTEAI_API_KEY Myte AI inference key
418
+ MYTE_CODY_API_BASE Myte AI gateway base URL
419
+ MYTE_CODY_HOME local MyteCody client cache/install directory
420
+ `);
421
+ }
422
+
423
+ function commonStatus(args, envPath) {
424
+ const keyInfo = getKeyInfo();
425
+ const current = readCurrentClientManifest();
426
+ return {
427
+ product: "MyteCody",
428
+ command: "mytecody",
429
+ package_version: PACKAGE_VERSION,
430
+ package: packageStatusBase(args),
431
+ mode: "team-gateway",
432
+ workspace: process.cwd(),
433
+ env_file: envPath,
434
+ auth: keyInfo,
435
+ gateway: {
436
+ base_url: gatewayBase(args),
437
+ inference_base_url: codyInferenceBase(args),
438
+ responses_transport: "async-job-bridge",
439
+ network_required_for_coding: true,
440
+ },
441
+ instruction_pack: {
442
+ owner: "myte-cody-gateway",
443
+ client_embedded: false,
444
+ },
445
+ runtime: {
446
+ context_window: DEFAULT_CONTEXT_WINDOW,
447
+ auto_compact_tokens: DEFAULT_AUTO_COMPACT_TOKENS,
448
+ tool_output_tokens: DEFAULT_TOOL_OUTPUT_TOKENS,
449
+ max_concurrent_agent_threads: DEFAULT_AGENT_THREADS,
450
+ },
451
+ release: {
452
+ channel: String(args.channel || process.env.MYTE_CODY_RELEASE_CHANNEL || DEFAULT_CHANNEL),
453
+ manifest_url: manifestUrl(args),
454
+ platform: platformKey(),
455
+ install_root: installRoot(),
456
+ engine_path: currentEnginePath(),
457
+ bridge_path: currentBridgePath(),
458
+ controller_path: currentControllerPath(),
459
+ client_manifest: currentClientManifestPath(),
460
+ client_installed: Boolean(current && installedClientCommand()),
461
+ bridge_installed: fs.existsSync(currentBridgePath()),
462
+ controller_installed: fs.existsSync(currentControllerPath()),
463
+ client_version: current && current.version ? current.version : null,
464
+ },
465
+ };
466
+ }
467
+
468
+ function packageLatestUrl(args = {}) {
469
+ return String(
470
+ args["package-latest-url"] ||
471
+ process.env.MYTE_CODY_PACKAGE_LATEST_URL ||
472
+ process.env.MYTE_PACKAGE_LATEST_URL ||
473
+ DEFAULT_PACKAGE_LATEST_URL,
474
+ );
475
+ }
476
+
477
+ function packageUpdateCheckEnabled(args = {}) {
478
+ if (args["package-update-check"] === false) return false;
479
+ return process.env.MYTE_CODY_PACKAGE_UPDATE_CHECK !== "0";
480
+ }
481
+
482
+ function packageStatusBase(args = {}) {
483
+ return {
484
+ name: PACKAGE_NAME,
485
+ installed_version: PACKAGE_VERSION,
486
+ latest_version: null,
487
+ update_available: null,
488
+ check_status: "not-checked",
489
+ latest_url: packageLatestUrl(args),
490
+ update_command: `npm install -g ${PACKAGE_NAME}@latest`,
491
+ engine_update_command: "mytecody update",
492
+ };
493
+ }
494
+
495
+ function parseSemverish(version) {
496
+ const main = String(version || "")
497
+ .trim()
498
+ .replace(/^v/i, "")
499
+ .split(/[+-]/)[0];
500
+ const match = main.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
501
+ if (!match) return null;
502
+ return [match[1], match[2] || "0", match[3] || "0"].map((part) => Number(part));
503
+ }
504
+
505
+ function compareSemverish(left, right) {
506
+ const leftParts = parseSemverish(left);
507
+ const rightParts = parseSemverish(right);
508
+ if (!leftParts || !rightParts) return String(left || "").localeCompare(String(right || ""));
509
+ for (let i = 0; i < 3; i += 1) {
510
+ if (leftParts[i] > rightParts[i]) return 1;
511
+ if (leftParts[i] < rightParts[i]) return -1;
512
+ }
513
+ return 0;
514
+ }
515
+
516
+ function isVersionNewer(latest, installed = PACKAGE_VERSION) {
517
+ return compareSemverish(latest, installed) > 0;
518
+ }
519
+
520
+ function packageUpdateTimeoutMs() {
521
+ const value = Number(process.env.MYTE_CODY_PACKAGE_UPDATE_TIMEOUT_MS || 1500);
522
+ return Number.isFinite(value) && value > 0 ? value : 1500;
523
+ }
524
+
525
+ async function checkPackageUpdate(args = {}) {
526
+ const status = packageStatusBase(args);
527
+ if (!packageUpdateCheckEnabled(args)) {
528
+ return { ...status, check_status: "skipped" };
529
+ }
530
+ try {
531
+ const response = await fetchJson(status.latest_url, {
532
+ timeoutMs: packageUpdateTimeoutMs(),
533
+ });
534
+ if (!response.ok) {
535
+ return {
536
+ ...status,
537
+ check_status: "unavailable",
538
+ registry_status: response.status,
539
+ };
540
+ }
541
+ const latestVersion = response.body && response.body.version ? String(response.body.version) : "";
542
+ if (!latestVersion) {
543
+ return { ...status, check_status: "invalid-response" };
544
+ }
545
+ return {
546
+ ...status,
547
+ latest_version: latestVersion,
548
+ update_available: isVersionNewer(latestVersion, PACKAGE_VERSION),
549
+ check_status: "ok",
550
+ };
551
+ } catch (error) {
552
+ return {
553
+ ...status,
554
+ check_status: "unavailable",
555
+ error: error && error.message ? error.message : String(error),
556
+ };
557
+ }
558
+ }
559
+
560
+ function packageUpdateNotice(packageStatus) {
561
+ if (!packageStatus || packageStatus.update_available !== true) return "";
562
+ return `myte package update available: ${packageStatus.latest_version} (installed ${packageStatus.installed_version}). Run ${packageStatus.update_command}`;
563
+ }
564
+
565
+ function printJson(payload) {
566
+ console.log(JSON.stringify(payload, null, 2));
567
+ }
568
+
569
+ function isUrl(value) {
570
+ return /^https?:\/\//i.test(String(value || ""));
571
+ }
572
+
573
+ function statusLine(message) {
574
+ if (process.env.MYTE_CODY_QUIET_SETUP === "1") return;
575
+ console.error(`[MYTE CODY] ${message}`);
576
+ }
577
+
578
+ function setupProgress(splash) {
579
+ return (message) => {
580
+ if (splash && splash.enabled) {
581
+ splash.setStatus(message);
582
+ return;
583
+ }
584
+ statusLine(message);
585
+ };
586
+ }
587
+
588
+ function formatBytes(bytes) {
589
+ const value = Number(bytes || 0);
590
+ if (!Number.isFinite(value) || value <= 0) return "unknown size";
591
+ const units = ["B", "KB", "MB", "GB"];
592
+ let size = value;
593
+ let unit = 0;
594
+ while (size >= 1024 && unit < units.length - 1) {
595
+ size /= 1024;
596
+ unit += 1;
597
+ }
598
+ return `${size.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
599
+ }
600
+
601
+ async function readManifest(source, { fetchManifest, progress } = {}) {
602
+ if (!source) return { status: "missing", manifest: null };
603
+ if (isUrl(source)) {
604
+ if (!fetchManifest) {
605
+ return { status: "skipped", manifest: null };
606
+ }
607
+ if (progress) progress("checking signed release manifest");
608
+ const response = await fetch(source);
609
+ const text = await response.text();
610
+ if (!response.ok) {
611
+ throw new Error(`Manifest fetch failed (${response.status}): ${text.slice(0, 300)}`);
612
+ }
613
+ return { status: "fetched", manifest: JSON.parse(text) };
614
+ }
615
+ const filePath = path.resolve(source);
616
+ const text = fs.readFileSync(filePath, "utf8");
617
+ return { status: "read", manifest: JSON.parse(text), file_path: filePath };
618
+ }
619
+
620
+ function stableJson(value) {
621
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
622
+ if (value && typeof value === "object") {
623
+ return `{${Object.keys(value)
624
+ .sort()
625
+ .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
626
+ .join(",")}}`;
627
+ }
628
+ return JSON.stringify(value);
629
+ }
630
+
631
+ function manifestWithoutSignature(manifest) {
632
+ const clone = JSON.parse(JSON.stringify(manifest || {}));
633
+ delete clone.signature;
634
+ return clone;
635
+ }
636
+
637
+ function verifyManifestSignature(manifest) {
638
+ const signature = manifest && manifest.signature;
639
+ if (!signature || !signature.value) {
640
+ return { status: "missing", verified: false };
641
+ }
642
+ const publicKey = builtInPublicKeyPem();
643
+ if (!publicKey) {
644
+ return {
645
+ status: "public-key-missing",
646
+ verified: false,
647
+ key_id: signature.key_id || null,
648
+ };
649
+ }
650
+ const verifier = crypto.createVerify("SHA256");
651
+ verifier.update(stableJson(manifestWithoutSignature(manifest)));
652
+ verifier.end();
653
+ return {
654
+ status: "checked",
655
+ verified: verifier.verify(publicKey, String(signature.value), "base64"),
656
+ key_id: signature.key_id || null,
657
+ };
658
+ }
659
+
660
+ function builtInPublicKeyPem() {
661
+ const publicKeyPath = path.join(__dirname, "lib", "mytecody-release-public-key.pem");
662
+ if (!fs.existsSync(publicKeyPath)) return "";
663
+ return fs.readFileSync(publicKeyPath, "utf8");
664
+ }
665
+
666
+ function artifactForPlatform(manifest) {
667
+ const artifacts = manifest && manifest.artifacts;
668
+ const platform = platformKey();
669
+ if (Array.isArray(artifacts)) {
670
+ return (
671
+ artifacts.find((artifact) => {
672
+ if (!artifact || typeof artifact !== "object") return false;
673
+ const combined = `${artifact.platform || process.platform}-${artifact.arch || process.arch}`;
674
+ return artifact.platform_key === platform || combined === platform;
675
+ }) || null
676
+ );
677
+ }
678
+ if (!artifacts || typeof artifacts !== "object") return null;
679
+ return artifacts[platform] || null;
680
+ }
681
+
682
+ function validateArtifactMetadata(artifact) {
683
+ if (!artifact) return { status: "missing", ok: false };
684
+ const missing = [];
685
+ if (!artifact.url) missing.push("url");
686
+ if (!artifact.sha256) missing.push("sha256");
687
+ return {
688
+ status: missing.length ? "invalid" : "present",
689
+ ok: missing.length === 0,
690
+ missing,
691
+ url: artifact.url || null,
692
+ format: artifact.format || null,
693
+ sha256_present: Boolean(artifact.sha256),
694
+ };
695
+ }
696
+
697
+ function localPathFromArtifactUrl(urlValue) {
698
+ const raw = String(urlValue || "").trim();
699
+ if (/^file:\/\//i.test(raw)) {
700
+ const url = new URL(raw);
701
+ return decodeURIComponent(url.pathname.replace(/^\/([A-Za-z]:)/, "$1"));
702
+ }
703
+ if (!isUrl(raw)) return path.resolve(raw);
704
+ return null;
705
+ }
706
+
707
+ async function readArtifactBytes(artifact, { progress, label = "MyteCody engine" } = {}) {
708
+ const urlValue = artifact && artifact.url ? String(artifact.url) : "";
709
+ const localPath = localPathFromArtifactUrl(urlValue);
710
+ if (localPath) {
711
+ if (progress) progress(`reading local ${label} artifact`);
712
+ return fs.readFileSync(localPath);
713
+ }
714
+ const headers = {};
715
+ const token = getAuthToken();
716
+ if (token) headers.Authorization = `Bearer ${token}`;
717
+ if (progress) {
718
+ const expectedSize = Number(artifact && artifact.size_bytes ? artifact.size_bytes : 0);
719
+ progress(`downloading ${label} (${formatBytes(expectedSize)})`);
720
+ }
721
+ const response = await fetch(urlValue, { method: "GET", headers });
722
+ if (!response.ok) {
723
+ const bytes = Buffer.from(await response.arrayBuffer());
724
+ throw new Error(`Artifact fetch failed (${response.status}): ${bytes.toString("utf8", 0, Math.min(bytes.length, 300))}`);
725
+ }
726
+ if (!response.body || typeof response.body.getReader !== "function") {
727
+ const bytes = Buffer.from(await response.arrayBuffer());
728
+ if (progress) progress(`downloaded ${label} (${formatBytes(bytes.length)})`);
729
+ return bytes;
730
+ }
731
+
732
+ const total = Number(response.headers.get("content-length") || artifact?.size_bytes || 0);
733
+ const reader = response.body.getReader();
734
+ const chunks = [];
735
+ let received = 0;
736
+ let lastPct = -1;
737
+ while (true) {
738
+ const { done, value } = await reader.read();
739
+ if (done) break;
740
+ const chunk = Buffer.from(value);
741
+ chunks.push(chunk);
742
+ received += chunk.length;
743
+ if (progress && total > 0) {
744
+ const pct = Math.min(100, Math.floor((received / total) * 100));
745
+ if (pct >= lastPct + 10 || pct === 100) {
746
+ progress(`downloading ${label} ${pct}% (${formatBytes(received)} / ${formatBytes(total)})`);
747
+ lastPct = pct;
748
+ }
749
+ }
750
+ }
751
+ const bytes = Buffer.concat(chunks);
752
+ if (progress) progress(`downloaded ${label} (${formatBytes(bytes.length)})`);
753
+ return bytes;
754
+ }
755
+
756
+ function sha256Hex(bytes) {
757
+ return crypto.createHash("sha256").update(bytes).digest("hex");
758
+ }
759
+
760
+ function artifactFormat(artifact) {
761
+ const explicit = String((artifact && artifact.format) || "").trim().toLowerCase();
762
+ if (explicit) return explicit;
763
+ const urlValue = String((artifact && artifact.url) || "").trim().toLowerCase();
764
+ if (urlValue.endsWith(".gz")) return "gzip";
765
+ return "exe";
766
+ }
767
+
768
+ function artifactBytesForInstall(bytes, artifact) {
769
+ const format = artifactFormat(artifact);
770
+ if (format === "exe" || format === "binary" || format === "raw") return bytes;
771
+ if (format === "gzip" || format === "gz") return zlib.gunzipSync(bytes);
772
+ throw new Error(`Unsupported MyteCody release artifact format: ${format}`);
773
+ }
774
+
775
+ function signatureAccepted(manifest, args = {}) {
776
+ const signature = verifyManifestSignature(manifest);
777
+ if (signature.verified) return { ok: true, signature, trusted_unsigned: false };
778
+ return { ok: false, signature, trusted_unsigned: false };
779
+ }
780
+
781
+ function installArtifactBytes(bytes, manifest, artifact) {
782
+ const installBytes = artifactBytesForInstall(bytes, artifact);
783
+ const enginePath = currentEnginePath();
784
+ fs.rmSync(path.dirname(path.dirname(enginePath)), { recursive: true, force: true });
785
+ fs.mkdirSync(path.dirname(enginePath), { recursive: true });
786
+ fs.writeFileSync(enginePath, installBytes);
787
+ if (process.platform !== "win32") {
788
+ fs.chmodSync(enginePath, 0o755);
789
+ }
790
+ const installedManifest = {
791
+ schema_version: manifest.schema_version || 1,
792
+ channel: manifest.channel || DEFAULT_CHANNEL,
793
+ version: manifest.version || "unknown",
794
+ installed_at: new Date().toISOString(),
795
+ launcher_version: PACKAGE_VERSION,
796
+ platform: platformKey(),
797
+ executable: enginePath,
798
+ artifact: {
799
+ url: artifact.url,
800
+ sha256: artifact.sha256,
801
+ format: artifactFormat(artifact),
802
+ size_bytes: bytes.length,
803
+ installed_sha256: sha256Hex(installBytes),
804
+ installed_size_bytes: installBytes.length,
805
+ },
806
+ };
807
+ fs.writeFileSync(currentClientManifestPath(), JSON.stringify(installedManifest, null, 2), "utf8");
808
+ return installedManifest;
809
+ }
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
+
868
+ async function installReleaseAssets(manifest, artifact, { progress } = {}) {
869
+ const assets = releaseAssetsForPlatform(manifest, artifact);
870
+ const installed = [];
871
+ for (const asset of assets) {
872
+ const name = String(asset.name || path.basename(String(asset.install_path || "")) || "client asset");
873
+ const bytes = await readArtifactBytes(asset, { progress, label: `MyteCody ${name}` });
874
+ const digest = sha256Hex(bytes);
875
+ if (digest.toLowerCase() !== String(asset.sha256 || "").toLowerCase()) {
876
+ throw new Error(`Release asset SHA-256 mismatch for ${name}: expected ${asset.sha256}, got ${digest}`);
877
+ }
878
+ const installBytes = artifactBytesForInstall(bytes, asset);
879
+ const targetPath = assertSafeReleaseAssetInstallPath(asset.install_path);
880
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
881
+ fs.writeFileSync(targetPath, installBytes);
882
+ installed.push({
883
+ name,
884
+ install_path: String(asset.install_path || "").replace(/\\/g, "/"),
885
+ url: asset.url,
886
+ sha256: asset.sha256,
887
+ format: artifactFormat(asset),
888
+ size_bytes: bytes.length,
889
+ installed_sha256: sha256Hex(installBytes),
890
+ installed_size_bytes: installBytes.length,
891
+ });
892
+ }
893
+ return installed;
894
+ }
895
+
896
+ async function fetchJson(url, { headers = {}, timeoutMs = 8000 } = {}) {
897
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : undefined;
898
+ const timeoutId =
899
+ controller && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
900
+ try {
901
+ const response = await fetch(url, {
902
+ method: "GET",
903
+ headers: {
904
+ Accept: "application/json",
905
+ ...headers,
906
+ },
907
+ signal: controller?.signal,
908
+ });
909
+ const text = await response.text();
910
+ let body = {};
911
+ if (text.trim()) {
912
+ try {
913
+ body = JSON.parse(text);
914
+ } catch {
915
+ body = { raw_text: text.slice(0, 500) };
916
+ }
917
+ }
918
+ return {
919
+ ok: Boolean(response.ok),
920
+ status: response.status,
921
+ body,
922
+ };
923
+ } finally {
924
+ if (timeoutId) clearTimeout(timeoutId);
925
+ }
926
+ }
927
+
928
+ async function probeGateway(args = {}) {
929
+ const token = getAuthToken();
930
+ const healthUrl = codyGatewayUrl(args, "/cody/health");
931
+ const modelsUrl = codyGatewayUrl(args, "/cody/v1/models");
932
+ const result = {
933
+ ok: false,
934
+ health: {
935
+ url: healthUrl,
936
+ ok: false,
937
+ status: null,
938
+ },
939
+ models: {
940
+ url: modelsUrl,
941
+ ok: false,
942
+ status: null,
943
+ skipped: !token,
944
+ },
945
+ };
946
+ try {
947
+ const health = await fetchJson(healthUrl);
948
+ result.health.ok = Boolean(health.ok && health.body && health.body.ok === true);
949
+ result.health.status = health.status;
950
+ result.health.service = health.body && health.body.service ? String(health.body.service) : null;
951
+ result.health.model = health.body && health.body.model ? health.body.model : null;
952
+ } catch (error) {
953
+ result.health.error = error && error.message ? error.message : String(error);
954
+ }
955
+
956
+ if (token) {
957
+ try {
958
+ const models = await fetchJson(modelsUrl, {
959
+ headers: { Authorization: `Bearer ${token}` },
960
+ });
961
+ const ids = Array.isArray(models.body?.data)
962
+ ? models.body.data.map((item) => String(item && item.id ? item.id : "")).filter(Boolean)
963
+ : [];
964
+ result.models.ok = Boolean(models.ok && ids.includes("myte"));
965
+ result.models.status = models.status;
966
+ result.models.ids = ids;
967
+ result.models.skipped = false;
968
+ } catch (error) {
969
+ result.models.error = error && error.message ? error.message : String(error);
970
+ result.models.skipped = false;
971
+ }
972
+ }
973
+
974
+ result.ok = Boolean(result.health.ok && result.models.ok);
975
+ return result;
976
+ }
977
+
978
+ function tomlString(value) {
979
+ return JSON.stringify(String(value || ""));
980
+ }
981
+
982
+ function tomlLiteralString(value) {
983
+ const text = String(value || "");
984
+ if (!text.includes("'") && !text.includes("\n") && !text.includes("\r")) {
985
+ return `'${text}'`;
986
+ }
987
+ return tomlString(text);
988
+ }
989
+
990
+ function pathForToml(value) {
991
+ return String(value || "");
992
+ }
993
+
994
+ function writeCodexModelCatalog() {
995
+ fs.mkdirSync(codexHome(), { recursive: true });
996
+ const catalog = {
997
+ models: [
998
+ {
999
+ slug: DEFAULT_MODEL_ALIAS,
1000
+ display_name: "Myte",
1001
+ description: "Myte AI coding model.",
1002
+ base_instructions: CLIENT_BASE_INSTRUCTIONS,
1003
+ default_reasoning_level: "medium",
1004
+ supported_reasoning_levels: [
1005
+ { effort: "low", description: "Fast local coding pass." },
1006
+ { effort: "medium", description: "Balanced coding pass." },
1007
+ { effort: "high", description: "Deeper coding pass." },
1008
+ ],
1009
+ shell_type: "shell_command",
1010
+ visibility: "list",
1011
+ supported_in_api: true,
1012
+ priority: 0,
1013
+ availability_nux: null,
1014
+ upgrade: null,
1015
+ supports_reasoning_summaries: false,
1016
+ default_reasoning_summary: "none",
1017
+ support_verbosity: false,
1018
+ default_verbosity: null,
1019
+ apply_patch_tool_type: "freeform",
1020
+ web_search_tool_type: "text",
1021
+ truncation_policy: {
1022
+ mode: "tokens",
1023
+ limit: 10000,
1024
+ },
1025
+ supports_parallel_tool_calls: true,
1026
+ supports_image_detail_original: false,
1027
+ context_window: DEFAULT_CONTEXT_WINDOW,
1028
+ max_context_window: DEFAULT_CONTEXT_WINDOW,
1029
+ auto_compact_token_limit: DEFAULT_AUTO_COMPACT_TOKENS,
1030
+ effective_context_window_percent: 90,
1031
+ experimental_supported_tools: [],
1032
+ input_modalities: ["text"],
1033
+ supports_search_tool: false,
1034
+ use_responses_lite: false,
1035
+ auto_review_model_override: null,
1036
+ },
1037
+ ],
1038
+ };
1039
+ const catalogPath = codexModelCatalogPath();
1040
+ fs.writeFileSync(catalogPath, JSON.stringify(catalog, null, 2), "utf8");
1041
+ return catalogPath;
1042
+ }
1043
+
1044
+ function writeCodexConfig(args = {}, providerBaseUrl = codyInferenceBase(args)) {
1045
+ fs.mkdirSync(codexHome(), { recursive: true });
1046
+ const catalogPath = writeCodexModelCatalog();
1047
+ const config = `model = ${tomlString(DEFAULT_MODEL_ALIAS)}
1048
+ model_provider = "myte_ai"
1049
+ model_catalog_json = ${tomlString(catalogPath)}
1050
+ model_context_window = ${DEFAULT_CONTEXT_WINDOW}
1051
+ model_auto_compact_token_limit = ${DEFAULT_AUTO_COMPACT_TOKENS}
1052
+ tool_output_token_limit = ${DEFAULT_TOOL_OUTPUT_TOKENS}
1053
+ web_search = "disabled"
1054
+ suppress_unstable_features_warning = true
1055
+ check_for_update_on_startup = false
1056
+
1057
+ [tui]
1058
+ show_tooltips = false
1059
+ status_line = ["run-state", "current-dir"]
1060
+ status_line_use_colors = true
1061
+ terminal_title = ["project"]
1062
+
1063
+ [model_providers.myte_ai]
1064
+ name = "Myte AI"
1065
+ base_url = ${tomlString(providerBaseUrl)}
1066
+ env_key = "MYTE_CODY_AUTH_TOKEN"
1067
+ wire_api = "responses"
1068
+ requires_openai_auth = false
1069
+
1070
+ [features]
1071
+ apps = false
1072
+ multi_agent = false
1073
+ hooks = false
1074
+ memories = false
1075
+ plugins = false
1076
+ tool_suggest = false
1077
+
1078
+ [features.multi_agent_v2]
1079
+ enabled = true
1080
+ max_concurrent_threads_per_session = ${DEFAULT_AGENT_THREADS}
1081
+
1082
+ [skills]
1083
+ include_instructions = true
1084
+
1085
+ [skills.bundled]
1086
+ enabled = false
1087
+
1088
+ [projects.${tomlLiteralString(process.cwd())}]
1089
+ trust_level = "trusted"
1090
+
1091
+ [windows]
1092
+ sandbox = "unelevated"
1093
+ `;
1094
+ const configPath = path.join(codexHome(), "config.toml");
1095
+ fs.writeFileSync(configPath, config, "utf8");
1096
+ return { configPath, catalogPath };
1097
+ }
1098
+
1099
+ function resolveCodexCommand() {
1100
+ const installed = installedClientCommand();
1101
+ if (installed) return installed;
1102
+ return null;
1103
+ }
1104
+
1105
+ function codexProviderArgs(args = {}, providerBaseUrl = codyInferenceBase(args)) {
1106
+ return [
1107
+ "-c",
1108
+ 'model_provider="myte_ai"',
1109
+ "-c",
1110
+ 'model_providers.myte_ai.name="Myte AI"',
1111
+ "-c",
1112
+ `model_providers.myte_ai.base_url="${providerBaseUrl}"`,
1113
+ "-c",
1114
+ 'model_providers.myte_ai.env_key="MYTE_CODY_AUTH_TOKEN"',
1115
+ "-c",
1116
+ 'model_providers.myte_ai.wire_api="responses"',
1117
+ "-c",
1118
+ `model_catalog_json=${tomlString(codexModelCatalogPath())}`,
1119
+ "-c",
1120
+ `model_context_window=${DEFAULT_CONTEXT_WINDOW}`,
1121
+ "-c",
1122
+ `model_auto_compact_token_limit=${DEFAULT_AUTO_COMPACT_TOKENS}`,
1123
+ "-c",
1124
+ `tool_output_token_limit=${DEFAULT_TOOL_OUTPUT_TOKENS}`,
1125
+ "-c",
1126
+ "web_search=\"disabled\"",
1127
+ "-c",
1128
+ "suppress_unstable_features_warning=true",
1129
+ "-c",
1130
+ "features.multi_agent_v2.enabled=true",
1131
+ "-c",
1132
+ `features.multi_agent_v2.max_concurrent_threads_per_session=${DEFAULT_AGENT_THREADS}`,
1133
+ "--sandbox",
1134
+ "danger-full-access",
1135
+ "--ask-for-approval",
1136
+ "never",
1137
+ "-m",
1138
+ DEFAULT_MODEL_ALIAS,
1139
+ ];
1140
+ }
1141
+
1142
+ function codexLaunchArgs(rawArgs, args = {}, providerBaseUrl = codyInferenceBase(args)) {
1143
+ const providerArgs = codexProviderArgs(args, providerBaseUrl);
1144
+ if (!rawArgs.length) return providerArgs;
1145
+ if (rawArgs[0] === "exec") return [...providerArgs, "exec", "--skip-git-repo-check", ...rawArgs.slice(1)];
1146
+ return [...providerArgs, ...rawArgs];
1147
+ }
1148
+
1149
+ function execArgsAndStdin(rawArgs) {
1150
+ if (!rawArgs.length) return { args: [], stdin: null };
1151
+ const valueOptions = new Set([
1152
+ "-a",
1153
+ "--ask-for-approval",
1154
+ "-C",
1155
+ "--cd",
1156
+ "-c",
1157
+ "--config",
1158
+ "-i",
1159
+ "--image",
1160
+ "-m",
1161
+ "--model",
1162
+ "-o",
1163
+ "--output-last-message",
1164
+ "-p",
1165
+ "--profile",
1166
+ "-s",
1167
+ "--sandbox",
1168
+ "--color",
1169
+ "--local-provider",
1170
+ "--output-schema",
1171
+ ]);
1172
+ const forwarded = [];
1173
+ let index = 0;
1174
+ while (index < rawArgs.length) {
1175
+ const arg = rawArgs[index];
1176
+ if (arg === "--") {
1177
+ index += 1;
1178
+ break;
1179
+ }
1180
+ if (arg === "-") break;
1181
+ if (!arg.startsWith("-")) break;
1182
+ forwarded.push(arg);
1183
+ index += 1;
1184
+ if (valueOptions.has(arg) && index < rawArgs.length) {
1185
+ forwarded.push(rawArgs[index]);
1186
+ index += 1;
1187
+ }
1188
+ }
1189
+ const promptParts = rawArgs.slice(index);
1190
+ if (!promptParts.length) return { args: forwarded, stdin: null };
1191
+ if (promptParts.length === 1 && promptParts[0] === "-") {
1192
+ return { args: [...forwarded, "-"], stdin: fs.readFileSync(0, "utf8") };
1193
+ }
1194
+ const prompt = promptParts.join(" ");
1195
+ if (prompt.includes("\n") || prompt.includes("\r") || promptParts.length > 1) {
1196
+ return { args: [...forwarded, "-"], stdin: prompt };
1197
+ }
1198
+ return { args: [...forwarded, prompt], stdin: null };
1199
+ }
1200
+
1201
+ function stripControllerArgs(rawArgs) {
1202
+ const kept = [];
1203
+ let enabled = process.env.MYTE_CODY_CONTROLLER === "1";
1204
+ for (let i = 0; i < rawArgs.length; i += 1) {
1205
+ const arg = rawArgs[i];
1206
+ if (arg === "--controller") {
1207
+ const next = rawArgs[i + 1];
1208
+ const normalizedNext = String(next || "").toLowerCase();
1209
+ if (next !== undefined && !next.startsWith("-") && ["0", "1", "false", "true", "off", "on", "raw"].includes(normalizedNext)) {
1210
+ enabled = !["0", "false", "off", "raw"].includes(String(next).toLowerCase());
1211
+ i += 1;
1212
+ } else {
1213
+ enabled = true;
1214
+ }
1215
+ continue;
1216
+ }
1217
+ if (arg.startsWith("--controller=")) {
1218
+ const value = arg.slice("--controller=".length);
1219
+ enabled = !["0", "false", "off", "raw"].includes(String(value).toLowerCase());
1220
+ continue;
1221
+ }
1222
+ if (arg === "--no-controller") {
1223
+ enabled = false;
1224
+ continue;
1225
+ }
1226
+ kept.push(arg);
1227
+ }
1228
+ return { enabled, args: kept };
1229
+ }
1230
+
1231
+ function controllerPromptFromExecArgs(rawArgs) {
1232
+ const execInput = execArgsAndStdin(rawArgs);
1233
+ if (execInput.stdin != null) return { prompt: execInput.stdin, forwardedArgs: [] };
1234
+ const prompt = execInput.args.length ? execInput.args[execInput.args.length - 1] : "";
1235
+ if (!prompt || prompt.startsWith("-")) {
1236
+ return { prompt: "", forwardedArgs: execInput.args };
1237
+ }
1238
+ return { prompt, forwardedArgs: execInput.args.slice(0, -1) };
1239
+ }
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
+
1259
+ function runEngineExecWorker({ command, args, providerBaseUrl, token, prompt, timeoutMs }) {
1260
+ return new Promise((resolve) => {
1261
+ const started = Date.now();
1262
+ const env = {
1263
+ ...process.env,
1264
+ CODEX_HOME: codexHome(),
1265
+ MYTE_CODY_AUTH_TOKEN: token,
1266
+ MYTE_CODY_BRAND: "1",
1267
+ MYTE_CODY_CONTROLLER: "0",
1268
+ MYTE_CODY_BRIDGE_BASE_URL: providerBaseUrl,
1269
+ };
1270
+ const launchArgs = [...command.args, ...codexLaunchArgs(["exec", "--json", "-"], args, providerBaseUrl)];
1271
+ const child = spawn(command.cmd, launchArgs, {
1272
+ cwd: process.cwd(),
1273
+ env,
1274
+ stdio: ["pipe", "pipe", "pipe"],
1275
+ shell: process.platform === "win32" && command.cmd === "codex",
1276
+ });
1277
+ let stdout = "";
1278
+ let stderr = "";
1279
+ let settled = false;
1280
+ const timer = setTimeout(() => {
1281
+ if (settled) return;
1282
+ try {
1283
+ child.kill();
1284
+ } catch {}
1285
+ }, timeoutMs || 180000);
1286
+ child.stdout.on("data", (chunk) => {
1287
+ stdout += chunk.toString();
1288
+ });
1289
+ child.stderr.on("data", (chunk) => {
1290
+ stderr += chunk.toString();
1291
+ });
1292
+ child.on("error", (error) => {
1293
+ settled = true;
1294
+ clearTimeout(timer);
1295
+ resolve({
1296
+ status: 1,
1297
+ stdout,
1298
+ stderr,
1299
+ error: error.message || String(error),
1300
+ durationMs: Date.now() - started,
1301
+ });
1302
+ });
1303
+ child.on("exit", (code, signal) => {
1304
+ settled = true;
1305
+ clearTimeout(timer);
1306
+ resolve({
1307
+ status: code == null ? 1 : code,
1308
+ signal: signal || null,
1309
+ stdout,
1310
+ stderr,
1311
+ error: null,
1312
+ durationMs: Date.now() - started,
1313
+ });
1314
+ });
1315
+ child.stdin.end(prompt);
1316
+ });
1317
+ }
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
+
1555
+ async function runCodex(rawArgs, args = {}, envPath = null) {
1556
+ const token = getAuthToken();
1557
+ if (!token) {
1558
+ console.error("MyteCody requires MYTEAI_API_KEY for coding.");
1559
+ return 1;
1560
+ }
1561
+ const splash = createMyteSplash();
1562
+ const progress = setupProgress(splash);
1563
+ splash.start("preparing trusted workspace");
1564
+ progress("preparing trusted workspace");
1565
+ try {
1566
+ const install = await ensureBrandedEngineInstalled(args, envPath, { progress });
1567
+ if (install.ok && install.installed) {
1568
+ progress(`engine installed: ${install.payload.installed.version}`);
1569
+ } else if (install.ok) {
1570
+ progress("engine ready");
1571
+ } else if (!install.ok) {
1572
+ await splash.stop();
1573
+ console.error(`MyteCody branded engine could not be verified: ${install.reason || "unknown"}.`);
1574
+ console.error("Run `mytecody update` with access to the Myte release manifest.");
1575
+ return 1;
1576
+ }
1577
+ } catch (error) {
1578
+ await splash.stop();
1579
+ console.error(`MyteCody engine verification failed: ${error && error.message ? error.message : error}`);
1580
+ return 1;
1581
+ }
1582
+
1583
+ let packageNotice = "";
1584
+ try {
1585
+ progress("checking myte package version");
1586
+ packageNotice = packageUpdateNotice(await checkPackageUpdate(args));
1587
+ } catch {
1588
+ packageNotice = "";
1589
+ }
1590
+
1591
+ const command = resolveCodexCommand();
1592
+ if (!command) {
1593
+ await splash.stop();
1594
+ console.error("MyteCody branded engine is not installed.");
1595
+ console.error("Run `mytecody update` with access to the Myte release manifest.");
1596
+ return 1;
1597
+ }
1598
+ let bridge = null;
1599
+ try {
1600
+ progress("opening Myte inference bridge");
1601
+ const signedBridge = loadSignedBridge();
1602
+ bridge = await signedBridge.startMyteCodyAsyncResponsesBridge({
1603
+ gatewayRoot: gatewayRoot(args),
1604
+ token,
1605
+ });
1606
+ writeCodexConfig(args, bridge.baseUrl);
1607
+ } catch (error) {
1608
+ await splash.stop();
1609
+ console.error(`MyteCody inference bridge failed to start: ${error && error.message ? error.message : error}`);
1610
+ return 1;
1611
+ }
1612
+
1613
+ const invocation = classifyMyteCodyInvocation(rawArgs);
1614
+ if (["controller-exec", "controller-prompt", "controller-shell", "controller-resume"].includes(invocation.mode)) {
1615
+ try {
1616
+ progress("opening MyteCody controller");
1617
+ const signedController = loadSignedController();
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);
1673
+ if (!promptInfo.prompt.trim()) {
1674
+ await splash.stop();
1675
+ await bridge.close();
1676
+ console.error("MyteCody controller requires a prompt.");
1677
+ return 1;
1678
+ }
1679
+ const code = await runSignedControllerPrompt({
1680
+ signedController,
1681
+ command,
1682
+ args,
1683
+ bridge,
1684
+ token,
1685
+ promptInfo,
1686
+ });
1687
+ await bridge.close();
1688
+ return code;
1689
+ } catch (error) {
1690
+ await splash.stop();
1691
+ await bridge.close();
1692
+ console.error(`MyteCody controller failed: ${error && error.message ? error.message : error}`);
1693
+ return 1;
1694
+ }
1695
+ }
1696
+
1697
+ const launchArgs = [...command.args, ...codexLaunchArgs(invocation.args, args, bridge.baseUrl)];
1698
+ const env = {
1699
+ ...process.env,
1700
+ CODEX_HOME: codexHome(),
1701
+ MYTE_CODY_AUTH_TOKEN: token,
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(),
1708
+ MYTE_CODY_BRIDGE_BASE_URL: bridge.baseUrl,
1709
+ };
1710
+ progress("opening MyteCody workspace");
1711
+ await splash.stop();
1712
+ if (packageNotice) statusLine(packageNotice);
1713
+ return new Promise((resolve) => {
1714
+ const child = spawn(command.cmd, launchArgs, {
1715
+ cwd: process.cwd(),
1716
+ env,
1717
+ stdio: "inherit",
1718
+ shell: process.platform === "win32" && command.cmd === "codex",
1719
+ });
1720
+ child.on("error", (error) => {
1721
+ console.error(`Unable to launch MyteCody engine: ${error.message || error}`);
1722
+ bridge.close().finally(() => resolve(1));
1723
+ });
1724
+ child.on("close", (code) => {
1725
+ bridge.close().finally(() => resolve(Number.isInteger(code) ? code : 1));
1726
+ });
1727
+ });
1728
+ }
1729
+
1730
+ async function runDoctor(args, envPath) {
1731
+ const payload = {
1732
+ ok: true,
1733
+ ready_for_coding: false,
1734
+ ...commonStatus(args, envPath),
1735
+ };
1736
+ payload.package = await checkPackageUpdate(args);
1737
+ if (args["probe-gateway"]) {
1738
+ payload.gateway.probe = await probeGateway(args);
1739
+ }
1740
+ payload.ready_for_coding =
1741
+ payload.auth.present &&
1742
+ payload.release.client_installed &&
1743
+ payload.release.bridge_installed;
1744
+ if (payload.gateway.probe) {
1745
+ payload.ready_for_coding = Boolean(payload.ready_for_coding && payload.gateway.probe.ok);
1746
+ }
1747
+ if (args.json) {
1748
+ printJson(payload);
1749
+ return 0;
1750
+ }
1751
+ console.log("MYTE CODY - Your Tech Your Way");
1752
+ console.log("");
1753
+ console.log(`mode: ${payload.mode}`);
1754
+ console.log(`workspace: ${payload.workspace}`);
1755
+ console.log(`auth: ${payload.auth.present ? `present (${payload.auth.source})` : "missing"}`);
1756
+ console.log(`gateway: ${payload.gateway.base_url}`);
1757
+ if (payload.gateway.probe) {
1758
+ console.log(`gateway probe: ${payload.gateway.probe.ok ? "ok" : "failed"}`);
1759
+ }
1760
+ console.log(`package: ${payload.package.installed_version}`);
1761
+ if (payload.package.check_status === "ok" && payload.package.update_available) {
1762
+ console.log(`package update: ${payload.package.latest_version} available`);
1763
+ console.log(`package command: ${payload.package.update_command}`);
1764
+ } else if (payload.package.check_status === "ok") {
1765
+ console.log("package update: current");
1766
+ } else if (payload.package.check_status === "skipped") {
1767
+ console.log("package update: skipped");
1768
+ } else {
1769
+ console.log(`package update: ${payload.package.check_status}`);
1770
+ }
1771
+ console.log(`client: ${payload.release.client_installed ? payload.release.client_version : "not installed"}`);
1772
+ console.log(`bridge: ${payload.release.bridge_installed ? "installed" : "not installed"}`);
1773
+ console.log(`install: ${payload.release.install_root}`);
1774
+ console.log("");
1775
+ console.log("Coding requires the Myte AI gateway and a Myte AI key.");
1776
+ return 0;
1777
+ }
1778
+
1779
+ async function buildUpdatePayload(args, envPath, { dryRun = false, progress = null } = {}) {
1780
+ const isDryRun = Boolean(dryRun);
1781
+ const source = manifestUrl(args);
1782
+ const manifestResult = await readManifest(source, {
1783
+ fetchManifest: Boolean(args["fetch-manifest"]) || !isDryRun,
1784
+ progress,
1785
+ });
1786
+ const manifest = manifestResult.manifest;
1787
+ const artifact = manifest ? artifactForPlatform(manifest) : null;
1788
+ const signature = manifest ? signatureAccepted(manifest, args) : { ok: false, signature: { status: "not-checked", verified: false } };
1789
+ const artifactMetadata = manifest ? validateArtifactMetadata(artifact) : { status: "not-checked", ok: false };
1790
+ const releaseAssets = manifest ? releaseAssetsForPlatform(manifest, artifact) : [];
1791
+ const payload = {
1792
+ ok: true,
1793
+ dry_run: isDryRun,
1794
+ would_write: !isDryRun,
1795
+ ...commonStatus(args, envPath),
1796
+ manifest: {
1797
+ source,
1798
+ read_status: manifestResult.status,
1799
+ version: manifest && manifest.version ? manifest.version : null,
1800
+ signature: signature.signature,
1801
+ trusted_unsigned: Boolean(signature.trusted_unsigned),
1802
+ },
1803
+ artifact: artifactMetadata,
1804
+ release_assets: releaseAssets.map((asset) => ({
1805
+ name: asset.name || null,
1806
+ install_path: asset.install_path,
1807
+ url: asset.url || null,
1808
+ format: asset.format || null,
1809
+ sha256_present: Boolean(asset.sha256),
1810
+ })),
1811
+ };
1812
+
1813
+ if (!isDryRun) {
1814
+ if (!manifest) {
1815
+ throw new Error("Cannot install MyteCody engine without a readable release manifest.");
1816
+ }
1817
+ if (!signature.ok) {
1818
+ throw new Error("MyteCody release manifest signature is not trusted.");
1819
+ }
1820
+ if (!artifactMetadata.ok) {
1821
+ throw new Error(`MyteCody release artifact metadata is ${artifactMetadata.status}.`);
1822
+ }
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);
1839
+ }
1840
+ const installedAssets = await installReleaseAssets(manifest, artifact, { progress });
1841
+ if (installedAssets.length) {
1842
+ installed.assets = installedAssets;
1843
+ fs.writeFileSync(currentClientManifestPath(), JSON.stringify(installed, null, 2), "utf8");
1844
+ }
1845
+ payload.installed = {
1846
+ ok: true,
1847
+ version: installed.version,
1848
+ executable: installed.executable,
1849
+ sha256: artifactDigest,
1850
+ size_bytes: artifactSizeBytes,
1851
+ installed_sha256: installed.artifact.installed_sha256,
1852
+ installed_size_bytes: installed.artifact.installed_size_bytes,
1853
+ format: installed.artifact.format,
1854
+ assets: installedAssets,
1855
+ };
1856
+ payload.release = {
1857
+ ...payload.release,
1858
+ client_installed: true,
1859
+ client_version: installed.version,
1860
+ engine_path: installed.executable,
1861
+ };
1862
+ }
1863
+
1864
+ return payload;
1865
+ }
1866
+
1867
+ function autoInstallEnabled(args = {}) {
1868
+ if (args["auto-update"] === false) return false;
1869
+ if (process.env.MYTE_CODY_AUTO_UPDATE === "0") return false;
1870
+ return true;
1871
+ }
1872
+
1873
+ async function ensureBrandedEngineInstalled(args = {}, envPath = null, { progress = null } = {}) {
1874
+ const updateArgs = {
1875
+ ...args,
1876
+ "fetch-manifest": true,
1877
+ };
1878
+ delete updateArgs["dry-run"];
1879
+ delete updateArgs.json;
1880
+
1881
+ const source = manifestUrl(updateArgs);
1882
+ let manifestResult;
1883
+ try {
1884
+ manifestResult = await readManifest(source, { fetchManifest: true, progress });
1885
+ } catch (error) {
1886
+ if (isUrl(source) && installedClientUsable()) {
1887
+ return {
1888
+ ok: true,
1889
+ installed: false,
1890
+ reason: "cached-engine-manifest-unavailable",
1891
+ manifest_status: "fetch-failed",
1892
+ error: error && error.message ? error.message : String(error),
1893
+ };
1894
+ }
1895
+ throw error;
1896
+ }
1897
+ const manifest = manifestResult.manifest;
1898
+ if (!manifest) {
1899
+ if (isUrl(source) && installedClientUsable()) {
1900
+ return {
1901
+ ok: true,
1902
+ installed: false,
1903
+ reason: "cached-engine-manifest-unavailable",
1904
+ manifest_status: manifestResult.status,
1905
+ };
1906
+ }
1907
+ return { ok: false, installed: false, reason: "manifest-unavailable", manifest_status: manifestResult.status };
1908
+ }
1909
+ const signature = signatureAccepted(manifest, updateArgs);
1910
+ if (!signature.ok) {
1911
+ return { ok: false, installed: false, reason: "manifest-untrusted", signature: signature.signature };
1912
+ }
1913
+ const artifact = artifactForPlatform(manifest);
1914
+ const artifactMetadata = validateArtifactMetadata(artifact);
1915
+ if (!artifactMetadata.ok) {
1916
+ return { ok: false, installed: false, reason: "artifact-metadata-invalid", artifact: artifactMetadata };
1917
+ }
1918
+ if (installedClientMatchesManifest(manifest, artifact)) {
1919
+ return { ok: true, installed: false, reason: "already-current" };
1920
+ }
1921
+
1922
+ if (!autoInstallEnabled(args)) {
1923
+ return { ok: false, installed: false, reason: "update-required-auto-install-disabled" };
1924
+ }
1925
+
1926
+ const payload = await buildUpdatePayload(updateArgs, envPath, { dryRun: false, progress });
1927
+ return {
1928
+ ok: Boolean(payload.installed && payload.installed.ok),
1929
+ installed: Boolean(payload.installed && payload.installed.ok),
1930
+ reason: "installed",
1931
+ payload,
1932
+ };
1933
+ }
1934
+
1935
+ async function runUpdate(args, envPath) {
1936
+ const dryRun = Boolean(args["dry-run"]);
1937
+ const payload = await buildUpdatePayload(args, envPath, {
1938
+ dryRun,
1939
+ progress: args.json ? null : statusLine,
1940
+ });
1941
+
1942
+ if (args.json) {
1943
+ printJson(payload);
1944
+ return 0;
1945
+ }
1946
+ console.log(dryRun ? "MYTE CODY update dry-run" : "MYTE CODY update");
1947
+ console.log("scope: MyteCody engine only");
1948
+ console.log(`manifest: ${payload.manifest.source}`);
1949
+ console.log(`manifest read: ${payload.manifest.read_status}`);
1950
+ console.log(`platform: ${payload.release.platform}`);
1951
+ console.log(`install: ${payload.release.install_root}`);
1952
+ console.log(`would write: ${payload.would_write}`);
1953
+ if (payload.manifest.read_status !== "skipped") {
1954
+ console.log(`version: ${payload.manifest.version || "unknown"}`);
1955
+ console.log(`signature: ${payload.manifest.signature.status}`);
1956
+ console.log(`artifact: ${payload.artifact.status}`);
1957
+ } else {
1958
+ console.log("manifest fetch skipped; pass --fetch-manifest to test the configured endpoint.");
1959
+ }
1960
+ if (payload.installed) {
1961
+ console.log(`installed: ${payload.installed.executable}`);
1962
+ }
1963
+ console.log(`npm launcher/API tools: ${payload.package.update_command}`);
1964
+ return 0;
1965
+ }
1966
+
1967
+ async function run(argv = process.argv.slice(2)) {
1968
+ const envPath = loadEnv();
1969
+ const parsed = parseArgs(argv);
1970
+ const command = argv[0] || "codex";
1971
+ const restArgs = parseArgs(argv.slice(1));
1972
+
1973
+ if (command === "help" || command === "--help" || command === "-h") {
1974
+ printHelp();
1975
+ return 0;
1976
+ }
1977
+ if (command === "doctor") return runDoctor(restArgs, envPath);
1978
+ if (command === "update") return runUpdate(restArgs, envPath);
1979
+ if (command === "controller-sidecar") return runControllerSidecar();
1980
+ if (command === "version" || command === "--version" || command === "-v") {
1981
+ console.log(PACKAGE_VERSION);
1982
+ return 0;
1983
+ }
1984
+
1985
+ return runCodex(argv, parsed, envPath);
1986
+ }
1987
+
1988
+ async function main() {
1989
+ try {
1990
+ const code = await run();
1991
+ process.exitCode = code;
1992
+ } catch (error) {
1993
+ console.error(error && error.message ? error.message : error);
1994
+ process.exitCode = 1;
1995
+ }
1996
+ }
1997
+
1998
+ if (require.main === module) {
1999
+ main();
2000
+ }
2001
+
2002
+ module.exports = {
2003
+ checkPackageUpdate,
2004
+ classifyMyteCodyInvocation,
2005
+ codexLaunchArgs,
2006
+ codexProviderArgs,
2007
+ codyInferenceBase,
2008
+ codyGatewayUrl,
2009
+ currentBridgePath,
2010
+ currentControllerPath,
2011
+ currentClientManifestPath,
2012
+ currentEnginePath,
2013
+ ensureBrandedEngineInstalled,
2014
+ gatewayRoot,
2015
+ installedClientCommand,
2016
+ isVersionNewer,
2017
+ packageUpdateNotice,
2018
+ resolveCodexCommand,
2019
+ run,
2020
+ sha256Hex,
2021
+ stableJson,
2022
+ tomlLiteralString,
2023
+ verifyManifestSignature,
2024
+ writeCodexConfig,
2025
+ };