@phnx-labs/agents-cli 1.20.20 → 1.20.21
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/lib/daemon.js
CHANGED
|
@@ -265,6 +265,9 @@ export async function runDaemon() {
|
|
|
265
265
|
scheduler.reloadAll();
|
|
266
266
|
const reloaded = scheduler.listScheduled();
|
|
267
267
|
log('INFO', `Reloaded ${reloaded.length} jobs`);
|
|
268
|
+
// Drop the memoized R2 config so rotated/added sync credentials are re-read
|
|
269
|
+
// on the next cycle instead of waiting for a restart.
|
|
270
|
+
void import('./session/sync/config.js').then(m => m.clearR2ConfigCache());
|
|
268
271
|
};
|
|
269
272
|
const handleShutdown = async () => {
|
|
270
273
|
log('INFO', 'Daemon shutting down');
|
|
@@ -13,14 +13,26 @@ export interface R2Config {
|
|
|
13
13
|
/** S3-compatible endpoint for the account (no bucket, no trailing slash). */
|
|
14
14
|
endpoint: string;
|
|
15
15
|
}
|
|
16
|
+
/** Window after a prompt-bearing resolution failure during which we skip
|
|
17
|
+
* re-attempting (and thus re-prompting). SIGHUP / restart bypasses it. */
|
|
18
|
+
export declare const RESOLVE_RETRY_COOLDOWN_MS: number;
|
|
19
|
+
/** Drop the cached resolution so the next call reads the bundle fresh. Called on
|
|
20
|
+
* daemon SIGHUP (to pick up rotated credentials) and between tests. */
|
|
21
|
+
export declare function clearR2ConfigCache(): void;
|
|
16
22
|
/**
|
|
17
|
-
* Resolve R2 credentials
|
|
18
|
-
*
|
|
19
|
-
*
|
|
23
|
+
* Resolve R2 credentials, reading the keychain at most once per process. The
|
|
24
|
+
* first call reads (and may prompt for Touch ID); every later call returns the
|
|
25
|
+
* memoized result. Throws if the bundle/keys are missing — failures are not
|
|
26
|
+
* memoized, but see isSyncConfigured for the re-prompt cooldown.
|
|
20
27
|
*/
|
|
21
28
|
export declare function loadR2Config(): R2Config;
|
|
22
|
-
/**
|
|
23
|
-
|
|
29
|
+
/**
|
|
30
|
+
* True when the sync bundle exists and resolves, without throwing. After a
|
|
31
|
+
* prompt-bearing failure (e.g. a cancelled Touch ID) it returns false without
|
|
32
|
+
* re-reading the keychain for RESOLVE_RETRY_COOLDOWN_MS, so a dismissed prompt
|
|
33
|
+
* does not re-storm every cycle. `now` is injectable for tests.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isSyncConfigured(now?: number): boolean;
|
|
24
36
|
/**
|
|
25
37
|
* This machine's stable, human-readable id, used as its R2 prefix and mirror
|
|
26
38
|
* directory name. Tailnet hostnames (zion, yosemite-s0, mac-mini) are already
|
|
@@ -12,7 +12,7 @@ export const SYNC_BUNDLE = 'r2.backups';
|
|
|
12
12
|
* actionable error if the bundle or any key is missing — sync cannot proceed
|
|
13
13
|
* without real credentials (no silent fallback).
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
function resolveR2Config() {
|
|
16
16
|
const { env } = readAndResolveBundleEnv(SYNC_BUNDLE, { caller: 'sessions-sync' });
|
|
17
17
|
const accountId = env.R2_ACCOUNT_ID?.trim();
|
|
18
18
|
const bucket = env.R2_BUCKET_NAME?.trim();
|
|
@@ -36,13 +36,60 @@ export function loadR2Config() {
|
|
|
36
36
|
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// ── Resolution cache ────────────────────────────────────────────────────────
|
|
40
|
+
// The daemon calls isSyncConfigured() + syncSessions() every ~90s, and each used
|
|
41
|
+
// to trigger a fresh read of the biometry-gated `r2.backups` keychain items —
|
|
42
|
+
// one Touch ID prompt per gated item, every cycle, forever. We instead resolve
|
|
43
|
+
// at most once per process: a success is memoized for the process lifetime
|
|
44
|
+
// (cleared on daemon SIGHUP via clearR2ConfigCache), so subsequent cycles never
|
|
45
|
+
// touch the keychain again. A *prompt-bearing* failure (cancelled Touch ID, etc.)
|
|
46
|
+
// starts a cooldown so a dismissed prompt is not re-issued every cycle. A simply
|
|
47
|
+
// absent bundle never prompts, so it is re-checked each cycle (fast pickup when
|
|
48
|
+
// the user later adds credentials).
|
|
49
|
+
let cachedConfig = null;
|
|
50
|
+
let lastPromptFailureAt = 0;
|
|
51
|
+
/** Window after a prompt-bearing resolution failure during which we skip
|
|
52
|
+
* re-attempting (and thus re-prompting). SIGHUP / restart bypasses it. */
|
|
53
|
+
export const RESOLVE_RETRY_COOLDOWN_MS = 30 * 60 * 1000; // 30 minutes
|
|
54
|
+
/** Drop the cached resolution so the next call reads the bundle fresh. Called on
|
|
55
|
+
* daemon SIGHUP (to pick up rotated credentials) and between tests. */
|
|
56
|
+
export function clearR2ConfigCache() {
|
|
57
|
+
cachedConfig = null;
|
|
58
|
+
lastPromptFailureAt = 0;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolve R2 credentials, reading the keychain at most once per process. The
|
|
62
|
+
* first call reads (and may prompt for Touch ID); every later call returns the
|
|
63
|
+
* memoized result. Throws if the bundle/keys are missing — failures are not
|
|
64
|
+
* memoized, but see isSyncConfigured for the re-prompt cooldown.
|
|
65
|
+
*/
|
|
66
|
+
export function loadR2Config() {
|
|
67
|
+
if (cachedConfig)
|
|
68
|
+
return cachedConfig;
|
|
69
|
+
cachedConfig = resolveR2Config();
|
|
70
|
+
return cachedConfig;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* True when the sync bundle exists and resolves, without throwing. After a
|
|
74
|
+
* prompt-bearing failure (e.g. a cancelled Touch ID) it returns false without
|
|
75
|
+
* re-reading the keychain for RESOLVE_RETRY_COOLDOWN_MS, so a dismissed prompt
|
|
76
|
+
* does not re-storm every cycle. `now` is injectable for tests.
|
|
77
|
+
*/
|
|
78
|
+
export function isSyncConfigured(now = Date.now()) {
|
|
79
|
+
if (cachedConfig)
|
|
80
|
+
return true;
|
|
81
|
+
if (lastPromptFailureAt && now - lastPromptFailureAt < RESOLVE_RETRY_COOLDOWN_MS)
|
|
82
|
+
return false;
|
|
41
83
|
try {
|
|
42
84
|
loadR2Config();
|
|
43
85
|
return true;
|
|
44
86
|
}
|
|
45
|
-
catch {
|
|
87
|
+
catch (err) {
|
|
88
|
+
// A missing bundle never prompts, so keep re-checking it each cycle (so a
|
|
89
|
+
// later `agents secrets add` is picked up quickly). Any other failure may
|
|
90
|
+
// have cost a prompt (cancelled Touch ID, keychain error) — back off.
|
|
91
|
+
if (!/not found/i.test(err.message))
|
|
92
|
+
lastPromptFailureAt = now;
|
|
46
93
|
return false;
|
|
47
94
|
}
|
|
48
95
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.21",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|