@zq-silk/yui 0.2.0 → 0.4.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/ARCHITECTURE.md +603 -133
- package/README.md +806 -31
- package/dist/agent/agent.js +2 -1
- package/dist/agent/argumentPolicy.js +3 -1
- package/dist/agent/launchEnvironment.js +106 -0
- package/dist/agent/managedRuntimeEnvironment.js +34 -0
- package/dist/brief/taskBrief.js +11 -1
- package/dist/cli/agentConfigurationPicker.js +287 -0
- package/dist/cli/commandCatalog.js +488 -60
- package/dist/cli/completion.js +146 -22
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli/interactionCandidates.js +53 -15
- package/dist/cli/interactionPolicy.js +267 -30
- package/dist/cli/interactiveSelection.js +6 -2
- package/dist/cli/invocationRouter.js +5 -1
- package/dist/cli/operatorWizard.js +87 -0
- package/dist/cli/roleOptionCatalog.js +1 -0
- package/dist/cli/roleWizard.js +185 -21
- package/dist/cli/updateCommand.js +62 -19
- package/dist/cli/updateOrchestrator.js +539 -0
- package/dist/cli/updatePorts.js +1119 -0
- package/dist/cli/upgradeCommand.js +112 -0
- package/dist/cli.js +1420 -86
- package/dist/commands/agentCommands.js +146 -3
- package/dist/commands/configCommands.js +126 -0
- package/dist/commands/controllerCommands.js +365 -0
- package/dist/commands/globalRoleCommands.js +168 -126
- package/dist/commands/jobCommands.js +18 -8
- package/dist/commands/operatorCommands.js +159 -9
- package/dist/commands/profileCommands.js +203 -0
- package/dist/commands/projectCommands.js +650 -0
- package/dist/commands/roleConfiguration.js +85 -24
- package/dist/commands/roleRuntimeGuard.js +12 -0
- package/dist/commands/roleSkillValidation.js +47 -0
- package/dist/commands/taskActor.js +127 -0
- package/dist/commands/taskCommands.js +4201 -313
- package/dist/commands/taskCompletionGate.js +131 -0
- package/dist/commands/taskContextCommand.js +244 -30
- package/dist/commands/taskInputCommands.js +177 -59
- package/dist/commands/taskIntegrationCommands.js +303 -0
- package/dist/commands/taskOverviewCommand.js +363 -0
- package/dist/commands/taskRoleRuntimeStatus.js +125 -19
- package/dist/commands/textInput.js +15 -0
- package/dist/completion/completionInstaller.js +26 -22
- package/dist/config/yuiConfig.js +4 -3
- package/dist/context/dispatchContext.js +90 -38
- package/dist/context/roleSessionContext.js +119 -0
- package/dist/controller/claudeLifecycleHook.js +203 -0
- package/dist/controller/clientRuntime.js +408 -56
- package/dist/controller/codexLifecycleHook.js +108 -0
- package/dist/controller/controller.js +1089 -32
- package/dist/controller/domainIdentity.js +505 -0
- package/dist/controller/ephemeralResourceReaper.js +131 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
- package/dist/controller/providerHookRunFence.js +127 -0
- package/dist/controller/resourceCleanupLinux.js +286 -0
- package/dist/controller/resourceInventory.js +531 -0
- package/dist/controller/resourceInventoryLinux.js +610 -0
- package/dist/controller/runtime.js +629 -10
- package/dist/controller/runtimeEventInbox.js +564 -0
- package/dist/controller/runtimeEventProcessor.js +248 -0
- package/dist/controller/runtimeLaunchCoordinator.js +477 -0
- package/dist/controller/sessionNotify.js +121 -78
- package/dist/coordination/deadlineScheduler.js +15 -0
- package/dist/coordination/mailboxScheduler.js +108 -0
- package/dist/coordination/workMailbox.js +329 -0
- package/dist/coordination/workMailboxQueue.js +86 -0
- package/dist/core/controllerClient.js +19 -5
- package/dist/core/controllerEndpoint.js +37 -0
- package/dist/core/controllerServer.js +218 -10
- package/dist/core/protocol.js +6 -2
- package/dist/decision/decision.js +2 -1
- package/dist/doctor/doctor.js +681 -32
- package/dist/domain/validation.js +53 -0
- package/dist/errors/cliError.js +5 -3
- package/dist/event/taskEvent.js +7 -3
- package/dist/execution/codexThreadNaming.js +160 -0
- package/dist/execution/executionGroup.js +579 -0
- package/dist/executor/agentAdapter.js +255 -40
- package/dist/executor/agentConfigurationCatalog.js +326 -0
- package/dist/executor/agentConfigurationProbe.js +506 -0
- package/dist/executor/agentExecutor.js +625 -10
- package/dist/executor/codexConfigConflict.js +290 -0
- package/dist/executor/effectiveLaunch.js +340 -0
- package/dist/executor/executorRegistry.js +238 -36
- package/dist/executor/fileRoleLaunchPlanner.js +550 -40
- package/dist/executor/turnCompletion.js +126 -0
- package/dist/input/inputRequest.js +30 -9
- package/dist/integration/changeSet.js +36 -0
- package/dist/integration/checkResult.js +24 -0
- package/dist/integration/gitIntegrationService.js +695 -0
- package/dist/integration/integrationAttempt.js +142 -0
- package/dist/interaction/operatorPresentation.js +96 -0
- package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
- package/dist/lifecycle/exactRunTerminalization.js +572 -0
- package/dist/lifecycle/providerLifecycleMapping.js +190 -0
- package/dist/lifecycle/taskRoleSessionReset.js +124 -0
- package/dist/message/message.js +23 -7
- package/dist/milestone/milestone.js +2 -1
- package/dist/operator/operatorSessionHistory.js +124 -0
- package/dist/output/agentConfigurationPresentation.js +43 -0
- package/dist/output/rolePresentation.js +34 -10
- package/dist/output/terminal.js +8 -0
- package/dist/output/timePresentation.js +55 -0
- package/dist/profile/agentProfile.js +128 -0
- package/dist/repository/gitWorkspace.js +578 -24
- package/dist/repository/project.js +213 -0
- package/dist/repository/taskWorkspaceCoordinator.js +392 -0
- package/dist/repository/taskWorkspacePreparer.js +1688 -191
- package/dist/review/reviewConfig.js +11 -0
- package/dist/review/reviewRound.js +399 -0
- package/dist/review/taskFinalReviewContract.js +90 -0
- package/dist/role/role.js +124 -23
- package/dist/run/agentRun.js +155 -12
- package/dist/run/runIdentity.js +82 -0
- package/dist/runtime/exactControlPlane.js +472 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/lifecycleReservation.js +38 -0
- package/dist/runtime/ports.js +11 -0
- package/dist/runtime/preallocatedNativeSession.js +13 -0
- package/dist/runtime/promptEnvelope.js +30 -0
- package/dist/runtime/runtimeBinding.js +31 -0
- package/dist/runtime/runtimeOwner.js +14 -0
- package/dist/runtime/sessionLaunchRequest.js +62 -0
- package/dist/runtime/sessionTitle.js +54 -0
- package/dist/runtime/taskRuntimeIsolation.js +643 -0
- package/dist/runtime/tmuxAdapters.js +315 -0
- package/dist/runtime/turnCompletion.js +3 -0
- package/dist/runtime/validation.js +23 -0
- package/dist/scheduler/activeRoleRunDelivery.js +342 -32
- package/dist/scheduler/activeTaskProgress.js +63 -0
- package/dist/scheduler/leaderFailure.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +307 -66
- package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
- package/dist/scheduler/operatorNotification.js +44 -2
- package/dist/scheduler/ports.js +28 -1
- package/dist/scheduler/roleRunLiveness.js +131 -25
- package/dist/scheduler/roleRunStall.js +951 -0
- package/dist/scheduler/taskExecutionProjection.js +544 -0
- package/dist/scheduler/wakeupQueue.js +3 -0
- package/dist/setup/setupCommand.js +302 -52
- package/dist/storage/compatibleTaskStore.js +102 -0
- package/dist/storage/migration/baseline.js +78 -0
- package/dist/storage/migration/classifier.js +51 -0
- package/dist/storage/migration/compatibleCodec.js +53 -0
- package/dist/storage/migration/engine.js +147 -0
- package/dist/storage/migration/index.js +33 -0
- package/dist/storage/migration/planner.js +154 -0
- package/dist/storage/migration/productionRegistry.js +486 -0
- package/dist/storage/migration/registry.js +169 -0
- package/dist/storage/migration/report.js +54 -0
- package/dist/storage/migration/types.js +31 -0
- package/dist/storage/storageSchema.js +147 -123
- package/dist/storage/storageVersions.js +11 -0
- package/dist/storage/taskStore.js +1793 -197
- package/dist/storage/upgrade/homeClassification.js +156 -0
- package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
- package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
- package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
- package/dist/storage/upgrade/recordVersionScan.js +176 -0
- package/dist/storage/upgrade/recordVersions.js +159 -0
- package/dist/storage/upgrade/switchProgress.js +80 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
- package/dist/storage/upgrade/upgradeReceipt.js +161 -0
- package/dist/storage/upgradeCoordination.js +186 -0
- package/dist/storage/upgradeFence.js +366 -0
- package/dist/task/task.js +132 -26
- package/dist/task/taskRecordReference.js +66 -0
- package/dist/tmux/commandExecutor.js +75 -2
- package/dist/tmux/tmuxManager.js +747 -49
- package/dist/version.js +23 -0
- package/dist/web/assets/assetManifest.js +62 -0
- package/dist/web/assets/client/app.js +631 -0
- package/dist/web/assets/client/components.js +605 -0
- package/dist/web/assets/client/dom.js +14 -0
- package/dist/web/assets/client/format.js +28 -0
- package/dist/web/assets/client/i18n.js +494 -0
- package/dist/web/assets/client/markdown.js +114 -0
- package/dist/web/assets/client/theme.js +32 -0
- package/dist/web/assets/client/view.js +458 -0
- package/dist/web/assets/fontData.js +12 -0
- package/dist/web/assets/fonts.js +12 -0
- package/dist/web/assets/shell.js +114 -0
- package/dist/web/assets/styles/cards.js +135 -0
- package/dist/web/assets/styles/layout.js +47 -0
- package/dist/web/assets/styles/markdown.js +29 -0
- package/dist/web/assets/styles/responsive.js +39 -0
- package/dist/web/assets/styles/tokens.js +101 -0
- package/dist/web/assets/styles/widgets.js +147 -0
- package/dist/web/tmuxWebTerminal.js +158 -0
- package/dist/web/webServer.js +463 -0
- package/dist/web/webSnapshot.js +148 -0
- package/dist/workItem/workItem.js +642 -23
- package/dist/workspace/gitChangeSetCapture.js +86 -0
- package/dist/workspace/workItemChangeSetManager.js +445 -0
- package/dist/worktree/managedWorkspace.js +202 -0
- package/docs/task-local-identity.md +62 -0
- package/i18n/README.zh-CN.md +406 -31
- package/package.json +10 -2
- package/skills/yui-leader/SKILL.md +601 -39
- package/skills/yui-operator/SKILL.md +255 -34
- package/skills/yui-reviewer/SKILL.md +57 -0
- package/skills/yui-worker/SKILL.md +214 -17
- package/dist/commands/repositoryCommands.js +0 -86
- package/dist/operator/operatorContext.js +0 -66
- package/dist/repository/repository.js +0 -55
- package/dist/scheduler/archivedTaskRuntime.js +0 -12
- package/dist/worktree/roleWorkspace.js +0 -62
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { roleNotFound, usageError } from "../errors/cliError.js";
|
|
2
2
|
import { createRoleSessionSet, recordRoleAgentSession } from "../executor/agentExecutor.js";
|
|
3
|
-
import {
|
|
4
|
-
import { resolveAgentEnvironment } from "../agent/agent.js";
|
|
3
|
+
import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
|
|
5
4
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
6
5
|
import { activeRoleSummary, renderRoleDetails } from "../output/rolePresentation.js";
|
|
7
|
-
import { activeRoleAgentBinding, createGlobalRole, createRoleAgentBinding, switchActiveRoleAgent, updateGlobalRole } from "../role/role.js";
|
|
6
|
+
import { activeRoleAgentBinding, createGlobalRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateGlobalRole } from "../role/role.js";
|
|
8
7
|
import { isSystemRoleName, SYSTEM_ROLE_NAMES, systemRoleDescription } from "../role/systemRoles.js";
|
|
9
8
|
import { hasAgentConfigOptions, parseRoleOptions, patchRoleAgentBinding, roleOptionSpecs, roleProfileFrom, roleProfilePatch } from "./roleConfiguration.js";
|
|
9
|
+
import { hasRoleLaunchContextOptions, validateConfiguredRoleSkills } from "./roleSkillValidation.js";
|
|
10
|
+
import { assertRoleRuntimeMutationAllowed } from "./roleRuntimeGuard.js";
|
|
10
11
|
export function runGlobalRoleCommand(args, store, options = {}) {
|
|
11
12
|
const [command, ...rest] = args;
|
|
12
13
|
switch (command) {
|
|
13
|
-
case "add": return addRole(rest, store);
|
|
14
|
+
case "add": return addRole(rest, store, options);
|
|
14
15
|
case "list": return listRoles(rest, store);
|
|
15
16
|
case "show": return showRole(rest, store);
|
|
16
|
-
case "update": return updateRole(rest, store);
|
|
17
|
+
case "update": return updateRole(rest, store, options);
|
|
17
18
|
case "remove": return removeRole(rest, store);
|
|
18
19
|
case "bind": return bindRole(rest, store);
|
|
19
|
-
case "
|
|
20
|
+
case "unbind": return unbindRole(rest, store);
|
|
21
|
+
case "enter": return enterRole(rest, store);
|
|
20
22
|
case "session": return roleSession(rest, store, options);
|
|
21
23
|
default:
|
|
22
24
|
throw usageError(command === undefined
|
|
@@ -24,25 +26,32 @@ export function runGlobalRoleCommand(args, store, options = {}) {
|
|
|
24
26
|
: `Unknown command: role ${command}`);
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
|
-
function addRole(args, store) {
|
|
29
|
+
function addRole(args, store, options) {
|
|
28
30
|
const [rawName, ...tail] = args;
|
|
29
31
|
const name = roleName(rawName);
|
|
30
32
|
const parsed = parseRoleOptions(tail, roleOptionSpecs({
|
|
31
33
|
update: false, includeAgent: true, includeWorkspace: true
|
|
32
34
|
}));
|
|
33
35
|
const agentId = required(parsed.one("--agent"), "--agent");
|
|
34
|
-
const agent = requireAgent(agentId, store);
|
|
35
36
|
if (parsed.has("--workspace") && trimmed(parsed.one("--workspace")) === undefined) {
|
|
36
37
|
throw usageError("--workspace is required.");
|
|
37
38
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
const now = new Date();
|
|
40
|
+
const created = store.transaction((tx) => {
|
|
41
|
+
assertRoleRuntimeMutationAllowed(tx, { scope: "global", roleName: name }, "creation");
|
|
42
|
+
const agent = requireAgent(agentId, tx);
|
|
43
|
+
const workspace = trimmed(parsed.one("--workspace"))
|
|
44
|
+
?? tx.getConfig().defaultWorkspace
|
|
45
|
+
?? process.cwd();
|
|
46
|
+
const binding = patchRoleAgentBinding(createRoleAgentBinding(definition(agent)), parsed);
|
|
47
|
+
const profile = roleProfileFrom(parsed);
|
|
48
|
+
validateConfiguredRoleSkills(options.yuiHome, profile.skills ?? []);
|
|
49
|
+
const role = createGlobalRole(name, [binding], agent.id, workspace, now, profile);
|
|
50
|
+
const result = tx.createGlobalRoleIfAbsent(role);
|
|
51
|
+
if (result === null)
|
|
52
|
+
throw usageError(`Role already exists: ${name}.`);
|
|
53
|
+
return result;
|
|
54
|
+
});
|
|
46
55
|
return presentRole(`Added role ${name}`, created, store);
|
|
47
56
|
}
|
|
48
57
|
function listRoles(args, store) {
|
|
@@ -85,10 +94,9 @@ function showRole(args, store) {
|
|
|
85
94
|
sessions: store.getGlobalRoleSessionSet(name)
|
|
86
95
|
});
|
|
87
96
|
}
|
|
88
|
-
function updateRole(args, store) {
|
|
97
|
+
function updateRole(args, store, options) {
|
|
89
98
|
const [rawName, ...tail] = args;
|
|
90
99
|
const name = roleName(rawName);
|
|
91
|
-
const role = requireRole(name, store);
|
|
92
100
|
const parsed = parseRoleOptions(tail, roleOptionSpecs({
|
|
93
101
|
update: true, includeAgent: true, includeWorkspace: true
|
|
94
102
|
}));
|
|
@@ -102,25 +110,35 @@ function updateRole(args, store) {
|
|
|
102
110
|
throw usageError("At least one role update option is required.");
|
|
103
111
|
}
|
|
104
112
|
const workspace = trimmed(parsed.one("--workspace"));
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
const next = store.transaction((tx) => {
|
|
114
|
+
const role = requireRole(name, tx);
|
|
115
|
+
const changesLaunchContext = hasRoleLaunchContextOptions(parsed);
|
|
116
|
+
const changesAgentConfig = hasAgentConfigOptions(parsed);
|
|
117
|
+
if (changesLaunchContext || changesAgentConfig) {
|
|
118
|
+
assertRoleRuntimeMutationAllowed(tx, {
|
|
119
|
+
scope: "global",
|
|
120
|
+
roleName: role.name
|
|
121
|
+
}, "desired launch configuration update");
|
|
113
122
|
}
|
|
114
|
-
bindings =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
let bindings = role.agentBindings;
|
|
124
|
+
if (changesAgentConfig) {
|
|
125
|
+
const agentId = parsed.one("--agent")?.trim() || role.activeAgentId;
|
|
126
|
+
const agent = requireAgent(agentId, tx);
|
|
127
|
+
assertOperatorAdapterAvailable(role, agent);
|
|
128
|
+
const binding = role.agentBindings[agentId] ?? createRoleAgentBinding(definition(agent));
|
|
129
|
+
bindings = { ...role.agentBindings, [agentId]: patchRoleAgentBinding(binding, parsed) };
|
|
130
|
+
}
|
|
131
|
+
const updated = updateGlobalRole(role, {
|
|
118
132
|
...(workspace === undefined ? {} : { workspace }),
|
|
119
133
|
...(bindings === role.agentBindings ? {} : { agentBindings: bindings }),
|
|
120
134
|
...roleProfilePatch(parsed)
|
|
121
|
-
}, new Date())
|
|
122
|
-
|
|
123
|
-
|
|
135
|
+
}, new Date());
|
|
136
|
+
if (changesLaunchContext) {
|
|
137
|
+
validateConfiguredRoleSkills(options.yuiHome, updated.skills ?? []);
|
|
138
|
+
}
|
|
139
|
+
tx.saveGlobalRole(updated);
|
|
140
|
+
return updated;
|
|
141
|
+
});
|
|
124
142
|
return renderRoleDetails(`Updated role ${name}`, next, {
|
|
125
143
|
kind: isSystemRoleName(name) ? "system" : "global",
|
|
126
144
|
sessions: store.getGlobalRoleSessionSet(name)
|
|
@@ -131,25 +149,51 @@ function bindRole(args, store) {
|
|
|
131
149
|
const name = roleName(rawName);
|
|
132
150
|
const agentId = required(rawAgentId, "Agent id");
|
|
133
151
|
assertNoArguments(rest, "Role bind usage: yui role bind <role> <agent-id>");
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
const now = new Date();
|
|
153
|
+
const result = store.transaction((tx) => {
|
|
154
|
+
const role = requireRole(name, tx);
|
|
155
|
+
assertRoleRuntimeMutationAllowed(tx, {
|
|
156
|
+
scope: "global",
|
|
157
|
+
roleName: role.name
|
|
158
|
+
}, "desired Agent binding update");
|
|
159
|
+
const agent = requireAgent(agentId, tx);
|
|
160
|
+
assertOperatorAdapterAvailable(role, agent);
|
|
161
|
+
const binding = role.agentBindings[agentId] ?? createRoleAgentBinding(definition(agent));
|
|
162
|
+
const withBinding = updateGlobalRole(role, {
|
|
163
|
+
agentBindings: { ...role.agentBindings, [agentId]: binding }
|
|
164
|
+
}, now);
|
|
165
|
+
if (agentId === role.activeAgentId) {
|
|
166
|
+
tx.saveGlobalRole(withBinding);
|
|
167
|
+
return {
|
|
168
|
+
message: `Role ${name} already bound to ${agentId}`,
|
|
169
|
+
role: withBinding
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const existingSet = tx.getGlobalRoleSessionSet(name);
|
|
173
|
+
const activeSession = existingSet?.sessions[existingSet.activeAgentId];
|
|
174
|
+
try {
|
|
175
|
+
const switched = switchActiveRoleAgent(withBinding, existingSet ?? createRoleSessionSet({ scope: "global", roleName: name }, role.activeAgentId, now), agentId, {
|
|
176
|
+
activeRun: false,
|
|
177
|
+
nativeProcessRunning: activeSession !== undefined
|
|
178
|
+
&& activeSession.status !== "stopped"
|
|
179
|
+
}, now);
|
|
180
|
+
tx.saveGlobalRoleWithSessionSet(switched.role, switched.sessions);
|
|
181
|
+
return { message: `Bound role ${name} to ${agentId}`, role: switched.role };
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
throw usageError(error instanceof Error ? error.message : String(error));
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return presentRole(result.message, result.role, store);
|
|
188
|
+
}
|
|
189
|
+
function assertOperatorAdapterAvailable(role, agent) {
|
|
190
|
+
if (role.name !== "operator" || Object.hasOwn(role.agentBindings, agent.id))
|
|
191
|
+
return;
|
|
192
|
+
const existing = Object.values(role.agentBindings).find((binding) => binding.adapterId === agent.adapterId);
|
|
193
|
+
if (existing !== undefined) {
|
|
194
|
+
throw usageError(`Operator already has a ${agent.adapterId} Agent: ${existing.agentId}. `
|
|
195
|
+
+ "Update that Agent's configuration, or activate another adapter and "
|
|
196
|
+
+ "unbind it before binding this Agent.");
|
|
153
197
|
}
|
|
154
198
|
}
|
|
155
199
|
function removeRole(args, store) {
|
|
@@ -158,25 +202,44 @@ function removeRole(args, store) {
|
|
|
158
202
|
assertNoArguments(rest, "Role remove usage: yui role remove <role>");
|
|
159
203
|
if (isSystemRoleName(name))
|
|
160
204
|
throw usageError(`System role cannot be removed: ${name}`);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
205
|
+
store.transaction((tx) => {
|
|
206
|
+
const role = requireRole(name, tx);
|
|
207
|
+
assertRoleRuntimeMutationAllowed(tx, {
|
|
208
|
+
scope: "global",
|
|
209
|
+
roleName: role.name
|
|
210
|
+
}, "removal");
|
|
211
|
+
const sessions = tx.getGlobalRoleSessionSet(name);
|
|
212
|
+
if (Object.values(sessions?.sessions ?? {}).some(({ status }) => status !== "stopped")) {
|
|
213
|
+
throw usageError(`GlobalRole is active and cannot be removed: ${name}.`);
|
|
214
|
+
}
|
|
215
|
+
if (!tx.removeGlobalRole(name))
|
|
216
|
+
throw roleNotFound(name);
|
|
217
|
+
});
|
|
168
218
|
return `Removed role ${name}\n`;
|
|
169
219
|
}
|
|
170
|
-
function
|
|
220
|
+
function unbindRole(args, store) {
|
|
221
|
+
const [rawName, rawAgentId, ...rest] = args;
|
|
222
|
+
const name = roleName(rawName);
|
|
223
|
+
const agentId = required(rawAgentId, "Agent id");
|
|
224
|
+
assertNoArguments(rest, "Role unbind usage: yui role unbind <role> <agent-id>");
|
|
225
|
+
store.transaction((tx) => {
|
|
226
|
+
const role = requireRole(name, tx);
|
|
227
|
+
try {
|
|
228
|
+
const result = unbindRoleAgent(role, tx.getGlobalRoleSessionSet(name), agentId, new Date());
|
|
229
|
+
tx.saveGlobalRoleWithSessionSet(result.role, result.sessions);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
throw usageError(error instanceof Error ? error.message : String(error));
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
return `Unbound Agent ${agentId} from role ${name}\n`;
|
|
236
|
+
}
|
|
237
|
+
function enterRole(args, store) {
|
|
171
238
|
const [rawName, ...rest] = args;
|
|
172
239
|
const name = roleName(rawName);
|
|
173
240
|
assertNoArguments(rest, "Role enter usage: yui role enter <role>");
|
|
174
241
|
const role = requireRole(name, store);
|
|
175
|
-
|
|
176
|
-
const set = store.getGlobalRoleSessionSet(name);
|
|
177
|
-
const session = set?.sessions[role.activeAgentId] ?? null;
|
|
178
|
-
const launch = compileGlobalRoleLaunch(role, agent, session?.nativeSessionId, options);
|
|
179
|
-
return { kind: "enter", role, launch };
|
|
242
|
+
return { kind: "enter", role };
|
|
180
243
|
}
|
|
181
244
|
function roleSession(args, store, options) {
|
|
182
245
|
const [command, rawName, ...tail] = args;
|
|
@@ -192,40 +255,48 @@ function roleSession(args, store, options) {
|
|
|
192
255
|
if (nativeSessionId.trim() !== nativeSessionId || nativeSessionId.length === 0) {
|
|
193
256
|
throw usageError("Native session id must not contain surrounding whitespace.");
|
|
194
257
|
}
|
|
195
|
-
const role = requireRole(name, store);
|
|
196
|
-
const binding = activeRoleAgentBinding(role);
|
|
197
|
-
const agent = requireAgent(binding.agentId, store);
|
|
198
258
|
const environment = options.env ?? process.env;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
259
|
+
return store.transaction((tx) => {
|
|
260
|
+
const role = requireRole(name, tx);
|
|
261
|
+
assertRoleRuntimeMutationAllowed(tx, {
|
|
262
|
+
scope: "global",
|
|
263
|
+
roleName: role.name
|
|
264
|
+
}, "manual native session update");
|
|
265
|
+
const binding = activeRoleAgentBinding(role);
|
|
266
|
+
requireAgent(binding.agentId, tx);
|
|
267
|
+
assertSessionProvenance(command, role, nativeSessionId, environment);
|
|
268
|
+
const now = new Date();
|
|
269
|
+
const set = tx.getGlobalRoleSessionSet(name)
|
|
270
|
+
?? createRoleSessionSet({ scope: "global", roleName: name }, role.activeAgentId, now);
|
|
271
|
+
const existing = set.sessions[role.activeAgentId] ?? null;
|
|
272
|
+
const input = {
|
|
273
|
+
agentId: binding.agentId,
|
|
274
|
+
adapterId: binding.adapterId,
|
|
275
|
+
nativeSessionId,
|
|
276
|
+
policy: "fixed",
|
|
277
|
+
status: environment.YUI_ROLE === name ? "running" : "ready",
|
|
278
|
+
effective: resolveEffectiveLaunch({ role, purpose: "execution" })
|
|
279
|
+
};
|
|
280
|
+
if (command === "record") {
|
|
281
|
+
if (existing !== null && existing.nativeSessionId !== nativeSessionId) {
|
|
282
|
+
throw usageError("GlobalRole session replacement must be explicit.");
|
|
283
|
+
}
|
|
284
|
+
tx.saveGlobalRoleSessionSet(recordRoleAgentSession(set, input, now));
|
|
285
|
+
return `Recorded native session for role ${name}\n`;
|
|
213
286
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
store.saveGlobalRoleSessionSet(recordRoleAgentSession(set, input(), new Date()));
|
|
228
|
-
return `Replaced native session for role ${name}\n`;
|
|
287
|
+
if (existing === null) {
|
|
288
|
+
throw usageError("Native session replacement requires an existing native session.");
|
|
289
|
+
}
|
|
290
|
+
if (existing.status !== "stopped") {
|
|
291
|
+
throw usageError("Native session replacement is blocked while the native Agent process is running.");
|
|
292
|
+
}
|
|
293
|
+
if (existing.nativeSessionId === nativeSessionId) {
|
|
294
|
+
throw usageError("Native session replacement requires a different native session identity.");
|
|
295
|
+
}
|
|
296
|
+
required(parsed.one("--reason"), "--reason");
|
|
297
|
+
tx.saveGlobalRoleSessionSet(recordRoleAgentSession(set, input, now));
|
|
298
|
+
return `Replaced native session for role ${name}\n`;
|
|
299
|
+
});
|
|
229
300
|
}
|
|
230
301
|
function assertSessionProvenance(command, role, nativeSessionId, environment) {
|
|
231
302
|
const values = [
|
|
@@ -282,35 +353,6 @@ function parseOptions(args, specs) {
|
|
|
282
353
|
many: (option) => [...(values.get(option) ?? [])]
|
|
283
354
|
};
|
|
284
355
|
}
|
|
285
|
-
function compileGlobalRoleLaunch(role, agent, nativeSessionId, options) {
|
|
286
|
-
const adapter = resolveAgentAdapter(agent.adapterId);
|
|
287
|
-
const input = {
|
|
288
|
-
agent: definition(agent),
|
|
289
|
-
config: activeRoleAgentBinding(role).config,
|
|
290
|
-
workspace: role.workspace,
|
|
291
|
-
systemPrompt: role.systemPrompt
|
|
292
|
-
};
|
|
293
|
-
const compiled = nativeSessionId === undefined
|
|
294
|
-
? adapter.compileNew(input)
|
|
295
|
-
: adapter.compileResume({ ...input, nativeSessionId });
|
|
296
|
-
const baseEnvironment = options.env ?? process.env;
|
|
297
|
-
return {
|
|
298
|
-
command: agent.command,
|
|
299
|
-
args: compiled.argv,
|
|
300
|
-
env: stringEnvironment({
|
|
301
|
-
...baseEnvironment,
|
|
302
|
-
...resolveAgentEnvironment(definition(agent), baseEnvironment),
|
|
303
|
-
...(options.yuiHome === undefined ? {} : { YUI_HOME: options.yuiHome }),
|
|
304
|
-
YUI_ROLE: role.name,
|
|
305
|
-
YUI_AGENT_ID: agent.id,
|
|
306
|
-
YUI_ADAPTER_ID: agent.adapterId,
|
|
307
|
-
YUI_WORKSPACE: role.workspace
|
|
308
|
-
})
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
function stringEnvironment(environment) {
|
|
312
|
-
return Object.fromEntries(Object.entries(environment).filter((entry) => entry[1] !== undefined));
|
|
313
|
-
}
|
|
314
356
|
function definition(agent) {
|
|
315
357
|
return { ...agent, baseArgs: [...agent.baseArgs], environment: [...agent.environment], source: "custom" };
|
|
316
358
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { runtimeError, taskNotFound, usageError } from "../errors/cliError.js";
|
|
2
2
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
3
|
-
import {
|
|
3
|
+
import { formatTimestamp } from "../output/timePresentation.js";
|
|
4
|
+
import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
4
5
|
const WAKEUP_PREFIX = "leader-wakeup:";
|
|
5
6
|
const RECOVERY_PREFIX = "leader-recovery:";
|
|
6
7
|
/**
|
|
@@ -23,6 +24,7 @@ function listJobs(args, store) {
|
|
|
23
24
|
const jobs = collectJobs(store);
|
|
24
25
|
if (jobs.length === 0)
|
|
25
26
|
return "No scheduler jobs found.\n";
|
|
27
|
+
const timeZone = store.getConfig().timeZone;
|
|
26
28
|
return `${renderTable("Scheduler jobs", [
|
|
27
29
|
{ header: "Job", minWidth: 12, maxWidth: 44 },
|
|
28
30
|
{ header: "Task", minWidth: 6, maxWidth: 20 },
|
|
@@ -35,7 +37,7 @@ function listJobs(args, store) {
|
|
|
35
37
|
job.taskId,
|
|
36
38
|
job.kind,
|
|
37
39
|
job.status,
|
|
38
|
-
job.updatedAt,
|
|
40
|
+
formatTimestamp(job.updatedAt, timeZone),
|
|
39
41
|
job.detail
|
|
40
42
|
]), defaultTableWidth())}\n`;
|
|
41
43
|
}
|
|
@@ -55,14 +57,17 @@ function collectJobs(store) {
|
|
|
55
57
|
}
|
|
56
58
|
const failure = store.getLeaderFailure(task.id);
|
|
57
59
|
const notification = store.getOperatorNotification(task.id);
|
|
58
|
-
|
|
60
|
+
const recoveryNotification = notification?.type === "leader-recovery-failed"
|
|
61
|
+
? notification
|
|
62
|
+
: null;
|
|
63
|
+
if (failure !== null || recoveryNotification !== null) {
|
|
59
64
|
jobs.push({
|
|
60
65
|
id: `${RECOVERY_PREFIX}${task.id}`,
|
|
61
66
|
taskId: task.id,
|
|
62
67
|
kind: "leader-recovery",
|
|
63
68
|
status: "failed",
|
|
64
|
-
detail: failure?.message ??
|
|
65
|
-
updatedAt: failure?.lastFailedAt ??
|
|
69
|
+
detail: failure?.message ?? recoveryNotification?.message ?? "Leader recovery failed.",
|
|
70
|
+
updatedAt: failure?.lastFailedAt ?? recoveryNotification?.updatedAt ?? task.updatedAt
|
|
66
71
|
});
|
|
67
72
|
}
|
|
68
73
|
}
|
|
@@ -88,13 +93,18 @@ function retryJob(args, store, options) {
|
|
|
88
93
|
if (task.status !== "active") {
|
|
89
94
|
throw usageError(`Leader recovery can only be retried for an active Task: ${task.id}.`);
|
|
90
95
|
}
|
|
91
|
-
if (tx.getLeaderFailure(task.id) === null
|
|
96
|
+
if (tx.getLeaderFailure(task.id) === null
|
|
97
|
+
&& tx.getOperatorNotification(task.id)?.type !== "leader-recovery-failed") {
|
|
92
98
|
throw usageError(`Job not found: ${id}.`);
|
|
93
99
|
}
|
|
94
100
|
tx.clearLeaderFailure(task.id);
|
|
95
101
|
tx.clearOperatorNotification(task.id);
|
|
96
|
-
|
|
102
|
+
enqueueWork(tx, { kind: "role", taskId: task.id, roleName: "leader" }, "recovery-retry", now, [{ type: "task", id: task.id }]);
|
|
97
103
|
});
|
|
98
|
-
|
|
104
|
+
const target = { kind: "role", taskId, roleName: "leader" };
|
|
105
|
+
if (runtime.notifyMailboxChanged !== undefined)
|
|
106
|
+
runtime.notifyMailboxChanged(target);
|
|
107
|
+
else
|
|
108
|
+
runtime.notifyStateChanged(taskId);
|
|
99
109
|
return `Retry requested for ${id}\n`;
|
|
100
110
|
}
|
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
import { createRoleSessionSet } from "../executor/agentExecutor.js";
|
|
3
|
+
import { listOperatorSessions, prepareOperatorNewSession, prepareOperatorResumeSession } from "../operator/operatorSessionHistory.js";
|
|
4
|
+
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
5
|
+
import { formatRelativeTimestamp } from "../output/timePresentation.js";
|
|
6
|
+
import { updateGlobalRole } from "../role/role.js";
|
|
2
7
|
import { submitOperatorMessage } from "./taskCommands.js";
|
|
3
|
-
|
|
8
|
+
import { readCommandText } from "./textInput.js";
|
|
9
|
+
/** Operator command parsing never launches or attaches a native process. */
|
|
4
10
|
export function runOperatorCommand(args, store, options = {}) {
|
|
5
11
|
const [command, ...rest] = args;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
switch (command) {
|
|
13
|
+
case "list": return listSessions(rest, store, options);
|
|
14
|
+
case "new": return newSession(rest, store);
|
|
15
|
+
case "resume": return resumeSession(rest, store);
|
|
16
|
+
case "submit": return submit(rest, store, options);
|
|
17
|
+
default:
|
|
18
|
+
throw usageError(command === undefined
|
|
19
|
+
? "Operator command is required."
|
|
20
|
+
: `Unknown command: operator ${command}`);
|
|
10
21
|
}
|
|
11
|
-
|
|
22
|
+
}
|
|
23
|
+
function submit(rest, store, options) {
|
|
24
|
+
const usage = "Operator submit usage: yui operator submit (<body>|--body-file <path|->) [--task <id>].";
|
|
12
25
|
const positionals = [];
|
|
13
26
|
let taskId;
|
|
27
|
+
let bodyFile;
|
|
14
28
|
for (let index = 0; index < rest.length; index += 1) {
|
|
15
29
|
const value = rest[index];
|
|
30
|
+
if (value === "--body-file") {
|
|
31
|
+
if (bodyFile !== undefined)
|
|
32
|
+
throw usageError("Option may only be specified once: --body-file.", usage);
|
|
33
|
+
const candidate = rest[index + 1];
|
|
34
|
+
if (candidate === undefined || candidate.startsWith("--"))
|
|
35
|
+
throw usageError("--body-file is required.", usage);
|
|
36
|
+
bodyFile = candidate;
|
|
37
|
+
index += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
16
40
|
if (value !== "--task") {
|
|
17
41
|
if (value.startsWith("--"))
|
|
18
42
|
throw usageError(`Unsupported option: ${value}.`, usage);
|
|
@@ -28,11 +52,137 @@ export function runOperatorCommand(args, store, options = {}) {
|
|
|
28
52
|
taskId = candidate.trim();
|
|
29
53
|
index += 1;
|
|
30
54
|
}
|
|
31
|
-
if (positionals.length
|
|
55
|
+
if (positionals.length > 1)
|
|
32
56
|
throw usageError(usage);
|
|
33
|
-
|
|
57
|
+
const body = readCommandText(positionals[0], bodyFile, "--body", usage);
|
|
58
|
+
return {
|
|
59
|
+
kind: "output",
|
|
60
|
+
output: submitOperatorMessage(body, taskId, store, options)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function listSessions(args, store, options) {
|
|
64
|
+
if (args.length !== 0)
|
|
65
|
+
throw usageError("Operator list usage: yui operator list.");
|
|
66
|
+
const sessions = listOperatorSessions(store.getGlobalRoleSessionSet("operator"));
|
|
67
|
+
const now = options.now?.() ?? new Date();
|
|
68
|
+
const output = sessions.length === 0
|
|
69
|
+
? "○ No Operator sessions are available.\n"
|
|
70
|
+
: `${renderTable("Operator sessions", [
|
|
71
|
+
{ header: "Updated", minWidth: 7, maxWidth: 12 },
|
|
72
|
+
{ header: "Agent", minWidth: 6, maxWidth: 16 },
|
|
73
|
+
{ header: "Conversation", minWidth: 20, maxWidth: 72 },
|
|
74
|
+
{ header: "State", minWidth: 7, maxWidth: 10 }
|
|
75
|
+
], sessions.map((session) => [
|
|
76
|
+
formatRelativeTimestamp(session.updatedAt, now),
|
|
77
|
+
adapterLabel(session.adapterId),
|
|
78
|
+
session.displayTitle,
|
|
79
|
+
session.state
|
|
80
|
+
]), options.width ?? defaultTableWidth())}\n\n${sessions.length} session${sessions.length === 1 ? "" : "s"}\n`;
|
|
34
81
|
return {
|
|
35
82
|
kind: "output",
|
|
36
|
-
output
|
|
83
|
+
output,
|
|
84
|
+
data: { sessions }
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function newSession(args, store) {
|
|
88
|
+
let agentId;
|
|
89
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
90
|
+
const value = args[index];
|
|
91
|
+
if (value !== "--agent") {
|
|
92
|
+
throw usageError(value.startsWith("--")
|
|
93
|
+
? `Unsupported option: ${value}.`
|
|
94
|
+
: `Unexpected argument: ${value}.`, "Operator new usage: yui operator new [--agent <id>].");
|
|
95
|
+
}
|
|
96
|
+
if (agentId !== undefined)
|
|
97
|
+
throw usageError("Option may only be specified once: --agent.");
|
|
98
|
+
agentId = requiredValue(args[index + 1], "--agent");
|
|
99
|
+
index += 1;
|
|
100
|
+
}
|
|
101
|
+
const role = requireOperator(store);
|
|
102
|
+
const targetAgentId = agentId ?? role.activeAgentId;
|
|
103
|
+
if (!Object.hasOwn(role.agentBindings, targetAgentId)) {
|
|
104
|
+
throw usageError(`Operator Agent is not bound: ${targetAgentId}.`);
|
|
105
|
+
}
|
|
106
|
+
return { kind: "session", action: "new", targetAgentId };
|
|
107
|
+
}
|
|
108
|
+
function resumeSession(args, store) {
|
|
109
|
+
let ref;
|
|
110
|
+
let last = false;
|
|
111
|
+
for (const value of args) {
|
|
112
|
+
if (value === "--last") {
|
|
113
|
+
if (last)
|
|
114
|
+
throw usageError("Option may only be specified once: --last.");
|
|
115
|
+
last = true;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (value.startsWith("--"))
|
|
119
|
+
throw usageError(`Unsupported option: ${value}.`);
|
|
120
|
+
if (ref !== undefined) {
|
|
121
|
+
throw usageError("Operator resume accepts only one session reference.");
|
|
122
|
+
}
|
|
123
|
+
ref = value;
|
|
124
|
+
}
|
|
125
|
+
if (last && ref !== undefined) {
|
|
126
|
+
throw usageError("Operator resume accepts either a session reference or --last.");
|
|
127
|
+
}
|
|
128
|
+
const sessions = listOperatorSessions(store.getGlobalRoleSessionSet("operator"));
|
|
129
|
+
const selectedRef = last ? sessions[0]?.ref : ref;
|
|
130
|
+
if (selectedRef === undefined) {
|
|
131
|
+
throw usageError(last
|
|
132
|
+
? "No Operator session is available to resume."
|
|
133
|
+
: "Operator resume requires a session selection.");
|
|
134
|
+
}
|
|
135
|
+
const selected = sessions.find((session) => session.ref === selectedRef);
|
|
136
|
+
if (selected === undefined)
|
|
137
|
+
throw usageError(`Operator session not found: ${selectedRef}.`);
|
|
138
|
+
const role = requireOperator(store);
|
|
139
|
+
if (!Object.hasOwn(role.agentBindings, selected.agentId)) {
|
|
140
|
+
throw usageError(`Operator session Agent is no longer bound: ${selected.agentId}.`);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
kind: "session",
|
|
144
|
+
action: "resume",
|
|
145
|
+
targetAgentId: selected.agentId,
|
|
146
|
+
ref: selected.ref
|
|
37
147
|
};
|
|
38
148
|
}
|
|
149
|
+
export function applyOperatorSessionControl(control, store, now = new Date()) {
|
|
150
|
+
return store.transaction((tx) => {
|
|
151
|
+
const role = requireOperator(tx);
|
|
152
|
+
const binding = role.agentBindings[control.targetAgentId];
|
|
153
|
+
if (binding === undefined) {
|
|
154
|
+
throw usageError(`Operator Agent is not bound: ${control.targetAgentId}.`);
|
|
155
|
+
}
|
|
156
|
+
const current = tx.getGlobalRoleSessionSet(role.name)
|
|
157
|
+
?? createRoleSessionSet({ scope: "global", roleName: role.name }, role.activeAgentId, now);
|
|
158
|
+
const sessions = control.action === "new"
|
|
159
|
+
? prepareOperatorNewSession(current, control.targetAgentId, now)
|
|
160
|
+
: prepareOperatorResumeSession(current, control.ref, now);
|
|
161
|
+
const selected = sessions.sessions[control.targetAgentId];
|
|
162
|
+
if (selected !== undefined && selected.adapterId !== binding.adapterId) {
|
|
163
|
+
throw usageError(`Operator session adapter does not match Agent: ${control.targetAgentId}.`);
|
|
164
|
+
}
|
|
165
|
+
const updatedRole = updateGlobalRole(role, { activeAgentId: control.targetAgentId }, now);
|
|
166
|
+
tx.saveGlobalRoleWithSessionSet(updatedRole, sessions);
|
|
167
|
+
return { role: updatedRole, sessions };
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function requireOperator(store) {
|
|
171
|
+
const role = store.getGlobalRole("operator");
|
|
172
|
+
if (role === null)
|
|
173
|
+
throw usageError("Operator is not configured. Run yui setup first.");
|
|
174
|
+
return role;
|
|
175
|
+
}
|
|
176
|
+
function adapterLabel(adapterId) {
|
|
177
|
+
return adapterId === "codex"
|
|
178
|
+
? "Codex"
|
|
179
|
+
: adapterId === "claude"
|
|
180
|
+
? "Claude"
|
|
181
|
+
: adapterId;
|
|
182
|
+
}
|
|
183
|
+
function requiredValue(value, option) {
|
|
184
|
+
if (value === undefined || value.startsWith("--") || value.trim().length === 0) {
|
|
185
|
+
throw usageError(`${option} is required.`);
|
|
186
|
+
}
|
|
187
|
+
return value.trim();
|
|
188
|
+
}
|