@kw-yeh/vibeflow 4.11.34 → 4.11.36

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/vibeflow.mjs CHANGED
@@ -494,6 +494,9 @@ var init_tmux_backend = __esm({
494
494
  write(key, data) {
495
495
  this.clients.get(key)?.proc.write(data);
496
496
  }
497
+ isAttached(key) {
498
+ return this.clients.has(key);
499
+ }
497
500
  resize(key, cols, rows) {
498
501
  try {
499
502
  this.clients.get(key)?.proc.resize(Math.max(cols, 1), Math.max(rows, 1));
@@ -543,6 +546,7 @@ function boardEnvPrefix(task, opts, workspacePath) {
543
546
  if (!board) return "";
544
547
  const entries = [
545
548
  ["VIBEFLOW_TASK_ID", task.id],
549
+ ["VIBEFLOW_MASTER_ID", task.masterId],
546
550
  ["VIBEFLOW_PROJECT_PATH", task.projectPath],
547
551
  ["VIBEFLOW_BRANCH", task.branch],
548
552
  ["VIBEFLOW_BASE_BRANCH", task.baseBranch],
@@ -594,6 +598,26 @@ function buildClaudeSettings(worktreePath) {
594
598
  function resolveSystemPrompt(custom) {
595
599
  return custom && custom.trim() ? custom : "";
596
600
  }
601
+ function slaveSystemPrompt(opts) {
602
+ const lines = [
603
+ "\u9019\u5F35\u5361\u7247\u7531 VibeFlow \u7684 Master agent \u6D3E\u5DE5\u4E26\u7D71\u7C4C\uFF08master \u7684 id \u5728 $VIBEFLOW_MASTER_ID\uFF09\uFF1B\u4F7F\u7528\u8005\u4E3B\u8981\u900F\u904E master \u8DDF\u4F60\u5354\u4F5C\u3002",
604
+ '\u4F60\u6536\u5230\u7684 <vibeflow from="\u2026"> \u8A0A\u606F\u4F86\u81EA master \u6216 VibeFlow \u672C\u8EAB\uFF0C\u7167\u5176\u4E2D\u7684\u6307\u793A\u505A\u3002'
605
+ ];
606
+ if (!opts.canWrite) {
607
+ lines.push("\u9019\u6B21\u555F\u52D5\u7121\u6CD5\u4F7F\u7528 board CLI\uFF0C\u4F60\u4E0D\u80FD\u4E3B\u52D5\u56DE\u5831\uFF1B\u5B8C\u6210\u6216\u5361\u4F4F\u6642\u76F4\u63A5\u5728 terminal \u5BEB\u6E05\u695A\u7D50\u679C\u3002");
608
+ return lines.join("\n");
609
+ }
610
+ lines.push(
611
+ "",
612
+ ...BOARD_CLI_SETUP,
613
+ "",
614
+ "\u56DE\u5831\uFF1A\u4EFB\u52D9\u5B8C\u6210\u3001\u9700\u8981 master \u6C7A\u5B9A\u3001\u6216\u5361\u4F4F\u7121\u6CD5\u7E7C\u7E8C\u6642\uFF0C\u7ACB\u523B\u7528\u9019\u500B\u6307\u4EE4\u56DE\u5831\uFF0C\u7136\u5F8C\u505C\u4E0B\u4F86\u7B49\u4E0B\u4E00\u6B65\u6307\u793A\uFF1A",
615
+ ' VF task send --store-path "$VIBEFLOW_STORE_DIR" --task "$VIBEFLOW_MASTER_ID" --text "<\u56DE\u5831\u5167\u5BB9>"',
616
+ "\u56DE\u5831\u5167\u5BB9\u8981\u81EA\u5305\u542B\uFF1A\u505A\u4E86\u4EC0\u9EBC\u3001\u600E\u9EBC\u9A57\u8B49\u3001\u7D50\u679C\uFF1B\u9A57\u6536\u985E\u5DE5\u4F5C\u9010\u9805\u5217\u51FA\u901A\u904E\u8207\u672A\u901A\u904E\u7684\u9805\u76EE\u53CA\u91CD\u73FE\u6B65\u9A5F\uFF1B\u958B\u4E86 PR \u5C31\u9644\u4E0A\u9023\u7D50\u3002",
617
+ "\u6C92\u6709 master \u8981\u6C42\u5C31\u4E0D\u8981\u958B PR\u3002"
618
+ );
619
+ return lines.join("\n");
620
+ }
597
621
  function masterSystemPrompt(opts) {
598
622
  const lines = [
599
623
  "\u4F60\u662F VibeFlow \u7684 Master agent\uFF0C\u4E0D\u5C6C\u65BC\u4EFB\u4F55 repo\uFF0C\u5DE5\u4F5C\u76EE\u9304\u53EA\u662F\u4F60\u7684\u7B46\u8A18\u5340\u3002",
@@ -608,8 +632,7 @@ function masterSystemPrompt(opts) {
608
632
  return lines.join("\n");
609
633
  }
610
634
  lines.push(
611
- '\u5148\u5B9A\u7FA9\u9019\u500B shell \u51FD\u5F0F\uFF0C\u4E4B\u5F8C\u6240\u6709\u6307\u4EE4\u90FD\u7528\u5B83\uFF0C\u4E26\u4E14\u4E00\u5F8B\u5E36\u4E0A --store-path "$VIBEFLOW_STORE_DIR"\uFF1A',
612
- ' VF() { if [ -n "$VIBEFLOW_CLI_LOADER" ]; then node --experimental-strip-types --import "$VIBEFLOW_CLI_LOADER" "$VIBEFLOW_CLI" "$@"; else node "$VIBEFLOW_CLI" "$@"; fi; }',
635
+ ...BOARD_CLI_SETUP,
613
636
  "",
614
637
  "1. \u5224\u65B7\uFF1A\u4E00\u5F35\u5361\u505A\u5F97\u5B8C\u5C31\u53EA\u5EFA\u4E00\u5F35\uFF1B\u6D89\u53CA\u591A\u500B repo \u6216\u53EF\u5E73\u884C\u7684\u7368\u7ACB\u5DE5\u4F5C\u624D\u62C6\u6210\u591A\u5F35\u3002\u6BCF\u5F35\u53EA\u52D5\u4E00\u500B repo\uFF0C\u5404\u81EA\u80FD\u7368\u7ACB\u9A57\u6536\u3002",
615
638
  '2. \u6D3E\u5DE5\uFF1AVF task create --store-path "$VIBEFLOW_STORE_DIR" --master "$VIBEFLOW_TASK_ID" --project <repo \u7D55\u5C0D\u8DEF\u5F91> --title <\u6A19\u984C> --prompt <\u5167\u5BB9>',
@@ -620,9 +643,13 @@ function masterSystemPrompt(opts) {
620
643
  opts.autoMode ? '3. \u555F\u52D5\uFF1A\u6C92\u6709\u524D\u7F6E\u4F9D\u8CF4\u7684\u5361\u76F4\u63A5 VF task start --store-path "$VIBEFLOW_STORE_DIR" --task <id>\uFF1B\u6709\u4F9D\u8CF4\u7684\u5148\u7559\u5728 Backlog\uFF0C\u524D\u7F6E\u5361\u5B8C\u6210\u5F8C\u518D\u555F\u52D5\u3002' : "3. \u555F\u52D5\uFF1A\u4E0D\u8981\u81EA\u5DF1\u555F\u52D5\u5361\u7247\u3002\u5217\u51FA\u4F60\u5EFA\u4E86\u54EA\u4E9B\u5361\u3001\u5EFA\u8B70\u7684\u555F\u52D5\u9806\u5E8F\uFF0C\u7B49\u4F7F\u7528\u8005\u5728 VibeFlow \u6309\u958B\u59CB\u3002"
621
644
  );
622
645
  lines.push(
623
- '4. \u76E3\u807D\uFF1AVF task list --store-path "$VIBEFLOW_STORE_DIR" --master "$VIBEFLOW_TASK_ID" \u6703\u56DE\u50B3\u6BCF\u5F35\u5361\u7684\u6B04\u4F4D\u3001\u662F\u5426\u5728\u8DD1\u3001todo \u9032\u5EA6\u3001\u6D3B\u52D5\u72C0\u614B\u8207 PR\u3002',
624
- " \u5B9A\u671F\u67E5\u770B\uFF1B\u5361\u5728\u7B49\u5F85\u6B0A\u9650\u3001\u6709 launchError\u3001\u6216\u9577\u6642\u9593\u6C92\u6709\u6D3B\u52D5\u6642\uFF0C\u544A\u8A34\u4F7F\u7528\u8005\u4E26\u63D0\u51FA\u8655\u7406\u65B9\u5F0F\u3002",
625
- "5. \u4F60\u4E0D\u6703\u628A\u5361\u7247\u79FB\u5230 Done\u3001\u4E0D\u6703\u5408\u4F75 PR\u3002\u5168\u90E8 slave \u5B8C\u6210\u5F8C\uFF0C\u6574\u7406\u6BCF\u5F35\u7684\u7D50\u679C\u8207 PR \u9023\u7D50\uFF0C\u4EA4\u7D66\u4F7F\u7528\u8005\u505A\u6700\u5F8C\u78BA\u8A8D\u3002"
646
+ "4. \u56DE\u5831\u6703\u81EA\u5DF1\u9001\u4F86\uFF1Aslave \u505A\u5B8C\u3001\u8981\u4F60\u6C7A\u5B9A\u6216\u5361\u4F4F\u6642\u6703\u7528 task send \u56DE\u5831\uFF1Bslave \u5361\u5728\u6B0A\u9650\u78BA\u8A8D\u3001\u555F\u52D5\u5931\u6557\u3001session \u7D50\u675F\u3001\u6216\u56DE\u5408\u7D50\u675F\u537B\u6C92\u56DE\u5831\u6642\uFF0CVibeFlow \u4E5F\u6703\u901A\u77E5\u4F60\u3002",
647
+ ' \u9019\u4E9B\u8A0A\u606F\u4EE5 <vibeflow from="\u2026"> \u5305\u4F4F\u3001\u76F4\u63A5\u51FA\u73FE\u5728\u4F60\u7684\u8F38\u5165\u88E1\u3002\u4E0D\u8981\u8F2A\u8A62 task list\uFF1B\u8981\u770B\u7D30\u7BC0\u6642\u624D\u7528 VF task show --store-path "$VIBEFLOW_STORE_DIR" --task <id>\u3002',
648
+ '5. \u8ABF\u5EA6\uFF1A\u7528 VF task send --store-path "$VIBEFLOW_STORE_DIR" --task <id> --text <\u5167\u5BB9> \u5C0D\u81EA\u5DF1\u7684\u5361\u4E0B\u6307\u793A\uFF1B\u5361\u8207\u5361\u4E4B\u9593\u4E0D\u80FD\u76F4\u63A5\u50B3\u8A71\uFF0C\u4E00\u5F8B\u7D93\u904E\u4F60\u3002',
649
+ " \u9700\u8981\u7368\u7ACB\u9A57\u6536\u6642\u53E6\u958B\u4E00\u5F35\u9A57\u6536\u5361\u3002\u9A57\u6536\u56DE\u5831\u554F\u984C\u6642\uFF0C\u628A\u5177\u9AD4\u554F\u984C\u8207\u91CD\u73FE\u6B65\u9A5F\u6574\u7406\u597D\u8F49\u7D66\u8CA0\u8CAC\u958B\u767C\u7684\u5361\u4FEE\u6B63\uFF0C\u4FEE\u5B8C\u518D\u8ACB\u9A57\u6536\u5361\u8907\u9A57\uFF0C\u76F4\u5230\u901A\u904E\u3002",
650
+ " task send \u56DE TARGET_BLOCKED \u4EE3\u8868\u5C0D\u65B9\u5361\u5728\u6B0A\u9650\u78BA\u8A8D\uFF0C\u8ACB\u4F7F\u7528\u8005\u5230\u90A3\u5F35\u5361\u8655\u7406\u3002",
651
+ "6. PR\uFF1A\u9A57\u6536\u901A\u904E\u5F8C\uFF0C\u4EA4\u4EE3\u958B\u767C\u5361\u7528 pr skill \u958B PR\uFF08\u74B0\u5883\u88E1\u6C92\u6709\u9019\u500B skill \u5C31\u7528 gh pr create\uFF09\uFF0C\u4E26\u56DE\u5831 PR \u9023\u7D50\u7D66\u4F60\u3002",
652
+ "7. \u4F7F\u7528\u8005\u4E3B\u8981\u53EA\u8DDF\u4F60\u6E9D\u901A\uFF1A\u4E3B\u52D5\u5411\u4F7F\u7528\u8005\u56DE\u5831\u6574\u9AD4\u9032\u5EA6\u8207\u9700\u8981\u4F7F\u7528\u8005\u6C7A\u5B9A\u7684\u4E8B\u3002\u4F60\u4E0D\u6703\u628A\u5361\u7247\u79FB\u5230 Done\u3001\u4E0D\u6703\u5408\u4F75 PR\uFF1B\u5168\u90E8\u5B8C\u6210\u5F8C\u6574\u7406\u6BCF\u5F35\u5361\u7684\u7D50\u679C\u8207 PR \u9023\u7D50\uFF0C\u4EA4\u7D66\u4F7F\u7528\u8005\u505A\u6700\u5F8C\u78BA\u8A8D\u3002"
626
653
  );
627
654
  return lines.join("\n");
628
655
  }
@@ -687,7 +714,9 @@ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
687
714
  const agent = taskAgent(task);
688
715
  const model = taskModel(task);
689
716
  const custom = resolveSystemPrompt(systemPrompt);
690
- const sys = task.role === "master" ? [custom, masterSystemPrompt({ canWrite: Boolean(opts?.boardCli?.cliPath), autoMode: opts?.autoMode === true })].filter(Boolean).join("\n\n") : custom;
717
+ const canWrite = Boolean(opts?.boardCli?.cliPath);
718
+ const role = task.role === "master" ? masterSystemPrompt({ canWrite, autoMode: opts?.autoMode === true }) : task.masterId ? slaveSystemPrompt({ canWrite }) : null;
719
+ const sys = role ? [custom, role].filter(Boolean).join("\n\n") : custom;
691
720
  const includeTaskPrompt = opts?.includeTaskPrompt !== false;
692
721
  const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
693
722
  const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
@@ -703,7 +732,7 @@ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
703
732
  workspacePath
704
733
  );
705
734
  }
706
- var ARTIFACTS_DIR_SUFFIX, SCRATCH_DIR_NAME, ARTIFACTS_FALLBACK_DIR, AUTO_PERMISSION_MODE, SUBAGENTS_DIR2, PROGRESS_EVENTS_DIR, TODO_TOOLS, AGENT_NAMES, RETIRED_MODELS;
735
+ var ARTIFACTS_DIR_SUFFIX, SCRATCH_DIR_NAME, ARTIFACTS_FALLBACK_DIR, AUTO_PERMISSION_MODE, SUBAGENTS_DIR2, PROGRESS_EVENTS_DIR, TODO_TOOLS, BOARD_CLI_SETUP, AGENT_NAMES, RETIRED_MODELS;
707
736
  var init_launch = __esm({
708
737
  "packages/core/src/launch.ts"() {
709
738
  "use strict";
@@ -714,6 +743,10 @@ var init_launch = __esm({
714
743
  SUBAGENTS_DIR2 = ".vibeflow-subagents";
715
744
  PROGRESS_EVENTS_DIR = ".vibeflow-events";
716
745
  TODO_TOOLS = "TodoWrite|TaskCreate|TaskUpdate";
746
+ BOARD_CLI_SETUP = [
747
+ '\u5148\u5B9A\u7FA9\u9019\u500B shell \u51FD\u5F0F\uFF0C\u4E4B\u5F8C\u6240\u6709\u6307\u4EE4\u90FD\u7528\u5B83\uFF0C\u4E26\u4E14\u4E00\u5F8B\u5E36\u4E0A --store-path "$VIBEFLOW_STORE_DIR"\uFF1A',
748
+ ' VF() { if [ -n "$VIBEFLOW_CLI_LOADER" ]; then node --experimental-strip-types --import "$VIBEFLOW_CLI_LOADER" "$VIBEFLOW_CLI" "$@"; else node "$VIBEFLOW_CLI" "$@"; fi; }'
749
+ ];
717
750
  AGENT_NAMES = {
718
751
  claude: "Claude Code",
719
752
  codex: "Codex CLI"
@@ -2579,6 +2612,8 @@ function createBridge(t) {
2579
2612
  startTask: (taskId) => t.invoke("vibeflow:startTask", taskId),
2580
2613
  /** Cards as a master sees them; filter by master or a single card. */
2581
2614
  taskSummaries: (filter) => t.invoke("task:summaries", filter),
2615
+ /** Type a message into another card's agent: a master to its cards, a card to its master. */
2616
+ sendMessage: (payload) => t.invoke("vibeflow:sendMessage", payload),
2582
2617
  /** Clear prior execution state and prepare a fresh run of this task. */
2583
2618
  resetTaskRun: (taskId) => t.invoke("vibeflow:resetTaskRun", taskId),
2584
2619
  removeTask: (taskId) => t.invoke("vibeflow:removeTask", taskId),
@@ -4748,7 +4783,7 @@ function connectToHost(lock) {
4748
4783
  // packages/cli/src/task-command.ts
4749
4784
  var AGENT_IDS = AGENT_CLIS.map((agent) => agent.id);
4750
4785
  var STATUSES = ["backlog", "in_progress", "done"];
4751
- var SUBCOMMANDS = ["create", "update", "start", "list", "show", "artifact"];
4786
+ var SUBCOMMANDS = ["create", "update", "start", "list", "show", "send", "artifact"];
4752
4787
  var ROLES = ["slave", "master"];
4753
4788
  var MODES = ["existing", "new"];
4754
4789
  var AUTO_MODES = ["on", "off"];
@@ -4761,6 +4796,7 @@ Usage:
4761
4796
  vibeflow task start --task <id> [options]
4762
4797
  vibeflow task list [--master <id>] [options]
4763
4798
  vibeflow task show --task <id> [options]
4799
+ vibeflow task send --task <id> --text <text> [options]
4764
4800
  vibeflow task artifact add --file <path> [options]
4765
4801
 
4766
4802
  task create options:
@@ -4769,7 +4805,8 @@ task create options:
4769
4805
  --project <path> Target project directory (required for a slave)
4770
4806
  --work-dir <path> Master only: run in this existing folder (git or not)
4771
4807
  instead of one VibeFlow creates
4772
- --master <id> Dispatch the new card under this master card
4808
+ --master <id> Dispatch the new card under this master card (default: the
4809
+ calling card, when a master runs this from its own shell)
4773
4810
  --title <text> Task title (required)
4774
4811
  --prompt <text> Task prompt / description (required)
4775
4812
  --status <column> ${STATUSES.join(" | ")} (default: backlog)
@@ -4800,12 +4837,17 @@ task update options:
4800
4837
  At least one of --title / --prompt / --status / --master is required. Branch,
4801
4838
  worktree and project path are provisioned on disk and cannot be changed here.
4802
4839
 
4803
- task start / list / show (need the running VibeFlow host):
4840
+ task start / list / show / send (need the running VibeFlow host):
4804
4841
  start --task <id> Move a Backlog card to In Progress and start its agent,
4805
4842
  the same as dragging it in the app
4806
4843
  list [--master <id>] Every card, or one master's cards, with column, whether
4807
4844
  its agent is running, todo progress, activity and PR
4808
4845
  show --task <id> The same summary for one card
4846
+ send --task <id> --text <text> [--force] [--from <id>]
4847
+ Type a message into that card's agent, tagged with the
4848
+ sender. Only a master and its own cards can message each
4849
+ other. --from defaults to VIBEFLOW_TASK_ID; --force types
4850
+ even into a permission dialog
4809
4851
 
4810
4852
  task artifact add options:
4811
4853
  --file <path> Copy a file into the card's user-facing Artifacts folder
@@ -4884,6 +4926,9 @@ async function taskCommand(argv) {
4884
4926
  "auto-mode": { type: "string" },
4885
4927
  attach: { type: "string", multiple: true },
4886
4928
  file: { type: "string" },
4929
+ text: { type: "string" },
4930
+ from: { type: "string" },
4931
+ force: { type: "boolean" },
4887
4932
  "store-path": { type: "string" },
4888
4933
  profile: { type: "string" },
4889
4934
  help: { type: "boolean", short: "h" }
@@ -4962,11 +5007,13 @@ async function taskCommand(argv) {
4962
5007
  } catch (err) {
4963
5008
  fail("ATTACHMENT_READ_FAILED", `\u7121\u6CD5\u8B80\u53D6\u9644\u4EF6\uFF1A${err.message}`);
4964
5009
  }
5010
+ const caller = process.env.VIBEFLOW_TASK_ID ? findTask(process.env.VIBEFLOW_TASK_ID) : void 0;
5011
+ const masterId = values.master ?? (!master && caller && isMasterTask(caller) ? caller.id : void 0);
4965
5012
  const { task, storePath: resolvedPath } = await createTaskFromInput({
4966
5013
  projectPath: values.project,
4967
5014
  role: master ? "master" : void 0,
4968
5015
  workDir: values["work-dir"],
4969
- masterId: values.master,
5016
+ masterId,
4970
5017
  title: values.title,
4971
5018
  description: values.prompt,
4972
5019
  status: values.status ?? "backlog",
@@ -4998,9 +5045,13 @@ async function taskCommand(argv) {
4998
5045
  }
4999
5046
  };
5000
5047
  }
5001
- var HOST_COMMANDS = ["start", "list", "show"];
5048
+ var HOST_COMMANDS = ["start", "list", "show", "send"];
5002
5049
  async function hostCommand(sub, values, storePath) {
5003
5050
  if (sub !== "list" && !values.task) fail("MISSING_ARGUMENT", "Missing required arguments: --task");
5051
+ const from = values.from ?? process.env.VIBEFLOW_TASK_ID;
5052
+ if (sub === "send" && (!values.text || !from)) {
5053
+ fail("MISSING_ARGUMENT", "task send needs --text and --from (or VIBEFLOW_TASK_ID)");
5054
+ }
5004
5055
  const lock = liveHost(storePath);
5005
5056
  if (!lock) fail("HOST_NOT_RUNNING", `No VibeFlow host is running for ${storePath}. Start it with \`vibeflow\` first.`);
5006
5057
  const remote = connectToHost(lock);
@@ -5009,12 +5060,21 @@ async function hostCommand(sub, values, storePath) {
5009
5060
  const { task } = await remote.api.startTask(values.task);
5010
5061
  return { task: { id: task.id, title: task.title, worktreePath: task.worktreePath ?? null } };
5011
5062
  }
5063
+ if (sub === "send") {
5064
+ try {
5065
+ return await remote.api.sendMessage({ from, to: values.task, text: values.text, force: values.force === true });
5066
+ } catch (err) {
5067
+ const e = err;
5068
+ fail(e.code ?? "HOST_ERROR", e.message ?? String(err));
5069
+ }
5070
+ }
5012
5071
  if (sub === "show") {
5013
5072
  const [task] = await remote.api.taskSummaries({ taskId: values.task });
5014
5073
  return { task };
5015
5074
  }
5016
5075
  return { tasks: await remote.api.taskSummaries(values.master ? { masterId: values.master } : void 0) };
5017
5076
  } catch (err) {
5077
+ if (err instanceof CliFailure) throw err;
5018
5078
  fail("HOST_ERROR", err.message);
5019
5079
  } finally {
5020
5080
  remote.close();
@@ -7199,11 +7259,201 @@ function createProgressTracker(opts) {
7199
7259
  };
7200
7260
  }
7201
7261
 
7262
+ // packages/core/src/mailbox.ts
7263
+ var PASTE_START = "\x1B[200~";
7264
+ var PASTE_END = "\x1B[201~";
7265
+ var PASTE_GAP_MS = 200;
7266
+ var DEFAULT_QUIET_MS = 3e3;
7267
+ var DEFAULT_TIMEOUT_MS = 12e4;
7268
+ function coded(code2, message) {
7269
+ return Object.assign(new Error(message), { code: code2 });
7270
+ }
7271
+ function isBlocked(activity) {
7272
+ return activity?.state === "waiting" && activity.waitingFor === "permission";
7273
+ }
7274
+ function bracketedPaste(text) {
7275
+ return PASTE_START + text.split(PASTE_END).join("") + PASTE_END;
7276
+ }
7277
+ function frameMessage(from, text) {
7278
+ const sender = from ? `${from.id} ${from.title}`.replace(/["\r\n]/g, " ") : "vibeflow";
7279
+ const body = text.replace(/<(\/?)vibeflow/gi, "\u2039$1vibeflow");
7280
+ return `<vibeflow from="${sender}">
7281
+ ${body}
7282
+ </vibeflow>`;
7283
+ }
7284
+ function quietFromEnv() {
7285
+ const n = Number(process.env.VIBEFLOW_QUIET_MS);
7286
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_QUIET_MS;
7287
+ }
7288
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7289
+ function createMailbox(options) {
7290
+ const lastInput = /* @__PURE__ */ new Map();
7291
+ const held = /* @__PURE__ */ new Map();
7292
+ const turns = /* @__PURE__ */ new Map();
7293
+ async function waitForQuiet(key, started) {
7294
+ const quiet = options.quietMs ?? quietFromEnv();
7295
+ const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
7296
+ for (; ; ) {
7297
+ const left = (lastInput.get(key) ?? 0) + quiet - Date.now();
7298
+ if (left <= 0) return;
7299
+ if (Date.now() - started > timeout) throw coded("TIMEOUT", "the terminal never went quiet");
7300
+ await sleep(Math.min(left, 250));
7301
+ }
7302
+ }
7303
+ async function type(key, text, opts, started) {
7304
+ await waitForQuiet(key, started);
7305
+ if (!opts.force && isBlocked(options.activity(key))) {
7306
+ throw coded("TARGET_BLOCKED", "the agent is on a permission dialog; typing could answer it");
7307
+ }
7308
+ held.set(key, []);
7309
+ try {
7310
+ options.write(key, bracketedPaste(text));
7311
+ await sleep(PASTE_GAP_MS);
7312
+ options.write(key, "\r");
7313
+ } finally {
7314
+ const keys = held.get(key) ?? [];
7315
+ held.delete(key);
7316
+ if (keys.length) {
7317
+ options.write(key, keys.join(""));
7318
+ lastInput.set(key, Date.now());
7319
+ }
7320
+ }
7321
+ return { queuedMs: Date.now() - started };
7322
+ }
7323
+ return {
7324
+ userInput(key, data) {
7325
+ const buffer = held.get(key);
7326
+ if (buffer) {
7327
+ buffer.push(data);
7328
+ return;
7329
+ }
7330
+ lastInput.set(key, Date.now());
7331
+ options.write(key, data);
7332
+ },
7333
+ deliver(key, text, opts = {}) {
7334
+ const started = Date.now();
7335
+ const run3 = (turns.get(key) ?? Promise.resolve()).catch(() => {
7336
+ }).then(() => type(key, text, opts, started));
7337
+ turns.set(key, run3);
7338
+ void run3.finally(() => {
7339
+ if (turns.get(key) === run3) turns.delete(key);
7340
+ }).catch(() => {
7341
+ });
7342
+ return run3;
7343
+ },
7344
+ forget(key) {
7345
+ lastInput.delete(key);
7346
+ held.delete(key);
7347
+ }
7348
+ };
7349
+ }
7350
+
7351
+ // packages/core/src/notifier.ts
7352
+ function describe(kind, id, title, detail) {
7353
+ const card = `\u5361\u7247 ${id}\u300C${title}\u300D`;
7354
+ const look = `\u67E5\u770B\uFF1AVF task show --store-path "$VIBEFLOW_STORE_DIR" --task ${id}`;
7355
+ switch (kind) {
7356
+ case "blocked":
7357
+ return `${card} \u5361\u5728\u6B0A\u9650\u78BA\u8A8D\uFF0C\u7B49\u4F7F\u7528\u8005\u5141\u8A31\u624D\u6703\u7E7C\u7E8C\u3002${look}`;
7358
+ case "launch_failed":
7359
+ return `${card} \u555F\u52D5\u5931\u6557\uFF0C\u5DF2\u9000\u56DE Backlog\uFF1A${detail ?? "\u539F\u56E0\u4E0D\u660E"}\u3002${look}`;
7360
+ case "exited":
7361
+ return `${card} \u7684 agent session \u5DF2\u7D50\u675F\u3002${look}`;
7362
+ case "turn_unreported":
7363
+ return `${card} \u9019\u4E00\u56DE\u5408\u7D50\u675F\u4E86\uFF0C\u4F46\u6C92\u6709\u56DE\u5831\u7D50\u679C\uFF08\u53EF\u80FD\u5728\u7B49\u4EBA\u56DE\u7B54\u554F\u984C\uFF09\u3002${look}`;
7364
+ }
7365
+ }
7366
+ function createNotifier(options) {
7367
+ const states = /* @__PURE__ */ new Map();
7368
+ const inTurn = /* @__PURE__ */ new Set();
7369
+ const reportedThisTurn = /* @__PURE__ */ new Set();
7370
+ const told = /* @__PURE__ */ new Map();
7371
+ const pending = /* @__PURE__ */ new Map();
7372
+ const flushing = /* @__PURE__ */ new Set();
7373
+ function note(taskId, kind, detail) {
7374
+ const card = options.cardOf(taskId);
7375
+ if (!card) return;
7376
+ const seen = told.get(taskId) ?? /* @__PURE__ */ new Set();
7377
+ if (seen.has(kind)) return;
7378
+ seen.add(kind);
7379
+ told.set(taskId, seen);
7380
+ const queue = pending.get(card.masterId) ?? [];
7381
+ queue.push(describe(kind, taskId, card.title, detail));
7382
+ pending.set(card.masterId, queue);
7383
+ schedule(card.masterId);
7384
+ }
7385
+ function schedule(masterId) {
7386
+ setTimeout(() => void flush(masterId), 0);
7387
+ }
7388
+ async function flush(masterId) {
7389
+ if (flushing.has(masterId) || !pending.get(masterId)?.length) return;
7390
+ flushing.add(masterId);
7391
+ try {
7392
+ const can = await options.canReceive(masterId);
7393
+ if (can === "never") {
7394
+ pending.delete(masterId);
7395
+ return;
7396
+ }
7397
+ if (can === "later") return;
7398
+ const batch = pending.get(masterId) ?? [];
7399
+ pending.delete(masterId);
7400
+ try {
7401
+ await options.deliver(masterId, batch.join("\n"));
7402
+ } catch {
7403
+ pending.set(masterId, [...batch, ...pending.get(masterId) ?? []]);
7404
+ }
7405
+ } finally {
7406
+ flushing.delete(masterId);
7407
+ }
7408
+ if (pending.get(masterId)?.length) schedule(masterId);
7409
+ }
7410
+ function turnStarted(taskId) {
7411
+ inTurn.add(taskId);
7412
+ reportedThisTurn.delete(taskId);
7413
+ told.delete(taskId);
7414
+ }
7415
+ return {
7416
+ activity(taskId, activity) {
7417
+ const before = states.get(taskId);
7418
+ states.set(taskId, activity);
7419
+ const wasBlocked = before?.state === "waiting" && before.waitingFor === "permission";
7420
+ if (activity.state === "working") {
7421
+ if (wasBlocked) told.get(taskId)?.delete("blocked");
7422
+ else if (before?.state !== "working") turnStarted(taskId);
7423
+ return;
7424
+ }
7425
+ if (activity.state !== "waiting") return;
7426
+ if (activity.waitingFor === "permission") {
7427
+ note(taskId, "blocked");
7428
+ return;
7429
+ }
7430
+ if (inTurn.delete(taskId) && !reportedThisTurn.has(taskId)) note(taskId, "turn_unreported");
7431
+ },
7432
+ launchFailed(taskId, reason) {
7433
+ note(taskId, "launch_failed", reason);
7434
+ },
7435
+ exited(taskId) {
7436
+ inTurn.delete(taskId);
7437
+ note(taskId, "exited");
7438
+ },
7439
+ reported(taskId) {
7440
+ reportedThisTurn.add(taskId);
7441
+ },
7442
+ retry(masterId) {
7443
+ if (pending.get(masterId)?.length) schedule(masterId);
7444
+ }
7445
+ };
7446
+ }
7447
+
7202
7448
  // packages/core/src/service.ts
7203
7449
  var InvalidRequestError = class extends Error {
7204
7450
  code = "INVALID_REQUEST";
7205
7451
  };
7206
7452
  var STANDALONE_KEY = /^terminal_[0-9a-f-]{36}$/;
7453
+ var MAX_MESSAGE_LENGTH = 2e4;
7454
+ function coded2(code2, message) {
7455
+ return Object.assign(new Error(message), { code: code2 });
7456
+ }
7207
7457
  function invalid(message) {
7208
7458
  throw new InvalidRequestError(message);
7209
7459
  }
@@ -7268,7 +7518,11 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7268
7518
  const progress = createProgressTracker({
7269
7519
  homeDir: home,
7270
7520
  codexHomes: () => [path24.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
7271
- onUpdate: (taskId, value) => bus.emit("progress:update", { taskId, progress: value }),
7521
+ onUpdate: (taskId, value) => {
7522
+ bus.emit("progress:update", { taskId, progress: value });
7523
+ notifier.activity(taskId, value.activity);
7524
+ notifier.retry(taskId);
7525
+ },
7272
7526
  onNotify: (taskId, list) => {
7273
7527
  const prefs = resolveNotificationSettings(getSettings());
7274
7528
  if (!prefs.enabled) return;
@@ -7283,6 +7537,27 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7283
7537
  bus.emit("progress:notify", { taskId, title: task.title, notifications });
7284
7538
  }
7285
7539
  });
7540
+ const agentActivity = (key) => progress.isTracking(key) ? progress.snapshot(key)?.activity ?? null : null;
7541
+ const mailbox = createMailbox({
7542
+ write: (key, data) => sessions.write(key, data),
7543
+ activity: agentActivity
7544
+ });
7545
+ const notifier = createNotifier({
7546
+ cardOf: (taskId) => {
7547
+ const task = findTask(taskId);
7548
+ return task?.masterId ? { title: task.title, masterId: task.masterId } : null;
7549
+ },
7550
+ canReceive: async (masterId) => {
7551
+ const master = findTask(masterId);
7552
+ if (!master || !isMasterTask(master) || columnOf(master.id) !== "in_progress") return "never";
7553
+ if (!await sessions.isAlive(master.id)) return "later";
7554
+ return isBlocked(agentActivity(master.id)) ? "later" : "yes";
7555
+ },
7556
+ deliver: async (masterId, text) => {
7557
+ await attachAgent(masterId);
7558
+ await mailbox.deliver(masterId, frameMessage(null, text));
7559
+ }
7560
+ });
7286
7561
  function gitWorktree(task) {
7287
7562
  return isMasterTask(task) ? void 0 : task.worktreePath;
7288
7563
  }
@@ -7325,6 +7600,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7325
7600
  standaloneTerminals.delete(key);
7326
7601
  terminalDirectories.delete(key);
7327
7602
  unwatchSubAgents(key);
7603
+ mailbox.forget(key);
7328
7604
  }
7329
7605
  function isStandaloneKey(key) {
7330
7606
  return standaloneTerminals.has(key) || listTerminals().some((t) => t.sessionKey === key);
@@ -7451,6 +7727,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7451
7727
  };
7452
7728
  setBoard(next);
7453
7729
  bus.emit("state:changed", getState());
7730
+ notifier.launchFailed(taskId, launchError);
7454
7731
  }
7455
7732
  let folderDialog = null;
7456
7733
  const github = createGithubService({
@@ -7486,7 +7763,10 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7486
7763
  cols: typeof p.cols === "number" ? p.cols : void 0,
7487
7764
  rows: typeof p.rows === "number" ? p.rows : void 0,
7488
7765
  // Session ended (natural exit included), so stop its watcher.
7489
- onExit: () => unwatchSubAgents(key)
7766
+ onExit: () => {
7767
+ unwatchSubAgents(key);
7768
+ if (key === task.id) notifier.exited(task.id);
7769
+ }
7490
7770
  });
7491
7771
  terminalDirectories.set(key, cwd);
7492
7772
  watchSubAgents(key, cwd, (subAgents) => {
@@ -7498,6 +7778,10 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7498
7778
  }
7499
7779
  return result;
7500
7780
  }
7781
+ async function attachAgent(taskId) {
7782
+ const task = findTask(taskId);
7783
+ if (task && sessions.isAttached?.(taskId) === false) await startTaskSession(task, taskId, { taskId });
7784
+ }
7501
7785
  async function summarizeTasks(filter) {
7502
7786
  const board = getState().board;
7503
7787
  const alive = new Set(await sessions.list());
@@ -7824,12 +8108,30 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7824
8108
  // Every card on the board, or one master's cards, as the CLI and a master's
7825
8109
  // agent read them.
7826
8110
  "task:summaries": (payload) => {
7827
- const p = payload === void 0 ? {} : obj(payload, "payload");
8111
+ const p = payload === void 0 || payload === null ? {} : obj(payload, "payload");
7828
8112
  return summarizeTasks({
7829
8113
  masterId: p.masterId === void 0 ? void 0 : requireTask(p.masterId).id,
7830
8114
  taskId: p.taskId === void 0 ? void 0 : requireTask(p.taskId).id
7831
8115
  });
7832
8116
  },
8117
+ // One card's agent talks to another's: a master to its own cards, a card to
8118
+ // its master, nothing else — cards coordinate through their master.
8119
+ "vibeflow:sendMessage": async (payload) => {
8120
+ const p = obj(payload, "payload");
8121
+ const from = findTask(str(p.from, "from"));
8122
+ const to = findTask(str(p.to, "to"));
8123
+ if (!from || !to) throw coded2("UNKNOWN_TASK", `unknown task ${!from ? String(p.from) : String(p.to)}`);
8124
+ const related = isMasterTask(from) && to.masterId === from.id || isMasterTask(to) && from.masterId === to.id;
8125
+ if (!related) throw coded2("NOT_RELATED", "\u53EA\u6709 master \u8207\u5B83\u81EA\u5DF1\u7684\u5361\u7247\u4E4B\u9593\u53EF\u4EE5\u4E92\u50B3\u8A0A\u606F");
8126
+ const text = str(p.text, "text").trim();
8127
+ if (!text) invalid("text must not be empty");
8128
+ if (text.length > MAX_MESSAGE_LENGTH) invalid(`text must be at most ${MAX_MESSAGE_LENGTH} characters`);
8129
+ if (!await sessions.isAlive(to.id)) throw coded2("NOT_RUNNING", `\u5361\u7247 ${to.id} \u7684 agent \u6C92\u6709\u5728\u57F7\u884C`);
8130
+ if (isMasterTask(to)) notifier.reported(from.id);
8131
+ await attachAgent(to.id);
8132
+ const { queuedMs } = await mailbox.deliver(to.id, frameMessage({ id: from.id, title: from.title }, text), { force: p.force === true });
8133
+ return { delivered: true, queuedMs };
8134
+ },
7833
8135
  "terminal:start": async (payload) => {
7834
8136
  const p = obj(payload, "payload");
7835
8137
  const key = str(p.sessionKey, "sessionKey");
@@ -7913,7 +8215,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7913
8215
  },
7914
8216
  "pty:input": (payload) => {
7915
8217
  const p = obj(payload, "payload");
7916
- sessions.write(requireSessionKey(p.sessionKey), str(p.data, "data"));
8218
+ mailbox.userInput(requireSessionKey(p.sessionKey), str(p.data, "data"));
7917
8219
  },
7918
8220
  "pty:resize": (payload) => {
7919
8221
  const p = obj(payload, "payload");
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><link rel="manifest" href="/manifest.webmanifest" data-next-head=""/><meta name="theme-color" content="#0d1117" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/1b8cc89c93b78db0.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1b8cc89c93b78db0.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-9dc72759edb7420f.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-8718d90c431152c1.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/iybQktKL4OxAZEKjzdSYP/_buildManifest.js" defer=""></script><script src="/_next/static/iybQktKL4OxAZEKjzdSYP/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"iybQktKL4OxAZEKjzdSYP","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><link rel="manifest" href="/manifest.webmanifest" data-next-head=""/><meta name="theme-color" content="#0d1117" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/1b8cc89c93b78db0.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1b8cc89c93b78db0.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-9dc72759edb7420f.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-ba514e8a4a55baf5.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/wFlo60T-hKTrS3FBCZ5OJ/_buildManifest.js" defer=""></script><script src="/_next/static/wFlo60T-hKTrS3FBCZ5OJ/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"wFlo60T-hKTrS3FBCZ5OJ","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
package/dist/web/404.html CHANGED
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><link rel="manifest" href="/manifest.webmanifest" data-next-head=""/><meta name="theme-color" content="#0d1117" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/1b8cc89c93b78db0.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1b8cc89c93b78db0.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-9dc72759edb7420f.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-8718d90c431152c1.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/iybQktKL4OxAZEKjzdSYP/_buildManifest.js" defer=""></script><script src="/_next/static/iybQktKL4OxAZEKjzdSYP/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"iybQktKL4OxAZEKjzdSYP","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><link rel="manifest" href="/manifest.webmanifest" data-next-head=""/><meta name="theme-color" content="#0d1117" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/1b8cc89c93b78db0.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1b8cc89c93b78db0.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-9dc72759edb7420f.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-ba514e8a4a55baf5.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/wFlo60T-hKTrS3FBCZ5OJ/_buildManifest.js" defer=""></script><script src="/_next/static/wFlo60T-hKTrS3FBCZ5OJ/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"wFlo60T-hKTrS3FBCZ5OJ","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>