ryugi-artifacts 1.30.0 → 1.31.0

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/cli.cjs CHANGED
@@ -27,85 +27,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_node_child_process3 = require("node:child_process");
28
28
  var import_node_util = require("node:util");
29
29
 
30
- // src/cli/comment.ts
31
- var import_node_fs3 = __toESM(require("node:fs"), 1);
32
-
33
- // src/server/agent.ts
34
- var NAMES = {
35
- claude: "Claude Code",
36
- "claude-code": "Claude Code",
37
- cowork: "Claude Cowork",
38
- codex: "Codex",
39
- cursor: "Cursor",
40
- "cursor-cli": "Cursor",
41
- gemini: "Gemini CLI",
42
- "gemini-cli": "Gemini CLI",
43
- "github-copilot": "GitHub Copilot",
44
- "github-copilot-cli": "GitHub Copilot",
45
- kilo: "Kilo Code",
46
- opencode: "OpenCode",
47
- "qwen-code": "Qwen Code",
48
- antigravity: "Antigravity",
49
- augment: "Augment",
50
- "augment-cli": "Augment",
51
- goose: "Goose",
52
- amp: "Amp",
53
- cline: "Cline",
54
- roo: "Roo Code",
55
- devin: "Devin",
56
- replit: "Replit",
57
- v0: "v0"
58
- };
59
- var AGENT_DISPLAY_NAMES = [...new Set(Object.values(NAMES))];
60
- var MARKERS = [
61
- ["cowork", (env) => !!env.CLAUDE_CODE_IS_COWORK],
62
- ["kilo", (env) => !!env.KILO],
63
- ["codex", (env) => !!(env.CODEX_THREAD_ID || env.CODEX_SANDBOX || env.CODEX_CI)],
64
- ["cursor", (env) => !!(env.CURSOR_AGENT || env.CURSOR_TRACE_ID)],
65
- ["gemini", (env) => !!env.GEMINI_CLI],
66
- ["github-copilot", (env) => !!env.COPILOT_CLI],
67
- ["opencode", (env) => env.OPENCODE === "1" || !!env.OPENCODE_CLIENT],
68
- ["qwen-code", (env) => !!env.QWEN_CODE],
69
- ["antigravity", (env) => !!env.ANTIGRAVITY_AGENT],
70
- ["augment", (env) => !!env.AUGMENT_AGENT],
71
- ["cline", (env) => !!env.CLINE_ACTIVE],
72
- ["roo", (env) => !!env.ROO_ACTIVE],
73
- ["claude", (env) => !!(env.CLAUDECODE || env.CLAUDE_CODE)]
74
- ];
75
- var AGENT_MARKERS = [
76
- "AI_AGENT",
77
- "AGENT",
78
- "CLAUDE_CODE_IS_COWORK",
79
- "KILO",
80
- "CODEX_THREAD_ID",
81
- "CODEX_SANDBOX",
82
- "CODEX_CI",
83
- "CURSOR_AGENT",
84
- "CURSOR_TRACE_ID",
85
- "GEMINI_CLI",
86
- "COPILOT_CLI",
87
- "OPENCODE",
88
- "OPENCODE_CLIENT",
89
- "QWEN_CODE",
90
- "ANTIGRAVITY_AGENT",
91
- "AUGMENT_AGENT",
92
- "CLINE_ACTIVE",
93
- "ROO_ACTIVE",
94
- "CLAUDECODE",
95
- "CLAUDE_CODE"
96
- ];
97
- var declaredName = (value) => value?.trim().split(/[@_]/)[0]?.toLowerCase() || void 0;
98
- function detectAgent(env) {
99
- if (env.CLAUDE_CODE_IS_COWORK) return NAMES.cowork;
100
- const declared = declaredName(env.AI_AGENT);
101
- if (declared) return NAMES[declared] ?? declared;
102
- const marked = MARKERS.find(([, test]) => test(env))?.[0];
103
- if (marked) return NAMES[marked];
104
- const agent = declaredName(env.AGENT);
105
- if (agent && !/^(1|true|yes)$/.test(agent)) return NAMES[agent] ?? agent;
106
- return void 0;
107
- }
108
-
109
30
  // src/cli/paths.ts
110
31
  var import_node_fs = __toESM(require("node:fs"), 1);
111
32
  var import_node_path = __toESM(require("node:path"), 1);
@@ -230,6 +151,114 @@ function serverClient(projectDir) {
230
151
  };
