@songsid/agend 2.3.2-beta.9 → 2.3.2

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.
Files changed (243) hide show
  1. package/README.md +6 -0
  2. package/README.zh-TW.md +6 -0
  3. package/dist/agent-cli.js +48 -0
  4. package/dist/agent-cli.js.map +1 -1
  5. package/dist/agent-endpoint.js +6 -0
  6. package/dist/agent-endpoint.js.map +1 -1
  7. package/dist/agent-whats-new.js +3 -0
  8. package/dist/agent-whats-new.js.map +1 -1
  9. package/dist/auth-api.js +2 -0
  10. package/dist/auth-api.js.map +1 -1
  11. package/dist/backend/claude-code.js +23 -1
  12. package/dist/backend/claude-code.js.map +1 -1
  13. package/dist/backend/codex-session-lookup.d.ts +15 -0
  14. package/dist/backend/codex-session-lookup.js +58 -0
  15. package/dist/backend/codex-session-lookup.js.map +1 -1
  16. package/dist/backend/codex.js +15 -4
  17. package/dist/backend/codex.js.map +1 -1
  18. package/dist/backend/kiro-classic-archive.d.ts +57 -0
  19. package/dist/backend/kiro-classic-archive.js +622 -0
  20. package/dist/backend/kiro-classic-archive.js.map +1 -0
  21. package/dist/backend/kiro-classic-links.d.ts +24 -0
  22. package/dist/backend/kiro-classic-links.js +135 -0
  23. package/dist/backend/kiro-classic-links.js.map +1 -0
  24. package/dist/backend/kiro-identity.d.ts +100 -1
  25. package/dist/backend/kiro-identity.js +262 -31
  26. package/dist/backend/kiro-identity.js.map +1 -1
  27. package/dist/backend/kiro-migration-verify.d.ts +10 -0
  28. package/dist/backend/kiro-migration-verify.js +52 -0
  29. package/dist/backend/kiro-migration-verify.js.map +1 -0
  30. package/dist/backend/kiro-reconcile-store.d.ts +27 -0
  31. package/dist/backend/kiro-reconcile-store.js +344 -0
  32. package/dist/backend/kiro-reconcile-store.js.map +1 -0
  33. package/dist/backend/kiro-v2-store-worker.js +35 -1
  34. package/dist/backend/kiro-v2-store-worker.js.map +1 -1
  35. package/dist/backend/kiro-v2-store.d.ts +31 -1
  36. package/dist/backend/kiro-v2-store.js +22 -12
  37. package/dist/backend/kiro-v2-store.js.map +1 -1
  38. package/dist/backend/kiro.d.ts +124 -1
  39. package/dist/backend/kiro.js +553 -29
  40. package/dist/backend/kiro.js.map +1 -1
  41. package/dist/backend/types.d.ts +102 -0
  42. package/dist/backend/types.js +9 -0
  43. package/dist/backend/types.js.map +1 -1
  44. package/dist/cache-service.d.ts +13 -1
  45. package/dist/cache-service.js +45 -2
  46. package/dist/cache-service.js.map +1 -1
  47. package/dist/channel/ipc-timeouts.js +3 -0
  48. package/dist/channel/ipc-timeouts.js.map +1 -1
  49. package/dist/channel/mcp-tools.js +40 -5
  50. package/dist/channel/mcp-tools.js.map +1 -1
  51. package/dist/channel/tool-router.js +2 -1
  52. package/dist/channel/tool-router.js.map +1 -1
  53. package/dist/classic-channel-manager.d.ts +6 -0
  54. package/dist/classic-channel-manager.js +25 -0
  55. package/dist/classic-channel-manager.js.map +1 -1
  56. package/dist/cli.js +50 -4
  57. package/dist/cli.js.map +1 -1
  58. package/dist/config-validator.d.ts +7 -1
  59. package/dist/config-validator.js +17 -8
  60. package/dist/config-validator.js.map +1 -1
  61. package/dist/config.js +2 -0
  62. package/dist/config.js.map +1 -1
  63. package/dist/daemon.d.ts +66 -0
  64. package/dist/daemon.js +421 -19
  65. package/dist/daemon.js.map +1 -1
  66. package/dist/dashboard-permissions.d.ts +15 -4
  67. package/dist/dashboard-permissions.js +49 -23
  68. package/dist/dashboard-permissions.js.map +1 -1
  69. package/dist/doctor.js +2 -1
  70. package/dist/doctor.js.map +1 -1
  71. package/dist/fleet-control-audit.d.ts +1 -1
  72. package/dist/fleet-control-audit.js +3 -0
  73. package/dist/fleet-control-audit.js.map +1 -1
  74. package/dist/fleet-manager.d.ts +90 -3
  75. package/dist/fleet-manager.js +569 -41
  76. package/dist/fleet-manager.js.map +1 -1
  77. package/dist/forged-envelope-scan.d.ts +9 -0
  78. package/dist/forged-envelope-scan.js +30 -5
  79. package/dist/forged-envelope-scan.js.map +1 -1
  80. package/dist/general-instance.d.ts +24 -0
  81. package/dist/general-instance.js +53 -0
  82. package/dist/general-instance.js.map +1 -1
  83. package/dist/general-knowledge/skills/docker-install/SKILL.md +25 -0
  84. package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -3
  85. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -1
  86. package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +34 -0
  87. package/dist/general-knowledge/skills/session-management/SKILL.md +1 -1
  88. package/dist/general-knowledge/skills/tui-effort/SKILL.md +1 -1
  89. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +32 -0
  90. package/dist/general-knowledge/skills/web-appearance/SKILL.md +32 -0
  91. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +57 -17
  92. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +57 -17
  93. package/dist/instance-config-impact.js +3 -2
  94. package/dist/instance-config-impact.js.map +1 -1
  95. package/dist/instance-lifecycle.d.ts +4 -0
  96. package/dist/instance-lifecycle.js +7 -0
  97. package/dist/instance-lifecycle.js.map +1 -1
  98. package/dist/kiro-migrate.d.ts +19 -2
  99. package/dist/kiro-migrate.js +133 -35
  100. package/dist/kiro-migrate.js.map +1 -1
  101. package/dist/kiro-reconcile.d.ts +21 -0
  102. package/dist/kiro-reconcile.js +88 -0
  103. package/dist/kiro-reconcile.js.map +1 -0
  104. package/dist/kiro-session-proof.d.ts +41 -0
  105. package/dist/kiro-session-proof.js +89 -0
  106. package/dist/kiro-session-proof.js.map +1 -0
  107. package/dist/kiro-tui-forensic.d.ts +19 -0
  108. package/dist/kiro-tui-forensic.js +124 -0
  109. package/dist/kiro-tui-forensic.js.map +1 -0
  110. package/dist/locale.js +198 -0
  111. package/dist/locale.js.map +1 -1
  112. package/dist/log-service.js +2 -2
  113. package/dist/log-service.js.map +1 -1
  114. package/dist/needs-you.d.ts +3 -1
  115. package/dist/needs-you.js +4 -2
  116. package/dist/needs-you.js.map +1 -1
  117. package/dist/office-levels.d.ts +64 -0
  118. package/dist/office-levels.js +66 -0
  119. package/dist/office-levels.js.map +1 -0
  120. package/dist/office-share-page.d.ts +3 -1
  121. package/dist/office-share-page.js +8 -3
  122. package/dist/office-share-page.js.map +1 -1
  123. package/dist/office-share-service.d.ts +6 -3
  124. package/dist/office-share-service.js +12 -8
  125. package/dist/office-share-service.js.map +1 -1
  126. package/dist/office-share.d.ts +45 -8
  127. package/dist/office-share.js +75 -15
  128. package/dist/office-share.js.map +1 -1
  129. package/dist/outbound-handlers.js +20 -1
  130. package/dist/outbound-handlers.js.map +1 -1
  131. package/dist/outbound-schemas.d.ts +47 -2
  132. package/dist/outbound-schemas.js +41 -2
  133. package/dist/outbound-schemas.js.map +1 -1
  134. package/dist/public-web-gateway.js +1 -1
  135. package/dist/public-web-gateway.js.map +1 -1
  136. package/dist/quickstart-api.d.ts +39 -1
  137. package/dist/quickstart-api.js +115 -17
  138. package/dist/quickstart-api.js.map +1 -1
  139. package/dist/remote-card-answer.d.ts +83 -0
  140. package/dist/remote-card-answer.js +208 -0
  141. package/dist/remote-card-answer.js.map +1 -0
  142. package/dist/remote-card-prompt.d.ts +38 -0
  143. package/dist/remote-card-prompt.js +86 -0
  144. package/dist/remote-card-prompt.js.map +1 -0
  145. package/dist/scheduler/task-board.d.ts +28 -6
  146. package/dist/scheduler/task-board.js +66 -11
  147. package/dist/scheduler/task-board.js.map +1 -1
  148. package/dist/service-installer.d.ts +13 -0
  149. package/dist/service-installer.js +15 -0
  150. package/dist/service-installer.js.map +1 -1
  151. package/dist/service-owner.d.ts +25 -0
  152. package/dist/service-owner.js +43 -2
  153. package/dist/service-owner.js.map +1 -1
  154. package/dist/settings-api.d.ts +2 -0
  155. package/dist/settings-api.js +18 -12
  156. package/dist/settings-api.js.map +1 -1
  157. package/dist/settings-change.js +10 -5
  158. package/dist/settings-change.js.map +1 -1
  159. package/dist/settings-effect.js +7 -4
  160. package/dist/settings-effect.js.map +1 -1
  161. package/dist/settings-instance-patch.js +1 -1
  162. package/dist/settings-instance-patch.js.map +1 -1
  163. package/dist/setup-form.d.ts +2 -2
  164. package/dist/setup-form.js +139 -93
  165. package/dist/setup-form.js.map +1 -1
  166. package/dist/setup-i18n.d.ts +25 -0
  167. package/dist/setup-i18n.js +226 -0
  168. package/dist/setup-i18n.js.map +1 -0
  169. package/dist/task-board-waker.js +7 -2
  170. package/dist/task-board-waker.js.map +1 -1
  171. package/dist/tool-permissions.js +18 -0
  172. package/dist/tool-permissions.js.map +1 -1
  173. package/dist/transcript-sources.d.ts +1 -0
  174. package/dist/transcript-sources.js +20 -3
  175. package/dist/transcript-sources.js.map +1 -1
  176. package/dist/types.d.ts +10 -0
  177. package/dist/ui/backend-signin.js +4 -3
  178. package/dist/ui/chat-store.js +3 -2
  179. package/dist/ui/chat-strings.js +2 -2
  180. package/dist/ui/fleet-office-card.js +114 -0
  181. package/dist/ui/fleet-office-editor.js +81 -9
  182. package/dist/ui/fleet-office-layout.js +56 -11
  183. package/dist/ui/fleet-office-levels.js +105 -0
  184. package/dist/ui/fleet-office-model.js +9 -1
  185. package/dist/ui/fleet-office-pack.d.ts +15 -3
  186. package/dist/ui/fleet-office-pack.js +177 -23
  187. package/dist/ui/fleet-office-skin.js +39 -3
  188. package/dist/ui/fleet-office-store.js +74 -3
  189. package/dist/ui/fleet-office-view.js +8 -0
  190. package/dist/ui/fleet-office-zones.js +102 -19
  191. package/dist/ui/fleet-office.js +308 -44
  192. package/dist/ui/fleet-org.js +2 -0
  193. package/dist/ui/fleet-task-card.js +2 -2
  194. package/dist/ui/fleet-task-common.js +30 -7
  195. package/dist/ui/fleet-task-work.js +2 -2
  196. package/dist/ui/fleet-tasks.js +10 -11
  197. package/dist/ui/office-share-dialog.js +31 -7
  198. package/dist/ui/office-share-page.js +1 -0
  199. package/dist/ui/panel-chat.js +14 -2
  200. package/dist/ui/panel-log.js +4 -4
  201. package/dist/ui/panel-needs.js +1 -1
  202. package/dist/ui/panel-settings.js +16 -8
  203. package/dist/ui/settings-discord.js +3 -2
  204. package/dist/ui/settings-guests.js +17 -7
  205. package/dist/ui/settings-model.js +17 -9
  206. package/dist/ui/settings-registry.js +1 -0
  207. package/dist/ui/settings-strings.js +16 -4
  208. package/dist/ui/settings-token.js +3 -2
  209. package/dist/ui/settings-wizard.js +25 -9
  210. package/dist/ui/shared/app-i18n.js +2 -2
  211. package/dist/ui/shared/app-stream.js +1 -1
  212. package/dist/ui/shared/app.css +70 -2
  213. package/dist/ui/shared/setup-host-text.js +107 -0
  214. package/dist/ui/structured-signin.js +89 -38
  215. package/dist/usage-rollup.d.ts +34 -3
  216. package/dist/usage-rollup.js +56 -3
  217. package/dist/usage-rollup.js.map +1 -1
  218. package/dist/web-api.d.ts +10 -1
  219. package/dist/web-api.js +207 -17
  220. package/dist/web-api.js.map +1 -1
  221. package/dist/web-auth.js +1 -1
  222. package/dist/web-auth.js.map +1 -1
  223. package/dist/web-office-acts.d.ts +5 -2
  224. package/dist/web-office-acts.js +9 -4
  225. package/dist/web-office-acts.js.map +1 -1
  226. package/dist/web-office-pack-read.d.ts +35 -0
  227. package/dist/web-office-pack-read.js +252 -0
  228. package/dist/web-office-pack-read.js.map +1 -0
  229. package/dist/web-office-pack-tools.d.ts +241 -0
  230. package/dist/web-office-pack-tools.js +785 -0
  231. package/dist/web-office-pack-tools.js.map +1 -0
  232. package/dist/web-office-skins.d.ts +68 -4
  233. package/dist/web-office-skins.js +324 -33
  234. package/dist/web-office-skins.js.map +1 -1
  235. package/dist/web-office-store.d.ts +47 -4
  236. package/dist/web-office-store.js +124 -23
  237. package/dist/web-office-store.js.map +1 -1
  238. package/dist/web-settings-patch.d.ts +3 -1
  239. package/dist/web-settings-patch.js +4 -1
  240. package/dist/web-settings-patch.js.map +1 -1
  241. package/dist/workflow-templates/default.md +1 -1
  242. package/package.json +2 -2
  243. package/templates/windows-shortcuts.ps1 +18 -17
