ryugi-artifacts 1.11.0 → 1.12.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
@@ -295,11 +295,151 @@ function commentText(value) {
295
295
  return null;
296
296
  }
297
297
 
298
+ // src/cli/config.ts
299
+ var import_node_os = __toESM(require("node:os"), 1);
300
+ var import_node_path6 = __toESM(require("node:path"), 1);
301
+
302
+ // src/server/config.ts
303
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
304
+ var import_node_path3 = __toESM(require("node:path"), 1);
305
+
306
+ // src/shared/cloud.ts
307
+ var isVisibility = (value) => value === "private" || value === "public";
308
+
309
+ // src/server/state.ts
310
+ var import_node_fs4 = __toESM(require("node:fs"), 1);
311
+ var import_node_path2 = __toESM(require("node:path"), 1);
312
+
313
+ // src/shared/validate.ts
314
+ var BOARD_NUMBERS = ["x", "y", "w", "h"];
315
+ var BOARD_REQUIRED = ["file", "page", ...BOARD_NUMBERS];
316
+
317
+ // src/server/state.ts
318
+ function writeJsonAtomic(file, value) {
319
+ import_node_fs4.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
320
+ const tmp = `${file}.${process.pid}.tmp`;
321
+ import_node_fs4.default.writeFileSync(tmp, `${JSON.stringify(value, null, 2)}
322
+ `, { mode: 384 });
323
+ import_node_fs4.default.renameSync(tmp, file);
324
+ }
325
+
326
+ // src/server/config.ts
327
+ var defaults = () => ({ version: 1, artifacts: {} });
328
+ function isCloudRecord(value) {
329
+ if (typeof value !== "object" || value === null) return false;
330
+ const { id, url, publishedAt, visibility } = value;
331
+ return typeof id === "string" && typeof url === "string" && (typeof publishedAt === "string" || publishedAt === null) && isVisibility(visibility);
332
+ }
333
+ function normalize(raw) {
334
+ const config2 = defaults();
335
+ if (typeof raw !== "object" || raw === null) return config2;
336
+ const { autoPublish } = raw;
337
+ if (typeof autoPublish === "boolean") config2.autoPublish = autoPublish;
338
+ const artifacts = raw.artifacts;
339
+ if (typeof artifacts !== "object" || artifacts === null) return config2;
340
+ for (const [key, value] of Object.entries(artifacts)) {
341
+ if (typeof value !== "object" || value === null) continue;
342
+ const { visibility, cloud, autoPublish: autoPublish2 } = value;
343
+ if (visibility !== void 0 && !isVisibility(visibility)) continue;
344
+ const entry = {};
345
+ if (visibility) entry.visibility = visibility;
346
+ if (isCloudRecord(cloud)) entry.cloud = { id: cloud.id, url: cloud.url, publishedAt: cloud.publishedAt, visibility: cloud.visibility };
347
+ if (autoPublish2 === false) entry.autoPublish = false;
348
+ config2.artifacts[key] = entry;
349
+ }
350
+ return config2;
351
+ }
352
+ function readConfig(file) {
353
+ let text2;
354
+ try {
355
+ text2 = import_node_fs5.default.readFileSync(file, "utf8");
356
+ } catch {
357
+ return { config: defaults() };
358
+ }
359
+ try {
360
+ return { config: normalize(JSON.parse(text2)) };
361
+ } catch (err) {
362
+ return { config: defaults(), warning: `config.json is not valid JSON: ${err.message}` };
363
+ }
364
+ }
365
+ function ensureGitignore(dir) {
366
+ const file = import_node_path3.default.join(dir, ".gitignore");
367
+ let current = "";
368
+ try {
369
+ current = import_node_fs5.default.readFileSync(file, "utf8");
370
+ } catch {
371
+ import_node_fs5.default.writeFileSync(file, "config.json\n");
372
+ return;
373
+ }
374
+ if (current.split(/\r?\n/).some((line) => line.trim() === "config.json")) return;
375
+ import_node_fs5.default.writeFileSync(file, `${current}${current === "" || current.endsWith("\n") ? "" : "\n"}config.json
376
+ `);
377
+ }
378
+ function ensureConfig(file) {
379
+ import_node_fs5.default.mkdirSync(import_node_path3.default.dirname(file), { recursive: true });
380
+ ensureGitignore(import_node_path3.default.dirname(file));
381
+ if (!import_node_fs5.default.existsSync(file)) writeJsonAtomic(file, defaults());
382
+ }
383
+ function setProjectAutoPublish(file, value) {
384
+ const { config: config2, warning } = readConfig(file);
385
+ if (warning) throw new Error(warning);
386
+ if (value === null) delete config2.autoPublish;
387
+ else config2.autoPublish = value;
388
+ import_node_fs5.default.mkdirSync(import_node_path3.default.dirname(file), { recursive: true });
389
+ writeJsonAtomic(file, config2);
390
+ }
391
+
392
+ // src/server/credentials.ts
393
+ var import_node_path4 = __toESM(require("node:path"), 1);
394
+ function credentialsFile(env, platform, home) {
395
+ if (platform === "win32" && env.APPDATA) return import_node_path4.default.join(env.APPDATA, "ryugi", "credentials.json");
396
+ return import_node_path4.default.join(env.XDG_CONFIG_HOME || import_node_path4.default.join(home, ".config"), "ryugi", "credentials.json");
397
+ }
398
+
399
+ // src/server/settings.ts
400
+ var import_node_fs6 = __toESM(require("node:fs"), 1);
401
+ var import_node_path5 = __toESM(require("node:path"), 1);
402
+ var settingsFileFor = (credentialsFile2) => import_node_path5.default.join(import_node_path5.default.dirname(credentialsFile2), "config.json");
403
+ function readSettings(file) {
404
+ try {
405
+ const raw = JSON.parse(import_node_fs6.default.readFileSync(file, "utf8"));
406
+ return typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
407
+ } catch {
408
+ return {};
409
+ }
410
+ }
411
+ function readGlobalAutoPublish(file) {
412
+ const value = readSettings(file).autoPublish;
413
+ return typeof value === "boolean" ? value : void 0;
414
+ }
415
+ function writeGlobalAutoPublish(file, value) {
416
+ import_node_fs6.default.mkdirSync(import_node_path5.default.dirname(file), { recursive: true, mode: 448 });
417
+ writeJsonAtomic(file, { ...readSettings(file), autoPublish: value });
418
+ }
419
+
420
+ // src/cli/config.ts
421
+ var VALUES = { on: true, off: false, inherit: null };
422
+ function config(options, emit) {
423
+ const globalFile = settingsFileFor(credentialsFile(process.env, process.platform, import_node_os.default.homedir()));
424
+ const projectFile = import_node_path6.default.join(import_node_path6.default.dirname(projectPaths(options.projectDir).root), "config.json");
425
+ if (options.autoPublish !== void 0) {
426
+ const value = VALUES[options.autoPublish];
427
+ if (value === void 0) throw new CliError("--auto-publish takes on, off, or (with --project) inherit");
428
+ if (options.project) {
429
+ ensureConfig(projectFile);
430
+ setProjectAutoPublish(projectFile, value);
431
+ } else if (value === null) throw new CliError("inherit only applies with --project");
432
+ else writeGlobalAutoPublish(globalFile, value);
433
+ }
434
+ emit({ type: "auto-publish", global: readGlobalAutoPublish(globalFile) ?? null, project: readConfig(projectFile).config.autoPublish ?? null });
435
+ return 0;
436
+ }
437
+
298
438
  // src/cli/start.ts
