@songsid/agend 2.1.12-beta.9 → 2.2.0-alpha.1
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/README.md +2 -2
- package/README.zh-TW.md +2 -2
- package/dist/auth/one-time-code.d.ts +19 -0
- package/dist/auth/one-time-code.js +49 -0
- package/dist/auth/one-time-code.js.map +1 -0
- package/dist/auth-api.d.ts +52 -0
- package/dist/auth-api.js +346 -0
- package/dist/auth-api.js.map +1 -0
- package/dist/backend/claude-code.d.ts +4 -0
- package/dist/backend/claude-code.js +97 -0
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.js +59 -30
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/kiro-agent.d.ts +66 -0
- package/dist/backend/kiro-agent.js +302 -0
- package/dist/backend/kiro-agent.js.map +1 -0
- package/dist/backend/kiro-identity.d.ts +80 -0
- package/dist/backend/kiro-identity.js +537 -0
- package/dist/backend/kiro-identity.js.map +1 -0
- package/dist/backend/kiro-v2-store-worker.d.ts +1 -0
- package/dist/backend/kiro-v2-store-worker.js +87 -0
- package/dist/backend/kiro-v2-store-worker.js.map +1 -0
- package/dist/backend/kiro-v2-store.d.ts +54 -0
- package/dist/backend/kiro-v2-store.js +131 -0
- package/dist/backend/kiro-v2-store.js.map +1 -0
- package/dist/backend/kiro-v3-identity.d.ts +5 -0
- package/dist/backend/kiro-v3-identity.js +22 -0
- package/dist/backend/kiro-v3-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +83 -7
- package/dist/backend/kiro.js +418 -91
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +45 -3
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +1 -0
- package/dist/channel/adapters/discord.js +29 -10
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +1 -0
- package/dist/channel/adapters/telegram.js +13 -1
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/types.d.ts +9 -1
- package/dist/classic-channel-manager.d.ts +18 -10
- package/dist/classic-channel-manager.js +45 -18
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +225 -127
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +5 -3
- package/dist/command-table.js +29 -27
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +46 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -1
- package/dist/connection-secrets.d.ts +6 -0
- package/dist/connection-secrets.js +4 -0
- package/dist/connection-secrets.js.map +1 -1
- package/dist/daemon.d.ts +89 -4
- package/dist/daemon.js +661 -77
- package/dist/daemon.js.map +1 -1
- package/dist/darwin-memory.d.ts +6 -1
- package/dist/darwin-memory.js +17 -6
- package/dist/darwin-memory.js.map +1 -1
- package/dist/delivery-consumed-watch.d.ts +85 -0
- package/dist/delivery-consumed-watch.js +244 -0
- package/dist/delivery-consumed-watch.js.map +1 -0
- package/dist/delivery-outbox.d.ts +61 -0
- package/dist/delivery-outbox.js +120 -2
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +14 -5
- package/dist/delivery-reconciliation.js +27 -4
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/delivery-reconciliation.test.js +35 -1
- package/dist/delivery-reconciliation.test.js.map +1 -1
- package/dist/event-loop-watch.d.ts +28 -0
- package/dist/event-loop-watch.js +75 -3
- package/dist/event-loop-watch.js.map +1 -1
- package/dist/fleet-context.d.ts +22 -2
- package/dist/fleet-level-config.d.ts +2 -2
- package/dist/fleet-level-config.js +6 -1
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +293 -12
- package/dist/fleet-manager.js +2364 -449
- package/dist/fleet-manager.js.map +1 -1
- package/dist/host-memory.d.ts +4 -0
- package/dist/host-memory.js.map +1 -1
- package/dist/instance-config-impact.js +5 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +10 -5
- package/dist/instance-lifecycle.js +162 -53
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +150 -16
- package/dist/locale.js.map +1 -1
- package/dist/login-controller.d.ts +2 -0
- package/dist/login-controller.js +17 -5
- package/dist/login-controller.js.map +1 -1
- package/dist/memory-pressure.d.ts +7 -8
- package/dist/memory-pressure.js +76 -15
- package/dist/memory-pressure.js.map +1 -1
- package/dist/monotonic-budget.d.ts +2 -0
- package/dist/monotonic-budget.js +28 -0
- package/dist/monotonic-budget.js.map +1 -0
- package/dist/needs-you-hub.d.ts +114 -0
- package/dist/needs-you-hub.js +370 -0
- package/dist/needs-you-hub.js.map +1 -0
- package/dist/needs-you-live.d.ts +116 -0
- package/dist/needs-you-live.js +228 -0
- package/dist/needs-you-live.js.map +1 -0
- package/dist/needs-you.d.ts +132 -0
- package/dist/needs-you.js +164 -0
- package/dist/needs-you.js.map +1 -0
- package/dist/node-version-guard.d.ts +30 -0
- package/dist/node-version-guard.js +50 -0
- package/dist/node-version-guard.js.map +1 -0
- package/dist/operator-control-paths.d.ts +4 -0
- package/dist/operator-control-paths.js +24 -0
- package/dist/operator-control-paths.js.map +1 -0
- package/dist/outbound-handlers.d.ts +2 -0
- package/dist/outbound-handlers.js +9 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +5 -5
- package/dist/profile-control.js +4 -20
- package/dist/profile-control.js.map +1 -1
- package/dist/public-web-gateway.d.ts +18 -0
- package/dist/public-web-gateway.js +141 -0
- package/dist/public-web-gateway.js.map +1 -0
- package/dist/public-web-link.d.ts +53 -0
- package/dist/public-web-link.js +175 -0
- package/dist/public-web-link.js.map +1 -0
- package/dist/quickstart-api.d.ts +8 -1
- package/dist/quickstart-api.js +86 -48
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +1 -0
- package/dist/quickstart.js +6 -2
- package/dist/quickstart.js.map +1 -1
- package/dist/scheduler/db.d.ts +7 -1
- package/dist/scheduler/db.js +38 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/index.d.ts +1 -1
- package/dist/scheduler/retry-label.d.ts +11 -0
- package/dist/scheduler/retry-label.js +24 -0
- package/dist/scheduler/retry-label.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +55 -2
- package/dist/scheduler/scheduler.js +220 -8
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/types.d.ts +22 -0
- package/dist/scheduler/types.js.map +1 -1
- package/dist/secret-store.d.ts +5 -0
- package/dist/secret-store.js +20 -0
- package/dist/secret-store.js.map +1 -1
- package/dist/service-activation.d.ts +110 -0
- package/dist/service-activation.js +302 -0
- package/dist/service-activation.js.map +1 -0
- package/dist/service-installer.d.ts +7 -0
- package/dist/service-installer.js +7 -4
- package/dist/service-installer.js.map +1 -1
- package/dist/service-restart-selection.d.ts +6 -5
- package/dist/service-restart-selection.js +6 -5
- package/dist/service-restart-selection.js.map +1 -1
- package/dist/settings-api.d.ts +10 -2
- package/dist/settings-api.js +175 -72
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-baseline.d.ts +20 -0
- package/dist/settings-baseline.js +104 -0
- package/dist/settings-baseline.js.map +1 -0
- package/dist/settings-change.d.ts +21 -0
- package/dist/settings-change.js +133 -0
- package/dist/settings-change.js.map +1 -0
- package/dist/settings-confirmation.d.ts +105 -0
- package/dist/settings-confirmation.js +232 -0
- package/dist/settings-confirmation.js.map +1 -0
- package/dist/settings-control.d.ts +41 -0
- package/dist/settings-control.js +294 -0
- package/dist/settings-control.js.map +1 -0
- package/dist/settings-effect.d.ts +24 -0
- package/dist/settings-effect.js +169 -0
- package/dist/settings-effect.js.map +1 -0
- package/dist/settings-http-confirmation.d.ts +29 -0
- package/dist/settings-http-confirmation.js +123 -0
- package/dist/settings-http-confirmation.js.map +1 -0
- package/dist/settings-instance-patch.d.ts +4 -0
- package/dist/settings-instance-patch.js +26 -0
- package/dist/settings-instance-patch.js.map +1 -0
- package/dist/settings-request-capability.d.ts +16 -0
- package/dist/settings-request-capability.js +32 -0
- package/dist/settings-request-capability.js.map +1 -0
- package/dist/settings-transaction.d.ts +65 -0
- package/dist/settings-transaction.js +223 -0
- package/dist/settings-transaction.js.map +1 -0
- package/dist/setup-auth.d.ts +10 -18
- package/dist/setup-auth.js +14 -38
- package/dist/setup-auth.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +75 -18
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-guide.d.ts +13 -0
- package/dist/setup-guide.js +25 -0
- package/dist/setup-guide.js.map +1 -0
- package/dist/setup-host.d.ts +16 -0
- package/dist/setup-host.js +197 -32
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +3 -0
- package/dist/setup-wizard.js.map +1 -1
- package/dist/slash-authz.d.ts +17 -3
- package/dist/slash-authz.js +5 -0
- package/dist/slash-authz.js.map +1 -1
- package/dist/spawn-gate.js +3 -3
- package/dist/spawn-gate.js.map +1 -1
- package/dist/statusline-watcher.d.ts +5 -0
- package/dist/statusline-watcher.js +2 -0
- package/dist/statusline-watcher.js.map +1 -1
- package/dist/steer-capability.d.ts +6 -0
- package/dist/steer-capability.js +8 -0
- package/dist/steer-capability.js.map +1 -1
- package/dist/sync-work-attribution.d.ts +14 -0
- package/dist/sync-work-attribution.js +43 -8
- package/dist/sync-work-attribution.js.map +1 -1
- package/dist/tips.js +10 -5
- package/dist/tips.js.map +1 -1
- package/dist/tmux-control.d.ts +35 -5
- package/dist/tmux-control.js +291 -57
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.d.ts +20 -6
- package/dist/tmux-manager.js +90 -47
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.d.ts +59 -0
- package/dist/tmux-read.js +213 -0
- package/dist/tmux-read.js.map +1 -0
- package/dist/topic-commands.d.ts +21 -10
- package/dist/topic-commands.js +191 -58
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.js +2 -1
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/tunnel/cloudflared-install.d.ts +2 -0
- package/dist/tunnel/cloudflared-install.js +1 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/purpose-lane.d.ts +14 -0
- package/dist/tunnel/purpose-lane.js +43 -0
- package/dist/tunnel/purpose-lane.js.map +1 -0
- package/dist/turn-reply-guard.d.ts +2 -0
- package/dist/turn-reply-guard.js +10 -1
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/ui/app.html +18 -0
- package/dist/ui/chat-render.js +478 -0
- package/dist/ui/chat-store.js +289 -0
- package/dist/ui/chat-strings.js +69 -0
- package/dist/ui/chat-thread.js +209 -0
- package/dist/ui/chat-tour.js +116 -0
- package/dist/ui/fleet-org.js +142 -0
- package/dist/ui/panel-chat.js +418 -0
- package/dist/ui/panel-fleet.js +413 -0
- package/dist/ui/panel-needs.js +202 -0
- package/dist/ui/panel-settings.js +750 -0
- package/dist/ui/preview.js +266 -0
- package/dist/ui/settings-apply.js +239 -0
- package/dist/ui/settings-confirm.js +186 -0
- package/dist/ui/settings-dialogs.js +546 -0
- package/dist/ui/settings-model.js +277 -0
- package/dist/ui/settings-strings.js +652 -0
- package/dist/ui/settings-wizard.js +140 -0
- package/dist/ui/shared/agend-auth.js +93 -0
- package/dist/ui/shared/app-ctx.js +80 -0
- package/dist/ui/shared/app-html.js +9 -0
- package/dist/ui/shared/app-i18n.js +95 -0
- package/dist/ui/shared/app-nav.js +73 -0
- package/dist/ui/shared/app-needs.js +114 -0
- package/dist/ui/shared/app-route.js +73 -0
- package/dist/ui/shared/app-session.js +66 -0
- package/dist/ui/shared/app-shell.js +344 -0
- package/dist/ui/shared/app-store.js +51 -0
- package/dist/ui/shared/app-stream.js +201 -0
- package/dist/ui/shared/app.css +676 -0
- package/dist/ui/shared/app.js +88 -0
- package/dist/ui/shared/fonts/OFL.txt +92 -0
- package/dist/ui/shared/fonts/README.md +21 -0
- package/dist/ui/shared/fonts/inter.woff2 +0 -0
- package/dist/ui/shared/htm.module.js +1 -0
- package/dist/ui/shared/panel-view.js +655 -0
- package/dist/ui/shared/preact-hooks.module.js +2 -0
- package/dist/ui/shared/preact.module.js +2 -0
- package/dist/ui/shared/signin.js +130 -0
- package/dist/ui/shared/theme.js +29 -0
- package/dist/ui/shared/tokens.css +153 -0
- package/dist/ui/shared/ui-confirm.js +43 -0
- package/dist/ui/shared/ui-dialog.js +45 -0
- package/dist/ui/shared/ui-icons.js +61 -0
- package/dist/ui/shared/ui-menu.js +39 -0
- package/dist/ui/shared/ui-states.js +20 -0
- package/dist/ui/shared/ui-toast.js +18 -0
- package/dist/ui/shared/vendor/htm.LICENSE +202 -0
- package/dist/ui/shared/vendor/preact.LICENSE +21 -0
- package/dist/ui/shared/vendor/vendor.json +39 -0
- package/dist/ui/shared/view-strings.js +309 -0
- package/dist/ui/signin.html +51 -0
- package/dist/update-check.d.ts +6 -2
- package/dist/update-check.js +12 -3
- package/dist/update-check.js.map +1 -1
- package/dist/update-install.d.ts +71 -0
- package/dist/update-install.js +162 -0
- package/dist/update-install.js.map +1 -0
- package/dist/upgrade-notices.d.ts +17 -0
- package/dist/upgrade-notices.js +93 -0
- package/dist/upgrade-notices.js.map +1 -0
- package/dist/usage/statusline-usage.d.ts +2 -0
- package/dist/usage/statusline-usage.js +14 -10
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/view-api.d.ts +6 -1
- package/dist/view-api.js +62 -51
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +71 -14
- package/dist/web-api.js +492 -77
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +71 -27
- package/dist/web-auth.js +169 -69
- package/dist/web-auth.js.map +1 -1
- package/dist/web-body.d.ts +3 -0
- package/dist/web-body.js +55 -0
- package/dist/web-body.js.map +1 -0
- package/dist/web-channel-echo.d.ts +9 -0
- package/dist/web-channel-echo.js +55 -0
- package/dist/web-channel-echo.js.map +1 -0
- package/dist/web-chat-history.d.ts +125 -0
- package/dist/web-chat-history.js +184 -0
- package/dist/web-chat-history.js.map +1 -0
- package/dist/web-continuation.d.ts +7 -0
- package/dist/web-continuation.js +22 -0
- package/dist/web-continuation.js.map +1 -0
- package/dist/web-host-guard.d.ts +34 -0
- package/dist/web-host-guard.js +57 -1
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-login.d.ts +72 -0
- package/dist/web-login.js +132 -0
- package/dist/web-login.js.map +1 -0
- package/dist/web-org.d.ts +55 -0
- package/dist/web-org.js +67 -0
- package/dist/web-org.js.map +1 -0
- package/dist/web-preview.d.ts +77 -0
- package/dist/web-preview.js +240 -0
- package/dist/web-preview.js.map +1 -0
- package/dist/web-request-context.d.ts +10 -0
- package/dist/web-request-context.js +13 -0
- package/dist/web-request-context.js.map +1 -0
- package/dist/web-session.d.ts +167 -0
- package/dist/web-session.js +389 -0
- package/dist/web-session.js.map +1 -0
- package/dist/web-shell-routes.d.ts +59 -0
- package/dist/web-shell-routes.js +96 -0
- package/dist/web-shell-routes.js.map +1 -0
- package/dist/web-upload.d.ts +183 -0
- package/dist/web-upload.js +468 -0
- package/dist/web-upload.js.map +1 -0
- package/dist/website-tips-renderer.d.ts +2 -2
- package/dist/website-tips-renderer.js +3 -3
- package/dist/website-tips-renderer.js.map +1 -1
- package/package.json +8 -5
- package/scripts/preinstall-guard.cjs +36 -0
- package/dist/ui/dashboard.html +0 -774
- package/dist/ui/settings.html +0 -2165
- package/dist/ui/view.html +0 -1139
package/dist/setup-auth.js
CHANGED
|
@@ -21,10 +21,9 @@
|
|
|
21
21
|
*
|
|
22
22
|
* See `docs/design/setup-host-tunnel.zh-TW.md` §3.8 and envelope rule 28.
|
|
23
23
|
*/
|
|
24
|
-
import { createHash, randomBytes
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
export const SETUP_CODE_LENGTH = 8;
|
|
24
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
25
|
+
import { constantTimeMatches, formatOneTimeCode, generateOneTimeCode, normalizeOneTimeCode, ONE_TIME_CODE_LENGTH, } from "./auth/one-time-code.js";
|
|
26
|
+
export const SETUP_CODE_LENGTH = ONE_TIME_CODE_LENGTH;
|
|
28
27
|
/**
|
|
29
28
|
* Five, not three.
|
|
30
29
|
*
|
|
@@ -34,40 +33,12 @@ export const SETUP_CODE_LENGTH = 8;
|
|
|
34
33
|
*/
|
|
35
34
|
export const MAX_SETUP_ATTEMPTS = 5;
|
|
36
35
|
export const SETUP_COOKIE_NAME = "agend_setup";
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
/** `ABCD-EFGH` — a shape that survives being read aloud and retyped. */
|
|
44
|
-
export function formatSetupCode(code) {
|
|
45
|
-
return `${code.slice(0, 4)}-${code.slice(4)}`;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* What the person typed, as the code it was meant to be.
|
|
49
|
-
*
|
|
50
|
-
* Case and the dash are presentation. Rejecting `abcd efgh` would be rejecting
|
|
51
|
-
* a correct answer, and the person retyping it has no idea which detail was
|
|
52
|
-
* wrong — so they spend attempts on formatting.
|
|
53
|
-
*/
|
|
54
|
-
export function normalizeSetupCode(input) {
|
|
55
|
-
return input.replace(/[^0-9A-Za-z]/g, "").toUpperCase();
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Fixed-length constant-time compare.
|
|
59
|
-
*
|
|
60
|
-
* Both sides are padded to the same length first: `timingSafeEqual` throws on a
|
|
61
|
-
* mismatch, and returning early on one leaks the length through timing.
|
|
62
|
-
*/
|
|
63
|
-
export function constantTimeMatches(provided, expected) {
|
|
64
|
-
const width = Math.max(provided.length, expected.length, 1);
|
|
65
|
-
const a = Buffer.alloc(width);
|
|
66
|
-
const b = Buffer.alloc(width);
|
|
67
|
-
a.write(provided, "utf8");
|
|
68
|
-
b.write(expected, "utf8");
|
|
69
|
-
return timingSafeEqual(a, b) && provided.length === expected.length;
|
|
70
|
-
}
|
|
36
|
+
// The code primitives live in `auth/one-time-code.ts`; these names stay because
|
|
37
|
+
// the setup page and its tests already speak them.
|
|
38
|
+
export const generateSetupCode = generateOneTimeCode;
|
|
39
|
+
export const formatSetupCode = formatOneTimeCode;
|
|
40
|
+
export const normalizeSetupCode = normalizeOneTimeCode;
|
|
41
|
+
export { constantTimeMatches };
|
|
71
42
|
/** The cookie carries a hash of the secret, never the secret itself. */
|
|
72
43
|
export function setupCookieValue(secret) {
|
|
73
44
|
return createHash("sha256").update(`agend-setup-session-v1:${secret}`).digest("hex");
|
|
@@ -97,6 +68,9 @@ export class SetupCredentials {
|
|
|
97
68
|
sid;
|
|
98
69
|
code;
|
|
99
70
|
secret = null;
|
|
71
|
+
sessionOwner = null;
|
|
72
|
+
get sessionIdentity() { return this.lockedOut ? null : this.sessionOwner; }
|
|
73
|
+
isSessionCurrent(owner) { return !this.lockedOut && !!this.secret && this.sessionOwner === owner; }
|
|
100
74
|
failures = 0;
|
|
101
75
|
constructor(opts = {}) {
|
|
102
76
|
this.sid = opts.sid ?? randomBytes(16).toString("hex");
|
|
@@ -114,6 +88,7 @@ export class SetupCredentials {
|
|
|
114
88
|
revoke() {
|
|
115
89
|
this.revoked = true;
|
|
116
90
|
this.secret = null;
|
|
91
|
+
this.sessionOwner = null;
|
|
117
92
|
}
|
|
118
93
|
get revokedNow() { return this.revoked; }
|
|
119
94
|
get lockedOut() { return this.revoked || this.failures >= MAX_SETUP_ATTEMPTS; }
|
|
@@ -153,6 +128,7 @@ export class SetupCredentials {
|
|
|
153
128
|
if (this.secret)
|
|
154
129
|
return { kind: "rejected", attemptsLeft: this.attemptsLeft };
|
|
155
130
|
this.secret = randomBytes(32).toString("hex");
|
|
131
|
+
this.sessionOwner = { id: randomBytes(16).toString("hex") };
|
|
156
132
|
return { kind: "ok", secret: this.secret };
|
|
157
133
|
}
|
|
158
134
|
/**
|
package/dist/setup-auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-auth.js","sourceRoot":"","sources":["../src/setup-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"setup-auth.js","sourceRoot":"","sources":["../src/setup-auth.ts"],"names":[],"mappings":"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"}
|
package/dist/setup-form.d.ts
CHANGED
|
@@ -15,4 +15,4 @@
|
|
|
15
15
|
* prove they also have the code they learn nothing from it.
|
|
16
16
|
*/
|
|
17
17
|
export declare const SETUP_CODE_PAGE_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<!-- Proof that a request reached THIS listener and not merely something that\n answers 200. The tunnel's readiness probe looks for exactly this. It is the\n sid, which is already in the URL, so it discloses nothing new. -->\n<meta name=\"agend-setup\" content=\"__AGEND_SETUP_MARKER__\">\n<title>Set up AgEnD</title>\n<style>\n body { font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; margin: 0; background: #ffffff; color: #111827; }\n main { max-width: 420px; margin: 0 auto; padding: 64px 20px; }\n h1 { font-size: 20px; margin: 0 0 8px; }\n p { color: #6b7280; font-size: 13px; margin: 0 0 20px; }\n input { width: 100%; padding: 12px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 20px; letter-spacing: 3px; text-align: center; text-transform: uppercase; box-sizing: border-box; }\n button { width: 100%; margin-top: 12px; padding: 12px; border: 1px solid #2563eb; border-radius: 6px; background: #2563eb; color: #fff; font-size: 15px; cursor: pointer; }\n button:disabled { opacity: .5; cursor: default; }\n .err { color: #dc2626; font-size: 13px; margin-top: 12px; min-height: 18px; }\n</style>\n</head>\n<body>\n<main>\n <h1>Set up AgEnD</h1>\n <p>Enter the setup code shown in the terminal where you ran <code>agend setup</code>.</p>\n <form id=\"f\">\n <input id=\"code\" autocomplete=\"off\" autocapitalize=\"characters\" spellcheck=\"false\" placeholder=\"ABCD-EFGH\" maxlength=\"12\" autofocus>\n <button id=\"go\" type=\"submit\">Continue</button>\n </form>\n <div class=\"err\" id=\"msg\"></div>\n</main>\n<script>\n(() => {\n \"use strict\";\n const msg = document.getElementById(\"msg\");\n const go = document.getElementById(\"go\");\n document.getElementById(\"f\").addEventListener(\"submit\", async (event) => {\n event.preventDefault();\n go.disabled = true;\n msg.textContent = \"\";\n let res;\n try {\n res = await fetch(\"open\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ code: document.getElementById(\"code\").value }),\n });\n } catch {\n msg.textContent = \"Could not reach the setup page.\";\n go.disabled = false;\n return;\n }\n if (res.ok) { location.reload(); return; }\n let body = null; try { body = await res.json(); } catch {}\n msg.textContent = (body && body.error) || \"That code was not accepted.\";\n // A spent budget ends the page; there is nothing useful left to type into.\n if (res.status !== 410) go.disabled = false;\n });\n})();\n</script>\n</body>\n</html>\n";
|
|
18
|
-
export declare const SETUP_FORM_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Set up AgEnD</title>\n<style>\n * { box-sizing: border-box; }\n body { font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; margin: 0; background: #ffffff; color: #111827; }\n main { max-width: 640px; margin: 0 auto; padding: 32px 20px 64px; }\n h1 { font-size: 20px; margin: 0 0 4px; }\n .sub { color: #6b7280; font-size: 13px; }\n .step { border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; margin-top: 16px; }\n .step h2 { font-size: 14px; margin: 0 0 12px; }\n label { display: block; font-size: 13px; font-weight: 600; margin: 12px 0 4px; }\n input, select { width: 100%; padding: 8px 10px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 14px; }\n button { padding: 8px 14px; border: 1px solid #e5e7eb; border-radius: 6px; background: #fff; font-size: 14px; cursor: pointer; }\n button.primary { background: #2563eb; color: #fff; border-color: #2563eb; }\n button:disabled { opacity: .5; cursor: default; }\n .row { display: flex; gap: 8px; align-items: center; margin-top: 12px; flex-wrap: wrap; }\n .msg { font-size: 13px; }\n .ok { color: #059669; } .err { color: #dc2626; }\n pre { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 6px; padding: 12px; font-size: 12px; overflow: auto; }\n</style>\n</head>\n<body>\n<main>\n <h1>Set up AgEnD</h1>\n <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>\n\n <div class=\"step\">\n <h2>1 \u00B7 Agent</h2>\n <label for=\"backend\">Backend</label>\n <select id=\"backend\"></select>\n <div class=\"sub\" id=\"backendHint\"></div>\n <label for=\"dir\">Working directory (absolute path)</label>\n <input id=\"dir\" placeholder=\"/home/you/projects/app\">\n <label for=\"name\">First agent name</label>\n <input id=\"name\" value=\"agent-1\">\n </div>\n\n <div class=\"step\">\n <h2>2 \u00B7 Platform</h2>\n <div class=\"row\">\n <button id=\"pickTelegram\" class=\"primary\">Telegram</button>\n <button id=\"pickDiscord\">Discord</button>\n </div>\n <div class=\"sub\" id=\"platformHint\">Create a bot with @BotFather, then add it to a group.</div>\n </div>\n\n <div class=\"step\">\n <h2>3 \u00B7 Credentials</h2>\n <label for=\"token\">Bot token</label>\n <input id=\"token\" type=\"password\" placeholder=\"123456:ABC-DEF\u2026\">\n <div class=\"sub\">Written to ~/.agend/.env and never shown again.</div>\n <label for=\"tokenEnv\">Environment variable</label>\n <input id=\"tokenEnv\" value=\"AGEND_BOT_TOKEN\">\n <div class=\"row\"><button id=\"verify\">Verify</button><span class=\"msg\" id=\"verifyMsg\"></span></div>\n <div id=\"platformFields\"></div>\n </div>\n\n <div class=\"step\">\n <h2>4 \u00B7 Review and start</h2>\n <div class=\"row\"><button id=\"preview\">Show what will be written</button></div>\n <pre id=\"planOut\" hidden></pre>\n <div class=\"row\"><button id=\"finish\" class=\"primary\" disabled>Create and start AgEnD</button><span class=\"msg\" id=\"finishMsg\"></span></div>\n </div>\n</main>\n<script>\n(() => {\n \"use strict\";\n const $ = (id) => document.getElementById(id);\n const state = { platform: \"telegram\", identity: null, guilds: [], offset: 0, plan: null };\n // Relative to the page's own directory, which is /s/<sid>/. An absolute path\n // would leave that namespace and 404 \u2014 the sid is where this page lives, not\n // a prefix bolted on to it.\n const api = async (path, opts) => {\n const res = await fetch(path, Object.assign({ headers: { \"Content-Type\": \"application/json\" } }, opts || {}));\n let body = null; try { body = await res.json(); } catch {}\n return { ok: res.ok, status: res.status, body };\n };\n\n async function loadEnvironment() {\n const res = await api(\"api/settings/quickstart/environment\");\n const backends = res.body?.backends || [];\n $(\"backend\").innerHTML = \"\";\n for (const name of backends.length ? backends : [\"claude-code\"]) {\n const opt = document.createElement(\"option\"); opt.value = name; opt.textContent = name; $(\"backend\").append(opt);\n }\n $(\"backendHint\").textContent = backends.length\n ? \"Found on this host: \" + backends.join(\", \")\n : \"No backend CLI found on this host \u2014 install one first.\";\n }\n\n function setPlatform(platform) {\n state.platform = platform;\n state.identity = null; $(\"verifyMsg\").textContent = \"\";\n $(\"pickTelegram\").className = platform === \"telegram\" ? \"primary\" : \"\";\n $(\"pickDiscord\").className = platform === \"discord\" ? \"primary\" : \"\";\n $(\"platformHint\").textContent = platform === \"telegram\"\n ? \"Create a bot with @BotFather, then add it to a group.\"\n : \"Create an application in the Discord developer portal and invite the bot to your server.\";\n renderPlatformFields();\n }\n\n // Only the ids that platform has \u2014 the same question the CLI asks.\n function renderPlatformFields() {\n const host = $(\"platformFields\"); host.innerHTML = \"\";\n const field = (id, labelText, hint) => {\n const label = document.createElement(\"label\"); label.textContent = labelText; label.htmlFor = id;\n const input = document.createElement(\"input\"); input.id = id;\n host.append(label, input);\n if (hint) { const h = document.createElement(\"div\"); h.className = \"sub\"; h.textContent = hint; host.append(h); }\n return input;\n };\n if (state.platform === \"discord\") {\n const guild = document.createElement(\"select\"); guild.id = \"guild\";\n const label = document.createElement(\"label\"); label.textContent = \"Server (guild)\"; label.htmlFor = \"guild\";\n host.append(label, guild);\n for (const g of state.guilds) { const opt = document.createElement(\"option\"); opt.value = g.id; opt.textContent = g.name + \" (\" + g.id + \")\"; guild.append(opt); }\n field(\"general\", \"General channel id\");\n field(\"admin\", \"Your Discord user id\", \"Becomes the first allowed user.\");\n } else {\n field(\"group\", \"Group id\", \"Or post any message in the group and press Detect.\");\n const row = document.createElement(\"div\"); row.className = \"row\";\n const detect = document.createElement(\"button\"); detect.textContent = \"Detect from a message\";\n detect.onclick = detectGroup; row.append(detect);\n const msg = document.createElement(\"span\"); msg.className = \"msg\"; msg.id = \"detectMsg\"; row.append(msg);\n host.append(row);\n field(\"admin\", \"Your Telegram user id\", \"Becomes the first allowed user.\");\n }\n }\n\n async function detectGroup() {\n $(\"detectMsg\").textContent = \"Waiting for a message in the group\u2026\";\n const res = await api(\"api/settings/quickstart/probe\", { method: \"POST\", body: JSON.stringify({ action: \"await-telegram-start\", token: $(\"token\").value.trim(), offset: state.offset }) });\n if (!res.ok) { $(\"detectMsg\").textContent = res.body?.error || \"Failed.\"; return; }\n state.offset = res.body.offset;\n if (res.body.found) {\n $(\"group\").value = String(res.body.found.groupId);\n $(\"admin\").value = String(res.body.found.userId);\n $(\"detectMsg\").textContent = \"Found.\";\n } else $(\"detectMsg\").textContent = \"No group message yet \u2014 post one and press Detect again.\";\n }\n\n function input() {\n return {\n platform: state.platform,\n token_env: $(\"tokenEnv\").value.trim(),\n backend: $(\"backend\").value,\n working_directory: $(\"dir\").value.trim(),\n instance_name: $(\"name\").value.trim(),\n group_id: $(\"group\") ? $(\"group\").value.trim() || undefined : undefined,\n guild_id: $(\"guild\") ? $(\"guild\").value || undefined : undefined,\n general_channel_id: $(\"general\") ? $(\"general\").value.trim() || undefined : undefined,\n admin_user_id: $(\"admin\") ? $(\"admin\").value.trim() || undefined : undefined,\n };\n }\n\n $(\"verify\").onclick = async () => {\n $(\"verifyMsg\").textContent = \"Asking the provider\u2026\";\n const token = $(\"token\").value.trim();\n const res = await api(\"api/settings/quickstart/probe\", { method: \"POST\", body: JSON.stringify({ action: \"verify\", platform: state.platform, token }) });\n state.identity = res.body?.identity || { valid: false };\n $(\"verifyMsg\").className = \"msg \" + (state.identity.valid ? \"ok\" : \"err\");\n $(\"verifyMsg\").textContent = state.identity.valid ? \"\u2713 \" + (state.identity.username || \"verified\") : \"\u2717 \" + (state.identity.reason || \"rejected\");\n if (state.identity.valid && state.platform === \"discord\") {\n const guilds = await api(\"api/settings/quickstart/probe\", { method: \"POST\", body: JSON.stringify({ action: \"guilds\", token }) });\n state.guilds = guilds.body?.guilds || [];\n renderPlatformFields();\n }\n };\n\n $(\"preview\").onclick = async () => {\n const res = await api(\"api/settings/quickstart/plan\", { method: \"POST\", body: JSON.stringify(input()) });\n if (!res.ok) { $(\"planOut\").hidden = false; $(\"planOut\").textContent = res.body?.error || \"Failed.\"; return; }\n state.plan = res.body;\n $(\"planOut\").hidden = false;\n $(\"planOut\").textContent = JSON.stringify(res.body, null, 2);\n $(\"finish\").disabled = !state.identity?.valid;\n };\n\n $(\"finish\").onclick = async () => {\n $(\"finish\").disabled = true;\n $(\"finishMsg\").textContent = \"Writing configuration\u2026\";\n const commit = await api(\"api/settings/quickstart/commit\", { method: \"POST\", body: JSON.stringify(Object.assign({}, input(), { token: $(\"token\").value.trim() })) });\n if (!commit.ok) { $(\"finishMsg\").className = \"msg err\"; $(\"finishMsg\").textContent = commit.body?.error || \"Failed.\"; $(\"finish\").disabled = false; return; }\n const finished = await api(\"setup/finish\", { method: \"POST\" });\n $(\"finishMsg\").className = \"msg\";\n if (finished.body && finished.body.watch === false) {\n // Behind a tunnel this page and its hostname are both about to stop\n // existing, so there is nothing here to watch. And the dashboard is not\n // the thing to promise: it binds loopback, so a link to it is one this\n // phone cannot open. The channel is what was just set up, and it is\n // where the agent will be.\n $(\"finishMsg\").className = \"msg ok\";\n $(\"finishMsg\").textContent = \"AgEnD is starting. Talk to it in the channel you just set up \u2014 this page is done.\";\n return;\n }\n $(\"finishMsg\").textContent = \"Starting AgEnD\u2026 this page will stop responding while the port changes hands.\";\n waitForFleet();\n };\n\n // The host is gone; the fleet is binding the same port. Connection refused is\n // the expected state in between, so it is not an error until the cap.\n function waitForFleet() {\n const deadline = Date.now() + 60_000;\n const tick = async () => {\n try {\n const res = await fetch(\"/health\", { cache: \"no-store\" });\n if (res.status) {\n $(\"finishMsg\").className = \"msg ok\";\n $(\"finishMsg\").textContent = \"AgEnD is up. Talk to it in the channel you just set up.\";\n return;\n }\n } catch { /* not listening yet */ }\n if (Date.now() > deadline) {\n $(\"finishMsg\").className = \"msg err\";\n $(\"finishMsg\").textContent = \"AgEnD has not come up within 60 seconds. Run `agend start` on the machine itself and check fleet.log \u2014 this page cannot tell you any more.\";\n return;\n }\n setTimeout(tick, 1000);\n };\n setTimeout(tick, 1500);\n }\n\n $(\"pickTelegram\").onclick = () => setPlatform(\"telegram\");\n $(\"pickDiscord\").onclick = () => setPlatform(\"discord\");\n loadEnvironment();\n setPlatform(\"telegram\");\n})();\n</script>\n</body>\n</html>\n";
|
|
18
|
+
export declare const SETUP_FORM_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Set up AgEnD</title>\n<style>\n * { box-sizing: border-box; }\n body { font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; margin: 0; background: #ffffff; color: #111827; }\n main { max-width: 640px; margin: 0 auto; padding: 32px 20px 64px; }\n h1 { font-size: 20px; margin: 0 0 4px; }\n .sub { color: #6b7280; font-size: 13px; }\n .step { border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; margin-top: 16px; }\n .step h2 { font-size: 14px; margin: 0 0 12px; }\n label { display: block; font-size: 13px; font-weight: 600; margin: 12px 0 4px; }\n input, select { width: 100%; padding: 8px 10px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 14px; }\n button { padding: 8px 14px; border: 1px solid #e5e7eb; border-radius: 6px; background: #fff; font-size: 14px; cursor: pointer; }\n button.primary { background: #2563eb; color: #fff; border-color: #2563eb; }\n button:disabled { opacity: .5; cursor: default; }\n .row { display: flex; gap: 8px; align-items: center; margin-top: 12px; flex-wrap: wrap; }\n .msg { font-size: 13px; }\n .ok { color: #059669; } .err { color: #dc2626; }\n pre { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 6px; padding: 12px; font-size: 12px; overflow: auto; }\n</style>\n</head>\n<body>\n<main>\n <h1>Set up AgEnD</h1>\n <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>\n\n <div class=\"step\">\n <h2>1 \u00B7 Agent</h2>\n <label for=\"backend\">Backend</label>\n <select id=\"backend\"></select>\n <div class=\"sub\" id=\"backendHint\"></div>\n <label for=\"dir\">Working directory (absolute path)</label>\n <input id=\"dir\" placeholder=\"/home/you/projects/app\">\n <label for=\"name\">First agent name</label>\n <input id=\"name\" value=\"agent-1\">\n </div>\n\n <div class=\"step\">\n <h2>2 \u00B7 Platform</h2>\n <div class=\"row\">\n <button id=\"pickTelegram\" class=\"primary\">Telegram</button>\n <button id=\"pickDiscord\">Discord</button>\n </div>\n <div class=\"sub\" id=\"platformHint\">Create a bot with @BotFather, then add it to a group.</div>\n </div>\n\n <div class=\"step\">\n <h2>3 \u00B7 Credentials</h2>\n <label for=\"token\">Bot token</label>\n <input id=\"token\" type=\"password\" placeholder=\"123456:ABC-DEF\u2026\">\n <div class=\"sub\">Written to ~/.agend/.env and never shown again.</div>\n <label for=\"tokenEnv\">Environment variable</label>\n <input id=\"tokenEnv\" value=\"AGEND_BOT_TOKEN\">\n <div class=\"row\"><button id=\"verify\">Verify</button><span class=\"msg\" id=\"verifyMsg\"></span></div>\n <div id=\"platformFields\"></div>\n </div>\n\n <div class=\"step\">\n <h2>4 \u00B7 Review and start</h2>\n <div class=\"row\"><button id=\"preview\">Show what will be written</button></div>\n <pre id=\"planOut\" hidden></pre>\n <div class=\"row\"><button id=\"finish\" class=\"primary\" disabled>Create and start AgEnD</button><span class=\"msg\" id=\"finishMsg\"></span></div>\n </div>\n</main>\n<script>\n(() => {\n \"use strict\";\n const $ = (id) => document.getElementById(id);\n const state = { platform: \"telegram\", identity: null, guilds: [], offset: 0, plan: null };\n // Relative to the page's own directory, which is /s/<sid>/. An absolute path\n // would leave that namespace and 404 \u2014 the sid is where this page lives, not\n // a prefix bolted on to it.\n const api = async (path, opts) => {\n const res = await fetch(path, Object.assign({ headers: { \"Content-Type\": \"application/json\" } }, opts || {}));\n let body = null; try { body = await res.json(); } catch {}\n return { ok: res.ok, status: res.status, body };\n };\n\n async function loadEnvironment() {\n const res = await api(\"api/settings/quickstart/environment\");\n if (formFrozen) return;\n const backends = res.body?.backends || [];\n $(\"backend\").innerHTML = \"\";\n for (const name of backends.length ? backends : [\"claude-code\"]) {\n const opt = document.createElement(\"option\"); opt.value = name; opt.textContent = name; $(\"backend\").append(opt);\n }\n $(\"backendHint\").textContent = backends.length\n ? \"Found on this host: \" + backends.join(\", \")\n : \"No backend CLI found on this host \u2014 install one first.\";\n }\n\n function setPlatform(platform) {\n if (formFrozen) return;\n state.platform = platform;\n state.identity = null; $(\"verifyMsg\").textContent = \"\";\n $(\"pickTelegram\").className = platform === \"telegram\" ? \"primary\" : \"\";\n $(\"pickDiscord\").className = platform === \"discord\" ? \"primary\" : \"\";\n $(\"platformHint\").textContent = platform === \"telegram\"\n ? \"Create a bot with @BotFather, then add it to a group.\"\n : \"Create an application in the Discord developer portal and invite the bot to your server.\";\n renderPlatformFields();\n }\n\n // Only the ids that platform has \u2014 the same question the CLI asks.\n function renderPlatformFields() {\n if (formFrozen) return;\n const host = $(\"platformFields\"); host.innerHTML = \"\";\n const field = (id, labelText, hint) => {\n const label = document.createElement(\"label\"); label.textContent = labelText; label.htmlFor = id;\n const input = document.createElement(\"input\"); input.id = id;\n host.append(label, input);\n if (hint) { const h = document.createElement(\"div\"); h.className = \"sub\"; h.textContent = hint; host.append(h); }\n return input;\n };\n if (state.platform === \"discord\") {\n const guild = document.createElement(\"select\"); guild.id = \"guild\";\n const label = document.createElement(\"label\"); label.textContent = \"Server (guild)\"; label.htmlFor = \"guild\";\n host.append(label, guild);\n for (const g of state.guilds) { const opt = document.createElement(\"option\"); opt.value = g.id; opt.textContent = g.name + \" (\" + g.id + \")\"; guild.append(opt); }\n field(\"general\", \"General channel id\");\n field(\"admin\", \"Your Discord user id\", \"Becomes the first allowed user.\");\n } else {\n field(\"group\", \"Group id\", \"Or post any message in the group and press Detect.\");\n const row = document.createElement(\"div\"); row.className = \"row\";\n const detect = document.createElement(\"button\"); detect.id = \"detect\"; detect.textContent = \"Detect from a message\";\n detect.onclick = detectGroup; row.append(detect);\n const msg = document.createElement(\"span\"); msg.className = \"msg\"; msg.id = \"detectMsg\"; row.append(msg);\n host.append(row);\n field(\"admin\", \"Your Telegram user id\", \"Becomes the first allowed user.\");\n }\n }\n\n async function detectGroup() {\n if (formFrozen) return;\n $(\"detectMsg\").textContent = \"Waiting for a message in the group\u2026\";\n const res = await api(\"api/settings/quickstart/probe\", { method: \"POST\", body: JSON.stringify({ action: \"await-telegram-start\", token: $(\"token\").value.trim(), offset: state.offset }) });\n if (formFrozen) return;\n if (!res.ok) { $(\"detectMsg\").textContent = res.body?.error || \"Failed.\"; return; }\n state.offset = res.body.offset;\n if (res.body.found) {\n $(\"group\").value = String(res.body.found.groupId);\n $(\"admin\").value = String(res.body.found.userId);\n $(\"detectMsg\").textContent = \"Found.\";\n } else $(\"detectMsg\").textContent = \"No group message yet \u2014 post one and press Detect again.\";\n }\n\n function input() {\n return {\n platform: state.platform,\n token_env: $(\"tokenEnv\").value.trim(),\n backend: $(\"backend\").value,\n working_directory: $(\"dir\").value.trim(),\n instance_name: $(\"name\").value.trim(),\n group_id: $(\"group\") ? $(\"group\").value.trim() || undefined : undefined,\n guild_id: $(\"guild\") ? $(\"guild\").value || undefined : undefined,\n general_channel_id: $(\"general\") ? $(\"general\").value.trim() || undefined : undefined,\n admin_user_id: $(\"admin\") ? $(\"admin\").value.trim() || undefined : undefined,\n };\n }\n\n $(\"verify\").onclick = async () => {\n if (formFrozen) return;\n $(\"verifyMsg\").textContent = \"Asking the provider\u2026\";\n const token = $(\"token\").value.trim();\n const res = await api(\"api/settings/quickstart/probe\", { method: \"POST\", body: JSON.stringify({ action: \"verify\", platform: state.platform, token }) });\n if (formFrozen) return;\n state.identity = res.body?.identity || { valid: false };\n $(\"verifyMsg\").className = \"msg \" + (state.identity.valid ? \"ok\" : \"err\");\n $(\"verifyMsg\").textContent = state.identity.valid ? \"\u2713 \" + (state.identity.username || \"verified\") : \"\u2717 \" + (state.identity.reason || \"rejected\");\n if (state.identity.valid && state.platform === \"discord\") {\n const guilds = await api(\"api/settings/quickstart/probe\", { method: \"POST\", body: JSON.stringify({ action: \"guilds\", token }) });\n if (formFrozen) return;\n state.guilds = guilds.body?.guilds || [];\n renderPlatformFields();\n }\n };\n\n $(\"preview\").onclick = async () => {\n if (formFrozen) return;\n const res = await api(\"api/settings/quickstart/plan\", { method: \"POST\", body: JSON.stringify(input()) });\n if (formFrozen) return;\n if (!res.ok) { $(\"planOut\").hidden = false; $(\"planOut\").textContent = res.body?.error || \"Failed.\"; return; }\n state.plan = res.body;\n $(\"planOut\").hidden = false;\n $(\"planOut\").textContent = JSON.stringify(res.body, null, 2);\n $(\"finish\").disabled = !state.identity?.valid;\n };\n\n let approvedSetup = false;\n let formFrozen = false;\n let submission = null;\n let pollGeneration = 0;\n const freezeFields = (frozen) => {\n formFrozen = frozen;\n for (const field of document.querySelectorAll(\"main input, main select, #verify, #preview, #pickTelegram, #pickDiscord, #detect\")) field.disabled = frozen;\n };\n const restoreEditing = (message) => {\n pollGeneration++; approvedSetup = false; submission = null; state.identity = null;\n freezeFields(false); $(\"finish\").disabled = true; $(\"finish\").textContent = \"Create and start AgEnD\";\n $(\"finishMsg\").className = \"msg err\"; $(\"finishMsg\").textContent = message;\n };\n for (const field of document.querySelectorAll(\"main input, main select\")) field.addEventListener(\"input\", () => {\n approvedSetup = false; submission = null; $(\"finish\").textContent = \"Create and start AgEnD\";\n });\n const watchPending = (pendingId) => {\n const generation = ++pollGeneration;\n freezeFields(true); $(\"finish\").disabled = true;\n $(\"finishMsg\").textContent = \"On the host, run: agend settings confirm \" + pendingId;\n const poll = async () => {\n if (generation !== pollGeneration) return;\n try {\n const result = await api(\"api/settings/pending/\" + pendingId);\n if (generation !== pollGeneration) return;\n if (!result.ok) { restoreEditing(\"Setup confirmation is no longer available. Review and enter the token again.\"); return; }\n const pending = result.body;\n if (pending.state === \"pending\" || pending.state === \"applying\") { setTimeout(poll, 1000); return; }\n if (pending.state !== \"applied\") { restoreEditing(pending.outcome?.message || \"Change was not applied. Review and enter the token again.\"); return; }\n approvedSetup = true; submission = null; $(\"finish\").disabled = false; $(\"finish\").textContent = \"Start AgEnD\";\n $(\"finishMsg\").textContent = \"Configuration confirmed. Select Start AgEnD to finish setup.\";\n } catch { if (generation === pollGeneration) setTimeout(poll, 1000); }\n };\n setTimeout(poll, 1000);\n };\n $(\"finish\").onclick = async () => {\n $(\"finish\").disabled = true;\n try {\n if (approvedSetup) {\n const finished = await api(\"setup/finish\", { method: \"POST\" });\n if (!finished.ok) { restoreEditing(finished.body?.error || \"Confirmation is no longer current.\"); return; }\n $(\"finishMsg\").textContent = \"AgEnD is starting. Continue in the channel you configured.\";\n if (finished.body?.watch !== false) waitForFleet();\n return;\n }\n // Preserve bytes and key on a lost HTTP response; a deliberate retry after\n // a terminal outcome receives a new key and requires secret reentry.\n submission ||= { key: crypto.randomUUID(), body: JSON.stringify(Object.assign({}, input(), { token: $(\"token\").value.trim() })) };\n freezeFields(true);\n $(\"finishMsg\").textContent = \"Requesting host confirmation\u2026\";\n const commit = await api(\"api/settings/quickstart/commit\", { method: \"POST\", headers: { \"Content-Type\": \"application/json\", \"Idempotency-Key\": submission.key }, body: submission.body });\n if (!commit.ok) { restoreEditing(commit.body?.error || \"Failed.\"); return; }\n if (commit.body?.result !== \"pending_confirmation\" || !commit.body.pending_change?.id) { restoreEditing(\"Expected a pending host confirmation.\"); return; }\n $(\"token\").value = \"\"; submission = null;\n watchPending(commit.body.pending_change.id);\n } catch { $(\"finishMsg\").textContent = \"Connection failed. Retry to find the same confirmation.\"; $(\"finish\").disabled = false; }\n };\n // Same-cookie reloads rediscover an unresolved consent request without a secret.\n api(\"api/settings/pending\").then(result => {\n const pending = result.ok && Array.isArray(result.body) && result.body.find(item => item.state === \"pending\" || item.state === \"applying\");\n if (pending) { watchPending(pending.id); return; }\n api(\"setup/status\").then(status => {\n if (!status.ok || !status.body?.finish_ready || submission || pollGeneration) return;\n approvedSetup = true; freezeFields(true); $(\"finish\").disabled = false; $(\"finish\").textContent = \"Start AgEnD\";\n $(\"finishMsg\").textContent = \"Configuration confirmed. Select Start AgEnD to finish setup.\";\n }).catch(() => {});\n }).catch(() => {});\n\n // The host is gone; the fleet is binding the same port. Connection refused is\n // the expected state in between, so it is not an error until the cap.\n function waitForFleet() {\n const deadline = Date.now() + 60_000;\n const tick = async () => {\n try {\n const res = await fetch(\"/health\", { cache: \"no-store\" });\n if (res.status) {\n $(\"finishMsg\").className = \"msg ok\";\n $(\"finishMsg\").textContent = \"AgEnD is up. Talk to it in the channel you just set up.\";\n return;\n }\n } catch { /* not listening yet */ }\n if (Date.now() > deadline) {\n $(\"finishMsg\").className = \"msg err\";\n $(\"finishMsg\").textContent = \"AgEnD has not come up within 60 seconds. Run `agend start` on the machine itself and check fleet.log \u2014 this page cannot tell you any more.\";\n return;\n }\n setTimeout(tick, 1000);\n };\n setTimeout(tick, 1500);\n }\n\n $(\"pickTelegram\").onclick = () => setPlatform(\"telegram\");\n $(\"pickDiscord\").onclick = () => setPlatform(\"discord\");\n loadEnvironment();\n setPlatform(\"telegram\");\n})();\n</script>\n</body>\n</html>\n";
|
package/dist/setup-form.js
CHANGED
|
@@ -161,6 +161,7 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
161
161
|
|
|
162
162
|
async function loadEnvironment() {
|
|
163
163
|
const res = await api("api/settings/quickstart/environment");
|
|
164
|
+
if (formFrozen) return;
|
|
164
165
|
const backends = res.body?.backends || [];
|
|
165
166
|
$("backend").innerHTML = "";
|
|
166
167
|
for (const name of backends.length ? backends : ["claude-code"]) {
|
|
@@ -172,6 +173,7 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
function setPlatform(platform) {
|
|
176
|
+
if (formFrozen) return;
|
|
175
177
|
state.platform = platform;
|
|
176
178
|
state.identity = null; $("verifyMsg").textContent = "";
|
|
177
179
|
$("pickTelegram").className = platform === "telegram" ? "primary" : "";
|
|
@@ -184,6 +186,7 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
184
186
|
|
|
185
187
|
// Only the ids that platform has — the same question the CLI asks.
|
|
186
188
|
function renderPlatformFields() {
|
|
189
|
+
if (formFrozen) return;
|
|
187
190
|
const host = $("platformFields"); host.innerHTML = "";
|
|
188
191
|
const field = (id, labelText, hint) => {
|
|
189
192
|
const label = document.createElement("label"); label.textContent = labelText; label.htmlFor = id;
|
|
@@ -202,7 +205,7 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
202
205
|
} else {
|
|
203
206
|
field("group", "Group id", "Or post any message in the group and press Detect.");
|
|
204
207
|
const row = document.createElement("div"); row.className = "row";
|
|
205
|
-
const detect = document.createElement("button"); detect.textContent = "Detect from a message";
|
|
208
|
+
const detect = document.createElement("button"); detect.id = "detect"; detect.textContent = "Detect from a message";
|
|
206
209
|
detect.onclick = detectGroup; row.append(detect);
|
|
207
210
|
const msg = document.createElement("span"); msg.className = "msg"; msg.id = "detectMsg"; row.append(msg);
|
|
208
211
|
host.append(row);
|
|
@@ -211,8 +214,10 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
211
214
|
}
|
|
212
215
|
|
|
213
216
|
async function detectGroup() {
|
|
217
|
+
if (formFrozen) return;
|
|
214
218
|
$("detectMsg").textContent = "Waiting for a message in the group…";
|
|
215
219
|
const res = await api("api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "await-telegram-start", token: $("token").value.trim(), offset: state.offset }) });
|
|
220
|
+
if (formFrozen) return;
|
|
216
221
|
if (!res.ok) { $("detectMsg").textContent = res.body?.error || "Failed."; return; }
|
|
217
222
|
state.offset = res.body.offset;
|
|
218
223
|
if (res.body.found) {
|
|
@@ -237,21 +242,26 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
237
242
|
}
|
|
238
243
|
|
|
239
244
|
$("verify").onclick = async () => {
|
|
245
|
+
if (formFrozen) return;
|
|
240
246
|
$("verifyMsg").textContent = "Asking the provider…";
|
|
241
247
|
const token = $("token").value.trim();
|
|
242
248
|
const res = await api("api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "verify", platform: state.platform, token }) });
|
|
249
|
+
if (formFrozen) return;
|
|
243
250
|
state.identity = res.body?.identity || { valid: false };
|
|
244
251
|
$("verifyMsg").className = "msg " + (state.identity.valid ? "ok" : "err");
|
|
245
252
|
$("verifyMsg").textContent = state.identity.valid ? "✓ " + (state.identity.username || "verified") : "✗ " + (state.identity.reason || "rejected");
|
|
246
253
|
if (state.identity.valid && state.platform === "discord") {
|
|
247
254
|
const guilds = await api("api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "guilds", token }) });
|
|
255
|
+
if (formFrozen) return;
|
|
248
256
|
state.guilds = guilds.body?.guilds || [];
|
|
249
257
|
renderPlatformFields();
|
|
250
258
|
}
|
|
251
259
|
};
|
|
252
260
|
|
|
253
261
|
$("preview").onclick = async () => {
|
|
262
|
+
if (formFrozen) return;
|
|
254
263
|
const res = await api("api/settings/quickstart/plan", { method: "POST", body: JSON.stringify(input()) });
|
|
264
|
+
if (formFrozen) return;
|
|
255
265
|
if (!res.ok) { $("planOut").hidden = false; $("planOut").textContent = res.body?.error || "Failed."; return; }
|
|
256
266
|
state.plan = res.body;
|
|
257
267
|
$("planOut").hidden = false;
|
|
@@ -259,26 +269,73 @@ export const SETUP_FORM_HTML = `<!DOCTYPE html>
|
|
|
259
269
|
$("finish").disabled = !state.identity?.valid;
|
|
260
270
|
};
|
|
261
271
|
|
|
272
|
+
let approvedSetup = false;
|
|
273
|
+
let formFrozen = false;
|
|
274
|
+
let submission = null;
|
|
275
|
+
let pollGeneration = 0;
|
|
276
|
+
const freezeFields = (frozen) => {
|
|
277
|
+
formFrozen = frozen;
|
|
278
|
+
for (const field of document.querySelectorAll("main input, main select, #verify, #preview, #pickTelegram, #pickDiscord, #detect")) field.disabled = frozen;
|
|
279
|
+
};
|
|
280
|
+
const restoreEditing = (message) => {
|
|
281
|
+
pollGeneration++; approvedSetup = false; submission = null; state.identity = null;
|
|
282
|
+
freezeFields(false); $("finish").disabled = true; $("finish").textContent = "Create and start AgEnD";
|
|
283
|
+
$("finishMsg").className = "msg err"; $("finishMsg").textContent = message;
|
|
284
|
+
};
|
|
285
|
+
for (const field of document.querySelectorAll("main input, main select")) field.addEventListener("input", () => {
|
|
286
|
+
approvedSetup = false; submission = null; $("finish").textContent = "Create and start AgEnD";
|
|
287
|
+
});
|
|
288
|
+
const watchPending = (pendingId) => {
|
|
289
|
+
const generation = ++pollGeneration;
|
|
290
|
+
freezeFields(true); $("finish").disabled = true;
|
|
291
|
+
$("finishMsg").textContent = "On the host, run: agend settings confirm " + pendingId;
|
|
292
|
+
const poll = async () => {
|
|
293
|
+
if (generation !== pollGeneration) return;
|
|
294
|
+
try {
|
|
295
|
+
const result = await api("api/settings/pending/" + pendingId);
|
|
296
|
+
if (generation !== pollGeneration) return;
|
|
297
|
+
if (!result.ok) { restoreEditing("Setup confirmation is no longer available. Review and enter the token again."); return; }
|
|
298
|
+
const pending = result.body;
|
|
299
|
+
if (pending.state === "pending" || pending.state === "applying") { setTimeout(poll, 1000); return; }
|
|
300
|
+
if (pending.state !== "applied") { restoreEditing(pending.outcome?.message || "Change was not applied. Review and enter the token again."); return; }
|
|
301
|
+
approvedSetup = true; submission = null; $("finish").disabled = false; $("finish").textContent = "Start AgEnD";
|
|
302
|
+
$("finishMsg").textContent = "Configuration confirmed. Select Start AgEnD to finish setup.";
|
|
303
|
+
} catch { if (generation === pollGeneration) setTimeout(poll, 1000); }
|
|
304
|
+
};
|
|
305
|
+
setTimeout(poll, 1000);
|
|
306
|
+
};
|
|
262
307
|
$("finish").onclick = async () => {
|
|
263
308
|
$("finish").disabled = true;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
$("finishMsg").textContent = "
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
309
|
+
try {
|
|
310
|
+
if (approvedSetup) {
|
|
311
|
+
const finished = await api("setup/finish", { method: "POST" });
|
|
312
|
+
if (!finished.ok) { restoreEditing(finished.body?.error || "Confirmation is no longer current."); return; }
|
|
313
|
+
$("finishMsg").textContent = "AgEnD is starting. Continue in the channel you configured.";
|
|
314
|
+
if (finished.body?.watch !== false) waitForFleet();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
// Preserve bytes and key on a lost HTTP response; a deliberate retry after
|
|
318
|
+
// a terminal outcome receives a new key and requires secret reentry.
|
|
319
|
+
submission ||= { key: crypto.randomUUID(), body: JSON.stringify(Object.assign({}, input(), { token: $("token").value.trim() })) };
|
|
320
|
+
freezeFields(true);
|
|
321
|
+
$("finishMsg").textContent = "Requesting host confirmation…";
|
|
322
|
+
const commit = await api("api/settings/quickstart/commit", { method: "POST", headers: { "Content-Type": "application/json", "Idempotency-Key": submission.key }, body: submission.body });
|
|
323
|
+
if (!commit.ok) { restoreEditing(commit.body?.error || "Failed."); return; }
|
|
324
|
+
if (commit.body?.result !== "pending_confirmation" || !commit.body.pending_change?.id) { restoreEditing("Expected a pending host confirmation."); return; }
|
|
325
|
+
$("token").value = ""; submission = null;
|
|
326
|
+
watchPending(commit.body.pending_change.id);
|
|
327
|
+
} catch { $("finishMsg").textContent = "Connection failed. Retry to find the same confirmation."; $("finish").disabled = false; }
|
|
281
328
|
};
|
|
329
|
+
// Same-cookie reloads rediscover an unresolved consent request without a secret.
|
|
330
|
+
api("api/settings/pending").then(result => {
|
|
331
|
+
const pending = result.ok && Array.isArray(result.body) && result.body.find(item => item.state === "pending" || item.state === "applying");
|
|
332
|
+
if (pending) { watchPending(pending.id); return; }
|
|
333
|
+
api("setup/status").then(status => {
|
|
334
|
+
if (!status.ok || !status.body?.finish_ready || submission || pollGeneration) return;
|
|
335
|
+
approvedSetup = true; freezeFields(true); $("finish").disabled = false; $("finish").textContent = "Start AgEnD";
|
|
336
|
+
$("finishMsg").textContent = "Configuration confirmed. Select Start AgEnD to finish setup.";
|
|
337
|
+
}).catch(() => {});
|
|
338
|
+
}).catch(() => {});
|
|
282
339
|
|
|
283
340
|
// The host is gone; the fleet is binding the same port. Connection refused is
|
|
284
341
|
// the expected state in between, so it is not an error until the cap.
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8DnC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmS9B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Locale-keyed setup guide URLs per platform. */
|
|
2
|
+
export declare const SETUP_GUIDE_URLS: {
|
|
3
|
+
readonly telegram: {
|
|
4
|
+
readonly en: "https://songsid.github.io/AgEnD/setup-telegram/";
|
|
5
|
+
readonly "zh-TW": "https://songsid.github.io/AgEnD/zh-tw/setup-telegram/";
|
|
6
|
+
};
|
|
7
|
+
readonly discord: {
|
|
8
|
+
readonly en: "https://songsid.github.io/AgEnD/setup-discord/";
|
|
9
|
+
readonly "zh-TW": "https://songsid.github.io/AgEnD/zh-tw/setup-discord/";
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
/** Returns the locale-appropriate setup guide URL for the given platform. */
|
|
13
|
+
export declare function setupGuideUrl(platform: "telegram" | "discord"): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup guide URLs for the bot-token steps in quickstart and the setup wizard.
|
|
3
|
+
*
|
|
4
|
+
* A single constant used by both src/quickstart.ts and src/setup-wizard.ts to
|
|
5
|
+
* avoid duplication and prevent a circular import (quickstart imports from
|
|
6
|
+
* setup-wizard, so the URLs must live in a third module that both can import).
|
|
7
|
+
*/
|
|
8
|
+
import { getLocale } from "./locale.js";
|
|
9
|
+
/** Locale-keyed setup guide URLs per platform. */
|
|
10
|
+
export const SETUP_GUIDE_URLS = {
|
|
11
|
+
telegram: {
|
|
12
|
+
en: "https://songsid.github.io/AgEnD/setup-telegram/",
|
|
13
|
+
"zh-TW": "https://songsid.github.io/AgEnD/zh-tw/setup-telegram/",
|
|
14
|
+
},
|
|
15
|
+
discord: {
|
|
16
|
+
en: "https://songsid.github.io/AgEnD/setup-discord/",
|
|
17
|
+
"zh-TW": "https://songsid.github.io/AgEnD/zh-tw/setup-discord/",
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
/** Returns the locale-appropriate setup guide URL for the given platform. */
|
|
21
|
+
export function setupGuideUrl(platform) {
|
|
22
|
+
const locale = getLocale();
|
|
23
|
+
return SETUP_GUIDE_URLS[platform][locale === "zh-TW" ? "zh-TW" : "en"];
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=setup-guide.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-guide.js","sourceRoot":"","sources":["../src/setup-guide.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,kDAAkD;AAClD,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE;QACR,EAAE,EAAE,iDAAiD;QACrD,OAAO,EAAE,uDAAuD;KACjE;IACD,OAAO,EAAE;QACP,EAAE,EAAE,gDAAgD;QACpD,OAAO,EAAE,sDAAsD;KAChE;CACO,CAAC;AAEX,6EAA6E;AAC7E,MAAM,UAAU,aAAa,CAAC,QAAgC;IAC5D,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACzE,CAAC"}
|
package/dist/setup-host.d.ts
CHANGED
|
@@ -67,6 +67,8 @@ export declare class SetupHost {
|
|
|
67
67
|
private get basePath();
|
|
68
68
|
private managedTunnel;
|
|
69
69
|
private tunnelHandle;
|
|
70
|
+
private tunnelStartup;
|
|
71
|
+
private tunnelAbort;
|
|
70
72
|
/** The exact host the tunnel answers on; nothing else is added to the set. */
|
|
71
73
|
private externalHost;
|
|
72
74
|
private tunnelStopResult;
|
|
@@ -80,6 +82,19 @@ export declare class SetupHost {
|
|
|
80
82
|
* "starting", and no fleet.
|
|
81
83
|
*/
|
|
82
84
|
private handoverRequested;
|
|
85
|
+
private confirmation;
|
|
86
|
+
private confirmationControl;
|
|
87
|
+
private setupContext;
|
|
88
|
+
private committedFingerprint;
|
|
89
|
+
private committedOwner;
|
|
90
|
+
private committedExecution;
|
|
91
|
+
private committedApproved;
|
|
92
|
+
private startedAt;
|
|
93
|
+
private admittedFinish;
|
|
94
|
+
private shutdownPromise;
|
|
95
|
+
private confirmationGate;
|
|
96
|
+
private finishReady;
|
|
97
|
+
private finishSetup;
|
|
83
98
|
private get ttlMs();
|
|
84
99
|
private get idleMs();
|
|
85
100
|
private log;
|
|
@@ -145,6 +160,7 @@ export declare class SetupHost {
|
|
|
145
160
|
* hang at "starting…" and the fleet would never be spawned.
|
|
146
161
|
*/
|
|
147
162
|
shutdown(spawnSuccessor: boolean, reason: string): Promise<void>;
|
|
163
|
+
private performShutdown;
|
|
148
164
|
/**
|
|
149
165
|
* Whether an unconfirmed tunnel may be left behind while the fleet starts.
|
|
150
166
|
*
|