castle-web-cli 0.4.59 → 0.4.60

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/ide.js CHANGED
@@ -6,18 +6,18 @@
6
6
  // Backend pattern ported from castle-cli's `ide` branch: @lydell/node-pty for
7
7
  // the PTY, an @xterm/headless screen + @xterm/addon-serialize so a reconnecting
8
8
  // client gets a full replay of the current screen + scrollback.
9
- import * as fs from 'fs';
10
- import * as path from 'path';
11
- import { createRequire } from 'module';
12
- import { fileURLToPath } from 'url';
13
- import { spawn as spawnPty } from '@lydell/node-pty';
14
- import headlessPkg from '@xterm/headless';
15
- import { SerializeAddon } from '@xterm/addon-serialize';
16
- import { WebSocketServer } from 'ws';
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+ import { createRequire } from "module";
12
+ import { fileURLToPath } from "url";
13
+ import { spawn as spawnPty } from "@lydell/node-pty";
14
+ import headlessPkg from "@xterm/headless";
15
+ import { SerializeAddon } from "@xterm/addon-serialize";
16
+ import { WebSocketServer } from "ws";
17
17
  const HeadlessTerminal = headlessPkg.Terminal;
18
18
  const require_ = createRequire(import.meta.url);
19
19
  const DIST_DIR = path.dirname(fileURLToPath(import.meta.url));
20
- const PTY_TERM = 'xterm-256color';
20
+ const PTY_TERM = "xterm-256color";
21
21
  const INITIAL_COLS = 80;
22
22
  const INITIAL_ROWS = 24;
23
23
  const SCROLLBACK = 4000;
@@ -25,8 +25,8 @@ const SCROLLBACK = 4000;
25
25
  // into the iframe at `/index.html`); `/__castle/ide/<asset>` are the shell's
26
26
  // static assets; `/__castle/pty` is the PTY WebSocket (handled via a direct
27
27
  // upgrade handler on Vite's HTTP server).
28
- export const IDE_ASSET_PREFIX = '/__castle/ide/';
29
- export const PTY_WS_PATH = '/__castle/pty';
28
+ export const IDE_ASSET_PREFIX = "/__castle/ide/";
29
+ export const PTY_WS_PATH = "/__castle/pty";
30
30
  // Injected into every served deck's <head>. When the deck runs embedded in
31
31
  // the IDE shell iframe, Ctrl+T moves keyboard focus out to the panel (the
32
32
  // chat input or the terminal, whichever view is active) -- but while the