299
439
  var import_node_child_process2 = require("node:child_process");
300
440
  var import_node_crypto = __toESM(require("node:crypto"), 1);
301
- var import_node_fs4 = __toESM(require("node:fs"), 1);
302
- var import_node_path2 = __toESM(require("node:path"), 1);
441
+ var import_node_fs7 = __toESM(require("node:fs"), 1);
442
+ var import_node_path7 = __toESM(require("node:path"), 1);
303
443
  var import_node_readline = __toESM(require("node:readline"), 1);
304
444
  var windowsLike = () => process.platform === "win32";
305
445
  function withLink(info, artifact, reused) {
@@ -314,13 +454,13 @@ function withLink(info, artifact, reused) {
314
454
  async function acquireLock(lock) {
315
455
  for (let attempt = 0; attempt < 150; attempt++) {
316
456
  try {
317
- import_node_fs4.default.mkdirSync(lock);
457
+ import_node_fs7.default.mkdirSync(lock);
318
458
  return true;
319
459
  } catch {
320
460
  }
321
461
  try {
322
- if (Date.now() - import_node_fs4.default.statSync(lock).mtimeMs > 6e4) {
323
- import_node_fs4.default.rmdirSync(lock);
462
+ if (Date.now() - import_node_fs7.default.statSync(lock).mtimeMs > 6e4) {
463
+ import_node_fs7.default.rmdirSync(lock);
324
464
  continue;
325
465
  }
326
466
  } catch {
@@ -350,13 +490,13 @@ function registerOwner(paths) {
350
490
  const owner = ownerPid();
351
491
  if (owner) owners.add(owner);
352
492
  const tmp = `${paths.owners}.${process.pid}`;
353
- import_node_fs4.default.writeFileSync(tmp, [...owners].sort((a, b) => a - b).map((pid) => `${pid}
493
+ import_node_fs7.default.writeFileSync(tmp, [...owners].sort((a, b) => a - b).map((pid) => `${pid}
354
494
  `).join(""));
355
- import_node_fs4.default.renameSync(tmp, paths.owners);
495
+ import_node_fs7.default.renameSync(tmp, paths.owners);
356
496
  }
357
497
  function recordAgent(paths) {
358
498
  const markers = Object.fromEntries(AGENT_MARKERS.flatMap((key) => process.env[key] ? [[key, process.env[key]]] : []));
359
- if (Object.keys(markers).length) import_node_fs4.default.writeFileSync(paths.agent, `${JSON.stringify(markers)}
499
+ if (Object.keys(markers).length) import_node_fs7.default.writeFileSync(paths.agent, `${JSON.stringify(markers)}
360
500
  `);
361
501
  }
362
502
  async function replace(pid, paths) {
@@ -388,9 +528,9 @@ async function start(options, emit) {
388
528
  const foreground = options.foreground || !options.background && (!!process.env.CODEX_CI || windowsLike());
389
529
  process.umask(63);
390
530
  const paths = projectPaths(options.projectDir);
391
- import_node_fs4.default.mkdirSync(paths.state, { recursive: true });
392
- if (!import_node_fs4.default.existsSync(import_node_path2.default.join(paths.state, ".gitignore"))) import_node_fs4.default.writeFileSync(import_node_path2.default.join(paths.state, ".gitignore"), "*\n");
393
- const server = import_node_path2.default.join(__dirname, "server.cjs");
531
+ import_node_fs7.default.mkdirSync(paths.state, { recursive: true });
532
+ if (!import_node_fs7.default.existsSync(import_node_path7.default.join(paths.state, ".gitignore"))) import_node_fs7.default.writeFileSync(import_node_path7.default.join(paths.state, ".gitignore"), "*\n");
533
+ const server = import_node_path7.default.join(__dirname, "server.cjs");
394
534
  const version = process.env.RYUGI_ARTIFACTS_VERSION ?? "";
395
535
  if (!await acquireLock(paths.lock)) throw new CliError("Another start for this project is still starting; retry in a moment");
396
536
  let locked = true;
@@ -398,7 +538,7 @@ async function start(options, emit) {
398
538
  if (!locked) return;
399
539
  locked = false;
400
540
  try {
401
- import_node_fs4.default.rmdirSync(paths.lock);
541
+ import_node_fs7.default.rmdirSync(paths.lock);
402
542
  } catch {
403
543
  }
404
544
  };
@@ -416,17 +556,17 @@ async function start(options, emit) {
416
556
  }
417
557
  registerOwner(paths);
418
558
  recordAgent(paths);
419
- import_node_fs4.default.rmSync(paths.info, { force: true });
559
+ import_node_fs7.default.rmSync(paths.info, { force: true });
420
560
  const id = import_node_crypto.default.randomBytes(24).toString("hex");
421
- import_node_fs4.default.writeFileSync(paths.serverId, `${id}
561
+ import_node_fs7.default.writeFileSync(paths.serverId, `${id}
422
562
  `);
423
- import_node_fs4.default.writeFileSync(paths.version, `${version}
563
+ import_node_fs7.default.writeFileSync(paths.version, `${version}
424
564
  `);
425
565
  const env = serverEnv(paths, options);
426
566
  const args = [server, `--artifacts-server-id=${id}`];
427
567
  if (foreground) {
428
568
  const child2 = (0, import_node_child_process2.spawn)(process.execPath, args, { env, stdio: ["ignore", "pipe", "inherit"] });
429
- import_node_fs4.default.writeFileSync(paths.pid, `${child2.pid}
569
+ import_node_fs7.default.writeFileSync(paths.pid, `${child2.pid}
430
570
  `);
431
571
  for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => child2.kill("SIGTERM"));
432
572
  import_node_readline.default.createInterface({ input: child2.stdout }).on("line", (line) => {
@@ -441,15 +581,15 @@ async function start(options, emit) {
441
581
  emit(value.type === "server-started" ? withLink(value, options.artifact, false) : value);
442
582
  });
443
583
  const exited = new Promise((resolve) => child2.on("close", (code) => resolve(code ?? 1)));
444
- for (let waited = 0; waited < 5e3 && !import_node_fs4.default.existsSync(paths.info); waited += 100) await sleep(100);
584
+ for (let waited = 0; waited < 5e3 && !import_node_fs7.default.existsSync(paths.info); waited += 100) await sleep(100);
445
585
  unlock();
446
586
  return await exited;
447
587
  }
448
- const log = import_node_fs4.default.openSync(paths.log, "w");
588
+ const log = import_node_fs7.default.openSync(paths.log, "w");
449
589
  const child = (0, import_node_child_process2.spawn)(process.execPath, args, { env, detached: true, stdio: ["ignore", log, log] });
450
- import_node_fs4.default.closeSync(log);
590
+ import_node_fs7.default.closeSync(log);
451
591
  child.unref();
452
- import_node_fs4.default.writeFileSync(paths.pid, `${child.pid}
592
+ import_node_fs7.default.writeFileSync(paths.pid, `${child.pid}
453
593
  `);
454
594
  for (let waited = 0; waited < 5e3; waited += 100) {
455
595
  const lines = (readText(paths.log) ?? "").split("\n");
@@ -509,13 +649,13 @@ async function publish(options, emit) {
509
649
  }
510
650
 
511
651
  // src/cli/stop.ts
512
- var import_node_fs5 = __toESM(require("node:fs"), 1);
652
+ var import_node_fs8 = __toESM(require("node:fs"), 1);
513
653
  async function stop(projectDir, emit) {
514
654
  const paths = projectPaths(projectDir);
515
655
  const markStopped = (reason) => {
516
- import_node_fs5.default.rmSync(paths.info, { force: true });
517
- import_node_fs5.default.mkdirSync(paths.state, { recursive: true });
518
- import_node_fs5.default.writeFileSync(paths.stopped, `${JSON.stringify({ reason, timestamp: Math.floor(Date.now() / 1e3) })}
656
+ import_node_fs8.default.rmSync(paths.info, { force: true });
657
+ import_node_fs8.default.mkdirSync(paths.state, { recursive: true });
658
+ import_node_fs8.default.writeFileSync(paths.stopped, `${JSON.stringify({ reason, timestamp: Math.floor(Date.now() / 1e3) })}
519
659
  `);
520
660
  };
521
661
  const pid = Number(readText(paths.pid));
@@ -525,8 +665,8 @@ async function stop(projectDir, emit) {
525
665
  }
526
666
  const id = readText(paths.serverId) ?? "";
527
667
  if (!Number.isInteger(pid) || pid <= 0 || !/^[A-Za-z0-9_-]{32,64}$/.test(id) || !hasServerId(pid, id)) {
528
- import_node_fs5.default.rmSync(paths.pid, { force: true });
529
- import_node_fs5.default.rmSync(paths.serverId, { force: true });
668
+ import_node_fs8.default.rmSync(paths.pid, { force: true });
669
+ import_node_fs8.default.rmSync(paths.serverId, { force: true });
530
670
  markStopped("stale_pid");
531
671
  emit({ status: "stale_pid" });
532
672
  return 0;
@@ -546,7 +686,7 @@ async function stop(projectDir, emit) {
546
686
  emit({ status: "failed", error: "process still running" });
547
687
  return 1;
548
688
  }
549
- for (const file of [paths.pid, paths.serverId, paths.log, paths.owners]) import_node_fs5.default.rmSync(file, { force: true });
689
+ for (const file of [paths.pid, paths.serverId, paths.log, paths.owners]) import_node_fs8.default.rmSync(file, { force: true });
550
690
  markStopped("stop");
551
691
  emit({ status: "stopped" });
552
692
  return 0;
@@ -571,6 +711,10 @@ Commands:
571
711
  reply --artifact document/<id> --thread <threadId> [--text <text>]
572
712
  resolve --artifact document/<id> --thread <threadId>
573
713
  Without --text, add and reply read the message from stdin.
714
+ config Auto-publish changed artifacts as private cloud copies when the agent's turn ends:
715
+ --auto-publish on|off for every project
716
+ --auto-publish on|off|inherit --project override for this project only
717
+ Without --auto-publish, prints the current values.
574
718
 
575
719
  Options:
576
720
  --project-dir <path> Project directory (default: current directory)
@@ -596,6 +740,7 @@ function text(value) {
596
740
  if (value.type === "login-required" || value.type === "login") return `${value.message}
597
741
  ${value.url}`;
598
742
  if (value.type === "published") return `${value.url} (${value.visibility})`;
743
+ 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`}.`;
599
744
  const commented = commentText(value);
600
745
  if (commented !== null) return commented;
601
746
  if (typeof value.status === "string") return value.status === "stopped" ? "Stopped." : value.status.replace(/_/g, " ");
@@ -706,8 +851,16 @@ async function main(argv) {
706
851
  text: values.text
707
852
  }, emit);
708
853
  }
854
+ if (command === "config") {
855
+ const { values } = parse(rest, { "auto-publish": { type: "string" }, project: { type: "boolean" } });
856
+ return config({
857
+ projectDir: values["project-dir"] ?? process.cwd(),
858
+ autoPublish: values["auto-publish"],
859
+ project: !!values.project
860
+ }, emit);
861
+ }
709
862
  if (command === "--version" || command === "-v") {
710
- console.log("1.11.0");
863
+ console.log("1.12.0");
711
864
  return 0;
712
865
  }
713
866
  if (command === "--help" || command === "-h") {