@mytegroupinc/myte-core 0.0.33 → 0.0.35

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.
Files changed (2) hide show
  1. package/mytecody-cli.js +147 -33
  2. package/package.json +1 -1
package/mytecody-cli.js CHANGED
@@ -179,6 +179,29 @@ function installedClientCommand() {
179
179
  return { cmd: enginePath, args: [], source: "myte-installed-engine" };
180
180
  }
181
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
+
182
205
  function printHelp() {
183
206
  console.log(`MYTE CODY - Your Tech Your Way
184
207
 
@@ -239,12 +262,31 @@ function isUrl(value) {
239
262
  return /^https?:\/\//i.test(String(value || ""));
240
263
  }
241
264
 
242
- async function readManifest(source, { fetchManifest }) {
265
+ function statusLine(message) {
266
+ if (process.env.MYTE_CODY_QUIET_SETUP === "1") return;
267
+ console.error(`[MYTE CODY] ${message}`);
268
+ }
269
+
270
+ function formatBytes(bytes) {
271
+ const value = Number(bytes || 0);
272
+ if (!Number.isFinite(value) || value <= 0) return "unknown size";
273
+ const units = ["B", "KB", "MB", "GB"];
274
+ let size = value;
275
+ let unit = 0;
276
+ while (size >= 1024 && unit < units.length - 1) {
277
+ size /= 1024;
278
+ unit += 1;
279
+ }
280
+ return `${size.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
281
+ }
282
+
283
+ async function readManifest(source, { fetchManifest, progress } = {}) {
243
284
  if (!source) return { status: "missing", manifest: null };
244
285
  if (isUrl(source)) {
245
286
  if (!fetchManifest) {
246
287
  return { status: "skipped", manifest: null };
247
288
  }
289
+ if (progress) progress("checking signed release manifest");
248
290
  const response = await fetch(source);
249
291
  const text = await response.text();
250
292
  if (!response.ok) {
@@ -344,20 +386,52 @@ function localPathFromArtifactUrl(urlValue) {
344
386
  return null;
345
387
  }
346
388
 
347
- async function readArtifactBytes(artifact) {
389
+ async function readArtifactBytes(artifact, { progress } = {}) {
348
390
  const urlValue = artifact && artifact.url ? String(artifact.url) : "";
349
391
  const localPath = localPathFromArtifactUrl(urlValue);
350
392
  if (localPath) {
393
+ if (progress) progress("reading local MyteCody engine artifact");
351
394
  return fs.readFileSync(localPath);
352
395
  }
353
396
  const headers = {};
354
397
  const token = getAuthToken();
355
398
  if (token) headers.Authorization = `Bearer ${token}`;
399
+ if (progress) {
400
+ const expectedSize = Number(artifact && artifact.size_bytes ? artifact.size_bytes : 0);
401
+ progress(`downloading MyteCody engine (${formatBytes(expectedSize)})`);
402
+ }
356
403
  const response = await fetch(urlValue, { method: "GET", headers });
357
- const bytes = Buffer.from(await response.arrayBuffer());
358
404
  if (!response.ok) {
405
+ const bytes = Buffer.from(await response.arrayBuffer());
359
406
  throw new Error(`Artifact fetch failed (${response.status}): ${bytes.toString("utf8", 0, Math.min(bytes.length, 300))}`);
360
407
  }
408
+ if (!response.body || typeof response.body.getReader !== "function") {
409
+ const bytes = Buffer.from(await response.arrayBuffer());
410
+ if (progress) progress(`downloaded MyteCody engine (${formatBytes(bytes.length)})`);
411
+ return bytes;
412
+ }
413
+
414
+ const total = Number(response.headers.get("content-length") || artifact?.size_bytes || 0);
415
+ const reader = response.body.getReader();
416
+ const chunks = [];
417
+ let received = 0;
418
+ let lastPct = -1;
419
+ while (true) {
420
+ const { done, value } = await reader.read();
421
+ if (done) break;
422
+ const chunk = Buffer.from(value);
423
+ chunks.push(chunk);
424
+ received += chunk.length;
425
+ if (progress && total > 0) {
426
+ const pct = Math.min(100, Math.floor((received / total) * 100));
427
+ if (pct >= lastPct + 10 || pct === 100) {
428
+ progress(`downloading MyteCody engine ${pct}% (${formatBytes(received)} / ${formatBytes(total)})`);
429
+ lastPct = pct;
430
+ }
431
+ }
432
+ }
433
+ const bytes = Buffer.concat(chunks);
434
+ if (progress) progress(`downloaded MyteCody engine (${formatBytes(bytes.length)})`);
361
435
  return bytes;
362
436
  }
363
437
 
@@ -502,8 +576,16 @@ function tomlString(value) {
502
576
  return JSON.stringify(String(value || ""));
503
577
  }
504
578
 
579
+ function tomlLiteralString(value) {
580
+ const text = String(value || "");
581
+ if (!text.includes("'") && !text.includes("\n") && !text.includes("\r")) {
582
+ return `'${text}'`;
583
+ }
584
+ return tomlString(text);
585
+ }
586
+
505
587
  function pathForToml(value) {
506
- return String(value || "").replace(/\\/g, "\\\\");
588
+ return String(value || "");
507
589
  }
508
590
 
509
591
  function writeCodexModelCatalog() {
@@ -574,7 +656,7 @@ function writeCodexConfig(args = {}) {
574
656
  const catalogPath = writeCodexModelCatalog();
575
657
  const config = `model = ${tomlString(DEFAULT_MODEL_ALIAS)}
576
658
  model_provider = "myte_ai"
577
- model_catalog_json = ${tomlString(pathForToml(catalogPath))}
659
+ model_catalog_json = ${tomlString(catalogPath)}
578
660
  model_context_window = ${DEFAULT_CONTEXT_WINDOW}
579
661
  model_auto_compact_token_limit = ${DEFAULT_AUTO_COMPACT_TOKENS}
580
662
  tool_output_token_limit = ${DEFAULT_TOOL_OUTPUT_TOKENS}
@@ -613,7 +695,7 @@ include_instructions = true
613
695
  [skills.bundled]
614
696
  enabled = false
615
697
 
616
- [projects.${tomlString(pathForToml(process.cwd()))}]
698
+ [projects.${tomlLiteralString(process.cwd())}]
617
699
  trust_level = "trusted"
618
700
 
619
701
  [windows]
@@ -643,7 +725,7 @@ function codexProviderArgs(args = {}) {
643
725
  "-c",
644
726
  'model_providers.myte_ai.wire_api="responses"',
645
727
  "-c",
646
- `model_catalog_json="${pathForToml(codexModelCatalogPath())}"`,
728
+ `model_catalog_json=${tomlString(codexModelCatalogPath())}`,
647
729
  "-c",
648
730
  `model_context_window=${DEFAULT_CONTEXT_WINDOW}`,
649
731
  "-c",
@@ -680,30 +762,36 @@ async function runCodex(rawArgs, args = {}, envPath = null) {
680
762
  console.error("MyteCody requires MYTEAI_API_KEY for coding.");
681
763
  return 1;
682
764
  }
765
+ statusLine("preparing trusted workspace");
683
766
  writeCodexConfig(args);
684
- let command = resolveCodexCommand();
685
- if (!command) {
686
- try {
687
- console.error("MyteCody branded engine is not installed; installing the current Myte release...");
688
- const install = await ensureBrandedEngineInstalled(args, envPath);
689
- if (install.ok && install.installed) {
690
- console.error(`MyteCody engine installed: ${install.payload.installed.version}`);
691
- }
692
- } catch (error) {
693
- console.error(`MyteCody engine auto-install failed: ${error && error.message ? error.message : error}`);
694
- }
695
- command = resolveCodexCommand();
696
- if (!command) {
697
- console.error("MyteCody branded engine is not installed.");
767
+ try {
768
+ const install = await ensureBrandedEngineInstalled(args, envPath, { progress: statusLine });
769
+ if (install.ok && install.installed) {
770
+ statusLine(`engine installed: ${install.payload.installed.version}`);
771
+ } else if (install.ok) {
772
+ statusLine("engine ready");
773
+ } else if (!install.ok) {
774
+ console.error(`MyteCody branded engine could not be verified: ${install.reason || "unknown"}.`);
698
775
  console.error("Run `mytecody update` with access to the Myte release manifest.");
699
776
  return 1;
700
777
  }
778
+ } catch (error) {
779
+ console.error(`MyteCody engine verification failed: ${error && error.message ? error.message : error}`);
780
+ return 1;
781
+ }
782
+
783
+ const command = resolveCodexCommand();
784
+ if (!command) {
785
+ console.error("MyteCody branded engine is not installed.");
786
+ console.error("Run `mytecody update` with access to the Myte release manifest.");
787
+ return 1;
701
788
  }
702
789
  const launchArgs = [...command.args, ...codexLaunchArgs(rawArgs, args)];
703
790
  const env = {
704
791
  ...process.env,
705
792
  CODEX_HOME: codexHome(),
706
793
  MYTE_CODY_AUTH_TOKEN: token,
794
+ MYTE_CODY_BRAND: "1",
707
795
  };
708
796
  return new Promise((resolve) => {
709
797
  const child = spawn(command.cmd, launchArgs, {
@@ -753,10 +841,13 @@ async function runDoctor(args, envPath) {
753
841
  return 0;
754
842
  }
755
843
 
756
- async function buildUpdatePayload(args, envPath, { dryRun = false } = {}) {
844
+ async function buildUpdatePayload(args, envPath, { dryRun = false, progress = null } = {}) {
757
845
  const isDryRun = Boolean(dryRun);
758
846
  const source = manifestUrl(args);
759
- const manifestResult = await readManifest(source, { fetchManifest: Boolean(args["fetch-manifest"]) || !isDryRun });
847
+ const manifestResult = await readManifest(source, {
848
+ fetchManifest: Boolean(args["fetch-manifest"]) || !isDryRun,
849
+ progress,
850
+ });
760
851
  const manifest = manifestResult.manifest;
761
852
  const artifact = manifest ? artifactForPlatform(manifest) : null;
762
853
  const signature = manifest ? signatureAccepted(manifest, args) : { ok: false, signature: { status: "not-checked", verified: false } };
@@ -786,7 +877,7 @@ async function buildUpdatePayload(args, envPath, { dryRun = false } = {}) {
786
877
  if (!artifactMetadata.ok) {
787
878
  throw new Error(`MyteCody release artifact metadata is ${artifactMetadata.status}.`);
788
879
  }
789
- const bytes = await readArtifactBytes(artifact);
880
+ const bytes = await readArtifactBytes(artifact, { progress });
790
881
  const digest = sha256Hex(bytes);
791
882
  if (digest.toLowerCase() !== String(artifact.sha256 || "").toLowerCase()) {
792
883
  throw new Error(`Artifact SHA-256 mismatch: expected ${artifact.sha256}, got ${digest}`);
@@ -819,20 +910,38 @@ function autoInstallEnabled(args = {}) {
819
910
  return true;
820
911
  }
821
912
 
822
- async function ensureBrandedEngineInstalled(args = {}, envPath = null) {
823
- if (installedClientCommand()) {
824
- return { ok: true, installed: false, reason: "already-installed" };
825
- }
826
- if (!autoInstallEnabled(args)) {
827
- return { ok: false, installed: false, reason: "auto-install-disabled" };
828
- }
913
+ async function ensureBrandedEngineInstalled(args = {}, envPath = null, { progress = null } = {}) {
829
914
  const updateArgs = {
830
915
  ...args,
831
916
  "fetch-manifest": true,
832
917
  };
833
918
  delete updateArgs["dry-run"];
834
919
  delete updateArgs.json;
835
- const payload = await buildUpdatePayload(updateArgs, envPath, { dryRun: false });
920
+
921
+ const source = manifestUrl(updateArgs);
922
+ const manifestResult = await readManifest(source, { fetchManifest: true, progress });
923
+ const manifest = manifestResult.manifest;
924
+ if (!manifest) {
925
+ return { ok: false, installed: false, reason: "manifest-unavailable", manifest_status: manifestResult.status };
926
+ }
927
+ const signature = signatureAccepted(manifest, updateArgs);
928
+ if (!signature.ok) {
929
+ return { ok: false, installed: false, reason: "manifest-untrusted", signature: signature.signature };
930
+ }
931
+ const artifact = artifactForPlatform(manifest);
932
+ const artifactMetadata = validateArtifactMetadata(artifact);
933
+ if (!artifactMetadata.ok) {
934
+ return { ok: false, installed: false, reason: "artifact-metadata-invalid", artifact: artifactMetadata };
935
+ }
936
+ if (installedClientMatchesManifest(manifest, artifact)) {
937
+ return { ok: true, installed: false, reason: "already-current" };
938
+ }
939
+
940
+ if (!autoInstallEnabled(args)) {
941
+ return { ok: false, installed: false, reason: "update-required-auto-install-disabled" };
942
+ }
943
+
944
+ const payload = await buildUpdatePayload(updateArgs, envPath, { dryRun: false, progress });
836
945
  return {
837
946
  ok: Boolean(payload.installed && payload.installed.ok),
838
947
  installed: Boolean(payload.installed && payload.installed.ok),
@@ -843,7 +952,10 @@ async function ensureBrandedEngineInstalled(args = {}, envPath = null) {
843
952
 
844
953
  async function runUpdate(args, envPath) {
845
954
  const dryRun = Boolean(args["dry-run"]);
846
- const payload = await buildUpdatePayload(args, envPath, { dryRun });
955
+ const payload = await buildUpdatePayload(args, envPath, {
956
+ dryRun,
957
+ progress: args.json ? null : statusLine,
958
+ });
847
959
 
848
960
  if (args.json) {
849
961
  printJson(payload);
@@ -907,6 +1019,7 @@ module.exports = {
907
1019
  codexProviderArgs,
908
1020
  codyInferenceBase,
909
1021
  codyGatewayUrl,
1022
+ currentClientManifestPath,
910
1023
  currentEnginePath,
911
1024
  ensureBrandedEngineInstalled,
912
1025
  gatewayRoot,
@@ -915,6 +1028,7 @@ module.exports = {
915
1028
  run,
916
1029
  sha256Hex,
917
1030
  stableJson,
1031
+ tomlLiteralString,
918
1032
  verifyManifestSignature,
919
1033
  writeCodexConfig,
920
1034
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mytegroupinc/myte-core",
3
- "version": "0.0.33",
3
+ "version": "0.0.35",
4
4
  "description": "Myte CLI core implementation.",
5
5
  "type": "commonjs",
6
6
  "main": "cli.js",