@vectorplane/ctrl-cli 0.1.11 → 0.1.13
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 +39 -0
- package/dist/commands/context.js +31 -15
- package/dist/commands/init.js +16 -60
- package/dist/commands/registry.d.ts +1 -0
- package/dist/commands/registry.js +116 -0
- package/dist/commands/session.js +2 -10
- package/dist/commands/sync.js +4 -5
- package/dist/commands/task.d.ts +1 -0
- package/dist/commands/task.js +544 -0
- package/dist/commands/workspace.js +219 -1
- package/dist/core/agent-runtime-detection.d.ts +13 -0
- package/dist/core/agent-runtime-detection.js +117 -0
- package/dist/core/agent-setup.d.ts +1 -13
- package/dist/core/agent-setup.js +1 -108
- package/dist/core/api.d.ts +113 -1
- package/dist/core/api.js +307 -0
- package/dist/core/init-workspace.d.ts +29 -0
- package/dist/core/init-workspace.js +73 -0
- package/dist/core/runtime-agent-setup.d.ts +18 -0
- package/dist/core/runtime-agent-setup.js +13 -0
- package/dist/core/task-runner.d.ts +16 -0
- package/dist/core/task-runner.js +62 -0
- package/dist/index.js +9 -1
- package/dist/types/api.d.ts +215 -0
- package/package.json +1 -1
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
import { getBooleanOption, getStringOption, parseArgs, requirePositional } from "../core/cli.js";
|
|
2
|
+
import { ensureSessionAvailable, getProfileState } from "../core/config.js";
|
|
3
|
+
import { collectGitContext } from "../core/git.js";
|
|
4
|
+
import { collectMachineContext, collectRuntimeContext } from "../core/machine.js";
|
|
5
|
+
import { loadRuntimeStatus } from "../core/runtime.js";
|
|
6
|
+
import { ensureFreshSession, resolveWorkspaceSlug } from "../core/session.js";
|
|
7
|
+
import { VectorPlaneApiClient } from "../core/api.js";
|
|
8
|
+
import { getBoundWorkspace, resolveWorkspaceRoot } from "../core/workspace-binding.js";
|
|
9
|
+
import { ValidationError } from "../core/errors.js";
|
|
10
|
+
import { executeLocalTaskStep } from "../core/task-runner.js";
|
|
11
|
+
async function resolveAuthenticatedWorkspace(cliVersion, rawArgs) {
|
|
12
|
+
const runtime = await loadRuntimeStatus();
|
|
13
|
+
const session = await ensureSessionAvailable(runtime.profile.name);
|
|
14
|
+
const parsed = parseArgs(rawArgs);
|
|
15
|
+
const [git, machine, runtimeContext] = await Promise.all([
|
|
16
|
+
collectGitContext(process.cwd()),
|
|
17
|
+
collectMachineContext(runtime.device, runtime.config),
|
|
18
|
+
collectRuntimeContext(cliVersion, "task", process.argv.slice(2)),
|
|
19
|
+
]);
|
|
20
|
+
const apiClient = new VectorPlaneApiClient(runtime.profile.apiBaseUrl, runtime.config.requestTimeoutMs, runtime.logger);
|
|
21
|
+
const freshSession = await ensureFreshSession({
|
|
22
|
+
profileName: runtime.profile.name,
|
|
23
|
+
session,
|
|
24
|
+
machine,
|
|
25
|
+
runtime: runtimeContext,
|
|
26
|
+
device: runtime.device,
|
|
27
|
+
apiClient,
|
|
28
|
+
logger: runtime.logger,
|
|
29
|
+
});
|
|
30
|
+
const rootPath = resolveWorkspaceRoot(process.cwd(), git);
|
|
31
|
+
const boundWorkspace = await getBoundWorkspace(rootPath);
|
|
32
|
+
const profileState = getProfileState(runtime.state, runtime.profile.name);
|
|
33
|
+
const workspace = resolveWorkspaceSlug(runtime.profile, freshSession, getStringOption(parsed, "workspace"), boundWorkspace, profileState.lastWorkspace);
|
|
34
|
+
if (!workspace) {
|
|
35
|
+
throw new ValidationError("Nenhum workspace resolvido para operar tasks.");
|
|
36
|
+
}
|
|
37
|
+
return { parsed, runtime, apiClient, freshSession, git, workspace, rootPath, profileState };
|
|
38
|
+
}
|
|
39
|
+
async function resolveAuthenticatedApi(cliVersion, rawArgs) {
|
|
40
|
+
const runtime = await loadRuntimeStatus();
|
|
41
|
+
const session = await ensureSessionAvailable(runtime.profile.name);
|
|
42
|
+
const [machine, runtimeContext] = await Promise.all([
|
|
43
|
+
collectMachineContext(runtime.device, runtime.config),
|
|
44
|
+
collectRuntimeContext(cliVersion, "task", process.argv.slice(2)),
|
|
45
|
+
]);
|
|
46
|
+
const apiClient = new VectorPlaneApiClient(runtime.profile.apiBaseUrl, runtime.config.requestTimeoutMs, runtime.logger);
|
|
47
|
+
const freshSession = await ensureFreshSession({
|
|
48
|
+
profileName: runtime.profile.name,
|
|
49
|
+
session,
|
|
50
|
+
machine,
|
|
51
|
+
runtime: runtimeContext,
|
|
52
|
+
device: runtime.device,
|
|
53
|
+
apiClient,
|
|
54
|
+
logger: runtime.logger,
|
|
55
|
+
});
|
|
56
|
+
return { parsed: parseArgs(rawArgs), runtime, apiClient, freshSession };
|
|
57
|
+
}
|
|
58
|
+
function printTask(task) {
|
|
59
|
+
process.stdout.write(`Task: ${task.id}\n`);
|
|
60
|
+
process.stdout.write(`Título: ${task.title}\n`);
|
|
61
|
+
process.stdout.write(`Status: ${task.status}\n`);
|
|
62
|
+
process.stdout.write(`Risk: ${task.riskLevel} (${task.riskScore})\n`);
|
|
63
|
+
process.stdout.write(`Policy: ${task.policyStatus}\n`);
|
|
64
|
+
process.stdout.write(`Conflict: ${task.conflictStatus}\n`);
|
|
65
|
+
if (task.requiresApproval && !task.approvedBy) {
|
|
66
|
+
process.stdout.write("Approval: pendente\n");
|
|
67
|
+
}
|
|
68
|
+
else if (task.approvedBy) {
|
|
69
|
+
process.stdout.write(`Approval: aprovado por ${task.approvedBy}\n`);
|
|
70
|
+
}
|
|
71
|
+
const approvalTrail = (task.handoffs ?? []).filter((handoff) => handoff.title === "Task approved" || handoff.title === "Approval rejected");
|
|
72
|
+
if (approvalTrail.length > 0) {
|
|
73
|
+
const latest = approvalTrail[approvalTrail.length - 1];
|
|
74
|
+
const rationale = typeof latest.summary?.rationale === "string" ? latest.summary.rationale : null;
|
|
75
|
+
const approvedBy = typeof latest.summary?.approvedBy === "string" ? latest.summary.approvedBy : null;
|
|
76
|
+
process.stdout.write(`Última decisão: ${latest.title} em ${latest.createdAt}\n`);
|
|
77
|
+
if (approvedBy) {
|
|
78
|
+
process.stdout.write(`Decisor: ${approvedBy}\n`);
|
|
79
|
+
}
|
|
80
|
+
if (rationale) {
|
|
81
|
+
process.stdout.write(`Rationale: ${rationale}\n`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (task.recommendedApproach) {
|
|
85
|
+
process.stdout.write(`Approach: ${task.recommendedApproach}\n`);
|
|
86
|
+
}
|
|
87
|
+
for (const step of task.steps) {
|
|
88
|
+
process.stdout.write(`- ${step.order}. ${step.name} | ${step.capability} | ${step.status}`);
|
|
89
|
+
if (step.assignedAgentName) {
|
|
90
|
+
process.stdout.write(` | ${step.assignedAgentName}`);
|
|
91
|
+
}
|
|
92
|
+
process.stdout.write("\n");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function printJsonIfRequested(parsed, payload) {
|
|
96
|
+
if (!getBooleanOption(parsed, "json")) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
function printClaimableStep(step) {
|
|
103
|
+
process.stdout.write(`Task: ${step.taskId} | ${step.taskTitle}\n`);
|
|
104
|
+
process.stdout.write(`Step: ${step.stepId} | ${step.stepName}\n`);
|
|
105
|
+
process.stdout.write(`Capability: ${step.capability}\n`);
|
|
106
|
+
process.stdout.write(`Agent: ${step.assignedAgentName} (${step.assignedAgentId})\n`);
|
|
107
|
+
process.stdout.write(`Status: ${step.status}\n`);
|
|
108
|
+
}
|
|
109
|
+
function printTaskTemplate(template) {
|
|
110
|
+
process.stdout.write(`${template.slug} | ${template.name} | ${template.defaultCapabilities.join(", ")}`);
|
|
111
|
+
if (template.archetypeScope) {
|
|
112
|
+
process.stdout.write(` | archetype=${template.archetypeScope}`);
|
|
113
|
+
}
|
|
114
|
+
process.stdout.write("\n");
|
|
115
|
+
}
|
|
116
|
+
function printHandoff(handoff) {
|
|
117
|
+
process.stdout.write(`Task: ${handoff.taskId}\n`);
|
|
118
|
+
process.stdout.write(`Título: ${handoff.title}\n`);
|
|
119
|
+
process.stdout.write(`Status: ${handoff.status}\n`);
|
|
120
|
+
if (handoff.currentTitle) {
|
|
121
|
+
process.stdout.write(`Handoff: ${handoff.currentTitle}\n`);
|
|
122
|
+
}
|
|
123
|
+
if (handoff.nextSteps?.length) {
|
|
124
|
+
process.stdout.write(`Próximos passos: ${handoff.nextSteps.join(" | ")}\n`);
|
|
125
|
+
}
|
|
126
|
+
if (handoff.risks?.length) {
|
|
127
|
+
process.stdout.write(`Riscos: ${handoff.risks.join(" | ")}\n`);
|
|
128
|
+
}
|
|
129
|
+
if (handoff.memoryLanes?.length) {
|
|
130
|
+
process.stdout.write(`Lanes de memória: ${handoff.memoryLanes.join(", ")}\n`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function printObservability(payload) {
|
|
134
|
+
if (payload.taskId) {
|
|
135
|
+
process.stdout.write(`Task: ${payload.taskId}\n`);
|
|
136
|
+
if (payload.status) {
|
|
137
|
+
process.stdout.write(`Status: ${String(payload.status)}\n`);
|
|
138
|
+
}
|
|
139
|
+
if (typeof payload.durationMs === "number") {
|
|
140
|
+
process.stdout.write(`Duração: ${payload.durationMs} ms\n`);
|
|
141
|
+
}
|
|
142
|
+
if (typeof payload.delegationCount === "number") {
|
|
143
|
+
process.stdout.write(`Delegações: ${payload.delegationCount}\n`);
|
|
144
|
+
}
|
|
145
|
+
if (Array.isArray(payload.steps)) {
|
|
146
|
+
for (const step of payload.steps) {
|
|
147
|
+
process.stdout.write(`- ${String(step.order ?? "?")}. ${String(step.name ?? "step")} | ${String(step.status ?? "unknown")} | attempts=${String(step.attempts ?? 0)}\n`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
process.stdout.write(`Workspace: ${payload.workspaceId}\n`);
|
|
153
|
+
process.stdout.write(`Tasks: ${String(payload.totalTasks ?? 0)}\n`);
|
|
154
|
+
process.stdout.write(`Concluídas: ${String(payload.completedTasks ?? 0)}\n`);
|
|
155
|
+
process.stdout.write(`Falhas: ${String(payload.failedTasks ?? 0)}\n`);
|
|
156
|
+
process.stdout.write(`Bloqueadas: ${String(payload.blockedTasks ?? 0)}\n`);
|
|
157
|
+
process.stdout.write(`Delegadas: ${String(payload.delegatedTasks ?? 0)}\n`);
|
|
158
|
+
process.stdout.write(`Success rate: ${String(payload.successRate ?? 0)}\n`);
|
|
159
|
+
}
|
|
160
|
+
function printWatchEvent(event) {
|
|
161
|
+
const scope = event.taskId ? `task=${event.taskId}` : `workspace=${event.workspaceId}`;
|
|
162
|
+
const step = event.stepId ? ` step=${event.stepId}` : "";
|
|
163
|
+
const summary = event.summary ? ` ${JSON.stringify(event.summary)}` : "";
|
|
164
|
+
process.stdout.write(`[${event.timestamp}] ${event.type} ${scope}${step}${summary}\n`);
|
|
165
|
+
}
|
|
166
|
+
async function runTaskList(cliVersion, args) {
|
|
167
|
+
const { parsed, apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
168
|
+
const tasks = await apiClient.listTasks(freshSession.accessToken, workspace);
|
|
169
|
+
if (printJsonIfRequested(parsed, tasks)) {
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
if (tasks.length === 0) {
|
|
173
|
+
process.stdout.write("VectorPlane: nenhuma task encontrada.\n");
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
for (const task of tasks) {
|
|
177
|
+
const approvalMarker = task.requiresApproval && !task.approvedBy ? " | [APPROVAL REQUIRED]" : "";
|
|
178
|
+
process.stdout.write(`${task.id} | ${task.status} | ${task.riskLevel} | ${task.title}${approvalMarker}\n`);
|
|
179
|
+
}
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
async function runTaskRun(cliVersion, args) {
|
|
183
|
+
const { parsed, apiClient, freshSession, git, workspace, runtime } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
184
|
+
const title = getStringOption(parsed, "title")?.trim();
|
|
185
|
+
const intention = getStringOption(parsed, "intention")?.trim() ?? getStringOption(parsed, "prompt")?.trim();
|
|
186
|
+
if (!title || !intention) {
|
|
187
|
+
throw new ValidationError("Informe `--title` e `--intention`.");
|
|
188
|
+
}
|
|
189
|
+
const capabilityRaw = getStringOption(parsed, "capability");
|
|
190
|
+
const pathRaw = getStringOption(parsed, "path");
|
|
191
|
+
const payload = {
|
|
192
|
+
title,
|
|
193
|
+
intention,
|
|
194
|
+
templateSlug: getStringOption(parsed, "template")?.trim() || undefined,
|
|
195
|
+
requestedCapabilities: capabilityRaw ? capabilityRaw.split(",").map((value) => value.trim()).filter(Boolean) : undefined,
|
|
196
|
+
targetPaths: pathRaw ? pathRaw.split(",").map((value) => value.trim()).filter(Boolean) : undefined,
|
|
197
|
+
featureKey: getStringOption(parsed, "feature")?.trim() || undefined,
|
|
198
|
+
taskKey: getStringOption(parsed, "task")?.trim() || undefined,
|
|
199
|
+
};
|
|
200
|
+
if (!payload.targetPaths && git.branch) {
|
|
201
|
+
payload.targetPaths = [];
|
|
202
|
+
}
|
|
203
|
+
const task = await apiClient.createTask(freshSession.accessToken, workspace, payload);
|
|
204
|
+
runtime.logger.success("task enviada ao orchestrator.");
|
|
205
|
+
printTask(task);
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
async function runTaskTemplates(cliVersion, args) {
|
|
209
|
+
const { parsed, apiClient, freshSession } = await resolveAuthenticatedApi(cliVersion, args);
|
|
210
|
+
const templates = await apiClient.listTaskTemplates(freshSession.accessToken);
|
|
211
|
+
if (printJsonIfRequested(parsed, templates)) {
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
if (templates.length === 0) {
|
|
215
|
+
process.stdout.write("VectorPlane: nenhum template cadastrado.\n");
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
for (const template of templates) {
|
|
219
|
+
printTaskTemplate(template);
|
|
220
|
+
}
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
async function runTaskInspect(cliVersion, args) {
|
|
224
|
+
const { apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
225
|
+
const parsed = parseArgs(args);
|
|
226
|
+
const taskId = requirePositional(parsed, 0, "Uso: vp task inspect <taskId>");
|
|
227
|
+
const task = await apiClient.getTask(freshSession.accessToken, workspace, taskId);
|
|
228
|
+
if (!printJsonIfRequested(parsed, task)) {
|
|
229
|
+
printTask(task);
|
|
230
|
+
}
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
async function resolveTaskClaimContext(cliVersion, args) {
|
|
234
|
+
const context = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
235
|
+
const agentName = getStringOption(context.parsed, "agent-name")?.trim() || "vp-cli";
|
|
236
|
+
const clientInstanceId = getStringOption(context.parsed, "client-instance-id")?.trim();
|
|
237
|
+
return { ...context, agentName, clientInstanceId: clientInstanceId || undefined };
|
|
238
|
+
}
|
|
239
|
+
async function runTaskClaim(cliVersion, args) {
|
|
240
|
+
const { parsed, apiClient, freshSession, workspace, runtime, profileState, agentName, clientInstanceId } = await resolveTaskClaimContext(cliVersion, args);
|
|
241
|
+
const capability = getStringOption(parsed, "capability")?.trim();
|
|
242
|
+
if (!capability) {
|
|
243
|
+
throw new ValidationError("Informe `--capability` para buscar um step claimable.");
|
|
244
|
+
}
|
|
245
|
+
const claimables = await apiClient.listClaimableTaskSteps(freshSession.accessToken, workspace, {
|
|
246
|
+
capability,
|
|
247
|
+
agentName,
|
|
248
|
+
});
|
|
249
|
+
if (claimables.length === 0) {
|
|
250
|
+
runtime.logger.info("nenhum step claimable encontrado.");
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
const selected = claimables[0];
|
|
254
|
+
const claimed = await apiClient.claimTaskStep(freshSession.accessToken, workspace, selected.taskId, selected.stepId, {
|
|
255
|
+
sessionId: profileState.lastSessionId ?? undefined,
|
|
256
|
+
clientInstanceId,
|
|
257
|
+
agentName,
|
|
258
|
+
});
|
|
259
|
+
runtime.logger.success("step claimado pelo agente CLI.");
|
|
260
|
+
if (!printJsonIfRequested(parsed, claimed)) {
|
|
261
|
+
printClaimableStep(claimed.claim);
|
|
262
|
+
}
|
|
263
|
+
return 0;
|
|
264
|
+
}
|
|
265
|
+
async function runTaskExecute(cliVersion, args) {
|
|
266
|
+
const { parsed, apiClient, freshSession, workspace, runtime, rootPath, profileState, agentName, clientInstanceId } = await resolveTaskClaimContext(cliVersion, args);
|
|
267
|
+
const taskId = requirePositional(parsed, 0, "Uso: vp task execute <taskId> --step <stepId> [--json]");
|
|
268
|
+
const stepId = getStringOption(parsed, "step");
|
|
269
|
+
if (!stepId) {
|
|
270
|
+
throw new ValidationError("Informe `--step`.");
|
|
271
|
+
}
|
|
272
|
+
const task = await apiClient.getTask(freshSession.accessToken, workspace, taskId);
|
|
273
|
+
const step = task.steps.find((item) => item.id === stepId);
|
|
274
|
+
if (!step) {
|
|
275
|
+
throw new ValidationError("Step não encontrado para a task informada.");
|
|
276
|
+
}
|
|
277
|
+
if (step.status !== "claimed" && step.status !== "pending_claim" && step.status !== "running") {
|
|
278
|
+
throw new ValidationError("O step informado não está disponível para execução local.");
|
|
279
|
+
}
|
|
280
|
+
const claim = step.status === "pending_claim"
|
|
281
|
+
? (await apiClient.claimTaskStep(freshSession.accessToken, workspace, taskId, stepId, {
|
|
282
|
+
sessionId: profileState.lastSessionId ?? undefined,
|
|
283
|
+
clientInstanceId,
|
|
284
|
+
agentName,
|
|
285
|
+
})).claim
|
|
286
|
+
: {
|
|
287
|
+
workspaceId: workspace,
|
|
288
|
+
taskId,
|
|
289
|
+
taskTitle: task.title,
|
|
290
|
+
stepId,
|
|
291
|
+
stepName: step.name,
|
|
292
|
+
capability: step.capability,
|
|
293
|
+
status: step.status,
|
|
294
|
+
assignedAgentId: String(step.assignedAgentId ?? ""),
|
|
295
|
+
assignedAgentName: String(step.assignedAgentName ?? ""),
|
|
296
|
+
execution: step.output && typeof step.output === "object" && "execution" in step.output
|
|
297
|
+
? step.output.execution
|
|
298
|
+
: null,
|
|
299
|
+
createdAt: task.createdAt ?? new Date().toISOString(),
|
|
300
|
+
updatedAt: task.updatedAt ?? new Date().toISOString(),
|
|
301
|
+
};
|
|
302
|
+
await apiClient.callbackTaskStep(freshSession.accessToken, workspace, taskId, stepId, {
|
|
303
|
+
status: "running",
|
|
304
|
+
agentId: claim.assignedAgentId || undefined,
|
|
305
|
+
output: {
|
|
306
|
+
executionMode: "cli_command",
|
|
307
|
+
startedBy: "vp task execute",
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
const result = await executeLocalTaskStep({
|
|
311
|
+
execution: claim.execution,
|
|
312
|
+
workspaceRoot: rootPath,
|
|
313
|
+
workspaceRef: workspace,
|
|
314
|
+
taskId,
|
|
315
|
+
stepId,
|
|
316
|
+
capability: step.capability,
|
|
317
|
+
});
|
|
318
|
+
const callbackPayload = result.exitCode === 0
|
|
319
|
+
? {
|
|
320
|
+
status: "completed",
|
|
321
|
+
agentId: claim.assignedAgentId || undefined,
|
|
322
|
+
output: {
|
|
323
|
+
executionMode: "cli_command",
|
|
324
|
+
command: result.command,
|
|
325
|
+
args: result.args,
|
|
326
|
+
cwd: result.cwd,
|
|
327
|
+
exitCode: result.exitCode,
|
|
328
|
+
stdout: result.stdout || null,
|
|
329
|
+
stderr: result.stderr || null,
|
|
330
|
+
},
|
|
331
|
+
}
|
|
332
|
+
: {
|
|
333
|
+
status: "failed",
|
|
334
|
+
agentId: claim.assignedAgentId || undefined,
|
|
335
|
+
output: {
|
|
336
|
+
executionMode: "cli_command",
|
|
337
|
+
command: result.command,
|
|
338
|
+
args: result.args,
|
|
339
|
+
cwd: result.cwd,
|
|
340
|
+
exitCode: result.exitCode,
|
|
341
|
+
stdout: result.stdout || null,
|
|
342
|
+
stderr: result.stderr || null,
|
|
343
|
+
},
|
|
344
|
+
error: result.stderr || `Comando finalizou com exit code ${result.exitCode}.`,
|
|
345
|
+
};
|
|
346
|
+
const updatedTask = await apiClient.callbackTaskStep(freshSession.accessToken, workspace, taskId, stepId, callbackPayload);
|
|
347
|
+
runtime.logger.success(callbackPayload.status === "completed" ? "step executado com sucesso." : "step executado com falha.");
|
|
348
|
+
if (!printJsonIfRequested(parsed, updatedTask)) {
|
|
349
|
+
printTask(updatedTask);
|
|
350
|
+
}
|
|
351
|
+
return callbackPayload.status === "completed" ? 0 : 1;
|
|
352
|
+
}
|
|
353
|
+
async function runTaskDaemon(cliVersion, args) {
|
|
354
|
+
const { parsed, apiClient, freshSession, workspace, runtime, profileState, agentName, clientInstanceId } = await resolveTaskClaimContext(cliVersion, args);
|
|
355
|
+
const capability = getStringOption(parsed, "capability")?.trim();
|
|
356
|
+
if (!capability) {
|
|
357
|
+
throw new ValidationError("Informe `--capability`.");
|
|
358
|
+
}
|
|
359
|
+
const maxSteps = Number(getStringOption(parsed, "max-steps") ?? "10");
|
|
360
|
+
let processed = 0;
|
|
361
|
+
while (processed < maxSteps) {
|
|
362
|
+
const claimables = await apiClient.listClaimableTaskSteps(freshSession.accessToken, workspace, { capability, agentName });
|
|
363
|
+
if (claimables.length === 0) {
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
const selected = claimables[0];
|
|
367
|
+
await apiClient.claimTaskStep(freshSession.accessToken, workspace, selected.taskId, selected.stepId, {
|
|
368
|
+
sessionId: profileState.lastSessionId ?? undefined,
|
|
369
|
+
clientInstanceId,
|
|
370
|
+
agentName,
|
|
371
|
+
});
|
|
372
|
+
const exitCode = await runTaskExecute(cliVersion, [selected.taskId, "--step", selected.stepId, "--agent-name", agentName]);
|
|
373
|
+
processed += 1;
|
|
374
|
+
if (exitCode !== 0) {
|
|
375
|
+
return exitCode;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
runtime.logger.success(`daemon finalizado após ${processed} step(s).`);
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
async function runTaskDelegate(cliVersion, args) {
|
|
382
|
+
const { apiClient, freshSession, workspace, runtime } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
383
|
+
const parsed = parseArgs(args);
|
|
384
|
+
const taskId = requirePositional(parsed, 0, "Uso: vp task delegate <taskId> --step <stepId> --agent-id <agentId> --reason <texto>");
|
|
385
|
+
const stepId = getStringOption(parsed, "step");
|
|
386
|
+
const agentId = getStringOption(parsed, "agent-id");
|
|
387
|
+
const reason = getStringOption(parsed, "reason");
|
|
388
|
+
if (!stepId || !agentId || !reason) {
|
|
389
|
+
throw new ValidationError("Informe `--step`, `--agent-id` e `--reason`.");
|
|
390
|
+
}
|
|
391
|
+
const task = await apiClient.delegateTask(freshSession.accessToken, workspace, taskId, stepId, agentId, reason);
|
|
392
|
+
runtime.logger.success("delegação registrada.");
|
|
393
|
+
if (!printJsonIfRequested(parsed, task)) {
|
|
394
|
+
printTask(task);
|
|
395
|
+
}
|
|
396
|
+
return 0;
|
|
397
|
+
}
|
|
398
|
+
async function runTaskStepUpdate(cliVersion, args) {
|
|
399
|
+
const { apiClient, freshSession, workspace, runtime } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
400
|
+
const parsed = parseArgs(args);
|
|
401
|
+
const taskId = requirePositional(parsed, 0, "Uso: vp task step-update <taskId> --step <stepId> --status <status>");
|
|
402
|
+
const stepId = getStringOption(parsed, "step");
|
|
403
|
+
const status = getStringOption(parsed, "status");
|
|
404
|
+
if (!stepId || !status) {
|
|
405
|
+
throw new ValidationError("Informe `--step` e `--status`.");
|
|
406
|
+
}
|
|
407
|
+
const output = getStringOption(parsed, "output");
|
|
408
|
+
const error = getStringOption(parsed, "error");
|
|
409
|
+
const task = await apiClient.callbackTaskStep(freshSession.accessToken, workspace, taskId, stepId, {
|
|
410
|
+
status,
|
|
411
|
+
output: output ? JSON.parse(output) : undefined,
|
|
412
|
+
error: error ?? undefined,
|
|
413
|
+
});
|
|
414
|
+
runtime.logger.success("estado do step atualizado.");
|
|
415
|
+
if (!printJsonIfRequested(parsed, task)) {
|
|
416
|
+
printTask(task);
|
|
417
|
+
}
|
|
418
|
+
return 0;
|
|
419
|
+
}
|
|
420
|
+
async function runTaskHandoff(cliVersion, args) {
|
|
421
|
+
const { apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
422
|
+
const parsed = parseArgs(args);
|
|
423
|
+
const taskId = requirePositional(parsed, 0, "Uso: vp task handoff <taskId>");
|
|
424
|
+
const handoff = await apiClient.getTaskHandoff(freshSession.accessToken, workspace, taskId);
|
|
425
|
+
if (!printJsonIfRequested(parsed, handoff)) {
|
|
426
|
+
printHandoff(handoff);
|
|
427
|
+
}
|
|
428
|
+
return 0;
|
|
429
|
+
}
|
|
430
|
+
async function runTaskObservability(cliVersion, args) {
|
|
431
|
+
const { apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
432
|
+
const parsed = parseArgs(args);
|
|
433
|
+
const taskId = getStringOption(parsed, "task");
|
|
434
|
+
const payload = taskId
|
|
435
|
+
? await apiClient.getTaskObservability(freshSession.accessToken, workspace, taskId)
|
|
436
|
+
: await apiClient.getWorkspaceTaskObservability(freshSession.accessToken, workspace);
|
|
437
|
+
if (!printJsonIfRequested(parsed, payload)) {
|
|
438
|
+
printObservability(payload);
|
|
439
|
+
}
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
async function runTaskWatch(cliVersion, args) {
|
|
443
|
+
const { apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
444
|
+
const parsed = parseArgs(args);
|
|
445
|
+
const taskId = requirePositional(parsed, 0, "Uso: vp task watch <taskId> [--json]");
|
|
446
|
+
const task = await apiClient.getTask(freshSession.accessToken, workspace, taskId);
|
|
447
|
+
if (printJsonIfRequested(parsed, task)) {
|
|
448
|
+
return 0;
|
|
449
|
+
}
|
|
450
|
+
printTask(task);
|
|
451
|
+
const controller = new AbortController();
|
|
452
|
+
let exitCode = 0;
|
|
453
|
+
await apiClient.streamWorkspaceEvents(freshSession.accessToken, workspace, {
|
|
454
|
+
taskId,
|
|
455
|
+
signal: controller.signal,
|
|
456
|
+
onEvent: (event) => {
|
|
457
|
+
if (event.type === "ready" || event.type === "heartbeat") {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
printWatchEvent(event);
|
|
461
|
+
if (event.type === "task.failed" || event.type === "task.blocked") {
|
|
462
|
+
exitCode = 1;
|
|
463
|
+
}
|
|
464
|
+
if (event.type === "task.completed" || event.type === "task.failed" || event.type === "task.blocked") {
|
|
465
|
+
controller.abort();
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
}).catch((error) => {
|
|
469
|
+
if (controller.signal.aborted) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
throw error;
|
|
473
|
+
});
|
|
474
|
+
return exitCode;
|
|
475
|
+
}
|
|
476
|
+
async function runTaskHealth(cliVersion, args) {
|
|
477
|
+
const { apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
478
|
+
const parsed = parseArgs(args);
|
|
479
|
+
const payload = await apiClient.getWorkspaceHealth(freshSession.accessToken, workspace);
|
|
480
|
+
if (!printJsonIfRequested(parsed, payload)) {
|
|
481
|
+
process.stdout.write(`Workspace: ${workspace}\n`);
|
|
482
|
+
process.stdout.write(`Health score: ${String(payload.healthScore ?? payload.health_score ?? "n/a")}\n`);
|
|
483
|
+
process.stdout.write(`Risk level: ${String(payload.riskLevel ?? payload.risk_level ?? "n/a")}\n`);
|
|
484
|
+
}
|
|
485
|
+
return 0;
|
|
486
|
+
}
|
|
487
|
+
async function runTaskApproval(cliVersion, args, approved) {
|
|
488
|
+
const { apiClient, freshSession, workspace, runtime } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
489
|
+
const parsed = parseArgs(args);
|
|
490
|
+
const taskId = requirePositional(parsed, 0, approved ? "Uso: vp task approve <taskId> [--rationale <texto>]" : "Uso: vp task reject <taskId> --rationale <texto>");
|
|
491
|
+
const rationale = getStringOption(parsed, "rationale")?.trim();
|
|
492
|
+
if (!approved && !rationale) {
|
|
493
|
+
throw new ValidationError("Informe `--rationale` ao rejeitar uma task.");
|
|
494
|
+
}
|
|
495
|
+
const task = await apiClient.approveTask(freshSession.accessToken, workspace, taskId, {
|
|
496
|
+
approved,
|
|
497
|
+
approvedBy: runtime.profile.name,
|
|
498
|
+
rationale: rationale || undefined,
|
|
499
|
+
});
|
|
500
|
+
runtime.logger.success(approved ? "aprovação registrada." : "rejeição registrada.");
|
|
501
|
+
if (!printJsonIfRequested(parsed, task)) {
|
|
502
|
+
printTask(task);
|
|
503
|
+
}
|
|
504
|
+
return 0;
|
|
505
|
+
}
|
|
506
|
+
export async function runTaskCommand(cliVersion, args) {
|
|
507
|
+
const parsed = parseArgs(args);
|
|
508
|
+
const subcommand = requirePositional(parsed, 0, "Uso: vp task <run|templates|list|inspect|watch|claim|execute|daemon|approve|reject|delegate|step-update|handoff|observability|health> [...]");
|
|
509
|
+
switch (subcommand) {
|
|
510
|
+
case "run":
|
|
511
|
+
return runTaskRun(cliVersion, args.slice(1));
|
|
512
|
+
case "templates":
|
|
513
|
+
return runTaskTemplates(cliVersion, args.slice(1));
|
|
514
|
+
case "list":
|
|
515
|
+
return runTaskList(cliVersion, args.slice(1));
|
|
516
|
+
case "inspect":
|
|
517
|
+
return runTaskInspect(cliVersion, args.slice(1));
|
|
518
|
+
case "watch":
|
|
519
|
+
return runTaskWatch(cliVersion, args.slice(1));
|
|
520
|
+
case "claim":
|
|
521
|
+
return runTaskClaim(cliVersion, args.slice(1));
|
|
522
|
+
case "execute":
|
|
523
|
+
return runTaskExecute(cliVersion, args.slice(1));
|
|
524
|
+
case "daemon":
|
|
525
|
+
return runTaskDaemon(cliVersion, args.slice(1));
|
|
526
|
+
case "approve":
|
|
527
|
+
return runTaskApproval(cliVersion, args.slice(1), true);
|
|
528
|
+
case "reject":
|
|
529
|
+
return runTaskApproval(cliVersion, args.slice(1), false);
|
|
530
|
+
case "delegate":
|
|
531
|
+
return runTaskDelegate(cliVersion, args.slice(1));
|
|
532
|
+
case "step-update":
|
|
533
|
+
return runTaskStepUpdate(cliVersion, args.slice(1));
|
|
534
|
+
case "handoff":
|
|
535
|
+
return runTaskHandoff(cliVersion, args.slice(1));
|
|
536
|
+
case "observability":
|
|
537
|
+
return runTaskObservability(cliVersion, args.slice(1));
|
|
538
|
+
case "health":
|
|
539
|
+
return runTaskHealth(cliVersion, args.slice(1));
|
|
540
|
+
default:
|
|
541
|
+
throw new ValidationError("Uso: vp task <run|templates|list|inspect|watch|claim|execute|daemon|approve|reject|delegate|step-update|handoff|observability|health> [...]");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
//# sourceMappingURL=task.js.map
|