@@ -38,20 +38,20 @@ export const DECK_FOCUS_SCRIPT = `<script>(function(){if(window.parent===window)
38
38
  `e.preventDefault();try{parent.postMessage({type:'castle-ide-toggle-focus'},'*');}catch(_){}}` +
39
39
  `},true);})();</script>`;
40
40
  function defaultShell() {
41
- if (process.platform === 'win32') {
42
- return { command: process.env.COMSPEC ?? 'cmd.exe', args: [] };
41
+ if (process.platform === "win32") {
42
+ return { command: process.env.COMSPEC ?? "cmd.exe", args: [] };
43
43
  }
44
- return { command: process.env.SHELL ?? '/bin/zsh', args: ['-l'] };
44
+ return { command: process.env.SHELL ?? "/bin/zsh", args: ["-l"] };
45
45
  }
46
46
  function ptyEnv() {
47
47
  const env = {
48
48
  ...process.env,
49
49
  TERM: PTY_TERM,
50
- COLORTERM: 'truecolor',
51
- CLICOLOR: '1',
52
- CLICOLOR_FORCE: '1',
53
- FORCE_COLOR: process.env.FORCE_COLOR === '0' ? '3' : process.env.FORCE_COLOR ?? '3',
54
- TERM_PROGRAM: 'castle-web-ide',
50
+ COLORTERM: "truecolor",
51
+ CLICOLOR: "1",
52
+ CLICOLOR_FORCE: "1",
53
+ FORCE_COLOR: process.env.FORCE_COLOR === "0" ? "3" : (process.env.FORCE_COLOR ?? "3"),
54
+ TERM_PROGRAM: "castle-web-ide",
55
55
  };
56
56
  delete env.NO_COLOR;
57
57
  delete env.NODE_DISABLE_COLORS;
@@ -65,10 +65,10 @@ function clampSize(value, fallback) {
65
65
  }
66
66
  export function rawDataToString(data) {
67
67
  if (Array.isArray(data))
68
- return Buffer.concat(data).toString('utf8');
68
+ return Buffer.concat(data).toString("utf8");
69
69
  if (Buffer.isBuffer(data))
70
- return data.toString('utf8');
71
- return Buffer.from(new Uint8Array(data)).toString('utf8');
70
+ return data.toString("utf8");
71
+ return Buffer.from(new Uint8Array(data)).toString("utf8");
72
72
  }
73
73
  function send(socket, body) {
74
74
  if (socket.readyState === socket.OPEN)
@@ -80,7 +80,7 @@ function queueScreenOp(session, op) {
80
80
  session.renderQueue = session.renderQueue
81
81
  .catch(() => undefined)
82
82
  .then(op)
83
- .catch((err) => console.error('[ide] screen render failed', err));
83
+ .catch((err) => console.error("[ide] screen render failed", err));
84
84
  }
85
85
  async function waitForStableScreen(session) {
86
86
  while (true) {
@@ -129,16 +129,16 @@ export function createIdeServer(opts) {
129
129
  };
130
130
  pty.onData((data) => {
131
131
  queueScreenOp(s, () => new Promise((done) => s.screen.write(data, done)));
132
- broadcast(s, { type: 'output', data });
132
+ broadcast(s, { type: "output", data });
133
133
  });
134
134
  pty.onExit(({ exitCode, signal }) => {
135
135
  s.exited = { exitCode, signal };
136
- broadcast(s, { type: 'exit', exitCode, signal });
136
+ broadcast(s, { type: "exit", exitCode, signal });
137
137
  // Clients get the `exit` message above (which disables their reconnect),
138
138
  // then we close the sockets so they don't sit half-open.
139
139
  for (const socket of s.clients) {
140
140
  try {
141
- socket.close(1000, 'shell exited');
141
+ socket.close(1000, "shell exited");
142
142
  }
143
143
  catch {
144
144
  /* ignore */
@@ -177,17 +177,21 @@ export function createIdeServer(opts) {
177
177
  if (socket.readyState !== socket.OPEN)
178
178
  return;
179
179
  send(socket, {
180
- type: 'replay',
180
+ type: "replay",
181
181
  data: s.serializeAddon.serialize({ scrollback: SCROLLBACK }),
182
182
  });
183
183
  if (s.exited) {
184
- send(socket, { type: 'exit', exitCode: s.exited.exitCode, signal: s.exited.signal });
184
+ send(socket, {
185
+ type: "exit",
186
+ exitCode: s.exited.exitCode,
187
+ signal: s.exited.signal,
188
+ });
185
189
  }
186
190
  }
187
191
  catch (err) {
188
192
  send(socket, {
189
- type: 'error',
190
- error: err instanceof Error ? err.message : 'could not restore screen',
193
+ type: "error",
194
+ error: err instanceof Error ? err.message : "could not restore screen",
191
195
  });
192
196
  }
193
197
  finally {
@@ -195,7 +199,7 @@ export function createIdeServer(opts) {
195
199
  s.clients.add(socket);
196
200
  }
197
201
  })();
198
- socket.on('message', (raw) => {
202
+ socket.on("message", (raw) => {
199
203
  let msg;
200
204
  try {
201
205
  msg = JSON.parse(rawDataToString(raw));
@@ -203,30 +207,33 @@ export function createIdeServer(opts) {
203
207
  catch {
204
208
  return;
205
209
  }
206
- if (msg.type === 'input' && typeof msg.data === 'string') {
210
+ if (msg.type === "input" && typeof msg.data === "string") {
207
211
  if (!s.exited)
208
212
  s.pty.write(msg.data);
209
213
  }
210
- else if (msg.type === 'resize') {
214
+ else if (msg.type === "resize") {
211
215
  resizeSession(s, Number(msg.cols), Number(msg.rows));
212
216
  }
213
217
  });
214
- socket.on('close', () => {
218
+ socket.on("close", () => {
215
219
  s.clients.delete(socket);
216
220
  });
217
221
  }
218
222
  const wss = new WebSocketServer({ noServer: true });
219
223
  function handleUpgrade(req, socket, head) {
220
- const url = new URL(req.url ?? '/', 'http://localhost');
224
+ const url = new URL(req.url ?? "/", "http://localhost");
221
225
  if (url.pathname !== PTY_WS_PATH)
222
226
  return false;
223
227
  wss.handleUpgrade(req, socket, head, (ws) => attachClient(ws));
224
228
  return true;
225
229
  }
226
230
  function handleHttpRequest(_req, res, reqPath) {
227
- if (reqPath === '/') {
231
+ if (reqPath === "/") {
228
232
  const html = renderIdePage(deckLabel);
229
- res.writeHead(200, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' });
233
+ res.writeHead(200, {
234
+ "content-type": "text/html; charset=utf-8",
235
+ "cache-control": "no-store",
236
+ });
230
237
  res.end(html);
231
238
  return true;
232
239
  }
@@ -238,8 +245,8 @@ export function createIdeServer(opts) {
238
245
  return true;
239
246
  }
240
247
  res.writeHead(200, {
241
- 'content-type': resolved.contentType,
242
- 'cache-control': 'no-store',
248
+ "content-type": resolved.contentType,
249
+ "cache-control": "no-store",
243
250
  });
244
251
  fs.createReadStream(resolved.filePath).pipe(res);
245
252
  return true;
@@ -256,7 +263,7 @@ export function createIdeServer(opts) {
256
263
  }
257
264
  for (const socket of session.clients) {
258
265
  try {
259
- socket.close(1001, 'serve shutting down');
266
+ socket.close(1001, "serve shutting down");
260
267
  }
261
268
  catch {
262
269
  /* ignore */
@@ -272,17 +279,26 @@ export function createIdeServer(opts) {
272
279
  // file by tsc into the same dist directory.
273
280
  function resolveIdeAsset(asset) {
274
281
  const assets = {
275
- 'xterm.js': { spec: '@xterm/xterm/lib/xterm.js', contentType: 'text/javascript; charset=utf-8' },
276
- 'xterm.css': { spec: '@xterm/xterm/css/xterm.css', contentType: 'text/css; charset=utf-8' },
277
- 'addon-fit.js': {
278
- spec: '@xterm/addon-fit/lib/addon-fit.js',
279
- contentType: 'text/javascript; charset=utf-8',
282
+ "xterm.js": {
283
+ spec: "@xterm/xterm/lib/xterm.js",
284
+ contentType: "text/javascript; charset=utf-8",
285
+ },
286
+ "xterm.css": {
287
+ spec: "@xterm/xterm/css/xterm.css",
288
+ contentType: "text/css; charset=utf-8",
289
+ },
290
+ "addon-fit.js": {
291
+ spec: "@xterm/addon-fit/lib/addon-fit.js",
292
+ contentType: "text/javascript; charset=utf-8",
280
293
  },
281
294
  };
282
295
  const fromModule = assets[asset];
283
296
  if (fromModule) {
284
297
  try {
285
- return { filePath: require_.resolve(fromModule.spec), contentType: fromModule.contentType };
298
+ return {
299
+ filePath: require_.resolve(fromModule.spec),
300
+ contentType: fromModule.contentType,
301
+ };
286
302
  }
287
303
  catch {
288
304
  return null;
@@ -291,9 +307,12 @@ function resolveIdeAsset(asset) {
291
307
  // React's package "exports" hides umd/ from require.resolve; resolve the
292
308
  // package root via its package.json (always exported) and join from there.
293
309
  const umdAssets = {
294
- 'react.js': { pkg: 'react', rel: 'umd/react.production.min.js' },
295
- 'react-dom.js': { pkg: 'react-dom', rel: 'umd/react-dom.production.min.js' },
296
- 'marked.js': { pkg: 'marked', rel: 'lib/marked.umd.js' },
310
+ "react.js": { pkg: "react", rel: "umd/react.production.min.js" },
311
+ "react-dom.js": {
312
+ pkg: "react-dom",
313
+ rel: "umd/react-dom.production.min.js",
314
+ },
315
+ "marked.js": { pkg: "marked", rel: "lib/marked.umd.js" },
297
316
  };
298
317
  const fromUmd = umdAssets[asset];
299
318
  if (fromUmd) {
@@ -301,17 +320,17 @@ function resolveIdeAsset(asset) {
301
320
  const pkgRoot = path.dirname(require_.resolve(`${fromUmd.pkg}/package.json`));
302
321
  const filePath = path.join(pkgRoot, fromUmd.rel);
303
322
  return fs.existsSync(filePath)
304
- ? { filePath, contentType: 'text/javascript; charset=utf-8' }
323
+ ? { filePath, contentType: "text/javascript; charset=utf-8" }
305
324
  : null;
306
325
  }
307
326
  catch {
308
327
  return null;
309
328
  }
310
329
  }
311
- if (asset === 'ide-client.js' || asset === 'chat-client.js') {
330
+ if (asset === "ide-client.js" || asset === "chat-client.js") {
312
331
  const filePath = path.join(DIST_DIR, asset);
313
332
  return fs.existsSync(filePath)
314
- ? { filePath, contentType: 'text/javascript; charset=utf-8' }
333
+ ? { filePath, contentType: "text/javascript; charset=utf-8" }
315
334
  : null;
316
335
  }
317
336
  return null;
@@ -632,9 +651,24 @@ const IDE_STYLES = `
632
651
  }
