@mytegroupinc/myte-core 0.0.32 → 0.0.34

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.
@@ -0,0 +1,964 @@
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 {
11
+ DEFAULT_MYTEAI_BASE,
12
+ normalizeMyteAiBase,
13
+ } = require("./lib/ai-gateway");
14
+
15
+ const PACKAGE_VERSION = require("./package.json").version;
16
+ const DEFAULT_CHANNEL = "alpha";
17
+ const DEFAULT_MODEL_ALIAS = "myte";
18
+ const DEFAULT_CONTEXT_WINDOW = Number(process.env.MYTE_CODY_CONTEXT_WINDOW || 49152);
19
+ const DEFAULT_AUTO_COMPACT_TOKENS = Number(process.env.MYTE_CODY_AUTO_COMPACT_TOKENS || 40960);
20
+ const DEFAULT_TOOL_OUTPUT_TOKENS = Number(process.env.MYTE_CODY_TOOL_OUTPUT_TOKENS || 10000);
21
+ const DEFAULT_AGENT_THREADS = Number(process.env.MYTE_CODY_AGENT_THREADS || 4);
22
+
23
+ function findEnvPath(startDir) {
24
+ let cur = startDir;
25
+ for (let i = 0; i < 8; i += 1) {
26
+ const candidate = path.join(cur, ".env");
27
+ if (fs.existsSync(candidate)) return candidate;
28
+ const parent = path.dirname(cur);
29
+ if (parent === cur) break;
30
+ cur = parent;
31
+ }
32
+ return null;
33
+ }
34
+
35
+ function loadEnv() {
36
+ const envPath = findEnvPath(process.cwd());
37
+ if (!envPath || !fs.existsSync(envPath)) return null;
38
+ const content = fs.readFileSync(envPath, "utf8");
39
+ content.split(/\r?\n/).forEach((line) => {
40
+ const trimmed = String(line || "").trim();
41
+ if (!trimmed || trimmed.startsWith("#")) return;
42
+ const idx = trimmed.indexOf("=");
43
+ if (idx === -1) return;
44
+ const key = trimmed.slice(0, idx).trim();
45
+ let value = trimmed.slice(idx + 1).trim();
46
+ if (
47
+ (value.startsWith('"') && value.endsWith('"')) ||
48
+ (value.startsWith("'") && value.endsWith("'"))
49
+ ) {
50
+ value = value.slice(1, -1);
51
+ }
52
+ if (key && !(key in process.env)) process.env[key] = value;
53
+ });
54
+ return envPath;
55
+ }
56
+
57
+ function parseArgs(argv) {
58
+ const parsed = { _: [] };
59
+ for (let i = 0; i < argv.length; i += 1) {
60
+ const token = argv[i];
61
+ if (token === "--") {
62
+ parsed._.push(...argv.slice(i + 1));
63
+ break;
64
+ }
65
+ if (token.startsWith("--no-")) {
66
+ parsed[token.slice(5)] = false;
67
+ continue;
68
+ }
69
+ if (token.startsWith("--")) {
70
+ const eqIdx = token.indexOf("=");
71
+ if (eqIdx !== -1) {
72
+ parsed[token.slice(2, eqIdx)] = token.slice(eqIdx + 1);
73
+ continue;
74
+ }
75
+ const key = token.slice(2);
76
+ const next = argv[i + 1];
77
+ if (next !== undefined && !next.startsWith("-")) {
78
+ parsed[key] = next;
79
+ i += 1;
80
+ } else {
81
+ parsed[key] = true;
82
+ }
83
+ continue;
84
+ }
85
+ parsed._.push(token);
86
+ }
87
+ return parsed;
88
+ }
89
+
90
+ function getKeyInfo(env = process.env) {
91
+ if (String(env.MYTEAI_API_KEY || "").trim()) {
92
+ return { present: true, source: "MYTEAI_API_KEY" };
93
+ }
94
+ if (String(env.MYTE_AI_API_KEY || "").trim()) {
95
+ return { present: true, source: "MYTE_AI_API_KEY" };
96
+ }
97
+ return { present: false, source: null };
98
+ }
99
+
100
+ function getAuthToken(env = process.env) {
101
+ return String(env.MYTEAI_API_KEY || env.MYTE_AI_API_KEY || "").trim();
102
+ }
103
+
104
+ function gatewayBase(args = {}) {
105
+ const raw =
106
+ args["base-url"] ||
107
+ process.env.MYTE_CODY_API_BASE ||
108
+ process.env.MYTEAI_API_BASE ||
109
+ process.env.MYTE_AI_API_BASE ||
110
+ DEFAULT_MYTEAI_BASE;
111
+ return normalizeMyteAiBase(raw);
112
+ }
113
+
114
+ function gatewayRoot(args = {}) {
115
+ return gatewayBase(args).replace(/\/v1$/i, "");
116
+ }
117
+
118
+ function codyGatewayUrl(args = {}, suffix = "") {
119
+ const root = gatewayRoot(args).replace(/\/+$/, "");
120
+ const tail = String(suffix || "").startsWith("/") ? String(suffix) : `/${suffix}`;
121
+ return `${root}${tail}`;
122
+ }
123
+
124
+ function codyInferenceBase(args = {}) {
125
+ return codyGatewayUrl(args, "/cody/v1");
126
+ }
127
+
128
+ function manifestUrl(args = {}) {
129
+ return String(
130
+ args.manifest ||
131
+ process.env.MYTE_CODY_RELEASE_MANIFEST ||
132
+ `${gatewayRoot(args)}/cody/releases/manifest.json`,
133
+ );
134
+ }
135
+
136
+ function platformKey() {
137
+ return `${process.platform}-${process.arch}`;
138
+ }
139
+
140
+ function installRoot() {
141
+ if (process.env.MYTE_CODY_HOME) return path.resolve(process.env.MYTE_CODY_HOME);
142
+ if (process.platform === "win32") {
143
+ const base = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
144
+ return path.join(base, "Myte", "Cody");
145
+ }
146
+ return path.join(os.homedir(), ".myte", "cody");
147
+ }
148
+
149
+ function currentClientManifestPath() {
150
+ return path.join(installRoot(), "current", "manifest.json");
151
+ }
152
+
153
+ function currentEnginePath() {
154
+ const executable = process.platform === "win32" ? "mytecody-engine.exe" : "mytecody-engine";
155
+ return path.join(installRoot(), "current", "bin", executable);
156
+ }
157
+
158
+ function codexHome() {
159
+ return path.join(installRoot(), "engine-home");
160
+ }
161
+
162
+ function codexModelCatalogPath() {
163
+ return path.join(codexHome(), "mytecody-models.json");
164
+ }
165
+
166
+ function readCurrentClientManifest() {
167
+ const filePath = currentClientManifestPath();
168
+ if (!fs.existsSync(filePath)) return null;
169
+ try {
170
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
171
+ } catch {
172
+ return null;
173
+ }
174
+ }
175
+
176
+ function installedClientCommand() {
177
+ const enginePath = currentEnginePath();
178
+ if (!fs.existsSync(enginePath)) return null;
179
+ return { cmd: enginePath, args: [], source: "myte-installed-engine" };
180
+ }
181
+
182
+ function installedClientMatchesManifest(manifest, artifact) {
183
+ const current = readCurrentClientManifest();
184
+ if (!current || !installedClientCommand()) return false;
185
+ if (manifest && manifest.version && current.version !== manifest.version) return false;
186
+
187
+ const currentArtifact = current.artifact || {};
188
+ if (artifact && artifact.sha256) {
189
+ if (String(currentArtifact.sha256 || "").toLowerCase() !== String(artifact.sha256 || "").toLowerCase()) {
190
+ return false;
191
+ }
192
+ }
193
+
194
+ const expectedInstalledSha =
195
+ artifact && (artifact.installed_sha256 || artifact.executable_sha256 || artifact.uncompressed_sha256);
196
+ if (expectedInstalledSha) {
197
+ if (String(currentArtifact.installed_sha256 || "").toLowerCase() !== String(expectedInstalledSha).toLowerCase()) {
198
+ return false;
199
+ }
200
+ }
201
+
202
+ return true;
203
+ }
204
+
205
+ function printHelp() {
206
+ console.log(`MYTE CODY - Your Tech Your Way
207
+
208
+ Usage:
209
+ mytecody
210
+ mytecody doctor [--json] [--base-url <url>] [--probe-gateway]
211
+ mytecody exec [prompt or agent exec args...]
212
+ mytecody update --dry-run [--json] [--manifest <url-or-file>] [--fetch-manifest]
213
+ mytecody update [--json] [--manifest <url-or-file>]
214
+ mytecody help
215
+
216
+ Network:
217
+ The distributed MyteCody client uses the Myte AI gateway for coding
218
+ inference. It can inspect local config without network, but coding requires
219
+ gateway access and a Myte AI key.
220
+
221
+ Environment:
222
+ MYTEAI_API_KEY Myte AI inference key
223
+ MYTE_CODY_API_BASE Myte AI gateway base URL
224
+ MYTE_CODY_HOME local MyteCody client cache/install directory
225
+ `);
226
+ }
227
+
228
+ function commonStatus(args, envPath) {
229
+ const keyInfo = getKeyInfo();
230
+ const current = readCurrentClientManifest();
231
+ return {
232
+ product: "MyteCody",
233
+ command: "mytecody",
234
+ package_version: PACKAGE_VERSION,
235
+ mode: "team-gateway",
236
+ workspace: process.cwd(),
237
+ env_file: envPath,
238
+ auth: keyInfo,
239
+ gateway: {
240
+ base_url: gatewayBase(args),
241
+ inference_base_url: codyInferenceBase(args),
242
+ network_required_for_coding: true,
243
+ },
244
+ release: {
245
+ channel: String(args.channel || process.env.MYTE_CODY_RELEASE_CHANNEL || DEFAULT_CHANNEL),
246
+ manifest_url: manifestUrl(args),
247
+ platform: platformKey(),
248
+ install_root: installRoot(),
249
+ engine_path: currentEnginePath(),
250
+ client_manifest: currentClientManifestPath(),
251
+ client_installed: Boolean(current && installedClientCommand()),
252
+ client_version: current && current.version ? current.version : null,
253
+ },
254
+ };
255
+ }
256
+
257
+ function printJson(payload) {
258
+ console.log(JSON.stringify(payload, null, 2));
259
+ }
260
+
261
+ function isUrl(value) {
262
+ return /^https?:\/\//i.test(String(value || ""));
263
+ }
264
+
265
+ async function readManifest(source, { fetchManifest }) {
266
+ if (!source) return { status: "missing", manifest: null };
267
+ if (isUrl(source)) {
268
+ if (!fetchManifest) {
269
+ return { status: "skipped", manifest: null };
270
+ }
271
+ const response = await fetch(source);
272
+ const text = await response.text();
273
+ if (!response.ok) {
274
+ throw new Error(`Manifest fetch failed (${response.status}): ${text.slice(0, 300)}`);
275
+ }
276
+ return { status: "fetched", manifest: JSON.parse(text) };
277
+ }
278
+ const filePath = path.resolve(source);
279
+ const text = fs.readFileSync(filePath, "utf8");
280
+ return { status: "read", manifest: JSON.parse(text), file_path: filePath };
281
+ }
282
+
283
+ function stableJson(value) {
284
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
285
+ if (value && typeof value === "object") {
286
+ return `{${Object.keys(value)
287
+ .sort()
288
+ .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
289
+ .join(",")}}`;
290
+ }
291
+ return JSON.stringify(value);
292
+ }
293
+
294
+ function manifestWithoutSignature(manifest) {
295
+ const clone = JSON.parse(JSON.stringify(manifest || {}));
296
+ delete clone.signature;
297
+ return clone;
298
+ }
299
+
300
+ function verifyManifestSignature(manifest) {
301
+ const signature = manifest && manifest.signature;
302
+ if (!signature || !signature.value) {
303
+ return { status: "missing", verified: false };
304
+ }
305
+ const publicKey = builtInPublicKeyPem();
306
+ if (!publicKey) {
307
+ return {
308
+ status: "public-key-missing",
309
+ verified: false,
310
+ key_id: signature.key_id || null,
311
+ };
312
+ }
313
+ const verifier = crypto.createVerify("SHA256");
314
+ verifier.update(stableJson(manifestWithoutSignature(manifest)));
315
+ verifier.end();
316
+ return {
317
+ status: "checked",
318
+ verified: verifier.verify(publicKey, String(signature.value), "base64"),
319
+ key_id: signature.key_id || null,
320
+ };
321
+ }
322
+
323
+ function builtInPublicKeyPem() {
324
+ const publicKeyPath = path.join(__dirname, "lib", "mytecody-release-public-key.pem");
325
+ if (!fs.existsSync(publicKeyPath)) return "";
326
+ return fs.readFileSync(publicKeyPath, "utf8");
327
+ }
328
+
329
+ function artifactForPlatform(manifest) {
330
+ const artifacts = manifest && manifest.artifacts;
331
+ const platform = platformKey();
332
+ if (Array.isArray(artifacts)) {
333
+ return (
334
+ artifacts.find((artifact) => {
335
+ if (!artifact || typeof artifact !== "object") return false;
336
+ const combined = `${artifact.platform || process.platform}-${artifact.arch || process.arch}`;
337
+ return artifact.platform_key === platform || combined === platform;
338
+ }) || null
339
+ );
340
+ }
341
+ if (!artifacts || typeof artifacts !== "object") return null;
342
+ return artifacts[platform] || null;
343
+ }
344
+
345
+ function validateArtifactMetadata(artifact) {
346
+ if (!artifact) return { status: "missing", ok: false };
347
+ const missing = [];
348
+ if (!artifact.url) missing.push("url");
349
+ if (!artifact.sha256) missing.push("sha256");
350
+ return {
351
+ status: missing.length ? "invalid" : "present",
352
+ ok: missing.length === 0,
353
+ missing,
354
+ url: artifact.url || null,
355
+ format: artifact.format || null,
356
+ sha256_present: Boolean(artifact.sha256),
357
+ };
358
+ }
359
+
360
+ function localPathFromArtifactUrl(urlValue) {
361
+ const raw = String(urlValue || "").trim();
362
+ if (/^file:\/\//i.test(raw)) {
363
+ const url = new URL(raw);
364
+ return decodeURIComponent(url.pathname.replace(/^\/([A-Za-z]:)/, "$1"));
365
+ }
366
+ if (!isUrl(raw)) return path.resolve(raw);
367
+ return null;
368
+ }
369
+
370
+ async function readArtifactBytes(artifact) {
371
+ const urlValue = artifact && artifact.url ? String(artifact.url) : "";
372
+ const localPath = localPathFromArtifactUrl(urlValue);
373
+ if (localPath) {
374
+ return fs.readFileSync(localPath);
375
+ }
376
+ const headers = {};
377
+ const token = getAuthToken();
378
+ if (token) headers.Authorization = `Bearer ${token}`;
379
+ const response = await fetch(urlValue, { method: "GET", headers });
380
+ const bytes = Buffer.from(await response.arrayBuffer());
381
+ if (!response.ok) {
382
+ throw new Error(`Artifact fetch failed (${response.status}): ${bytes.toString("utf8", 0, Math.min(bytes.length, 300))}`);
383
+ }
384
+ return bytes;
385
+ }
386
+
387
+ function sha256Hex(bytes) {
388
+ return crypto.createHash("sha256").update(bytes).digest("hex");
389
+ }
390
+
391
+ function artifactFormat(artifact) {
392
+ const explicit = String((artifact && artifact.format) || "").trim().toLowerCase();
393
+ if (explicit) return explicit;
394
+ const urlValue = String((artifact && artifact.url) || "").trim().toLowerCase();
395
+ if (urlValue.endsWith(".gz")) return "gzip";
396
+ return "exe";
397
+ }
398
+
399
+ function artifactBytesForInstall(bytes, artifact) {
400
+ const format = artifactFormat(artifact);
401
+ if (format === "exe" || format === "binary" || format === "raw") return bytes;
402
+ if (format === "gzip" || format === "gz") return zlib.gunzipSync(bytes);
403
+ throw new Error(`Unsupported MyteCody release artifact format: ${format}`);
404
+ }
405
+
406
+ function signatureAccepted(manifest, args = {}) {
407
+ const signature = verifyManifestSignature(manifest);
408
+ if (signature.verified) return { ok: true, signature, trusted_unsigned: false };
409
+ return { ok: false, signature, trusted_unsigned: false };
410
+ }
411
+
412
+ function installArtifactBytes(bytes, manifest, artifact) {
413
+ const installBytes = artifactBytesForInstall(bytes, artifact);
414
+ const enginePath = currentEnginePath();
415
+ fs.rmSync(path.dirname(path.dirname(enginePath)), { recursive: true, force: true });
416
+ fs.mkdirSync(path.dirname(enginePath), { recursive: true });
417
+ fs.writeFileSync(enginePath, installBytes);
418
+ if (process.platform !== "win32") {
419
+ fs.chmodSync(enginePath, 0o755);
420
+ }
421
+ const installedManifest = {
422
+ schema_version: manifest.schema_version || 1,
423
+ channel: manifest.channel || DEFAULT_CHANNEL,
424
+ version: manifest.version || "unknown",
425
+ installed_at: new Date().toISOString(),
426
+ launcher_version: PACKAGE_VERSION,
427
+ platform: platformKey(),
428
+ executable: enginePath,
429
+ artifact: {
430
+ url: artifact.url,
431
+ sha256: artifact.sha256,
432
+ format: artifactFormat(artifact),
433
+ size_bytes: bytes.length,
434
+ installed_sha256: sha256Hex(installBytes),
435
+ installed_size_bytes: installBytes.length,
436
+ },
437
+ };
438
+ fs.writeFileSync(currentClientManifestPath(), JSON.stringify(installedManifest, null, 2), "utf8");
439
+ return installedManifest;
440
+ }
441
+
442
+ async function fetchJson(url, { headers = {}, timeoutMs = 8000 } = {}) {
443
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : undefined;
444
+ const timeoutId =
445
+ controller && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
446
+ try {
447
+ const response = await fetch(url, {
448
+ method: "GET",
449
+ headers: {
450
+ Accept: "application/json",
451
+ ...headers,
452
+ },
453
+ signal: controller?.signal,
454
+ });
455
+ const text = await response.text();
456
+ let body = {};
457
+ if (text.trim()) {
458
+ try {
459
+ body = JSON.parse(text);
460
+ } catch {
461
+ body = { raw_text: text.slice(0, 500) };
462
+ }
463
+ }
464
+ return {
465
+ ok: Boolean(response.ok),
466
+ status: response.status,
467
+ body,
468
+ };
469
+ } finally {
470
+ if (timeoutId) clearTimeout(timeoutId);
471
+ }
472
+ }
473
+
474
+ async function probeGateway(args = {}) {
475
+ const token = getAuthToken();
476
+ const healthUrl = codyGatewayUrl(args, "/cody/health");
477
+ const modelsUrl = codyGatewayUrl(args, "/cody/v1/models");
478
+ const result = {
479
+ ok: false,
480
+ health: {
481
+ url: healthUrl,
482
+ ok: false,
483
+ status: null,
484
+ },
485
+ models: {
486
+ url: modelsUrl,
487
+ ok: false,
488
+ status: null,
489
+ skipped: !token,
490
+ },
491
+ };
492
+ try {
493
+ const health = await fetchJson(healthUrl);
494
+ result.health.ok = Boolean(health.ok && health.body && health.body.ok === true);
495
+ result.health.status = health.status;
496
+ result.health.service = health.body && health.body.service ? String(health.body.service) : null;
497
+ result.health.model = health.body && health.body.model ? health.body.model : null;
498
+ } catch (error) {
499
+ result.health.error = error && error.message ? error.message : String(error);
500
+ }
501
+
502
+ if (token) {
503
+ try {
504
+ const models = await fetchJson(modelsUrl, {
505
+ headers: { Authorization: `Bearer ${token}` },
506
+ });
507
+ const ids = Array.isArray(models.body?.data)
508
+ ? models.body.data.map((item) => String(item && item.id ? item.id : "")).filter(Boolean)
509
+ : [];
510
+ result.models.ok = Boolean(models.ok && ids.includes("myte"));
511
+ result.models.status = models.status;
512
+ result.models.ids = ids;
513
+ result.models.skipped = false;
514
+ } catch (error) {
515
+ result.models.error = error && error.message ? error.message : String(error);
516
+ result.models.skipped = false;
517
+ }
518
+ }
519
+
520
+ result.ok = Boolean(result.health.ok && result.models.ok);
521
+ return result;
522
+ }
523
+
524
+ function tomlString(value) {
525
+ return JSON.stringify(String(value || ""));
526
+ }
527
+
528
+ function pathForToml(value) {
529
+ return String(value || "").replace(/\\/g, "\\\\");
530
+ }
531
+
532
+ function writeCodexModelCatalog() {
533
+ fs.mkdirSync(codexHome(), { recursive: true });
534
+ const baseInstructions = [
535
+ "You are MyteCody, a coding agent running in the Myte terminal harness.",
536
+ "",
537
+ "You and the user share one local workspace. Start in the current repository and operate there by default. Inspect outside the current repository only when explicitly asked.",
538
+ "",
539
+ "Your job is to understand the user's intent, inspect the relevant files, run local commands when useful, edit files with the available patch tool, run focused verification, and stop when the task is actually handled.",
540
+ "",
541
+ "For documentation, architecture, and repo-summary work, verify each material claim independently before presenting it as implemented fact. Separate current implementation from planned direction and unknowns. Treat planning docs as intent unless source code, config, command output, or logs prove the behavior exists.",
542
+ "",
543
+ "For edits, prefer apply_patch. If a patch fails, read the error, inspect the target file again, and retry with a corrected patch. Do not claim completion until after inspecting the resulting file or relevant diff.",
544
+ "",
545
+ "Keep responses concise and factual. Do not invent commands, flags, telemetry names, tool behavior, context-window defaults, retrieval behavior, or implemented features.",
546
+ ].join("\n");
547
+ const catalog = {
548
+ models: [
549
+ {
550
+ slug: DEFAULT_MODEL_ALIAS,
551
+ display_name: "Myte",
552
+ description: "Myte AI coding model.",
553
+ base_instructions: baseInstructions,
554
+ default_reasoning_level: "medium",
555
+ supported_reasoning_levels: [
556
+ { effort: "low", description: "Fast local coding pass." },
557
+ { effort: "medium", description: "Balanced coding pass." },
558
+ { effort: "high", description: "Deeper coding pass." },
559
+ ],
560
+ shell_type: "shell_command",
561
+ visibility: "list",
562
+ supported_in_api: true,
563
+ priority: 0,
564
+ availability_nux: null,
565
+ upgrade: null,
566
+ supports_reasoning_summaries: false,
567
+ default_reasoning_summary: "none",
568
+ support_verbosity: false,
569
+ default_verbosity: null,
570
+ apply_patch_tool_type: "freeform",
571
+ web_search_tool_type: "text",
572
+ truncation_policy: {
573
+ mode: "tokens",
574
+ limit: 10000,
575
+ },
576
+ supports_parallel_tool_calls: true,
577
+ supports_image_detail_original: false,
578
+ context_window: DEFAULT_CONTEXT_WINDOW,
579
+ max_context_window: DEFAULT_CONTEXT_WINDOW,
580
+ auto_compact_token_limit: DEFAULT_AUTO_COMPACT_TOKENS,
581
+ effective_context_window_percent: 90,
582
+ experimental_supported_tools: [],
583
+ input_modalities: ["text"],
584
+ supports_search_tool: false,
585
+ use_responses_lite: false,
586
+ auto_review_model_override: null,
587
+ },
588
+ ],
589
+ };
590
+ const catalogPath = codexModelCatalogPath();
591
+ fs.writeFileSync(catalogPath, JSON.stringify(catalog, null, 2), "utf8");
592
+ return catalogPath;
593
+ }
594
+
595
+ function writeCodexConfig(args = {}) {
596
+ fs.mkdirSync(codexHome(), { recursive: true });
597
+ const catalogPath = writeCodexModelCatalog();
598
+ const config = `model = ${tomlString(DEFAULT_MODEL_ALIAS)}
599
+ model_provider = "myte_ai"
600
+ model_catalog_json = ${tomlString(pathForToml(catalogPath))}
601
+ model_context_window = ${DEFAULT_CONTEXT_WINDOW}
602
+ model_auto_compact_token_limit = ${DEFAULT_AUTO_COMPACT_TOKENS}
603
+ tool_output_token_limit = ${DEFAULT_TOOL_OUTPUT_TOKENS}
604
+ web_search = "disabled"
605
+ suppress_unstable_features_warning = true
606
+ check_for_update_on_startup = false
607
+
608
+ [tui]
609
+ show_tooltips = false
610
+ status_line = ["run-state", "current-dir"]
611
+ status_line_use_colors = true
612
+ terminal_title = ["project"]
613
+
614
+ [model_providers.myte_ai]
615
+ name = "Myte AI"
616
+ base_url = ${tomlString(codyInferenceBase(args))}
617
+ env_key = "MYTE_CODY_AUTH_TOKEN"
618
+ wire_api = "responses"
619
+ requires_openai_auth = false
620
+
621
+ [features]
622
+ apps = false
623
+ multi_agent = false
624
+ hooks = false
625
+ memories = false
626
+ plugins = false
627
+ tool_suggest = false
628
+
629
+ [features.multi_agent_v2]
630
+ enabled = true
631
+ max_concurrent_threads_per_session = ${DEFAULT_AGENT_THREADS}
632
+
633
+ [skills]
634
+ include_instructions = true
635
+
636
+ [skills.bundled]
637
+ enabled = false
638
+
639
+ [projects.${tomlString(pathForToml(process.cwd()))}]
640
+ trust_level = "trusted"
641
+
642
+ [windows]
643
+ sandbox = "unelevated"
644
+ `;
645
+ const configPath = path.join(codexHome(), "config.toml");
646
+ fs.writeFileSync(configPath, config, "utf8");
647
+ return { configPath, catalogPath };
648
+ }
649
+
650
+ function resolveCodexCommand() {
651
+ const installed = installedClientCommand();
652
+ if (installed) return installed;
653
+ return null;
654
+ }
655
+
656
+ function codexProviderArgs(args = {}) {
657
+ return [
658
+ "-c",
659
+ 'model_provider="myte_ai"',
660
+ "-c",
661
+ 'model_providers.myte_ai.name="Myte AI"',
662
+ "-c",
663
+ `model_providers.myte_ai.base_url="${codyInferenceBase(args)}"`,
664
+ "-c",
665
+ 'model_providers.myte_ai.env_key="MYTE_CODY_AUTH_TOKEN"',
666
+ "-c",
667
+ 'model_providers.myte_ai.wire_api="responses"',
668
+ "-c",
669
+ `model_catalog_json="${pathForToml(codexModelCatalogPath())}"`,
670
+ "-c",
671
+ `model_context_window=${DEFAULT_CONTEXT_WINDOW}`,
672
+ "-c",
673
+ `model_auto_compact_token_limit=${DEFAULT_AUTO_COMPACT_TOKENS}`,
674
+ "-c",
675
+ `tool_output_token_limit=${DEFAULT_TOOL_OUTPUT_TOKENS}`,
676
+ "-c",
677
+ "web_search=\"disabled\"",
678
+ "-c",
679
+ "suppress_unstable_features_warning=true",
680
+ "-c",
681
+ "features.multi_agent_v2.enabled=true",
682
+ "-c",
683
+ `features.multi_agent_v2.max_concurrent_threads_per_session=${DEFAULT_AGENT_THREADS}`,
684
+ "--sandbox",
685
+ "danger-full-access",
686
+ "--ask-for-approval",
687
+ "never",
688
+ "-m",
689
+ DEFAULT_MODEL_ALIAS,
690
+ ];
691
+ }
692
+
693
+ function codexLaunchArgs(rawArgs, args = {}) {
694
+ const providerArgs = codexProviderArgs(args);
695
+ if (!rawArgs.length) return providerArgs;
696
+ if (rawArgs[0] === "exec") return [...providerArgs, "exec", "--skip-git-repo-check", ...rawArgs.slice(1)];
697
+ return [...providerArgs, ...rawArgs];
698
+ }
699
+
700
+ async function runCodex(rawArgs, args = {}, envPath = null) {
701
+ const token = getAuthToken();
702
+ if (!token) {
703
+ console.error("MyteCody requires MYTEAI_API_KEY for coding.");
704
+ return 1;
705
+ }
706
+ writeCodexConfig(args);
707
+ try {
708
+ const install = await ensureBrandedEngineInstalled(args, envPath);
709
+ if (install.ok && install.installed) {
710
+ console.error(`MyteCody engine installed: ${install.payload.installed.version}`);
711
+ } else if (!install.ok) {
712
+ console.error(`MyteCody branded engine could not be verified: ${install.reason || "unknown"}.`);
713
+ console.error("Run `mytecody update` with access to the Myte release manifest.");
714
+ return 1;
715
+ }
716
+ } catch (error) {
717
+ console.error(`MyteCody engine verification failed: ${error && error.message ? error.message : error}`);
718
+ return 1;
719
+ }
720
+
721
+ const command = resolveCodexCommand();
722
+ if (!command) {
723
+ console.error("MyteCody branded engine is not installed.");
724
+ console.error("Run `mytecody update` with access to the Myte release manifest.");
725
+ return 1;
726
+ }
727
+ const launchArgs = [...command.args, ...codexLaunchArgs(rawArgs, args)];
728
+ const env = {
729
+ ...process.env,
730
+ CODEX_HOME: codexHome(),
731
+ MYTE_CODY_AUTH_TOKEN: token,
732
+ };
733
+ return new Promise((resolve) => {
734
+ const child = spawn(command.cmd, launchArgs, {
735
+ cwd: process.cwd(),
736
+ env,
737
+ stdio: "inherit",
738
+ shell: process.platform === "win32" && command.cmd === "codex",
739
+ });
740
+ child.on("error", (error) => {
741
+ console.error(`Unable to launch MyteCody engine: ${error.message || error}`);
742
+ resolve(1);
743
+ });
744
+ child.on("close", (code) => resolve(Number.isInteger(code) ? code : 1));
745
+ });
746
+ }
747
+
748
+ async function runDoctor(args, envPath) {
749
+ const payload = {
750
+ ok: true,
751
+ ready_for_coding: false,
752
+ ...commonStatus(args, envPath),
753
+ };
754
+ if (args["probe-gateway"]) {
755
+ payload.gateway.probe = await probeGateway(args);
756
+ }
757
+ payload.ready_for_coding = payload.auth.present && payload.release.client_installed;
758
+ if (payload.gateway.probe) {
759
+ payload.ready_for_coding = Boolean(payload.ready_for_coding && payload.gateway.probe.ok);
760
+ }
761
+ if (args.json) {
762
+ printJson(payload);
763
+ return 0;
764
+ }
765
+ console.log("MYTE CODY - Your Tech Your Way");
766
+ console.log("");
767
+ console.log(`mode: ${payload.mode}`);
768
+ console.log(`workspace: ${payload.workspace}`);
769
+ console.log(`auth: ${payload.auth.present ? `present (${payload.auth.source})` : "missing"}`);
770
+ console.log(`gateway: ${payload.gateway.base_url}`);
771
+ if (payload.gateway.probe) {
772
+ console.log(`gateway probe: ${payload.gateway.probe.ok ? "ok" : "failed"}`);
773
+ }
774
+ console.log(`client: ${payload.release.client_installed ? payload.release.client_version : "not installed"}`);
775
+ console.log(`install: ${payload.release.install_root}`);
776
+ console.log("");
777
+ console.log("Coding requires the Myte AI gateway and a Myte AI key.");
778
+ return 0;
779
+ }
780
+
781
+ async function buildUpdatePayload(args, envPath, { dryRun = false } = {}) {
782
+ const isDryRun = Boolean(dryRun);
783
+ const source = manifestUrl(args);
784
+ const manifestResult = await readManifest(source, { fetchManifest: Boolean(args["fetch-manifest"]) || !isDryRun });
785
+ const manifest = manifestResult.manifest;
786
+ const artifact = manifest ? artifactForPlatform(manifest) : null;
787
+ const signature = manifest ? signatureAccepted(manifest, args) : { ok: false, signature: { status: "not-checked", verified: false } };
788
+ const artifactMetadata = manifest ? validateArtifactMetadata(artifact) : { status: "not-checked", ok: false };
789
+ const payload = {
790
+ ok: true,
791
+ dry_run: isDryRun,
792
+ would_write: !isDryRun,
793
+ ...commonStatus(args, envPath),
794
+ manifest: {
795
+ source,
796
+ read_status: manifestResult.status,
797
+ version: manifest && manifest.version ? manifest.version : null,
798
+ signature: signature.signature,
799
+ trusted_unsigned: Boolean(signature.trusted_unsigned),
800
+ },
801
+ artifact: artifactMetadata,
802
+ };
803
+
804
+ if (!isDryRun) {
805
+ if (!manifest) {
806
+ throw new Error("Cannot install MyteCody engine without a readable release manifest.");
807
+ }
808
+ if (!signature.ok) {
809
+ throw new Error("MyteCody release manifest signature is not trusted.");
810
+ }
811
+ if (!artifactMetadata.ok) {
812
+ throw new Error(`MyteCody release artifact metadata is ${artifactMetadata.status}.`);
813
+ }
814
+ const bytes = await readArtifactBytes(artifact);
815
+ const digest = sha256Hex(bytes);
816
+ if (digest.toLowerCase() !== String(artifact.sha256 || "").toLowerCase()) {
817
+ throw new Error(`Artifact SHA-256 mismatch: expected ${artifact.sha256}, got ${digest}`);
818
+ }
819
+ const installed = installArtifactBytes(bytes, manifest, artifact);
820
+ payload.installed = {
821
+ ok: true,
822
+ version: installed.version,
823
+ executable: installed.executable,
824
+ sha256: digest,
825
+ size_bytes: bytes.length,
826
+ installed_sha256: installed.artifact.installed_sha256,
827
+ installed_size_bytes: installed.artifact.installed_size_bytes,
828
+ format: installed.artifact.format,
829
+ };
830
+ payload.release = {
831
+ ...payload.release,
832
+ client_installed: true,
833
+ client_version: installed.version,
834
+ engine_path: installed.executable,
835
+ };
836
+ }
837
+
838
+ return payload;
839
+ }
840
+
841
+ function autoInstallEnabled(args = {}) {
842
+ if (args["auto-update"] === false) return false;
843
+ if (process.env.MYTE_CODY_AUTO_UPDATE === "0") return false;
844
+ return true;
845
+ }
846
+
847
+ async function ensureBrandedEngineInstalled(args = {}, envPath = null) {
848
+ const updateArgs = {
849
+ ...args,
850
+ "fetch-manifest": true,
851
+ };
852
+ delete updateArgs["dry-run"];
853
+ delete updateArgs.json;
854
+
855
+ const source = manifestUrl(updateArgs);
856
+ const manifestResult = await readManifest(source, { fetchManifest: true });
857
+ const manifest = manifestResult.manifest;
858
+ if (!manifest) {
859
+ return { ok: false, installed: false, reason: "manifest-unavailable", manifest_status: manifestResult.status };
860
+ }
861
+ const signature = signatureAccepted(manifest, updateArgs);
862
+ if (!signature.ok) {
863
+ return { ok: false, installed: false, reason: "manifest-untrusted", signature: signature.signature };
864
+ }
865
+ const artifact = artifactForPlatform(manifest);
866
+ const artifactMetadata = validateArtifactMetadata(artifact);
867
+ if (!artifactMetadata.ok) {
868
+ return { ok: false, installed: false, reason: "artifact-metadata-invalid", artifact: artifactMetadata };
869
+ }
870
+ if (installedClientMatchesManifest(manifest, artifact)) {
871
+ return { ok: true, installed: false, reason: "already-current" };
872
+ }
873
+
874
+ if (!autoInstallEnabled(args)) {
875
+ return { ok: false, installed: false, reason: "update-required-auto-install-disabled" };
876
+ }
877
+
878
+ const payload = await buildUpdatePayload(updateArgs, envPath, { dryRun: false });
879
+ return {
880
+ ok: Boolean(payload.installed && payload.installed.ok),
881
+ installed: Boolean(payload.installed && payload.installed.ok),
882
+ reason: "installed",
883
+ payload,
884
+ };
885
+ }
886
+
887
+ async function runUpdate(args, envPath) {
888
+ const dryRun = Boolean(args["dry-run"]);
889
+ const payload = await buildUpdatePayload(args, envPath, { dryRun });
890
+
891
+ if (args.json) {
892
+ printJson(payload);
893
+ return 0;
894
+ }
895
+ console.log(dryRun ? "MYTE CODY update dry-run" : "MYTE CODY update");
896
+ console.log(`manifest: ${payload.manifest.source}`);
897
+ console.log(`manifest read: ${payload.manifest.read_status}`);
898
+ console.log(`platform: ${payload.release.platform}`);
899
+ console.log(`install: ${payload.release.install_root}`);
900
+ console.log(`would write: ${payload.would_write}`);
901
+ if (payload.manifest.read_status !== "skipped") {
902
+ console.log(`version: ${payload.manifest.version || "unknown"}`);
903
+ console.log(`signature: ${payload.manifest.signature.status}`);
904
+ console.log(`artifact: ${payload.artifact.status}`);
905
+ } else {
906
+ console.log("manifest fetch skipped; pass --fetch-manifest to test the configured endpoint.");
907
+ }
908
+ if (payload.installed) {
909
+ console.log(`installed: ${payload.installed.executable}`);
910
+ }
911
+ return 0;
912
+ }
913
+
914
+ async function run(argv = process.argv.slice(2)) {
915
+ const envPath = loadEnv();
916
+ const parsed = parseArgs(argv);
917
+ const command = argv[0] || "codex";
918
+ const restArgs = parseArgs(argv.slice(1));
919
+
920
+ if (command === "help" || command === "--help" || command === "-h") {
921
+ printHelp();
922
+ return 0;
923
+ }
924
+ if (command === "doctor") return runDoctor(restArgs, envPath);
925
+ if (command === "update") return runUpdate(restArgs, envPath);
926
+ if (command === "version" || command === "--version" || command === "-v") {
927
+ console.log(PACKAGE_VERSION);
928
+ return 0;
929
+ }
930
+
931
+ return runCodex(argv, parsed, envPath);
932
+ }
933
+
934
+ async function main() {
935
+ try {
936
+ const code = await run();
937
+ process.exitCode = code;
938
+ } catch (error) {
939
+ console.error(error && error.message ? error.message : error);
940
+ process.exitCode = 1;
941
+ }
942
+ }
943
+
944
+ if (require.main === module) {
945
+ main();
946
+ }
947
+
948
+ module.exports = {
949
+ codexLaunchArgs,
950
+ codexProviderArgs,
951
+ codyInferenceBase,
952
+ codyGatewayUrl,
953
+ currentClientManifestPath,
954
+ currentEnginePath,
955
+ ensureBrandedEngineInstalled,
956
+ gatewayRoot,
957
+ installedClientCommand,
958
+ resolveCodexCommand,
959
+ run,
960
+ sha256Hex,
961
+ stableJson,
962
+ verifyManifestSignature,
963
+ writeCodexConfig,
964
+ };