arcane-os 0.97.0 → 0.97.1

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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.97.1
4
+
5
+ - Extract repeated selected TAR members in order, retaining the last complete
6
+ occurrence instead of rejecting valid upstream model companion archives.
7
+ - Preserve earlier extracted files through interrupted replacements and replay
8
+ unfinished archive selections on retry. Completed archive selections remain
9
+ reusable while independent model files resume their own transfers.
10
+
3
11
  ## 0.97.0
4
12
 
5
13
  - Share native model storage across applications for the same operating-system
package/README.md CHANGED
@@ -25,7 +25,7 @@ selected SDK projection or an explicit live SDK source mount. Both profiles
25
25
  share the theme, packaging, event, cancellation,
26
26
  and browser run contracts while retaining their selected application layout.
27
27
 
28
- This checkout defines the `0.97.0` SDK contract. Development applications declare
28
+ This checkout defines the `0.97.1` SDK contract. Development applications declare
29
29
  `arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
30
30
  designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
31
31
  to refresh and compare the installed SDK and lockfile with the published channel;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.96.0",
3
+ "sdkVersion": "0.97.0",
4
4
  "environment": {
5
5
  "runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
6
6
  "minimumVersion": "22.23.2 for Node entrypoints",
@@ -183,6 +183,13 @@ path records select members; a leading `./` is archive path framing. Missing
183
183
  selected members reject preparation and identify the missing names. Gzip and
184
184
  other archive formats are outside this reader's contract.
185
185
 
186
+ Repeated selected TAR names follow sequential extraction: the last complete
187
+ occurrence replaces the earlier file only after closing its temporary output.
188
+ An interrupted archive pass retains those files and replays its selected
189
+ segments on retry, so an earlier occurrence is never mistaken for the final
190
+ one. Fully completed archive selections are reused while other model files
191
+ continue their own resumable downloads.
192
+
186
193
  When the upstream catalog supplies aligned TAR segments, an archive may include
187
194
  `ranges:[{start,end,stopBefore?}]`. The reader requests each inclusive HTTP range
188
195
  once and parses its TAR framing; `stopBefore` ends that segment before the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.97.0",
3
+ "version": "0.97.1",
4
4
  "description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -503,9 +503,8 @@ async function extractTarRange(response, range, selected, found, entry, progress
503
503
  if (range?.stopBefore && (memberName === range.stopBefore || path.posix.basename(memberName) === range.stopBefore)) break;
504
504
  const memberIndexes = selected.get(memberName);
505
505
  if (memberIndexes && (type === '0' || type === '\0')) {
506
- if (memberIndexes.some(function alreadyRead(index) { return found.has(index); })) {
507
- throw failure('MODEL_ASSET_ARCHIVE_FORMAT', `The selected TAR member occurs more than once: ${memberName}`);
508
- }
506
+ // TAR extraction is ordered: a later complete occurrence replaces
507
+ // the earlier file only after its own stream and file close.
509
508
  const handles = [];
510
509
  const errors = [];
511
510
  try {
@@ -744,6 +743,7 @@ async function acquireNativeMembers(entry) {
744
743
  state.transports ??= {};
745
744
  state.completedMembers = current?.completedMembers ?? [];
746
745
  const finished = new Set(state.completedMembers);
746
+ const completedArchiveMembers = new Set(state.completedArchiveMembers ?? []);
747
747
  completed = finished.size;
748
748
  await mkdir(path.join(selected.root, 'parts'), {recursive: true});
749
749
  progress('open');
@@ -786,17 +786,22 @@ async function acquireNativeMembers(entry) {
786
786
  let archiveIndex = members.findIndex(function archiveMember(member) { return member.archiveMember !== undefined; });
787
787
  for (const archive of entry.archives) {
788
788
  const selected = new Map();
789
+ const archiveMembers = [];
789
790
  for (const member of archive.members) {
790
791
  const memberIndex = archiveIndex++;
791
- if (finished.has(memberIndex)) continue;
792
+ archiveMembers.push(memberIndex);
792
793
  const name = tarName(member.member);
793
794
  if (!selected.has(name)) selected.set(name, []);
794
795
  selected.get(name).push(memberIndex);
795
796
  }
796
797
  if (!selected.size) continue;
798
+ if (archiveMembers.every(function confirmed(index) { return finished.has(index) && completedArchiveMembers.has(index); })) continue;
799
+ for (const memberIndex of archiveMembers) completedArchiveMembers.delete(memberIndex);
797
800
  jobs.push(async function downloadArchiveMembers() {
798
801
  const found = new Set();
799
802
  try {
803
+ state.completedArchiveMembers = [...completedArchiveMembers];
804
+ await save();
800
805
  for (const range of archive.ranges ?? [undefined]) {
801
806
  signal.throwIfAborted();
802
807
  const response = await fetchModelResponse(archive.url, signal, range);
@@ -811,6 +816,11 @@ async function acquireNativeMembers(entry) {
811
816
  error.members = missing;
812
817
  throw error;
813
818
  }
819
+ // A file written before a later TAR occurrence is retained
820
+ // on interruption, but only a complete pass confirms reuse.
821
+ for (const memberIndex of archiveMembers) completedArchiveMembers.add(memberIndex);
822
+ state.completedArchiveMembers = [...completedArchiveMembers];
823
+ await save();
814
824
  } catch (error) {
815
825
  noteFailure(error, error.code?.startsWith('MODEL_ASSET_ARCHIVE_') ? 'failed' : 'interrupted');
816
826
  throw error;