@@ -1,3 +1,4 @@
1
+ import { SETUP_CODE_STRINGS, SETUP_FORM_STRINGS, setupI18nRuntime } from "./setup-i18n.js";
1
2
  /**
2
3
  * The pre-fleet form, inline.
3
4
  *
@@ -33,27 +34,35 @@ export const SETUP_CODE_PAGE_HTML = `<!DOCTYPE html>
33
34
  button { width: 100%; margin-top: 12px; padding: 12px; border: 1px solid #2563eb; border-radius: 6px; background: #2563eb; color: #fff; font-size: 15px; cursor: pointer; }
34
35
  button:disabled { opacity: .5; cursor: default; }
35
36
  .err { color: #dc2626; font-size: 13px; margin-top: 12px; min-height: 18px; }
37
+ .lang { display: flex; justify-content: flex-end; }
38
+ .lang button { width: auto; margin: 0; padding: 4px 10px; border-color: #e5e7eb; background: #fff; color: #111827; font-size: 13px; }
36
39
  </style>
37
40
  </head>
38
41
  <body>
39
42
  <main>
40
- <h1>Set up AgEnD</h1>
41
- <p>Enter the setup code shown in the terminal where you ran <code>agend setup</code>.</p>
43
+ <div class="lang"><button id="lang" type="button" data-i18n="otherLang" data-i18n-label="langTitle">中文</button></div>
44
+ <h1 data-i18n="title">Set up AgEnD</h1>
45
+ <p id="lead">Enter the setup code shown in the terminal where you ran <code>agend setup</code>.</p>
42
46
  <form id="f">
43
47
  <input id="code" autocomplete="off" autocapitalize="characters" spellcheck="false" placeholder="ABCD-EFGH" maxlength="12" autofocus>
44
- <button id="go" type="submit">Continue</button>
48
+ <button id="go" type="submit" data-i18n="continue">Continue</button>
45
49
  </form>
46
50
  <div class="err" id="msg"></div>
47
51
  </main>
48
52
  <script>
49
53
  (() => {
50
54
  "use strict";
55
+ ${setupI18nRuntime(SETUP_CODE_STRINGS)}
56
+ show(document.getElementById("lead"), el => {
57
+ const [before, after] = tr("lead").split("{0}"), command = document.createElement("code");
58
+ command.textContent = "agend setup"; el.replaceChildren(before, command, after ?? "");
59
+ });
51
60
  const msg = document.getElementById("msg");
52
61
  const go = document.getElementById("go");
53
62
  document.getElementById("f").addEventListener("submit", async (event) => {
54
63
  event.preventDefault();
55
64
  go.disabled = true;
56
- msg.textContent = "";
65
+ show(msg, el => { el.textContent = ""; });
57
66
  let res;
58
67
  try {
59
68
  res = await fetch("open", {
@@ -62,13 +71,14 @@ export const SETUP_CODE_PAGE_HTML = `<!DOCTYPE html>
62
71
  body: JSON.stringify({ code: document.getElementById("code").value }),
63
72
  });
64
73
  } catch {
65
- msg.textContent = "Could not reach the setup page.";
74
+ show(msg, el => { el.textContent = tr("unreachable"); });
66
75
  go.disabled = false;
67
76
  return;
68
77
  }
69
78
  if (res.ok) { location.reload(); return; }
70
79
  let body = null; try { body = await res.json(); } catch {}
71
- msg.textContent = (body && body.error) || "That code was not accepted.";
80
+ const error = body && body.error;
81
+ show(msg, el => { el.textContent = error ? trHost(error) : tr("notAccepted"); });
72
82
  // A spent budget ends the page; there is nothing useful left to type into.
73
83
  if (res.status !== 410) go.disabled = false;
74
84
  });
@@ -100,51 +110,55 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
100
110
  .msg { font-size: 13px; }
101
111
  .ok { color: #059669; } .err { color: #dc2626; }
102
112
  pre { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 6px; padding: 12px; font-size: 12px; overflow: auto; }
113
+ .top { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
114
+ #lang { padding: 4px 10px; font-size: 13px; flex: none; }
103
115
  </style>
104
116
  </head>
105
117
  <body>
106
118
  <main>
107
- <h1>Set up AgEnD</h1>
108
- <div class="sub" id="intro">This page is open only while setup is running. It closes itself when you finish, or after 15 minutes.</div>
119
+ <div class="top"><h1 data-i18n="title">Set up AgEnD</h1><button id="lang" type="button" data-i18n="otherLang" data-i18n-label="langTitle">中文</button></div>
120
+ <div class="sub" id="intro" data-i18n="intro">This page is open only while setup is running. It closes itself when you finish, or after 15 minutes.</div>
109
121
  <div class="sub msg" id="ttlMsg" role="status"></div>
110
122
 
111
- <div class="step" id="setupSignins" aria-label="AI backend sign-ins"></div>
123
+ <div class="step" id="setupSignins" aria-label="AI backend sign-ins" data-i18n-label="signinsLabel"></div>
112
124
 
113
125
  <div class="step">
114
- <h2>1 · Agent</h2>
115
- <label for="backend">Backend</label>
126
+ <h2 data-i18n="stepAgent">1 · Agent</h2>
127
+ <label for="backend" data-i18n="backend">Backend</label>
116
128
  <select id="backend"></select>
117
129
  <div class="sub" id="backendHint"></div>
118
- <label for="dir">Working directory (absolute path)</label>
119
- <input id="dir" placeholder="/home/you/projects/app">
120
- <label for="name">First agent name</label>
121
- <input id="name" value="agent-1">
130
+ <label for="dir" data-i18n="dir">Working directory (optional)</label>
131
+ <input id="dir" placeholder="/home/you/projects/app" data-i18n-placeholder="dirPlaceholder" aria-describedby="dirHint">
132
+ <div class="sub" id="dirHint"></div>
133
+ <label for="name" data-i18n="name">First agent name</label>
134
+ <input id="name" value="general" aria-describedby="generalNote">
135
+ <div class="sub" id="generalNote" data-i18n="generalNote" hidden>This agent is the General: it answers in the chat's own General topic and hands work to the agents you add later.</div>
122
136
  </div>
123
137
 
124
138
  <div class="step">
125
- <h2>2 · Platform</h2>
139
+ <h2 data-i18n="stepPlatform">2 · Platform</h2>
126
140
  <div class="row">
127
141
  <button id="pickTelegram" class="primary">Telegram</button>
128
142
  <button id="pickDiscord">Discord</button>
129
- <button id="pickWeb">Web only (no bot)</button>
143
+ <button id="pickWeb" data-i18n="webOnly">Web only (no bot)</button>
130
144
  </div>
131
145
  <div class="sub" id="platformHint">Create a bot with @BotFather, then add it to a group.</div>
132
146
  </div>
133
147
 
134
148
  <div class="step" id="credStep">
135
- <h2>3 · Credentials</h2>
136
- <label for="token">Bot token</label>
149
+ <h2 data-i18n="stepCredentials">3 · Credentials</h2>
150
+ <label for="token" data-i18n="token">Bot token</label>
137
151
  <input id="token" type="password" placeholder="123456:ABC-DEF…">
138
- <div class="sub">Written to ~/.agend/.env and never shown again.</div>
139
- <label for="tokenEnv">Environment variable</label>
152
+ <div class="sub" data-i18n="tokenNote">Written to ~/.agend/.env and never shown again.</div>
153
+ <label for="tokenEnv" data-i18n="tokenEnv">Environment variable</label>
140
154
  <input id="tokenEnv" value="AGEND_BOT_TOKEN">
141
- <div class="row"><button id="verify">Verify</button><span class="msg" id="verifyMsg"></span></div>
155
+ <div class="row"><button id="verify" data-i18n="verify">Verify</button><span class="msg" id="verifyMsg"></span></div>
142
156
  <div id="platformFields"></div>
143
157
  </div>
144
158
 
145
159
  <div class="step">
146
- <h2>4 · Review and start</h2>
147
- <div class="row"><button id="preview">Show what will be written</button></div>
160
+ <h2 data-i18n="stepReview">4 · Review and start</h2>
161
+ <div class="row"><button id="preview" data-i18n="preview">Show what will be written</button></div>
148
162
  <pre id="planOut" hidden></pre>
149
163
  <div class="row"><button id="finish" class="primary" disabled>Create and start AgEnD</button><span class="msg" id="finishMsg"></span></div>
150
164
  </div>
@@ -152,8 +166,10 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
152
166
  <script>
153
167
  (() => {
154
168
  "use strict";
169
+ ${setupI18nRuntime(SETUP_FORM_STRINGS)}
155
170
  const $ = (id) => document.getElementById(id);
156
- const state = { platform: "telegram", identity: null, guilds: [], offset: 0, plan: null };
171
+ // M-2069: firstGeneral — a chat setup's new agent is the fleet's General, named "general" until the name is typed.
172
+ const state = { platform: "telegram", identity: null, guilds: [], offset: 0, plan: null, workspaces: null, firstGeneral: true, nameTyped: false };
157
173
  // Relative to the page's own directory, which is /s/<sid>/. An absolute path
158
174
  // would leave that namespace and 404 — the sid is where this page lives, not
159
175
  // a prefix bolted on to it.
@@ -162,31 +178,48 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
162
178
  let body = null; try { body = await res.json(); } catch {}
163
179
  return { ok: res.ok, status: res.status, body };
164
180
  };
181
+ // A line of this page in its language: a key and its values, or (host) the host's own words, translated when known.
182
+ const say = (id, key, ...values) => show($(id), el => { el.textContent = tr(key, ...values); });
183
+ const sayHost = (id, text, fallback) => show($(id), el => { el.textContent = text ? trHost(text) : tr(fallback); });
184
+
185
+ // #2726: no directory is the agent's own workspace, <AGEND_HOME>/workspaces/<name> — said under the field, by name.
186
+ const showDirHint = () => show($("dirHint"), el => {
187
+ const name = $("name").value.trim();
188
+ el.textContent = state.workspaces && name ? tr("dirDefault", state.workspaces + "/" + name) : tr("dirDefaultName");
189
+ });
165
190
 
166
191
  async function loadEnvironment() {
167
192
  const res = await api("api/settings/quickstart/environment");
168
193
  if (formFrozen) return;
169
194
  const backends = res.body?.backends || [];
195
+ if (typeof res.body?.workspaces_dir === "string") { state.workspaces = res.body.workspaces_dir; showDirHint(); }
196
+ if (typeof res.body?.first_general === "boolean") { state.firstGeneral = res.body.first_general; showGeneral(); }
170
197
  $("backend").innerHTML = "";
171
198
  for (const name of backends.length ? backends : ["claude-code"]) {
172
199
  const opt = document.createElement("option"); opt.value = name; opt.textContent = name; $("backend").append(opt);
173
200
  }
174
- $("backendHint").textContent = backends.length
175
- ? "Found on this host: " + backends.join(", ")
176
- : "No backend CLI found on this host — install one first.";
201
+ if (backends.length) say("backendHint", "backendFound", backends.join(", "));
202
+ else say("backendHint", "backendNone");
203
+ }
204
+
205
+ // M-2069: on a chat platform, with no directory typed, the first agent is the General ("general"); web only has no
206
+ // General, and a typed directory makes a worker ("agent-1"). A name the user typed is kept.
207
+ function showGeneral() {
208
+ // #2744 review: only with no directory — the General works in its own folder, never in a project of the user's.
209
+ const general = state.firstGeneral && state.platform !== "web" && !$("dir").value.trim();
210
+ $("generalNote").hidden = !general;
211
+ if (!state.nameTyped) { $("name").value = general ? "general" : "agent-1"; showDirHint(); }
177
212
  }
178
213
 
179
214
  function setPlatform(platform) {
180
215
  if (formFrozen) return;
181
216
  state.platform = platform;
182
- state.identity = null; $("verifyMsg").textContent = "";
217
+ showGeneral();
218
+ state.identity = null; say("verifyMsg", "");
183
219
  $("pickTelegram").className = platform === "telegram" ? "primary" : "";
184
220
  $("pickDiscord").className = platform === "discord" ? "primary" : "";
185
221
  $("pickWeb").className = platform === "web" ? "primary" : "";
186
- $("platformHint").textContent = platform === "telegram"
187
- ? "Create a bot with @BotFather, then add it to a group."
188
- : platform === "discord" ? "Create an application in the Discord developer portal and invite the bot to your server."
189
- : "No bot: you use AgEnD from its web dashboard (chat, sign-ins, settings). Discord or Telegram can be added later in Settings.";
222
+ say("platformHint", platform === "telegram" ? "hintTelegram" : platform === "discord" ? "hintDiscord" : "hintWeb");
190
223
  // #2233: web only has no credentials step — no token, no admin id, nothing to verify.
191
224
  $("credStep").hidden = platform === "web";
192
225
  state.plan = null; $("planOut").hidden = true; $("finish").disabled = true;
@@ -198,47 +231,48 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
198
231
  if (formFrozen) return;
199
232
  const host = $("platformFields"); host.innerHTML = "";
200
233
  if (state.platform === "web") return;
201
- const field = (id, labelText, hint) => {
202
- const label = document.createElement("label"); label.textContent = labelText; label.htmlFor = id;
234
+ const field = (id, labelKey, hintKey) => {
235
+ const label = document.createElement("label"); label.htmlFor = id; show(label, el => { el.textContent = tr(labelKey); });
203
236
  const input = document.createElement("input"); input.id = id;
204
237
  host.append(label, input);
205
- if (hint) { const h = document.createElement("div"); h.className = "sub"; h.textContent = hint; host.append(h); }
238
+ if (hintKey) { const h = document.createElement("div"); h.className = "sub"; show(h, el => { el.textContent = tr(hintKey); }); host.append(h); }
206
239
  return input;
207
240
  };
208
241
  if (state.platform === "discord") {
209
242
  const guild = document.createElement("select"); guild.id = "guild";
210
- const label = document.createElement("label"); label.textContent = "Server (guild)"; label.htmlFor = "guild";
243
+ const label = document.createElement("label"); label.htmlFor = "guild"; show(label, el => { el.textContent = tr("guild"); });
211
244
  host.append(label, guild);
212
245
  for (const g of state.guilds) { const opt = document.createElement("option"); opt.value = g.id; opt.textContent = g.name + " (" + g.id + ")"; guild.append(opt); }
213
- field("general", "General channel id");
214
- field("admin", "Your Discord user id", "Becomes the first allowed user.");
246
+ field("general", "general");
247
+ field("admin", "discordAdmin", "firstAllowed");
215
248
  } else {
216
- field("group", "Group id", "Or post any message in the group and press Detect.");
249
+ field("group", "group", "groupHint");
217
250
  const row = document.createElement("div"); row.className = "row";
218
- const detect = document.createElement("button"); detect.id = "detect"; detect.textContent = "Detect from a message";
251
+ const detect = document.createElement("button"); detect.id = "detect"; show(detect, el => { el.textContent = tr("detect"); });
219
252
  detect.onclick = detectGroup; row.append(detect);
220
253
  const msg = document.createElement("span"); msg.className = "msg"; msg.id = "detectMsg"; row.append(msg);
221
254
  host.append(row);
222
- field("admin", "Your Telegram user id", "Becomes the first allowed user.");
255
+ field("admin", "telegramAdmin", "firstAllowed");
223
256
  }
224
257
  }
225
258
 
226
259
  async function detectGroup() {
227
260
  if (formFrozen) return;
228
- $("detectMsg").textContent = "Waiting for a message in the group…";
261
+ say("detectMsg", "detectWaiting");
229
262
  const res = await api("api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "await-telegram-start", token: $("token").value.trim(), offset: state.offset }) });
230
263
  if (formFrozen) return;
231
- if (!res.ok) { $("detectMsg").textContent = res.body?.error || "Failed."; return; }
264
+ if (!res.ok) { sayHost("detectMsg", res.body?.error, "failed"); return; }
232
265
  state.offset = res.body.offset;
233
266
  if (res.body.found) {
234
267
  $("group").value = String(res.body.found.groupId);
235
268
  $("admin").value = String(res.body.found.userId);
236
- $("detectMsg").textContent = "Found.";
237
- } else $("detectMsg").textContent = "No group message yet — post one and press Detect again.";
269
+ say("detectMsg", "detectFound");
270
+ } else say("detectMsg", "detectNone");
238
271
  }
239
272
 
240
273
  function input() {
241
274
  // #2233: the agent alone; a web-only setup carries no connection field at all.
275
+ // #2726: an empty directory is sent as it is — the host gives the agent its own workspace.
242
276
  if (state.platform === "web") return { web_only: true, backend: $("backend").value, working_directory: $("dir").value.trim(), instance_name: $("name").value.trim() };
243
277
  return {
244
278
  platform: state.platform,
@@ -255,14 +289,16 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
255
289
 
256
290
  $("verify").onclick = async () => {
257
291
  if (formFrozen) return;
258
- $("verifyMsg").textContent = "Asking the provider…";
292
+ say("verifyMsg", "verifying");
259
293
  const token = $("token").value.trim();
260
294
  const res = await api("api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "verify", platform: state.platform, token }) });
261
295
  if (formFrozen) return;
262
- state.identity = res.body?.identity || { valid: false };
263
- $("verifyMsg").className = "msg " + (state.identity.valid ? "ok" : "err");
264
- $("verifyMsg").textContent = state.identity.valid ? "✓ " + (state.identity.username || "verified") : "✗ " + (state.identity.reason || "rejected");
265
- if (state.identity.valid && state.platform === "discord") {
296
+ const identity = state.identity = res.body?.identity || { valid: false };
297
+ show($("verifyMsg"), el => {
298
+ el.className = "msg " + (identity.valid ? "ok" : "err");
299
+ el.textContent = identity.valid ? "✓ " + (identity.username || tr("verified")) : "✗ " + (identity.reason ? trHost(identity.reason) : tr("rejected"));
300
+ });
301
+ if (identity.valid && state.platform === "discord") {
266
302
  const guilds = await api("api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "guilds", token }) });
267
303
  if (formFrozen) return;
268
304
  state.guilds = guilds.body?.guilds || [];
@@ -274,10 +310,10 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
274
310
  if (formFrozen) return;
275
311
  const res = await api("api/settings/quickstart/plan", { method: "POST", body: JSON.stringify(input()) });
276
312
  if (formFrozen) return;
277
- if (!res.ok) { $("planOut").hidden = false; $("planOut").textContent = res.body?.error || "Failed."; return; }
313
+ if (!res.ok) { $("planOut").hidden = false; sayHost("planOut", res.body?.error, "failed"); return; }
278
314
  state.plan = res.body;
279
315
  $("planOut").hidden = false;
280
- $("planOut").textContent = JSON.stringify(res.body, null, 2);
316
+ show($("planOut"), el => { el.textContent = JSON.stringify(res.body, null, 2); });
281
317
  $("finish").disabled = !(state.platform === "web" || state.identity?.valid);
282
318
  };
283
319
 
@@ -287,13 +323,16 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
287
323
  let pollGeneration = 0;
288
324
  // #2650 item 10: how long this page has left, counted here from the host's own remaining time (no polling: a request
289
325
  // would keep an idle page open). After it, the page says it has closed and how to reopen it, instead of freezing.
290
- const REOPEN = "Reopen it where you started setup: in the Windows installer, press Enter in its window; otherwise run agend setup again on the computer running AgEnD.";
291
326
  let closesAt = null, handedOver = false, pageClosed = false;
327
+ const setFinishLabel = (key) => show($("finish"), el => { el.textContent = tr(key); });
328
+ // finishMsg: a key of this page, or the host's words; with its colour.
329
+ const finishSay = (cls, key, ...values) => show($("finishMsg"), el => { el.className = cls; el.textContent = tr(key, ...values); });
330
+ const finishHost = (text, fallback) => show($("finishMsg"), el => { el.className = "msg err"; el.textContent = text ? trHost(text) : tr(fallback); });
292
331
  const closePage = () => {
293
332
  if (handedOver || pageClosed) return;
294
333
  pageClosed = true; pollGeneration++; freezeFields(true); $("finish").disabled = true;
295
- $("ttlMsg").className = "sub msg err"; $("ttlMsg").textContent = "This setup page has closed.";
296
- $("finishMsg").className = "msg err"; $("finishMsg").textContent = "This setup page has closed. " + REOPEN + " Backends you already signed in stay signed in.";
334
+ show($("ttlMsg"), el => { el.className = "sub msg err"; el.textContent = tr("closed"); });
335
+ show($("finishMsg"), el => { el.className = "msg err"; el.textContent = tr("closedFinish", tr("reopen")); });
297
336
  };
298
337
  const showRemaining = () => {
299
338
  if (closesAt === null || handedOver || pageClosed) return;
@@ -301,9 +340,10 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
301
340
  if (left <= 0) { closePage(); return; }
302
341
  const total = Math.ceil(left / 1000), text = Math.floor(total / 60) + ":" + String(total % 60).padStart(2, "0");
303
342
  const soon = left <= 3 * 60_000;
304
- $("ttlMsg").className = soon ? "sub msg err" : "sub msg";
305
- $("ttlMsg").textContent = "This page closes in " + text + " at the latest (sooner after 10 minutes without activity)."
306
- + (soon ? " Finish soon. If it closes first: " + REOPEN : "");
343
+ show($("ttlMsg"), el => {
344
+ el.className = soon ? "sub msg err" : "sub msg";
345
+ el.textContent = soon ? tr("closesSoon", tr("closesIn", text), tr("reopen")) : tr("closesIn", text);
346
+ });
307
347
  setTimeout(showRemaining, 1000);
308
348
  };
309
349
  const trackRemaining = (status) => {
@@ -316,39 +356,45 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
316
356
  formFrozen = frozen;
317
357
  for (const field of document.querySelectorAll("main input, main select, #verify, #preview, #pickTelegram, #pickDiscord, #pickWeb, #detect")) field.disabled = frozen;
318
358
  };
319
- const restoreEditing = (message) => {
359
+ const restoreEditing = (render) => {
320
360
  if (pageClosed) return; // a late answer never reopens a closed page (#2653 review)
321
361
  pollGeneration++; approvedSetup = false; submission = null; state.identity = null;
322
- freezeFields(false); $("finish").disabled = true; $("finish").textContent = "Create and start AgEnD";
323
- $("finishMsg").className = "msg err"; $("finishMsg").textContent = message;
362
+ freezeFields(false); $("finish").disabled = true; setFinishLabel("finish");
363
+ render();
324
364
  };
325
365
  for (const field of document.querySelectorAll("main input, main select")) field.addEventListener("input", () => {
326
- approvedSetup = false; submission = null; $("finish").textContent = "Create and start AgEnD";
366
+ approvedSetup = false; submission = null; setFinishLabel("finish");
327
367
  });
368
+ // A host confirmation that did not apply: its outcome in this page's language (by its reason), else the host's words.
369
+ const outcomeText = (pending) => {
370
+ const reason = pending.outcome?.reason_code;
371
+ if (reason && I18N.en["outcome_" + reason]) return tr("outcome_" + reason);
372
+ return pending.outcome?.message ? trHost(pending.outcome.message) : tr("notApplied");
373
+ };
328
374
  const watchPending = (pendingId) => {
329
375
  if (pageClosed) return; // a late pending list or commit answer: the page stays closed
330
376
  const generation = ++pollGeneration;
331
377
  freezeFields(true); $("finish").disabled = true;
332
378
  // #2650 item 1: the page asks; a person at the computer running AgEnD approves. Nothing on this page can.
333
- const ask = "Approve this on the computer running AgEnD. The Windows installer opens a confirmation window for it, and shows a command to paste if no window appears. Otherwise run there: agend settings confirm " + pendingId;
334
- $("finishMsg").className = "msg"; $("finishMsg").textContent = ask;
379
+ const ask = () => finishSay("msg", "approveHost", pendingId);
380
+ ask();
335
381
  let misses = 0;
336
382
  const poll = async () => {
337
383
  if (generation !== pollGeneration) return;
338
384
  try {
339
385
  const result = await api("api/settings/pending/" + pendingId);
340
386
  if (generation !== pollGeneration) return;
341
- if (misses) { misses = 0; $("finishMsg").className = "msg"; $("finishMsg").textContent = ask; }
342
- if (!result.ok) { restoreEditing("Setup confirmation is no longer available. Review and enter the token again."); return; }
387
+ if (misses) { misses = 0; ask(); }
388
+ if (!result.ok) { restoreEditing(() => finishSay("msg err", "confirmGone")); return; }
343
389
  const pending = result.body;
344
390
  if (pending.state === "pending" || pending.state === "applying") { setTimeout(poll, 1000); return; }
345
- if (pending.state !== "applied") { restoreEditing(pending.outcome?.message || "Change was not applied. Review and enter the token again."); return; }
346
- approvedSetup = true; submission = null; $("finish").disabled = false; $("finish").textContent = "Start AgEnD";
347
- $("finishMsg").textContent = "Configuration confirmed. Select Start AgEnD to finish setup.";
391
+ if (pending.state !== "applied") { restoreEditing(() => show($("finishMsg"), el => { el.className = "msg err"; el.textContent = outcomeText(pending); })); return; }
392
+ approvedSetup = true; submission = null; $("finish").disabled = false; setFinishLabel("start");
393
+ finishSay("msg", "confirmed");
348
394
  } catch {
349
395
  if (generation !== pollGeneration) return;
350
396
  // #2650 item 10: a lost connection is said, not waited out silently; the pending confirmation keeps its id.
351
- if (++misses === 5) { $("finishMsg").className = "msg err"; $("finishMsg").textContent = "Lost contact with the setup page; still trying. If this stays, the page has closed. " + REOPEN; }
397
+ if (++misses === 5) show($("finishMsg"), el => { el.className = "msg err"; el.textContent = tr("lostContact", tr("reopen")); });
352
398
  setTimeout(poll, misses < 5 ? 1000 : 3000);
353
399
  }
354
400
  };
@@ -357,15 +403,15 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
357
403
  $("finish").onclick = async () => {
358
404
  if (pageClosed) return;
359
405
  if ($("setupSignins").dataset.ready === "false") {
360
- $("finishMsg").textContent = "Finish backend sign-in or choose Sign in later before continuing."; return;
406
+ finishSay($("finishMsg").className, "finishSignins"); return;
361
407
  }
362
408
  $("finish").disabled = true;
363
409
  try {
364
410
  if (approvedSetup) {
365
411
  const finished = await api("setup/finish", { method: "POST" });
366
- if (!finished.ok) { restoreEditing(finished.body?.error || "Confirmation is no longer current."); return; }
367
- handedOver = true; $("ttlMsg").textContent = "";
368
- $("finishMsg").textContent = state.platform === "web" ? "AgEnD is starting. Its dashboard link shows here once it is up." : "AgEnD is starting. Continue in the channel you configured.";
412
+ if (!finished.ok) { restoreEditing(() => finishHost(finished.body?.error, "notCurrent")); return; }
413
+ handedOver = true; show($("ttlMsg"), el => { el.textContent = ""; });
414
+ finishSay($("finishMsg").className, state.platform === "web" ? "startingWeb" : "startingChat");
369
415
  if (finished.body?.watch !== false) waitForFleet();
370
416
  return;
371
417
  }
@@ -373,13 +419,13 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
373
419
  // a terminal outcome receives a new key and requires secret reentry.
374
420
  submission ||= { key: crypto.randomUUID(), body: JSON.stringify(state.platform === "web" ? input() : Object.assign({}, input(), { token: $("token").value.trim() })) };
375
421
  freezeFields(true);
376
- $("finishMsg").textContent = "Requesting host confirmation…";
422
+ finishSay($("finishMsg").className, "requesting");
377
423
  const commit = await api("api/settings/quickstart/commit", { method: "POST", headers: { "Content-Type": "application/json", "Idempotency-Key": submission.key }, body: submission.body });
378
- if (!commit.ok) { restoreEditing(commit.body?.error || "Failed."); return; }
379
- if (commit.body?.result !== "pending_confirmation" || !commit.body.pending_change?.id) { restoreEditing("Expected a pending host confirmation."); return; }
424
+ if (!commit.ok) { restoreEditing(() => finishHost(commit.body?.error, "failed")); return; }
425
+ if (commit.body?.result !== "pending_confirmation" || !commit.body.pending_change?.id) { restoreEditing(() => finishSay("msg err", "expectedPending")); return; }
380
426
  $("token").value = ""; submission = null;
381
427
  watchPending(commit.body.pending_change.id);
382
- } catch { if (pageClosed) return; $("finishMsg").textContent = "Connection failed. Retry to find the same confirmation."; $("finish").disabled = false; }
428
+ } catch { if (pageClosed) return; finishSay($("finishMsg").className, "connectionFailed"); $("finish").disabled = false; }
383
429
  };
384
430
  // Same-cookie reloads rediscover an unresolved consent request without a secret.
385
431
  const status = api("setup/status").then(result => { if (result.ok) trackRemaining(result.body); return result; });
@@ -389,8 +435,8 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
389
435
  if (pending) { watchPending(pending.id); return; }
390
436
  status.then(current => {
391
437
  if (pageClosed || !current.ok || !current.body?.finish_ready || submission || pollGeneration) return;
392
- approvedSetup = true; freezeFields(true); $("finish").disabled = false; $("finish").textContent = "Start AgEnD";
393
- $("finishMsg").textContent = "Configuration confirmed. Select Start AgEnD to finish setup.";
438
+ approvedSetup = true; freezeFields(true); $("finish").disabled = false; setFinishLabel("start");
439
+ finishSay($("finishMsg").className, "confirmed");
394
440
  }).catch(() => {});
395
441
  }).catch(() => {});
396
442
 
@@ -402,23 +448,19 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
402
448
  try {
403
449
  const res = await fetch("/health", { cache: "no-store" });
404
450
  if (res.status) {
405
- $("finishMsg").className = "msg ok";
406
451
  // #1519 P7: the fleet answers on this address now — its dashboard is here too.
407
- const link = document.createElement("a"); link.href = "/"; link.textContent = location.origin + "/";
408
- if (state.platform === "web") {
409
- // #2233: no bot — the dashboard is where AgEnD is used; its sign-in code comes from the machine.
410
- $("finishMsg").textContent = "AgEnD is up. Open its dashboard: ";
411
- $("finishMsg").append(link, " — sign in with the code from agend web --code on the machine running AgEnD. Sign your agents' CLIs in from Settings → Agents → Sign-ins.");
412
- return;
413
- }
414
- $("finishMsg").textContent = "AgEnD is up. Talk to it in the channel you just set up, or open its dashboard: ";
415
- $("finishMsg").append(link, " (sign in with agend web --code on the machine running AgEnD, or send /web to your bot).");
452
+ // #2233: with no bot, the dashboard is where AgEnD is used; its sign-in code comes from the machine.
453
+ show($("finishMsg"), el => {
454
+ el.className = "msg ok";
455
+ const link = document.createElement("a"); link.href = "/"; link.textContent = location.origin + "/";
456
+ const [before, after] = tr(state.platform === "web" ? "upWeb" : "upChat").split("{0}");
457
+ el.textContent = before; el.append(link, after ?? "");
458
+ });
416
459
  return;
417
460
  }
418
461
  } catch { /* not listening yet */ }