633
652
  /* Live stream of a running task agent (text lines + [tool labels]) shown
634
653
  on expand -- capped height, auto-scrolled, fading at both scroll edges. */
654
+ .task-feed-tool {
655
+ padding-left: 14px;
656
+ color: #8c959f;
657
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
658
+ font-size: 11px;
659
+ }
660
+ .task-feed-msg > :first-child { margin-top: 0; }
661
+ .task-feed-msg > :last-child { margin-bottom: 0; }
662
+ .task-feed-msg p { margin: 0.25em 0; }
663
+ .task-feed-msg code {
664
+ background: rgba(175, 184, 193, 0.2);
665
+ border-radius: 3px;
666
+ padding: 0 3px;
667
+ font-size: 11px;
668
+ }
635
669
  .task-feed {
636
670
  margin-top: 6px;
637
- max-height: 150px;
671
+ max-height: 65px;
638
672
  overflow-y: auto;
639
673
  font-size: 12px;
640
674
  line-height: 1.5;
@@ -644,17 +678,10 @@ const IDE_STYLES = `
644
678
  -webkit-mask-image: linear-gradient(
645
679
  to bottom,
646
680
  transparent,
647
- #000000 14px,
648
- #000000 calc(100% - 14px),
649
- transparent
650
- );
651
- mask-image: linear-gradient(
652
- to bottom,
653
- transparent,
654
- #000000 14px,
655
- #000000 calc(100% - 14px),
656
- transparent
681
+ #000000 5px,
682
+ #000000 100%
657
683
  );
684
+ mask-image: linear-gradient(to bottom, transparent, #000000 5px, #000000 100%);
658
685
  }
659
686
  .task-row { display: flex; align-items: center; gap: 8px; }
660
687
  .task-title {
@@ -667,6 +694,7 @@ const IDE_STYLES = `
667
694
  .task-title::first-letter { text-transform: uppercase; }
668
695
  .task-status { color: #6e7781; font-size: 12px; flex: 0 0 auto; }
669
696
  .task-status.failed, .task-status.interrupted { color: #a40e26; }
697
+ .task-time { color: #6e7781; font-size: 12px; flex: 0 0 auto; font-variant-numeric: tabular-nums; }
670
698
  .task-pie {
671
699
  width: 15px;
672
700
  height: 15px;
@@ -862,5 +890,5 @@ function renderIdePage(deckLabel) {
862
890
  `;
863
891
  }
864
892
  function escapeHtml(value) {
865
- return value.replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c] ?? c);
893
+ return value.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c] ?? c);
866
894
  }
package/dist/serve.js CHANGED
@@ -8,6 +8,8 @@ import { createIdeServer, DECK_FOCUS_SCRIPT } from './ide.js';
8
8
  import { createAgentServer } from './agent.js';
9
9
  import { sceneFilesPlugin } from './vitePlugins.js';
10
10
  import * as config from './config.js';
11
+ import { graphql as castleGraphql } from './api.js';
12
+ import { executeCommand } from './castle-host/host.js';
11
13
  function isPortFree(port) {
12
14
  return new Promise((resolve) => {
13
15
  const srv = net.createServer();
@@ -29,7 +31,7 @@ function castlePlugin(wsPort, ideServer, agentServer) {
29
31
  transformIndexHtml: {
30
32
  order: 'pre',
31
33
  handler(html) {
32
- return html.replace(/<head(\s[^>]*)?>/i, (match) => `${match}\n <script>window.CastleEmbed={edit:true};</script>\n ${DECK_FOCUS_SCRIPT}`);
34
+ return html.replace(/<head(\s[^>]*)?>/i, (match) => `${match}\n <script>window.CastleEmbed={edit:true,host:'dev'};</script>\n ${DECK_FOCUS_SCRIPT}`);
33
35
  },
34
36
  },
35
37
  configureServer(server) {
@@ -64,6 +66,38 @@ function castlePlugin(wsPort, ideServer, agentServer) {
64
66
  },
65
67
  };
66
68
  }
69
+ // The dev author's username, fetched once (config stores token+userId but not
70
+ // username). Cached for the serve lifetime so User.getCurrent works in dev.
71
+ let cachedDevUsername;
72
+ async function devUsername() {
73
+ if (cachedDevUsername !== undefined)
74
+ return cachedDevUsername;
75
+ try {
76
+ const res = await castleGraphql('query { me { username } }', {});
77
+ const me = res.data?.me;
78
+ cachedDevUsername = me?.username ?? null;
79
+ }
80
+ catch {
81
+ cachedDevUsername = null;
82
+ }
83
+ return cachedDevUsername;
84
+ }
85
+ async function devHostContext(projectDir) {
86
+ const ctx = readDeckContext(projectDir);
87
+ return {
88
+ deckId: ctx.deckId ?? null,
89
+ sessionId: null,
90
+ userId: config.getUserId(),
91
+ username: await devUsername(),
92
+ };
93
+ }
94
+ async function handleCastleCommand(ws, projectDir, requestId, command, params) {
95
+ const ctx = await devHostContext(projectDir);
96
+ const result = await executeCommand(ctx, command, params, castleGraphql);
97
+ if (ws.readyState === WebSocket.OPEN) {
98
+ ws.send(JSON.stringify({ type: 'castle_command_response', castleSdk: 1, requestId, ...result }));
99
+ }
100
+ }
67
101
  function readDeckContext(projectDir) {
68
102
  try {
69
103
  const raw = fs.readFileSync(path.join(projectDir, 'castle.json'), 'utf8');
@@ -407,6 +441,13 @@ function startWSServer(port, projectDir, logFile, screenshotsDir, viteHolder) {
407
441
  ...response,
408
442
  }));
409
443
  }
444
+ // SDK command from a locally-served deck. The dev server is the host:
445
+ // it runs the command via the SDK host executor with the CLI's login
446
+ // token, then replies to just this socket. See castle-web-sdk's
447
+ // transport.ts (local channel) + host.ts.
448
+ if (msg.type === 'castle_command' && typeof msg.requestId === 'string') {
449
+ void handleCastleCommand(ws, projectDir, msg.requestId, msg.command, msg.params);
450
+ }
410
451
  }
411
452
  catch {
412
453
  // ignore malformed frames
@@ -65,7 +65,7 @@ A fresh `Behavior` instance is constructed per actor per frame from the actor's
65
65
  "id": "paddle",
66
66
  "components": {
67
67
  "Layout": { "x": 210, "y": 640, "width": 80, "height": 12 },
68
- "Drawing": { "file": "drawings/block.drawing" },
68
+ "Drawing": { "file": "drawings/paddle.drawing" },
69
69
  "Collider": { "kind": "solid", "width": 80, "height": 12 },
70
70
  "Paddle": { "speed": 360 }
71
71
  }
@@ -79,8 +79,9 @@ Rules: every actor needs a unique `id` (any string) and almost always a `Layout`
79
79
  ## Built-in behaviors
80
80
 
81
81
  - **Layout** — `{ x, y, width, height, z?, rotation? }`. Every actor needs one. `z` orders draw (low first). `rotation` is degrees about the center.
82
- - **Drawing** — `{ file: "drawings/foo.drawing", tint?: "#rrggbbaa" }`. Renders the pixel-art file scaled into the Layout box. `tint` multiplies; use white (`#ffffffff`) or omit for the original colors. For a solid-color rectangle, point `file` at `drawings/block.drawing` (an 8×8 white pixel sprite) and set `tint` to your color.
83
- - **Don't distort pixel art.** Keep an actor's Layout aspect ratio close to its drawing's native aspect ratio, and don't scale a sprite way upstretching a small sprite into a long/tall actor wrecks the pixels (a brick sprite stretched into a wall ruins the bricks). For long surfaces (walls, floors, platforms), make a drawing proportioned to the surface (or tile several actors) instead of stretching one. `block.drawing` is the exception — it's a flat solid fill, so any size/tint is fine.
82
+ - **Drawing** — `{ file: "drawings/foo.drawing", tint?: "#rrggbbaa" }`. Renders the pixel-art file scaled into the Layout box. `tint` multiplies; use white (`#ffffffff`) or omit for the original colors. For a solid-color rectangle, point `file` at `drawings/default.drawing` (an 8×8 white block) and set `tint` to your color.
83
+ - **Missing drawings fall back to a block.** If `file` names a drawing that doesn't exist yet, it renders as `drawings/default.drawing` (a plain white block)so you can give an actor its REAL intended drawing name (`drawings/paddle.drawing`) right away and it shows a block until that file is created. Reference real names from the start; don't wait for the art.
84
+ - **Don't distort pixel art.** Keep an actor's Layout aspect ratio close to its drawing's native aspect ratio, and don't scale a sprite way up — stretching a small sprite into a long/tall actor wrecks the pixels (a brick sprite stretched into a wall ruins the bricks). For long surfaces (walls, floors, platforms), make a drawing proportioned to the surface (or tile several actors) instead of stretching one. `default.drawing` is the exception — it's a flat solid fill, so any size/tint is fine.
84
85
  - **Collider** — `{ kind: 'solid'|'pickup', width, height, offsetX?, offsetY?, debug? }`. Just a rect; the framework does NOT auto-resolve collisions for you. Use it as data:
85
86
 
86
87
  ```jsx
@@ -6,7 +6,7 @@ export class Drawing {
6
6
  static behaviorName = 'Drawing';
7
7
 
8
8
  static defaultProps = {
9
- file: 'drawings/block.drawing',
9
+ file: 'drawings/default.drawing',
10
10
  tint: '#ffffffff',
11
11
  };
12
12
 
@@ -18,7 +18,10 @@ export class Drawing {
18
18
  if (actor.runtime?.collected) return;
19
19
  const layout = actor.components.Layout;
20
20
  if (!layout) return;
21
- const drawing = scene.drawings[this.props.file];
21
+ // Fall back to default.drawing (a plain block) when the named file does not
22
+ // exist yet -- so a scene can reference drawings/foo.drawing before the
23
+ // drawing task has created it, and it shows a block until the real art lands.
24
+ const drawing = scene.drawings[this.props.file] ?? scene.drawings['drawings/default.drawing'];
22
25
  if (!drawing) return;
23
26
  drawPixelDrawing(
24
27
  ctx,
@@ -0,0 +1,70 @@
1
+ {
2
+ "width": 8,
3
+ "height": 8,
4
+ "pixels": [
5
+ "#ffffffff",
6
+ "#ffffffff",
7
+ "#ffffffff",
8
+ "#ffffffff",
9
+ "#ffffffff",
10
+ "#ffffffff",
11
+ "#ffffffff",
12
+ "#ffffffff",
13
+ "#ffffffff",
14
+ "#d8d8d8ff",
15
+ "#d8d8d8ff",
16
+ "#d8d8d8ff",
17
+ "#d8d8d8ff",
18
+ "#d8d8d8ff",
19
+ "#d8d8d8ff",
20
+ "#ffffffff",
21
+ "#ffffffff",
22
+ "#d8d8d8ff",
23
+ "#ffffffff",
24
+ "#ffffffff",
25
+ "#ffffffff",
26
+ "#ffffffff",
27
+ "#d8d8d8ff",
28
+ "#ffffffff",
29
+ "#ffffffff",
30
+ "#d8d8d8ff",
31
+ "#ffffffff",
32
+ "#ffffffff",
33
+ "#ffffffff",
34
+ "#ffffffff",
35
+ "#d8d8d8ff",
36
+ "#ffffffff",
37
+ "#ffffffff",
38
+ "#d8d8d8ff",
39
+ "#ffffffff",
40
+ "#ffffffff",
41
+ "#ffffffff",
42
+ "#ffffffff",
43
+ "#d8d8d8ff",
44
+ "#ffffffff",
45
+ "#ffffffff",
46
+ "#d8d8d8ff",
47
+ "#ffffffff",
48
+ "#ffffffff",
49
+ "#ffffffff",
50
+ "#ffffffff",
51
+ "#d8d8d8ff",
52
+ "#ffffffff",
53
+ "#ffffffff",
54
+ "#d8d8d8ff",
55
+ "#d8d8d8ff",
56
+ "#d8d8d8ff",
57
+ "#d8d8d8ff",
58
+ "#d8d8d8ff",
59
+ "#d8d8d8ff",
60
+ "#ffffffff",
61
+ "#ffffffff",
62
+ "#ffffffff",
63
+ "#ffffffff",
64
+ "#ffffffff",
65
+ "#ffffffff",
66
+ "#ffffffff",
67
+ "#ffffffff",
68
+ "#ffffffff"
69
+ ]
70
+ }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "castle-web-cli",
3
- "version": "0.4.59",
3
+ "version": "0.4.60",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "castle-web": "./dist/index.js"
7
7
  },
8
8
  "scripts": {
9
- "build": "tsc && rm -rf kits && cp -R ../kits kits",
9
+ "build": "tsc && cp -R src/castle-host dist/castle-host && rm -rf kits && cp -R ../kits kits",
10
10
  "dev": "tsc --watch",
11
11
  "check": "eslint . && jscpd && tsc --noEmit"
12
12
  },
@@ -14,6 +14,9 @@
14
14
  "path": [
15
15
  "src"
16
16
  ],
17
+ "ignore": [
18
+ "src/castle-host/**"
19
+ ],
17
20
  "threshold": 0,
18
21
  "reporters": [
19
22
  "consoleFull"