@orchagent/cli 0.3.36 → 0.3.38
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/commands/config.js +13 -0
- package/dist/commands/install.js +31 -0
- package/dist/commands/pricing.js +23 -2
- package/dist/commands/publish.js +3 -36
- package/dist/commands/run.js +14 -7
- package/dist/commands/security.js +17 -5
- package/dist/commands/skill.js +40 -1
- package/dist/lib/api.js +2 -1
- package/dist/lib/config.js +19 -0
- package/package.json +1 -1
package/dist/commands/config.js
CHANGED
|
@@ -78,6 +78,13 @@ async function getConfigValue(key) {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
+
async function unsetConfigValue(key) {
|
|
82
|
+
if (!isValidKey(key)) {
|
|
83
|
+
throw new errors_1.CliError(`Unknown config key: ${key}. Supported keys: ${SUPPORTED_KEYS.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
await (0, config_1.unsetConfigKey)(key);
|
|
86
|
+
process.stdout.write(`Unset ${key}\n`);
|
|
87
|
+
}
|
|
81
88
|
async function listConfigValues() {
|
|
82
89
|
const config = await (0, config_1.loadConfig)();
|
|
83
90
|
process.stdout.write('CLI Configuration:\n\n');
|
|
@@ -123,6 +130,12 @@ function registerConfigCommand(program) {
|
|
|
123
130
|
.action(async (key) => {
|
|
124
131
|
await getConfigValue(key);
|
|
125
132
|
});
|
|
133
|
+
config
|
|
134
|
+
.command('unset <key>')
|
|
135
|
+
.description('Remove a configuration value (restore to default)')
|
|
136
|
+
.action(async (key) => {
|
|
137
|
+
await unsetConfigValue(key);
|
|
138
|
+
});
|
|
126
139
|
config
|
|
127
140
|
.command('list')
|
|
128
141
|
.description('List all configuration values')
|
package/dist/commands/install.js
CHANGED
|
@@ -86,6 +86,37 @@ async function downloadAgentWithFallback(config, org, name, version) {
|
|
|
86
86
|
`Use: orch call ${org}/${name}@${version} --input '{...}'`);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
// Check if download is disabled (server-only agent)
|
|
90
|
+
if (publicMeta && publicMeta.allow_local_download === false) {
|
|
91
|
+
// Check if owner (can bypass)
|
|
92
|
+
if (config.apiKey) {
|
|
93
|
+
const callerOrg = await (0, api_1.getOrg)(config);
|
|
94
|
+
const isOwner = (publicMeta.org_id && callerOrg.id === publicMeta.org_id) ||
|
|
95
|
+
(publicMeta.org_slug && callerOrg.slug === publicMeta.org_slug);
|
|
96
|
+
if (isOwner) {
|
|
97
|
+
// Owner - fetch from authenticated endpoint
|
|
98
|
+
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
99
|
+
const matching = myAgents.filter(a => a.name === name);
|
|
100
|
+
if (matching.length > 0) {
|
|
101
|
+
let targetAgent;
|
|
102
|
+
if (version === 'latest') {
|
|
103
|
+
targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const found = matching.find(a => a.version === version);
|
|
107
|
+
if (!found) {
|
|
108
|
+
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
109
|
+
}
|
|
110
|
+
targetAgent = found;
|
|
111
|
+
}
|
|
112
|
+
const agentData = await (0, api_1.request)(config, 'GET', `/agents/${targetAgent.id}`);
|
|
113
|
+
return { ...agentData, org_slug: org };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw new errors_1.CliError(`This agent is server-only and cannot be downloaded.\n\n` +
|
|
118
|
+
`Use: orch call ${org}/${name}@${version} --input '{...}'`);
|
|
119
|
+
}
|
|
89
120
|
// Free agent - proceed normally with public data
|
|
90
121
|
if (publicMeta) {
|
|
91
122
|
// Cast PublicAgent to Agent for use as CanonicalAgent
|
package/dist/commands/pricing.js
CHANGED
|
@@ -12,7 +12,9 @@ function registerPricingCommand(program) {
|
|
|
12
12
|
program
|
|
13
13
|
.command('pricing <agent> <mode>')
|
|
14
14
|
.description('Set pricing for your agent (free or per-call in USD)')
|
|
15
|
-
.
|
|
15
|
+
.option('--local-download', 'Allow users to download and run locally')
|
|
16
|
+
.option('--no-local-download', 'Restrict to server-only (orch call)')
|
|
17
|
+
.action(async (agentRef, mode, options) => {
|
|
16
18
|
const resolved = await (0, config_1.getResolvedConfig)();
|
|
17
19
|
// Parse agent reference
|
|
18
20
|
const [orgOrAgent, maybeName] = agentRef.split('/');
|
|
@@ -56,17 +58,36 @@ function registerPricingCommand(program) {
|
|
|
56
58
|
pricingMode = 'per_call';
|
|
57
59
|
pricePerCallCents = Math.round(priceFloat * 100);
|
|
58
60
|
}
|
|
61
|
+
// Determine allow_local_download value
|
|
62
|
+
let allowLocalDownload;
|
|
63
|
+
if (pricingMode === 'per_call') {
|
|
64
|
+
// Paid agents are always server-only
|
|
65
|
+
allowLocalDownload = false;
|
|
66
|
+
if (options.localDownload) {
|
|
67
|
+
process.stderr.write(chalk_1.default.yellow('Note: Paid agents are always server-only. --local-download ignored.\n'));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (options.localDownload !== undefined) {
|
|
71
|
+
allowLocalDownload = options.localDownload;
|
|
72
|
+
}
|
|
59
73
|
// Set pricing
|
|
60
|
-
await (0, api_1.setAgentPricing)(resolved, agent.id, pricingMode, pricePerCallCents);
|
|
74
|
+
await (0, api_1.setAgentPricing)(resolved, agent.id, pricingMode, pricePerCallCents, allowLocalDownload);
|
|
61
75
|
// Show confirmation
|
|
62
76
|
process.stdout.write(chalk_1.default.green('✓ Pricing updated\n'));
|
|
63
77
|
process.stdout.write(`Agent: ${org}/${agentName}\n`);
|
|
64
78
|
if (pricingMode === 'free') {
|
|
65
79
|
process.stdout.write(`Mode: FREE\n`);
|
|
80
|
+
if (allowLocalDownload === true) {
|
|
81
|
+
process.stdout.write(`Local download: enabled\n`);
|
|
82
|
+
}
|
|
83
|
+
else if (allowLocalDownload === false) {
|
|
84
|
+
process.stdout.write(`Local download: disabled (server-only)\n`);
|
|
85
|
+
}
|
|
66
86
|
}
|
|
67
87
|
else {
|
|
68
88
|
process.stdout.write(`Mode: Pay per call\n`);
|
|
69
89
|
process.stdout.write(`Price: $${(pricePerCallCents / 100).toFixed(2)} USD per call\n`);
|
|
90
|
+
process.stdout.write(`Local download: disabled (paid agents are server-only)\n`);
|
|
70
91
|
}
|
|
71
92
|
});
|
|
72
93
|
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -54,36 +54,6 @@ function deriveInputSchema(variables) {
|
|
|
54
54
|
required: [...variables],
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Handle security flagged error response (422 with error: 'content_flagged')
|
|
59
|
-
* Returns true if the error was handled, false otherwise
|
|
60
|
-
*/
|
|
61
|
-
function handleSecurityFlaggedError(err) {
|
|
62
|
-
if (!(err instanceof api_1.ApiError) || err.status !== 422) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
const payload = err.payload;
|
|
66
|
-
if (payload?.error !== 'content_flagged') {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
process.stderr.write('\n');
|
|
70
|
-
process.stderr.write('Error: Skill flagged for security review\n\n');
|
|
71
|
-
if (payload.concerns && payload.concerns.length > 0) {
|
|
72
|
-
process.stderr.write('Concerns found:\n');
|
|
73
|
-
for (const concern of payload.concerns) {
|
|
74
|
-
const severityLabel = concern.severity.toUpperCase();
|
|
75
|
-
const fileInfo = concern.file_path ? ` in ${concern.file_path}` : '';
|
|
76
|
-
process.stderr.write(` [${severityLabel}] ${concern.category}${fileInfo}\n`);
|
|
77
|
-
process.stderr.write(` "${concern.description}"\n\n`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (payload.summary) {
|
|
81
|
-
process.stderr.write(`Summary: ${payload.summary}\n\n`);
|
|
82
|
-
}
|
|
83
|
-
process.stderr.write('Please review and remove suspicious patterns before publishing.\n');
|
|
84
|
-
process.stderr.write('If you believe this is a false positive, contact support@orchagent.com\n');
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
57
|
/**
|
|
88
58
|
* Check if orchagent-sdk is listed in requirements.txt or pyproject.toml
|
|
89
59
|
*/
|
|
@@ -204,6 +174,7 @@ function registerPublishCommand(program) {
|
|
|
204
174
|
.option('--docker', 'Include Dockerfile for custom environment (builds E2B template)')
|
|
205
175
|
.option('--price <amount>', 'Set price per call in USD (e.g., 0.50 for $0.50/call)')
|
|
206
176
|
.option('--pricing-mode <mode>', 'Pricing mode: free or per_call (default: free)')
|
|
177
|
+
.option('--local-download', 'Allow users to download and run locally (default: server-only)')
|
|
207
178
|
.action(async (options) => {
|
|
208
179
|
if (options.private) {
|
|
209
180
|
process.stderr.write('Warning: --private is deprecated (private is now the default). You can safely remove it.\n');
|
|
@@ -270,6 +241,7 @@ function registerPublishCommand(program) {
|
|
|
270
241
|
skills_locked: options.skillsLocked || undefined,
|
|
271
242
|
// SC-05: Include all skill files for UI preview
|
|
272
243
|
skill_files: hasMultipleFiles ? skillFiles : undefined,
|
|
244
|
+
allow_local_download: options.localDownload || false,
|
|
273
245
|
});
|
|
274
246
|
const skillVersion = skillResult.agent?.version || 'v1';
|
|
275
247
|
const skillAgentId = skillResult.agent?.id;
|
|
@@ -316,9 +288,6 @@ function registerPublishCommand(program) {
|
|
|
316
288
|
process.stdout.write(`\nView analytics and usage: https://orchagent.io/dashboard\n`);
|
|
317
289
|
}
|
|
318
290
|
catch (err) {
|
|
319
|
-
if (handleSecurityFlaggedError(err)) {
|
|
320
|
-
process.exit(1);
|
|
321
|
-
}
|
|
322
291
|
throw err;
|
|
323
292
|
}
|
|
324
293
|
return;
|
|
@@ -545,12 +514,10 @@ function registerPublishCommand(program) {
|
|
|
545
514
|
manifest: manifest.manifest,
|
|
546
515
|
default_skills: skillsFromFlag || manifest.default_skills,
|
|
547
516
|
skills_locked: manifest.skills_locked || options.skillsLocked || undefined,
|
|
517
|
+
allow_local_download: options.localDownload || false,
|
|
548
518
|
});
|
|
549
519
|
}
|
|
550
520
|
catch (err) {
|
|
551
|
-
if (handleSecurityFlaggedError(err)) {
|
|
552
|
-
process.exit(1);
|
|
553
|
-
}
|
|
554
521
|
throw err;
|
|
555
522
|
}
|
|
556
523
|
const assignedVersion = result.agent?.version || 'v1';
|
package/dist/commands/run.js
CHANGED
|
@@ -74,11 +74,12 @@ async function downloadAgent(config, org, agent, version) {
|
|
|
74
74
|
return await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/download`);
|
|
75
75
|
}
|
|
76
76
|
catch (err) {
|
|
77
|
-
// Check for paid-agent error
|
|
77
|
+
// Check for paid-agent or download-disabled error
|
|
78
78
|
if (err instanceof api_1.ApiError && err.status === 403) {
|
|
79
79
|
const payload = err.payload;
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
const errorCode = payload?.error?.code;
|
|
81
|
+
if (errorCode === 'PAID_AGENT_SERVER_ONLY' || errorCode === 'DOWNLOAD_DISABLED') {
|
|
82
|
+
// Try owner path if authenticated
|
|
82
83
|
if (config.apiKey) {
|
|
83
84
|
try {
|
|
84
85
|
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
@@ -112,10 +113,16 @@ async function downloadAgent(config, org, agent, version) {
|
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
// Non-owner - block with helpful message
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`
|
|
116
|
+
if (errorCode === 'PAID_AGENT_SERVER_ONLY') {
|
|
117
|
+
const price = payload.error.price_per_call_cents || 0;
|
|
118
|
+
const priceStr = price ? `$${(price / 100).toFixed(2)}/call` : 'PAID';
|
|
119
|
+
throw new errors_1.CliError(`This agent is paid (${priceStr}) and runs on server only.\n\n` +
|
|
120
|
+
`Use: orch call ${org}/${agent}@${version} --input '{...}'`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
throw new errors_1.CliError(`This agent is server-only and cannot be downloaded.\n\n` +
|
|
124
|
+
`Use: orch call ${org}/${agent}@${version} --input '{...}'`);
|
|
125
|
+
}
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
128
|
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.extractCount = extractCount;
|
|
6
7
|
exports.registerSecurityCommand = registerSecurityCommand;
|
|
7
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -61,6 +62,14 @@ function riskLevelColor(level) {
|
|
|
61
62
|
return level;
|
|
62
63
|
}
|
|
63
64
|
}
|
|
65
|
+
/** Extract count from summary value (handles both flat numbers and {total, leaked} objects). */
|
|
66
|
+
function extractCount(value) {
|
|
67
|
+
if (typeof value === 'number')
|
|
68
|
+
return value;
|
|
69
|
+
if (value && typeof value === 'object' && 'leaked' in value)
|
|
70
|
+
return value.leaked;
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
64
73
|
function formatSummaryOutput(result) {
|
|
65
74
|
process.stdout.write('\n');
|
|
66
75
|
process.stdout.write(chalk_1.default.bold('Security Scan Results\n'));
|
|
@@ -78,8 +87,8 @@ function formatSummaryOutput(result) {
|
|
|
78
87
|
process.stdout.write(chalk_1.default.bold('By Severity:\n'));
|
|
79
88
|
const severityOrder = ['critical', 'high', 'medium', 'low'];
|
|
80
89
|
for (const sev of severityOrder) {
|
|
81
|
-
const count = result.summary.by_severity[sev];
|
|
82
|
-
if (count
|
|
90
|
+
const count = extractCount(result.summary.by_severity[sev]);
|
|
91
|
+
if (count > 0) {
|
|
83
92
|
process.stdout.write(` ${severityColor(sev)}: ${count}\n`);
|
|
84
93
|
}
|
|
85
94
|
}
|
|
@@ -88,7 +97,8 @@ function formatSummaryOutput(result) {
|
|
|
88
97
|
// Breakdown by category
|
|
89
98
|
if (Object.keys(result.summary.by_category).length > 0) {
|
|
90
99
|
process.stdout.write(chalk_1.default.bold('By Category:\n'));
|
|
91
|
-
for (const [cat,
|
|
100
|
+
for (const [cat, rawCount] of Object.entries(result.summary.by_category)) {
|
|
101
|
+
const count = extractCount(rawCount);
|
|
92
102
|
if (count > 0) {
|
|
93
103
|
process.stdout.write(` ${cat}: ${count}\n`);
|
|
94
104
|
}
|
|
@@ -293,7 +303,8 @@ function generateMarkdownReport(result) {
|
|
|
293
303
|
if (Object.keys(result.summary.by_severity).length > 0) {
|
|
294
304
|
lines.push('### By Severity');
|
|
295
305
|
lines.push('');
|
|
296
|
-
for (const [sev,
|
|
306
|
+
for (const [sev, rawCount] of Object.entries(result.summary.by_severity)) {
|
|
307
|
+
const count = extractCount(rawCount);
|
|
297
308
|
if (count > 0) {
|
|
298
309
|
lines.push(`- ${sev.toUpperCase()}: ${count}`);
|
|
299
310
|
}
|
|
@@ -303,7 +314,8 @@ function generateMarkdownReport(result) {
|
|
|
303
314
|
if (Object.keys(result.summary.by_category).length > 0) {
|
|
304
315
|
lines.push('### By Category');
|
|
305
316
|
lines.push('');
|
|
306
|
-
for (const [cat,
|
|
317
|
+
for (const [cat, rawCount] of Object.entries(result.summary.by_category)) {
|
|
318
|
+
const count = extractCount(rawCount);
|
|
307
319
|
if (count > 0) {
|
|
308
320
|
lines.push(`- ${cat}: ${count}`);
|
|
309
321
|
}
|
package/dist/commands/skill.js
CHANGED
|
@@ -155,6 +155,42 @@ async function downloadSkillWithFallback(config, org, skill, version) {
|
|
|
155
155
|
`Paid skills are loaded automatically during server execution.`);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
// Check if download is disabled (server-only skill)
|
|
159
|
+
if (skillMeta && skillMeta.allow_local_download === false) {
|
|
160
|
+
if (config.apiKey) {
|
|
161
|
+
const callerOrg = await (0, api_1.getOrg)(config);
|
|
162
|
+
const isOwner = (skillMeta.org_id && callerOrg.id === skillMeta.org_id) ||
|
|
163
|
+
(skillMeta.org_slug && callerOrg.slug === skillMeta.org_slug);
|
|
164
|
+
if (isOwner) {
|
|
165
|
+
// Owner - fetch from authenticated endpoint
|
|
166
|
+
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
167
|
+
const matching = myAgents.filter(a => a.name === skill && a.type === 'skill');
|
|
168
|
+
if (matching.length > 0) {
|
|
169
|
+
let targetAgent;
|
|
170
|
+
if (version === 'latest') {
|
|
171
|
+
targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
const found = matching.find(a => a.version === version);
|
|
175
|
+
if (!found) {
|
|
176
|
+
throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
|
|
177
|
+
}
|
|
178
|
+
targetAgent = found;
|
|
179
|
+
}
|
|
180
|
+
const skillData = await (0, api_1.request)(config, 'GET', `/agents/${targetAgent.id}`);
|
|
181
|
+
return {
|
|
182
|
+
type: skillData.type,
|
|
183
|
+
name: skillData.name,
|
|
184
|
+
version: skillData.version,
|
|
185
|
+
description: skillData.description,
|
|
186
|
+
prompt: skillData.prompt,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
throw new errors_1.CliError(`This skill is server-only and cannot be downloaded.\n\n` +
|
|
192
|
+
`Skills are loaded automatically during server execution via 'orch call'.`);
|
|
193
|
+
}
|
|
158
194
|
// Free skill or public metadata available - proceed with normal download
|
|
159
195
|
if (skillMeta) {
|
|
160
196
|
try {
|
|
@@ -165,11 +201,14 @@ async function downloadSkillWithFallback(config, org, skill, version) {
|
|
|
165
201
|
if (err instanceof api_1.ApiError && err.status === 403) {
|
|
166
202
|
const payload = err.payload;
|
|
167
203
|
if (payload?.error?.code === 'PAID_AGENT_SERVER_ONLY') {
|
|
168
|
-
// Legacy error handling (shouldn't reach here with new logic)
|
|
169
204
|
const price = payload.error.price_per_call_cents || 0;
|
|
170
205
|
throw new errors_1.CliError(`This skill costs $${(price / 100).toFixed(2)}/call and runs on server only.\n\n` +
|
|
171
206
|
`Use: orch call ${org}/${skill}@${version} --input '{...}'`);
|
|
172
207
|
}
|
|
208
|
+
if (payload?.error?.code === 'DOWNLOAD_DISABLED') {
|
|
209
|
+
throw new errors_1.CliError(`This skill is server-only and cannot be downloaded.\n\n` +
|
|
210
|
+
`Skills are loaded automatically during server execution via 'orch call'.`);
|
|
211
|
+
}
|
|
173
212
|
}
|
|
174
213
|
throw err;
|
|
175
214
|
}
|
package/dist/lib/api.js
CHANGED
|
@@ -547,11 +547,12 @@ async function getSellerDashboardLink(config) {
|
|
|
547
547
|
async function getSellerEarnings(config) {
|
|
548
548
|
return request(config, 'GET', '/billing/earnings');
|
|
549
549
|
}
|
|
550
|
-
async function setAgentPricing(config, agentId, pricingMode, pricePerCallCents) {
|
|
550
|
+
async function setAgentPricing(config, agentId, pricingMode, pricePerCallCents, allowLocalDownload) {
|
|
551
551
|
return request(config, 'PUT', `/agents/${agentId}/pricing`, {
|
|
552
552
|
body: JSON.stringify({
|
|
553
553
|
pricing_mode: pricingMode,
|
|
554
554
|
price_per_call_cents: pricePerCallCents,
|
|
555
|
+
allow_local_download: allowLocalDownload,
|
|
555
556
|
}),
|
|
556
557
|
headers: { 'Content-Type': 'application/json' },
|
|
557
558
|
});
|
package/dist/lib/config.js
CHANGED
|
@@ -47,6 +47,7 @@ exports.getDefaultScope = getDefaultScope;
|
|
|
47
47
|
exports.setDefaultScope = setDefaultScope;
|
|
48
48
|
exports.getDefaultProvider = getDefaultProvider;
|
|
49
49
|
exports.setDefaultProvider = setDefaultProvider;
|
|
50
|
+
exports.unsetConfigKey = unsetConfigKey;
|
|
50
51
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
51
52
|
const path_1 = __importDefault(require("path"));
|
|
52
53
|
const os_1 = __importDefault(require("os"));
|
|
@@ -152,3 +153,21 @@ async function setDefaultProvider(provider) {
|
|
|
152
153
|
config.default_provider = provider;
|
|
153
154
|
await saveConfig(config);
|
|
154
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Remove a config key, restoring it to "not set" state.
|
|
158
|
+
* Maps CLI key names (kebab-case) to ConfigFile properties.
|
|
159
|
+
*/
|
|
160
|
+
const CONFIG_KEY_MAP = {
|
|
161
|
+
'default-format': 'default_formats',
|
|
162
|
+
'default-scope': 'default_scope',
|
|
163
|
+
'default-provider': 'default_provider',
|
|
164
|
+
};
|
|
165
|
+
async function unsetConfigKey(cliKey) {
|
|
166
|
+
const configProp = CONFIG_KEY_MAP[cliKey];
|
|
167
|
+
if (!configProp) {
|
|
168
|
+
throw new Error(`Unknown config key: ${cliKey}`);
|
|
169
|
+
}
|
|
170
|
+
const config = await loadConfig();
|
|
171
|
+
delete config[configProp];
|
|
172
|
+
await saveConfig(config);
|
|
173
|
+
}
|