@zq-silk/yui 0.6.13 → 0.6.15
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 +34 -5
- package/dist/cli/commandCatalog.js +172 -62
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli.js +102 -11
- package/dist/commands/configCommands.js +521 -171
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/executionAuditCommands.js +56 -3
- package/dist/commands/projectCommands.js +518 -55
- package/dist/commands/releaseCommands.js +0 -1
- package/dist/commands/taskActor.js +17 -0
- package/dist/commands/taskBaseCommands.js +29 -0
- package/dist/commands/taskCommands.js +577 -126
- package/dist/commands/taskContextCommand.js +36 -2
- package/dist/commands/taskNextActionCommand.js +48 -3
- package/dist/commands/taskPublicationCommands.js +319 -0
- package/dist/commands/taskRoleRuntimeStatus.js +83 -59
- package/dist/commands/telemetryCommands.js +14 -13
- package/dist/config/yuiConfig.js +161 -8
- package/dist/context/sessionContextBudget.js +71 -0
- package/dist/context/wakeNotification.js +65 -0
- package/dist/controller/clientRuntime.js +2 -1
- package/dist/controller/ephemeralResourceReaper.js +2 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +191 -16
- package/dist/controller/jobSupervisor.js +5 -4
- package/dist/controller/resourceCleanupLinux.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +3 -3
- package/dist/controller/runtime.js +88 -10
- package/dist/controller/updateReconciliation.js +4 -3
- package/dist/doctor/doctor.js +26 -7
- package/dist/executor/agentConfigurationCatalog.js +18 -0
- package/dist/executor/fileRoleLaunchPlanner.js +3 -3
- package/dist/lifecycle/contextBudgetRollover.js +81 -0
- package/dist/lifecycle/exactRunTerminalization.js +16 -2
- package/dist/lifecycle/providerErrorClass.js +33 -12
- package/dist/observability/executionAudit.js +214 -6
- package/dist/output/table.js +18 -0
- package/dist/repository/gitWorkspace.js +92 -0
- package/dist/repository/project.js +218 -4
- package/dist/repository/taskBaseFreshness.js +318 -0
- package/dist/repository/taskWorkspacePreparer.js +16 -2
- package/dist/review/deltaRecheck.js +253 -0
- package/dist/review/reviewConfig.js +31 -0
- package/dist/review/reviewFindingLedger.js +5 -1
- package/dist/review/reviewRound.js +156 -1
- package/dist/run/providerRetry.js +21 -3
- package/dist/run/providerRetryConfig.js +13 -60
- package/dist/run/recoveryProjection.js +198 -0
- package/dist/runtime/builtinAgentDrivers.js +3 -0
- package/dist/runtime/builtinTranscriptUsage.js +76 -32
- package/dist/runtime/continuationManager.js +17 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/launchDiagnostics.js +154 -0
- package/dist/runtime/lifecycleReservation.js +13 -0
- package/dist/runtime/providerContinuation.js +38 -0
- package/dist/runtime/providerContinuationReconciliationService.js +1 -0
- package/dist/runtime/providerErrorCodes.js +278 -0
- package/dist/runtime/runtimeHealthPolicy.js +20 -0
- package/dist/runtime/runtimeObservation.js +7 -1
- package/dist/runtime/runtimeProjection.js +115 -23
- package/dist/runtime/tmuxAdapters.js +242 -48
- package/dist/scheduler/activeRoleRunDelivery.js +139 -3
- package/dist/scheduler/activeTaskProgress.js +4 -3
- package/dist/scheduler/leaderWakeupProcessor.js +105 -15
- package/dist/scheduler/roleRunLiveness.js +2 -1
- package/dist/scheduler/roleRunStall.js +3 -2
- package/dist/scheduler/taskWake.js +72 -0
- package/dist/scheduler/wakeReason.js +64 -0
- package/dist/scheduler/wakeupQueue.js +2 -1
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/migration/productionRegistry.js +325 -1
- package/dist/storage/sqliteSchema.js +61 -2
- package/dist/storage/sqliteStore.js +129 -2
- package/dist/storage/storeRpc.js +1 -0
- package/dist/storage/taskStore.js +262 -5
- package/dist/storage/upgrade/recordVersions.js +6 -1
- package/dist/storage/upgrade/sqliteStateMigration.js +22 -2
- package/dist/task/completionReadiness.js +289 -0
- package/dist/task/publicationReference.js +123 -0
- package/dist/task/taskRecordReference.js +3 -1
- package/dist/telemetry/telemetryConfig.js +23 -18
- package/dist/telemetry/telemetryWiring.js +8 -8
- package/dist/tmux/tmuxManager.js +50 -9
- package/dist/web/assets/client/i18n.js +4 -0
- package/dist/web/assets/client/view.js +18 -0
- package/dist/web/webSnapshot.js +100 -10
- package/i18n/README.zh-CN.md +5 -5
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +49 -10
- package/skills/yui-operator/SKILL.md +17 -3
- package/skills/yui-worker/SKILL.md +8 -0
|
@@ -1,200 +1,550 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
-
import { DEFAULT_RECONCILIATION_INTERVAL_SECONDS, reconciliationIntervalMilliseconds, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode } from "../config/yuiConfig.js";
|
|
2
|
+
import { DEFAULT_CONTEXT_HARD_TOKENS, DEFAULT_CONTEXT_SOFT_TOKENS, DEFAULT_LEADER_NEXT_ACTION_MODE, DEFAULT_PROVIDER_RETRY_MODE, DEFAULT_RECONCILIATION_INTERVAL_SECONDS, DEFAULT_RESOURCES_GC_MODE, LEADER_NEXT_ACTION_MODES, PROVIDER_RETRY_MODES, reconciliationIntervalMilliseconds, resolveContextBudget, resolveGitBin, resolveLeaderNextActionMode, resolveProviderRetryAdapters, resolveProviderRetryMaxWindowMs, resolveProviderRetryMode, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveTelemetryMode, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveYieldReceiptReplay } from "../config/yuiConfig.js";
|
|
3
3
|
import { resolveTimeZone } from "../output/timePresentation.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
5
|
+
import { DEFAULT_DELTA_RECHECK_MAX_CHANGED_FILES, DEFAULT_DELTA_RECHECK_MAX_CHANGED_LINES, REVIEW_FINDING_LEDGER_MODES, REVIEW_DELTA_RECHECK_MODES, REVIEW_TRIGGERS } from "../review/reviewConfig.js";
|
|
6
|
+
/**
|
|
7
|
+
* One uniform key model for every durable Yui setting. `config show` displays
|
|
8
|
+
* each key's effective value, `config set <key> <value...>` updates one key,
|
|
9
|
+
* and `config clear <key>` resets it to its documented default. Strategy
|
|
10
|
+
* settings such as `review` and `leader-next-action` are keys like any other;
|
|
11
|
+
* they deliberately have no separate command trees. The stored YuiConfig
|
|
12
|
+
* shape is unchanged, so existing Homes keep working without migration.
|
|
13
|
+
*/
|
|
14
|
+
export const CONFIG_KEYS = [
|
|
15
|
+
"time-zone",
|
|
16
|
+
"reconciliation-interval-seconds",
|
|
17
|
+
"leader-next-action",
|
|
18
|
+
"context-budget",
|
|
19
|
+
"resources-gc-mode",
|
|
20
|
+
"resources-gc-auto-quarantine",
|
|
21
|
+
"provider-retry-mode",
|
|
22
|
+
"provider-retry-adapters",
|
|
23
|
+
"provider-retry-max-window-ms",
|
|
24
|
+
"yield-receipt-replay",
|
|
25
|
+
"tmux-bin",
|
|
26
|
+
"git-bin",
|
|
27
|
+
"telemetry-mode",
|
|
28
|
+
"telemetry-terminal-keep",
|
|
29
|
+
"telemetry-run-cap",
|
|
30
|
+
"review"
|
|
31
|
+
];
|
|
32
|
+
const CONFIG_USAGE = "Config usage: yui config show | yui config set <key> <value...> | yui config clear <key>.";
|
|
33
|
+
const CONFIG_SET_USAGE = `Config set usage: yui config set <key> <value...>; keys: ${CONFIG_KEYS.join(", ")}.`;
|
|
34
|
+
const CONFIG_CLEAR_USAGE = `Config clear usage: yui config clear <key>; keys: ${CONFIG_KEYS.join(", ")}.`;
|
|
35
|
+
const TIME_ZONE_SET_USAGE = "Config set usage: yui config set time-zone <IANA timezone>.";
|
|
36
|
+
const RECONCILIATION_SET_USAGE = "Config set usage: yui config set reconciliation-interval-seconds <5-300>.";
|
|
37
|
+
const RESOURCES_GC_MODE_SET_USAGE = "Config set usage: yui config set resources-gc-mode <report|quarantine>.";
|
|
38
|
+
const RESOURCES_GC_AUTO_QUARANTINE_SET_USAGE = "Config set usage: yui config set resources-gc-auto-quarantine <true|false>.";
|
|
39
|
+
const LEADER_NEXT_ACTION_SET_USAGE = `Config set usage: yui config set leader-next-action <${LEADER_NEXT_ACTION_MODES.join("|")}>.`;
|
|
40
|
+
const CONTEXT_BUDGET_SET_USAGE = "Config set usage: yui config set context-budget [--soft-tokens <n>] [--hard-tokens <n>].";
|
|
41
|
+
const REVIEW_SET_USAGE = "Config set usage: yui config set review --role <global-role> --trigger <always|leader|final> "
|
|
42
|
+
+ "[--finding-ledger <shadow|enforce>] [--delta-recheck <enabled|disabled>] "
|
|
43
|
+
+ "[--delta-recheck-max-lines <n>] [--delta-recheck-max-files <n>].";
|
|
11
44
|
export function runConfigCommand(args, store) {
|
|
12
45
|
const [command, ...rest] = args;
|
|
13
|
-
if (command === "review")
|
|
14
|
-
return runReviewConfigCommand(rest, store);
|
|
15
|
-
if (command === "leader-next-action")
|
|
16
|
-
return runLeaderNextActionConfigCommand(rest, store);
|
|
17
46
|
if (command === "show") {
|
|
18
47
|
if (rest.length !== 0)
|
|
19
48
|
throw usageError("Config show usage: yui config show.");
|
|
20
49
|
const config = store.getConfig();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
reconciliationIntervalMilliseconds(reconciliationIntervalSeconds);
|
|
24
|
-
return [
|
|
25
|
-
`Time zone: ${resolveTimeZone(config.timeZone)}`,
|
|
26
|
-
`Reconciliation interval: ${reconciliationIntervalSeconds} seconds`,
|
|
27
|
-
`Leader next-action mode: ${resolveLeaderNextActionMode(config.leaderNextActionMode)}`,
|
|
28
|
-
`Resources GC mode: ${resolveResourcesGcMode(config.resourcesGcMode)}`,
|
|
29
|
-
`Resources GC auto-quarantine: ${resolveResourcesGcAutoQuarantine(config.resourcesGcAutoQuarantine) ? "on" : "off"}`,
|
|
30
|
-
""
|
|
31
|
-
].join("\n");
|
|
50
|
+
reconciliationIntervalMilliseconds(config.reconciliationIntervalSeconds);
|
|
51
|
+
return { output: renderConfigShow(config), data: effectiveConfigData(config) };
|
|
32
52
|
}
|
|
33
|
-
if (command === "set")
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
if (command === "set")
|
|
54
|
+
return { output: runConfigSet(rest, store) };
|
|
55
|
+
if (command === "clear")
|
|
56
|
+
return { output: runConfigClear(rest, store) };
|
|
57
|
+
throw usageError(command === undefined ? "Config command is required." : `Unknown command: config ${command}`, CONFIG_USAGE);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* `config show` is row/column data, so it renders through the shared
|
|
61
|
+
* `renderTable` like every other list command. The column contract follows
|
|
62
|
+
* the shared rules: left-aligned cells, widths fitted between min/max,
|
|
63
|
+
* terminal width from `defaultTableWidth()`, and empty values as empty cells.
|
|
64
|
+
*/
|
|
65
|
+
function renderConfigShow(config) {
|
|
66
|
+
return renderTable("Yui configuration", [
|
|
67
|
+
{ header: "Setting", minWidth: 20, maxWidth: 32 },
|
|
68
|
+
{ header: "Value", minWidth: 10, maxWidth: 60 }
|
|
69
|
+
], CONFIG_KEY_HANDLERS.map((handler) => [handler.showLabel, handler.showValue(config)]), defaultTableWidth());
|
|
70
|
+
}
|
|
71
|
+
/** Effective values under the same field names as the stored YuiConfig. */
|
|
72
|
+
function effectiveConfigData(config) {
|
|
73
|
+
return {
|
|
74
|
+
timeZone: resolveTimeZone(config.timeZone),
|
|
75
|
+
reconciliationIntervalSeconds: config.reconciliationIntervalSeconds
|
|
76
|
+
?? DEFAULT_RECONCILIATION_INTERVAL_SECONDS,
|
|
77
|
+
leaderNextActionMode: resolveLeaderNextActionMode(config.leaderNextActionMode),
|
|
78
|
+
resourcesGcMode: resolveResourcesGcMode(config.resourcesGcMode),
|
|
79
|
+
resourcesGcAutoQuarantine: resolveResourcesGcAutoQuarantine(config.resourcesGcAutoQuarantine),
|
|
80
|
+
review: config.review === undefined
|
|
81
|
+
? null
|
|
82
|
+
: {
|
|
83
|
+
roleName: config.review.roleName,
|
|
84
|
+
trigger: config.review.trigger,
|
|
85
|
+
findingLedger: config.review.findingLedger ?? "shadow",
|
|
86
|
+
deltaRecheck: config.review.deltaRecheck ?? "disabled",
|
|
87
|
+
deltaRecheckMaxChangedLines: config.review.deltaRecheckMaxChangedLines
|
|
88
|
+
?? DEFAULT_DELTA_RECHECK_MAX_CHANGED_LINES,
|
|
89
|
+
deltaRecheckMaxChangedFiles: config.review.deltaRecheckMaxChangedFiles
|
|
90
|
+
?? DEFAULT_DELTA_RECHECK_MAX_CHANGED_FILES
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function runConfigSet(args, store) {
|
|
95
|
+
const [key, ...values] = args;
|
|
96
|
+
if (key === undefined)
|
|
97
|
+
throw usageError(CONFIG_SET_USAGE);
|
|
98
|
+
const handler = CONFIG_KEY_HANDLERS.find((entry) => entry.key === key);
|
|
99
|
+
if (handler === undefined) {
|
|
100
|
+
throw usageError(`Unknown config key: ${key}`, CONFIG_SET_USAGE);
|
|
101
|
+
}
|
|
102
|
+
return handler.set(values, store);
|
|
103
|
+
}
|
|
104
|
+
function runConfigClear(args, store) {
|
|
105
|
+
const [key, ...rest] = args;
|
|
106
|
+
if (key === undefined)
|
|
107
|
+
throw usageError(CONFIG_CLEAR_USAGE);
|
|
108
|
+
if (rest.length !== 0)
|
|
109
|
+
throw usageError(`Config clear usage: yui config clear ${key}.`);
|
|
110
|
+
const handler = CONFIG_KEY_HANDLERS.find((entry) => entry.key === key);
|
|
111
|
+
if (handler === undefined) {
|
|
112
|
+
throw usageError(`Unknown config key: ${key}`, CONFIG_CLEAR_USAGE);
|
|
113
|
+
}
|
|
114
|
+
return handler.clear(store);
|
|
115
|
+
}
|
|
116
|
+
function saveConfigKey(store, update) {
|
|
117
|
+
store.transaction((tx) => {
|
|
118
|
+
tx.saveConfig(update(tx.getConfig()));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function validatedConfigValue(validate, usage) {
|
|
122
|
+
try {
|
|
123
|
+
return validate();
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (error instanceof TypeError) {
|
|
127
|
+
throw usageError(error.message, usage);
|
|
36
128
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function parseBudgetToken(value, label) {
|
|
133
|
+
if (!/^\d+$/.test(value)) {
|
|
134
|
+
throw usageError(`${label} must be a positive integer.`);
|
|
135
|
+
}
|
|
136
|
+
return Number(value);
|
|
137
|
+
}
|
|
138
|
+
function parseReconciliationIntervalSeconds(value) {
|
|
139
|
+
if (!/^\d+$/.test(value))
|
|
140
|
+
return Number.NaN;
|
|
141
|
+
return Number(value);
|
|
142
|
+
}
|
|
143
|
+
function parseBooleanConfigValue(value) {
|
|
144
|
+
return value === "true" ? true : value === "false" ? false : value;
|
|
145
|
+
}
|
|
146
|
+
function parsePositiveIntegerOption(value, usage) {
|
|
147
|
+
if (value === undefined)
|
|
148
|
+
return undefined;
|
|
149
|
+
if (!/^\d+$/.test(value) || Number(value) < 1) {
|
|
150
|
+
throw usageError(usage);
|
|
151
|
+
}
|
|
152
|
+
return Number(value);
|
|
153
|
+
}
|
|
154
|
+
const CONFIG_KEY_HANDLERS = [
|
|
155
|
+
{
|
|
156
|
+
key: "time-zone",
|
|
157
|
+
showLabel: "Time zone",
|
|
158
|
+
showValue: (config) => resolveTimeZone(config.timeZone),
|
|
159
|
+
set(args, store) {
|
|
160
|
+
if (args.length !== 1)
|
|
161
|
+
throw usageError(TIME_ZONE_SET_USAGE);
|
|
162
|
+
const timeZone = validatedConfigValue(() => resolveTimeZone(args[0]), TIME_ZONE_SET_USAGE);
|
|
163
|
+
saveConfigKey(store, (config) => ({ ...config, timeZone }));
|
|
42
164
|
return `Time zone set to ${timeZone}\n`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
tx.saveConfig({ ...tx.getConfig(), reconciliationIntervalSeconds });
|
|
165
|
+
},
|
|
166
|
+
clear(store) {
|
|
167
|
+
saveConfigKey(store, (config) => {
|
|
168
|
+
const { timeZone: _removed, ...rest } = config;
|
|
169
|
+
return rest;
|
|
49
170
|
});
|
|
171
|
+
return `Time zone reset to ${resolveTimeZone(undefined)}\n`;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
key: "reconciliation-interval-seconds",
|
|
176
|
+
showLabel: "Reconciliation interval",
|
|
177
|
+
showValue: (config) => `${config.reconciliationIntervalSeconds ?? DEFAULT_RECONCILIATION_INTERVAL_SECONDS} seconds`,
|
|
178
|
+
set(args, store) {
|
|
179
|
+
if (args.length !== 1)
|
|
180
|
+
throw usageError(RECONCILIATION_SET_USAGE);
|
|
181
|
+
const reconciliationIntervalSeconds = parseReconciliationIntervalSeconds(args[0]);
|
|
182
|
+
validatedConfigValue(() => reconciliationIntervalMilliseconds(reconciliationIntervalSeconds), RECONCILIATION_SET_USAGE);
|
|
183
|
+
saveConfigKey(store, (config) => ({ ...config, reconciliationIntervalSeconds }));
|
|
50
184
|
return `Reconciliation interval set to ${reconciliationIntervalSeconds} seconds\n`;
|
|
185
|
+
},
|
|
186
|
+
clear(store) {
|
|
187
|
+
saveConfigKey(store, (config) => {
|
|
188
|
+
const { reconciliationIntervalSeconds: _removed, ...rest } = config;
|
|
189
|
+
return rest;
|
|
190
|
+
});
|
|
191
|
+
return `Reconciliation interval reset to ${DEFAULT_RECONCILIATION_INTERVAL_SECONDS} seconds\n`;
|
|
51
192
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
key: "leader-next-action",
|
|
196
|
+
showLabel: "Leader next-action mode",
|
|
197
|
+
showValue: (config) => resolveLeaderNextActionMode(config.leaderNextActionMode),
|
|
198
|
+
set(args, store) {
|
|
199
|
+
if (args.length !== 1)
|
|
200
|
+
throw usageError(LEADER_NEXT_ACTION_SET_USAGE);
|
|
201
|
+
let mode;
|
|
202
|
+
try {
|
|
203
|
+
mode = resolveLeaderNextActionMode(args[0]);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
throw usageError(error instanceof Error ? error.message : String(error), LEADER_NEXT_ACTION_SET_USAGE);
|
|
207
|
+
}
|
|
208
|
+
saveConfigKey(store, (config) => ({ ...config, leaderNextActionMode: mode }));
|
|
209
|
+
return `Leader next-action mode set to ${mode}\n`;
|
|
210
|
+
},
|
|
211
|
+
clear(store) {
|
|
212
|
+
saveConfigKey(store, (config) => {
|
|
213
|
+
const { leaderNextActionMode: _removed, ...rest } = config;
|
|
214
|
+
return rest;
|
|
56
215
|
});
|
|
57
|
-
return `
|
|
216
|
+
return `Leader next-action mode reset to ${DEFAULT_LEADER_NEXT_ACTION_MODE}\n`;
|
|
58
217
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
key: "context-budget",
|
|
221
|
+
showLabel: "Context budget",
|
|
222
|
+
showValue: (config) => {
|
|
223
|
+
const budget = resolveContextBudget(config.contextBudget);
|
|
224
|
+
return `soft ${budget.softTokens} / hard ${budget.hardTokens} tokens`;
|
|
225
|
+
},
|
|
226
|
+
set(args, store) {
|
|
227
|
+
if (args.length !== 2 && args.length !== 4)
|
|
228
|
+
throw usageError(CONTEXT_BUDGET_SET_USAGE);
|
|
229
|
+
const options = new Map();
|
|
230
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
231
|
+
const name = args[index];
|
|
232
|
+
const value = args[index + 1];
|
|
233
|
+
if (!["--soft-tokens", "--hard-tokens"].includes(name)
|
|
234
|
+
|| value === undefined
|
|
235
|
+
|| options.has(name)) {
|
|
236
|
+
throw usageError(CONTEXT_BUDGET_SET_USAGE);
|
|
237
|
+
}
|
|
238
|
+
options.set(name, value);
|
|
239
|
+
}
|
|
240
|
+
const current = resolveContextBudget(store.getConfig().contextBudget);
|
|
241
|
+
const next = {
|
|
242
|
+
...(options.get("--soft-tokens") === undefined
|
|
243
|
+
? {}
|
|
244
|
+
: { softTokens: parseBudgetToken(options.get("--soft-tokens"), "--soft-tokens") }),
|
|
245
|
+
...(options.get("--hard-tokens") === undefined
|
|
246
|
+
? {}
|
|
247
|
+
: { hardTokens: parseBudgetToken(options.get("--hard-tokens"), "--hard-tokens") })
|
|
248
|
+
};
|
|
249
|
+
const resolved = resolveContextBudget({ ...current, ...next });
|
|
250
|
+
saveConfigKey(store, (config) => ({ ...config, contextBudget: resolved }));
|
|
251
|
+
return `Context budget set to soft ${resolved.softTokens} / hard ${resolved.hardTokens} tokens\n`;
|
|
252
|
+
},
|
|
253
|
+
clear(store) {
|
|
254
|
+
saveConfigKey(store, (config) => {
|
|
255
|
+
const { contextBudget: _removed, ...rest } = config;
|
|
256
|
+
return rest;
|
|
257
|
+
});
|
|
258
|
+
return `Context budget reset to soft ${DEFAULT_CONTEXT_SOFT_TOKENS} / hard ${DEFAULT_CONTEXT_HARD_TOKENS} tokens\n`;
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
key: "resources-gc-mode",
|
|
263
|
+
showLabel: "Resources GC mode",
|
|
264
|
+
showValue: (config) => resolveResourcesGcMode(config.resourcesGcMode),
|
|
265
|
+
set(args, store) {
|
|
266
|
+
if (args.length !== 1)
|
|
267
|
+
throw usageError(RESOURCES_GC_MODE_SET_USAGE);
|
|
268
|
+
const resourcesGcMode = validatedConfigValue(() => resolveResourcesGcMode(args[0]), RESOURCES_GC_MODE_SET_USAGE);
|
|
269
|
+
saveConfigKey(store, (config) => ({ ...config, resourcesGcMode }));
|
|
270
|
+
return `Resources GC mode set to ${resourcesGcMode}\n`;
|
|
271
|
+
},
|
|
272
|
+
clear(store) {
|
|
273
|
+
saveConfigKey(store, (config) => {
|
|
274
|
+
const { resourcesGcMode: _removed, ...rest } = config;
|
|
275
|
+
return rest;
|
|
63
276
|
});
|
|
277
|
+
return `Resources GC mode reset to ${DEFAULT_RESOURCES_GC_MODE}\n`;
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
key: "resources-gc-auto-quarantine",
|
|
282
|
+
showLabel: "Resources GC auto-quarantine",
|
|
283
|
+
showValue: (config) => (resolveResourcesGcAutoQuarantine(config.resourcesGcAutoQuarantine) ? "on" : "off"),
|
|
284
|
+
set(args, store) {
|
|
285
|
+
if (args.length !== 1)
|
|
286
|
+
throw usageError(RESOURCES_GC_AUTO_QUARANTINE_SET_USAGE);
|
|
287
|
+
const resourcesGcAutoQuarantine = validatedConfigValue(() => resolveResourcesGcAutoQuarantine(parseBooleanConfigValue(args[0])), RESOURCES_GC_AUTO_QUARANTINE_SET_USAGE);
|
|
288
|
+
saveConfigKey(store, (config) => ({ ...config, resourcesGcAutoQuarantine }));
|
|
64
289
|
return `Resources GC auto-quarantine set to ${resourcesGcAutoQuarantine ? "on" : "off"}\n`;
|
|
290
|
+
},
|
|
291
|
+
clear(store) {
|
|
292
|
+
saveConfigKey(store, (config) => {
|
|
293
|
+
const { resourcesGcAutoQuarantine: _removed, ...rest } = config;
|
|
294
|
+
return rest;
|
|
295
|
+
});
|
|
296
|
+
return "Resources GC auto-quarantine reset to off\n";
|
|
65
297
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
key: "provider-retry-mode",
|
|
301
|
+
showLabel: "Provider retry mode",
|
|
302
|
+
showValue: (config) => resolveProviderRetryMode(config.providerRetryMode),
|
|
303
|
+
set(args, store) {
|
|
304
|
+
if (args.length !== 1)
|
|
305
|
+
throw usageError(`Config set usage: yui config set provider-retry-mode <${PROVIDER_RETRY_MODES.join("|")}>.`);
|
|
306
|
+
const mode = validatedConfigValue(() => resolveProviderRetryMode(args[0]), `Config set usage: yui config set provider-retry-mode <${PROVIDER_RETRY_MODES.join("|")}>.`);
|
|
307
|
+
saveConfigKey(store, (config) => ({ ...config, providerRetryMode: mode }));
|
|
308
|
+
return `Provider retry mode set to ${mode}\n`;
|
|
309
|
+
},
|
|
310
|
+
clear(store) {
|
|
311
|
+
saveConfigKey(store, (config) => {
|
|
312
|
+
const { providerRetryMode: _removed, ...rest } = config;
|
|
313
|
+
return rest;
|
|
314
|
+
});
|
|
315
|
+
return `Provider retry mode reset to ${DEFAULT_PROVIDER_RETRY_MODE}\n`;
|
|
77
316
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
store
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
key: "provider-retry-adapters",
|
|
320
|
+
showLabel: "Provider retry adapters",
|
|
321
|
+
showValue: (config) => resolveProviderRetryAdapters(config.providerRetryAdapters).join(", ") || "none",
|
|
322
|
+
set(args, store) {
|
|
323
|
+
if (args.length !== 1)
|
|
324
|
+
throw usageError("Config set usage: yui config set provider-retry-adapters <all|claude,codex|off>.");
|
|
325
|
+
const raw = args[0].trim().toLowerCase();
|
|
326
|
+
const adapters = raw === "off" || raw === "" || raw === "0"
|
|
327
|
+
? []
|
|
328
|
+
: validatedConfigValue(() => resolveProviderRetryAdapters(raw.split(",")), "Config set usage: yui config set provider-retry-adapters <all|claude,codex|off>.");
|
|
329
|
+
saveConfigKey(store, (config) => ({ ...config, providerRetryAdapters: adapters }));
|
|
330
|
+
return `Provider retry adapters set to ${adapters.join(", ") || "none"}\n`;
|
|
331
|
+
},
|
|
332
|
+
clear(store) {
|
|
333
|
+
saveConfigKey(store, (config) => {
|
|
334
|
+
const { providerRetryAdapters: _removed, ...rest } = config;
|
|
335
|
+
return rest;
|
|
336
|
+
});
|
|
337
|
+
return "Provider retry adapters reset to all supported\n";
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
key: "provider-retry-max-window-ms",
|
|
342
|
+
showLabel: "Provider retry max window",
|
|
343
|
+
showValue: (config) => `${resolveProviderRetryMaxWindowMs(config.providerRetryMaxWindowMs)} ms`,
|
|
344
|
+
set(args, store) {
|
|
345
|
+
if (args.length !== 1)
|
|
346
|
+
throw usageError("Config set usage: yui config set provider-retry-max-window-ms <milliseconds>.");
|
|
347
|
+
const maxWindowMs = validatedConfigValue(() => resolveProviderRetryMaxWindowMs(Number(args[0])), "Config set usage: yui config set provider-retry-max-window-ms <milliseconds>.");
|
|
348
|
+
saveConfigKey(store, (config) => ({ ...config, providerRetryMaxWindowMs: maxWindowMs }));
|
|
349
|
+
return `Provider retry max window set to ${maxWindowMs} ms\n`;
|
|
350
|
+
},
|
|
351
|
+
clear(store) {
|
|
352
|
+
saveConfigKey(store, (config) => {
|
|
353
|
+
const { providerRetryMaxWindowMs: _removed, ...rest } = config;
|
|
354
|
+
return rest;
|
|
355
|
+
});
|
|
356
|
+
return "Provider retry max window reset to default\n";
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
key: "yield-receipt-replay",
|
|
361
|
+
showLabel: "Yield receipt replay",
|
|
362
|
+
showValue: (config) => (resolveYieldReceiptReplay(config.yieldReceiptReplay) ? "on" : "off"),
|
|
363
|
+
set(args, store) {
|
|
364
|
+
if (args.length !== 1)
|
|
365
|
+
throw usageError("Config set usage: yui config set yield-receipt-replay <true|false>.");
|
|
366
|
+
const yieldReceiptReplay = validatedConfigValue(() => resolveYieldReceiptReplay(parseBooleanConfigValue(args[0])), "Config set usage: yui config set yield-receipt-replay <true|false>.");
|
|
367
|
+
saveConfigKey(store, (config) => ({ ...config, yieldReceiptReplay }));
|
|
368
|
+
return `Yield receipt replay set to ${yieldReceiptReplay ? "on" : "off"}\n`;
|
|
369
|
+
},
|
|
370
|
+
clear(store) {
|
|
371
|
+
saveConfigKey(store, (config) => {
|
|
372
|
+
const { yieldReceiptReplay: _removed, ...rest } = config;
|
|
373
|
+
return rest;
|
|
374
|
+
});
|
|
375
|
+
return "Yield receipt replay reset to on\n";
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
key: "tmux-bin",
|
|
380
|
+
showLabel: "Tmux bin",
|
|
381
|
+
showValue: (config) => resolveTmuxBin(config.tmuxBin),
|
|
382
|
+
set(args, store) {
|
|
383
|
+
if (args.length !== 1)
|
|
384
|
+
throw usageError("Config set usage: yui config set tmux-bin <path>.");
|
|
385
|
+
const tmuxBin = validatedConfigValue(() => resolveTmuxBin(args[0]), "Config set usage: yui config set tmux-bin <path>.");
|
|
386
|
+
saveConfigKey(store, (config) => ({ ...config, tmuxBin }));
|
|
387
|
+
return `Tmux bin set to ${tmuxBin}\n`;
|
|
388
|
+
},
|
|
389
|
+
clear(store) {
|
|
390
|
+
saveConfigKey(store, (config) => {
|
|
391
|
+
const { tmuxBin: _removed, ...rest } = config;
|
|
392
|
+
return rest;
|
|
393
|
+
});
|
|
394
|
+
return "Tmux bin reset to tmux\n";
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
key: "git-bin",
|
|
399
|
+
showLabel: "Git bin",
|
|
400
|
+
showValue: (config) => resolveGitBin(config.gitBin),
|
|
401
|
+
set(args, store) {
|
|
402
|
+
if (args.length !== 1)
|
|
403
|
+
throw usageError("Config set usage: yui config set git-bin <path>.");
|
|
404
|
+
const gitBin = validatedConfigValue(() => resolveGitBin(args[0]), "Config set usage: yui config set git-bin <path>.");
|
|
405
|
+
saveConfigKey(store, (config) => ({ ...config, gitBin }));
|
|
406
|
+
return `Git bin set to ${gitBin}\n`;
|
|
407
|
+
},
|
|
408
|
+
clear(store) {
|
|
409
|
+
saveConfigKey(store, (config) => {
|
|
410
|
+
const { gitBin: _removed, ...rest } = config;
|
|
411
|
+
return rest;
|
|
412
|
+
});
|
|
413
|
+
return "Git bin reset to git\n";
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
key: "telemetry-mode",
|
|
418
|
+
showLabel: "Telemetry mode",
|
|
419
|
+
showValue: (config) => resolveTelemetryMode(config.telemetryMode),
|
|
420
|
+
set(args, store) {
|
|
421
|
+
if (args.length !== 1)
|
|
422
|
+
throw usageError("Config set usage: yui config set telemetry-mode <legacy|dual|bounded>.");
|
|
423
|
+
const telemetryMode = validatedConfigValue(() => resolveTelemetryMode(args[0]), "Config set usage: yui config set telemetry-mode <legacy|dual|bounded>.");
|
|
424
|
+
saveConfigKey(store, (config) => ({ ...config, telemetryMode }));
|
|
425
|
+
return `Telemetry mode set to ${telemetryMode}\n`;
|
|
426
|
+
},
|
|
427
|
+
clear(store) {
|
|
428
|
+
saveConfigKey(store, (config) => {
|
|
429
|
+
const { telemetryMode: _removed, ...rest } = config;
|
|
430
|
+
return rest;
|
|
431
|
+
});
|
|
432
|
+
return "Telemetry mode reset to legacy\n";
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
key: "telemetry-terminal-keep",
|
|
437
|
+
showLabel: "Telemetry terminal keep",
|
|
438
|
+
showValue: (config) => String(resolveTelemetryTerminalKeep(config.telemetryTerminalKeep)),
|
|
439
|
+
set(args, store) {
|
|
440
|
+
if (args.length !== 1)
|
|
441
|
+
throw usageError("Config set usage: yui config set telemetry-terminal-keep <n>.");
|
|
442
|
+
const telemetryTerminalKeep = validatedConfigValue(() => resolveTelemetryTerminalKeep(Number(args[0])), "Config set usage: yui config set telemetry-terminal-keep <n>.");
|
|
443
|
+
saveConfigKey(store, (config) => ({ ...config, telemetryTerminalKeep }));
|
|
444
|
+
return `Telemetry terminal keep set to ${telemetryTerminalKeep}\n`;
|
|
445
|
+
},
|
|
446
|
+
clear(store) {
|
|
447
|
+
saveConfigKey(store, (config) => {
|
|
448
|
+
const { telemetryTerminalKeep: _removed, ...rest } = config;
|
|
449
|
+
return rest;
|
|
450
|
+
});
|
|
451
|
+
return "Telemetry terminal keep reset to default\n";
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
key: "telemetry-run-cap",
|
|
456
|
+
showLabel: "Telemetry run cap",
|
|
457
|
+
showValue: (config) => String(resolveTelemetryRunCap(config.telemetryRunCap)),
|
|
458
|
+
set(args, store) {
|
|
459
|
+
if (args.length !== 1)
|
|
460
|
+
throw usageError("Config set usage: yui config set telemetry-run-cap <n>.");
|
|
461
|
+
const telemetryRunCap = validatedConfigValue(() => resolveTelemetryRunCap(Number(args[0])), "Config set usage: yui config set telemetry-run-cap <n>.");
|
|
462
|
+
saveConfigKey(store, (config) => ({ ...config, telemetryRunCap }));
|
|
463
|
+
return `Telemetry run cap set to ${telemetryRunCap}\n`;
|
|
464
|
+
},
|
|
465
|
+
clear(store) {
|
|
466
|
+
saveConfigKey(store, (config) => {
|
|
467
|
+
const { telemetryRunCap: _removed, ...rest } = config;
|
|
468
|
+
return rest;
|
|
469
|
+
});
|
|
470
|
+
return "Telemetry run cap reset to default\n";
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
key: "review",
|
|
475
|
+
showLabel: "Review",
|
|
476
|
+
showValue: (config) => (config.review === undefined
|
|
477
|
+
? "disabled"
|
|
478
|
+
: `${config.review.roleName} (${config.review.trigger}; finding ledger: ${config.review.findingLedger ?? "shadow"}`
|
|
479
|
+
+ `${config.review.deltaRecheck === "enabled" ? "; delta recheck: enabled" : ""})`),
|
|
480
|
+
set(args, store) {
|
|
481
|
+
if (args.length < 4 || args.length % 2 !== 0)
|
|
482
|
+
throw usageError(REVIEW_SET_USAGE);
|
|
483
|
+
const options = new Map();
|
|
484
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
485
|
+
const name = args[index];
|
|
486
|
+
const value = args[index + 1];
|
|
487
|
+
if (!["--role", "--trigger", "--finding-ledger", "--delta-recheck",
|
|
488
|
+
"--delta-recheck-max-lines", "--delta-recheck-max-files"].includes(name)
|
|
489
|
+
|| value === undefined
|
|
490
|
+
|| options.has(name)) {
|
|
491
|
+
throw usageError(REVIEW_SET_USAGE);
|
|
492
|
+
}
|
|
493
|
+
options.set(name, value);
|
|
144
494
|
}
|
|
145
|
-
options.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|| !REVIEW_TRIGGERS.includes(rawTrigger)) {
|
|
152
|
-
throw usageError(usage);
|
|
153
|
-
}
|
|
154
|
-
if (store.getGlobalRole?.(roleName) === null) {
|
|
155
|
-
throw usageError(`Global Role not found: ${roleName}.`);
|
|
156
|
-
}
|
|
157
|
-
const trigger = rawTrigger;
|
|
158
|
-
const rawLedgerMode = options.get("--finding-ledger")?.trim();
|
|
159
|
-
let findingLedger;
|
|
160
|
-
if (rawLedgerMode !== undefined) {
|
|
161
|
-
if (!REVIEW_FINDING_LEDGER_MODES.includes(rawLedgerMode)) {
|
|
162
|
-
throw usageError(usage);
|
|
495
|
+
const roleName = options.get("--role")?.trim();
|
|
496
|
+
const rawTrigger = options.get("--trigger")?.trim();
|
|
497
|
+
if (roleName === undefined || roleName.length === 0
|
|
498
|
+
|| rawTrigger === undefined
|
|
499
|
+
|| !REVIEW_TRIGGERS.includes(rawTrigger)) {
|
|
500
|
+
throw usageError(REVIEW_SET_USAGE);
|
|
163
501
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
502
|
+
if (store.getGlobalRole?.(roleName) === null) {
|
|
503
|
+
throw usageError(`Global Role not found: ${roleName}.`);
|
|
504
|
+
}
|
|
505
|
+
const trigger = rawTrigger;
|
|
506
|
+
const rawLedgerMode = options.get("--finding-ledger")?.trim();
|
|
507
|
+
let findingLedger;
|
|
508
|
+
if (rawLedgerMode !== undefined) {
|
|
509
|
+
if (!REVIEW_FINDING_LEDGER_MODES.includes(rawLedgerMode)) {
|
|
510
|
+
throw usageError(REVIEW_SET_USAGE);
|
|
511
|
+
}
|
|
512
|
+
findingLedger = rawLedgerMode;
|
|
513
|
+
}
|
|
514
|
+
const rawDeltaRecheck = options.get("--delta-recheck")?.trim();
|
|
515
|
+
let deltaRecheck;
|
|
516
|
+
if (rawDeltaRecheck !== undefined) {
|
|
517
|
+
if (!REVIEW_DELTA_RECHECK_MODES.includes(rawDeltaRecheck)) {
|
|
518
|
+
throw usageError(REVIEW_SET_USAGE);
|
|
519
|
+
}
|
|
520
|
+
deltaRecheck = rawDeltaRecheck;
|
|
521
|
+
}
|
|
522
|
+
const deltaRecheckMaxChangedLines = parsePositiveIntegerOption(options.get("--delta-recheck-max-lines"), REVIEW_SET_USAGE);
|
|
523
|
+
const deltaRecheckMaxChangedFiles = parsePositiveIntegerOption(options.get("--delta-recheck-max-files"), REVIEW_SET_USAGE);
|
|
524
|
+
saveConfigKey(store, (config) => ({
|
|
525
|
+
...config,
|
|
169
526
|
review: {
|
|
170
527
|
roleName,
|
|
171
528
|
trigger,
|
|
172
|
-
...(findingLedger === undefined ? {} : { findingLedger })
|
|
529
|
+
...(findingLedger === undefined ? {} : { findingLedger }),
|
|
530
|
+
...(deltaRecheck === undefined ? {} : { deltaRecheck }),
|
|
531
|
+
...(deltaRecheckMaxChangedLines === undefined
|
|
532
|
+
? {}
|
|
533
|
+
: { deltaRecheckMaxChangedLines }),
|
|
534
|
+
...(deltaRecheckMaxChangedFiles === undefined
|
|
535
|
+
? {}
|
|
536
|
+
: { deltaRecheckMaxChangedFiles })
|
|
173
537
|
}
|
|
538
|
+
}));
|
|
539
|
+
return `Review set to ${roleName} (${trigger}; finding ledger: ${findingLedger ?? "shadow"}; `
|
|
540
|
+
+ `delta recheck: ${deltaRecheck ?? "disabled"})\n`;
|
|
541
|
+
},
|
|
542
|
+
clear(store) {
|
|
543
|
+
saveConfigKey(store, (config) => {
|
|
544
|
+
const { review: _removed, ...rest } = config;
|
|
545
|
+
return rest;
|
|
174
546
|
});
|
|
175
|
-
|
|
176
|
-
return `Review set to ${roleName} (${trigger}; finding ledger: ${findingLedger ?? "shadow"})\n`;
|
|
177
|
-
}
|
|
178
|
-
throw usageError(command === undefined
|
|
179
|
-
? "Config review command is required."
|
|
180
|
-
: `Unknown command: config review ${command}`);
|
|
181
|
-
}
|
|
182
|
-
function parseReconciliationIntervalSeconds(value) {
|
|
183
|
-
if (!/^\d+$/.test(value))
|
|
184
|
-
return Number.NaN;
|
|
185
|
-
return Number(value);
|
|
186
|
-
}
|
|
187
|
-
function validatedConfigValue(validate) {
|
|
188
|
-
try {
|
|
189
|
-
return validate();
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
if (error instanceof TypeError) {
|
|
193
|
-
throw usageError(error.message, CONFIG_SET_USAGE);
|
|
547
|
+
return "Review disabled\n";
|
|
194
548
|
}
|
|
195
|
-
throw error;
|
|
196
549
|
}
|
|
197
|
-
|
|
198
|
-
function configSetUsageError() {
|
|
199
|
-
return usageError(CONFIG_SET_USAGE);
|
|
200
|
-
}
|
|
550
|
+
];
|