@ouro.bot/cli 0.1.0-alpha.447 → 0.1.0-alpha.449
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/changelog.json +16 -0
- package/dist/heart/daemon/agent-config-check.js +20 -14
- package/dist/heart/daemon/cli-exec.js +22 -5
- package/dist/heart/daemon/provider-ping-progress.js +2 -0
- package/dist/heart/provider-binding-resolver.js +16 -0
- package/dist/heart/provider-credentials.js +9 -1
- package/dist/heart/provider-ping.js +32 -9
- package/dist/heart/provider-visibility.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ Task docs do not live in this repo anymore. Planning and doing docs live in the
|
|
|
99
99
|
- Vault unlock material is local machine state. Prefer macOS Keychain, Windows DPAPI, or Linux Secret Service; plaintext fallback is allowed only by explicit human choice.
|
|
100
100
|
- New vault unlock secrets are confirmed before use and rejected if they do not meet the minimum strength requirements.
|
|
101
101
|
- Provider and runtime credentials are loaded into process memory at startup/auth/unlock/refresh and reused. The remote vault is not queried for every model or sense request.
|
|
102
|
-
- Human TTY commands share one CLI surface family: bare `ouro` opens the home deck, `ouro up` uses the boot checklist, `ouro connect`/`ouro auth verify`/`ouro repair`
|
|
102
|
+
- Human TTY commands share one CLI surface family: bare `ouro` opens the home deck, `ouro up` uses the boot checklist, `ouro connect`/`ouro auth verify`/`ouro repair` agree on provider and vault truth, and `ouro help`/`ouro whoami`/`ouro versions`/`ouro hatch` render through the same Ouro-branded wizard/guide language instead of raw transcript walls. Orientation commands such as root `ouro connect` may use shorter live probes, while startup and verification commands own durable readiness updates.
|
|
103
103
|
- Human-facing CLI commands that can wait on browser auth, vault IO, daemon startup, daemon restart, provider checks, or connector setup use a shared progress checklist. If a cursor may blink for more than a few seconds, the command should print or animate the current step instead of going quiet.
|
|
104
104
|
- CLI commands that mutate bundle config, such as vault setup or `ouro connect bluebubbles`, run bundle sync after the change when `sync.enabled` is true and report a compact `bundle sync:` line.
|
|
105
105
|
- The daemon discovers bundles dynamically from `~/AgentBundles`.
|
package/changelog.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.449",
|
|
6
|
+
"changes": [
|
|
7
|
+
"`ouro connect` now uses a bounded live provider probe before opening the connection menu: it still checks the real selected providers, but it makes one orientation attempt with a 5-second hard timeout instead of spending the full startup retry budget before the human can choose a setup path.",
|
|
8
|
+
"Provider ping timeouts are now hard timeouts even when an SDK ignores the abort signal. The failing attempt is classified through the same shared retry machinery and reports a clear `provider ping timed out after <ms>ms` message instead of leaving the CLI stuck behind a blinking cursor.",
|
|
9
|
+
"Root connect provider checks no longer overwrite durable provider readiness. A quick menu probe can show `needs attention` for the current screen, while `ouro up`, `ouro check`, auth verification, and chat startup remain the flows that record lasting ready/failed provider state."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.1.0-alpha.448",
|
|
14
|
+
"changes": [
|
|
15
|
+
"`ouro status` and agent prompt provider visibility now distinguish \"the daemon has not loaded provider credentials in this process\" from \"the agent vault is missing credentials.\" A saved live check that passed stays ready instead of being downgraded to stale/missing just because status rendering is running in a fresh daemon process.",
|
|
16
|
+
"Provider visibility now carries a safe `not-loaded` credential state and renders it as `checked previously`, preserving the last live-check result without reading or printing secrets during ordinary status/prompt rendering.",
|
|
17
|
+
"Regression coverage locks the installed-product failure shape: an empty in-process provider cache plus ready provider state no longer produces misleading auth repair guidance."
|
|
18
|
+
]
|
|
19
|
+
},
|
|
4
20
|
{
|
|
5
21
|
"version": "0.1.0-alpha.447",
|
|
6
22
|
"changes": [
|
|
@@ -389,6 +389,7 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
|
|
|
389
389
|
return { ok: true };
|
|
390
390
|
deps.onProgress?.(selectedProviderPlan(agentName, stateResult.state));
|
|
391
391
|
const ping = deps.pingProvider ?? (await Promise.resolve().then(() => __importStar(require("../provider-ping")))).pingProvider;
|
|
392
|
+
const shouldRecordReadiness = deps.recordReadiness ?? true;
|
|
392
393
|
const providers = selectedProvidersForState(stateResult.state);
|
|
393
394
|
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName, {
|
|
394
395
|
...(deps.onProgress ? { onProgress: mapVaultRefreshProgress(agentName, deps.onProgress) } : {}),
|
|
@@ -430,6 +431,7 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
|
|
|
430
431
|
const pingResults = await Promise.all(groups.map(async (group) => {
|
|
431
432
|
const result = await ping(group.provider, providerCredentialConfig(group.record), {
|
|
432
433
|
model: group.model,
|
|
434
|
+
...(deps.providerPingOptions ?? {}),
|
|
433
435
|
...(deps.onProgress
|
|
434
436
|
? (0, provider_ping_progress_1.createProviderPingProgressReporter)({
|
|
435
437
|
provider: group.provider,
|
|
@@ -443,29 +445,33 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
|
|
|
443
445
|
let firstFailure = null;
|
|
444
446
|
for (const { group, result } of pingResults) {
|
|
445
447
|
if (!result.ok) {
|
|
448
|
+
if (shouldRecordReadiness) {
|
|
449
|
+
for (const lane of group.lanes) {
|
|
450
|
+
writeLaneReadiness({
|
|
451
|
+
agentRoot: stateResult.agentRoot,
|
|
452
|
+
state: stateResult.state,
|
|
453
|
+
lane,
|
|
454
|
+
status: "failed",
|
|
455
|
+
credentialRevision: group.record.revision,
|
|
456
|
+
error: result.message,
|
|
457
|
+
attempts: pingAttemptCount(result),
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
firstFailure ??= failedPingResult(agentName, group.lanes[0], group.provider, group.model, result);
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (shouldRecordReadiness) {
|
|
446
465
|
for (const lane of group.lanes) {
|
|
447
466
|
writeLaneReadiness({
|
|
448
467
|
agentRoot: stateResult.agentRoot,
|
|
449
468
|
state: stateResult.state,
|
|
450
469
|
lane,
|
|
451
|
-
status: "
|
|
470
|
+
status: "ready",
|
|
452
471
|
credentialRevision: group.record.revision,
|
|
453
|
-
error: result.message,
|
|
454
472
|
attempts: pingAttemptCount(result),
|
|
455
473
|
});
|
|
456
474
|
}
|
|
457
|
-
firstFailure ??= failedPingResult(agentName, group.lanes[0], group.provider, group.model, result);
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
for (const lane of group.lanes) {
|
|
461
|
-
writeLaneReadiness({
|
|
462
|
-
agentRoot: stateResult.agentRoot,
|
|
463
|
-
state: stateResult.state,
|
|
464
|
-
lane,
|
|
465
|
-
status: "ready",
|
|
466
|
-
credentialRevision: group.record.revision,
|
|
467
|
-
attempts: pingAttemptCount(result),
|
|
468
|
-
});
|
|
469
475
|
}
|
|
470
476
|
}
|
|
471
477
|
if (firstFailure)
|
|
@@ -106,6 +106,10 @@ const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
|
|
|
106
106
|
const DEFAULT_DAEMON_STARTUP_STABILITY_WINDOW_MS = 1_500;
|
|
107
107
|
const DEFAULT_DAEMON_STARTUP_RETRY_LIMIT = 1;
|
|
108
108
|
const DEFAULT_DAEMON_STARTUP_LOG_LINES = 10;
|
|
109
|
+
const CONNECT_PROVIDER_ORIENTATION_PING_OPTIONS = {
|
|
110
|
+
attemptPolicy: { maxAttempts: 1, baseDelayMs: 0, backoffMultiplier: 2 },
|
|
111
|
+
timeoutMs: 5_000,
|
|
112
|
+
};
|
|
109
113
|
function summarizeCliUpdateCheckStatus(error, timedOut = false) {
|
|
110
114
|
const normalized = error.trim().toLowerCase();
|
|
111
115
|
if (timedOut || normalized.includes("timed out") || normalized.includes("abort")) {
|
|
@@ -218,13 +222,20 @@ async function checkAgentProviders(deps, agentsOverride, onProgress) {
|
|
|
218
222
|
}
|
|
219
223
|
return degraded;
|
|
220
224
|
}
|
|
221
|
-
async function checkAgentProviderHealth(agentName, bundlesRoot, deps, onProgress) {
|
|
225
|
+
async function checkAgentProviderHealth(agentName, bundlesRoot, deps, onProgress, options = {}) {
|
|
222
226
|
const liveDeps = {};
|
|
223
227
|
if (deps.homeDir)
|
|
224
228
|
liveDeps.homeDir = deps.homeDir;
|
|
225
229
|
if (onProgress)
|
|
226
230
|
liveDeps.onProgress = onProgress;
|
|
227
|
-
if (
|
|
231
|
+
if (options.providerPingOptions)
|
|
232
|
+
liveDeps.providerPingOptions = options.providerPingOptions;
|
|
233
|
+
if (options.recordReadiness !== undefined)
|
|
234
|
+
liveDeps.recordReadiness = options.recordReadiness;
|
|
235
|
+
if (liveDeps.homeDir ||
|
|
236
|
+
liveDeps.onProgress ||
|
|
237
|
+
liveDeps.providerPingOptions ||
|
|
238
|
+
liveDeps.recordReadiness !== undefined) {
|
|
228
239
|
return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot, liveDeps);
|
|
229
240
|
}
|
|
230
241
|
return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot);
|
|
@@ -2040,7 +2051,10 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2040
2051
|
let providerHealth;
|
|
2041
2052
|
try {
|
|
2042
2053
|
onProgress?.("checking selected providers");
|
|
2043
|
-
providerHealth = await checkAgentProviderHealth(agent, bundlesRoot, deps, onProgress
|
|
2054
|
+
providerHealth = await checkAgentProviderHealth(agent, bundlesRoot, deps, onProgress, {
|
|
2055
|
+
providerPingOptions: CONNECT_PROVIDER_ORIENTATION_PING_OPTIONS,
|
|
2056
|
+
recordReadiness: false,
|
|
2057
|
+
});
|
|
2044
2058
|
}
|
|
2045
2059
|
catch (error) {
|
|
2046
2060
|
providerHealth = {
|
|
@@ -2966,7 +2980,7 @@ async function executeProviderCheck(command, deps) {
|
|
|
2966
2980
|
throw error;
|
|
2967
2981
|
}
|
|
2968
2982
|
}
|
|
2969
|
-
function renderProviderCredentialLine(credential) {
|
|
2983
|
+
function renderProviderCredentialLine(agentName, credential) {
|
|
2970
2984
|
if (credential.status === "present") {
|
|
2971
2985
|
const credentialFields = credential.credentialFields.length > 0 ? ` credentials: ${credential.credentialFields.join(", ")}` : " credentials: none";
|
|
2972
2986
|
const configFields = credential.configFields.length > 0 ? ` config: ${credential.configFields.join(", ")}` : " config: none";
|
|
@@ -2975,6 +2989,9 @@ function renderProviderCredentialLine(credential) {
|
|
|
2975
2989
|
if (credential.status === "invalid-pool") {
|
|
2976
2990
|
return `credentials: vault unavailable (${credential.error}); repair: ${credential.repair.command}`;
|
|
2977
2991
|
}
|
|
2992
|
+
if (credential.status === "not-loaded") {
|
|
2993
|
+
return `credentials: not loaded in this process; run \`ouro provider refresh --agent ${agentName}\` to read the vault now`;
|
|
2994
|
+
}
|
|
2978
2995
|
return `credentials: missing; repair: ${credential.repair.command}`;
|
|
2979
2996
|
}
|
|
2980
2997
|
async function executeProviderStatus(command, deps) {
|
|
@@ -3010,7 +3027,7 @@ async function executeProviderStatus(command, deps) {
|
|
|
3010
3027
|
const binding = resolved.binding;
|
|
3011
3028
|
lines.push(` ${lane}: ${binding.provider} / ${binding.model} (${binding.source})`);
|
|
3012
3029
|
lines.push(` readiness: ${binding.readiness.status}${binding.readiness.error ? ` (${binding.readiness.error})` : ""}`);
|
|
3013
|
-
lines.push(` ${renderProviderCredentialLine(binding.credential)}`);
|
|
3030
|
+
lines.push(` ${renderProviderCredentialLine(command.agent, binding.credential)}`);
|
|
3014
3031
|
for (const warning of binding.warnings) {
|
|
3015
3032
|
lines.push(` warning: ${warning.message}`);
|
|
3016
3033
|
}
|
|
@@ -34,6 +34,8 @@ function providerRetryTiming(delayMs) {
|
|
|
34
34
|
}
|
|
35
35
|
function formatProviderAttemptProgress(context, attempt, maxAttempts) {
|
|
36
36
|
const prefix = context.subject ? `${context.subject}: ` : "";
|
|
37
|
+
if (maxAttempts <= 1)
|
|
38
|
+
return `${prefix}checking ${formatProviderPingLabel(context)}...`;
|
|
37
39
|
return `${prefix}checking ${formatProviderPingLabel(context)} (attempt ${attempt} of ${maxAttempts})...`;
|
|
38
40
|
}
|
|
39
41
|
function formatProviderRetryProgress(context, record, maxAttempts) {
|
|
@@ -89,6 +89,16 @@ function resolveCredential(poolResult, provider, agentName) {
|
|
|
89
89
|
warnings: [missingCredentialWarning(provider)],
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
+
if ((0, provider_credentials_1.isProviderCredentialPoolNotLoaded)(poolResult)) {
|
|
93
|
+
return {
|
|
94
|
+
credential: {
|
|
95
|
+
status: "not-loaded",
|
|
96
|
+
provider,
|
|
97
|
+
poolPath: poolResult.poolPath,
|
|
98
|
+
},
|
|
99
|
+
warnings: [],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
92
102
|
if (poolResult.reason === "invalid" || poolResult.reason === "unavailable") {
|
|
93
103
|
return {
|
|
94
104
|
credential: {
|
|
@@ -130,6 +140,9 @@ function staleReadiness(readiness, reason) {
|
|
|
130
140
|
}
|
|
131
141
|
function resolveReadiness(input) {
|
|
132
142
|
if (!input.readiness) {
|
|
143
|
+
if (input.credential.status === "not-loaded") {
|
|
144
|
+
return { readiness: { status: "unknown" }, warnings: [] };
|
|
145
|
+
}
|
|
133
146
|
if (input.credential.status === "missing") {
|
|
134
147
|
return { readiness: { status: "unknown", reason: "credential-missing" }, warnings: [] };
|
|
135
148
|
}
|
|
@@ -170,6 +183,9 @@ function resolveReadiness(input) {
|
|
|
170
183
|
warnings: [],
|
|
171
184
|
};
|
|
172
185
|
}
|
|
186
|
+
if (input.credential.status === "not-loaded") {
|
|
187
|
+
return { readiness: readinessFromState(input.readiness), warnings: [] };
|
|
188
|
+
}
|
|
173
189
|
return { readiness: readinessFromState(input.readiness), warnings: [] };
|
|
174
190
|
}
|
|
175
191
|
function resolveEffectiveProviderBinding(input) {
|
|
@@ -33,10 +33,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PROVIDER_CREDENTIAL_POOL_NOT_LOADED_ERROR = void 0;
|
|
36
37
|
exports.splitProviderCredentialFields = splitProviderCredentialFields;
|
|
37
38
|
exports.providerCredentialsVaultPath = providerCredentialsVaultPath;
|
|
38
39
|
exports.providerCredentialItemName = providerCredentialItemName;
|
|
39
40
|
exports.providerCredentialMachineHomeDir = providerCredentialMachineHomeDir;
|
|
41
|
+
exports.isProviderCredentialPoolNotLoaded = isProviderCredentialPoolNotLoaded;
|
|
40
42
|
exports.readProviderCredentialPool = readProviderCredentialPool;
|
|
41
43
|
exports.refreshProviderCredentialPool = refreshProviderCredentialPool;
|
|
42
44
|
exports.createProviderCredentialRecord = createProviderCredentialRecord;
|
|
@@ -54,6 +56,7 @@ const credential_access_1 = require("../repertoire/credential-access");
|
|
|
54
56
|
const VALID_PROVIDERS = ["azure", "minimax", "anthropic", "openai-codex", "github-copilot"];
|
|
55
57
|
const VALID_PROVENANCE_SOURCES = ["auth-flow", "manual"];
|
|
56
58
|
const VAULT_ITEM_PREFIX = "providers/";
|
|
59
|
+
exports.PROVIDER_CREDENTIAL_POOL_NOT_LOADED_ERROR = "provider credentials have not been loaded from vault";
|
|
57
60
|
const PROVIDER_FIELD_SPLITS = {
|
|
58
61
|
anthropic: {
|
|
59
62
|
credentials: ["setupToken", "refreshToken", "expiresAt"],
|
|
@@ -197,7 +200,7 @@ function recordFromPayload(payload) {
|
|
|
197
200
|
provenance: { ...payload.provenance },
|
|
198
201
|
};
|
|
199
202
|
}
|
|
200
|
-
function missingPool(agentName, error =
|
|
203
|
+
function missingPool(agentName, error = exports.PROVIDER_CREDENTIAL_POOL_NOT_LOADED_ERROR) {
|
|
201
204
|
return {
|
|
202
205
|
ok: false,
|
|
203
206
|
reason: "missing",
|
|
@@ -205,6 +208,11 @@ function missingPool(agentName, error = "provider credentials have not been load
|
|
|
205
208
|
error,
|
|
206
209
|
};
|
|
207
210
|
}
|
|
211
|
+
function isProviderCredentialPoolNotLoaded(result) {
|
|
212
|
+
return !result.ok
|
|
213
|
+
&& result.reason === "missing"
|
|
214
|
+
&& result.error === exports.PROVIDER_CREDENTIAL_POOL_NOT_LOADED_ERROR;
|
|
215
|
+
}
|
|
208
216
|
function cacheResult(agentName, result) {
|
|
209
217
|
cachedPools.set(agentName, result);
|
|
210
218
|
return result;
|
|
@@ -83,6 +83,37 @@ async function readGithubCopilotModelPingError(response) {
|
|
|
83
83
|
function createStatusError(message, status) {
|
|
84
84
|
return Object.assign(new Error(message), { status });
|
|
85
85
|
}
|
|
86
|
+
function normalizePingTimeoutMs(timeoutMs) {
|
|
87
|
+
if (timeoutMs === undefined)
|
|
88
|
+
return PING_TIMEOUT_MS;
|
|
89
|
+
if (!Number.isFinite(timeoutMs))
|
|
90
|
+
return PING_TIMEOUT_MS;
|
|
91
|
+
return Math.max(1, Math.floor(timeoutMs));
|
|
92
|
+
}
|
|
93
|
+
function createPingTimeoutError(timeoutMs) {
|
|
94
|
+
return Object.assign(new Error(`provider ping timed out after ${timeoutMs}ms`), {
|
|
95
|
+
code: "ETIMEDOUT",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async function runPingWithHardTimeout(runtime, timeoutMs) {
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
let timeout;
|
|
101
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
102
|
+
timeout = setTimeout(() => {
|
|
103
|
+
controller.abort();
|
|
104
|
+
reject(createPingTimeoutError(timeoutMs));
|
|
105
|
+
}, timeoutMs);
|
|
106
|
+
});
|
|
107
|
+
try {
|
|
108
|
+
await Promise.race([
|
|
109
|
+
Promise.resolve().then(() => runtime.ping(controller.signal)),
|
|
110
|
+
timeoutPromise,
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
clearTimeout(timeout);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
86
117
|
async function pingGithubCopilotModel(baseUrl, token, model, fetchImpl = fetch) {
|
|
87
118
|
const base = baseUrl.replace(/\/+$/, "");
|
|
88
119
|
const isClaude = model.startsWith("claude");
|
|
@@ -183,15 +214,7 @@ async function pingProvider(provider, config, options = {}) {
|
|
|
183
214
|
onAttemptStart: options.onAttemptStart,
|
|
184
215
|
onRetry: options.onRetry,
|
|
185
216
|
run: async () => {
|
|
186
|
-
|
|
187
|
-
/* v8 ignore next -- timeout callback: only fires after 10s, tests resolve faster @preserve */
|
|
188
|
-
const timeout = setTimeout(() => controller.abort(), PING_TIMEOUT_MS);
|
|
189
|
-
try {
|
|
190
|
-
await runtime.ping(controller.signal);
|
|
191
|
-
}
|
|
192
|
-
finally {
|
|
193
|
-
clearTimeout(timeout);
|
|
194
|
-
}
|
|
217
|
+
await runPingWithHardTimeout(runtime, normalizePingTimeoutMs(options.timeoutMs));
|
|
195
218
|
},
|
|
196
219
|
});
|
|
197
220
|
if (attempt.ok) {
|
|
@@ -21,7 +21,7 @@ function credentialVisibility(binding) {
|
|
|
21
21
|
}
|
|
22
22
|
return {
|
|
23
23
|
status: credential.status,
|
|
24
|
-
repairCommand: credential.repair.command,
|
|
24
|
+
...("repair" in credential ? { repairCommand: credential.repair.command } : {}),
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
function readinessVisibility(binding) {
|
|
@@ -87,6 +87,8 @@ function credentialLabel(credential) {
|
|
|
87
87
|
return credential.source ?? "vault";
|
|
88
88
|
if (credential.status === "invalid-pool")
|
|
89
89
|
return "vault unavailable";
|
|
90
|
+
if (credential.status === "not-loaded")
|
|
91
|
+
return "checked previously";
|
|
90
92
|
return "missing";
|
|
91
93
|
}
|
|
92
94
|
function readinessLabel(readiness) {
|
|
@@ -102,7 +104,7 @@ function readinessLabel(readiness) {
|
|
|
102
104
|
return readiness.status;
|
|
103
105
|
}
|
|
104
106
|
function providerStatusDetail(lane) {
|
|
105
|
-
if (lane.credential.status
|
|
107
|
+
if (lane.credential.status === "missing" || lane.credential.status === "invalid-pool")
|
|
106
108
|
return undefined;
|
|
107
109
|
return lane.readiness.error;
|
|
108
110
|
}
|