castle-web-cli 0.4.38 → 0.4.39
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 +145 -167
- 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,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as fs from
|
|
3
|
-
import * as path from
|
|
4
|
-
import { COMMON_INSTRUCTIONS } from
|
|
5
|
-
import { getCliEntryPath, getKitsDir, getRepoRoot, getSdkPackagePath, toPosixPath } from
|
|
6
|
-
import { serve } from
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { COMMON_INSTRUCTIONS } from "./commonInstructions.js";
|
|
5
|
+
import { getCliEntryPath, getKitsDir, getRepoRoot, getSdkPackagePath, toPosixPath, } from "./localPaths.js";
|
|
6
|
+
import { serve } from "./serve.js";
|
|
7
7
|
const INDEX_HTML = `<!DOCTYPE html>
|
|
8
8
|
<html>
|
|
9
9
|
<head>
|
|
@@ -31,66 +31,20 @@ card.appendChild(el);
|
|
|
31
31
|
`;
|
|
32
32
|
// Default kit copied by `init` when no --kit is given. `none`/`bare` skip the
|
|
33
33
|
// kit and produce the minimal index.html + game.js stub above.
|
|
34
|
-
const DEFAULT_KIT =
|
|
34
|
+
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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([
|
|
42
|
+
"node_modules",
|
|
43
|
+
".castle",
|
|
44
|
+
"dist",
|
|
45
|
+
".git",
|
|
46
|
+
"castle.json",
|
|
47
|
+
]);
|
|
94
48
|
// Resolve how a scaffolded deck should reference the sdk + cli. Both the bare
|
|
95
49
|
// and kit scaffold paths go through here so they stay in sync.
|
|
96
50
|
// workspace mode (sdk/ sits next to cli/, i.e. running from a checkout):
|
|
@@ -104,11 +58,17 @@ function resolveScaffoldRefs() {
|
|
|
104
58
|
// npm package and need the published refs + `castle-web` binary.
|
|
105
59
|
const workspaceMode = fs.existsSync(sdkPath);
|
|
106
60
|
const sdkPathPosix = workspaceMode ? toPosixPath(sdkPath) : null;
|
|
107
|
-
const cliDistAbs = workspaceMode
|
|
61
|
+
const cliDistAbs = workspaceMode
|
|
62
|
+
? toPosixPath(path.dirname(getCliEntryPath()))
|
|
63
|
+
: null;
|
|
108
64
|
return {
|
|
109
65
|
workspaceMode,
|
|
110
|
-
sdkRef: workspaceMode
|
|
111
|
-
|
|
66
|
+
sdkRef: workspaceMode
|
|
67
|
+
? `file:${sdkPathPosix}`
|
|
68
|
+
: `^${PUBLISHED_SDK_VERSION}`,
|
|
69
|
+
cliCommand: workspaceMode
|
|
70
|
+
? `node ${toPosixPath(getCliEntryPath())}`
|
|
71
|
+
: "castle-web",
|
|
112
72
|
cliDistAbs,
|
|
113
73
|
sdkPathPosix,
|
|
114
74
|
};
|
|
@@ -118,9 +78,9 @@ function makeClaudeMd() {
|
|
|
118
78
|
// castle-experimental-web checkout, but breaks when the scaffold lives
|
|
119
79
|
// outside the repo (the relative path no longer resolves).
|
|
120
80
|
const repoRoot = getRepoRoot();
|
|
121
|
-
const upstream = path.join(repoRoot,
|
|
81
|
+
const upstream = path.join(repoRoot, "CLAUDE.md");
|
|
122
82
|
try {
|
|
123
|
-
return fs.readFileSync(upstream,
|
|
83
|
+
return fs.readFileSync(upstream, "utf8").trimEnd() + "\n";
|
|
124
84
|
}
|
|
125
85
|
catch {
|
|
126
86
|
return `# Castle Experimental Web\n\nSee https://github.com/castle-xyz/castle-experimental-web for the agent guide.\n`;
|
|
@@ -130,13 +90,15 @@ function makeClaudeMd() {
|
|
|
130
90
|
// the kit's (or bare) CLAUDE.md is written; the AGENTS.md symlink picks the
|
|
131
91
|
// appended content up for free.
|
|
132
92
|
function appendCommonInstructions(projectDir) {
|
|
133
|
-
const claudePath = path.join(projectDir,
|
|
134
|
-
const existing = fs.existsSync(claudePath)
|
|
93
|
+
const claudePath = path.join(projectDir, "CLAUDE.md");
|
|
94
|
+
const existing = fs.existsSync(claudePath)
|
|
95
|
+
? fs.readFileSync(claudePath, "utf8").trimEnd() + "\n\n"
|
|
96
|
+
: "";
|
|
135
97
|
fs.writeFileSync(claudePath, existing + COMMON_INSTRUCTIONS);
|
|
136
98
|
}
|
|
137
99
|
function tryMakeAgentsSymlink(agentsPath) {
|
|
138
100
|
try {
|
|
139
|
-
fs.symlinkSync(
|
|
101
|
+
fs.symlinkSync("CLAUDE.md", agentsPath);
|
|
140
102
|
}
|
|
141
103
|
catch {
|
|
142
104
|
// symlink already exists / unsupported FS — non-fatal
|
|
@@ -147,79 +109,45 @@ function makePackageJson(projectDir) {
|
|
|
147
109
|
return {
|
|
148
110
|
name: path.basename(projectDir),
|
|
149
111
|
private: true,
|
|
150
|
-
type:
|
|
112
|
+
type: "module",
|
|
151
113
|
scripts: {
|
|
152
114
|
restart: `${cliCommand} restart .`,
|
|
153
115
|
screenshot: `${cliCommand} screenshot .`,
|
|
154
|
-
|
|
116
|
+
"save-deck": `${cliCommand} save-deck .`,
|
|
155
117
|
},
|
|
156
118
|
dependencies: {
|
|
157
|
-
|
|
119
|
+
"castle-web-sdk": sdkRef,
|
|
158
120
|
},
|
|
159
121
|
};
|
|
160
122
|
}
|
|
161
123
|
// Some coding agents read AGENTS.md by convention. Symlink so they get the
|
|
162
124
|
// same guidance without a duplicate copy.
|
|
163
125
|
function ensureAgentsSymlink(projectDir) {
|
|
164
|
-
const agentsPath = path.join(projectDir,
|
|
126
|
+
const agentsPath = path.join(projectDir, "AGENTS.md");
|
|
165
127
|
if (fs.lstatSync(agentsPath, { throwIfNoEntry: false }))
|
|
166
128
|
return;
|
|
167
129
|
// Don't create a dangling link — only symlink when CLAUDE.md is present.
|
|
168
|
-
if (!fs.existsSync(path.join(projectDir,
|
|
130
|
+
if (!fs.existsSync(path.join(projectDir, "CLAUDE.md")))
|
|
169
131
|
return;
|
|
170
132
|
tryMakeAgentsSymlink(agentsPath);
|
|
171
133
|
}
|
|
172
134
|
// Bare scaffold: a plain code-only deck with no kit framework.
|
|
173
135
|
function scaffoldBare(projectDir) {
|
|
174
136
|
fs.mkdirSync(projectDir, { recursive: true });
|
|
175
|
-
fs.writeFileSync(path.join(projectDir,
|
|
176
|
-
fs.writeFileSync(path.join(projectDir,
|
|
177
|
-
fs.writeFileSync(path.join(projectDir,
|
|
137
|
+
fs.writeFileSync(path.join(projectDir, "index.html"), INDEX_HTML);
|
|
138
|
+
fs.writeFileSync(path.join(projectDir, "game.js"), GAME_JS);
|
|
139
|
+
fs.writeFileSync(path.join(projectDir, "CLAUDE.md"), makeClaudeMd());
|
|
178
140
|
appendCommonInstructions(projectDir);
|
|
179
141
|
ensureAgentsSymlink(projectDir);
|
|
180
|
-
fs.writeFileSync(path.join(projectDir,
|
|
181
|
-
}
|
|
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);
|
|
142
|
+
fs.writeFileSync(path.join(projectDir, "package.json"), JSON.stringify(makePackageJson(projectDir), null, 2) + "\n");
|
|
214
143
|
}
|
|
215
144
|
// 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).
|
|
145
|
+
// build/dependency junk and castle.json.
|
|
218
146
|
function scaffoldFromKit(kit, projectDir) {
|
|
219
147
|
const kitDir = path.join(getKitsDir(), kit);
|
|
220
148
|
if (!fs.existsSync(kitDir) || !fs.statSync(kitDir).isDirectory()) {
|
|
221
149
|
console.error(`Kit "${kit}" not found at ${kitDir}.`);
|
|
222
|
-
console.error(
|
|
150
|
+
console.error("Available kits:");
|
|
223
151
|
try {
|
|
224
152
|
const kits = fs
|
|
225
153
|
.readdirSync(getKitsDir())
|
|
@@ -228,43 +156,103 @@ function scaffoldFromKit(kit, projectDir) {
|
|
|
228
156
|
for (const name of kits)
|
|
229
157
|
console.error(` ${name}`);
|
|
230
158
|
else
|
|
231
|
-
console.error(
|
|
159
|
+
console.error(" (none)");
|
|
232
160
|
}
|
|
233
161
|
catch {
|
|
234
|
-
console.error(
|
|
162
|
+
console.error(" (none — kits/ directory is missing)");
|
|
235
163
|
}
|
|
236
|
-
console.error(
|
|
164
|
+
console.error("Or use `--kit none` for a bare code-only deck.");
|
|
237
165
|
process.exit(1);
|
|
238
166
|
}
|
|
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);
|
|
167
|
+
fs.cpSync(kitDir, projectDir, {
|
|
168
|
+
recursive: true,
|
|
169
|
+
// Keep symlinks verbatim so the kit's AGENTS.md -> CLAUDE.md stays a link.
|
|
170
|
+
verbatimSymlinks: true,
|
|
171
|
+
filter: (src) => src === kitDir || !KIT_COPY_EXCLUDE.has(path.basename(src)),
|
|
172
|
+
});
|
|
250
173
|
// The kit's package.json carries the kit's name; rename it to the deck dir.
|
|
251
174
|
// Kit-relative refs to `../../sdk` and `../../cli/dist` only resolve when the
|
|
252
175
|
// deck lives at castle-experimental-web/decks/<name>/. Rewrite both to
|
|
253
176
|
// absolute paths so the scaffolded deck works anywhere -- including under
|
|
254
177
|
// /tmp where macOS's /tmp -> /private/tmp symlink breaks relative-path math.
|
|
255
|
-
const pkgPath = path.join(projectDir,
|
|
178
|
+
const pkgPath = path.join(projectDir, "package.json");
|
|
256
179
|
if (fs.existsSync(pkgPath)) {
|
|
257
180
|
try {
|
|
258
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath,
|
|
259
|
-
|
|
260
|
-
|
|
181
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
182
|
+
pkg.name = path.basename(projectDir);
|
|
183
|
+
// Local-dev paths (`file:../../sdk` / `node ../../cli/dist/index.js`) only
|
|
184
|
+
// work when the deck lives inside the castle-experimental-web workspace.
|
|
185
|
+
// For a deck scaffolded from a globally-installed castle-web, rewrite to
|
|
186
|
+
// the published packages instead. Same workspace-vs-published resolution
|
|
187
|
+
// the bare scaffold path uses.
|
|
188
|
+
const { workspaceMode, sdkRef, cliDistAbs, sdkPathPosix } = resolveScaffoldRefs();
|
|
189
|
+
if (pkg.dependencies &&
|
|
190
|
+
typeof pkg.dependencies["castle-web-sdk"] === "string" &&
|
|
191
|
+
pkg.dependencies["castle-web-sdk"].startsWith("file:")) {
|
|
192
|
+
pkg.dependencies["castle-web-sdk"] = sdkRef;
|
|
193
|
+
}
|
|
194
|
+
if (pkg.scripts) {
|
|
195
|
+
for (const k of Object.keys(pkg.scripts)) {
|
|
196
|
+
if (typeof pkg.scripts[k] !== "string")
|
|
197
|
+
continue;
|
|
198
|
+
if (workspaceMode) {
|
|
199
|
+
pkg.scripts[k] = pkg.scripts[k]
|
|
200
|
+
.replace(/\.\.\/\.\.\/cli\/dist/g, cliDistAbs)
|
|
201
|
+
.replace(/\.\.\/\.\.\/sdk/g, sdkPathPosix);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// Globally-installed: route through the `castle-web` binary on PATH.
|
|
205
|
+
pkg.scripts[k] = pkg.scripts[k]
|
|
206
|
+
.replace(/node\s+\.\.\/\.\.\/cli\/dist\/index\.js/g, "castle-web")
|
|
207
|
+
.replace(/await import\((['"])\.\.\/\.\.\/cli\/dist\/bundle\.js\1\)/g, "await import('castle-web-cli/dist/bundle.js')")
|
|
208
|
+
.replace(/\.\.\/\.\.\/sdk/g, "");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
261
213
|
}
|
|
262
214
|
catch {
|
|
263
215
|
// kit shipped an unparseable package.json — leave it for the user to fix
|
|
264
216
|
}
|
|
265
217
|
}
|
|
266
|
-
|
|
267
|
-
|
|
218
|
+
// Every deck needs a CLAUDE.md so coding agents know how castle-web works.
|
|
219
|
+
// Keep the kit's own if it ships one; otherwise generate from the upstream.
|
|
220
|
+
const claudePath = path.join(projectDir, "CLAUDE.md");
|
|
221
|
+
if (!fs.existsSync(claudePath)) {
|
|
222
|
+
fs.writeFileSync(claudePath, makeClaudeMd());
|
|
223
|
+
}
|
|
224
|
+
appendCommonInstructions(projectDir);
|
|
225
|
+
ensureAgentsSymlink(projectDir);
|
|
226
|
+
}
|
|
227
|
+
function hasPnpm() {
|
|
228
|
+
try {
|
|
229
|
+
execSync("pnpm --version", { stdio: "ignore" });
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Install the scaffolded deck's deps. Prefer pnpm -- in the e2b template a pnpm
|
|
237
|
+
// store is baked in, so `pnpm install` is near-instant (hardlinks from the
|
|
238
|
+
// store, no download). Fall back to npm when pnpm isn't on PATH (e.g. a local
|
|
239
|
+
// laptop that never installed it). --prefer-offline uses the store/cache first
|
|
240
|
+
// and only hits the network for anything missing.
|
|
241
|
+
function installDeps(projectDir) {
|
|
242
|
+
if (hasPnpm()) {
|
|
243
|
+
console.log("Installing deps (pnpm)...");
|
|
244
|
+
execSync("pnpm install --prefer-offline", {
|
|
245
|
+
cwd: projectDir,
|
|
246
|
+
stdio: "inherit",
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
console.log("Installing deps (npm)...");
|
|
251
|
+
execSync("npm install --no-audit --no-fund --loglevel=error", {
|
|
252
|
+
cwd: projectDir,
|
|
253
|
+
stdio: "inherit",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
268
256
|
}
|
|
269
257
|
export async function init(dir, opts = {}) {
|
|
270
258
|
const projectDir = path.resolve(dir);
|
|
@@ -273,54 +261,44 @@ export async function init(dir, opts = {}) {
|
|
|
273
261
|
process.exit(1);
|
|
274
262
|
}
|
|
275
263
|
const kit = opts.kit ?? DEFAULT_KIT;
|
|
276
|
-
const bare = kit ===
|
|
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;
|
|
264
|
+
const bare = kit === "none" || kit === "bare";
|
|
280
265
|
if (bare) {
|
|
281
266
|
scaffoldBare(projectDir);
|
|
282
267
|
}
|
|
283
268
|
else {
|
|
284
|
-
|
|
269
|
+
scaffoldFromKit(kit, projectDir);
|
|
270
|
+
}
|
|
271
|
+
console.log(`Created project in ${projectDir}/${bare ? "" : ` (from kit "${kit}")`}`);
|
|
272
|
+
// Always install deps so the deck is ready to serve/edit immediately. The
|
|
273
|
+
// serve step is what `--no-serve` skips (callers like the cloud launcher run
|
|
274
|
+
// their own serve), but they still want the deps in place.
|
|
275
|
+
console.log("");
|
|
276
|
+
let installed = false;
|
|
277
|
+
try {
|
|
278
|
+
installDeps(projectDir);
|
|
279
|
+
installed = true;
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
console.error("dependency install failed; re-run `pnpm install` (or `npm install`) in the deck.");
|
|
285
283
|
}
|
|
286
|
-
console.log(`Created project in ${projectDir}/${bare ? '' : ` (from kit "${kit}")`}`);
|
|
287
|
-
// Auto-run npm install + serve so the page is up the moment the
|
|
288
|
-
// agent / user starts editing. Pass --no-serve to skip.
|
|
289
284
|
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
|
-
}
|
|
285
|
+
if (autoServe && installed) {
|
|
308
286
|
// Call serve() with detach so init returns once the server is up. serve()
|
|
309
287
|
// handles the background spawn internally; init doesn't shell out.
|
|
310
288
|
// Bind all interfaces by default so a tailnet / LAN browser can reach
|
|
311
289
|
// the served page; users can override host on a subsequent serve call.
|
|
312
290
|
// Open in the user's default browser unless we're clearly headless (SSH
|
|
313
291
|
// session) or the user has opted out via CASTLE_WEB_CLI_NO_OPEN=1.
|
|
314
|
-
const noOpen = process.env.CASTLE_WEB_CLI_NO_OPEN ===
|
|
292
|
+
const noOpen = process.env.CASTLE_WEB_CLI_NO_OPEN === "1" ||
|
|
315
293
|
!!process.env.SSH_CONNECTION ||
|
|
316
294
|
!!process.env.SSH_TTY;
|
|
317
|
-
await serve(projectDir, { host:
|
|
295
|
+
await serve(projectDir, { host: "0.0.0.0", detach: true, open: !noOpen });
|
|
318
296
|
return;
|
|
319
297
|
}
|
|
320
|
-
console.log(
|
|
321
|
-
console.log(
|
|
298
|
+
console.log("");
|
|
299
|
+
console.log("Next steps:");
|
|
322
300
|
console.log(` cd ${dir}`);
|
|
323
|
-
if (!
|
|
324
|
-
console.log(
|
|
325
|
-
console.log(
|
|
301
|
+
if (!installed)
|
|
302
|
+
console.log(" pnpm install # or: npm install");
|
|
303
|
+
console.log(" castle-web serve . # & in your shell to background it");
|
|
326
304
|
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|