231
152
  }
232
153
 
154
+ // src/cli/check.ts
155
+ async function check(options, emit) {
156
+ if (!options.artifact || !ARTIFACT_PATTERN.test(options.artifact)) throw new CliError(ARTIFACT_ERROR);
157
+ const server = serverClient(options.projectDir);
158
+ const { status, json } = await server.call("POST", `/api/agent/${options.artifact}/check`);
159
+ if (status === 404 && typeof json.error !== "string") {
160
+ throw new CliError(`The running artifacts server is older than this CLI. Run \`${invocation()} start --project-dir ${server.project}\` to replace it, then run this again.`);
161
+ }
162
+ if (status < 200 || status >= 300) throw new CliError(json.error || `HTTP ${status}`);
163
+ emit({ type: "checked", artifact: options.artifact, ...json });
164
+ return 0;
165
+ }
166
+ function checkText(value) {
167
+ if (value.type !== "checked") return null;
168
+ const warnings = value.warnings ?? [];
169
+ const pending = value.pending ?? [];
170
+ const dynamic = value.dynamic ?? {};
171
+ const unrun = Object.entries(dynamic).filter(([, block]) => block.ran === false).map(([file]) => file);
172
+ const lines = [
173
+ ...warnings.map((warning) => `warning: ${warning}`),
174
+ ...pending.length ? [`pending: ${pending.join(", ")}`] : [],
175
+ ...value.merging ? ["still merging the last edit; check again"] : [],
176
+ ...value.ran === false ? ["not run in a browser yet"] : [],
177
+ ...unrun.length ? [`not run in a browser yet: ${unrun.join(", ")}`] : []
178
+ ];
179
+ return lines.length ? `${value.artifact}
180
+ ${lines.join("\n")}` : `${value.artifact}: no warnings`;
181
+ }
182
+
183
+ // src/cli/comment.ts
184
+ var import_node_fs3 = __toESM(require("node:fs"), 1);
185
+
186
+ // src/server/agent.ts
187
+ var NAMES = {
188
+ claude: "Claude Code",
189
+ "claude-code": "Claude Code",
190
+ cowork: "Claude Cowork",
191
+ codex: "Codex",
192
+ cursor: "Cursor",
193
+ "cursor-cli": "Cursor",
194
+ gemini: "Gemini CLI",
195
+ "gemini-cli": "Gemini CLI",
196
+ "github-copilot": "GitHub Copilot",
197
+ "github-copilot-cli": "GitHub Copilot",
198
+ kilo: "Kilo Code",
199
+ opencode: "OpenCode",
200
+ "qwen-code": "Qwen Code",
201
+ antigravity: "Antigravity",
202
+ augment: "Augment",
203
+ "augment-cli": "Augment",
204
+ goose: "Goose",
205
+ amp: "Amp",
206
+ cline: "Cline",
207
+ roo: "Roo Code",
208
+ devin: "Devin",
209
+ replit: "Replit",
210
+ v0: "v0"
211
+ };
212
+ var AGENT_DISPLAY_NAMES = [...new Set(Object.values(NAMES))];
213
+ var MARKERS = [
214
+ ["cowork", (env) => !!env.CLAUDE_CODE_IS_COWORK],
215
+ ["kilo", (env) => !!env.KILO],
216
+ ["codex", (env) => !!(env.CODEX_THREAD_ID || env.CODEX_SANDBOX || env.CODEX_CI)],
217
+ ["cursor", (env) => !!(env.CURSOR_AGENT || env.CURSOR_TRACE_ID)],
218
+ ["gemini", (env) => !!env.GEMINI_CLI],
219
+ ["github-copilot", (env) => !!env.COPILOT_CLI],
220
+ ["opencode", (env) => env.OPENCODE === "1" || !!env.OPENCODE_CLIENT],
221
+ ["qwen-code", (env) => !!env.QWEN_CODE],
222
+ ["antigravity", (env) => !!env.ANTIGRAVITY_AGENT],
223
+ ["augment", (env) => !!env.AUGMENT_AGENT],
224
+ ["cline", (env) => !!env.CLINE_ACTIVE],
225
+ ["roo", (env) => !!env.ROO_ACTIVE],
226
+ ["claude", (env) => !!(env.CLAUDECODE || env.CLAUDE_CODE)]
227
+ ];
228
+ var AGENT_MARKERS = [
229
+ "AI_AGENT",
230
+ "AGENT",
231
+ "CLAUDE_CODE_IS_COWORK",
232
+ "KILO",
233
+ "CODEX_THREAD_ID",
234
+ "CODEX_SANDBOX",
235
+ "CODEX_CI",
236
+ "CURSOR_AGENT",
237
+ "CURSOR_TRACE_ID",
238
+ "GEMINI_CLI",
239
+ "COPILOT_CLI",
240
+ "OPENCODE",
241
+ "OPENCODE_CLIENT",
242
+ "QWEN_CODE",
243
+ "ANTIGRAVITY_AGENT",
244
+ "AUGMENT_AGENT",
245
+ "CLINE_ACTIVE",
246
+ "ROO_ACTIVE",
247
+ "CLAUDECODE",
248
+ "CLAUDE_CODE"
249
+ ];
250
+ var declaredName = (value) => value?.trim().split(/[@_]/)[0]?.toLowerCase() || void 0;
251
+ function detectAgent(env) {
252
+ if (env.CLAUDE_CODE_IS_COWORK) return NAMES.cowork;
253
+ const declared = declaredName(env.AI_AGENT);
254
+ if (declared) return NAMES[declared] ?? declared;
255
+ const marked = MARKERS.find(([, test]) => test(env))?.[0];
256
+ if (marked) return NAMES[marked];
257
+ const agent = declaredName(env.AGENT);
258
+ if (agent && !/^(1|true|yes)$/.test(agent)) return NAMES[agent] ?? agent;
259
+ return void 0;
260
+ }
261
+
233
262
  // src/cli/comment.ts
