@inneranimalmedia/agentsam-sdk 2.6.0 → 2.6.2
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/docs/AUTH_IDENTITY_CONTRACT.md +25 -27
- package/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
- package/docs/RELEASES.md +7 -7
- package/docs/SDK_WORKER.md +4 -3
- package/docs/SOURCE_ARCHITECTURE.md +58 -0
- package/docs/TEST_TIERS.md +26 -0
- package/migrations/runtime/0001_cli_runtime.sql +298 -0
- package/package.json +28 -7
- package/packages/agentsam-repository/README.md +15 -0
- package/packages/agentsam-repository/package.json +25 -0
- package/packages/agentsam-repository/src/contracts.js +113 -0
- package/packages/agentsam-repository/src/index.js +3 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
- package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
- package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
- package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
- package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
- package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
- package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
- package/packages/identity/package.json +2 -2
- package/packages/identity/src/contracts/auth-config.js +23 -40
- package/packages/identity/tests/auth-config.test.mjs +14 -23
- package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
- package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
- package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
- package/protocol/repository/repository-contract.schema.json +24 -0
- package/protocol/repository/repository-dependency.schema.json +24 -0
- package/protocol/repository/repository-identity.schema.json +17 -0
- package/protocol/rpc/v1/common.proto +16 -0
- package/protocol/rpc/v1/errors.proto +35 -0
- package/protocol/rpc/v1/knowledge.proto +77 -0
- package/services/knowledge/package-lock.json +333 -0
- package/services/knowledge/package.json +5 -1
- package/src/agent/responses-runner.js +63 -35
- package/src/capabilities/repository-snapshot.js +3 -3
- package/src/cli.js +36 -29
- package/src/commands/account-auth.js +1 -1
- package/src/commands/context-economics.js +17 -2
- package/src/commands/context.js +1 -1
- package/src/commands/db.js +20 -3
- package/src/commands/deploy.js +2 -2
- package/src/commands/env.js +90 -0
- package/src/commands/knowledge.js +12 -4
- package/src/commands/merkle-persist.js +30 -11
- package/src/commands/merkle.js +1 -1
- package/src/commands/models.js +123 -65
- package/src/commands/ollama.js +26 -0
- package/src/commands/preferences.js +53 -26
- package/src/commands/providers.js +308 -0
- package/src/commands/shell.js +240 -48
- package/src/commands/tunnel.js +8 -8
- package/src/commands/whoami.js +42 -21
- package/src/errors/contract.js +236 -0
- package/src/errors/diagnostic.js +1 -0
- package/src/errors/index.js +14 -0
- package/src/index.js +13 -1
- package/src/knowledge/service/auth.js +13 -0
- package/src/knowledge/service/grpc-client.js +115 -0
- package/src/knowledge/service/grpc-codec.js +237 -0
- package/src/knowledge/service/grpc-server.js +83 -0
- package/src/knowledge/service/job-engine.js +248 -0
- package/src/knowledge/service/server.js +87 -135
- package/src/knowledge/source.js +1 -1
- package/src/lib/account-session.js +94 -25
- package/src/lib/auth.js +314 -51
- package/src/lib/cli-preferences.js +31 -4
- package/src/lib/core-client.js +75 -30
- package/src/lib/deploy-receipt/index.js +2 -2
- package/src/lib/detect-context.js +14 -16
- package/src/lib/knowledge-docker.js +6 -3
- package/src/lib/local-sessions.js +23 -2
- package/src/lib/local-status.js +1 -1
- package/src/lib/project-config.js +1 -1
- package/src/lib/provider-credentials.js +230 -14
- package/src/lib/slash-commands.js +5 -3
- package/src/local/migrations.js +93 -0
- package/src/local/runtime-store.js +141 -0
- package/src/local/sqlite.js +2 -0
- package/src/local-pty/server.js +113 -51
- package/src/models/discovery.js +327 -0
- package/src/providers/anthropic-messages.js +192 -0
- package/src/providers/cloudflare-chat.js +183 -0
- package/src/providers/factory.js +69 -0
- package/src/providers/gemini-generate-content.js +208 -0
- package/src/providers/index.js +5 -0
- package/src/providers/ollama-chat.js +148 -0
- package/src/providers/openai-responses.js +226 -75
- package/src/repository/index.js +14 -2
- package/src/rpc/generated/common_grpc_pb.js +1 -0
- package/src/rpc/generated/common_pb.js +536 -0
- package/src/rpc/generated/errors_grpc_pb.js +1 -0
- package/src/rpc/generated/errors_pb.js +482 -0
- package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
- package/src/rpc/generated/knowledge_pb.js +2168 -0
- package/src/rpc/generated/package.json +3 -0
- package/src/security/trust-boundary.js +2 -2
- package/src/telemetry/events.js +4 -1
- package/src/ui/cli/activity.js +76 -0
- package/src/ui/cli/compaction.js +15 -0
- package/src/ui/cli/footer.js +39 -0
- package/src/ui/cli/help.js +194 -0
- package/src/ui/cli/plan.js +20 -0
- package/src/ui/cli/runtime-events.js +110 -0
- package/src/ui/cli/waiting.js +16 -0
- package/src/ui/merkle/render.js +1 -1
- package/test/account-session.test.mjs +48 -11
- package/test/apps-scaffold-contract.test.mjs +43 -0
- package/test/cli/preferences-runtime.test.mjs +11 -0
- package/test/cli/runtime-ui.test.mjs +74 -0
- package/test/error-diagnostics.test.mjs +57 -1
- package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
- package/test/integration/cli-help.test.mjs +37 -0
- package/test/integration/knowledge-rpc.test.mjs +112 -0
- package/test/integration/merkle-cli.test.mjs +61 -0
- package/test/integration/merkle-persistence-identity.test.mjs +48 -0
- package/test/integration/provider-env-cli.test.mjs +49 -0
- package/test/integration/provider-factory.test.mjs +197 -0
- package/test/integration/repository-company-graph.test.mjs +90 -0
- package/test/integration/runtime-migrations.test.mjs +82 -0
- package/test/knowledge-service.test.mjs +5 -0
- package/test/knowledge.test.mjs +16 -0
- package/test/live/terminal-transport.live.test.mjs +24 -0
- package/test/local-sessions.test.mjs +7 -1
- package/test/models.test.mjs +101 -4
- package/test/ollama.test.mjs +21 -0
- package/test/portable-context.test.mjs +1 -1
- package/test/provider-credentials.test.mjs +45 -1
- package/test/release-hygiene.test.mjs +13 -5
- package/test/responses-runner.test.mjs +3 -1
- package/test/sdk-worker-contract.test.mjs +5 -1
- package/test/shell.test.mjs +50 -8
- package/test/smoke.mjs +2 -1
- package/test/terminal/local-pty.mock.test.mjs +151 -0
- package/test/whoami-resume.test.mjs +6 -5
- package/src/lib/prompt-byok.js +0 -57
- package/src/lib/save-sdk-token.js +0 -19
- /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cancel,
|
|
3
|
+
confirm,
|
|
4
|
+
intro,
|
|
5
|
+
isCancel,
|
|
6
|
+
multiselect,
|
|
7
|
+
outro,
|
|
8
|
+
password,
|
|
9
|
+
spinner,
|
|
10
|
+
text,
|
|
11
|
+
} from '@clack/prompts';
|
|
12
|
+
import { getJson } from '../lib/core-client.js';
|
|
13
|
+
import {
|
|
14
|
+
PROVIDER_CREDENTIALS,
|
|
15
|
+
describeProviderCredential,
|
|
16
|
+
listProviderCredentialStatus,
|
|
17
|
+
normalizeProviderId,
|
|
18
|
+
providerCredentialSpec,
|
|
19
|
+
removeProviderCredential,
|
|
20
|
+
resolveProviderCredential,
|
|
21
|
+
setProviderCredential,
|
|
22
|
+
} from '../lib/provider-credentials.js';
|
|
23
|
+
import { discoverProviderModels } from '../models/discovery.js';
|
|
24
|
+
|
|
25
|
+
const PROVIDER_ORDER = Object.freeze([
|
|
26
|
+
'openai',
|
|
27
|
+
'anthropic',
|
|
28
|
+
'gemini',
|
|
29
|
+
'cursor',
|
|
30
|
+
'xai',
|
|
31
|
+
'cloudflare',
|
|
32
|
+
'inneranimalmedia',
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
function clean(value) { return value == null ? '' : String(value).trim(); }
|
|
36
|
+
function writeLine(write, value = '') { write(`${value}\n`); }
|
|
37
|
+
|
|
38
|
+
export function providerChoices(options = {}) {
|
|
39
|
+
const statuses = new Map(listProviderCredentialStatus(options).map((row) => [row.provider, row]));
|
|
40
|
+
return PROVIDER_ORDER.map((provider) => {
|
|
41
|
+
const spec = providerCredentialSpec(provider);
|
|
42
|
+
const status = statuses.get(provider);
|
|
43
|
+
return {
|
|
44
|
+
value: provider,
|
|
45
|
+
label: spec?.label || provider,
|
|
46
|
+
hint: status?.configured
|
|
47
|
+
? `configured · ${status.source || 'runtime'}`
|
|
48
|
+
: status?.error ? `blocked · ${status.error}` : spec?.env || '',
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function verifyInnerAnimalMedia(credential, options = {}) {
|
|
54
|
+
if (!credential?.configured || !credential?.value) {
|
|
55
|
+
return { attempted: false, ok: false, error: credential?.error || 'credential unavailable', models: [] };
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const loader = options.iamContextLoader || ((token) => getJson('/api/sdk/context', token));
|
|
59
|
+
const context = await loader(credential.value);
|
|
60
|
+
return {
|
|
61
|
+
attempted: true,
|
|
62
|
+
ok: true,
|
|
63
|
+
error: null,
|
|
64
|
+
models: [],
|
|
65
|
+
identity: {
|
|
66
|
+
user_id: context?.user_id || null,
|
|
67
|
+
account_id: context?.account_id || null,
|
|
68
|
+
email: context?.email || context?.user?.email || null,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return { attempted: true, ok: false, error: error?.message || String(error), models: [] };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function verifyProviderCredential(provider, options = {}) {
|
|
77
|
+
const id = normalizeProviderId(provider);
|
|
78
|
+
const credential = resolveProviderCredential(id, options);
|
|
79
|
+
if (id === 'inneranimalmedia') return verifyInnerAnimalMedia(credential, options);
|
|
80
|
+
if (!credential.configured) {
|
|
81
|
+
return { attempted: false, ok: false, error: credential.error || 'credential unavailable', models: [] };
|
|
82
|
+
}
|
|
83
|
+
return discoverProviderModels(id, credential, { fetchImpl: options.fetchImpl || fetch });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function collectProviderStatus(options = {}) {
|
|
87
|
+
const rows = listProviderCredentialStatus(options);
|
|
88
|
+
if (options.verify !== true) {
|
|
89
|
+
return rows.map((row) => ({ ...row, verification: null }));
|
|
90
|
+
}
|
|
91
|
+
const verified = await Promise.all(rows.map(async (row) => {
|
|
92
|
+
if (!row.configured) return { ...row, verification: null };
|
|
93
|
+
const verification = await verifyProviderCredential(row.provider, options);
|
|
94
|
+
return {
|
|
95
|
+
...row,
|
|
96
|
+
verification: {
|
|
97
|
+
attempted: verification.attempted === true,
|
|
98
|
+
ok: verification.ok === true,
|
|
99
|
+
error: verification.error || null,
|
|
100
|
+
model_count: Array.isArray(verification.models) ? verification.models.length : 0,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}));
|
|
104
|
+
return verified;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function renderProviderStatus(rows = []) {
|
|
108
|
+
const lines = ['', ' Agent Sam · providers', ''];
|
|
109
|
+
for (const row of rows) {
|
|
110
|
+
const state = row.configured ? 'configured' : row.error ? `blocked (${row.error})` : 'not configured';
|
|
111
|
+
let verification = '';
|
|
112
|
+
if (row.verification) {
|
|
113
|
+
verification = row.verification.ok
|
|
114
|
+
? ` · verified${row.verification.model_count ? ` · ${row.verification.model_count} models` : ''}`
|
|
115
|
+
: ` · verify failed: ${row.verification.error || 'unknown'}`;
|
|
116
|
+
}
|
|
117
|
+
lines.push(` ${String(row.label || row.provider).padEnd(18)} ${state}${verification}`);
|
|
118
|
+
}
|
|
119
|
+
lines.push('');
|
|
120
|
+
lines.push(' Profiles: ~/.agentsam/env.d/<provider>.env · mode 0600');
|
|
121
|
+
lines.push(' Load one or many: source ~/.agentsam/load-agent-env.sh openai gemini cursor');
|
|
122
|
+
lines.push('');
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function promptProviderCredential(provider, options = {}) {
|
|
127
|
+
const spec = providerCredentialSpec(provider);
|
|
128
|
+
if (!spec) throw new Error(`unsupported_provider:${provider}`);
|
|
129
|
+
const promptPassword = options.passwordImpl || password;
|
|
130
|
+
const promptText = options.textImpl || text;
|
|
131
|
+
const secret = await promptPassword({
|
|
132
|
+
message: `${spec.label} · ${spec.env}`,
|
|
133
|
+
mask: '•',
|
|
134
|
+
validate(value) {
|
|
135
|
+
const trimmed = clean(value);
|
|
136
|
+
if (!trimmed) return 'Credential is required';
|
|
137
|
+
if (spec.tokenPrefix && !trimmed.startsWith(spec.tokenPrefix)) return `Expected ${spec.tokenPrefix}…`;
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
if (isCancel(secret)) return null;
|
|
141
|
+
|
|
142
|
+
let accountId = '';
|
|
143
|
+
if (provider === 'cloudflare') {
|
|
144
|
+
const answer = await promptText({
|
|
145
|
+
message: 'Cloudflare account ID',
|
|
146
|
+
placeholder: '32-character account ID',
|
|
147
|
+
validate(value) {
|
|
148
|
+
const id = clean(value);
|
|
149
|
+
if (!id) return 'Cloudflare account ID is required';
|
|
150
|
+
if (!/^[a-f0-9]{32}$/i.test(id)) return 'Expected a 32-character hexadecimal account ID';
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
if (isCancel(answer)) return null;
|
|
154
|
+
accountId = clean(answer);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
setProviderCredential(provider, String(secret), { ...options, accountId });
|
|
158
|
+
return verifyProviderCredential(provider, options);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function runInteractiveProviders(options = {}) {
|
|
162
|
+
const promptMultiselect = options.multiselectImpl || multiselect;
|
|
163
|
+
const selected = await promptMultiselect({
|
|
164
|
+
message: 'Providers to configure',
|
|
165
|
+
required: false,
|
|
166
|
+
options: providerChoices(options),
|
|
167
|
+
});
|
|
168
|
+
if (isCancel(selected)) {
|
|
169
|
+
cancel('Provider setup cancelled.');
|
|
170
|
+
return collectProviderStatus(options);
|
|
171
|
+
}
|
|
172
|
+
if (!selected.length) return collectProviderStatus(options);
|
|
173
|
+
|
|
174
|
+
intro('Agent Sam provider setup');
|
|
175
|
+
const spin = options.spinnerImpl ? options.spinnerImpl() : spinner();
|
|
176
|
+
for (const provider of selected) {
|
|
177
|
+
const result = await promptProviderCredential(provider, options);
|
|
178
|
+
if (!result) continue;
|
|
179
|
+
spin.start(`Verifying ${providerCredentialSpec(provider)?.label || provider}`);
|
|
180
|
+
if (result.ok) {
|
|
181
|
+
const suffix = result.models?.length ? ` · ${result.models.length} models` : '';
|
|
182
|
+
spin.stop(`Verified${suffix}`);
|
|
183
|
+
} else {
|
|
184
|
+
spin.stop(`Saved · verification failed: ${result.error || 'unknown'}`, 1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
outro('Provider setup complete');
|
|
188
|
+
return collectProviderStatus(options);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function parseArgs(argv = []) {
|
|
192
|
+
const out = { command: 'interactive', provider: '', json: false, verify: false, yes: false, fromEnv: '' };
|
|
193
|
+
const args = [...argv];
|
|
194
|
+
if (args[0] && !args[0].startsWith('-')) out.command = args.shift();
|
|
195
|
+
if (['add', 'set', 'remove', 'verify', 'status'].includes(out.command) && args[0] && !args[0].startsWith('-')) {
|
|
196
|
+
out.provider = normalizeProviderId(args.shift());
|
|
197
|
+
}
|
|
198
|
+
while (args.length) {
|
|
199
|
+
const arg = args.shift();
|
|
200
|
+
if (arg === '--json') out.json = true;
|
|
201
|
+
else if (arg === '--verify') out.verify = true;
|
|
202
|
+
else if (arg === '--yes' || arg === '-y') out.yes = true;
|
|
203
|
+
else if (arg === '--from-env') out.fromEnv = clean(args.shift());
|
|
204
|
+
else if (arg === '--help' || arg === '-h') out.command = 'help';
|
|
205
|
+
else throw new Error(`unknown providers option: ${arg}`);
|
|
206
|
+
}
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function runProviders(argv = [], options = {}) {
|
|
211
|
+
const write = options.write || ((value) => process.stdout.write(value));
|
|
212
|
+
const parsed = parseArgs(argv);
|
|
213
|
+
|
|
214
|
+
if (parsed.command === 'help') {
|
|
215
|
+
write([
|
|
216
|
+
'agentsam providers',
|
|
217
|
+
'agentsam providers status [provider] [--verify] [--json]',
|
|
218
|
+
'agentsam providers add <provider> [--from-env NAME]',
|
|
219
|
+
'agentsam providers verify [provider] [--json]',
|
|
220
|
+
'agentsam providers remove <provider> [--yes]',
|
|
221
|
+
'',
|
|
222
|
+
`Providers: ${PROVIDER_ORDER.join(', ')}`,
|
|
223
|
+
'',
|
|
224
|
+
].join('\n'));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (parsed.command === 'interactive') {
|
|
229
|
+
if (options.interactive === false || !(options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY))) {
|
|
230
|
+
const rows = await collectProviderStatus({ ...options, verify: false });
|
|
231
|
+
write(renderProviderStatus(rows));
|
|
232
|
+
return rows;
|
|
233
|
+
}
|
|
234
|
+
const rows = await runInteractiveProviders(options);
|
|
235
|
+
write(renderProviderStatus(rows));
|
|
236
|
+
return rows;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (parsed.provider && !providerCredentialSpec(parsed.provider)) throw new Error(`unsupported_provider:${parsed.provider}`);
|
|
240
|
+
|
|
241
|
+
if (parsed.command === 'status') {
|
|
242
|
+
let rows = await collectProviderStatus({ ...options, verify: parsed.verify });
|
|
243
|
+
if (parsed.provider) rows = rows.filter((row) => row.provider === parsed.provider);
|
|
244
|
+
if (parsed.json) writeLine(write, JSON.stringify(rows, null, 2));
|
|
245
|
+
else write(renderProviderStatus(rows));
|
|
246
|
+
return rows;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (parsed.command === 'verify') {
|
|
250
|
+
const providers = parsed.provider
|
|
251
|
+
? [parsed.provider]
|
|
252
|
+
: Object.keys(PROVIDER_CREDENTIALS).filter((provider) => describeProviderCredential(provider, options).configured);
|
|
253
|
+
const results = [];
|
|
254
|
+
for (const provider of providers) {
|
|
255
|
+
const result = await verifyProviderCredential(provider, options);
|
|
256
|
+
results.push({
|
|
257
|
+
provider,
|
|
258
|
+
ok: result.ok === true,
|
|
259
|
+
error: result.error || null,
|
|
260
|
+
model_count: Array.isArray(result.models) ? result.models.length : 0,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
if (parsed.json) writeLine(write, JSON.stringify(results, null, 2));
|
|
264
|
+
else {
|
|
265
|
+
writeLine(write, '');
|
|
266
|
+
for (const row of results) {
|
|
267
|
+
writeLine(write, ` ${row.provider.padEnd(18)} ${row.ok ? `verified${row.model_count ? ` · ${row.model_count} models` : ''}` : `failed · ${row.error}`}`);
|
|
268
|
+
}
|
|
269
|
+
writeLine(write, '');
|
|
270
|
+
}
|
|
271
|
+
return results;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (parsed.command === 'add' || parsed.command === 'set') {
|
|
275
|
+
if (!parsed.provider) throw new Error('providers add requires a provider');
|
|
276
|
+
if (parsed.fromEnv) {
|
|
277
|
+
const value = clean((options.env || process.env)[parsed.fromEnv]);
|
|
278
|
+
if (!value) throw new Error(`environment credential missing: ${parsed.fromEnv}`);
|
|
279
|
+
setProviderCredential(parsed.provider, value, options);
|
|
280
|
+
return verifyProviderCredential(parsed.provider, options);
|
|
281
|
+
}
|
|
282
|
+
if (options.interactive === false || !(options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY))) {
|
|
283
|
+
throw new Error('providers add requires an interactive terminal or --from-env NAME');
|
|
284
|
+
}
|
|
285
|
+
const result = await promptProviderCredential(parsed.provider, options);
|
|
286
|
+
if (!result) return null;
|
|
287
|
+
writeLine(write, result.ok ? ` ${parsed.provider} verified` : ` ${parsed.provider} saved · verification failed: ${result.error}`);
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (parsed.command === 'remove') {
|
|
292
|
+
if (!parsed.provider) throw new Error('providers remove requires a provider');
|
|
293
|
+
let approved = parsed.yes;
|
|
294
|
+
if (!approved) {
|
|
295
|
+
if (options.interactive === false || !(options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY))) {
|
|
296
|
+
throw new Error('providers remove requires --yes in non-interactive mode');
|
|
297
|
+
}
|
|
298
|
+
const answer = await (options.confirmImpl || confirm)({ message: `Remove ${providerCredentialSpec(parsed.provider)?.label} credential profile from this machine?` });
|
|
299
|
+
approved = !isCancel(answer) && answer === true;
|
|
300
|
+
}
|
|
301
|
+
if (!approved) return { provider: parsed.provider, removed: false };
|
|
302
|
+
const result = removeProviderCredential(parsed.provider, options);
|
|
303
|
+
writeLine(write, result.removed ? ` removed ${parsed.provider}` : ` ${parsed.provider} was not configured`);
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
throw new Error(`unknown providers command: ${parsed.command}`);
|
|
308
|
+
}
|