419
462
  if (Date.now() > deadline) {
420
- $("finishMsg").className = "msg err";
421
- $("finishMsg").textContent = "AgEnD has not come up within 60 seconds. Run \`agend start\` on the machine itself and check fleet.log — this page cannot tell you any more.";
463
+ finishSay("msg err", "notUp");
422
464
  return;
423
465
  }
424
466
  setTimeout(tick, 1000);
@@ -429,6 +471,10 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
429
471
  $("pickTelegram").onclick = () => setPlatform("telegram");
430
472
  $("pickDiscord").onclick = () => setPlatform("discord");
431
473
  $("pickWeb").onclick = () => setPlatform("web");
474
+ $("name").addEventListener("input", () => { state.nameTyped = true; showDirHint(); });
475
+ $("dir").addEventListener("input", showGeneral);
476
+ setFinishLabel("finish");
477
+ showDirHint();
432
478
  loadEnvironment();
433
479
  setPlatform("telegram");
434
480
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"setup-form.js","sourceRoot":"","sources":["../src/setup-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8DnC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuW9B,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAa;IACtD,OAAO;QACL,oBAAoB;QACpB,qBAAqB,KAAK,GAAG;QAC7B,oBAAoB,KAAK,GAAG;QAC5B,sBAAsB;QACtB,oBAAoB;QACpB,iBAAiB;QACjB,oBAAoB;QACpB,wBAAwB;KACzB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAa;IACnD,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,kBAAkB,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,iBAAiB,KAAK,IAAI,CAAC,CAAC;AACvH,CAAC"}
1
+ {"version":3,"file":"setup-form.js","sourceRoot":"","sources":["../src/setup-form.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE3F;;;;;;;;GAQG;AACH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAqChC,gBAAgB,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCvC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+E3B,gBAAgB,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4TvC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAa;IACtD,OAAO;QACL,oBAAoB;QACpB,qBAAqB,KAAK,GAAG;QAC7B,oBAAoB,KAAK,GAAG;QAC5B,sBAAsB;QACtB,oBAAoB;QACpB,iBAAiB;QACjB,oBAAoB;QACpB,wBAAwB;KACzB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAa;IACnD,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,kBAAkB,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,iBAAiB,KAAK,IAAI,CAAC,CAAC;AACvH,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * #2726: the setup pages in English and 繁體中文.
3
+ *
4
+ * Both pages are inline (setup-form.ts) and run before any fleet — no dashboard, no /assets — so they carry their own
5
+ * small dictionary and the few lines that use it (SETUP_I18N_RUNTIME). The choice works as the dashboard's does
6
+ * (src/ui/shared/app-i18n.js): the language chosen in this browser (`agend_lang`, the dashboard's own key, so the
7
+ * dashboard opened afterwards on this address keeps it), else the browser's language; a toggle on the page changes it.
8
+ * The wording is the dashboard's where it already says the same thing (settings-strings.js wizard*).
9
+ *
10
+ * What the host says (its errors) is English, or a code: src/ui/shared/setup-host-text.js says each one the page can show
11
+ * in both languages — the same module the dashboard's Setup wizard uses. One it does not know is shown as it came.
12
+ */
13
+ export type SetupLang = "en" | "zh-TW";
14
+ type Table = Record<string, string>;
15
+ /** The code-entry page: before the code, nothing about this machine (setup-form.ts SETUP_CODE_PAGE_HTML). */
16
+ export declare const SETUP_CODE_STRINGS: Record<SetupLang, Table>;
17
+ /** The setup form (setup-form.ts SETUP_FORM_HTML). */
18
+ export declare const SETUP_FORM_STRINGS: Record<SetupLang, Table>;
19
+ /**
20
+ * The few lines both pages run first: the dictionary (JSON, `<` escaped so no string can end the script), the language,
21
+ * `tr` (a key in it), `trHost` (a host message in it), `show` (a text that follows the language when it changes) and
22
+ * `applyLang`. Plain script, no `${` and no template literal: it is pasted into the pages' own template strings.
23
+ */
24
+ export declare function setupI18nRuntime(strings: Record<SetupLang, Table>): string;
25
+ export {};