234
263
  var COMMENT_ACTIONS = ["list", "add", "reply", "resolve"];
235
264
  var ARTIFACT = /^(document|artboard)\/[a-z0-9][a-z0-9-]*$/;
@@ -655,7 +684,7 @@ async function start(options, emit) {
655
684
  const lines = (readText(paths.log) ?? "").split("\n");
656
685
  const started = lines.find((line) => line.includes('"server-started"'));
657
686
  if (started) {
658
- for (let check = 0; check < 20; check++) {
687
+ for (let check2 = 0; check2 < 20; check2++) {
659
688
  if (!alive(child.pid)) throw new CliError(`Server started but was killed. Retry in a persistent terminal with: ${invocation()} start --project-dir ${paths.project}${options.artifact ? ` --artifact ${options.artifact}` : ""} --host ${options.host} --url-host ${options.urlHost} --foreground`);
660
689
  await sleep(100);
661
690
  }
@@ -782,6 +811,8 @@ running until Ctrl+C.
782
811
  Commands:
783
812
  start Start the project's artifacts server, or reuse the running one
784
813
  stop Stop the project's artifacts server; the artifacts are kept
814
+ check Wait until the server has checked the latest write, then print its warnings (needs a running server):
815
+ --artifact <type>/<id>
785
816
  publish Publish an artifact to the Ryugi cloud (needs a running server):
786
817
  --artifact <type>/<id> [--public | --private] [--unpublish]
787
818
  --list | --delete <cloud-id> | --login | --logout
@@ -822,6 +853,8 @@ function text(value) {
822
853
  ${value.url}`;
823
854
  if (value.type === "published") return `${value.url} (${value.visibility})`;
824
855
  if (value.type === "auto-publish") return `Auto-publish: ${value.global === null ? "not decided" : value.global ? "on" : "off"} for every project${value.project === null ? "" : `, ${value.project ? "on" : "off"} for this project`}.`;
856
+ const checked = checkText(value);
857
+ if (checked !== null) return checked;
825
858
  const commented = commentText(value);
826
859
  if (commented !== null) return commented;
827
860
  if (typeof value.status === "string") return value.status === "stopped" ? "Stopped." : value.status.replace(/_/g, " ");
@@ -890,6 +923,13 @@ async function main(argv) {
890
923
  const { values, positionals } = parse(rest, {}, 1);
891
924
  return await stop(values["project-dir"] ?? positionals[0] ?? process.cwd(), emit);
892
925
  }
926
+ if (command === "check") {
927
+ const { values } = parse(rest, { artifact: { type: "string" } });
928
+ return await check({
929
+ projectDir: values["project-dir"] ?? process.cwd(),
930
+ artifact: values.artifact
931
+ }, emit);
932
+ }
893
933
  if (command === "publish") {
894
934
  const { values } = parse(rest, {
895
935
  artifact: { type: "string" },
@@ -947,7 +987,7 @@ async function main(argv) {
947
987
  }, emit);
948
988
  }
949
989
  if (command === "--version" || command === "-v") {
950
- console.log("1.30.0");
990
+ console.log("1.31.0");
951
991
  return 0;
952
992
  }
953
993
  if (command === "--help" || command === "-h") {
package/dist/server.cjs CHANGED
@@ -5186,6 +5186,7 @@ var BOARD_TYPES = ["html", "image", "video"];
5186
5186
  var isMediaBoard = (board) => board.type === "image" || board.type === "video";
5187
5187
 
5188
5188
  // src/server/artifacts.ts
5189
+ var import_node_crypto = __toESM(require("node:crypto"), 1);
5189
5190
  var import_node_fs = __toESM(require("node:fs"), 1);
5190
5191
  var import_node_path = __toESM(require("node:path"), 1);
5191
5192
 
@@ -5233,9 +5234,9 @@ function artifactKeyOf(relative) {
5233
5234
  return { type, id: id2 };
5234
5235
  }
5235
5236
  function artifactSignature(dir, type) {
5236
- let count = 0;
5237
5237
  let size = 0;
5238
5238
  let latest = 0;
5239
+ const names = [];
5239
5240
  const walk = (current) => {
5240
5241
  let entries2;
5241
5242
  try {
@@ -5249,7 +5250,7 @@ function artifactSignature(dir, type) {
5249
5250
  const full = import_node_path.default.join(current, entry.name);
5250
5251
  try {
5251
5252
  const stat = import_node_fs.default.statSync(full);
5252
- count += 1;
5253
+ names.push(import_node_path.default.relative(dir, full));
5253
5254
  latest = Math.max(latest, stat.mtimeMs);
5254
5255
  if (entry.isDirectory()) walk(full);
5255
5256
  else size += stat.size;
@@ -5259,7 +5260,8 @@ function artifactSignature(dir, type) {
5259
5260
  }
5260
5261
  };
5261
5262
  walk(dir);
5262
- return `${count}:${size}:${latest}`;
5263
+ const hash = import_node_crypto.default.createHash("sha1").update(names.sort().join("\0")).digest("hex").slice(0, 12);
5264
+ return `${names.length}:${size}:${latest}:${hash}`;
5263
5265
  }
5264
5266
 
5265
5267
  // src/server/document.ts
@@ -17573,7 +17575,7 @@ setPrototypeOf(Document4, Document2).prototype = Document2.prototype;
17573
17575
  registerParseHTML((html) => parseHTML2(html).document);
17574
17576
 
17575
17577
  // node_modules/.pnpm/nanoid@5.1.16/node_modules/nanoid/index.js
17576
- var import_node_crypto = require("node:crypto");
17578
+ var import_node_crypto2 = require("node:crypto");
17577
17579
 
17578
17580
  // node_modules/.pnpm/nanoid@5.1.16/node_modules/nanoid/url-alphabet/index.js
17579
17581
  var urlAlphabet2 = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
@@ -17587,10 +17589,10 @@ function fillPool(bytes) {
17587
17589
  try {
17588
17590
  if (!pool || pool.length < bytes) {
17589
17591
  pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
17590
- import_node_crypto.webcrypto.getRandomValues(pool);
17592
+ import_node_crypto2.webcrypto.getRandomValues(pool);
17591
17593
  poolOffset = 0;
17592
17594
  } else if (poolOffset + bytes > pool.length) {
17593
- import_node_crypto.webcrypto.getRandomValues(pool);
17595
+ import_node_crypto2.webcrypto.getRandomValues(pool);
17594
17596
  poolOffset = 0;
17595
17597
  }
17596
17598
  } catch (e5) {
@@ -17800,7 +17802,7 @@ function boxDrawingDiagrams(state) {
17800
17802
  }
17801
17803
 
17802
17804
  // src/server/board-comments.ts
17803
- var import_node_crypto2 = __toESM(require("node:crypto"), 1);
17805
+ var import_node_crypto3 = __toESM(require("node:crypto"), 1);
17804
17806
  var import_node_fs8 = __toESM(require("node:fs"), 1);
17805
17807
  var import_node_path7 = __toESM(require("node:path"), 1);
17806
17808
 
@@ -18663,7 +18665,7 @@ function fileHash(dir, relative) {
18663
18665
  const stamp2 = `${stat.size}:${stat.mtimeMs}`;
18664
18666
  const cached = hashCache.get(file);
18665
18667
  if (cached?.stamp === stamp2) return cached.hash;
18666
- const hash = import_node_crypto2.default.createHash("sha1").update(import_node_fs8.default.readFileSync(file)).digest("hex").slice(0, 12);
18668
+ const hash = import_node_crypto3.default.createHash("sha1").update(import_node_fs8.default.readFileSync(file)).digest("hex").slice(0, 12);
18667
18669
  hashCache.set(file, { stamp: stamp2, hash });
18668
18670
  return hash;
18669
18671
  } catch {
@@ -88956,7 +88958,7 @@ function injectRuntime(html, options = {}) {
88956
88958
  }
88957
88959
 
88958
88960
  // src/server/http.ts
88959
- var import_node_crypto3 = __toESM(require("node:crypto"), 1);
88961
+ var import_node_crypto4 = __toESM(require("node:crypto"), 1);
88960
88962
  var import_node_fs10 = __toESM(require("node:fs"), 1);
88961
88963
  var import_node_path10 = __toESM(require("node:path"), 1);
88962
88964
  var MIME2 = {
@@ -88982,11 +88984,11 @@ var MIME2 = {
88982
88984
  ".webm": "video/webm"
88983
88985
  };
88984
88986
  var mimeOf = (file) => MIME2[import_node_path10.default.extname(file).toLowerCase()] ?? "application/octet-stream";
88985
- var randomToken = () => import_node_crypto3.default.randomBytes(24).toString("base64url");
88987
+ var randomToken = () => import_node_crypto4.default.randomBytes(24).toString("base64url");
88986
88988
  function safeEqual(a5, b6) {
88987
88989
  const ab = Buffer.from(a5);
88988
88990
  const bb = Buffer.from(b6);
88989
- return ab.length === bb.length && import_node_crypto3.default.timingSafeEqual(ab, bb);
88991
+ return ab.length === bb.length && import_node_crypto4.default.timingSafeEqual(ab, bb);
88990
88992
  }
88991
88993
  function parseCookies(header) {
88992
88994
  const cookies = {};
@@ -89499,7 +89501,7 @@ function createRelayClient(options) {
89499
89501
  }
89500
89502
 
89501
89503
  // src/server/ws.ts
89502
- var import_node_crypto4 = __toESM(require("node:crypto"), 1);
89504
+ var import_node_crypto5 = __toESM(require("node:crypto"), 1);
89503
89505
  var WS_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
89504
89506
  var OPCODE_TEXT = 1;
89505
89507
  var OPCODE_CLOSE = 8;
@@ -89507,7 +89509,7 @@ var OPCODE_PING = 9;
89507
89509
  var OPCODE_PONG = 10;
89508
89510
  var MAX_CLIENT_FRAME = 8 * 1024 * 1024;
89509
89511
  function acceptKey(clientKey) {
89510
- return import_node_crypto4.default.createHash("sha1").update(clientKey + WS_MAGIC).digest("base64");
89512
+ return import_node_crypto5.default.createHash("sha1").update(clientKey + WS_MAGIC).digest("base64");
89511
89513
  }
89512
89514
  function encodeFrame(opcode, payload) {
89513
89515
  const len = payload.length;
@@ -89646,7 +89648,7 @@ function hostIdFor(credentialsFile2) {
89646
89648
  }
89647
89649
 
89648
89650
  // src/server/avatars.ts
89649
- var import_node_crypto5 = require("node:crypto");
89651
+ var import_node_crypto6 = require("node:crypto");
89650
89652
  var AVATAR_ROUTE = /^\/api\/avatar\/([a-z]+)\/([^/]+)$/;
89651
89653
  var MAX_BYTES = 512 * 1024;
89652
89654
  var TIMEOUT_MS = 5e3;
@@ -89655,7 +89657,7 @@ var SOURCES = {
89655
89657
  gravatar: { valid: /^[0-9a-f]{64}$/, url: (id2) => `https://gravatar.com/avatar/${id2}?s=64&d=404` }
89656
89658
  };
89657
89659
  var githubAvatar = (login) => `/api/avatar/github/${login}`;
89658
- var gravatarAvatar = (email) => `/api/avatar/gravatar/${(0, import_node_crypto5.createHash)("sha256").update(email.trim().toLowerCase()).digest("hex")}`;
89660
+ var gravatarAvatar = (email) => `/api/avatar/gravatar/${(0, import_node_crypto6.createHash)("sha256").update(email.trim().toLowerCase()).digest("hex")}`;
89659
89661
  function createAvatars(fetchImpl = fetch) {
89660
89662
  const cache = /* @__PURE__ */ new Map();
89661
89663
  const fetchAvatar = async (url) => {
@@ -89687,7 +89689,7 @@ function createAvatars(fetchImpl = fetch) {
89687
89689
  }
89688
89690
 
89689
89691
  // src/server/cloud.ts
89690
- var import_node_crypto6 = __toESM(require("node:crypto"), 1);
89692
+ var import_node_crypto7 = __toESM(require("node:crypto"), 1);
89691
89693
 
89692
89694
  // src/shared/brand.ts
89693
89695
  var RYUGI_MARK_VIEWBOX = "0 0 512 512";
@@ -90002,7 +90004,7 @@ function createCloudRoutes(options) {
90002
90004
  const state = randomToken();
90003
90005
  const verifier = randomToken();
90004
90006
  pendingStates.set(state, { expires: now + LOGIN_STATE_TTL_MS, verifier });
90005
- const challenge = import_node_crypto6.default.createHash("sha256").update(verifier).digest("base64url");
90007
+ const challenge = import_node_crypto7.default.createHash("sha256").update(verifier).digest("base64url");
90006
90008
  return `${origin}/auth/cli?port=${options.port()}&state=${state}&challenge=${challenge}`;
90007
90009
  };
90008
90010
  const cloudFetch = async (method, path22, body) => {
@@ -90074,7 +90076,7 @@ function createCloudRoutes(options) {
90074
90076
  } catch (err) {
90075
90077
  throw new CloudRefused(413, err.message);
90076
90078
  }
90077
- const sha = (data) => import_node_crypto6.default.createHash("sha256").update(data).digest("hex");
90079
+ const sha = (data) => import_node_crypto7.default.createHash("sha256").update(data).digest("hex");
90078
90080
  const hashed = withCloudRuntime(ref.type, packed).map((file) => ({ ...file, hash: sha(file.data) }));
90079
90081
  const content = { title: inspected.title, payload: inspected.payload, files: hashed.map(({ path: path22, hash }) => ({ path: path22, hash })) };
90080
90082
  const fingerprint = sha(JSON.stringify({ title: inspected.title, payload: fingerprintPayload(inspected.payload), files: packed.map((file) => ({ path: file.path, hash: sha(file.data) })) }));
@@ -90448,7 +90450,7 @@ function commentNames({ agentFile, projectDir, cloud }) {
90448
90450
  }
90449
90451
 
90450
90452
  // src/server/viewer-page.ts
90451
- var import_node_crypto7 = require("node:crypto");
90453
+ var import_node_crypto8 = require("node:crypto");
90452
90454
  var escapeHtml2 = (value) => value.replace(/[&<>"']/g, (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[ch]);
90453
90455
  function viewerPage(template, page) {
90454
90456
  const values = {
@@ -90468,12 +90470,12 @@ function viewerPage(template, page) {
90468
90470
  }
90469
90471
  var INLINE_SCRIPT = /<script\b(?![^>]*\bsrc=)[^>]*>([\s\S]*?)<\/script>/gi;
90470
90472
  function allowInlineScripts(csp, html) {
90471
- const hashes = [...html.matchAll(INLINE_SCRIPT)].map(([, body]) => `'sha256-${(0, import_node_crypto7.createHash)("sha256").update(body).digest("base64")}'`);
90473
+ const hashes = [...html.matchAll(INLINE_SCRIPT)].map(([, body]) => `'sha256-${(0, import_node_crypto8.createHash)("sha256").update(body).digest("base64")}'`);
90472
90474
  return hashes.length ? csp.replace(/script-src [^;]*/, (directive) => `${directive} ${hashes.join(" ")}`) : csp;
90473
90475
  }
90474
90476
 
90475
90477
  // src/server/auto-publish.ts
90476
- var import_node_crypto8 = __toESM(require("node:crypto"), 1);
90478
+ var import_node_crypto9 = __toESM(require("node:crypto"), 1);
90477
90479
  var import_node_fs20 = __toESM(require("node:fs"), 1);
90478
90480
  var IDLE_MS = 5 * 60 * 1e3;
90479
90481
  var MAX_WAIT_MS = 15 * 60 * 1e3;
@@ -90508,7 +90510,7 @@ function createAutoPublisher(options) {
90508
90510
  const failures = /* @__PURE__ */ new Map();
90509
90511
  const save = () => writeJsonAtomic(options.stateFile, { dirty: [...dirty], notice, paused });
90510
90512
  const raise = (kind, message, upgradeUrl) => {
90511
- if (notice?.kind !== kind) notice = { id: import_node_crypto8.default.randomUUID(), kind, message, ...upgradeUrl ? { upgradeUrl } : {} };
90513
+ if (notice?.kind !== kind) notice = { id: import_node_crypto9.default.randomUUID(), kind, message, ...upgradeUrl ? { upgradeUrl } : {} };
90512
90514
  };
90513
90515
  let idleTimer = null;
90514
90516
  let maxTimer = null;
@@ -90723,9 +90725,12 @@ var MAX_COMMENT_OP = 64 * 1024;
90723
90725
  var API_AGENT_COMMENTS_ROUTE = /^\/api\/agent\/document\/([^/]+)\/comments$/;
90724
90726
  var API_BOARD_COMMENTS_ROUTE = /^\/api\/artboard\/([^/]+)\/comments$/;
90725
90727
  var API_AGENT_BOARD_COMMENTS_ROUTE = /^\/api\/agent\/artboard\/([^/]+)\/comments$/;
90728
+ var API_AGENT_CHECK_ROUTE = /^\/api\/agent\/([^/]+)\/([^/]+)\/check$/;
90726
90729
  var MAX_DOCUMENT_UPDATE = 8 * 1024 * 1024;
90727
90730
  var MAX_RELAY_FILE = 8 * 1024 * 1024;
90728
90731
  var MAX_RUNTIME_MESSAGE = 2048;
90732
+ var CHECK_WATCH_WAIT_MS = 2e3;
90733
+ var CHECK_MERGE_WAIT_MS = 6e4;
90729
90734
  var MAX_RUNTIME_WARNINGS = 20;
90730
90735
  function readArtifact(dir, ref, runtime = []) {
90731
90736
  if (ref.type === "artboard") return readCanvas(dir);
@@ -91235,6 +91240,17 @@ async function startArtifactsServer(options) {
91235
91240
  });
91236
91241
  return;
91237
91242
  }
91243
+ const agentCheckMatch = API_AGENT_CHECK_ROUTE.exec(pathname);
91244
+ if (agentCheckMatch) {
91245
+ if (req.method !== "POST" || key2 === null || !safeEqual(key2, token)) {
91246
+ send(res, 403, "Forbidden: agent calls need ?key=");
91247
+ return;
91248
+ }
91249
+ void serveAgentCheck(res, agentCheckMatch[1], agentCheckMatch[2]).catch((err) => {
91250
+ if (!res.headersSent) send(res, 500, `Artifacts error: ${err.message}`);
91251
+ });
91252
+ return;
91253
+ }
91238
91254
  const boardCommentsMatch = API_BOARD_COMMENTS_ROUTE.exec(pathname);
91239
91255
  if (boardCommentsMatch) {
91240
91256
  void serveBoardComments(req, res, boardCommentsMatch[1]).catch((err) => {
@@ -91431,6 +91447,31 @@ async function startArtifactsServer(options) {
91431
91447
  }
91432
91448
  }
91433
91449
  }) : null;
91450
+ const serveAgentCheck = async (res, type, id2) => {
91451
+ const ref = artifactKeyOf(`${type}/${id2}`);
91452
+ const dir = ref && resolveArtifactDir(root, ref.type, ref.id);
91453
+ if (!ref || !dir) {
91454
+ sendJson(res, 404, { error: `${type}/${id2} not found in ${root}` });
91455
+ return;
91456
+ }
91457
+ const key2 = `${ref.type}/${ref.id}`;
91458
+ for (let waited = 0; waited < CHECK_WATCH_WAIT_MS && signatures.get(key2) !== artifactSignature(dir, ref.type); waited += 50) {
91459
+ await new Promise((resolve) => setTimeout(resolve, 50));
91460
+ }
91461
+ let merging = false;
91462
+ if (ref.type === "document") {
91463
+ const session = await documents.open(ref.id);
91464
+ const text2 = await import_node_fs23.default.promises.readFile(import_node_path20.default.join(dir, "doc.xml"), "utf8").catch(() => null);
91465
+ if (session && text2 !== null) {
91466
+ session.onFileText(text2);
91467
+ const timeout = new Promise((resolve) => setTimeout(() => resolve("timeout"), CHECK_MERGE_WAIT_MS).unref());
91468
+ merging = await Promise.race([session.settled(), timeout]) === "timeout";
91469
+ }
91470
+ }
91471
+ writeState(ref, dir);
91472
+ const state = JSON.parse(await import_node_fs23.default.promises.readFile(import_node_path20.default.join(stateDir, ref.type, ref.id, "warnings.json"), "utf8"));
91473
+ sendJson(res, 200, merging ? { ...state, merging: true } : state);
91474
+ };
91434
91475
  const watcher = watchTree(
91435
91476
  root,
91436
91477
  (files) => {
@@ -91601,7 +91642,7 @@ function openReady(payload) {
91601
91642
  }
91602
91643
 
91603
91644
  // package.json
91604
- var version = "1.30.0";
91645
+ var version = "1.31.0";
91605
91646
 
91606
91647
  // src/server/main.ts
91607
91648
  async function main() {
@@ -1 +1 @@
1
- import{i as e}from"./rolldown-runtime-COiEfhsA.js";import{n as t,t as n}from"./jsx-runtime-CldxteV5.js";import{a as r}from"./useSandboxFrame-DJc_WnFq.js";import{t as i}from"./index.js";import a from"./CloudDocument-F3ikpU1A.js";import{DocumentView as o,t as s}from"./DocumentPage-DJWzglCT.js";var c=e(t(),1),l=`1.30.0`,u=e=>/^(\d+)\.(\d+)\./.exec(e??``)?.slice(1,3).join(`.`)??null;function d(e,t){let n=u(e);return n!==null&&n===u(t)}var f=(e,t)=>`电脑上的 ryugi 是 ${t??`未知版本`},网页是 ${e},版本对上之前只能评论`,p=n();function m({id:e,state:t,contentBase:n,liveContentBase:u,comments:m}){let{transport:h,presence:g}=s(e,u??n??``),_=g?.online??!1;r(e,{online:g?.online??null});let[v,y]=(0,c.useState)(!1);if((0,c.useEffect)(()=>{_&&y(!0)},[_]),!h||!(v||_))return!t||!n?(0,p.jsx)(i,{children:g===null?`正在连接…`:`还没发布。电脑在线时,这里会实时显示。`}):(0,p.jsx)(a,{id:e,state:t,contentBase:n,comments:m,offline:g!==null&&!_});let b=_&&d(l,g?.version??null),x=_&&!b?f(l,g?.version??null):null;return(0,p.jsx)(o,{id:e,transport:h,live:_,cloud:{online:_,canEdit:b,reason:x}})}export{m as default};
1
+ import{i as e}from"./rolldown-runtime-COiEfhsA.js";import{n as t,t as n}from"./jsx-runtime-CldxteV5.js";import{a as r}from"./useSandboxFrame-DJc_WnFq.js";import{t as i}from"./index.js";import a from"./CloudDocument-F3ikpU1A.js";import{DocumentView as o,t as s}from"./DocumentPage-DJWzglCT.js";var c=e(t(),1),l=`1.31.0`,u=e=>/^(\d+)\.(\d+)\./.exec(e??``)?.slice(1,3).join(`.`)??null;function d(e,t){let n=u(e);return n!==null&&n===u(t)}var f=(e,t)=>`电脑上的 ryugi 是 ${t??`未知版本`},网页是 ${e},版本对上之前只能评论`,p=n();function m({id:e,state:t,contentBase:n,liveContentBase:u,comments:m}){let{transport:h,presence:g}=s(e,u??n??``),_=g?.online??!1;r(e,{online:g?.online??null});let[v,y]=(0,c.useState)(!1);if((0,c.useEffect)(()=>{_&&y(!0)},[_]),!h||!(v||_))return!t||!n?(0,p.jsx)(i,{children:g===null?`正在连接…`:`还没发布。电脑在线时,这里会实时显示。`}):(0,p.jsx)(a,{id:e,state:t,contentBase:n,comments:m,offline:g!==null&&!_});let b=_&&d(l,g?.version??null),x=_&&!b?f(l,g?.version??null):null;return(0,p.jsx)(o,{id:e,transport:h,live:_,cloud:{online:_,canEdit:b,reason:x}})}export{m as default};