castle-web-cli 0.4.38 → 0.4.40
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/index.js +0 -8
- package/dist/init.d.ts +0 -18
- package/dist/init.js +95 -130
- package/package.json +1 -1
- package/kits/basic-2d/published-deck.tgz +0 -0
- package/kits/basic-3d/published-deck.tgz +0 -0
package/dist/index.js
CHANGED
|
@@ -135,14 +135,6 @@ async function main() {
|
|
|
135
135
|
case 'login':
|
|
136
136
|
await login();
|
|
137
137
|
break;
|
|
138
|
-
case 'build-archives': {
|
|
139
|
-
// Dev/publish tool: generate the prebuilt per-kit deck archives that
|
|
140
|
-
// `init` extracts in published mode. Run after `npm run build -w cli`.
|
|
141
|
-
const { buildKitArchives } = await import('./buildArchive.js');
|
|
142
|
-
const only = getFlagValue('--kit');
|
|
143
|
-
buildKitArchives(only ? [only] : undefined);
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
138
|
default:
|
|
147
139
|
usage();
|
|
148
140
|
}
|
package/dist/init.d.ts
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
export declare const PUBLISHED_SDK_VERSION = "0.4.4";
|
|
2
|
-
export declare const KIT_COPY_EXCLUDE: Set<string>;
|
|
3
|
-
export declare const ARCHIVE_NAME = "published-deck.tgz";
|
|
4
|
-
export declare function getKitArchivePath(kit: string): string;
|
|
5
|
-
export declare function copyKitSource(kitDir: string, dest: string, extraExclude?: Set<string>): void;
|
|
6
|
-
export declare function rewriteKitPackageJson(pkg: {
|
|
7
|
-
name?: string;
|
|
8
|
-
dependencies?: Record<string, string>;
|
|
9
|
-
scripts?: Record<string, string>;
|
|
10
|
-
}, name: string, refs: ReturnType<typeof resolveScaffoldRefs>): void;
|
|
11
|
-
declare function resolveScaffoldRefs(): {
|
|
12
|
-
workspaceMode: boolean;
|
|
13
|
-
sdkRef: string;
|
|
14
|
-
cliCommand: string;
|
|
15
|
-
cliDistAbs: string | null;
|
|
16
|
-
sdkPathPosix: string | null;
|
|
17
|
-
};
|
|
18
1
|
export declare function init(dir: string, opts?: {
|
|
19
2
|
kit?: string;
|
|
20
3
|
serve?: boolean;
|
|
21
4
|
}): Promise<void>;
|
|
22
|
-
export {};
|
package/dist/init.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { COMMON_INSTRUCTIONS } from './commonInstructions.js';
|
|
@@ -35,62 +35,10 @@ const DEFAULT_KIT = 'basic-2d';
|
|
|
35
35
|
// Registry version of castle-web-sdk to inject when scaffolding from a
|
|
36
36
|
// globally-installed castle-web (not from inside the workspace). Bumped
|
|
37
37
|
// alongside cli/sdk version bumps.
|
|
38
|
-
|
|
38
|
+
const PUBLISHED_SDK_VERSION = '0.4.4';
|
|
39
39
|
// Never copied into a fresh deck: build/dependency junk, and castle.json (a
|
|
40
40
|
// fresh deck has no deckId until its first save-deck).
|
|
41
|
-
|
|
42
|
-
// A globally-installed castle-web ships one prebuilt, ready-to-serve archive
|
|
43
|
-
// per kit (kit source + installed node_modules + published-mode package.json),
|
|
44
|
-
// gzipped. `init` in published mode extracts it instead of cp + `npm install`.
|
|
45
|
-
// Lives inside each bundled kit dir (cli/kits/<kit>/), generated by
|
|
46
|
-
// `build-archives` (see buildArchive.ts). Workspace mode never uses it.
|
|
47
|
-
export const ARCHIVE_NAME = 'published-deck.tgz';
|
|
48
|
-
export function getKitArchivePath(kit) {
|
|
49
|
-
return path.join(getKitsDir(), kit, ARCHIVE_NAME);
|
|
50
|
-
}
|
|
51
|
-
// Copy a framework kit into `dest`, dropping build/dependency junk + castle.json
|
|
52
|
-
// (and any caller-supplied extra names, e.g. a stale archive). Shared by the
|
|
53
|
-
// live scaffold and archive generation so the two stay in sync.
|
|
54
|
-
export function copyKitSource(kitDir, dest, extraExclude) {
|
|
55
|
-
fs.cpSync(kitDir, dest, {
|
|
56
|
-
recursive: true,
|
|
57
|
-
// Keep symlinks verbatim so the kit's AGENTS.md -> CLAUDE.md stays a link.
|
|
58
|
-
verbatimSymlinks: true,
|
|
59
|
-
filter: (src) => src === kitDir ||
|
|
60
|
-
(!KIT_COPY_EXCLUDE.has(path.basename(src)) && !extraExclude?.has(path.basename(src))),
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
// Rewrite a kit's package.json (in place) so the scaffolded deck references the
|
|
64
|
-
// sdk + cli per the resolved mode. Workspace mode points at the local checkout
|
|
65
|
-
// (file: sdk + node <abs cli dist>); published mode points at the registry sdk
|
|
66
|
-
// + the `castle-web` binary. Shared by the live scaffold and archive generation.
|
|
67
|
-
export function rewriteKitPackageJson(pkg, name, refs) {
|
|
68
|
-
pkg.name = name;
|
|
69
|
-
const { workspaceMode, sdkRef, cliDistAbs, sdkPathPosix } = refs;
|
|
70
|
-
if (pkg.dependencies &&
|
|
71
|
-
typeof pkg.dependencies['castle-web-sdk'] === 'string' &&
|
|
72
|
-
pkg.dependencies['castle-web-sdk'].startsWith('file:')) {
|
|
73
|
-
pkg.dependencies['castle-web-sdk'] = sdkRef;
|
|
74
|
-
}
|
|
75
|
-
if (!pkg.scripts)
|
|
76
|
-
return;
|
|
77
|
-
for (const k of Object.keys(pkg.scripts)) {
|
|
78
|
-
if (typeof pkg.scripts[k] !== 'string')
|
|
79
|
-
continue;
|
|
80
|
-
if (workspaceMode) {
|
|
81
|
-
pkg.scripts[k] = pkg.scripts[k]
|
|
82
|
-
.replace(/\.\.\/\.\.\/cli\/dist/g, cliDistAbs)
|
|
83
|
-
.replace(/\.\.\/\.\.\/sdk/g, sdkPathPosix);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
// Globally-installed: route through the `castle-web` binary on PATH.
|
|
87
|
-
pkg.scripts[k] = pkg.scripts[k]
|
|
88
|
-
.replace(/node\s+\.\.\/\.\.\/cli\/dist\/index\.js/g, 'castle-web')
|
|
89
|
-
.replace(/await import\((['"])\.\.\/\.\.\/cli\/dist\/bundle\.js\1\)/g, "await import('castle-web-cli/dist/bundle.js')")
|
|
90
|
-
.replace(/\.\.\/\.\.\/sdk/g, '');
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
41
|
+
const KIT_COPY_EXCLUDE = new Set(['node_modules', '.castle', 'dist', '.git', 'castle.json']);
|
|
94
42
|
// Resolve how a scaffolded deck should reference the sdk + cli. Both the bare
|
|
95
43
|
// and kit scaffold paths go through here so they stay in sync.
|
|
96
44
|
// workspace mode (sdk/ sits next to cli/, i.e. running from a checkout):
|
|
@@ -179,42 +127,8 @@ function scaffoldBare(projectDir) {
|
|
|
179
127
|
ensureAgentsSymlink(projectDir);
|
|
180
128
|
fs.writeFileSync(path.join(projectDir, 'package.json'), JSON.stringify(makePackageJson(projectDir), null, 2) + '\n');
|
|
181
129
|
}
|
|
182
|
-
// Finalize a deck whose files are already on disk (mirroring scaffoldFromKit's
|
|
183
|
-
// tail): ensure a CLAUDE.md, append the cli-owned common guidance, and create
|
|
184
|
-
// the AGENTS.md symlink. Used by both the cp path and the archive-extract path.
|
|
185
|
-
function finalizeDeckDocs(projectDir) {
|
|
186
|
-
// Every deck needs a CLAUDE.md so coding agents know how castle-web works.
|
|
187
|
-
// Keep the kit's own if it ships one; otherwise generate from the upstream.
|
|
188
|
-
const claudePath = path.join(projectDir, 'CLAUDE.md');
|
|
189
|
-
if (!fs.existsSync(claudePath)) {
|
|
190
|
-
fs.writeFileSync(claudePath, makeClaudeMd());
|
|
191
|
-
}
|
|
192
|
-
appendCommonInstructions(projectDir);
|
|
193
|
-
ensureAgentsSymlink(projectDir);
|
|
194
|
-
}
|
|
195
|
-
// Published fast path: extract the prebuilt, ready-to-serve archive into the
|
|
196
|
-
// deck dir (no cp, no `npm install`). The archive carries the kit source +
|
|
197
|
-
// installed node_modules + a published-mode package.json with a generic name;
|
|
198
|
-
// rename it to the deck dir, then run the shared doc finalization.
|
|
199
|
-
function extractKitArchive(archivePath, projectDir) {
|
|
200
|
-
fs.mkdirSync(projectDir, { recursive: true });
|
|
201
|
-
execFileSync('tar', ['-xzf', archivePath, '-C', projectDir], { stdio: 'inherit' });
|
|
202
|
-
const pkgPath = path.join(projectDir, 'package.json');
|
|
203
|
-
if (fs.existsSync(pkgPath)) {
|
|
204
|
-
try {
|
|
205
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
206
|
-
pkg.name = path.basename(projectDir);
|
|
207
|
-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
208
|
-
}
|
|
209
|
-
catch {
|
|
210
|
-
// archived an unparseable package.json — leave it for the user to fix
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
finalizeDeckDocs(projectDir);
|
|
214
|
-
}
|
|
215
130
|
// Copy a framework kit from kits/<kit>/ into the new deck dir, dropping
|
|
216
|
-
// build/dependency junk and castle.json.
|
|
217
|
-
// installed (true only on the published archive-extract fast path).
|
|
131
|
+
// build/dependency junk and castle.json.
|
|
218
132
|
function scaffoldFromKit(kit, projectDir) {
|
|
219
133
|
const kitDir = path.join(getKitsDir(), kit);
|
|
220
134
|
if (!fs.existsSync(kitDir) || !fs.statSync(kitDir).isDirectory()) {
|
|
@@ -236,17 +150,12 @@ function scaffoldFromKit(kit, projectDir) {
|
|
|
236
150
|
console.error('Or use `--kit none` for a bare code-only deck.');
|
|
237
151
|
process.exit(1);
|
|
238
152
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
console.log('Extracting prebuilt deck (deps included)...');
|
|
246
|
-
extractKitArchive(archivePath, projectDir);
|
|
247
|
-
return { depsInstalled: true };
|
|
248
|
-
}
|
|
249
|
-
copyKitSource(kitDir, projectDir);
|
|
153
|
+
fs.cpSync(kitDir, projectDir, {
|
|
154
|
+
recursive: true,
|
|
155
|
+
// Keep symlinks verbatim so the kit's AGENTS.md -> CLAUDE.md stays a link.
|
|
156
|
+
verbatimSymlinks: true,
|
|
157
|
+
filter: (src) => src === kitDir || !KIT_COPY_EXCLUDE.has(path.basename(src)),
|
|
158
|
+
});
|
|
250
159
|
// The kit's package.json carries the kit's name; rename it to the deck dir.
|
|
251
160
|
// Kit-relative refs to `../../sdk` and `../../cli/dist` only resolve when the
|
|
252
161
|
// deck lives at castle-experimental-web/decks/<name>/. Rewrite both to
|
|
@@ -256,15 +165,81 @@ function scaffoldFromKit(kit, projectDir) {
|
|
|
256
165
|
if (fs.existsSync(pkgPath)) {
|
|
257
166
|
try {
|
|
258
167
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
259
|
-
|
|
168
|
+
pkg.name = path.basename(projectDir);
|
|
169
|
+
// Local-dev paths (`file:../../sdk` / `node ../../cli/dist/index.js`) only
|
|
170
|
+
// work when the deck lives inside the castle-experimental-web workspace.
|
|
171
|
+
// For a deck scaffolded from a globally-installed castle-web, rewrite to
|
|
172
|
+
// the published packages instead. Same workspace-vs-published resolution
|
|
173
|
+
// the bare scaffold path uses.
|
|
174
|
+
const { workspaceMode, sdkRef, cliDistAbs, sdkPathPosix } = resolveScaffoldRefs();
|
|
175
|
+
if (pkg.dependencies &&
|
|
176
|
+
typeof pkg.dependencies['castle-web-sdk'] === 'string' &&
|
|
177
|
+
pkg.dependencies['castle-web-sdk'].startsWith('file:')) {
|
|
178
|
+
pkg.dependencies['castle-web-sdk'] = sdkRef;
|
|
179
|
+
}
|
|
180
|
+
if (pkg.scripts) {
|
|
181
|
+
for (const k of Object.keys(pkg.scripts)) {
|
|
182
|
+
if (typeof pkg.scripts[k] !== 'string')
|
|
183
|
+
continue;
|
|
184
|
+
if (workspaceMode) {
|
|
185
|
+
pkg.scripts[k] = pkg.scripts[k]
|
|
186
|
+
.replace(/\.\.\/\.\.\/cli\/dist/g, cliDistAbs)
|
|
187
|
+
.replace(/\.\.\/\.\.\/sdk/g, sdkPathPosix);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// Globally-installed: route through the `castle-web` binary on PATH.
|
|
191
|
+
pkg.scripts[k] = pkg.scripts[k]
|
|
192
|
+
.replace(/node\s+\.\.\/\.\.\/cli\/dist\/index\.js/g, 'castle-web')
|
|
193
|
+
.replace(/await import\((['"])\.\.\/\.\.\/cli\/dist\/bundle\.js\1\)/g, "await import('castle-web-cli/dist/bundle.js')")
|
|
194
|
+
.replace(/\.\.\/\.\.\/sdk/g, '');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
260
198
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
261
199
|
}
|
|
262
200
|
catch {
|
|
263
201
|
// kit shipped an unparseable package.json — leave it for the user to fix
|
|
264
202
|
}
|
|
265
203
|
}
|
|
266
|
-
|
|
267
|
-
|
|
204
|
+
// Every deck needs a CLAUDE.md so coding agents know how castle-web works.
|
|
205
|
+
// Keep the kit's own if it ships one; otherwise generate from the upstream.
|
|
206
|
+
const claudePath = path.join(projectDir, 'CLAUDE.md');
|
|
207
|
+
if (!fs.existsSync(claudePath)) {
|
|
208
|
+
fs.writeFileSync(claudePath, makeClaudeMd());
|
|
209
|
+
}
|
|
210
|
+
appendCommonInstructions(projectDir);
|
|
211
|
+
ensureAgentsSymlink(projectDir);
|
|
212
|
+
}
|
|
213
|
+
function hasPnpm() {
|
|
214
|
+
try {
|
|
215
|
+
execSync('pnpm --version', { stdio: 'ignore' });
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Install the scaffolded deck's deps. Prefer pnpm -- in the e2b template a pnpm
|
|
223
|
+
// store is baked in, so this is near-instant (hardlinks from the store, no
|
|
224
|
+
// download). Fall back to npm when pnpm isn't on PATH (e.g. a laptop that never
|
|
225
|
+
// installed it). --prefer-offline uses the store/cache first; --ignore-scripts
|
|
226
|
+
// skips dep build scripts (pnpm 10 gates them and exits non-zero otherwise, and
|
|
227
|
+
// the deck's deps are all prebuilt pure JS that don't need them).
|
|
228
|
+
function installDeps(projectDir) {
|
|
229
|
+
if (hasPnpm()) {
|
|
230
|
+
console.log('Installing deps (pnpm)...');
|
|
231
|
+
execSync('pnpm install --prefer-offline --ignore-scripts', {
|
|
232
|
+
cwd: projectDir,
|
|
233
|
+
stdio: 'inherit',
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
console.log('Installing deps (npm)...');
|
|
238
|
+
execSync('npm install --no-audit --no-fund --loglevel=error', {
|
|
239
|
+
cwd: projectDir,
|
|
240
|
+
stdio: 'inherit',
|
|
241
|
+
});
|
|
242
|
+
}
|
|
268
243
|
}
|
|
269
244
|
export async function init(dir, opts = {}) {
|
|
270
245
|
const projectDir = path.resolve(dir);
|
|
@@ -274,37 +249,27 @@ export async function init(dir, opts = {}) {
|
|
|
274
249
|
}
|
|
275
250
|
const kit = opts.kit ?? DEFAULT_KIT;
|
|
276
251
|
const bare = kit === 'none' || kit === 'bare';
|
|
277
|
-
// depsInstalled is true only when the published archive fast-path ran, which
|
|
278
|
-
// already ships node_modules -- so we can skip `npm install` below.
|
|
279
|
-
let depsInstalled = false;
|
|
280
252
|
if (bare) {
|
|
281
253
|
scaffoldBare(projectDir);
|
|
282
254
|
}
|
|
283
255
|
else {
|
|
284
|
-
|
|
256
|
+
scaffoldFromKit(kit, projectDir);
|
|
285
257
|
}
|
|
286
258
|
console.log(`Created project in ${projectDir}/${bare ? '' : ` (from kit "${kit}")`}`);
|
|
287
|
-
//
|
|
288
|
-
//
|
|
259
|
+
// Always install deps so the deck is ready to serve/edit immediately.
|
|
260
|
+
// `--no-serve` only skips the serve step below (callers like the cloud
|
|
261
|
+
// launcher run their own serve, but still want deps in place).
|
|
262
|
+
console.log('');
|
|
263
|
+
let installed = false;
|
|
264
|
+
try {
|
|
265
|
+
installDeps(projectDir);
|
|
266
|
+
installed = true;
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
console.error('dependency install failed; re-run `pnpm install` (or `npm install`) in the deck.');
|
|
270
|
+
}
|
|
289
271
|
const autoServe = opts.serve !== false;
|
|
290
|
-
if (autoServe) {
|
|
291
|
-
console.log('');
|
|
292
|
-
if (!depsInstalled) {
|
|
293
|
-
console.log('Installing deps + serving (pass --no-serve to skip)...');
|
|
294
|
-
try {
|
|
295
|
-
execSync('npm install --no-audit --no-fund --loglevel=error', {
|
|
296
|
-
cwd: projectDir,
|
|
297
|
-
stdio: 'inherit',
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
console.error('npm install failed; skipping serve. Re-run yourself with `npm install && castle-web serve .` (& in your shell to background it).');
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
console.log('Serving (deps prebuilt)...');
|
|
307
|
-
}
|
|
272
|
+
if (autoServe && installed) {
|
|
308
273
|
// Call serve() with detach so init returns once the server is up. serve()
|
|
309
274
|
// handles the background spawn internally; init doesn't shell out.
|
|
310
275
|
// Bind all interfaces by default so a tailnet / LAN browser can reach
|
|
@@ -320,7 +285,7 @@ export async function init(dir, opts = {}) {
|
|
|
320
285
|
console.log('');
|
|
321
286
|
console.log('Next steps:');
|
|
322
287
|
console.log(` cd ${dir}`);
|
|
323
|
-
if (!
|
|
324
|
-
console.log(' npm install');
|
|
288
|
+
if (!installed)
|
|
289
|
+
console.log(' pnpm install # or: npm install');
|
|
325
290
|
console.log(' castle-web serve . # & in your shell to background it');
|
|
326
291
|
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|