@phnx-labs/agents-cli 1.22.84 → 1.22.86
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/CHANGELOG.md +96 -0
- package/README.md +80 -53
- package/dist/bootstrap.d.ts +7 -6
- package/dist/bootstrap.js +20 -27
- package/dist/cli/command-registry.js +5 -1
- package/dist/commands/accounts.d.ts +6 -13
- package/dist/commands/accounts.js +330 -143
- package/dist/commands/alias.js +7 -3
- package/dist/commands/apply.js +1 -1
- package/dist/commands/auth-mint.d.ts +7 -3
- package/dist/commands/auth-mint.js +21 -9
- package/dist/commands/auth.js +2 -3
- package/dist/commands/browser.js +10 -10
- package/dist/commands/daemon.d.ts +7 -3
- package/dist/commands/daemon.js +40 -25
- package/dist/commands/doctor.js +28 -2
- package/dist/commands/exec.js +87 -39
- package/dist/commands/fleet-capture.js +21 -2
- package/dist/commands/harness-wizard.js +2 -2
- package/dist/commands/lease.js +7 -12
- package/dist/commands/profiles.d.ts +2 -2
- package/dist/commands/profiles.js +20 -13
- package/dist/commands/repo.js +3 -3
- package/dist/commands/run-account-picker.d.ts +3 -3
- package/dist/commands/run-account-picker.js +29 -30
- package/dist/commands/secrets-passthrough.d.ts +20 -0
- package/dist/commands/secrets-passthrough.js +43 -0
- package/dist/commands/setup-accounts.d.ts +3 -2
- package/dist/commands/setup-accounts.js +4 -4
- package/dist/commands/setup-secrets.d.ts +16 -21
- package/dist/commands/setup-secrets.js +43 -224
- package/dist/commands/ssh.js +2 -2
- package/dist/commands/sync.js +1 -1
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.js +43 -40
- package/dist/commands/versions.d.ts +11 -11
- package/dist/commands/versions.js +131 -83
- package/dist/commands/view.d.ts +1 -9
- package/dist/commands/view.js +41 -55
- package/dist/commands/webhook.js +5 -5
- package/dist/commands/workflows.js +2 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.js +13 -42
- package/dist/lib/account-capabilities.d.ts +1 -1
- package/dist/lib/account-capabilities.js +1 -1
- package/dist/lib/account-catalog.d.ts +121 -6
- package/dist/lib/account-catalog.js +421 -7
- package/dist/lib/account-registry.d.ts +8 -30
- package/dist/lib/account-registry.js +36 -79
- package/dist/lib/account-schema.d.ts +1 -1
- package/dist/lib/account-schema.js +1 -1
- package/dist/lib/accounting/account-pool-collect.js +16 -4
- package/dist/lib/accounting/rotate.d.ts +20 -7
- package/dist/lib/accounting/rotate.js +89 -13
- package/dist/lib/accounting/usage.js +11 -16
- package/dist/lib/accounts/add.d.ts +138 -0
- package/dist/lib/accounts/add.js +651 -0
- package/dist/lib/accounts/migrate.d.ts +117 -0
- package/dist/lib/accounts/migrate.js +536 -0
- package/dist/lib/accounts/slots.d.ts +13 -0
- package/dist/lib/accounts/slots.js +100 -0
- package/dist/lib/agent-spec/agents.d.ts +16 -0
- package/dist/lib/agent-spec/agents.js +38 -4
- package/dist/lib/app-bundle-install.js +5 -4
- package/dist/lib/auth-health.d.ts +3 -0
- package/dist/lib/auth-health.js +11 -0
- package/dist/lib/auth-mint.d.ts +52 -16
- package/dist/lib/auth-mint.js +117 -39
- package/dist/lib/browser/chrome.d.ts +1 -1
- package/dist/lib/browser/chrome.js +5 -5
- package/dist/lib/byok-usage.js +3 -3
- package/dist/lib/claude-account-token.d.ts +47 -3
- package/dist/lib/claude-account-token.js +172 -50
- package/dist/lib/cloud/antigravity.js +4 -4
- package/dist/lib/cloud/cursor.js +4 -4
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +6 -6
- package/dist/lib/crabbox/runtimes.d.ts +3 -3
- package/dist/lib/crabbox/runtimes.js +5 -5
- package/dist/lib/daemon/account-state-daemon-service.d.ts +37 -1
- package/dist/lib/daemon/account-state-daemon-service.js +199 -3
- package/dist/lib/daemon/auth-sync-service.js +32 -1
- package/dist/lib/daemon/daemon.d.ts +6 -16
- package/dist/lib/daemon/daemon.js +52 -97
- package/dist/lib/daemon/daemon.test-fixture.d.ts +7 -9
- package/dist/lib/daemon/daemon.test-fixture.js +22 -40
- package/dist/lib/daemon/harness-update-service.d.ts +1 -1
- package/dist/lib/daemon/harness-update-service.js +1 -1
- package/dist/lib/daemon/runner.d.ts +15 -3
- package/dist/lib/daemon/runner.js +49 -24
- package/dist/lib/daemon-health.d.ts +1 -2
- package/dist/lib/daemon-health.js +7 -6
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +1 -11
- package/dist/lib/daemon-webhooks.d.ts +8 -7
- package/dist/lib/daemon-webhooks.js +10 -9
- package/dist/lib/device-config.js +1 -1
- package/dist/lib/devices/doctor-findings.d.ts +1 -1
- package/dist/lib/devices/harness-inventory.d.ts +39 -0
- package/dist/lib/devices/harness-inventory.js +126 -4
- package/dist/lib/doctor-diff.js +2 -1
- package/dist/lib/exec-account-home.d.ts +38 -0
- package/dist/lib/exec-account-home.js +164 -0
- package/dist/lib/exec.d.ts +30 -0
- package/dist/lib/exec.js +101 -31
- package/dist/lib/fleet/apply.d.ts +2 -2
- package/dist/lib/fleet/apply.js +4 -4
- package/dist/lib/fleet/auth-sync.js +4 -3
- package/dist/lib/fleet-shared-repo-sync.js +6 -9
- package/dist/lib/harness/adapter.d.ts +7 -7
- package/dist/lib/harness/adapter.js +10 -6
- package/dist/lib/harness/adapters/grok.js +8 -3
- package/dist/lib/harness/adapters/muse.js +1 -1
- package/dist/lib/harness/adapters/opencode.js +12 -3
- package/dist/lib/harness-auth-capabilities.d.ts +61 -0
- package/dist/lib/harness-auth-capabilities.js +52 -0
- package/dist/lib/helper-versions.d.ts +6 -4
- package/dist/lib/helper-versions.js +5 -4
- package/dist/lib/hosts/credential-transport.d.ts +10 -0
- package/dist/lib/hosts/credential-transport.js +37 -0
- package/dist/lib/hosts/dispatch.d.ts +7 -0
- package/dist/lib/hosts/dispatch.js +13 -6
- package/dist/lib/identity/client.d.ts +3 -3
- package/dist/lib/identity/client.js +3 -3
- package/dist/lib/installations/index.d.ts +1 -1
- package/dist/lib/installations/index.js +1 -1
- package/dist/lib/installations/migrate.js +9 -0
- package/dist/lib/installations/resolve.js +1 -1
- package/dist/lib/installations/shims.d.ts +21 -4
- package/dist/lib/installations/shims.js +86 -5
- package/dist/lib/installations/store.d.ts +43 -0
- package/dist/lib/installations/store.js +75 -7
- package/dist/lib/installations/versions.js +1 -1
- package/dist/lib/menubar/install-menubar.d.ts +3 -3
- package/dist/lib/menubar/install-menubar.js +3 -3
- package/dist/lib/native-accounts.d.ts +35 -0
- package/dist/lib/native-accounts.js +41 -0
- package/dist/lib/net-close.d.ts +11 -0
- package/dist/lib/net-close.js +25 -0
- package/dist/lib/openclaw-keychain.js +27 -1
- package/dist/lib/profiles.js +22 -9
- package/dist/lib/project-resources.js +1 -1
- package/dist/lib/reserved-stores.d.ts +53 -0
- package/dist/lib/reserved-stores.js +120 -0
- package/dist/lib/secrets-client.d.ts +199 -0
- package/dist/lib/secrets-client.js +718 -0
- package/dist/lib/secrets-policy.d.ts +235 -0
- package/dist/lib/secrets-policy.js +505 -0
- package/dist/lib/secrets-types.d.ts +188 -0
- package/dist/lib/secrets-types.js +24 -0
- package/dist/lib/self-heal/checks/shims.js +5 -3
- package/dist/lib/service-manifest.js +3 -4
- package/dist/lib/session/db.d.ts +18 -0
- package/dist/lib/session/db.js +47 -14
- package/dist/lib/session/sync/config.js +2 -2
- package/dist/lib/sha256-asset.d.ts +11 -16
- package/dist/lib/sha256-asset.js +11 -16
- package/dist/lib/share/config.js +14 -5
- package/dist/lib/signin-badge.d.ts +26 -0
- package/dist/lib/signin-badge.js +42 -0
- package/dist/lib/staleness/detectors/workflows.js +3 -105
- package/dist/lib/staleness/writers/workflows.js +2 -1
- package/dist/lib/state.d.ts +7 -4
- package/dist/lib/state.js +18 -7
- package/dist/lib/sync-umbrella.js +2 -2
- package/dist/lib/teams/agents.js +1 -1
- package/dist/lib/types.d.ts +74 -33
- package/dist/lib/view-types.d.ts +6 -3
- package/dist/lib/workflows-registry.d.ts +71 -0
- package/dist/lib/workflows-registry.js +280 -0
- package/dist/lib/workflows.d.ts +21 -21
- package/dist/lib/workflows.js +12 -327
- package/package.json +2 -3
- package/scripts/postinstall.js +35 -30
- package/dist/commands/secrets-import.d.ts +0 -18
- package/dist/commands/secrets-import.js +0 -74
- package/dist/commands/secrets-migrate.d.ts +0 -25
- package/dist/commands/secrets-migrate.js +0 -334
- package/dist/commands/secrets-rotate-passphrase.d.ts +0 -17
- package/dist/commands/secrets-rotate-passphrase.js +0 -96
- package/dist/commands/secrets-sync.d.ts +0 -11
- package/dist/commands/secrets-sync.js +0 -153
- package/dist/commands/secrets-vault.d.ts +0 -10
- package/dist/commands/secrets-vault.js +0 -130
- package/dist/commands/secrets.d.ts +0 -212
- package/dist/commands/secrets.js +0 -3030
- package/dist/lib/accounts/connect.d.ts +0 -176
- package/dist/lib/accounts/connect.js +0 -453
- package/dist/lib/daemon/keychain-reap-service.d.ts +0 -17
- package/dist/lib/daemon/keychain-reap-service.js +0 -32
- package/dist/lib/daemon/secrets-broker-service.d.ts +0 -21
- package/dist/lib/daemon/secrets-broker-service.js +0 -51
- package/dist/lib/secrets/agent.d.ts +0 -358
- package/dist/lib/secrets/agent.js +0 -1291
- package/dist/lib/secrets/audit.d.ts +0 -46
- package/dist/lib/secrets/audit.js +0 -101
- package/dist/lib/secrets/bundles.d.ts +0 -290
- package/dist/lib/secrets/bundles.js +0 -1547
- package/dist/lib/secrets/download-keychain.d.ts +0 -47
- package/dist/lib/secrets/download-keychain.js +0 -70
- package/dist/lib/secrets/drivers/rush.d.ts +0 -14
- package/dist/lib/secrets/drivers/rush.js +0 -90
- package/dist/lib/secrets/fallback.d.ts +0 -48
- package/dist/lib/secrets/fallback.js +0 -48
- package/dist/lib/secrets/filestore.d.ts +0 -222
- package/dist/lib/secrets/filestore.js +0 -1099
- package/dist/lib/secrets/headless.d.ts +0 -43
- package/dist/lib/secrets/headless.js +0 -56
- package/dist/lib/secrets/icloud-import.d.ts +0 -79
- package/dist/lib/secrets/icloud-import.js +0 -206
- package/dist/lib/secrets/index.d.ts +0 -451
- package/dist/lib/secrets/index.js +0 -1568
- package/dist/lib/secrets/install-helper.d.ts +0 -72
- package/dist/lib/secrets/install-helper.js +0 -245
- package/dist/lib/secrets/lease.d.ts +0 -25
- package/dist/lib/secrets/lease.js +0 -44
- package/dist/lib/secrets/linux.d.ts +0 -77
- package/dist/lib/secrets/linux.js +0 -393
- package/dist/lib/secrets/list-filter.d.ts +0 -109
- package/dist/lib/secrets/list-filter.js +0 -261
- package/dist/lib/secrets/mcp.d.ts +0 -93
- package/dist/lib/secrets/mcp.js +0 -211
- package/dist/lib/secrets/profiles.d.ts +0 -10
- package/dist/lib/secrets/profiles.js +0 -13
- package/dist/lib/secrets/push.d.ts +0 -133
- package/dist/lib/secrets/push.js +0 -273
- package/dist/lib/secrets/rc-hygiene.d.ts +0 -63
- package/dist/lib/secrets/rc-hygiene.js +0 -143
- package/dist/lib/secrets/read-backoff.d.ts +0 -27
- package/dist/lib/secrets/read-backoff.js +0 -64
- package/dist/lib/secrets/reaper.d.ts +0 -90
- package/dist/lib/secrets/reaper.js +0 -243
- package/dist/lib/secrets/remote.d.ts +0 -152
- package/dist/lib/secrets/remote.js +0 -344
- package/dist/lib/secrets/reserved-sync.d.ts +0 -66
- package/dist/lib/secrets/reserved-sync.js +0 -147
- package/dist/lib/secrets/scope.d.ts +0 -26
- package/dist/lib/secrets/scope.js +0 -29
- package/dist/lib/secrets/session-store.d.ts +0 -107
- package/dist/lib/secrets/session-store.js +0 -342
- package/dist/lib/secrets/sync-backend.d.ts +0 -48
- package/dist/lib/secrets/sync-backend.js +0 -13
- package/dist/lib/secrets/sync-commands.d.ts +0 -21
- package/dist/lib/secrets/sync-commands.js +0 -21
- package/dist/lib/secrets/sync.d.ts +0 -48
- package/dist/lib/secrets/sync.js +0 -237
- package/dist/lib/secrets/unlock-hints.d.ts +0 -27
- package/dist/lib/secrets/unlock-hints.js +0 -36
- package/dist/lib/secrets/usage-db.d.ts +0 -46
- package/dist/lib/secrets/usage-db.js +0 -96
- package/dist/lib/secrets/vault-age-helper.d.ts +0 -1
- package/dist/lib/secrets/vault-age-helper.js +0 -34
- package/dist/lib/secrets/vault.d.ts +0 -49
- package/dist/lib/secrets/vault.js +0 -399
- package/dist/lib/secrets/windows.d.ts +0 -81
- package/dist/lib/secrets/windows.js +0 -556
- package/scripts/install-helper.js +0 -97
- /package/dist/lib/{secrets/sync-passphrase.d.ts → sync-passphrase.d.ts} +0 -0
- /package/dist/lib/{secrets/sync-passphrase.js → sync-passphrase.js} +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as os from 'os';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { bundleBackendSync, bundleExistsSync, isSecretsClientError, readAndResolveBundleEnvSync, secretsKeychainItem, storeGetSync, storeHasSync, } from './secrets-client.js';
|
|
5
|
+
import { AUTH_STORE_ALIAS, ReservedBundleWrongBackendError, assertReservedAuthBackend, isReservedBundleBackendError, isReservedStoreName, } from './reserved-stores.js';
|
|
6
|
+
import { ensureSlot, recordSlot } from './accounts/slots.js';
|
|
7
|
+
import { harnessWorkerKinds } from './harness-auth-capabilities.js';
|
|
7
8
|
/**
|
|
8
9
|
* Reserved FILE-BASED secrets bundle holding long-lived, non-rotating Claude
|
|
9
10
|
* setup-tokens. Usage/probe reads authenticate with these instead of Claude
|
|
@@ -12,8 +13,9 @@ import { secretsKeychainItem } from './secrets/index.js';
|
|
|
12
13
|
* bare key, so one account's token can't be misapplied to another in a
|
|
13
14
|
* multi-account fleet.
|
|
14
15
|
*/
|
|
15
|
-
/** Alias of the
|
|
16
|
-
export const AUTH_BUNDLE =
|
|
16
|
+
/** Alias of the reserved-store name so mint/seed shares one source of truth. */
|
|
17
|
+
export const AUTH_BUNDLE = AUTH_STORE_ALIAS;
|
|
18
|
+
export { ReservedBundleWrongBackendError };
|
|
17
19
|
/**
|
|
18
20
|
* A well-formed Claude OAuth setup-token: the `sk-ant-oat01-` prefix followed by
|
|
19
21
|
* token-safe characters only, on a single line. `claude setup-token` mints exactly
|
|
@@ -28,8 +30,49 @@ export const AUTH_BUNDLE = AUTH_BUNDLE_NAME;
|
|
|
28
30
|
* where a corrupt bundle entry must be caught before it reaches the auth header.
|
|
29
31
|
*/
|
|
30
32
|
const SETUP_TOKEN_RE = /^sk-ant-oat01-[A-Za-z0-9_-]+$/;
|
|
31
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Process-local memo of resolved setup-tokens, keyed by the caller's cache key
|
|
35
|
+
* AND the per-account token key — a version home that switches accounts must
|
|
36
|
+
* miss, never be served the previous account's token. Plaintext setup-tokens
|
|
37
|
+
* are never written to another cache. The bundle now
|
|
38
|
+
* lives behind the standalone `secrets` process (one spawn per read), so the
|
|
39
|
+
* memo is what keeps a probe loop over many accounts from re-decrypting the
|
|
40
|
+
* whole `auth` bundle per account; it is bounded by {@link SETUP_TOKEN_MEMO_TTL_MS}
|
|
41
|
+
* so a rotation performed by another process is observed within seconds, and
|
|
42
|
+
* an in-process writer clears it outright ({@link invalidateClaudeSetupTokenCache}).
|
|
43
|
+
*/
|
|
32
44
|
const setupTokenCache = new Map();
|
|
45
|
+
const SETUP_TOKEN_MEMO_TTL_MS = 10_000;
|
|
46
|
+
/** Drop every memoized setup-token; called after this process seeds or rotates one. */
|
|
47
|
+
export function invalidateClaudeSetupTokenCache() {
|
|
48
|
+
setupTokenCache.clear();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Read the reserved `auth` bundle through the standalone. Returns null when it
|
|
52
|
+
* does not exist — checked explicitly, because a prompt-free (`agentOnly`) read
|
|
53
|
+
* of a bundle that does not exist reports LOCKED, and a genuinely locked store
|
|
54
|
+
* must stay distinguishable from an absent bundle. A keychain- or vault-backed
|
|
55
|
+
* `auth` fails loud with {@link ReservedBundleWrongBackendError} — whether the
|
|
56
|
+
* standalone refused the resolve (`WRONG_BACKEND`) or the returned metadata
|
|
57
|
+
* names the wrong backend — so usage/probe never silently ignores a seeded
|
|
58
|
+
* token (SEC-GAP-3).
|
|
59
|
+
*/
|
|
60
|
+
function readReservedAuthBundle(caller) {
|
|
61
|
+
if (!bundleExistsSync(AUTH_BUNDLE))
|
|
62
|
+
return null;
|
|
63
|
+
let resolved;
|
|
64
|
+
try {
|
|
65
|
+
resolved = readAndResolveBundleEnvSync(AUTH_BUNDLE, { caller, agentOnly: true });
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (isSecretsClientError(err, 'WRONG_BACKEND')) {
|
|
69
|
+
throw new ReservedBundleWrongBackendError(AUTH_BUNDLE, bundleBackendSync(AUTH_BUNDLE));
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
assertReservedAuthBackend(resolved.bundle.backend ?? 'keychain');
|
|
74
|
+
return resolved;
|
|
75
|
+
}
|
|
33
76
|
function credentialFingerprint(credentialPath) {
|
|
34
77
|
try {
|
|
35
78
|
const stat = fs.statSync(credentialPath, { bigint: true });
|
|
@@ -117,7 +160,7 @@ function discoverClaudeAccountEmailFromOauthToken(home) {
|
|
|
117
160
|
return email;
|
|
118
161
|
}
|
|
119
162
|
catch (err) {
|
|
120
|
-
if (err
|
|
163
|
+
if (isReservedBundleBackendError(err))
|
|
121
164
|
throw err;
|
|
122
165
|
return null;
|
|
123
166
|
}
|
|
@@ -132,13 +175,10 @@ function discoverEmailUncached(home, tokenPath) {
|
|
|
132
175
|
}
|
|
133
176
|
if (!isValidClaudeSetupToken(token))
|
|
134
177
|
return null;
|
|
135
|
-
|
|
178
|
+
const resolved = readReservedAuthBundle('usage');
|
|
179
|
+
if (!resolved)
|
|
136
180
|
return null;
|
|
137
|
-
|
|
138
|
-
throw new ReservedBundleWrongBackendError(AUTH_BUNDLE, bundleBackend(AUTH_BUNDLE));
|
|
139
|
-
}
|
|
140
|
-
const { env } = readAndResolveBundleEnv(AUTH_BUNDLE, { caller: 'usage', agentOnly: true });
|
|
141
|
-
for (const [key, value] of Object.entries(env)) {
|
|
181
|
+
for (const [key, value] of Object.entries(resolved.env)) {
|
|
142
182
|
if (value.trim() !== token)
|
|
143
183
|
continue;
|
|
144
184
|
const email = emailFromTokenKey(key);
|
|
@@ -175,8 +215,8 @@ function emailFromTokenKey(key) {
|
|
|
175
215
|
}
|
|
176
216
|
/**
|
|
177
217
|
* Resolve a long-lived setup-token for an EXPLICIT account email, independent of
|
|
178
|
-
* any version home's `.claude.json`. This is what lets
|
|
179
|
-
*
|
|
218
|
+
* any version home's `.claude.json`. This is what lets worker-slot provisioning
|
|
219
|
+
* seed a headless worker home that has never had an interactive login: the
|
|
180
220
|
* account's non-rotating setup-token is already fleet-synced in the file-based
|
|
181
221
|
* `auth` bundle, keyed by email ({@link claudeAccountTokenKey}), so we can write
|
|
182
222
|
* the home's `.oauth_token` from it without the circular
|
|
@@ -192,44 +232,23 @@ export function resolveClaudeSetupTokenForEmail(email, cacheKey) {
|
|
|
192
232
|
const trimmed = email.trim();
|
|
193
233
|
if (!trimmed)
|
|
194
234
|
return null;
|
|
195
|
-
if (!bundleExists(AUTH_BUNDLE))
|
|
196
|
-
return null;
|
|
197
|
-
const backend = bundleBackend(AUTH_BUNDLE);
|
|
198
|
-
if (backend !== 'file') {
|
|
199
|
-
// SEC-GAP-3: a keychain/vault-backed `auth` used to return null here, so
|
|
200
|
-
// usage/probe fell through to the interactive login (Touch ID) with no
|
|
201
|
-
// hint that the seeded setup-token was being ignored.
|
|
202
|
-
throw new ReservedBundleWrongBackendError(AUTH_BUNDLE, backend);
|
|
203
|
-
}
|
|
204
235
|
const key = claudeAccountTokenKey(trimmed);
|
|
205
|
-
const ck = cacheKey ?? `email:${trimmed}`;
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const { env } = readAndResolveBundleEnv(AUTH_BUNDLE, { caller: 'usage', agentOnly: true });
|
|
219
|
-
const v = (env[key] ?? '').trim();
|
|
220
|
-
const token = v.length > 0 && isValidClaudeSetupToken(v) ? v : null;
|
|
221
|
-
const after = credentialFingerprint(credentialPath);
|
|
222
|
-
if (before === after) {
|
|
223
|
-
setupTokenCache.set(ck, { credentialPath, fingerprint: after, token });
|
|
224
|
-
return token;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// The credential changed during both decrypt attempts. Fail closed rather
|
|
228
|
-
// than return a token whose current file fingerprint was never observed.
|
|
229
|
-
return null;
|
|
236
|
+
const ck = `${cacheKey ?? `email:${trimmed}`}\0${key}`;
|
|
237
|
+
const cached = setupTokenCache.get(ck);
|
|
238
|
+
if (cached && Date.now() - cached.readAt < SETUP_TOKEN_MEMO_TTL_MS)
|
|
239
|
+
return cached.token;
|
|
240
|
+
// SEC-GAP-3: a keychain/vault-backed `auth` used to return null here, so
|
|
241
|
+
// usage/probe fell through to the interactive login (Touch ID) with no
|
|
242
|
+
// hint that the seeded setup-token was being ignored. readReservedAuthBundle
|
|
243
|
+
// throws ReservedBundleWrongBackendError instead, which propagates.
|
|
244
|
+
const resolved = readReservedAuthBundle('usage');
|
|
245
|
+
const v = (resolved?.env[key] ?? '').trim();
|
|
246
|
+
const token = v.length > 0 && isValidClaudeSetupToken(v) ? v : null;
|
|
247
|
+
setupTokenCache.set(ck, { readAt: Date.now(), token });
|
|
248
|
+
return token;
|
|
230
249
|
}
|
|
231
250
|
catch (err) {
|
|
232
|
-
if (err
|
|
251
|
+
if (isReservedBundleBackendError(err))
|
|
233
252
|
throw err;
|
|
234
253
|
return null;
|
|
235
254
|
}
|
|
@@ -244,6 +263,109 @@ export function resolveClaudeSetupTokenForEmail(email, cacheKey) {
|
|
|
244
263
|
* It never copies a rotating OAuth credential (`.credentials.json`) — the setup-token
|
|
245
264
|
* stays the credential of record.
|
|
246
265
|
*/
|
|
266
|
+
/**
|
|
267
|
+
* Write a Claude worker slot's `.oauth_token` (0600) from a durable setup-token,
|
|
268
|
+
* the way the pre-slot worker home was provisioned. Refuses a malformed token so
|
|
269
|
+
* a corrupt bundle entry can never reach the auth header (see {@link SETUP_TOKEN_RE}).
|
|
270
|
+
* `home` is the slot dir; the claude adapter shim reads `$CLAUDE_CONFIG_DIR/.oauth_token`
|
|
271
|
+
* where `CLAUDE_CONFIG_DIR` is `<home>/.claude`. Returns the written path.
|
|
272
|
+
*/
|
|
273
|
+
export function writeClaudeWorkerOauthToken(home, token) {
|
|
274
|
+
if (!isValidClaudeSetupToken(token)) {
|
|
275
|
+
throw new Error('Refusing to write a malformed Claude setup-token to a worker slot.');
|
|
276
|
+
}
|
|
277
|
+
const tokenPath = path.join(home, '.claude', '.oauth_token');
|
|
278
|
+
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
|
|
279
|
+
fs.writeFileSync(tokenPath, token, { mode: 0o600 });
|
|
280
|
+
return tokenPath;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Read one non-rotating credential value from a reserved store by its storage
|
|
284
|
+
* key. Returns null when the bundle or key is absent. Used to materialize a
|
|
285
|
+
* worker slot from a synced durable credential (setup-token / API key).
|
|
286
|
+
*
|
|
287
|
+
* A reserved `__<harness>__` store is file-backed by design (headless, fleet-
|
|
288
|
+
* shareable) and its item is read directly, symmetric with how it is written —
|
|
289
|
+
* `readAndResolveBundleEnv`'s name validation does not yet accept a reserved
|
|
290
|
+
* name on the READ path (a secrets-track seam; see the PR body). A non-reserved
|
|
291
|
+
* bundle (the legacy `auth` alias, a provider bundle) goes through the normal
|
|
292
|
+
* resolver so refs/expiry/lease gates still apply.
|
|
293
|
+
*/
|
|
294
|
+
export function readReservedCredential(bundle, key) {
|
|
295
|
+
try {
|
|
296
|
+
if (isReservedStoreName(bundle) && bundle.startsWith('__')) {
|
|
297
|
+
const item = secretsKeychainItem(bundle, key);
|
|
298
|
+
if (!storeHasSync('file', item))
|
|
299
|
+
return null;
|
|
300
|
+
const value = storeGetSync('file', item).trim();
|
|
301
|
+
return value.length > 0 ? value : null;
|
|
302
|
+
}
|
|
303
|
+
const { env } = readAndResolveBundleEnvSync(bundle, {
|
|
304
|
+
keys: [key],
|
|
305
|
+
keyMode: 'storage',
|
|
306
|
+
agentOnly: true,
|
|
307
|
+
caller: 'provision-worker-slot',
|
|
308
|
+
});
|
|
309
|
+
const value = (env[key] ?? '').trim();
|
|
310
|
+
return value.length > 0 ? value : null;
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
if (isReservedBundleBackendError(err))
|
|
314
|
+
throw err;
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Materialize a worker slot for a portable account from its synced durable
|
|
320
|
+
* credential (PHNX-3940 T6 — the generalization of the pre-slot Claude worker-home
|
|
321
|
+
* provisioning). Creates the HOME-shaped slot dir (T1 `ensureSlot`), then, for a
|
|
322
|
+
* durable harness, writes the credential into it the way the Claude worker home is
|
|
323
|
+
* provisioned today: for `claude`, the setup-token → `.oauth_token` (0600) + the
|
|
324
|
+
* seeded identity email (the read-side join in agent-spec then completes the uuids
|
|
325
|
+
* from the registry row). API-key harnesses need no file — the key is injected at
|
|
326
|
+
* spawn from the reserved store (T5) — so their slot is created and recorded
|
|
327
|
+
* `durable` with no write. A per-device harness (`worker: 'none'`) gets a
|
|
328
|
+
* `per-device` slot and no credential — it logs in per box.
|
|
329
|
+
*
|
|
330
|
+
* This is worker-side reconciliation: it runs on the box where the key landed and
|
|
331
|
+
* NEVER transports anything (the SSH push that delivered the key is the daemon's
|
|
332
|
+
* job — invariant 1). Fails loud when a durable claude account has no resolvable
|
|
333
|
+
* token on this device rather than recording a slot that cannot authenticate.
|
|
334
|
+
*/
|
|
335
|
+
export function provisionWorkerSlot(account) {
|
|
336
|
+
const harness = account.agent;
|
|
337
|
+
const durable = harnessWorkerKinds(harness).some((kind) => kind === 'setup-token' || kind.startsWith('api-key'));
|
|
338
|
+
const slot = ensureSlot(harness, account.id);
|
|
339
|
+
const checkedAt = new Date().toISOString();
|
|
340
|
+
if (!durable) {
|
|
341
|
+
const record = { ...slot, authMode: 'per-device', verdict: 'unconfigured', checkedAt };
|
|
342
|
+
recordSlot(account.id, record);
|
|
343
|
+
return record;
|
|
344
|
+
}
|
|
345
|
+
if (harness === 'claude') {
|
|
346
|
+
const cred = account.workerCredential;
|
|
347
|
+
const token = cred
|
|
348
|
+
? readReservedCredential(cred.bundle, cred.key)
|
|
349
|
+
// Claude row predating T1 (no workerCredential): the legacy `auth` bundle
|
|
350
|
+
// keys the token by the account email.
|
|
351
|
+
: account.identityLabel
|
|
352
|
+
? resolveClaudeSetupTokenForEmail(account.identityLabel, slot.slotDir)
|
|
353
|
+
: null;
|
|
354
|
+
if (!token) {
|
|
355
|
+
const where = cred ? `${cred.bundle}:${cred.key}` : `'${AUTH_BUNDLE}' keyed by ${account.identityLabel ?? '(no email)'}`;
|
|
356
|
+
throw new Error(`No durable Claude setup-token for account ${account.id} on this device (${where}). `
|
|
357
|
+
+ `Mint and sync it from the headed device.`);
|
|
358
|
+
}
|
|
359
|
+
writeClaudeWorkerOauthToken(slot.slotDir, token);
|
|
360
|
+
if (account.identityLabel)
|
|
361
|
+
seedClaudeWorkerHomeIdentity(slot.slotDir, account.identityLabel);
|
|
362
|
+
}
|
|
363
|
+
// API-key harnesses: the key rides the reserved store and is injected at spawn
|
|
364
|
+
// (T5); nothing is written into the slot here.
|
|
365
|
+
const record = { ...slot, authMode: 'durable', verdict: 'unverified', checkedAt };
|
|
366
|
+
recordSlot(account.id, record);
|
|
367
|
+
return record;
|
|
368
|
+
}
|
|
247
369
|
export function seedClaudeWorkerHomeIdentity(versionHome, email) {
|
|
248
370
|
const trimmed = email.trim();
|
|
249
371
|
if (!trimmed)
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* back to GEMINI_API_KEY / GOOGLE_API_KEY in the environment.
|
|
21
21
|
*/
|
|
22
22
|
import { resolveDispatchRepos, normalizeProviderStatus } from './types.js';
|
|
23
|
-
import { readAndResolveBundleEnv } from '../secrets
|
|
23
|
+
import { readAndResolveBundleEnv } from '../secrets-client.js';
|
|
24
24
|
const INTERACTIONS_URL = 'https://generativelanguage.googleapis.com/v1beta/interactions';
|
|
25
25
|
const DEFAULT_MODEL = 'antigravity-preview-05-2026';
|
|
26
26
|
const KEY_NAMES = ['GEMINI_API_KEY', 'GOOGLE_API_KEY'];
|
|
@@ -62,7 +62,7 @@ export class AntigravityCloudProvider {
|
|
|
62
62
|
return KEY_NAMES.some((k) => Boolean(process.env[k]));
|
|
63
63
|
}
|
|
64
64
|
/** Resolve the Gemini API key from the configured bundle or the environment. */
|
|
65
|
-
resolveApiKey() {
|
|
65
|
+
async resolveApiKey() {
|
|
66
66
|
if (this.secretsBundle) {
|
|
67
67
|
try {
|
|
68
68
|
// Cloud dispatch resolves the key on its own (no human at a sheet), so the
|
|
@@ -71,7 +71,7 @@ export class AntigravityCloudProvider {
|
|
|
71
71
|
// catch below re-raises verbatim — dispatch genuinely needs the key, so it
|
|
72
72
|
// fails LOUD with the unlock hint rather than swallowing it into a wrong
|
|
73
73
|
// path. A `never`/no-ACL or broker-held bundle resolves silently.
|
|
74
|
-
const { env } = readAndResolveBundleEnv(this.secretsBundle, { caller: 'cloud:antigravity', agentOnly: true });
|
|
74
|
+
const { env } = await readAndResolveBundleEnv(this.secretsBundle, { caller: 'cloud:antigravity', agentOnly: true });
|
|
75
75
|
for (const k of KEY_NAMES) {
|
|
76
76
|
if (env[k])
|
|
77
77
|
return env[k];
|
|
@@ -112,7 +112,7 @@ export class AntigravityCloudProvider {
|
|
|
112
112
|
throw new Error(`Antigravity cloud is a raw sandbox with no GitHub repo → PR flow. Got repo(s): ${repos.join(', ')}. ` +
|
|
113
113
|
`Use --provider rush for repo-backed dispatch.`);
|
|
114
114
|
}
|
|
115
|
-
const apiKey = this.resolveApiKey();
|
|
115
|
+
const apiKey = await this.resolveApiKey();
|
|
116
116
|
const model = options.model ?? this.model;
|
|
117
117
|
const body = buildInteractionBody(options.prompt, model);
|
|
118
118
|
let resp;
|
package/dist/lib/cloud/cursor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Cursor Cloud Agents provider backed by the public v1 REST API. */
|
|
2
2
|
import { normalizeProviderStatus, resolveDispatchRepos } from './types.js';
|
|
3
|
-
import { readAndResolveBundleEnv } from '../secrets
|
|
3
|
+
import { readAndResolveBundleEnv } from '../secrets-client.js';
|
|
4
4
|
export const CURSOR_API_BASE_URL = 'https://api.cursor.com/v1';
|
|
5
5
|
const KEY_NAME = 'CURSOR_API_KEY';
|
|
6
6
|
/** Translate unified dispatch options into Cursor's documented v1 create shape. */
|
|
@@ -111,10 +111,10 @@ export class CursorCloudProvider {
|
|
|
111
111
|
hasKeySource() {
|
|
112
112
|
return Boolean(this.secretsBundle || process.env[KEY_NAME]);
|
|
113
113
|
}
|
|
114
|
-
resolveApiKey() {
|
|
114
|
+
async resolveApiKey() {
|
|
115
115
|
if (this.secretsBundle) {
|
|
116
116
|
try {
|
|
117
|
-
const { env } = readAndResolveBundleEnv(this.secretsBundle, { caller: 'cloud:cursor', agentOnly: true });
|
|
117
|
+
const { env } = await readAndResolveBundleEnv(this.secretsBundle, { caller: 'cloud:cursor', agentOnly: true });
|
|
118
118
|
if (env[KEY_NAME])
|
|
119
119
|
return env[KEY_NAME];
|
|
120
120
|
throw new Error(`Secrets bundle '${this.secretsBundle}' has no ${KEY_NAME}. Add one: agents secrets add ${this.secretsBundle} ${KEY_NAME}`);
|
|
@@ -143,7 +143,7 @@ export class CursorCloudProvider {
|
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
async request(path, init) {
|
|
146
|
-
const apiKey = this.resolveApiKey();
|
|
146
|
+
const apiKey = await this.resolveApiKey();
|
|
147
147
|
let response;
|
|
148
148
|
try {
|
|
149
149
|
response = await fetch(`${CURSOR_API_BASE_URL}${path}`, {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* needs a provider token (e.g. HCLOUD_TOKEN) in the environment. We inject it
|
|
12
12
|
* from a secrets bundle when one is configured (see `crabboxEnv`).
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import type { SecretsBundle } from '../secrets-types.js';
|
|
15
15
|
/** A crabbox machine as reported by `crabbox list --json`. */
|
|
16
16
|
export interface CrabboxBox {
|
|
17
17
|
/** Provider machine name, e.g. `crabbox-blue-hermit-1039689b`. */
|
package/dist/lib/crabbox/cli.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* from a secrets bundle when one is configured (see `crabboxEnv`).
|
|
13
13
|
*/
|
|
14
14
|
import { spawn, spawnSync } from 'child_process';
|
|
15
|
-
import {
|
|
15
|
+
import { readAndResolveBundleEnvSync, listBundlesSync, bundleExistsSync } from '../secrets-client.js';
|
|
16
16
|
import { readMeta, writeMeta } from '../state.js';
|
|
17
17
|
import { DEFAULT_CRABBOX_PROFILE } from './config.js';
|
|
18
18
|
/** Locate the crabbox binary, or throw an actionable error. */
|
|
@@ -59,7 +59,7 @@ function resolveTailscaleBundleMemo() {
|
|
|
59
59
|
if (!tailscaleBundleMemo) {
|
|
60
60
|
let value;
|
|
61
61
|
try {
|
|
62
|
-
value = pickTailscaleBundleFromList(
|
|
62
|
+
value = pickTailscaleBundleFromList(listBundlesSync());
|
|
63
63
|
}
|
|
64
64
|
catch {
|
|
65
65
|
/* secrets unreadable — no auto-detect */
|
|
@@ -88,7 +88,7 @@ function resolveTailscaleKeyValueMemo(ts) {
|
|
|
88
88
|
if (!tailscaleValueMemo) {
|
|
89
89
|
let value;
|
|
90
90
|
try {
|
|
91
|
-
const { env } =
|
|
91
|
+
const { env } = readAndResolveBundleEnvSync(ts.name, {
|
|
92
92
|
caller: 'agents run --lease (crabbox tailscale)',
|
|
93
93
|
keys: [ts.key],
|
|
94
94
|
agentOnly: true,
|
|
@@ -129,14 +129,14 @@ export function resolveLeaseBundle() {
|
|
|
129
129
|
return { name: env };
|
|
130
130
|
try {
|
|
131
131
|
const configured = readMeta().lease?.secretsBundle;
|
|
132
|
-
if (configured &&
|
|
132
|
+
if (configured && bundleExistsSync(configured))
|
|
133
133
|
return { name: configured };
|
|
134
134
|
}
|
|
135
135
|
catch {
|
|
136
136
|
/* config unreadable — fall through to auto-detect */
|
|
137
137
|
}
|
|
138
138
|
try {
|
|
139
|
-
const bundles =
|
|
139
|
+
const bundles = listBundlesSync();
|
|
140
140
|
const name = pickLeaseBundleFromList(bundles);
|
|
141
141
|
if (name) {
|
|
142
142
|
const b = bundles.find((x) => x.name === name);
|
|
@@ -187,7 +187,7 @@ function resolveLeaseEnvMemo(explicitBundle) {
|
|
|
187
187
|
// privilege; an unrelated bundle can't leak its other secrets into crabbox).
|
|
188
188
|
// An explicitly-named bundle (env/config or `opts.secretsBundle`) injects
|
|
189
189
|
// whole — the user chose it. Same resolver `agents secrets exec` uses.
|
|
190
|
-
const { env } =
|
|
190
|
+
const { env } = readAndResolveBundleEnvSync(resolved.name, {
|
|
191
191
|
caller: 'agents run --lease (crabbox)',
|
|
192
192
|
keys: resolved.keys,
|
|
193
193
|
// --lease is headless by contract and a locked bundle must fail loud with
|
|
@@ -78,9 +78,9 @@ export declare const CLAUDE_TOKEN_REMOTE = ".claude/.credentials.json";
|
|
|
78
78
|
* The RAW wrapped Claude credential payload (`{"claudeAiOauth":{…}}`) to write to
|
|
79
79
|
* the box's `~/.claude/.credentials.json`, or null if no signed-in token is found.
|
|
80
80
|
*
|
|
81
|
-
* On macOS the token is in the login Keychain, read SILENTLY via
|
|
82
|
-
* `
|
|
83
|
-
* no Touch ID). A default native install uses the bare `Claude Code-credentials`
|
|
81
|
+
* On macOS the token is in the login Keychain, read SILENTLY via the standalone
|
|
82
|
+
* `secrets` client's `getKeychainTokenSync` (the `/usr/bin/security … -w` path —
|
|
83
|
+
* Claude's item trusts it, no Touch ID). A default native install uses the bare `Claude Code-credentials`
|
|
84
84
|
* service; an agents-cli managed install (where `~/.claude` symlinks into a
|
|
85
85
|
* versioned home) uses a hash-suffixed service, so we try the bare service first,
|
|
86
86
|
* then enumerate installed version homes (preferring the account whose email
|
|
@@ -16,7 +16,7 @@ import * as os from 'os';
|
|
|
16
16
|
import * as path from 'path';
|
|
17
17
|
import * as fs from 'fs';
|
|
18
18
|
import { getAccountInfo } from '../agents.js';
|
|
19
|
-
import {
|
|
19
|
+
import { getKeychainTokenSync } from '../secrets-client.js';
|
|
20
20
|
import { getClaudeKeychainService } from '../accounting/usage.js';
|
|
21
21
|
import { listInstalledVersions, getVersionHomePath } from '../installations/versions.js';
|
|
22
22
|
export const LEASE_RUNTIMES = [
|
|
@@ -185,9 +185,9 @@ function isClaudeCredentialsBlob(s) {
|
|
|
185
185
|
* The RAW wrapped Claude credential payload (`{"claudeAiOauth":{…}}`) to write to
|
|
186
186
|
* the box's `~/.claude/.credentials.json`, or null if no signed-in token is found.
|
|
187
187
|
*
|
|
188
|
-
* On macOS the token is in the login Keychain, read SILENTLY via
|
|
189
|
-
* `
|
|
190
|
-
* no Touch ID). A default native install uses the bare `Claude Code-credentials`
|
|
188
|
+
* On macOS the token is in the login Keychain, read SILENTLY via the standalone
|
|
189
|
+
* `secrets` client's `getKeychainTokenSync` (the `/usr/bin/security … -w` path —
|
|
190
|
+
* Claude's item trusts it, no Touch ID). A default native install uses the bare `Claude Code-credentials`
|
|
191
191
|
* service; an agents-cli managed install (where `~/.claude` symlinks into a
|
|
192
192
|
* versioned home) uses a hash-suffixed service, so we try the bare service first,
|
|
193
193
|
* then enumerate installed version homes (preferring the account whose email
|
|
@@ -203,7 +203,7 @@ function isClaudeCredentialsBlob(s) {
|
|
|
203
203
|
* real Keychain.
|
|
204
204
|
*/
|
|
205
205
|
export async function resolveClaudeCredentialsBlob(opts) {
|
|
206
|
-
const readItem = opts?.readItem ??
|
|
206
|
+
const readItem = opts?.readItem ?? getKeychainTokenSync;
|
|
207
207
|
const service = opts?.service ?? getClaudeKeychainService;
|
|
208
208
|
const listVersions = opts?.listVersions ?? (() => listInstalledVersions('claude'));
|
|
209
209
|
const versionHome = opts?.versionHome ?? ((v) => getVersionHomePath('claude', v));
|
|
@@ -21,10 +21,23 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { BasePeriodicService, type DaemonContext } from './service.js';
|
|
23
23
|
import type { DaemonServiceId } from '../daemon-services.js';
|
|
24
|
+
import type { AuthProbeRow } from '../auth-health.js';
|
|
24
25
|
/** Usage cache refresh cadence. */
|
|
25
26
|
export declare const USAGE_STATE_TICK_MS = 60000;
|
|
26
27
|
/** Fleet-auth refresh cadence — deliberately slower (rate-limited endpoint). */
|
|
27
28
|
export declare const AUTH_STATE_TICK_MS: number;
|
|
29
|
+
type AccountAuthRefresh = (signal: AbortSignal) => Promise<AuthProbeRow[] | void>;
|
|
30
|
+
type AccountTransitionNotifier = (transition: DeadAccountTransition) => Promise<void>;
|
|
31
|
+
export interface DeadAccountTransition {
|
|
32
|
+
agent: AuthProbeRow['agent'];
|
|
33
|
+
/** Display label (email / account name) — presentation only, never a key. */
|
|
34
|
+
account: string;
|
|
35
|
+
verdict: 'expired' | 'revoked';
|
|
36
|
+
}
|
|
37
|
+
interface AccountAuthServiceOptions {
|
|
38
|
+
stateFile?: string;
|
|
39
|
+
notify?: AccountTransitionNotifier;
|
|
40
|
+
}
|
|
28
41
|
/**
|
|
29
42
|
* Refresh the usage cache the `agents run` router reads. Independent circuit
|
|
30
43
|
* breaker from auth — a persistently-failing usage endpoint parks only this.
|
|
@@ -49,8 +62,31 @@ export declare class AccountAuthService extends BasePeriodicService {
|
|
|
49
62
|
readonly intervalMs: number;
|
|
50
63
|
readonly deadlineMs: number;
|
|
51
64
|
private readonly refresh;
|
|
52
|
-
|
|
65
|
+
private readonly stateFile;
|
|
66
|
+
private readonly notify;
|
|
67
|
+
constructor(refresh?: AccountAuthRefresh, options?: AccountAuthServiceOptions);
|
|
53
68
|
protected onStart(): Promise<void>;
|
|
54
69
|
protected onStop(): Promise<void>;
|
|
55
70
|
protected onTick(_ctx: DaemonContext, signal: AbortSignal): Promise<void>;
|
|
56
71
|
}
|
|
72
|
+
export declare function publishAccountDaemonStateRows(rows: readonly AuthProbeRow[], userAgentsDir?: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Detect live → expired/revoked transitions and deliver one owner-important
|
|
75
|
+
* feed post each, through a persisted outbox:
|
|
76
|
+
*
|
|
77
|
+
* 1. Newly-dead accounts are ENQUEUED into `pending` and the state is written
|
|
78
|
+
* BEFORE any delivery is attempted — a crash or a failed sink can never
|
|
79
|
+
* strand a transition in the already-recorded `entries` with no retry.
|
|
80
|
+
* 2. Every pending item (fresh and retried) is delivered; an item leaves
|
|
81
|
+
* `pending` only after its notifier resolves, and the state is written
|
|
82
|
+
* again. A crash in the narrow window between a successful post and that
|
|
83
|
+
* second write re-sends once — at-least-once, never zero.
|
|
84
|
+
*
|
|
85
|
+
* Delivery failures keep their outbox entries AND throw after the state is
|
|
86
|
+
* durable, so the supervisor's circuit breaker still sees the unhealthy tick.
|
|
87
|
+
*/
|
|
88
|
+
export declare function processAccountAuthTransitions(rows: readonly AuthProbeRow[], options?: {
|
|
89
|
+
stateFile?: string;
|
|
90
|
+
notify?: AccountTransitionNotifier;
|
|
91
|
+
}): Promise<DeadAccountTransition[]>;
|
|
92
|
+
export {};
|