@songsid/agend 2.1.4-beta.25 → 2.1.4-beta.26
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/channel/types.d.ts +1 -1
- package/dist/fleet-context.d.ts +23 -0
- package/dist/fleet-manager.d.ts +46 -0
- package/dist/fleet-manager.js +272 -0
- package/dist/fleet-manager.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +16 -0
- package/dist/instance-lifecycle.js +44 -2
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +44 -0
- package/dist/locale.js.map +1 -1
- package/dist/login-flows.d.ts +79 -0
- package/dist/login-flows.js +152 -0
- package/dist/login-flows.js.map +1 -0
- package/dist/login-manager.d.ts +63 -0
- package/dist/login-manager.js +134 -0
- package/dist/login-manager.js.map +1 -0
- package/dist/tmux-manager.d.ts +6 -0
- package/dist/tmux-manager.js +12 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/topic-commands.d.ts +6 -0
- package/dist/topic-commands.js +53 -0
- package/dist/topic-commands.js.map +1 -1
- package/package.json +1 -1
package/dist/channel/types.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface InstanceStatusData {
|
|
|
10
10
|
costCents: number;
|
|
11
11
|
}
|
|
12
12
|
export interface AlertData {
|
|
13
|
-
type: "hang" | "cost_warn" | "cost_limit" | "schedule_deferred" | "rotation" | "cancel" | "interactive_prompt" | "exit_restart" | "clear_confirm" | "tip";
|
|
13
|
+
type: "hang" | "cost_warn" | "cost_limit" | "schedule_deferred" | "rotation" | "cancel" | "interactive_prompt" | "exit_restart" | "clear_confirm" | "tip" | "login";
|
|
14
14
|
instanceName: string;
|
|
15
15
|
message: string;
|
|
16
16
|
choices?: Choice[];
|
package/dist/fleet-context.d.ts
CHANGED
|
@@ -93,6 +93,29 @@ export interface FleetContext {
|
|
|
93
93
|
promptTip?(generalName: string, adapter: import("./channel/types.js").ChannelAdapter, chatId: string, threadId?: string): Promise<"posted" | "empty" | "unavailable">;
|
|
94
94
|
/** Persistently enable advanced tips without waiting for the dismissal threshold. */
|
|
95
95
|
unlockAdvancedTips?(userId: string): boolean;
|
|
96
|
+
/** `/login` backend chooser buttons. Caller enforces fleet-admin. */
|
|
97
|
+
promptLoginBackends?(chat: {
|
|
98
|
+
adapter: import("./channel/types.js").ChannelAdapter;
|
|
99
|
+
adapterId: string;
|
|
100
|
+
chatId: string;
|
|
101
|
+
threadId?: string;
|
|
102
|
+
}): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Start a remote login session for one backend; returns a status line, or
|
|
105
|
+
* null when a still-valid-auth confirmation prompt was posted instead.
|
|
106
|
+
*/
|
|
107
|
+
startLoginSession?(backend: string, chat: {
|
|
108
|
+
adapter: import("./channel/types.js").ChannelAdapter;
|
|
109
|
+
adapterId: string;
|
|
110
|
+
chatId: string;
|
|
111
|
+
threadId?: string;
|
|
112
|
+
}, opts?: {
|
|
113
|
+
skipAuthCheck?: boolean;
|
|
114
|
+
}): Promise<string | null>;
|
|
115
|
+
/** Paste admin-supplied text (auth code / start URL) into the login window. */
|
|
116
|
+
loginSubmitInput?(text: string): Promise<string>;
|
|
117
|
+
/** Abort the active login session. */
|
|
118
|
+
cancelLoginSession?(): Promise<string>;
|
|
96
119
|
/** Human-readable effective model for an instance (resolves inherited defaults). */
|
|
97
120
|
modelDisplayForInstance?(name: string): string;
|
|
98
121
|
/**
|
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -811,6 +811,52 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
811
811
|
private isDeliveryEpochCurrent;
|
|
812
812
|
/** Invalidate work queued before a user cancel and wake idle-gate waiters. */
|
|
813
813
|
private cancelPendingDeliveries;
|
|
814
|
+
private activeLogin;
|
|
815
|
+
/** Post the backend chooser for a bare `/login`. Caller enforces admin. */
|
|
816
|
+
promptLoginBackends(chat: {
|
|
817
|
+
adapter: ChannelAdapter;
|
|
818
|
+
adapterId: string;
|
|
819
|
+
chatId: string;
|
|
820
|
+
threadId?: string;
|
|
821
|
+
}): Promise<void>;
|
|
822
|
+
/**
|
|
823
|
+
* Start a login session for one backend. Caller enforces admin.
|
|
824
|
+
* Returns a status line to post, or null when a confirmation prompt was
|
|
825
|
+
* posted instead (auth still valid — see the pre-check below).
|
|
826
|
+
*/
|
|
827
|
+
startLoginSession(backendArg: string, chat: {
|
|
828
|
+
adapter: ChannelAdapter;
|
|
829
|
+
adapterId: string;
|
|
830
|
+
chatId: string;
|
|
831
|
+
threadId?: string;
|
|
832
|
+
}, opts?: {
|
|
833
|
+
skipAuthCheck?: boolean;
|
|
834
|
+
}): Promise<string | null>;
|
|
835
|
+
/** Create the login window and session (pre-check already settled). */
|
|
836
|
+
private launchLoginSession;
|
|
837
|
+
/** `/login code <text>` — paste admin-supplied text into the login window. */
|
|
838
|
+
loginSubmitInput(text: string): Promise<string>;
|
|
839
|
+
/** `/login cancel` — abort the active session and remove its window. */
|
|
840
|
+
cancelLoginSession(): Promise<string>;
|
|
841
|
+
/**
|
|
842
|
+
* The URL (+ code) is a live credential: whoever completes it binds THEIR
|
|
843
|
+
* account to this fleet's CLI. Telegram gets an HTML spoiler (same treatment
|
|
844
|
+
* as the dashboard token); other adapters get plain text with the warning.
|
|
845
|
+
*/
|
|
846
|
+
private sendLoginSecret;
|
|
847
|
+
/**
|
|
848
|
+
* After a successful login: wake the paused instances of that backend AND
|
|
849
|
+
* restart the running ones — a live CLI holds the old token in memory and
|
|
850
|
+
* only re-reads credentials on process start (a paused instance's CLI is
|
|
851
|
+
* already dead, so waking it respawns with the new token for free).
|
|
852
|
+
*/
|
|
853
|
+
private recoverBackendInstances;
|
|
854
|
+
/** Backend chooser button → start that backend's login session. */
|
|
855
|
+
private handleLoginBackendSelect;
|
|
856
|
+
/** Re-login confirmation (auth pre-check said credentials still work). */
|
|
857
|
+
private handleLoginConfirm;
|
|
858
|
+
/** Kiro provider button → drive the CLI's arrow-key selector. */
|
|
859
|
+
private handleLoginMenuSelect;
|
|
814
860
|
queueMirrorMessage(text: string): void;
|
|
815
861
|
/** Push an SSE event to all connected Web UI clients. */
|
|
816
862
|
emitSseEvent(event: string, data: unknown): void;
|
package/dist/fleet-manager.js
CHANGED
|
@@ -44,6 +44,8 @@ import { outboundHandlers } from "./outbound-handlers.js";
|
|
|
44
44
|
import { handleWebRequest, broadcastSseEvent } from "./web-api.js";
|
|
45
45
|
import { handleViewRequest, isViewPath } from "./view-api.js";
|
|
46
46
|
import { handleUsageRequest, isUsagePath, usageProviderIdForBackend } from "./usage/usage-api.js";
|
|
47
|
+
import { LOGIN_FLOWS, LOGIN_BACKEND_ALIASES, checkAuthStatus } from "./login-flows.js";
|
|
48
|
+
import { LoginSession } from "./login-manager.js";
|
|
47
49
|
import { handleSettingsRequest } from "./settings-api.js";
|
|
48
50
|
import { setLocale, detectLocale, getLocale, t } from "./locale.js";
|
|
49
51
|
import { handleAgentRequest } from "./agent-endpoint.js";
|
|
@@ -209,6 +211,9 @@ const HANG_CALLBACK_PREFIX = "hang:";
|
|
|
209
211
|
const CLEAR_CONFIRM_CALLBACK_PREFIX = "clear-confirm:";
|
|
210
212
|
const TIP_DISMISS_CALLBACK_PREFIX = "tip-dismiss:";
|
|
211
213
|
const TIP_UNLOCK_CALLBACK_PREFIX = "tip-unlock:";
|
|
214
|
+
const LOGIN_CALLBACK_PREFIX = "login:";
|
|
215
|
+
const LOGIN_MENU_CALLBACK_PREFIX = "login-menu:";
|
|
216
|
+
const LOGIN_CONFIRM_CALLBACK_PREFIX = "login-confirm:";
|
|
212
217
|
const CLEAR_CONFIRM_TIMEOUT_MS = 15_000;
|
|
213
218
|
/** Default lifetime for long-lived nonce prompts (clear overrides this to 15s). */
|
|
214
219
|
const NONCE_BUTTON_TIMEOUT_MS = 15 * 60_000;
|
|
@@ -2364,6 +2369,12 @@ export class FleetManager {
|
|
|
2364
2369
|
return;
|
|
2365
2370
|
if (await this.handleTipUnlock(data, adapterId, this.adapter ?? undefined))
|
|
2366
2371
|
return;
|
|
2372
|
+
if (await this.handleLoginBackendSelect(data, adapterId, this.adapter ?? undefined))
|
|
2373
|
+
return;
|
|
2374
|
+
if (await this.handleLoginMenuSelect(data, adapterId, this.adapter ?? undefined))
|
|
2375
|
+
return;
|
|
2376
|
+
if (await this.handleLoginConfirm(data, adapterId, this.adapter ?? undefined))
|
|
2377
|
+
return;
|
|
2367
2378
|
if (await this.handleClearConfirmation(data, adapterId, this.adapter ?? undefined))
|
|
2368
2379
|
return;
|
|
2369
2380
|
if (await this.handleExitRestartPrompt(data, adapterId, this.adapter ?? undefined))
|
|
@@ -2737,6 +2748,12 @@ export class FleetManager {
|
|
|
2737
2748
|
return;
|
|
2738
2749
|
if (await this.handleTipUnlock(data, adapterId, adapter))
|
|
2739
2750
|
return;
|
|
2751
|
+
if (await this.handleLoginBackendSelect(data, adapterId, adapter))
|
|
2752
|
+
return;
|
|
2753
|
+
if (await this.handleLoginMenuSelect(data, adapterId, adapter))
|
|
2754
|
+
return;
|
|
2755
|
+
if (await this.handleLoginConfirm(data, adapterId, adapter))
|
|
2756
|
+
return;
|
|
2740
2757
|
if (await this.handleClearConfirmation(data, adapterId, adapter))
|
|
2741
2758
|
return;
|
|
2742
2759
|
if (await this.handleExitRestartPrompt(data, adapterId, adapter))
|
|
@@ -6175,6 +6192,261 @@ export class FleetManager {
|
|
|
6175
6192
|
check();
|
|
6176
6193
|
return next;
|
|
6177
6194
|
}
|
|
6195
|
+
// ── Remote CLI login (`/login`) ──────────────────────────────────────────
|
|
6196
|
+
//
|
|
6197
|
+
// One session at a time, in a dedicated tmux window — never an instance
|
|
6198
|
+
// pane. Credentials are per-backend (codex homes symlink auth.json to the
|
|
6199
|
+
// shared ~/.codex; the other CLIs use one real home), so a single sign-in
|
|
6200
|
+
// repairs every instance of that backend, and instance delivery, pane-state
|
|
6201
|
+
// detection, tool progress, and mcp_proxy_reply never observe login output.
|
|
6202
|
+
activeLogin = null;
|
|
6203
|
+
/** Post the backend chooser for a bare `/login`. Caller enforces admin. */
|
|
6204
|
+
async promptLoginBackends(chat) {
|
|
6205
|
+
const configured = new Set();
|
|
6206
|
+
for (const [, config] of Object.entries(this.fleetConfig?.instances ?? {})) {
|
|
6207
|
+
configured.add(config.backend ?? this.fleetConfig?.defaults?.backend ?? "claude-code");
|
|
6208
|
+
}
|
|
6209
|
+
const choices = Object.keys(LOGIN_FLOWS)
|
|
6210
|
+
.filter(backend => configured.size === 0 || configured.has(backend))
|
|
6211
|
+
.map(backend => ({ action: backend, label: backend }));
|
|
6212
|
+
if (choices.length === 0) {
|
|
6213
|
+
await chat.adapter.sendText(chat.chatId, t("login.unsupported", [...configured].join(", ")), { threadId: chat.threadId });
|
|
6214
|
+
return;
|
|
6215
|
+
}
|
|
6216
|
+
await this.postNonceButtonPrompt({
|
|
6217
|
+
prefix: LOGIN_CALLBACK_PREFIX,
|
|
6218
|
+
alertType: "login",
|
|
6219
|
+
instanceName: "login",
|
|
6220
|
+
adapter: chat.adapter,
|
|
6221
|
+
adapterId: chat.adapterId,
|
|
6222
|
+
chatId: chat.chatId,
|
|
6223
|
+
threadId: chat.threadId,
|
|
6224
|
+
message: t("login.choose_backend"),
|
|
6225
|
+
choices,
|
|
6226
|
+
expiredText: t("buttons.stale"),
|
|
6227
|
+
});
|
|
6228
|
+
}
|
|
6229
|
+
/**
|
|
6230
|
+
* Start a login session for one backend. Caller enforces admin.
|
|
6231
|
+
* Returns a status line to post, or null when a confirmation prompt was
|
|
6232
|
+
* posted instead (auth still valid — see the pre-check below).
|
|
6233
|
+
*/
|
|
6234
|
+
async startLoginSession(backendArg, chat, opts = {}) {
|
|
6235
|
+
const backend = LOGIN_BACKEND_ALIASES[backendArg.toLowerCase()] ?? backendArg.toLowerCase();
|
|
6236
|
+
const flow = LOGIN_FLOWS[backend];
|
|
6237
|
+
if (!flow)
|
|
6238
|
+
return t("login.unsupported", backendArg);
|
|
6239
|
+
if (this.activeLogin)
|
|
6240
|
+
return t("login.busy", this.activeLogin.backend);
|
|
6241
|
+
// Token-free pre-check (5s cap): re-login while auth still works is
|
|
6242
|
+
// usually a mistake, so it needs a confirmed click. An invalid OR
|
|
6243
|
+
// uncertain result (timeout, missing binary) proceeds straight to login —
|
|
6244
|
+
// an unreliable probe must never block the re-login the admin asked for.
|
|
6245
|
+
if (!opts.skipAuthCheck && flow.authCheck) {
|
|
6246
|
+
const status = await checkAuthStatus(flow.authCheck);
|
|
6247
|
+
if (status === "valid") {
|
|
6248
|
+
await this.postNonceButtonPrompt({
|
|
6249
|
+
prefix: LOGIN_CONFIRM_CALLBACK_PREFIX,
|
|
6250
|
+
alertType: "login",
|
|
6251
|
+
instanceName: backend,
|
|
6252
|
+
adapter: chat.adapter,
|
|
6253
|
+
adapterId: chat.adapterId,
|
|
6254
|
+
chatId: chat.chatId,
|
|
6255
|
+
threadId: chat.threadId,
|
|
6256
|
+
message: t("login.still_valid", backend),
|
|
6257
|
+
choices: [
|
|
6258
|
+
{ action: "go", label: t("login.relogin_go") },
|
|
6259
|
+
{ action: "cancel", label: t("login.relogin_cancel") },
|
|
6260
|
+
],
|
|
6261
|
+
expiredText: t("buttons.stale"),
|
|
6262
|
+
});
|
|
6263
|
+
return null;
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
6266
|
+
return this.launchLoginSession(flow, backend, chat);
|
|
6267
|
+
}
|
|
6268
|
+
/** Create the login window and session (pre-check already settled). */
|
|
6269
|
+
async launchLoginSession(flow, backend, chat) {
|
|
6270
|
+
if (this.activeLogin)
|
|
6271
|
+
return t("login.busy", this.activeLogin.backend);
|
|
6272
|
+
const sessionName = getTmuxSession();
|
|
6273
|
+
await TmuxManager.ensureSession(sessionName);
|
|
6274
|
+
const tmux = new TmuxManager(sessionName, "");
|
|
6275
|
+
const session = new LoginSession(flow, tmux, {
|
|
6276
|
+
onMenu: async (options) => {
|
|
6277
|
+
await this.postNonceButtonPrompt({
|
|
6278
|
+
prefix: LOGIN_MENU_CALLBACK_PREFIX,
|
|
6279
|
+
alertType: "login",
|
|
6280
|
+
instanceName: backend,
|
|
6281
|
+
adapter: chat.adapter,
|
|
6282
|
+
adapterId: chat.adapterId,
|
|
6283
|
+
chatId: chat.chatId,
|
|
6284
|
+
threadId: chat.threadId,
|
|
6285
|
+
message: t("login.choose_provider"),
|
|
6286
|
+
choices: options.map((label, index) => ({ action: String(index), label })),
|
|
6287
|
+
expiredText: t("buttons.stale"),
|
|
6288
|
+
});
|
|
6289
|
+
},
|
|
6290
|
+
onAuthHint: async (url, code) => {
|
|
6291
|
+
await this.sendLoginSecret(chat, backend, url, code);
|
|
6292
|
+
},
|
|
6293
|
+
onNeedInput: async (promptExcerpt) => {
|
|
6294
|
+
await chat.adapter.sendText(chat.chatId, t("login.need_input", backend, promptExcerpt), { threadId: chat.threadId }).catch(() => { });
|
|
6295
|
+
},
|
|
6296
|
+
onDone: async ({ ok, detail }) => {
|
|
6297
|
+
this.activeLogin = null;
|
|
6298
|
+
let text;
|
|
6299
|
+
if (ok) {
|
|
6300
|
+
const { woken, restarted } = await this.recoverBackendInstances(backend);
|
|
6301
|
+
const none = t("login.none");
|
|
6302
|
+
text = t("login.success", backend, woken.length ? woken.join(", ") : none, restarted.length ? restarted.join(", ") : none);
|
|
6303
|
+
}
|
|
6304
|
+
else if (detail === "cancelled") {
|
|
6305
|
+
text = t("login.cancelled", backend);
|
|
6306
|
+
}
|
|
6307
|
+
else {
|
|
6308
|
+
text = t("login.failed", backend, detail);
|
|
6309
|
+
}
|
|
6310
|
+
await chat.adapter.sendText(chat.chatId, text, { threadId: chat.threadId }).catch(() => { });
|
|
6311
|
+
},
|
|
6312
|
+
}, this.logger);
|
|
6313
|
+
// Claim the slot before the first await so two admins racing /login cannot
|
|
6314
|
+
// both create windows; release on startup failure.
|
|
6315
|
+
this.activeLogin = { session, backend, chat };
|
|
6316
|
+
try {
|
|
6317
|
+
await session.start();
|
|
6318
|
+
}
|
|
6319
|
+
catch (err) {
|
|
6320
|
+
this.activeLogin = null;
|
|
6321
|
+
return t("login.failed", backend, err.message);
|
|
6322
|
+
}
|
|
6323
|
+
return t("login.started", backend);
|
|
6324
|
+
}
|
|
6325
|
+
/** `/login code <text>` — paste admin-supplied text into the login window. */
|
|
6326
|
+
async loginSubmitInput(text) {
|
|
6327
|
+
if (!this.activeLogin)
|
|
6328
|
+
return t("login.no_session");
|
|
6329
|
+
const ok = await this.activeLogin.session.submitInput(text);
|
|
6330
|
+
return ok ? t("login.input_sent") : t("login.input_failed");
|
|
6331
|
+
}
|
|
6332
|
+
/** `/login cancel` — abort the active session and remove its window. */
|
|
6333
|
+
async cancelLoginSession() {
|
|
6334
|
+
if (!this.activeLogin)
|
|
6335
|
+
return t("login.no_session");
|
|
6336
|
+
const backend = this.activeLogin.backend;
|
|
6337
|
+
await this.activeLogin.session.cancel();
|
|
6338
|
+
return t("login.cancelled", backend);
|
|
6339
|
+
}
|
|
6340
|
+
/**
|
|
6341
|
+
* The URL (+ code) is a live credential: whoever completes it binds THEIR
|
|
6342
|
+
* account to this fleet's CLI. Telegram gets an HTML spoiler (same treatment
|
|
6343
|
+
* as the dashboard token); other adapters get plain text with the warning.
|
|
6344
|
+
*/
|
|
6345
|
+
async sendLoginSecret(chat, backend, url, code) {
|
|
6346
|
+
const codeLine = code ? `\n${t("login.auth_code", code)}` : "";
|
|
6347
|
+
try {
|
|
6348
|
+
if (chat.adapter.type === "telegram") {
|
|
6349
|
+
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
6350
|
+
await chat.adapter.sendText(chat.chatId, `${esc(t("login.auth_hint", backend))}\n<tg-spoiler>${esc(url)}${esc(codeLine)}</tg-spoiler>`, { threadId: chat.threadId, format: "html" });
|
|
6351
|
+
}
|
|
6352
|
+
else {
|
|
6353
|
+
await chat.adapter.sendText(chat.chatId, `${t("login.auth_hint", backend)}\n${url}${codeLine}`, { threadId: chat.threadId });
|
|
6354
|
+
}
|
|
6355
|
+
}
|
|
6356
|
+
catch (err) {
|
|
6357
|
+
this.logger.warn({ err: err.message, backend }, "Failed to deliver login URL");
|
|
6358
|
+
}
|
|
6359
|
+
}
|
|
6360
|
+
/**
|
|
6361
|
+
* After a successful login: wake the paused instances of that backend AND
|
|
6362
|
+
* restart the running ones — a live CLI holds the old token in memory and
|
|
6363
|
+
* only re-reads credentials on process start (a paused instance's CLI is
|
|
6364
|
+
* already dead, so waking it respawns with the new token for free).
|
|
6365
|
+
*/
|
|
6366
|
+
async recoverBackendInstances(backend) {
|
|
6367
|
+
const woken = [];
|
|
6368
|
+
const restarted = [];
|
|
6369
|
+
for (const [name, config] of Object.entries(this.fleetConfig?.instances ?? {})) {
|
|
6370
|
+
const effective = config.backend ?? this.fleetConfig?.defaults?.backend ?? "claude-code";
|
|
6371
|
+
if (effective !== backend)
|
|
6372
|
+
continue;
|
|
6373
|
+
const status = this.getInstanceStatus(name);
|
|
6374
|
+
try {
|
|
6375
|
+
if (status === "paused") {
|
|
6376
|
+
if (this.daemons.has(name))
|
|
6377
|
+
await this.lifecycle.wake(name, 30_000);
|
|
6378
|
+
else
|
|
6379
|
+
await this.startPersistedPausedInstance(name);
|
|
6380
|
+
woken.push(name);
|
|
6381
|
+
}
|
|
6382
|
+
else if (status === "running") {
|
|
6383
|
+
await this.restartSingleInstance(name);
|
|
6384
|
+
restarted.push(name);
|
|
6385
|
+
}
|
|
6386
|
+
}
|
|
6387
|
+
catch (err) {
|
|
6388
|
+
this.logger.warn({ err: err.message, name, status }, "Post-login recovery failed");
|
|
6389
|
+
}
|
|
6390
|
+
}
|
|
6391
|
+
return { woken, restarted };
|
|
6392
|
+
}
|
|
6393
|
+
/** Backend chooser button → start that backend's login session. */
|
|
6394
|
+
async handleLoginBackendSelect(data, callbackAdapterId, receivingAdapter) {
|
|
6395
|
+
const claimed = this.consumeNonceCallback(LOGIN_CALLBACK_PREFIX, /^login:([0-9a-f]+):([a-z][a-z-]*)$/, data, callbackAdapterId, receivingAdapter);
|
|
6396
|
+
if (claimed === null)
|
|
6397
|
+
return false;
|
|
6398
|
+
if (claimed === "consumed")
|
|
6399
|
+
return true;
|
|
6400
|
+
const { entry, action: backend } = claimed;
|
|
6401
|
+
await this.retireNonceButtons(entry, entry.messageId ?? data.messageId, t("login.starting_backend", backend));
|
|
6402
|
+
const text = await this.startLoginSession(backend, {
|
|
6403
|
+
adapter: entry.adapter,
|
|
6404
|
+
adapterId: entry.adapterId,
|
|
6405
|
+
chatId: entry.chatId,
|
|
6406
|
+
threadId: entry.threadId,
|
|
6407
|
+
});
|
|
6408
|
+
if (text)
|
|
6409
|
+
await entry.adapter.sendText(entry.chatId, text, { threadId: entry.threadId }).catch(() => { });
|
|
6410
|
+
return true;
|
|
6411
|
+
}
|
|
6412
|
+
/** Re-login confirmation (auth pre-check said credentials still work). */
|
|
6413
|
+
async handleLoginConfirm(data, callbackAdapterId, receivingAdapter) {
|
|
6414
|
+
const claimed = this.consumeNonceCallback(LOGIN_CONFIRM_CALLBACK_PREFIX, /^login-confirm:([0-9a-f]+):(go|cancel)$/, data, callbackAdapterId, receivingAdapter);
|
|
6415
|
+
if (claimed === null)
|
|
6416
|
+
return false;
|
|
6417
|
+
if (claimed === "consumed")
|
|
6418
|
+
return true;
|
|
6419
|
+
const { entry, action } = claimed;
|
|
6420
|
+
const backend = entry.instanceName;
|
|
6421
|
+
if (action === "cancel") {
|
|
6422
|
+
await this.retireNonceButtons(entry, entry.messageId ?? data.messageId, t("login.cancelled", backend));
|
|
6423
|
+
return true;
|
|
6424
|
+
}
|
|
6425
|
+
await this.retireNonceButtons(entry, entry.messageId ?? data.messageId, t("login.starting_backend", backend));
|
|
6426
|
+
const text = await this.startLoginSession(backend, {
|
|
6427
|
+
adapter: entry.adapter,
|
|
6428
|
+
adapterId: entry.adapterId,
|
|
6429
|
+
chatId: entry.chatId,
|
|
6430
|
+
threadId: entry.threadId,
|
|
6431
|
+
}, { skipAuthCheck: true });
|
|
6432
|
+
if (text)
|
|
6433
|
+
await entry.adapter.sendText(entry.chatId, text, { threadId: entry.threadId }).catch(() => { });
|
|
6434
|
+
return true;
|
|
6435
|
+
}
|
|
6436
|
+
/** Kiro provider button → drive the CLI's arrow-key selector. */
|
|
6437
|
+
async handleLoginMenuSelect(data, callbackAdapterId, receivingAdapter) {
|
|
6438
|
+
const claimed = this.consumeNonceCallback(LOGIN_MENU_CALLBACK_PREFIX, /^login-menu:([0-9a-f]+):(\d)$/, data, callbackAdapterId, receivingAdapter);
|
|
6439
|
+
if (claimed === null)
|
|
6440
|
+
return false;
|
|
6441
|
+
if (claimed === "consumed")
|
|
6442
|
+
return true;
|
|
6443
|
+
const { entry, action } = claimed;
|
|
6444
|
+
const index = Number(action);
|
|
6445
|
+
const label = this.activeLogin?.session.flow.menu?.options[index] ?? action;
|
|
6446
|
+
const ok = await this.activeLogin?.session.selectMenuOption(index) ?? false;
|
|
6447
|
+
await this.retireNonceButtons(entry, entry.messageId ?? data.messageId, ok ? t("login.provider_selected", label) : t("login.no_session"));
|
|
6448
|
+
return true;
|
|
6449
|
+
}
|
|
6178
6450
|
queueMirrorMessage(text) {
|
|
6179
6451
|
const mirrorTopicId = this.fleetConfig?.channel?.mirror_topic_id;
|
|
6180
6452
|
if (mirrorTopicId == null || !this.adapter)
|