@songsid/agend 2.3.2-beta.12 → 2.3.2-beta.14
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/auth-api.js +2 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/kiro-classic-archive.js +55 -2
- package/dist/backend/kiro-classic-archive.js.map +1 -1
- package/dist/backend/kiro-classic-links.d.ts +24 -0
- package/dist/backend/kiro-classic-links.js +135 -0
- package/dist/backend/kiro-classic-links.js.map +1 -0
- package/dist/backend/kiro-identity.d.ts +71 -1
- package/dist/backend/kiro-identity.js +204 -31
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro-v2-store-worker.js +6 -1
- package/dist/backend/kiro-v2-store-worker.js.map +1 -1
- package/dist/backend/kiro-v2-store.d.ts +12 -1
- package/dist/backend/kiro-v2-store.js +22 -12
- package/dist/backend/kiro-v2-store.js.map +1 -1
- package/dist/backend/kiro.d.ts +6 -0
- package/dist/backend/kiro.js +50 -7
- package/dist/backend/kiro.js.map +1 -1
- package/dist/fleet-manager.js +6 -1
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +7 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +7 -1
- package/dist/kiro-migrate.d.ts +8 -2
- package/dist/kiro-migrate.js +48 -5
- package/dist/kiro-migrate.js.map +1 -1
- package/dist/locale.js +32 -0
- package/dist/locale.js.map +1 -1
- package/dist/quickstart-api.d.ts +14 -0
- package/dist/quickstart-api.js +62 -11
- package/dist/quickstart-api.js.map +1 -1
- package/dist/settings-effect.js +5 -4
- package/dist/settings-effect.js.map +1 -1
- package/dist/setup-form.d.ts +2 -2
- package/dist/setup-form.js +124 -92
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-i18n.d.ts +25 -0
- package/dist/setup-i18n.js +224 -0
- package/dist/setup-i18n.js.map +1 -0
- package/dist/ui/backend-signin.js +4 -3
- package/dist/ui/panel-settings.js +4 -2
- package/dist/ui/settings-discord.js +3 -2
- package/dist/ui/settings-strings.js +8 -2
- package/dist/ui/settings-token.js +3 -2
- package/dist/ui/settings-wizard.js +11 -6
- package/dist/ui/shared/setup-host-text.js +105 -0
- package/dist/ui/structured-signin.js +89 -38
- package/package.json +1 -1
- package/templates/windows-shortcuts.ps1 +3 -3
package/dist/setup-form.js
CHANGED
|
@@ -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
|
-
<
|
|
41
|
-
<
|
|
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
|
|
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
|
-
|
|
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,54 @@ 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 (
|
|
119
|
-
<input id="dir" placeholder="/home/you/projects/app">
|
|
120
|
-
<
|
|
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>
|
|
121
134
|
<input id="name" value="agent-1">
|
|
122
135
|
</div>
|
|
123
136
|
|
|
124
137
|
<div class="step">
|
|
125
|
-
<h2>2 · Platform</h2>
|
|
138
|
+
<h2 data-i18n="stepPlatform">2 · Platform</h2>
|
|
126
139
|
<div class="row">
|
|
127
140
|
<button id="pickTelegram" class="primary">Telegram</button>
|
|
128
141
|
<button id="pickDiscord">Discord</button>
|
|
129
|
-
<button id="pickWeb">Web only (no bot)</button>
|
|
142
|
+
<button id="pickWeb" data-i18n="webOnly">Web only (no bot)</button>
|
|
130
143
|
</div>
|
|
131
144
|
<div class="sub" id="platformHint">Create a bot with @BotFather, then add it to a group.</div>
|
|
132
145
|
</div>
|
|
133
146
|
|
|
134
147
|
<div class="step" id="credStep">
|
|
135
|
-
<h2>3 · Credentials</h2>
|
|
136
|
-
<label for="token">Bot token</label>
|
|
148
|
+
<h2 data-i18n="stepCredentials">3 · Credentials</h2>
|
|
149
|
+
<label for="token" data-i18n="token">Bot token</label>
|
|
137
150
|
<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>
|
|
151
|
+
<div class="sub" data-i18n="tokenNote">Written to ~/.agend/.env and never shown again.</div>
|
|
152
|
+
<label for="tokenEnv" data-i18n="tokenEnv">Environment variable</label>
|
|
140
153
|
<input id="tokenEnv" value="AGEND_BOT_TOKEN">
|
|
141
|
-
<div class="row"><button id="verify">Verify</button><span class="msg" id="verifyMsg"></span></div>
|
|
154
|
+
<div class="row"><button id="verify" data-i18n="verify">Verify</button><span class="msg" id="verifyMsg"></span></div>
|
|
142
155
|
<div id="platformFields"></div>
|
|
143
156
|
</div>
|
|
144
157
|
|
|
145
158
|
<div class="step">
|
|
146
|
-
<h2>4 · Review and start</h2>
|
|
147
|
-
<div class="row"><button id="preview">Show what will be written</button></div>
|
|
159
|
+
<h2 data-i18n="stepReview">4 · Review and start</h2>
|
|
160
|
+
<div class="row"><button id="preview" data-i18n="preview">Show what will be written</button></div>
|
|
148
161
|
<pre id="planOut" hidden></pre>
|
|
149
162
|
<div class="row"><button id="finish" class="primary" disabled>Create and start AgEnD</button><span class="msg" id="finishMsg"></span></div>
|
|
150
163
|
</div>
|
|
@@ -152,8 +165,9 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
152
165
|
<script>
|
|
153
166
|
(() => {
|
|
154
167
|
"use strict";
|
|
168
|
+
${setupI18nRuntime(SETUP_FORM_STRINGS)}
|
|
155
169
|
const $ = (id) => document.getElementById(id);
|
|
156
|
-
const state = { platform: "telegram", identity: null, guilds: [], offset: 0, plan: null };
|
|
170
|
+
const state = { platform: "telegram", identity: null, guilds: [], offset: 0, plan: null, workspaces: null };
|
|
157
171
|
// Relative to the page's own directory, which is /s/<sid>/. An absolute path
|
|
158
172
|
// would leave that namespace and 404 — the sid is where this page lives, not
|
|
159
173
|
// a prefix bolted on to it.
|
|
@@ -162,31 +176,37 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
162
176
|
let body = null; try { body = await res.json(); } catch {}
|
|
163
177
|
return { ok: res.ok, status: res.status, body };
|
|
164
178
|
};
|
|
179
|
+
// A line of this page in its language: a key and its values, or (host) the host's own words, translated when known.
|
|
180
|
+
const say = (id, key, ...values) => show($(id), el => { el.textContent = tr(key, ...values); });
|
|
181
|
+
const sayHost = (id, text, fallback) => show($(id), el => { el.textContent = text ? trHost(text) : tr(fallback); });
|
|
182
|
+
|
|
183
|
+
// #2726: no directory is the agent's own workspace, <AGEND_HOME>/workspaces/<name> — said under the field, by name.
|
|
184
|
+
const showDirHint = () => show($("dirHint"), el => {
|
|
185
|
+
const name = $("name").value.trim();
|
|
186
|
+
el.textContent = state.workspaces && name ? tr("dirDefault", state.workspaces + "/" + name) : tr("dirDefaultName");
|
|
187
|
+
});
|
|
165
188
|
|
|
166
189
|
async function loadEnvironment() {
|
|
167
190
|
const res = await api("api/settings/quickstart/environment");
|
|
168
191
|
if (formFrozen) return;
|
|
169
192
|
const backends = res.body?.backends || [];
|
|
193
|
+
if (typeof res.body?.workspaces_dir === "string") { state.workspaces = res.body.workspaces_dir; showDirHint(); }
|
|
170
194
|
$("backend").innerHTML = "";
|
|
171
195
|
for (const name of backends.length ? backends : ["claude-code"]) {
|
|
172
196
|
const opt = document.createElement("option"); opt.value = name; opt.textContent = name; $("backend").append(opt);
|
|
173
197
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
: "No backend CLI found on this host — install one first.";
|
|
198
|
+
if (backends.length) say("backendHint", "backendFound", backends.join(", "));
|
|
199
|
+
else say("backendHint", "backendNone");
|
|
177
200
|
}
|
|
178
201
|
|
|
179
202
|
function setPlatform(platform) {
|
|
180
203
|
if (formFrozen) return;
|
|
181
204
|
state.platform = platform;
|
|
182
|
-
state.identity = null;
|
|
205
|
+
state.identity = null; say("verifyMsg", "");
|
|
183
206
|
$("pickTelegram").className = platform === "telegram" ? "primary" : "";
|
|
184
207
|
$("pickDiscord").className = platform === "discord" ? "primary" : "";
|
|
185
208
|
$("pickWeb").className = platform === "web" ? "primary" : "";
|
|
186
|
-
|
|
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.";
|
|
209
|
+
say("platformHint", platform === "telegram" ? "hintTelegram" : platform === "discord" ? "hintDiscord" : "hintWeb");
|
|
190
210
|
// #2233: web only has no credentials step — no token, no admin id, nothing to verify.
|
|
191
211
|
$("credStep").hidden = platform === "web";
|
|
192
212
|
state.plan = null; $("planOut").hidden = true; $("finish").disabled = true;
|
|
@@ -198,47 +218,48 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
198
218
|
if (formFrozen) return;
|
|
199
219
|
const host = $("platformFields"); host.innerHTML = "";
|
|
200
220
|
if (state.platform === "web") return;
|
|
201
|
-
const field = (id,
|
|
202
|
-
const label = document.createElement("label"); label.
|
|
221
|
+
const field = (id, labelKey, hintKey) => {
|
|
222
|
+
const label = document.createElement("label"); label.htmlFor = id; show(label, el => { el.textContent = tr(labelKey); });
|
|
203
223
|
const input = document.createElement("input"); input.id = id;
|
|
204
224
|
host.append(label, input);
|
|
205
|
-
if (
|
|
225
|
+
if (hintKey) { const h = document.createElement("div"); h.className = "sub"; show(h, el => { el.textContent = tr(hintKey); }); host.append(h); }
|
|
206
226
|
return input;
|
|
207
227
|
};
|
|
208
228
|
if (state.platform === "discord") {
|
|
209
229
|
const guild = document.createElement("select"); guild.id = "guild";
|
|
210
|
-
const label = document.createElement("label"); label.
|
|
230
|
+
const label = document.createElement("label"); label.htmlFor = "guild"; show(label, el => { el.textContent = tr("guild"); });
|
|
211
231
|
host.append(label, guild);
|
|
212
232
|
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", "
|
|
214
|
-
field("admin", "
|
|
233
|
+
field("general", "general");
|
|
234
|
+
field("admin", "discordAdmin", "firstAllowed");
|
|
215
235
|
} else {
|
|
216
|
-
field("group", "
|
|
236
|
+
field("group", "group", "groupHint");
|
|
217
237
|
const row = document.createElement("div"); row.className = "row";
|
|
218
|
-
const detect = document.createElement("button"); detect.id = "detect"; detect.textContent = "
|
|
238
|
+
const detect = document.createElement("button"); detect.id = "detect"; show(detect, el => { el.textContent = tr("detect"); });
|
|
219
239
|
detect.onclick = detectGroup; row.append(detect);
|
|
220
240
|
const msg = document.createElement("span"); msg.className = "msg"; msg.id = "detectMsg"; row.append(msg);
|
|
221
241
|
host.append(row);
|
|
222
|
-
field("admin", "
|
|
242
|
+
field("admin", "telegramAdmin", "firstAllowed");
|
|
223
243
|
}
|
|
224
244
|
}
|
|
225
245
|
|
|
226
246
|
async function detectGroup() {
|
|
227
247
|
if (formFrozen) return;
|
|
228
|
-
|
|
248
|
+
say("detectMsg", "detectWaiting");
|
|
229
249
|
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
250
|
if (formFrozen) return;
|
|
231
|
-
if (!res.ok) {
|
|
251
|
+
if (!res.ok) { sayHost("detectMsg", res.body?.error, "failed"); return; }
|
|
232
252
|
state.offset = res.body.offset;
|
|
233
253
|
if (res.body.found) {
|
|
234
254
|
$("group").value = String(res.body.found.groupId);
|
|
235
255
|
$("admin").value = String(res.body.found.userId);
|
|
236
|
-
|
|
237
|
-
} else
|
|
256
|
+
say("detectMsg", "detectFound");
|
|
257
|
+
} else say("detectMsg", "detectNone");
|
|
238
258
|
}
|
|
239
259
|
|
|
240
260
|
function input() {
|
|
241
261
|
// #2233: the agent alone; a web-only setup carries no connection field at all.
|
|
262
|
+
// #2726: an empty directory is sent as it is — the host gives the agent its own workspace.
|
|
242
263
|
if (state.platform === "web") return { web_only: true, backend: $("backend").value, working_directory: $("dir").value.trim(), instance_name: $("name").value.trim() };
|
|
243
264
|
return {
|
|
244
265
|
platform: state.platform,
|
|
@@ -255,14 +276,16 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
255
276
|
|
|
256
277
|
$("verify").onclick = async () => {
|
|
257
278
|
if (formFrozen) return;
|
|
258
|
-
|
|
279
|
+
say("verifyMsg", "verifying");
|
|
259
280
|
const token = $("token").value.trim();
|
|
260
281
|
const res = await api("api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "verify", platform: state.platform, token }) });
|
|
261
282
|
if (formFrozen) return;
|
|
262
|
-
state.identity = res.body?.identity || { valid: false };
|
|
263
|
-
$("verifyMsg")
|
|
264
|
-
|
|
265
|
-
|
|
283
|
+
const identity = state.identity = res.body?.identity || { valid: false };
|
|
284
|
+
show($("verifyMsg"), el => {
|
|
285
|
+
el.className = "msg " + (identity.valid ? "ok" : "err");
|
|
286
|
+
el.textContent = identity.valid ? "✓ " + (identity.username || tr("verified")) : "✗ " + (identity.reason ? trHost(identity.reason) : tr("rejected"));
|
|
287
|
+
});
|
|
288
|
+
if (identity.valid && state.platform === "discord") {
|
|
266
289
|
const guilds = await api("api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "guilds", token }) });
|
|
267
290
|
if (formFrozen) return;
|
|
268
291
|
state.guilds = guilds.body?.guilds || [];
|
|
@@ -274,10 +297,10 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
274
297
|
if (formFrozen) return;
|
|
275
298
|
const res = await api("api/settings/quickstart/plan", { method: "POST", body: JSON.stringify(input()) });
|
|
276
299
|
if (formFrozen) return;
|
|
277
|
-
if (!res.ok) { $("planOut").hidden = false;
|
|
300
|
+
if (!res.ok) { $("planOut").hidden = false; sayHost("planOut", res.body?.error, "failed"); return; }
|
|
278
301
|
state.plan = res.body;
|
|
279
302
|
$("planOut").hidden = false;
|
|
280
|
-
$("planOut").textContent = JSON.stringify(res.body, null, 2);
|
|
303
|
+
show($("planOut"), el => { el.textContent = JSON.stringify(res.body, null, 2); });
|
|
281
304
|
$("finish").disabled = !(state.platform === "web" || state.identity?.valid);
|
|
282
305
|
};
|
|
283
306
|
|
|
@@ -287,13 +310,16 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
287
310
|
let pollGeneration = 0;
|
|
288
311
|
// #2650 item 10: how long this page has left, counted here from the host's own remaining time (no polling: a request
|
|
289
312
|
// 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
313
|
let closesAt = null, handedOver = false, pageClosed = false;
|
|
314
|
+
const setFinishLabel = (key) => show($("finish"), el => { el.textContent = tr(key); });
|
|
315
|
+
// finishMsg: a key of this page, or the host's words; with its colour.
|
|
316
|
+
const finishSay = (cls, key, ...values) => show($("finishMsg"), el => { el.className = cls; el.textContent = tr(key, ...values); });
|
|
317
|
+
const finishHost = (text, fallback) => show($("finishMsg"), el => { el.className = "msg err"; el.textContent = text ? trHost(text) : tr(fallback); });
|
|
292
318
|
const closePage = () => {
|
|
293
319
|
if (handedOver || pageClosed) return;
|
|
294
320
|
pageClosed = true; pollGeneration++; freezeFields(true); $("finish").disabled = true;
|
|
295
|
-
$("ttlMsg").className = "sub msg err";
|
|
296
|
-
$("finishMsg").className = "msg err";
|
|
321
|
+
show($("ttlMsg"), el => { el.className = "sub msg err"; el.textContent = tr("closed"); });
|
|
322
|
+
show($("finishMsg"), el => { el.className = "msg err"; el.textContent = tr("closedFinish", tr("reopen")); });
|
|
297
323
|
};
|
|
298
324
|
const showRemaining = () => {
|
|
299
325
|
if (closesAt === null || handedOver || pageClosed) return;
|
|
@@ -301,9 +327,10 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
301
327
|
if (left <= 0) { closePage(); return; }
|
|
302
328
|
const total = Math.ceil(left / 1000), text = Math.floor(total / 60) + ":" + String(total % 60).padStart(2, "0");
|
|
303
329
|
const soon = left <= 3 * 60_000;
|
|
304
|
-
$("ttlMsg")
|
|
305
|
-
|
|
306
|
-
|
|
330
|
+
show($("ttlMsg"), el => {
|
|
331
|
+
el.className = soon ? "sub msg err" : "sub msg";
|
|
332
|
+
el.textContent = soon ? tr("closesSoon", tr("closesIn", text), tr("reopen")) : tr("closesIn", text);
|
|
333
|
+
});
|
|
307
334
|
setTimeout(showRemaining, 1000);
|
|
308
335
|
};
|
|
309
336
|
const trackRemaining = (status) => {
|
|
@@ -316,39 +343,45 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
316
343
|
formFrozen = frozen;
|
|
317
344
|
for (const field of document.querySelectorAll("main input, main select, #verify, #preview, #pickTelegram, #pickDiscord, #pickWeb, #detect")) field.disabled = frozen;
|
|
318
345
|
};
|
|
319
|
-
const restoreEditing = (
|
|
346
|
+
const restoreEditing = (render) => {
|
|
320
347
|
if (pageClosed) return; // a late answer never reopens a closed page (#2653 review)
|
|
321
348
|
pollGeneration++; approvedSetup = false; submission = null; state.identity = null;
|
|
322
|
-
freezeFields(false); $("finish").disabled = true;
|
|
323
|
-
|
|
349
|
+
freezeFields(false); $("finish").disabled = true; setFinishLabel("finish");
|
|
350
|
+
render();
|
|
324
351
|
};
|
|
325
352
|
for (const field of document.querySelectorAll("main input, main select")) field.addEventListener("input", () => {
|
|
326
|
-
approvedSetup = false; submission = null;
|
|
353
|
+
approvedSetup = false; submission = null; setFinishLabel("finish");
|
|
327
354
|
});
|
|
355
|
+
// A host confirmation that did not apply: its outcome in this page's language (by its reason), else the host's words.
|
|
356
|
+
const outcomeText = (pending) => {
|
|
357
|
+
const reason = pending.outcome?.reason_code;
|
|
358
|
+
if (reason && I18N.en["outcome_" + reason]) return tr("outcome_" + reason);
|
|
359
|
+
return pending.outcome?.message ? trHost(pending.outcome.message) : tr("notApplied");
|
|
360
|
+
};
|
|
328
361
|
const watchPending = (pendingId) => {
|
|
329
362
|
if (pageClosed) return; // a late pending list or commit answer: the page stays closed
|
|
330
363
|
const generation = ++pollGeneration;
|
|
331
364
|
freezeFields(true); $("finish").disabled = true;
|
|
332
365
|
// #2650 item 1: the page asks; a person at the computer running AgEnD approves. Nothing on this page can.
|
|
333
|
-
const ask =
|
|
334
|
-
|
|
366
|
+
const ask = () => finishSay("msg", "approveHost", pendingId);
|
|
367
|
+
ask();
|
|
335
368
|
let misses = 0;
|
|
336
369
|
const poll = async () => {
|
|
337
370
|
if (generation !== pollGeneration) return;
|
|
338
371
|
try {
|
|
339
372
|
const result = await api("api/settings/pending/" + pendingId);
|
|
340
373
|
if (generation !== pollGeneration) return;
|
|
341
|
-
if (misses) { misses = 0;
|
|
342
|
-
if (!result.ok) { restoreEditing(
|
|
374
|
+
if (misses) { misses = 0; ask(); }
|
|
375
|
+
if (!result.ok) { restoreEditing(() => finishSay("msg err", "confirmGone")); return; }
|
|
343
376
|
const pending = result.body;
|
|
344
377
|
if (pending.state === "pending" || pending.state === "applying") { setTimeout(poll, 1000); return; }
|
|
345
|
-
if (pending.state !== "applied") { restoreEditing(
|
|
346
|
-
approvedSetup = true; submission = null; $("finish").disabled = false;
|
|
347
|
-
|
|
378
|
+
if (pending.state !== "applied") { restoreEditing(() => show($("finishMsg"), el => { el.className = "msg err"; el.textContent = outcomeText(pending); })); return; }
|
|
379
|
+
approvedSetup = true; submission = null; $("finish").disabled = false; setFinishLabel("start");
|
|
380
|
+
finishSay("msg", "confirmed");
|
|
348
381
|
} catch {
|
|
349
382
|
if (generation !== pollGeneration) return;
|
|
350
383
|
// #2650 item 10: a lost connection is said, not waited out silently; the pending confirmation keeps its id.
|
|
351
|
-
if (++misses === 5)
|
|
384
|
+
if (++misses === 5) show($("finishMsg"), el => { el.className = "msg err"; el.textContent = tr("lostContact", tr("reopen")); });
|
|
352
385
|
setTimeout(poll, misses < 5 ? 1000 : 3000);
|
|
353
386
|
}
|
|
354
387
|
};
|
|
@@ -357,15 +390,15 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
357
390
|
$("finish").onclick = async () => {
|
|
358
391
|
if (pageClosed) return;
|
|
359
392
|
if ($("setupSignins").dataset.ready === "false") {
|
|
360
|
-
$("finishMsg").
|
|
393
|
+
finishSay($("finishMsg").className, "finishSignins"); return;
|
|
361
394
|
}
|
|
362
395
|
$("finish").disabled = true;
|
|
363
396
|
try {
|
|
364
397
|
if (approvedSetup) {
|
|
365
398
|
const finished = await api("setup/finish", { method: "POST" });
|
|
366
|
-
if (!finished.ok) { restoreEditing(finished.body?.error
|
|
367
|
-
handedOver = true; $("ttlMsg").textContent = "";
|
|
368
|
-
$("finishMsg").
|
|
399
|
+
if (!finished.ok) { restoreEditing(() => finishHost(finished.body?.error, "notCurrent")); return; }
|
|
400
|
+
handedOver = true; show($("ttlMsg"), el => { el.textContent = ""; });
|
|
401
|
+
finishSay($("finishMsg").className, state.platform === "web" ? "startingWeb" : "startingChat");
|
|
369
402
|
if (finished.body?.watch !== false) waitForFleet();
|
|
370
403
|
return;
|
|
371
404
|
}
|
|
@@ -373,13 +406,13 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
373
406
|
// a terminal outcome receives a new key and requires secret reentry.
|
|
374
407
|
submission ||= { key: crypto.randomUUID(), body: JSON.stringify(state.platform === "web" ? input() : Object.assign({}, input(), { token: $("token").value.trim() })) };
|
|
375
408
|
freezeFields(true);
|
|
376
|
-
$("finishMsg").
|
|
409
|
+
finishSay($("finishMsg").className, "requesting");
|
|
377
410
|
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
|
|
379
|
-
if (commit.body?.result !== "pending_confirmation" || !commit.body.pending_change?.id) { restoreEditing(
|
|
411
|
+
if (!commit.ok) { restoreEditing(() => finishHost(commit.body?.error, "failed")); return; }
|
|
412
|
+
if (commit.body?.result !== "pending_confirmation" || !commit.body.pending_change?.id) { restoreEditing(() => finishSay("msg err", "expectedPending")); return; }
|
|
380
413
|
$("token").value = ""; submission = null;
|
|
381
414
|
watchPending(commit.body.pending_change.id);
|
|
382
|
-
} catch { if (pageClosed) return; $("finishMsg").
|
|
415
|
+
} catch { if (pageClosed) return; finishSay($("finishMsg").className, "connectionFailed"); $("finish").disabled = false; }
|
|
383
416
|
};
|
|
384
417
|
// Same-cookie reloads rediscover an unresolved consent request without a secret.
|
|
385
418
|
const status = api("setup/status").then(result => { if (result.ok) trackRemaining(result.body); return result; });
|
|
@@ -389,8 +422,8 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
389
422
|
if (pending) { watchPending(pending.id); return; }
|
|
390
423
|
status.then(current => {
|
|
391
424
|
if (pageClosed || !current.ok || !current.body?.finish_ready || submission || pollGeneration) return;
|
|
392
|
-
approvedSetup = true; freezeFields(true); $("finish").disabled = false;
|
|
393
|
-
$("finishMsg").
|
|
425
|
+
approvedSetup = true; freezeFields(true); $("finish").disabled = false; setFinishLabel("start");
|
|
426
|
+
finishSay($("finishMsg").className, "confirmed");
|
|
394
427
|
}).catch(() => {});
|
|
395
428
|
}).catch(() => {});
|
|
396
429
|
|
|
@@ -402,23 +435,19 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
402
435
|
try {
|
|
403
436
|
const res = await fetch("/health", { cache: "no-store" });
|
|
404
437
|
if (res.status) {
|
|
405
|
-
$("finishMsg").className = "msg ok";
|
|
406
438
|
// #1519 P7: the fleet answers on this address now — its dashboard is here too.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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).");
|
|
439
|
+
// #2233: with no bot, the dashboard is where AgEnD is used; its sign-in code comes from the machine.
|
|
440
|
+
show($("finishMsg"), el => {
|
|
441
|
+
el.className = "msg ok";
|
|
442
|
+
const link = document.createElement("a"); link.href = "/"; link.textContent = location.origin + "/";
|
|
443
|
+
const [before, after] = tr(state.platform === "web" ? "upWeb" : "upChat").split("{0}");
|
|
444
|
+
el.textContent = before; el.append(link, after ?? "");
|
|
445
|
+
});
|
|
416
446
|
return;
|
|
417
447
|
}
|
|
418
448
|
} catch { /* not listening yet */ }
|
|
419
449
|
if (Date.now() > deadline) {
|
|
420
|
-
|
|
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.";
|
|
450
|
+
finishSay("msg err", "notUp");
|
|
422
451
|
return;
|
|
423
452
|
}
|
|
424
453
|
setTimeout(tick, 1000);
|
|
@@ -429,6 +458,9 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
429
458
|
$("pickTelegram").onclick = () => setPlatform("telegram");
|
|
430
459
|
$("pickDiscord").onclick = () => setPlatform("discord");
|
|
431
460
|
$("pickWeb").onclick = () => setPlatform("web");
|
|
461
|
+
$("name").addEventListener("input", showDirHint);
|
|
462
|
+
setFinishLabel("finish");
|
|
463
|
+
showDirHint();
|
|
432
464
|
loadEnvironment();
|
|
433
465
|
setPlatform("telegram");
|
|
434
466
|
})();
|
package/dist/setup-form.js.map
CHANGED
|
@@ -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
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8E3B,gBAAgB,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+SvC,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 {};
|