@neocompose/cli 0.58.1 → 0.58.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.58.2] - 2026-09-24
4
+
5
+ - Pull downloads up to eight managed image or audio files concurrently. Matching local bytes are still reused.
6
+ - Pull verifies and stages all downloaded files before replacing local binaries or updating conflict artifacts. A failed transfer leaves existing files intact.
7
+
3
8
  ## [0.58.1] - 2026-09-24
4
9
 
5
10
  - Push writes `@id` on list entries nested inside a new computed row, such as the items of a list on a new `new Event(...) { ... }` entry, so `neo status` is clean afterwards. Before, those entries stayed without ids and status kept reporting changes.
package/dist/neo.mjs CHANGED
@@ -92493,7 +92493,8 @@ function planCommitPreparationLoadedClosureScope(args) {
92493
92493
  if (rules.has("ancestor-paths") && record6.parentRecordKind !== null && record6.parentRecordKind !== void 0 && record6.parentRecordId !== null && record6.parentRecordId !== void 0) {
92494
92494
  rememberExact(record6.parentRecordKind, record6.parentRecordId);
92495
92495
  }
92496
- const canOwnStoredDescendants = record6.recordKind !== "value" || !isLiteralValueContent(record6.data) || !["string", "number", "boolean"].includes(typeof record6.data.value) || typeof record6.data.classId === "string" || isVirtualInstanceRootShape(record6.data);
92496
+ const memberWithoutStoredChildren = record6.recordKind === "member" && isMemberBase(record6.data) && getOptionalString(record6.data, "valueId") === void 0 && record6.data.defaultValue === void 0;
92497
+ const canOwnStoredDescendants = !memberWithoutStoredChildren && (record6.recordKind !== "value" || !isLiteralValueContent(record6.data) || !["string", "number", "boolean"].includes(typeof record6.data.value) || typeof record6.data.classId === "string" || isVirtualInstanceRootShape(record6.data));
92497
92498
  if (rules.has("descendants") && record6.includeDescendants !== false && canOwnStoredDescendants && (record6.recordKind === "member" || record6.recordKind === "value" || record6.recordKind === "class" || record6.recordKind === "variant")) {
92498
92499
  rememberRange({
92499
92500
  rangeKind: "children",
@@ -131065,6 +131066,35 @@ var init_push_hook = __esm({
131065
131066
  }
131066
131067
  });
131067
131068
 
131069
+ // src/concurrency.ts
131070
+ async function mapWithConcurrency(values, concurrency, run) {
131071
+ let nextIndex = 0;
131072
+ let failure;
131073
+ const results = new Array(values.length);
131074
+ const worker = async () => {
131075
+ while (failure === void 0) {
131076
+ const index = nextIndex;
131077
+ nextIndex += 1;
131078
+ if (index >= values.length) return;
131079
+ try {
131080
+ results[index] = await run(values[index], index);
131081
+ } catch (error) {
131082
+ failure ??= { error };
131083
+ }
131084
+ }
131085
+ };
131086
+ await Promise.all(
131087
+ Array.from({ length: Math.min(concurrency, values.length) }, worker)
131088
+ );
131089
+ if (failure !== void 0) throw failure.error;
131090
+ return results;
131091
+ }
131092
+ var init_concurrency = __esm({
131093
+ "src/concurrency.ts"() {
131094
+ "use strict";
131095
+ }
131096
+ });
131097
+
131068
131098
  // src/project-source/project-file-push.ts
131069
131099
  import { basename as basename2, join as join9 } from "node:path";
131070
131100
  import { readFileSync as readFileSync11 } from "node:fs";
@@ -131493,28 +131523,6 @@ async function waitForRetry(attempt, signal) {
131493
131523
  signal.addEventListener("abort", abort, { once: true });
131494
131524
  });
131495
131525
  }
131496
- async function mapWithConcurrency(values, concurrency, run) {
131497
- let nextIndex = 0;
131498
- let firstError;
131499
- const results = new Array(values.length);
131500
- const worker = async () => {
131501
- while (firstError === void 0) {
131502
- const index = nextIndex;
131503
- nextIndex += 1;
131504
- if (index >= values.length) return;
131505
- try {
131506
- results[index] = await run(values[index]);
131507
- } catch (error) {
131508
- firstError ??= error;
131509
- }
131510
- }
131511
- };
131512
- await Promise.all(
131513
- Array.from({ length: Math.min(concurrency, values.length) }, worker)
131514
- );
131515
- if (firstError !== void 0) throw firstError;
131516
- return results;
131517
- }
131518
131526
  function combineSignals(...signals) {
131519
131527
  const defined = signals.filter(
131520
131528
  (signal) => signal !== void 0
@@ -131545,6 +131553,7 @@ var UPLOAD_CONCURRENCY, PRESIGN_BATCH_SIZE, MULTIPART_PART_CONCURRENCY, PRESIGN_
131545
131553
  var init_project_file_push = __esm({
131546
131554
  "src/project-source/project-file-push.ts"() {
131547
131555
  "use strict";
131556
+ init_concurrency();
131548
131557
  init_project_files();
131549
131558
  init_projection();
131550
131559
  init_src();
@@ -136245,11 +136254,19 @@ var init_reset = __esm({
136245
136254
  });
136246
136255
 
136247
136256
  // src/project-source/project-file-pull.ts
136248
- import { existsSync as existsSync11, rmSync as rmSync7 } from "node:fs";
136257
+ import {
136258
+ existsSync as existsSync11,
136259
+ mkdtempSync,
136260
+ readFileSync as readFileSync15,
136261
+ rmSync as rmSync7,
136262
+ writeFileSync as writeFileSync10
136263
+ } from "node:fs";
136264
+ import { tmpdir } from "node:os";
136249
136265
  import { join as join13 } from "node:path";
136250
136266
  async function pullProjectBinariesV4(args) {
136251
136267
  const resolvePath = createWorkspaceSourcePathResolver(args.workspace.root);
136252
- let client = args.client ?? null;
136268
+ const plans = /* @__PURE__ */ new Map();
136269
+ const downloads = [];
136253
136270
  const localById = new Map(
136254
136271
  (args.localBinaries ?? []).map((binary) => [binary.fileId, binary])
136255
136272
  );
@@ -136263,7 +136280,14 @@ async function pullProjectBinariesV4(args) {
136263
136280
  throw new Error(`project-file:${record6.recordId} must be an object.`);
136264
136281
  }
136265
136282
  if (record6.data.status !== "uploaded") continue;
136266
- client ??= NeoApiClient.forBaseUrl(args.workspace.config.apiBaseUrl);
136283
+ const existing = plans.get(record6.recordId);
136284
+ if (existing !== void 0) {
136285
+ if (existing.contentHash !== record6.contentHash)
136286
+ throw new Error(
136287
+ `Project file ${record6.recordId} appears with conflicting content hashes in the pulled document.`
136288
+ );
136289
+ continue;
136290
+ }
136267
136291
  const key = `project-file:${record6.recordId}`;
136268
136292
  const previous = args.previousRecords[key];
136269
136293
  const localStatus = localById.get(record6.recordId);
@@ -136276,7 +136300,6 @@ async function pullProjectBinariesV4(args) {
136276
136300
  record6.data,
136277
136301
  `project-file:${record6.recordId} metadata`
136278
136302
  );
136279
- let remoteBytes = null;
136280
136303
  let action = planBinaryMergeV4({
136281
136304
  baseDigest,
136282
136305
  localDigest,
@@ -136287,98 +136310,142 @@ async function pullProjectBinariesV4(args) {
136287
136310
  if (args.destructive === true) {
136288
136311
  action = localDigest === remoteDigest ? "unchanged" : "download";
136289
136312
  }
136290
- if (action === "download" || action === "conflict") {
136291
- if (remoteBytes === null) {
136292
- const response = await download(
136293
- record6.data,
136294
- record6.recordId,
136295
- client,
136296
- args.workspace
136297
- );
136298
- remoteBytes = response.bytes;
136299
- if (response.sha256 !== remoteDigest) {
136313
+ const plan = {
136314
+ key,
136315
+ fileId: record6.recordId,
136316
+ contentHash: record6.contentHash,
136317
+ data: record6.data,
136318
+ previous,
136319
+ path,
136320
+ baseDigest,
136321
+ remoteDigest,
136322
+ action
136323
+ };
136324
+ plans.set(record6.recordId, plan);
136325
+ if (action === "download" || action === "conflict") downloads.push(plan);
136326
+ }
136327
+ let stagingDirectory;
136328
+ try {
136329
+ const stagedPaths = /* @__PURE__ */ new Map();
136330
+ if (downloads.length > 0) {
136331
+ stagingDirectory = mkdtempSync(join13(tmpdir(), "neo-pull-files-"));
136332
+ const directory = stagingDirectory;
136333
+ const client = args.client ?? NeoApiClient.forBaseUrl(args.workspace.config.apiBaseUrl);
136334
+ await mapWithConcurrency(
136335
+ downloads,
136336
+ DOWNLOAD_CONCURRENCY,
136337
+ async (plan, index) => {
136338
+ const response = await download(
136339
+ plan.data,
136340
+ plan.fileId,
136341
+ client,
136342
+ args.workspace
136343
+ );
136344
+ if (response.sha256 !== plan.remoteDigest) {
136345
+ throw new Error(
136346
+ `Project file ${plan.fileId} content changed while pull was in progress (${plan.remoteDigest} -> ${response.sha256}). Pull again against a stable version head.`
136347
+ );
136348
+ }
136349
+ const stagedPath = join13(directory, String(index));
136350
+ writeFileSync10(stagedPath, response.bytes);
136351
+ stagedPaths.set(plan.fileId, stagedPath);
136352
+ }
136353
+ );
136354
+ }
136355
+ for (const plan of plans.values()) {
136356
+ const {
136357
+ key,
136358
+ fileId,
136359
+ data,
136360
+ previous,
136361
+ path,
136362
+ baseDigest,
136363
+ remoteDigest,
136364
+ action
136365
+ } = plan;
136366
+ const absolute = resolvePath(path);
136367
+ const stagedPath = stagedPaths.get(fileId);
136368
+ const remoteBytes = stagedPath === void 0 ? null : readFileSync15(stagedPath);
136369
+ if (action === "download") {
136370
+ if (remoteBytes === null) {
136300
136371
  throw new Error(
136301
- `Project file ${record6.recordId} content changed while pull was in progress (${remoteDigest} -> ${response.sha256}). Pull again against a stable version head.`
136372
+ `Project file ${fileId} download plan has no verified remote bytes.`
136302
136373
  );
136303
136374
  }
136375
+ writeVerifiedBinaryDownloadV4(absolute, remoteBytes, remoteDigest);
136376
+ downloaded += 1;
136377
+ states.set(key, binaryState(path, remoteDigest, data));
136378
+ continue;
136304
136379
  }
136305
- }
136306
- if (action === "download") {
136307
- if (remoteBytes === null) {
136308
- throw new Error(
136309
- `Project file ${record6.recordId} download plan has no verified remote bytes.`
136310
- );
136380
+ if (action === "conflict") {
136381
+ if (remoteBytes === null) {
136382
+ throw new Error(
136383
+ `Project file ${fileId} conflict plan has no verified remote bytes.`
136384
+ );
136385
+ }
136386
+ const artifactPath = writeBinaryConflictArtifactV4(
136387
+ args.workspace.root,
136388
+ fileId,
136389
+ fileName(data),
136390
+ remoteBytes,
136391
+ remoteDigest
136392
+ );
136393
+ const state = binaryState(path, baseDigest, data);
136394
+ states.set(key, {
136395
+ ...state,
136396
+ conflict: {
136397
+ action: "conflict",
136398
+ remoteSha256: remoteDigest,
136399
+ artifactPath
136400
+ }
136401
+ });
136402
+ conflicts.push(path);
136403
+ conflicted += 1;
136404
+ continue;
136311
136405
  }
136312
- writeVerifiedBinaryDownloadV4(absolute, remoteBytes, remoteDigest);
136313
- downloaded += 1;
136314
- states.set(key, binaryState(path, remoteDigest, record6.data));
136315
- continue;
136316
- }
136317
- if (action === "conflict") {
136318
- if (remoteBytes === null) {
136319
- throw new Error(
136320
- `Project file ${record6.recordId} conflict plan has no verified remote bytes.`
136321
- );
136406
+ if (action === "converged" || action === "unchanged") {
136407
+ states.set(key, binaryState(path, remoteDigest, data));
136408
+ removePreviousConflict(args.workspace.root, previous?.projectBinary);
136409
+ continue;
136322
136410
  }
136323
- const artifactPath = writeBinaryConflictArtifactV4(
136324
- args.workspace.root,
136325
- record6.recordId,
136326
- fileName(record6.data),
136327
- remoteBytes,
136328
- remoteDigest
136329
- );
136330
- const state = binaryState(path, baseDigest, record6.data);
136331
- states.set(key, {
136332
- ...state,
136333
- conflict: {
136334
- action: "conflict",
136335
- remoteSha256: remoteDigest,
136336
- artifactPath
136337
- }
136338
- });
136339
- conflicts.push(path);
136340
- conflicted += 1;
136341
- continue;
136342
- }
136343
- if (action === "converged" || action === "unchanged") {
136344
- states.set(key, binaryState(path, remoteDigest, record6.data));
136345
- removePreviousConflict(args.workspace.root, previous?.projectBinary);
136346
- continue;
136347
- }
136348
- states.set(key, binaryState(path, baseDigest, record6.data));
136349
- }
136350
- for (const [key, previous] of Object.entries(args.previousRecords)) {
136351
- if (previous.recordKind !== "project-file") continue;
136352
- if (args.document.records.has(key)) continue;
136353
- const localStatus = localById.get(previous.recordId);
136354
- const path = localStatus?.path ?? previous.projectBinary?.path ?? canonicalProjectBinaryPathV42(previous.data);
136355
- const absolute = resolvePath(path);
136356
- const localDigest = existsSync11(absolute) ? sha256File(absolute) : null;
136357
- const baseDigest = previous.projectBinary?.sha256 ?? readSha256(previous.data) ?? null;
136358
- const action = planBinaryMergeV4({
136359
- baseDigest,
136360
- localDigest,
136361
- remoteDigest: null,
136362
- declarationPresent: localStatus !== void 0,
136363
- hadLocalBytes: previous.projectBinary !== void 0
136364
- });
136365
- if (action === "delete-modify") {
136366
- const data = isObjectRecord2(previous.data) ? previous.data : {};
136367
- states.set(key, {
136368
- ...binaryState(path, baseDigest, data),
136369
- conflict: {
136370
- action: "delete-modify",
136371
- remoteSha256: null
136372
- }
136411
+ states.set(key, binaryState(path, baseDigest, data));
136412
+ }
136413
+ for (const [key, previous] of Object.entries(args.previousRecords)) {
136414
+ if (previous.recordKind !== "project-file") continue;
136415
+ if (args.document.records.has(key)) continue;
136416
+ const localStatus = localById.get(previous.recordId);
136417
+ const path = localStatus?.path ?? previous.projectBinary?.path ?? canonicalProjectBinaryPathV42(previous.data);
136418
+ const absolute = resolvePath(path);
136419
+ const localDigest = existsSync11(absolute) ? sha256File(absolute) : null;
136420
+ const baseDigest = previous.projectBinary?.sha256 ?? readSha256(previous.data) ?? null;
136421
+ const action = planBinaryMergeV4({
136422
+ baseDigest,
136423
+ localDigest,
136424
+ remoteDigest: null,
136425
+ declarationPresent: localStatus !== void 0,
136426
+ hadLocalBytes: previous.projectBinary !== void 0
136373
136427
  });
136374
- conflicts.push(path);
136375
- conflicted += 1;
136376
- continue;
136428
+ if (action === "delete-modify") {
136429
+ const data = isObjectRecord2(previous.data) ? previous.data : {};
136430
+ states.set(key, {
136431
+ ...binaryState(path, baseDigest, data),
136432
+ conflict: {
136433
+ action: "delete-modify",
136434
+ remoteSha256: null
136435
+ }
136436
+ });
136437
+ conflicts.push(path);
136438
+ conflicted += 1;
136439
+ continue;
136440
+ }
136441
+ rmSync7(absolute, { force: true });
136442
+ removePreviousConflict(args.workspace.root, previous.projectBinary);
136377
136443
  }
136378
- rmSync7(absolute, { force: true });
136379
- removePreviousConflict(args.workspace.root, previous.projectBinary);
136444
+ return { states, downloaded, conflicted, conflicts };
136445
+ } finally {
136446
+ if (stagingDirectory !== void 0)
136447
+ rmSync7(stagingDirectory, { recursive: true, force: true });
136380
136448
  }
136381
- return { states, downloaded, conflicted, conflicts };
136382
136449
  }
136383
136450
  async function download(data, fileId, client, workspace) {
136384
136451
  const version = typeof data.storageETag === "string" ? `?v=${encodeURIComponent(data.storageETag)}` : "";
@@ -136447,13 +136514,16 @@ function removePreviousConflict(root, state) {
136447
136514
  if (state?.conflict?.artifactPath === void 0) return;
136448
136515
  rmSync7(join13(root, state.conflict.artifactPath), { force: true });
136449
136516
  }
136517
+ var DOWNLOAD_CONCURRENCY;
136450
136518
  var init_project_file_pull = __esm({
136451
136519
  "src/project-source/project-file-pull.ts"() {
136452
136520
  "use strict";
136453
136521
  init_workspace_source_path();
136522
+ init_concurrency();
136454
136523
  init_http();
136455
136524
  init_projection();
136456
136525
  init_project_files();
136526
+ DOWNLOAD_CONCURRENCY = 8;
136457
136527
  }
136458
136528
  });
136459
136529
 
@@ -136578,10 +136648,10 @@ __export(pull_exports, {
136578
136648
  });
136579
136649
  import {
136580
136650
  mkdirSync as mkdirSync10,
136581
- writeFileSync as writeFileSync10,
136651
+ writeFileSync as writeFileSync11,
136582
136652
  rmSync as rmSync8,
136583
136653
  existsSync as existsSync12,
136584
- readFileSync as readFileSync15
136654
+ readFileSync as readFileSync16
136585
136655
  } from "node:fs";
136586
136656
  import { dirname as dirname9 } from "node:path";
136587
136657
  async function runPull(workspace, options, progress = spinner("Checking local workspace\u2026")) {
@@ -137048,7 +137118,7 @@ async function finishFormat4Pull(args) {
137048
137118
  let written = 0;
137049
137119
  for (const path of emittedPaths) {
137050
137120
  const absolute = resolveSourcePath(path);
137051
- const existing = existsSync12(absolute) ? readFileSync15(absolute, "utf8") : null;
137121
+ const existing = existsSync12(absolute) ? readFileSync16(absolute, "utf8") : null;
137052
137122
  const unprojectedConflictKeys = authoredLocalConflictKeysByPath.get(path) ?? [];
137053
137123
  if (unprojectedConflictKeys.length > 0 && existing === null) {
137054
137124
  throw new Error(
@@ -137070,7 +137140,7 @@ async function finishFormat4Pull(args) {
137070
137140
  mkdirSync10(dirname9(absolute), { recursive: true });
137071
137141
  if (existing !== null && !marked && !rewritePaths.has(path)) continue;
137072
137142
  if (existing !== content) {
137073
- writeFileSync10(absolute, content, "utf8");
137143
+ writeFileSync11(absolute, content, "utf8");
137074
137144
  written += 1;
137075
137145
  }
137076
137146
  }
@@ -137504,7 +137574,7 @@ var init_exports = {};
137504
137574
  __export(init_exports, {
137505
137575
  runInit: () => runInit
137506
137576
  });
137507
- import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "node:fs";
137577
+ import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync17, writeFileSync as writeFileSync12 } from "node:fs";
137508
137578
  import { join as join14, resolve as resolve3 } from "node:path";
137509
137579
  async function runInit(options) {
137510
137580
  let projectId = options.projectId;
@@ -137629,13 +137699,13 @@ async function runInit(options) {
137629
137699
  }
137630
137700
  function ensurePrivateStateIgnored(root) {
137631
137701
  const path = join14(root, ".gitignore");
137632
- const existing = existsSync13(path) ? readFileSync16(path, "utf8") : "";
137702
+ const existing = existsSync13(path) ? readFileSync17(path, "utf8") : "";
137633
137703
  if (existing.split(/\r?\n/u).some((line) => line.trim() === ".neo/" || line.trim() === ".neo")) {
137634
137704
  return;
137635
137705
  }
137636
137706
  const prefix = existing.length === 0 || existing.endsWith("\n") ? existing : `${existing}
137637
137707
  `;
137638
- writeFileSync11(path, `${prefix}.neo/
137708
+ writeFileSync12(path, `${prefix}.neo/
137639
137709
  `, "utf8");
137640
137710
  }
137641
137711
  var init_init = __esm({
@@ -139039,7 +139109,7 @@ __export(script_exports, {
139039
139109
  readDocumentArrays: () => readDocumentArrays,
139040
139110
  runScript: () => runScript
139041
139111
  });
139042
- import { readFileSync as readFileSync17 } from "node:fs";
139112
+ import { readFileSync as readFileSync18 } from "node:fs";
139043
139113
  function readDocumentArrays(raw) {
139044
139114
  const arrayOf2 = (field) => {
139045
139115
  const value = raw[field];
@@ -139559,9 +139629,9 @@ function buildRootValue(document, lookups) {
139559
139629
  }
139560
139630
  function readSource(options, fallback) {
139561
139631
  if (options.source !== null) return options.source;
139562
- if (options.file !== null) return readFileSync17(options.file, "utf8");
139632
+ if (options.file !== null) return readFileSync18(options.file, "utf8");
139563
139633
  if (fallback !== void 0) return fallback;
139564
- const stdin = readFileSync17(0, "utf8");
139634
+ const stdin = readFileSync18(0, "utf8");
139565
139635
  if (stdin.trim().length === 0) {
139566
139636
  throw new Error(
139567
139637
  "Provide NeoScript source as an argument, --file, or stdin."
@@ -140610,7 +140680,7 @@ var init_registry2 = __esm({
140610
140680
  "schema-contract/registry.mjs"() {
140611
140681
  "use strict";
140612
140682
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
140613
- cliVersion: "0.58.1",
140683
+ cliVersion: "0.58.2",
140614
140684
  projectFileUploadBatchSize: 32,
140615
140685
  documentRecords: {
140616
140686
  member: {
@@ -142038,12 +142108,12 @@ import {
142038
142108
  existsSync as existsSync14,
142039
142109
  mkdirSync as mkdirSync12,
142040
142110
  readdirSync as readdirSync4,
142041
- readFileSync as readFileSync18,
142111
+ readFileSync as readFileSync19,
142042
142112
  realpathSync as realpathSync2,
142043
142113
  renameSync as renameSync7,
142044
142114
  rmSync as rmSync9,
142045
142115
  statSync as statSync2,
142046
- writeFileSync as writeFileSync12
142116
+ writeFileSync as writeFileSync13
142047
142117
  } from "node:fs";
142048
142118
  import {
142049
142119
  basename as basename3,
@@ -142257,7 +142327,7 @@ function preparedHookCandidate(workspace) {
142257
142327
  );
142258
142328
  }
142259
142329
  const parsed = JSON.parse(
142260
- readFileSync18(join15(directory, "candidate.json"), "utf8")
142330
+ readFileSync19(join15(directory, "candidate.json"), "utf8")
142261
142331
  );
142262
142332
  if (!isRecord10(parsed)) {
142263
142333
  throw new NeoTestPreparedCandidateError(
@@ -142327,7 +142397,7 @@ function testCandidateCachePath(workspace, inputFingerprint) {
142327
142397
  function cachedTestCandidate(workspace, inputFingerprint) {
142328
142398
  try {
142329
142399
  const candidatePath = testCandidateCachePath(workspace, inputFingerprint);
142330
- const parsed = JSON.parse(readFileSync18(candidatePath, "utf8"));
142400
+ const parsed = JSON.parse(readFileSync19(candidatePath, "utf8"));
142331
142401
  if (!isRecord10(parsed)) return null;
142332
142402
  if (parsed.version !== 1) return null;
142333
142403
  if (parsed.candidateRevision !== TEST_CANDIDATE_CACHE_REVISION) return null;
@@ -142338,7 +142408,7 @@ function cachedTestCandidate(workspace, inputFingerprint) {
142338
142408
  return null;
142339
142409
  }
142340
142410
  if (typeof parsed.documentSha256 !== "string") return null;
142341
- const documentJson = readFileSync18(
142411
+ const documentJson = readFileSync19(
142342
142412
  join15(dirname10(candidatePath), "document.json"),
142343
142413
  "utf8"
142344
142414
  );
@@ -142378,7 +142448,7 @@ function cacheTestCandidate(workspace, inputFingerprint, candidate) {
142378
142448
  function compileSpec(workspace, document, absolutePath, projectCompilationHash2) {
142379
142449
  const scriptDocument = readDocumentArrays(document);
142380
142450
  const path = relative8(workspace.root, absolutePath).split(sep8).join("/");
142381
- const source = readFileSync18(absolutePath, "utf8");
142451
+ const source = readFileSync19(absolutePath, "utf8");
142382
142452
  const sourceHash = createHash11("sha256").update(source).digest("hex");
142383
142453
  const artifactPath = join15(
142384
142454
  workspace.root,
@@ -142389,7 +142459,7 @@ function compileSpec(workspace, document, absolutePath, projectCompilationHash2)
142389
142459
  `${sourceHash}.json`
142390
142460
  );
142391
142461
  try {
142392
- const cached = JSON.parse(readFileSync18(artifactPath, "utf8"));
142462
+ const cached = JSON.parse(readFileSync19(artifactPath, "utf8"));
142393
142463
  if (isRecord10(cached) && cached.version === 1 && cached.compilerRevision === NEOSCRIPT_COMPILER_REVISION && cached.projectCompilationHash === projectCompilationHash2 && cached.sourceHash === sourceHash && isRecord10(cached.action) && typeof cached.artifactSha256 === "string" && createHash11("sha256").update(JSON.stringify(cached.action)).digest("hex") === cached.artifactSha256 && cached.action.compilerRevision === NEOSCRIPT_COMPILER_REVISION && Array.isArray(cached.action.parameters) && Array.isArray(cached.action.instructions) && isRecord10(cached.action.typeInfo)) {
142394
142464
  return {
142395
142465
  path,
@@ -143206,7 +143276,7 @@ async function executeRegisteredSpec(registered, document, selected, timeoutMs,
143206
143276
  function atomicWrite(path, content) {
143207
143277
  mkdirSync12(dirname10(path), { recursive: true });
143208
143278
  const temporary = `${path}.tmp-${String(process.pid)}-${randomUUID6()}`;
143209
- writeFileSync12(temporary, content, "utf8");
143279
+ writeFileSync13(temporary, content, "utf8");
143210
143280
  renameSync7(temporary, path);
143211
143281
  }
143212
143282
  function testBuildFiles(root) {
@@ -143752,7 +143822,7 @@ import {
143752
143822
  constants as fsConstants,
143753
143823
  accessSync,
143754
143824
  existsSync as existsSync15,
143755
- readFileSync as readFileSync19
143825
+ readFileSync as readFileSync20
143756
143826
  } from "node:fs";
143757
143827
  import { extname as extname2, isAbsolute as isAbsolute4, join as join16, relative as relative9, sep as sep9 } from "node:path";
143758
143828
  function inspectNeoDoctor(workspace) {
@@ -143837,7 +143907,7 @@ function inspectSourceContract(workspace) {
143837
143907
  );
143838
143908
  try {
143839
143909
  const inputs = files.map((file, index) => {
143840
- const text = readFileSync19(file, "utf8");
143910
+ const text = readFileSync20(file, "utf8");
143841
143911
  return {
143842
143912
  uri: relativeFiles[index],
143843
143913
  kind: requiredProjectSourceKindV4(relativeFiles[index], text),
@@ -143885,7 +143955,7 @@ function inspectExtensionContract(root) {
143885
143955
  };
143886
143956
  }
143887
143957
  try {
143888
- const value = JSON.parse(readFileSync19(cachePath, "utf8"));
143958
+ const value = JSON.parse(readFileSync20(cachePath, "utf8"));
143889
143959
  assertProjectSourceAnalysisV4(value);
143890
143960
  return {
143891
143961
  id: NEO_VSCODE_EXTENSION_ID,
@@ -144231,7 +144301,7 @@ __export(migrate_exports, {
144231
144301
  runMigrate: () => runMigrate
144232
144302
  });
144233
144303
  import { randomUUID as randomUUID7 } from "node:crypto";
144234
- import { existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "node:fs";
144304
+ import { existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "node:fs";
144235
144305
  import { join as join17, relative as relative10 } from "node:path";
144236
144306
  async function runMigrate(workspace, subcommand, positional, targetRef, json, dependencies = {}) {
144237
144307
  if (subcommand === "new") {
@@ -144245,7 +144315,7 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
144245
144315
  mkdirSync13(migrationsDir, { recursive: true });
144246
144316
  let nextOrder = 1;
144247
144317
  for (const path of listProjectSourceFilesV4(workspace.root)) {
144248
- const source = readFileSync20(path, "utf8");
144318
+ const source = readFileSync21(path, "utf8");
144249
144319
  if (neoProjectSourceKind(path, source) !== "migration") continue;
144250
144320
  const migration = parseMigrationFile(
144251
144321
  source,
@@ -144268,7 +144338,7 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
144268
144338
  target === "project" ? "// Runs once with root context. Write the NeoScript action below." : `// Runs once per ${target} value; \`this\` is the instance.`,
144269
144339
  ""
144270
144340
  ].join("\n");
144271
- writeFileSync13(absolute, template, "utf8");
144341
+ writeFileSync14(absolute, template, "utf8");
144272
144342
  console.log(`Created ${relPath} \u2014 edit the action body, then "neo push".`);
144273
144343
  console.log("(The id is assigned at push, same as schema creates.)");
144274
144344
  return;
@@ -146431,7 +146501,7 @@ __export(content_exports, {
146431
146501
  makeContentContext: () => makeContentContext,
146432
146502
  runLoc: () => runLoc
146433
146503
  });
146434
- import { readFileSync as readFileSync21 } from "node:fs";
146504
+ import { readFileSync as readFileSync22 } from "node:fs";
146435
146505
  import { randomUUID as randomUUID8 } from "node:crypto";
146436
146506
  function versionPath2(workspace, suffix) {
146437
146507
  return `/api/projects/${workspace.config.projectId}/versions/${workspace.config.versionId}/${suffix}`;
@@ -146439,9 +146509,9 @@ function versionPath2(workspace, suffix) {
146439
146509
  function readBatch(file) {
146440
146510
  let raw = null;
146441
146511
  if (file !== null) {
146442
- raw = readFileSync21(file, "utf8");
146512
+ raw = readFileSync22(file, "utf8");
146443
146513
  } else if (!process.stdin.isTTY) {
146444
- raw = readFileSync21(0, "utf8");
146514
+ raw = readFileSync22(0, "utf8");
146445
146515
  if (raw.trim().length === 0) raw = null;
146446
146516
  }
146447
146517
  if (raw === null) return null;
@@ -147689,9 +147759,9 @@ import { createHash as createHash12 } from "node:crypto";
147689
147759
  import {
147690
147760
  existsSync as existsSync17,
147691
147761
  mkdirSync as mkdirSync14,
147692
- readFileSync as readFileSync22,
147762
+ readFileSync as readFileSync23,
147693
147763
  unlinkSync,
147694
- writeFileSync as writeFileSync14
147764
+ writeFileSync as writeFileSync15
147695
147765
  } from "node:fs";
147696
147766
  import { dirname as dirname11, join as join18 } from "node:path";
147697
147767
  function validatePath(path) {
@@ -147702,7 +147772,7 @@ function validatePath(path) {
147702
147772
  }
147703
147773
  function readManifest(path) {
147704
147774
  if (!existsSync17(path)) return null;
147705
- const data = JSON.parse(readFileSync22(path, "utf8"));
147775
+ const data = JSON.parse(readFileSync23(path, "utf8"));
147706
147776
  if (data === null || typeof data !== "object" || !("schemaVersion" in data) || data.schemaVersion !== 1)
147707
147777
  throw new Error(`Invalid generated-file manifest version: ${path}`);
147708
147778
  if (!("files" in data) || !Array.isArray(data.files) || data.files.length === 0)
@@ -147782,7 +147852,7 @@ function writeUnityGeneratedFiles(directory, projectId, files) {
147782
147852
  const meta = join18(directory, old.path + ".meta");
147783
147853
  operations.push([
147784
147854
  path + ".meta",
147785
- existsSync17(meta) ? readFileSync22(meta, "utf8") : null
147855
+ existsSync17(meta) ? readFileSync23(meta, "utf8") : null
147786
147856
  ]);
147787
147857
  }
147788
147858
  operations.push([path, file.content]);
@@ -147809,7 +147879,7 @@ function writeUnityGeneratedFiles(directory, projectId, files) {
147809
147879
  if (!previous.has(key))
147810
147880
  previous.set(key, {
147811
147881
  path,
147812
- content: existsSync17(path) ? readFileSync22(path, "utf8") : null
147882
+ content: existsSync17(path) ? readFileSync23(path, "utf8") : null
147813
147883
  });
147814
147884
  }
147815
147885
  const write = (path, content) => {
@@ -147817,13 +147887,13 @@ function writeUnityGeneratedFiles(directory, projectId, files) {
147817
147887
  if (existsSync17(path)) unlinkSync(path);
147818
147888
  } else {
147819
147889
  mkdirSync14(dirname11(path), { recursive: true });
147820
- writeFileSync14(path, content);
147890
+ writeFileSync15(path, content);
147821
147891
  }
147822
147892
  };
147823
147893
  const changed = [];
147824
147894
  try {
147825
147895
  for (const [path, content] of operations) {
147826
- const current = existsSync17(path) ? readFileSync22(path, "utf8") : null;
147896
+ const current = existsSync17(path) ? readFileSync23(path, "utf8") : null;
147827
147897
  if (current === content) continue;
147828
147898
  changed.push(path);
147829
147899
  write(path, content);
@@ -147836,7 +147906,7 @@ function writeUnityGeneratedFiles(directory, projectId, files) {
147836
147906
  restored.add(key);
147837
147907
  const original = previous.get(key);
147838
147908
  if (original.path !== path && existsSync17(path)) unlinkSync(path);
147839
- const current = existsSync17(original.path) ? readFileSync22(original.path, "utf8") : null;
147909
+ const current = existsSync17(original.path) ? readFileSync23(original.path, "utf8") : null;
147840
147910
  if (current !== original.content) write(original.path, original.content);
147841
147911
  }
147842
147912
  throw error;
@@ -147853,7 +147923,7 @@ var export_exports = {};
147853
147923
  __export(export_exports, {
147854
147924
  runExportUnity: () => runExportUnity
147855
147925
  });
147856
- import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "node:fs";
147926
+ import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync16 } from "node:fs";
147857
147927
  import { join as join19 } from "node:path";
147858
147928
  async function runExportUnity(workspace, outDir) {
147859
147929
  if (outDir === null) {
@@ -147876,9 +147946,9 @@ async function runExportUnity(workspace, outDir) {
147876
147946
  response.projectId,
147877
147947
  response.generatedFiles
147878
147948
  );
147879
- writeFileSync15(join19(resourcesDir, "project.json"), response.projectJson);
147949
+ writeFileSync16(join19(resourcesDir, "project.json"), response.projectJson);
147880
147950
  for (const file of response.localizationFiles ?? []) {
147881
- writeFileSync15(join19(localizationDir, file.fileName), file.content);
147951
+ writeFileSync16(join19(localizationDir, file.fileName), file.content);
147882
147952
  }
147883
147953
  console.log(`wrote ${join19(resourcesDir, "project.json")}`);
147884
147954
  console.log(`synchronized generated files in ${scriptsDir}`);
@@ -148077,7 +148147,7 @@ __export(resolve_exports, {
148077
148147
  resolveMarkers: () => resolveMarkers,
148078
148148
  runResolve: () => runResolve
148079
148149
  });
148080
- import { readFileSync as readFileSync23, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "node:fs";
148150
+ import { readFileSync as readFileSync24, rmSync as rmSync10, writeFileSync as writeFileSync17 } from "node:fs";
148081
148151
  import { join as join21, relative as relative11 } from "node:path";
148082
148152
  function runResolve(workspace, side) {
148083
148153
  const marked = readMarkedProjectSourcesV4(workspace.root);
@@ -148088,7 +148158,7 @@ function runResolve(workspace, side) {
148088
148158
  const resolvedRecords = adoptServerConflictBases(workspace);
148089
148159
  let resolvedFiles = 0;
148090
148160
  for (const file of marked) {
148091
- writeFileSync16(file.absolutePath, resolveMarkers(file.source, side), "utf8");
148161
+ writeFileSync17(file.absolutePath, resolveMarkers(file.source, side), "utf8");
148092
148162
  resolvedFiles += 1;
148093
148163
  }
148094
148164
  let resolvedBinaries = 0;
@@ -148101,7 +148171,7 @@ function runResolve(workspace, side) {
148101
148171
  if (conflict2.remoteSha256 !== null && conflict2.artifactPath !== void 0) {
148102
148172
  writeVerifiedBinaryDownloadV4(
148103
148173
  destination,
148104
- readFileSync23(join21(workspace.root, conflict2.artifactPath)),
148174
+ readFileSync24(join21(workspace.root, conflict2.artifactPath)),
148105
148175
  conflict2.remoteSha256
148106
148176
  );
148107
148177
  binary.sha256 = conflict2.remoteSha256;
@@ -148130,7 +148200,7 @@ function runResolve(workspace, side) {
148130
148200
  function readMarkedProjectSourcesV4(root) {
148131
148201
  const files = [];
148132
148202
  for (const absolutePath of listProjectSourceFilesV4(root)) {
148133
- const source = readFileSync23(absolutePath, "utf8");
148203
+ const source = readFileSync24(absolutePath, "utf8");
148134
148204
  if (detectConflictMarkers(source) === null) continue;
148135
148205
  files.push({
148136
148206
  path: normalizeWorkspaceSourcePath(relative11(root, absolutePath)),
@@ -148431,7 +148501,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
148431
148501
  async function main() {
148432
148502
  const args = parseArgs(process.argv.slice(2));
148433
148503
  if (args.command === "--version") {
148434
- console.log("0.58.1");
148504
+ console.log("0.58.2");
148435
148505
  return;
148436
148506
  }
148437
148507
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.58.1",
3
+ "version": "0.58.2",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.58.1 -->
12
+ <!-- reviewed-through-cli: 0.58.2 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -97,7 +97,7 @@ wrappers.
97
97
  The marker near the top of `SKILL.md` must exactly match the package version:
98
98
 
99
99
  ```html
100
- <!-- reviewed-through-cli: 0.58.1 -->
100
+ <!-- reviewed-through-cli: 0.58.2 -->
101
101
  ```
102
102
 
103
103
  The quoted version above is checked too, so this instruction cannot go stale
@@ -61,6 +61,11 @@ deliberately replace a checkout whose state is gone, back it up first and use
61
61
  `neo pull --force`; the CLI recovers custom paths from the remaining declarations.
62
62
  The force pull replaces local source with the server version.
63
63
 
64
+ Pull downloads up to eight managed image or audio files concurrently and reuses
65
+ matching local bytes. It verifies all downloads before replacing binaries or
66
+ updating conflict artifacts. A failed transfer leaves existing files intact;
67
+ retry the pull after resolving the reported download error.
68
+
64
69
  Pull pins every page and snapshot batch to one published generation and logical
65
70
  revision. If either changes during assembly, the CLI discards the attempt and
66
71
  restarts. Upgrade the CLI together with the P83 server read protocol.