@songsid/agend 2.3.2-beta.4 → 2.3.2-beta.5
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/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/antigravity-usage-scan.d.ts +58 -0
- package/dist/antigravity-usage-scan.js +257 -0
- package/dist/antigravity-usage-scan.js.map +1 -0
- package/dist/antigravity-usage-worker.d.ts +1 -0
- package/dist/antigravity-usage-worker.js +10 -0
- package/dist/antigravity-usage-worker.js.map +1 -0
- package/dist/backend/kiro-identity.d.ts +45 -0
- package/dist/backend/kiro-identity.js +74 -0
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +15 -0
- package/dist/backend/kiro.js +42 -5
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +6 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-signin-guide.d.ts +1 -0
- package/dist/backend-signin-guide.js +9 -0
- package/dist/backend-signin-guide.js.map +1 -0
- package/dist/backend-signin-pending.d.ts +10 -0
- package/dist/backend-signin-pending.js +44 -0
- package/dist/backend-signin-pending.js.map +1 -0
- package/dist/browser-backend-signins.d.ts +56 -0
- package/dist/browser-backend-signins.js +189 -0
- package/dist/browser-backend-signins.js.map +1 -0
- package/dist/cache-ledger.d.ts +38 -2
- package/dist/cache-ledger.js +133 -8
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-prices.d.ts +2 -1
- package/dist/cache-prices.js +5 -2
- package/dist/cache-prices.js.map +1 -1
- package/dist/cache-service.d.ts +68 -0
- package/dist/cache-service.js +355 -13
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/mcp-tools.js +3 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +21 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.js +25 -0
- package/dist/config.js.map +1 -1
- package/dist/cpu-quota.d.ts +23 -0
- package/dist/cpu-quota.js +86 -0
- package/dist/cpu-quota.js.map +1 -0
- package/dist/daemon.js +6 -3
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +35 -0
- package/dist/fleet-manager.js +233 -11
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +5 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +14 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +6 -3
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +6 -3
- package/dist/instance-lifecycle.d.ts +9 -0
- package/dist/instance-lifecycle.js +27 -1
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/kiro-db-reader.d.ts +5 -0
- package/dist/kiro-db-reader.js +18 -0
- package/dist/kiro-db-reader.js.map +1 -1
- package/dist/kiro-engine-status.d.ts +2 -0
- package/dist/kiro-engine-status.js +9 -0
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-migrate.d.ts +49 -0
- package/dist/kiro-migrate.js +105 -0
- package/dist/kiro-migrate.js.map +1 -0
- package/dist/kiro-transcript-lane.d.ts +2 -0
- package/dist/kiro-transcript-lane.js.map +1 -1
- package/dist/kiro-transcript-worker.js +1 -0
- package/dist/kiro-transcript-worker.js.map +1 -1
- package/dist/kiro-usage-run.d.ts +7 -0
- package/dist/kiro-usage-run.js +39 -0
- package/dist/kiro-usage-run.js.map +1 -0
- package/dist/kiro-usage-scan.d.ts +49 -0
- package/dist/kiro-usage-scan.js +223 -0
- package/dist/kiro-usage-scan.js.map +1 -0
- package/dist/kiro-usage-worker.d.ts +1 -0
- package/dist/kiro-usage-worker.js +10 -0
- package/dist/kiro-usage-worker.js.map +1 -0
- package/dist/kiro-usage.d.ts +58 -0
- package/dist/kiro-usage.js +310 -0
- package/dist/kiro-usage.js.map +1 -0
- package/dist/locale.js +70 -2
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/login-url.d.ts +4 -0
- package/dist/login-url.js +22 -0
- package/dist/login-url.js.map +1 -0
- package/dist/needs-you-hub.d.ts +2 -1
- package/dist/needs-you-hub.js +1 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +8 -2
- package/dist/needs-you.js +6 -0
- package/dist/needs-you.js.map +1 -1
- package/dist/opencode-usage-scan.d.ts +28 -0
- package/dist/opencode-usage-scan.js +92 -0
- package/dist/opencode-usage-scan.js.map +1 -0
- package/dist/opencode-usage-worker.d.ts +1 -0
- package/dist/opencode-usage-worker.js +10 -0
- package/dist/opencode-usage-worker.js.map +1 -0
- package/dist/outbound-handlers.js +22 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +4 -0
- package/dist/outbound-schemas.js +4 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/quickstart-api.js +13 -2
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.js +2 -1
- package/dist/quickstart.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +7 -1
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-host.d.ts +9 -0
- package/dist/setup-host.js +124 -5
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-signin-assets.d.ts +2 -0
- package/dist/setup-signin-assets.js +6 -0
- package/dist/setup-signin-assets.js.map +1 -0
- package/dist/setup-wizard.js +2 -0
- package/dist/setup-wizard.js.map +1 -1
- package/dist/structured-signin-transport.d.ts +8 -0
- package/dist/structured-signin-transport.js +79 -0
- package/dist/structured-signin-transport.js.map +1 -0
- package/dist/structured-signin.d.ts +131 -0
- package/dist/structured-signin.js +388 -0
- package/dist/structured-signin.js.map +1 -0
- package/dist/tool-permissions.js +2 -0
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.js +4 -1
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-sources.d.ts +26 -2
- package/dist/transcript-sources.js +87 -40
- package/dist/transcript-sources.js.map +1 -1
- package/dist/types.d.ts +3 -2
- package/dist/ui/backend-signin.js +63 -10
- package/dist/ui/fleet-usage.js +19 -15
- package/dist/ui/panel-log.js +2 -2
- package/dist/ui/panel-needs.js +4 -4
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app.css +1 -0
- package/dist/ui/structured-signin.js +115 -0
- package/dist/usage-report.d.ts +20 -0
- package/dist/usage-report.js +54 -17
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.d.ts +9 -1
- package/dist/usage-rollup.js +20 -4
- package/dist/usage-rollup.js.map +1 -1
- package/dist/web-api.d.ts +1 -0
- package/dist/web-api.js +99 -0
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +1 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.d.ts +1 -7
- package/dist/web-backend-login.js +2 -24
- package/dist/web-backend-login.js.map +1 -1
- package/package.json +2 -1
- package/templates/windows-shortcuts.ps1 +7 -5
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -85,6 +85,7 @@ import { type CrossInstanceRecord, type XmsgVisibility } from "./search/search-d
|
|
|
85
85
|
import { WebSettingsTools } from "./web-settings-tools.js";
|
|
86
86
|
import { type Delegation, type TrafficItem } from "./fleet-traffic.js";
|
|
87
87
|
import { WebBackendLogins, type SignInTarget } from "./web-backend-login.js";
|
|
88
|
+
import { BrowserBackendSignIns } from "./browser-backend-signins.js";
|
|
88
89
|
import { type RawConfigPatch } from "./settings-api.js";
|
|
89
90
|
import { type AgentEndpointContext } from "./agent-endpoint.js";
|
|
90
91
|
import { ClassicChannelManager } from "./classic-channel-manager.js";
|
|
@@ -246,6 +247,7 @@ export declare const CLI_ENV_PROBE_DEADLINE_MS = 16000;
|
|
|
246
247
|
* started ten CLIs together, saturated the CPU, and healthy starts then missed
|
|
247
248
|
* their startup budget — which used to cost the user their conversation. Cores
|
|
248
249
|
* bound how many can actually make progress; memory bounds how many fit.
|
|
250
|
+
* `cores` is what this process may use (cpuset and cgroup quota, #749), not the host's count.
|
|
249
251
|
*/
|
|
250
252
|
export declare function deriveSpawnConcurrency(freeMemMB: number, cores: number): number;
|
|
251
253
|
/** #2499 review: one line of Settings → Guests' Recent changes (guestOverview), from the event log. */
|
|
@@ -454,6 +456,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
454
456
|
private pendingBackendSelects;
|
|
455
457
|
/** nonce → pending button prompt (hang restart, interactive assist, clean-exit restart). */
|
|
456
458
|
private pendingNonceButtons;
|
|
459
|
+
/** #2598 B2: the kiro moves waiting on a fleet admin's Confirm in General, by prompt nonce. */
|
|
460
|
+
private pendingKiroMigrations;
|
|
457
461
|
/** #1386 "Needs you": the list, its live chat messages and Acknowledge. Started with the fleet (finishStartup). */
|
|
458
462
|
private needsYou;
|
|
459
463
|
/** When the fleet first saw an instance crashed, for its "Needs you" age; cleared when it is not. */
|
|
@@ -764,6 +768,21 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
764
768
|
/** Exact configured effect owners; this does not depend on prompt location. */
|
|
765
769
|
private settingsChatAuthority;
|
|
766
770
|
private promptSettingsChange;
|
|
771
|
+
private kiroMigrateDeps;
|
|
772
|
+
/**
|
|
773
|
+
* #2598 B2, migrate_kiro_instance: judge the move, then ask in General for a fleet admin's Confirm. Nothing changes
|
|
774
|
+
* here; the move runs on the Confirm (handleKiroMigrateCallback), judged again then.
|
|
775
|
+
*/
|
|
776
|
+
requestKiroMigration(name: string, startFresh: boolean): Promise<{
|
|
777
|
+
ok: true;
|
|
778
|
+
text: string;
|
|
779
|
+
classic_conversation: string | null;
|
|
780
|
+
} | {
|
|
781
|
+
ok: false;
|
|
782
|
+
error: string;
|
|
783
|
+
}>;
|
|
784
|
+
/** A fleet admin's Confirm or Cancel on a kiro move. Any fleet admin (the default nonce authorization), in General. */
|
|
785
|
+
private handleKiroMigrateCallback;
|
|
767
786
|
private handleSettingsChangeCallback;
|
|
768
787
|
startSettingsConfirmationControl(): Promise<void>;
|
|
769
788
|
private get webToken();
|
|
@@ -2410,6 +2429,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
2410
2429
|
* non-identity instance leaves are canonicalized to inheritance afterward.
|
|
2411
2430
|
*/
|
|
2412
2431
|
saveFleetConfig(explicitPatches?: RawConfigPatch[]): void;
|
|
2432
|
+
/** A permission tightening is effective when its save returns, independently of the later apply job or IPC. */
|
|
2433
|
+
private refreshSavedDashboardPermissions;
|
|
2413
2434
|
/** Why the config written to `candidate` must not replace fleet.yaml, or null: it does not load, or it adds errors. */
|
|
2414
2435
|
private savedFleetConfigProblem;
|
|
2415
2436
|
/**
|
|
@@ -2890,6 +2911,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
2890
2911
|
* without one); codex "unrecorded" (its rollouts don't say which login wrote them).
|
|
2891
2912
|
*/
|
|
2892
2913
|
private usageProfileOf;
|
|
2914
|
+
/** #2122 §10: a Kiro instance with a credential profile writes its classic store in that profile's home. */
|
|
2915
|
+
private kiroDbPathOf;
|
|
2893
2916
|
/** #2122: the fleet's day boundary zone — defaults.scheduler.default_timezone, else this host's. */
|
|
2894
2917
|
usageZone(): string;
|
|
2895
2918
|
/**
|
|
@@ -3370,6 +3393,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
3370
3393
|
*/
|
|
3371
3394
|
private readonly loginWindow;
|
|
3372
3395
|
private get webLogin();
|
|
3396
|
+
private browserBackendSignInsInstance;
|
|
3397
|
+
private structuredLoginCleanupFailed;
|
|
3398
|
+
private readonly pendingBackendSince;
|
|
3399
|
+
private browserSignInOwnerCurrent;
|
|
3400
|
+
/** #2599: default-account controls share the exact writer reservation with chat/profile/install. */
|
|
3401
|
+
get browserBackendSignIns(): BrowserBackendSignIns;
|
|
3402
|
+
private needsYouBackendSignIns;
|
|
3373
3403
|
/** #2233: sign-ins started from the web dashboard (local admin sessions), on the same controller as chat's. */
|
|
3374
3404
|
private webBackendLoginsInstance;
|
|
3375
3405
|
get webBackendLogins(): WebBackendLogins;
|
|
@@ -3663,6 +3693,11 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
3663
3693
|
private cliEnvPath;
|
|
3664
3694
|
/** Read the cached CLI env for a backend (a credential profile's own, when given), or null if missing/stale/unparseable. */
|
|
3665
3695
|
private readCliEnv;
|
|
3696
|
+
/**
|
|
3697
|
+
* #2122 §10: the version the CLI probe last recorded for a backend — its cache file read whatever its age (the usage
|
|
3698
|
+
* reader's version gate needs the last known version, not a fresh one); null when none was recorded. Never a probe.
|
|
3699
|
+
*/
|
|
3700
|
+
private lastRecordedCliVersion;
|
|
3666
3701
|
/** Return cached CLI versions immediately. Refresh is started after /sysinfo is sent. */
|
|
3667
3702
|
getBackendCliVersionSnapshot(): BackendCliVersionSnapshot;
|
|
3668
3703
|
/** Called only after the user-visible sysinfo send/respond has settled. */
|
package/dist/fleet-manager.js
CHANGED
|
@@ -31,13 +31,16 @@ import { ProfileControlServer } from "./profile-control.js";
|
|
|
31
31
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, rmSync, readdirSync, renameSync, copyFileSync, chmodSync, statSync, accessSync, realpathSync, lstatSync, constants as fsConstants, closeSync } from "node:fs";
|
|
32
32
|
import { atomicWriteFileSync } from "./atomic-write.js";
|
|
33
33
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
34
|
-
import { freemem, totalmem,
|
|
34
|
+
import { freemem, totalmem, homedir } from "node:os";
|
|
35
|
+
import { effectiveCpuCount } from "./cpu-quota.js";
|
|
35
36
|
import { createServer } from "node:http";
|
|
36
37
|
import { Worker } from "node:worker_threads";
|
|
37
38
|
import { ProbeWorkerPool } from "./probe-worker-pool.js";
|
|
38
39
|
import { join, dirname, basename, delimiter, isAbsolute, relative, resolve as resolvePath } from "node:path";
|
|
39
40
|
import { fileURLToPath } from "node:url";
|
|
41
|
+
import { isDeepStrictEqual } from "node:util";
|
|
40
42
|
import { getAgendHome, ensureWorkspaceGit } from "./paths.js";
|
|
43
|
+
import { applyKiroMigration, kiroMigrationPrompt, planKiroMigration } from "./kiro-migrate.js";
|
|
41
44
|
import { fleetLabel } from "./fleet-label.js";
|
|
42
45
|
import { beginFullRestartProgress as persistFullRestartProgress, beginUpdateProgress as persistUpdateProgress, clearUpdateMarker, isUpdateInProgress, readUpdateProgress, sameUpdateMarker, setUpdateProgressStage, updateMarkerIdentity, updateProgressOperation, updateReceiptOf, } from "./update-marker.js";
|
|
43
46
|
import { formatUpdateProgress } from "./update-progress.js";
|
|
@@ -56,7 +59,7 @@ const __dirname = dirname(__filename);
|
|
|
56
59
|
const DEFAULT_OPEN_ACCESS = { mode: "open", allowed_users: [], max_pending_codes: 0, code_expiry_minutes: 0 };
|
|
57
60
|
import { STATUS_EMOJI_CONFIG_KEYS, STATUS_EMOJI_KEYS, STATUS_EMOJI_SUGGESTIONS, TELEGRAM_REACTION_EMOJIS, builtinStatusEmojis, customEmojiValue, emojiImageUrl, normalizeEmoji, previewStatusEmojis, reactionForm, reactionMatchKey, resolveStatusEmojis, statusAvoidList, statusEmojiProblem, statusMatchKey, statusMatchKeys, textForm, } from "./status-emojis.js";
|
|
58
61
|
import { isProbeableRouteTarget } from "./fleet-context.js";
|
|
59
|
-
import { getEffectiveInstanceDefaults, loadFleetConfig, loadRawFleetConfig, DEFAULT_COST_GUARD, DEFAULT_DAILY_SUMMARY, DEFAULT_INSTANCE_CONFIG } from "./config.js";
|
|
62
|
+
import { getEffectiveInstanceDefaults, loadFleetConfig, loadRawFleetConfig, DEFAULT_COST_GUARD, DEFAULT_DAILY_SUMMARY, DEFAULT_INSTANCE_CONFIG, autoGeneralInstanceConfig } from "./config.js";
|
|
60
63
|
import { EventLog } from "./event-log.js";
|
|
61
64
|
import { binaryProbe } from "./binary-probe.js";
|
|
62
65
|
import { AVAILABILITY_STALE_MS, BackendAvailabilityStore, hostAvailabilityProbes } from "./backend-availability.js";
|
|
@@ -193,7 +196,7 @@ import { discordGeneralChannelId } from "./discord-general.js";
|
|
|
193
196
|
import { parseEnvText } from "./env-file.js";
|
|
194
197
|
import { isDisallowedIntentsError, isRejectedTokenError } from "./discord-permissions.js";
|
|
195
198
|
import { filterUsageProviders, formatDiscordUsageActivity, getUsageSnapshot, handleUsageRequest, isUsagePath, usageProviderIdForBackend } from "./usage/usage-api.js";
|
|
196
|
-
import { LOGIN_FLOWS, LOGIN_BACKEND_ALIASES } from "./login-flows.js";
|
|
199
|
+
import { LOGIN_FLOWS, checkAuthStatus, LOGIN_BACKEND_ALIASES } from "./login-flows.js";
|
|
197
200
|
import { LoginSession } from "./login-manager.js";
|
|
198
201
|
import { runOnLoginShell } from "./login-shell.js";
|
|
199
202
|
import { tightenInstanceDirs } from "./private-dir.js";
|
|
@@ -213,6 +216,10 @@ import { admitProfileLogin, LoginController, LOGIN_TOKEN_RESEND_PREFIX, POST_LOG
|
|
|
213
216
|
import { WebBackendLogins, dashboardSignInSupported } from "./web-backend-login.js";
|
|
214
217
|
import { runBeforeDeadline } from "./deadline.js";
|
|
215
218
|
import { LoginWindowLock } from "./login-window-lock.js";
|
|
219
|
+
import { BrowserBackendSignIns } from "./browser-backend-signins.js";
|
|
220
|
+
import { createStructuredSignInTransport } from "./structured-signin-transport.js";
|
|
221
|
+
import { ONBOARDING_BACKENDS, readPendingBackendSignIns } from "./backend-signin-pending.js";
|
|
222
|
+
import { structuredSignInSupported } from "./structured-signin.js";
|
|
216
223
|
import { handleSettingsRequest } from "./settings-api.js";
|
|
217
224
|
import { setLocale, detectLocale, getLocale, t } from "./locale.js";
|
|
218
225
|
import { recentChatContext } from "./log-tail.js";
|
|
@@ -276,6 +283,7 @@ import { PROVIDER_SECRET_SPECS, PUBLIC_TUNNEL_SECRET_SPEC, providerSecretSpec, p
|
|
|
276
283
|
/** A self-restart is a whole service restart; 120s is the apply budget, not this. */
|
|
277
284
|
const SELF_RESTART_DEADLINE_MS = 300_000;
|
|
278
285
|
import { APPLY_FLEET_TARGET, ApplyJobStore, viewOf, } from "./apply-job.js";
|
|
286
|
+
import { kiroHome } from "./backend/kiro-kas-store.js";
|
|
279
287
|
import { CHAT_PROFILE_LOGIN_BACKENDS, credentialHomeSpec, credentialProfileHome, credentialProfileLogin, credentialProfileLoginCommand, credentialProfileStoreHome, credentialSwitchCarriesConversation, credentialSwitchStartsFresh, instanceCredentialProfile } from "./backend/credential-profile.js";
|
|
280
288
|
import { carryClaudeConversation } from "./backend/claude-carry.js";
|
|
281
289
|
import { mergeBackendOptions, profileKey, profileName, profileOf } from "./classic-bindings.js";
|
|
@@ -473,6 +481,10 @@ const OOM_RETRY_CALLBACK_PREFIX = "oom-retry:";
|
|
|
473
481
|
const NEEDS_PAUSE_TTL_MS = 10_000;
|
|
474
482
|
const HANG_CALLBACK_PREFIX = "hang:";
|
|
475
483
|
const CLEAR_CONFIRM_CALLBACK_PREFIX = "clear-confirm:";
|
|
484
|
+
/** #2598 B2: a kiro classic → TUI move's Confirm in General. "kiro-migrate:" (13) + 32 hex + ":confirm" (8) = 53 bytes. */
|
|
485
|
+
const KIRO_MIGRATE_CALLBACK_PREFIX = "kiro-migrate:";
|
|
486
|
+
/** Long enough for an admin to read the prompt; the move is judged again on the click. */
|
|
487
|
+
const KIRO_MIGRATE_TIMEOUT_MS = 10 * 60_000;
|
|
476
488
|
/**
|
|
477
489
|
* The prompts the web dashboard also offers: the ones about an instance's own health, which a dashboard
|
|
478
490
|
* user — holding the full-fleet web credential — may answer exactly as a fleet admin on the platform may.
|
|
@@ -568,6 +580,7 @@ export const CLI_ENV_PROBE_DEADLINE_MS = 16_000;
|
|
|
568
580
|
* started ten CLIs together, saturated the CPU, and healthy starts then missed
|
|
569
581
|
* their startup budget — which used to cost the user their conversation. Cores
|
|
570
582
|
* bound how many can actually make progress; memory bounds how many fit.
|
|
583
|
+
* `cores` is what this process may use (cpuset and cgroup quota, #749), not the host's count.
|
|
571
584
|
*/
|
|
572
585
|
export function deriveSpawnConcurrency(freeMemMB, cores) {
|
|
573
586
|
const byMemory = Math.floor(freeMemMB / 300);
|
|
@@ -885,6 +898,8 @@ export class FleetManager {
|
|
|
885
898
|
pendingBackendSelects = new Map();
|
|
886
899
|
/** nonce → pending button prompt (hang restart, interactive assist, clean-exit restart). */
|
|
887
900
|
pendingNonceButtons = new Map();
|
|
901
|
+
/** #2598 B2: the kiro moves waiting on a fleet admin's Confirm in General, by prompt nonce. */
|
|
902
|
+
pendingKiroMigrations = new Map();
|
|
888
903
|
/** #1386 "Needs you": the list, its live chat messages and Acknowledge. Started with the fleet (finishStartup). */
|
|
889
904
|
needsYou = null;
|
|
890
905
|
/** When the fleet first saw an instance crashed, for its "Needs you" age; cleared when it is not. */
|
|
@@ -1723,6 +1738,74 @@ export class FleetManager {
|
|
|
1723
1738
|
// No platform confirmation was reachable. Host inspection is still required.
|
|
1724
1739
|
this.logger.info({ id: view.id }, "Settings change requires local `agend settings confirm <id>`");
|
|
1725
1740
|
}
|
|
1741
|
+
kiroMigrateDeps() {
|
|
1742
|
+
return {
|
|
1743
|
+
agendHome: getAgendHome(),
|
|
1744
|
+
fleetConfig: () => this.fleetConfig,
|
|
1745
|
+
saveFleetConfig: () => this.saveFleetConfig(),
|
|
1746
|
+
status: name => this.getInstanceStatus(name),
|
|
1747
|
+
restart: name => this.restartSingleInstance(name, { explicit: true }),
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* #2598 B2, migrate_kiro_instance: judge the move, then ask in General for a fleet admin's Confirm. Nothing changes
|
|
1752
|
+
* here; the move runs on the Confirm (handleKiroMigrateCallback), judged again then.
|
|
1753
|
+
*/
|
|
1754
|
+
async requestKiroMigration(name, startFresh) {
|
|
1755
|
+
const judged = planKiroMigration(this.kiroMigrateDeps(), name, startFresh);
|
|
1756
|
+
if (!judged.ok)
|
|
1757
|
+
return judged;
|
|
1758
|
+
const general = this.findGeneralInstance();
|
|
1759
|
+
const adapterId = general ? this.getInstanceAdapterId(general) : undefined;
|
|
1760
|
+
const adapter = general ? this.getAdapterForInstance(general) : undefined;
|
|
1761
|
+
const chatId = general ? this.getGroupIdForInstance(general) : undefined;
|
|
1762
|
+
const topic = general ? String(this.fleetConfig?.instances[general]?.topic_id ?? "") : "";
|
|
1763
|
+
if (!general || !adapterId || !adapter || !chatId)
|
|
1764
|
+
return { ok: false, error: t("kiro_migrate.no_general") };
|
|
1765
|
+
const { plan } = judged;
|
|
1766
|
+
try {
|
|
1767
|
+
const nonce = await this.postNonceButtonPromptOrThrow({
|
|
1768
|
+
prefix: KIRO_MIGRATE_CALLBACK_PREFIX, alertType: "clear_confirm", instanceName: name, adapter, adapterId, chatId,
|
|
1769
|
+
...(topic ? { threadId: topic } : {}),
|
|
1770
|
+
message: kiroMigrationPrompt(plan, this.getInstanceStatus(name) === "running"),
|
|
1771
|
+
choices: [{ action: "confirm", label: t("kiro_migrate.confirm_button") }, { action: "cancel", label: t("kiro_migrate.cancel_button") }],
|
|
1772
|
+
expiredText: t("kiro_migrate.expired", name),
|
|
1773
|
+
timeoutMs: KIRO_MIGRATE_TIMEOUT_MS,
|
|
1774
|
+
});
|
|
1775
|
+
this.pendingKiroMigrations.set(nonce, plan);
|
|
1776
|
+
const expiry = setTimeout(() => this.pendingKiroMigrations.delete(nonce), KIRO_MIGRATE_TIMEOUT_MS + 60_000);
|
|
1777
|
+
expiry.unref?.();
|
|
1778
|
+
}
|
|
1779
|
+
catch (err) {
|
|
1780
|
+
return { ok: false, error: `${t("kiro_migrate.no_general")} (${err.message})` };
|
|
1781
|
+
}
|
|
1782
|
+
return { ok: true, text: t("kiro_migrate.requested"), classic_conversation: plan.classicId };
|
|
1783
|
+
}
|
|
1784
|
+
/** A fleet admin's Confirm or Cancel on a kiro move. Any fleet admin (the default nonce authorization), in General. */
|
|
1785
|
+
async handleKiroMigrateCallback(data, adapterId, adapter) {
|
|
1786
|
+
const claimed = this.consumeNonceCallback(KIRO_MIGRATE_CALLBACK_PREFIX, /^kiro-migrate:([0-9a-f]{32}):(confirm|cancel)$/, data, adapterId, adapter);
|
|
1787
|
+
if (claimed === null)
|
|
1788
|
+
return false;
|
|
1789
|
+
if (claimed === "consumed")
|
|
1790
|
+
return true;
|
|
1791
|
+
const { entry, action } = claimed;
|
|
1792
|
+
const nonce = data.callbackData.slice(KIRO_MIGRATE_CALLBACK_PREFIX.length, KIRO_MIGRATE_CALLBACK_PREFIX.length + 32);
|
|
1793
|
+
const plan = this.pendingKiroMigrations.get(nonce);
|
|
1794
|
+
this.pendingKiroMigrations.delete(nonce);
|
|
1795
|
+
const messageId = entry.messageId ?? data.messageId;
|
|
1796
|
+
if (action === "cancel" || !plan) {
|
|
1797
|
+
await this.retireNonceButtons(entry, messageId, t(plan ? "kiro_migrate.cancelled" : "kiro_migrate.expired", entry.instanceName));
|
|
1798
|
+
return true;
|
|
1799
|
+
}
|
|
1800
|
+
this.eventLog?.insert(plan.name, "kiro_migrate", { classicId: plan.classicId, userId: data.userId });
|
|
1801
|
+
await this.retireNonceButtons(entry, messageId, t("kiro_migrate.applying", plan.name));
|
|
1802
|
+
const outcome = await applyKiroMigration(this.kiroMigrateDeps(), plan);
|
|
1803
|
+
this.logger.info({ name: plan.name, ok: outcome.ok, classicId: plan.classicId }, "kiro classic → TUI move");
|
|
1804
|
+
this.notifyFleetError(outcome.text);
|
|
1805
|
+
if (outcome.ok)
|
|
1806
|
+
this.notifyInstanceTopic(plan.name, outcome.text);
|
|
1807
|
+
return true;
|
|
1808
|
+
}
|
|
1726
1809
|
async handleSettingsChangeCallback(data, adapterId, adapter) {
|
|
1727
1810
|
const result = this.consumeNonceCallback("settings-confirm:", /^settings-confirm:([0-9a-f]{32}):(confirm|reject)$/, data, adapterId, adapter, undefined, (userId, entry) => entry.confirmationAdmin?.(userId) === true);
|
|
1728
1811
|
if (!result)
|
|
@@ -2576,7 +2659,7 @@ export class FleetManager {
|
|
|
2576
2659
|
const explicit = this.fleetConfig?.defaults?.startup?.concurrency;
|
|
2577
2660
|
if (explicit != null)
|
|
2578
2661
|
return Math.max(1, Math.min(20, explicit));
|
|
2579
|
-
return deriveSpawnConcurrency(Math.round(freemem() / (1024 * 1024)),
|
|
2662
|
+
return deriveSpawnConcurrency(Math.round(freemem() / (1024 * 1024)), effectiveCpuCount());
|
|
2580
2663
|
}
|
|
2581
2664
|
/** #1685: "elevated" or "critical" while host memory pressure slows or holds new agents; otherwise null (an
|
|
2582
2665
|
* unknown macOS reading — the advisory case — is unknown, so null too). */
|
|
@@ -7774,7 +7857,8 @@ export class FleetManager {
|
|
|
7774
7857
|
}
|
|
7775
7858
|
}
|
|
7776
7859
|
async startAllWithinDeadline(configPath, deadline) {
|
|
7777
|
-
this.
|
|
7860
|
+
if (!this.structuredLoginCleanupFailed)
|
|
7861
|
+
this.loginWindow.reopen(); // a stopAll → startAll restart must accept login windows again
|
|
7778
7862
|
this.loginController?.reopen();
|
|
7779
7863
|
const startupStartedAt = Date.now();
|
|
7780
7864
|
FleetManager.signalTarget = this;
|
|
@@ -8035,12 +8119,7 @@ export class FleetManager {
|
|
|
8035
8119
|
mkdirSync(generalDir, { recursive: true, mode: 0o700 });
|
|
8036
8120
|
const backendName = fleet.defaults.backend ?? "claude-code";
|
|
8037
8121
|
this.ensureGeneralInstructions(generalDir, backendName, name);
|
|
8038
|
-
fleet.instances[name] =
|
|
8039
|
-
...DEFAULT_INSTANCE_CONFIG,
|
|
8040
|
-
working_directory: generalDir,
|
|
8041
|
-
general_topic: true,
|
|
8042
|
-
channel_id: need.adapterId,
|
|
8043
|
-
};
|
|
8122
|
+
fleet.instances[name] = autoGeneralInstanceConfig(fleet.defaults, generalDir, need.adapterId); // #2598: its kiro_ui is the fleet's
|
|
8044
8123
|
generalsCreated = true;
|
|
8045
8124
|
}
|
|
8046
8125
|
if (generalsCreated)
|
|
@@ -12251,9 +12330,46 @@ export class FleetManager {
|
|
|
12251
12330
|
},
|
|
12252
12331
|
});
|
|
12253
12332
|
this.rawFleetConfig = loadRawFleetConfig(this.configPath);
|
|
12333
|
+
// The instance map is defaults-expanded. A defaults edit changes that view even when it writes no instance
|
|
12334
|
+
// leaves. Re-resolve from the committed YAML so inherited values cannot become stale overrides on the next save.
|
|
12335
|
+
// Keep the FleetConfig object itself: Settings and other request contexts hold it.
|
|
12336
|
+
const resolvedInstances = loadFleetConfig(this.configPath).instances;
|
|
12337
|
+
for (const [name, resolved] of Object.entries(resolvedInstances)) {
|
|
12338
|
+
const current = this.fleetConfig.instances[name];
|
|
12339
|
+
if (!current) {
|
|
12340
|
+
this.fleetConfig.instances[name] = resolved;
|
|
12341
|
+
continue;
|
|
12342
|
+
}
|
|
12343
|
+
// Lifecycle rollback fences hold these object identities across save. Refresh their values, not their owner.
|
|
12344
|
+
for (const key of Object.keys(current))
|
|
12345
|
+
if (!Object.hasOwn(resolved, key))
|
|
12346
|
+
delete current[key];
|
|
12347
|
+
Object.assign(current, resolved);
|
|
12348
|
+
}
|
|
12254
12349
|
this.savedFleetConfigSnapshot = structuredClone(this.fleetConfig);
|
|
12350
|
+
this.refreshSavedDashboardPermissions();
|
|
12255
12351
|
this.logger.info({ path: this.configPath, strippedDefaults: redundantPaths.length }, "Saved fleet config (lossless patch)");
|
|
12256
12352
|
}
|
|
12353
|
+
/** A permission tightening is effective when its save returns, independently of the later apply job or IPC. */
|
|
12354
|
+
refreshSavedDashboardPermissions() {
|
|
12355
|
+
if (!this.fleetConfig)
|
|
12356
|
+
return;
|
|
12357
|
+
const classicNames = new Set(this.classicChannels?.getAll().map(ch => ch.instanceName) ?? []);
|
|
12358
|
+
for (const [name, daemon] of this.daemons) {
|
|
12359
|
+
const next = this.fleetConfig.instances[name] ?? (classicNames.has(name) ? getEffectiveInstanceDefaults(this.fleetConfig.defaults) : undefined);
|
|
12360
|
+
const previous = daemon.getConfigSnapshot?.();
|
|
12361
|
+
if (!next || !previous)
|
|
12362
|
+
continue;
|
|
12363
|
+
if (isDeepStrictEqual(previous.dashboard, next.dashboard) && previous.office_access === next.office_access)
|
|
12364
|
+
continue;
|
|
12365
|
+
// Only permissions are adopted here. Cold fields retain the live daemon's old snapshot, so reconcile still
|
|
12366
|
+
// detects that they require a restart. MCP menus also watch the raw file; this updates the next spawn's env.
|
|
12367
|
+
daemon.applyConfigUpdate({ dashboard: next.dashboard ?? null, office_access: next.office_access ?? null });
|
|
12368
|
+
if (taskBoardSwitchedOn(previous.dashboard, next.dashboard)) {
|
|
12369
|
+
this.admitTaskBoardNotice(name, TASK_BOARD_ON_NOTICE, `task-board-on:${name}:${Date.now()}`);
|
|
12370
|
+
}
|
|
12371
|
+
}
|
|
12372
|
+
}
|
|
12257
12373
|
/** Why the config written to `candidate` must not replace fleet.yaml, or null: it does not load, or it adds errors. */
|
|
12258
12374
|
savedFleetConfigProblem(candidate) {
|
|
12259
12375
|
let after;
|
|
@@ -14357,6 +14473,8 @@ export class FleetManager {
|
|
|
14357
14473
|
return true;
|
|
14358
14474
|
if (await this.handleSettingsChangeCallback(data, adapterId, adapter))
|
|
14359
14475
|
return true;
|
|
14476
|
+
if (await this.handleKiroMigrateCallback(data, adapterId, adapter))
|
|
14477
|
+
return true;
|
|
14360
14478
|
if (await this.handleDashboardCallback(data, adapterId, adapter))
|
|
14361
14479
|
return true;
|
|
14362
14480
|
if (await this.handleClassicApproval(data, adapterId, adapter))
|
|
@@ -14477,6 +14595,7 @@ export class FleetManager {
|
|
|
14477
14595
|
const fleet = Object.entries(this.fleetConfig?.instances ?? {}).map(([name, cfg]) => ({
|
|
14478
14596
|
name, backend: this.backendNameOf(name), workingDirectory: cfg.working_directory, ledgerPath: join(this.getInstanceDir(name), "cache-ledger.json"),
|
|
14479
14597
|
claudeConfigDir: this.claudeConfigDirOf(name), profile: this.usageProfileOf(name, this.backendNameOf(name)),
|
|
14598
|
+
kiroDbPath: this.kiroDbPathOf(name, this.backendNameOf(name)),
|
|
14480
14599
|
}));
|
|
14481
14600
|
const taken = new Set(fleet.map(i => i.name));
|
|
14482
14601
|
const classic = (this.classicChannels?.getAll() ?? []).filter(ch => !taken.has(ch.instanceName)).map(ch => ({
|
|
@@ -14486,6 +14605,7 @@ export class FleetManager {
|
|
|
14486
14605
|
ledgerPath: join(this.getInstanceDir(ch.instanceName), "cache-ledger.json"),
|
|
14487
14606
|
claudeConfigDir: this.claudeConfigDirOf(ch.instanceName),
|
|
14488
14607
|
profile: this.usageProfileOf(ch.instanceName, this.classicChannels?.getBackendByInstance(ch.instanceName, this.fleetConfig?.defaults?.backend) ?? this.fleetConfig?.defaults?.backend ?? "claude-code"),
|
|
14608
|
+
kiroDbPath: this.kiroDbPathOf(ch.instanceName, this.classicChannels?.getBackendByInstance(ch.instanceName, this.fleetConfig?.defaults?.backend) ?? this.fleetConfig?.defaults?.backend ?? "claude-code"),
|
|
14489
14609
|
}));
|
|
14490
14610
|
return [...fleet, ...new Map(classic.map(c => [c.name, c])).values()];
|
|
14491
14611
|
}
|
|
@@ -14506,8 +14626,19 @@ export class FleetManager {
|
|
|
14506
14626
|
return this.credentialProfileOf(name, "claude-code") ?? "default";
|
|
14507
14627
|
if (backend === "codex")
|
|
14508
14628
|
return "unrecorded";
|
|
14629
|
+
if (backend === "kiro-cli")
|
|
14630
|
+
return this.credentialProfileOf(name, "kiro-cli") ?? "default";
|
|
14631
|
+
if (backend === "grok" || backend === "muse" || backend === "opencode" || backend === "antigravity")
|
|
14632
|
+
return "default";
|
|
14509
14633
|
return undefined;
|
|
14510
14634
|
}
|
|
14635
|
+
/** #2122 §10: a Kiro instance with a credential profile writes its classic store in that profile's home. */
|
|
14636
|
+
kiroDbPathOf(name, backend) {
|
|
14637
|
+
if (backend !== "kiro-cli")
|
|
14638
|
+
return undefined;
|
|
14639
|
+
const profile = this.credentialProfileOf(name, "kiro-cli");
|
|
14640
|
+
return profile ? join(credentialProfileStoreHome(getAgendHome(), "kiro-cli", profile), "data.sqlite3") : undefined;
|
|
14641
|
+
}
|
|
14511
14642
|
/** #2122: the fleet's day boundary zone — defaults.scheduler.default_timezone, else this host's. */
|
|
14512
14643
|
usageZone() {
|
|
14513
14644
|
return this.fleetConfig?.defaults?.scheduler?.default_timezone?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
@@ -14550,6 +14681,19 @@ export class FleetManager {
|
|
|
14550
14681
|
rolloutsUnreadable: root => sharedRolloutIndex(root).unreadable,
|
|
14551
14682
|
metaPath: join(this.dataDir, "cache-codex-rollouts.json"),
|
|
14552
14683
|
zone: () => this.usageZone(),
|
|
14684
|
+
// #2122 §10: Kiro's shared classic store (XDG_DATA_HOME, as kiro-cli resolves it) and its KIRO_HOME.
|
|
14685
|
+
kiroDbPath: () => join(process.env.XDG_DATA_HOME?.trim() || join(homedir(), ".local", "share"), "kiro-cli", "data.sqlite3"),
|
|
14686
|
+
kiroHome: () => kiroHome(),
|
|
14687
|
+
// #2122 §10 step 3: grok keeps its sessions under ~/.grok (grok.ts reads them there too).
|
|
14688
|
+
grokSessionsDir: () => join(homedir(), ".grok", "sessions"),
|
|
14689
|
+
// #2122 §10 step 4: muse keeps sessions under $XDG_DATA_HOME/muse (else ~/.local/share/muse), as muse.ts reads them.
|
|
14690
|
+
// #2122 §10 step 5: OpenCode's database, under XDG_DATA_HOME as opencode resolves it.
|
|
14691
|
+
// #2122 §10 step 6: agy's conversations, under ~/.gemini as the antigravity backend reads its settings.
|
|
14692
|
+
antigravityDir: () => join(homedir(), ".gemini", "antigravity-cli", "conversations"),
|
|
14693
|
+
// The version AgEnD's CLI probe last recorded for agy: its cache file, whatever its age (never runs agy).
|
|
14694
|
+
antigravityVersion: () => this.lastRecordedCliVersion("antigravity"),
|
|
14695
|
+
opencodeDbPath: () => join(process.env.XDG_DATA_HOME?.trim() || join(homedir(), ".local", "share"), "opencode", "opencode.db"),
|
|
14696
|
+
museSessionsDir: () => join(process.env.XDG_DATA_HOME?.trim() || join(homedir(), ".local", "share"), "muse", "sessions"),
|
|
14553
14697
|
log: (level, msg, extra) => this.logger[level](extra ?? {}, msg),
|
|
14554
14698
|
});
|
|
14555
14699
|
}
|
|
@@ -14670,6 +14814,7 @@ export class FleetManager {
|
|
|
14670
14814
|
return this.deliveryOutbox.acknowledge(deliveryId, by);
|
|
14671
14815
|
},
|
|
14672
14816
|
tasks: () => this.needsYouTaskItems(),
|
|
14817
|
+
backendSignIns: () => this.needsYouBackendSignIns(),
|
|
14673
14818
|
taskAction: action => this.taskActionFromChat(action),
|
|
14674
14819
|
settings: () => ({
|
|
14675
14820
|
liveMessage: this.fleetConfig?.needs_you?.live_message !== false,
|
|
@@ -16685,6 +16830,55 @@ export class FleetManager {
|
|
|
16685
16830
|
}
|
|
16686
16831
|
return this.loginController;
|
|
16687
16832
|
}
|
|
16833
|
+
browserBackendSignInsInstance = null;
|
|
16834
|
+
structuredLoginCleanupFailed = false;
|
|
16835
|
+
pendingBackendSince = new Map();
|
|
16836
|
+
browserSignInOwnerCurrent(owner) {
|
|
16837
|
+
const record = owner;
|
|
16838
|
+
return !this.shuttingDown && typeof record.handle === "string" && this.webSessions?.findByHandle(record.handle) === owner
|
|
16839
|
+
&& this.isLocalAdminSession(record.handle);
|
|
16840
|
+
}
|
|
16841
|
+
/** #2599: default-account controls share the exact writer reservation with chat/profile/install. */
|
|
16842
|
+
get browserBackendSignIns() {
|
|
16843
|
+
return this.browserBackendSignInsInstance ??= new BrowserBackendSignIns({
|
|
16844
|
+
dataDir: this.dataDir, lock: this.loginWindow,
|
|
16845
|
+
current: owner => this.browserSignInOwnerCurrent(owner),
|
|
16846
|
+
installed: async () => ONBOARDING_BACKENDS.filter(backend => this.isCliInstalled(backend)),
|
|
16847
|
+
checkAuth: backend => checkAuthStatus(LOGIN_FLOWS[backend].authCheck),
|
|
16848
|
+
checkConsoleAuth: () => checkAuthStatus(LOGIN_FLOWS.antigravity.authCheck),
|
|
16849
|
+
transport: () => createStructuredSignInTransport(),
|
|
16850
|
+
changed: (event, owner, launched) => {
|
|
16851
|
+
try {
|
|
16852
|
+
this.eventLog?.insert("login", "browser_backend_signin", event);
|
|
16853
|
+
}
|
|
16854
|
+
catch { /* safe metadata only */ }
|
|
16855
|
+
broadcastSseEvent(this.sseClients, "backend_login", {}, err => this.logger.debug({ err }, "backend_login event failed"));
|
|
16856
|
+
this.needsYou?.poke();
|
|
16857
|
+
if (!this.browserSignInOwnerCurrent(owner))
|
|
16858
|
+
return;
|
|
16859
|
+
if (event.phase === "starting") {
|
|
16860
|
+
const record = owner;
|
|
16861
|
+
this.notifyFleetError(t("login.web_dashboard_notice", event.backend, record.label, record.handle.slice(0, 8)));
|
|
16862
|
+
}
|
|
16863
|
+
if (event.phase === "done") {
|
|
16864
|
+
void this.backendAvailability.refresh([event.backend], { force: true }).catch(() => { });
|
|
16865
|
+
if (launched)
|
|
16866
|
+
void this.recoverBackendInstances(event.backend, undefined, null).catch(() => this.logger.warn({ backend: event.backend }, "Post sign-in recovery failed"));
|
|
16867
|
+
}
|
|
16868
|
+
},
|
|
16869
|
+
});
|
|
16870
|
+
}
|
|
16871
|
+
needsYouBackendSignIns() {
|
|
16872
|
+
const pending = readPendingBackendSignIns(this.dataDir).pending;
|
|
16873
|
+
for (const backend of this.pendingBackendSince?.keys() ?? [])
|
|
16874
|
+
if (!pending.includes(backend))
|
|
16875
|
+
this.pendingBackendSince.delete(backend);
|
|
16876
|
+
return pending.map(backend => {
|
|
16877
|
+
if (!this.pendingBackendSince.has(backend))
|
|
16878
|
+
this.pendingBackendSince.set(backend, Date.now());
|
|
16879
|
+
return { backend, since: this.pendingBackendSince.get(backend), browser: structuredSignInSupported(backend) };
|
|
16880
|
+
});
|
|
16881
|
+
}
|
|
16688
16882
|
/** #2233: sign-ins started from the web dashboard (local admin sessions), on the same controller as chat's. */
|
|
16689
16883
|
webBackendLoginsInstance = null;
|
|
16690
16884
|
get webBackendLogins() {
|
|
@@ -17037,6 +17231,14 @@ export class FleetManager {
|
|
|
17037
17231
|
// "cancelled" is the detail both legacy onDone handlers keep quiet about —
|
|
17038
17232
|
// a stopping fleet must not announce "login failed — fleet shutdown".
|
|
17039
17233
|
await Promise.all([
|
|
17234
|
+
bounded(this.browserBackendSignInsInstance?.close().then(confirmed => {
|
|
17235
|
+
if (confirmed)
|
|
17236
|
+
this.browserBackendSignInsInstance = null;
|
|
17237
|
+
else {
|
|
17238
|
+
this.structuredLoginCleanupFailed = true;
|
|
17239
|
+
this.logger.error("Structured login cleanup failed; sign-in admission remains closed until the host is restarted.");
|
|
17240
|
+
}
|
|
17241
|
+
}), "browser-controls"),
|
|
17040
17242
|
bounded(this.loginController?.shutdown(), "web-login"),
|
|
17041
17243
|
bounded(this.activeInstall?.session.cancel("cancelled"), "install"),
|
|
17042
17244
|
]);
|
|
@@ -17053,6 +17255,13 @@ export class FleetManager {
|
|
|
17053
17255
|
this.installHandoff = null;
|
|
17054
17256
|
return t("login.cancelled", backend);
|
|
17055
17257
|
}
|
|
17258
|
+
const browserLogin = this.browserBackendSignInsInstance, browserBackend = browserLogin?.activeBackend;
|
|
17259
|
+
if (browserLogin && browserBackend) {
|
|
17260
|
+
const confirmed = await browserLogin.close();
|
|
17261
|
+
if (confirmed)
|
|
17262
|
+
this.browserBackendSignInsInstance = null;
|
|
17263
|
+
return confirmed ? t("login.cancelled", browserBackend) : t("login.browser_cleanup_failed");
|
|
17264
|
+
}
|
|
17056
17265
|
if (this.loginController?.isActive())
|
|
17057
17266
|
return this.loginController.cancel();
|
|
17058
17267
|
return t("login.no_session");
|
|
@@ -18564,6 +18773,19 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
|
|
|
18564
18773
|
catch { /* missing / stale / corrupt */ }
|
|
18565
18774
|
return null;
|
|
18566
18775
|
}
|
|
18776
|
+
/**
|
|
18777
|
+
* #2122 §10: the version the CLI probe last recorded for a backend — its cache file read whatever its age (the usage
|
|
18778
|
+
* reader's version gate needs the last known version, not a fresh one); null when none was recorded. Never a probe.
|
|
18779
|
+
*/
|
|
18780
|
+
lastRecordedCliVersion(backend) {
|
|
18781
|
+
try {
|
|
18782
|
+
const v = JSON.parse(readFileSync(this.cliEnvPath(backend), "utf-8")).version;
|
|
18783
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
18784
|
+
}
|
|
18785
|
+
catch {
|
|
18786
|
+
return null;
|
|
18787
|
+
}
|
|
18788
|
+
}
|
|
18567
18789
|
/** Return cached CLI versions immediately. Refresh is started after /sysinfo is sent. */
|
|
18568
18790
|
getBackendCliVersionSnapshot() {
|
|
18569
18791
|
const snapshot = {};
|