castle-web-cli 0.4.98 → 0.4.100

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/dist/imports.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import * as fs from 'fs';
2
- import * as os from 'os';
3
2
  import * as path from 'path';
4
3
  import { nanoid } from 'nanoid';
5
4
  import * as api from './api.js';
@@ -248,20 +247,32 @@ export async function restoreMissingImports(deckDir) {
248
247
  }
249
248
  return restored;
250
249
  }
251
- // Download an archive and unpack it at `destDir`, via a temp dir so a failure
252
- // partway leaves nothing behind.
250
+ // Download an archive and unpack it at `destDir`, replacing whatever is there.
251
+ //
252
+ // Staged NEXT TO destDir rather than in the OS temp dir. In a sandbox `/tmp` and
253
+ // the workspace are separate filesystems, and renaming across them fails with
254
+ // EXDEV -- which, when the old copy had already been cleared to make room, left
255
+ // the deck with no kit at all. Staging inside `imports/` keeps the move on one
256
+ // filesystem, and nothing existing is touched until the new copy is unpacked and
257
+ // whole: a failed download or a bad archive now leaves the deck exactly as it
258
+ // was.
253
259
  async function fetchDeckSourceInto(archiveUrl, destDir) {
254
- const res = await fetch(archiveUrl, { signal: AbortSignal.timeout(60000) });
255
- if (!res.ok)
256
- throw new Error(`Archive fetch failed: HTTP ${res.status}`);
257
- const buf = Buffer.from(await res.arrayBuffer());
258
- const tmpFile = path.join(os.tmpdir(), `castle-import-${nanoid(8)}.tar.gz`);
259
- const tmpDir = path.join(os.tmpdir(), `castle-import-${nanoid(8)}`);
260
- fs.writeFileSync(tmpFile, buf);
260
+ const importsDir = path.dirname(destDir);
261
+ fs.mkdirSync(importsDir, { recursive: true });
262
+ const tmpFile = path.join(importsDir, `.castle-import-${nanoid(8)}.tar.gz`);
263
+ const tmpDir = path.join(importsDir, `.castle-import-${nanoid(8)}`);
261
264
  fs.mkdirSync(tmpDir, { recursive: true });
262
265
  try {
266
+ const res = await fetch(archiveUrl, { signal: AbortSignal.timeout(60000) });
267
+ if (!res.ok)
268
+ throw new Error(`Archive fetch failed: HTTP ${res.status}`);
269
+ fs.writeFileSync(tmpFile, Buffer.from(await res.arrayBuffer()));
263
270
  await runTar(['-xzf', tmpFile, '-C', tmpDir]);
264
- fs.mkdirSync(path.dirname(destDir), { recursive: true });
271
+ // The new copy is whole; only now is it safe to drop the old one.
272
+ if (fs.existsSync(destDir)) {
273
+ unlockImportTree(destDir);
274
+ fs.rmSync(destDir, { recursive: true, force: true });
275
+ }
265
276
  fs.renameSync(tmpDir, destDir);
266
277
  }
267
278
  finally {
@@ -347,11 +358,11 @@ function backupImport(deckDir, alias) {
347
358
  async function placeImport(deckDir, alias, deckId, source, via) {
348
359
  const destDir = path.join(deckDir, IMPORTS_DIR, alias);
349
360
  const replaced = fs.existsSync(destDir);
350
- if (replaced) {
361
+ // Back up first, but leave the old copy in place: fetchDeckSourceInto only
362
+ // clears it once the replacement is unpacked, so a failed update is a no-op
363
+ // rather than a deck with its kit missing.
364
+ if (replaced)
351
365
  backupImport(deckDir, alias);
352
- unlockImportTree(destDir);
353
- fs.rmSync(destDir, { recursive: true, force: true });
354
- }
355
366
  await fetchDeckSourceInto(source.archiveUrl, destDir);
356
367
  lockImportTree(destDir);
357
368
  clearStatusCache(deckDir);
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { serve } from './serve.js';
6
6
  import { saveDeck } from './save-deck.js';
7
7
  import { getDeck } from './get-deck.js';
8
8
  import { addImport, updateImport } from './imports.js';
9
- import { init } from './init.js';
9
+ import { getCliVersion, init } from './init.js';
10
10
  import { install } from './install.js';
11
11
  import { connectWS, savePreviewImage, savePreviewIfNeeded, takeScreenshot } from './preview.js';
12
12
  const args = process.argv.slice(2);
@@ -86,6 +86,7 @@ function usage() {
86
86
  castle-web update-import [alias] [dir] [--check] [--revert] (re-fetches imports; no alias means all)
87
87
  castle-web install [dir]
88
88
  castle-web login
89
+ castle-web --version
89
90
 
90
91
  Kits:
91
92
  basic-2d Actor / behavior / scene framework for 2D games. Includes an editor and Layout / Drawing / Collider / Camera built-ins. Default if --kit is omitted.
@@ -95,6 +96,13 @@ After init, read the scaffolded deck's CLAUDE.md (also AGENTS.md) for the kit's
95
96
  process.exit(1);
96
97
  }
97
98
  async function main() {
99
+ // Before the switch: `--version` is a flag, not a command, and it has to work
100
+ // when there is no command at all. Which version is installed is the first
101
+ // thing anyone asks when a sandbox behaves like an older build.
102
+ if (command === '--version' || command === '-v' || command === 'version') {
103
+ console.log(getCliVersion());
104
+ return;
105
+ }
98
106
  switch (command) {
99
107
  case 'init': {
100
108
  const dir = findPositionalDir();
package/dist/init.d.ts CHANGED
@@ -5,6 +5,7 @@ export declare function resolveScaffoldRefs(): {
5
5
  cliDistAbs: string | null;
6
6
  sdkPathPosix: string | null;
7
7
  };
8
+ export declare function getCliVersion(): string;
8
9
  export declare function init(dir: string, opts?: {
9
10
  kit?: string;
10
11
  serve?: boolean;
package/dist/init.js CHANGED
@@ -237,7 +237,7 @@ function readJsonFile(file) {
237
237
  function writeJsonFile(file, value) {
238
238
  fs.writeFileSync(file, JSON.stringify(value, null, 2) + "\n", "utf8");
239
239
  }
240
- function getCliVersion() {
240
+ export function getCliVersion() {
241
241
  const pkg = readJsonFile(path.join(getKitsDir(), "..", "package.json"));
242
242
  return typeof pkg?.version === "string" ? pkg.version : "0.0.0";
243
243
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "castle-web-cli",
3
- "version": "0.4.98",
3
+ "version": "0.4.100",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "castle-web": "./dist/index.js"