@vectorplane/ctrl-cli 0.1.12 → 0.1.14
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 +58 -95
- package/dist/commands/context.js +14 -5
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +13 -20
- package/dist/commands/login.js +24 -3
- package/dist/commands/logout.js +3 -0
- package/dist/commands/status.js +8 -2
- package/dist/commands/task.js +44 -2
- package/dist/commands/workspace.js +65 -0
- package/dist/core/api.d.ts +23 -1
- package/dist/core/api.js +112 -0
- package/dist/core/auth.d.ts +2 -2
- package/dist/core/auth.js +89 -46
- package/dist/core/constants.js +21 -1
- package/dist/core/machine.d.ts +8 -1
- package/dist/core/machine.js +49 -18
- package/dist/index.js +5 -5
- package/dist/types/api.d.ts +46 -0
- package/dist/types/auth.d.ts +1 -0
- package/dist/types/config.d.ts +8 -0
- package/dist/types/machine.d.ts +3 -3
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
CLI oficial do VectorPlane.
|
|
4
4
|
|
|
5
|
-
Use o comando `vp` para autenticar, verificar a sessão local
|
|
5
|
+
Use o comando `vp` para autenticar, verificar a sessão local, sincronizar o workspace e operar o control plane com uma sessão local endurecida.
|
|
6
6
|
|
|
7
7
|
## Instalação
|
|
8
8
|
|
|
@@ -15,116 +15,73 @@ npm install -g @vectorplane/ctrl-cli
|
|
|
15
15
|
```bash
|
|
16
16
|
vp init
|
|
17
17
|
vp login
|
|
18
|
+
vp login --manual
|
|
19
|
+
vp login --device
|
|
18
20
|
vp logout
|
|
19
21
|
vp status
|
|
22
|
+
vp doctor --policy
|
|
20
23
|
vp sync
|
|
21
24
|
vp task templates
|
|
22
25
|
vp task run --title "Entrega X" --intention "Implementar fluxo Y"
|
|
23
|
-
vp task
|
|
24
|
-
vp task claim --capability qa.test
|
|
25
|
-
vp task execute <task-id> --step <step-id>
|
|
26
|
-
vp task daemon --capability qa.test
|
|
26
|
+
vp task watch <task-id>
|
|
27
27
|
vp context --search "decisões sobre autenticação"
|
|
28
|
-
vp
|
|
29
|
-
vp
|
|
30
|
-
vp session check-in --workspace <workspace> --agent codex --type codex --client openai-codex --feature feature-key --task task-key --owning-path "src/core,src/ui" --need "api-contract" --provide "implementation" --status "starting implementation"
|
|
31
|
-
vp draft create --type progress --title "Entrega concluída" --content "Resumo da mudança"
|
|
28
|
+
vp workspace webhook list
|
|
29
|
+
vp session check-in --workspace <workspace> --agent codex --type codex --client openai-codex
|
|
32
30
|
```
|
|
33
31
|
|
|
34
|
-
## Comandos
|
|
35
|
-
|
|
36
|
-
### `vp init`
|
|
37
|
-
|
|
38
|
-
- autentica localmente se ainda não houver sessão
|
|
39
|
-
- resolve e associa o workspace atual a partir do `git remote`, somente quando houver correspondência com um workspace que o usuário pode acessar
|
|
40
|
-
- detecta o agente local automaticamente, pede confirmação se houver ambiguidade e aceita `--agent`
|
|
41
|
-
- baixa o template oficial do backend
|
|
42
|
-
- grava o arquivo local e adiciona a entrada no `.gitignore`
|
|
43
|
-
- aceita `--force` para sobrescrever template existente
|
|
44
|
-
- durante execução, se outro agente passar a operar no workspace e a detecção for confiável, o setup local é reconciliado automaticamente
|
|
32
|
+
## Comandos principais
|
|
45
33
|
|
|
46
34
|
### `vp login`
|
|
47
35
|
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
- em ambientes remotos ou isolados, o loopback local pode exigir um fluxo alternativo
|
|
53
|
-
- a URL web do login é emitida pelo control plane; o domínio do app não deve ser hardcoded em automações externas
|
|
36
|
+
- suporta `browser`, `manual` e `device`
|
|
37
|
+
- `--manual` evita abrir o navegador automaticamente
|
|
38
|
+
- `--device` evita dependência de callback loopback e fecha melhor com SSH, container, CI e WSL
|
|
39
|
+
- a URL web do login é emitida pelo control plane
|
|
54
40
|
|
|
55
41
|
### `vp logout`
|
|
56
42
|
|
|
57
|
-
-
|
|
58
|
-
-
|
|
43
|
+
- tenta revogar a sessão remotamente
|
|
44
|
+
- limpa a sessão local do dispositivo
|
|
45
|
+
- registra auditoria local mínima da operação
|
|
46
|
+
|
|
47
|
+
### `vp doctor`
|
|
59
48
|
|
|
60
|
-
|
|
49
|
+
- valida storage de sessão, política ativa, conectividade e ambiente local
|
|
50
|
+
- suporta `--policy` para checar só conformidade local
|
|
51
|
+
- recomenda o modo de login mais seguro para o ambiente atual
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
-
|
|
53
|
+
### `vp config`
|
|
54
|
+
|
|
55
|
+
- gerencia perfis, política local e envelope de privacidade
|
|
56
|
+
- `vp config privacy set <standard|minimal|enterprise>`
|
|
57
|
+
- `vp config policy set <chave> <true|false>`
|
|
65
58
|
|
|
66
59
|
### `vp status`
|
|
67
60
|
|
|
68
|
-
- mostra
|
|
69
|
-
- exibe
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
- cria drafts editoriais ligados ao workspace atual
|
|
93
|
-
- lista drafts existentes para conferência rápida
|
|
94
|
-
- suporta `ui`, `ux`, `project_skeleton`, `template_engineering`, `patterns`, `progress`, `decisions` e `architecture`
|
|
95
|
-
- aceita `--no-impact` para registrar explicitamente quando uma lane não foi afetada
|
|
96
|
-
|
|
97
|
-
### `vp session`
|
|
98
|
-
|
|
99
|
-
- suporta `check-in`, `heartbeat` e `check-out`
|
|
100
|
-
- permite declarar `feature`, `task`, `component`, `role`, `owning-path`, `need`, `provide` e `status`
|
|
101
|
-
- deve ser usado em conjunto com `vp context --delivery` para reduzir conflito entre agentes ativos
|
|
102
|
-
|
|
103
|
-
### Comandos adicionais já implementados
|
|
104
|
-
|
|
105
|
-
- `vp whoami`
|
|
106
|
-
- mostra a identidade autenticada
|
|
107
|
-
- `vp doctor`
|
|
108
|
-
- executa verificações locais e de conectividade
|
|
109
|
-
- `vp config`
|
|
110
|
-
- gerencia perfis e configuração local
|
|
111
|
-
- inclui `vp config privacy` e `vp config policy`
|
|
112
|
-
- `vp workspace`
|
|
113
|
-
- gerencia o workspace atual
|
|
114
|
-
- `vp session`
|
|
115
|
-
- gerencia sessões de agente
|
|
116
|
-
- `vp context`
|
|
117
|
-
- carrega contexto remoto do workspace
|
|
118
|
-
- suporta `--search "QUERY"` para busca semântica na memória
|
|
119
|
-
- com `--delivery --search`, retorna delivery context com fragmentos semânticos relevantes
|
|
120
|
-
- `vp bootstrap`
|
|
121
|
-
- obtém instruções de setup do workspace
|
|
122
|
-
- `vp event send`
|
|
123
|
-
- envia eventos operacionais
|
|
124
|
-
- `vp draft`
|
|
125
|
-
- envia e consulta drafts editoriais do workspace
|
|
126
|
-
- `vp task`
|
|
127
|
-
- opera tasks do autonomous control plane
|
|
61
|
+
- mostra estado da sessão local
|
|
62
|
+
- exibe storage usado, último login, último refresh, último logout e falhas recentes de auth
|
|
63
|
+
|
|
64
|
+
## Privacidade de máquina
|
|
65
|
+
|
|
66
|
+
Perfis:
|
|
67
|
+
|
|
68
|
+
- `standard`: envia o contexto operacional mínimo usual
|
|
69
|
+
- `minimal`: reduz ao máximo dados locais e desabilita lookup de IP/interface por padrão
|
|
70
|
+
- `enterprise`: permite envelope mais rico, mas continua respeitando policies locais
|
|
71
|
+
|
|
72
|
+
Policies disponíveis:
|
|
73
|
+
|
|
74
|
+
- `requireSecureStorage`
|
|
75
|
+
- `disableFileSessionFallback`
|
|
76
|
+
- `requireManualLogin`
|
|
77
|
+
- `requireDeviceLogin`
|
|
78
|
+
- `blockPublicIpLookup`
|
|
79
|
+
- `blockMacAddressCollection`
|
|
80
|
+
- `blockUsernameCollection`
|
|
81
|
+
- `blockNetworkInterfaceCollection`
|
|
82
|
+
- `blockCurrentDirectoryCollection`
|
|
83
|
+
- `blockHomeDirectoryCollection`
|
|
84
|
+
- `blockShellCollection`
|
|
128
85
|
|
|
129
86
|
## Persistência local
|
|
130
87
|
|
|
@@ -139,8 +96,14 @@ vp draft create --type progress --title "Entrega concluída" --content "Resumo d
|
|
|
139
96
|
## Segurança
|
|
140
97
|
|
|
141
98
|
- tokens nunca são impressos no terminal
|
|
142
|
-
-
|
|
143
|
-
- o callback valida o `state`
|
|
99
|
+
- logs mascaram chaves sensíveis e ids de sessão
|
|
144
100
|
- a sessão usa secure storage do sistema quando disponível
|
|
145
|
-
- existe fallback para arquivo local apenas quando permitido
|
|
101
|
+
- existe fallback para arquivo local apenas quando permitido pela policy
|
|
146
102
|
- `vp logout` encerra a sessão local e tenta revogação remota
|
|
103
|
+
- o CLI continua sendo thin client: autorização e regra crítica permanecem no backend
|
|
104
|
+
|
|
105
|
+
## Tutoriais relacionados
|
|
106
|
+
|
|
107
|
+
- setup completo do ecossistema: [/home/developer/Documentos/Projetos/conductor-edge-ia/docs/tutorials/setup-ecossistema.md](/home/developer/Documentos/Projetos/conductor-edge-ia/docs/tutorials/setup-ecossistema.md)
|
|
108
|
+
- operação de workspace: [/home/developer/Documentos/Projetos/conductor-edge-ia/docs/tutorials/operacao-workspace.md](/home/developer/Documentos/Projetos/conductor-edge-ia/docs/tutorials/operacao-workspace.md)
|
|
109
|
+
- memória e agentes: [/home/developer/Documentos/Projetos/conductor-edge-ia/docs/tutorials/memoria-e-agentes.md](/home/developer/Documentos/Projetos/conductor-edge-ia/docs/tutorials/memoria-e-agentes.md)
|
package/dist/commands/context.js
CHANGED
|
@@ -12,6 +12,7 @@ export async function runContextCommand(cliVersion, args) {
|
|
|
12
12
|
const parsed = parseArgs(args);
|
|
13
13
|
const delivery = getBooleanOption(parsed, "delivery");
|
|
14
14
|
const snapshot = getBooleanOption(parsed, "snapshot");
|
|
15
|
+
const diff = getBooleanOption(parsed, "diff");
|
|
15
16
|
const search = getStringOption(parsed, "search")?.trim();
|
|
16
17
|
const runtime = await loadRuntimeStatus();
|
|
17
18
|
const session = await ensureSessionAvailable(runtime.profile.name);
|
|
@@ -38,6 +39,9 @@ export async function runContextCommand(cliVersion, args) {
|
|
|
38
39
|
throw new ValidationError("Nenhum workspace resolvido para carregar contexto.");
|
|
39
40
|
}
|
|
40
41
|
await ensureRuntimeAgentSetup({ rootPath, workspace, git, session: freshSession, apiClient, logger: runtime.logger });
|
|
42
|
+
if (diff && (delivery || snapshot || search)) {
|
|
43
|
+
throw new ValidationError("`--diff` não pode ser combinado com `--delivery`, `--snapshot` ou `--search`.");
|
|
44
|
+
}
|
|
41
45
|
const payload = search
|
|
42
46
|
? (delivery
|
|
43
47
|
? await apiClient.getWorkspaceDeliveryContextSemantic(freshSession.accessToken, workspace, {
|
|
@@ -52,11 +56,16 @@ export async function runContextCommand(cliVersion, args) {
|
|
|
52
56
|
authority: getStringOption(parsed, "authority")?.trim(),
|
|
53
57
|
limit: Number(getStringOption(parsed, "limit") ?? "0") || undefined,
|
|
54
58
|
}))
|
|
55
|
-
:
|
|
56
|
-
? await apiClient.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
: diff
|
|
60
|
+
? await apiClient.getWorkspaceSnapshotDiff(freshSession.accessToken, workspace, {
|
|
61
|
+
from: getStringOption(parsed, "from")?.trim() || "latest-1",
|
|
62
|
+
to: getStringOption(parsed, "to")?.trim() || "latest",
|
|
63
|
+
})
|
|
64
|
+
: delivery
|
|
65
|
+
? await apiClient.getWorkspaceDeliveryContext(freshSession.accessToken, workspace)
|
|
66
|
+
: snapshot
|
|
67
|
+
? await apiClient.getWorkspaceSnapshot(freshSession.accessToken, workspace)
|
|
68
|
+
: await apiClient.getWorkspaceContext(freshSession.accessToken, workspace);
|
|
60
69
|
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
61
70
|
return 0;
|
|
62
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function runDoctorCommand(cliVersion: string): Promise<number>;
|
|
1
|
+
export declare function runDoctorCommand(cliVersion: string, args?: string[]): Promise<number>;
|
package/dist/commands/doctor.js
CHANGED
|
@@ -2,10 +2,11 @@ import { access } from "node:fs/promises";
|
|
|
2
2
|
import { constants as fsConstants } from "node:fs";
|
|
3
3
|
import { configDirectoryExists, ensureSessionAvailable, getConfigDirectoryPath, getSessionStorageInfo } from "../core/config.js";
|
|
4
4
|
import { collectGitContext } from "../core/git.js";
|
|
5
|
-
import { collectMachineContext, collectRuntimeContext } from "../core/machine.js";
|
|
5
|
+
import { assessExecutionEnvironment, collectMachineContext, collectRuntimeContext, describePrivacyEnvelope } from "../core/machine.js";
|
|
6
6
|
import { loadRuntimeStatus } from "../core/runtime.js";
|
|
7
7
|
import { ensureFreshSession } from "../core/session.js";
|
|
8
8
|
import { VectorPlaneApiClient } from "../core/api.js";
|
|
9
|
+
import { getBooleanOption, parseArgs } from "../core/cli.js";
|
|
9
10
|
async function writable(filePath) {
|
|
10
11
|
try {
|
|
11
12
|
await access(filePath, fsConstants.W_OK);
|
|
@@ -18,23 +19,9 @@ async function writable(filePath) {
|
|
|
18
19
|
function printCheck(label, ok, details) {
|
|
19
20
|
process.stdout.write(`${ok ? "OK" : "FAIL"} ${label}: ${details}\n`);
|
|
20
21
|
}
|
|
21
|
-
function
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
risks.push("wsl");
|
|
25
|
-
}
|
|
26
|
-
if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT) {
|
|
27
|
-
risks.push("ssh");
|
|
28
|
-
}
|
|
29
|
-
if (process.env.CI === "true") {
|
|
30
|
-
risks.push("ci");
|
|
31
|
-
}
|
|
32
|
-
if (process.env.CONTAINER || process.env.DOCKER_CONTAINER) {
|
|
33
|
-
risks.push("container");
|
|
34
|
-
}
|
|
35
|
-
return risks;
|
|
36
|
-
}
|
|
37
|
-
export async function runDoctorCommand(cliVersion) {
|
|
22
|
+
export async function runDoctorCommand(cliVersion, args = []) {
|
|
23
|
+
const parsed = parseArgs(args);
|
|
24
|
+
const policyOnly = getBooleanOption(parsed, "policy");
|
|
38
25
|
const runtime = await loadRuntimeStatus();
|
|
39
26
|
const configDirectory = await getConfigDirectoryPath();
|
|
40
27
|
const [hasConfigDir, git, canWriteConfig] = await Promise.all([
|
|
@@ -43,15 +30,21 @@ export async function runDoctorCommand(cliVersion) {
|
|
|
43
30
|
writable(configDirectory),
|
|
44
31
|
]);
|
|
45
32
|
const storage = await getSessionStorageInfo(runtime.profile.name);
|
|
46
|
-
const
|
|
33
|
+
const environment = assessExecutionEnvironment();
|
|
34
|
+
const privacyEnvelope = describePrivacyEnvelope(runtime.profile.machinePrivacyProfile, runtime.config.policy);
|
|
47
35
|
printCheck("config_dir", hasConfigDir, configDirectory);
|
|
48
36
|
printCheck("config_dir_writable", canWriteConfig, canWriteConfig ? "gravável" : "sem permissão de escrita");
|
|
49
37
|
printCheck("git", git.isRepository, git.isRepository ? (git.rootPath ?? process.cwd()) : "repositório não detectado");
|
|
50
38
|
printCheck("node", true, process.version);
|
|
51
39
|
printCheck("privacy_profile", true, runtime.profile.machinePrivacyProfile);
|
|
40
|
+
printCheck("privacy_envelope", true, JSON.stringify(privacyEnvelope));
|
|
52
41
|
printCheck("session_storage", (storage?.protected ?? false) || runtime.config.preferredSessionStorage === "system", storage ? `${storage.backend}:${storage.protected ? "protected" : "file"}` : runtime.config.preferredSessionStorage);
|
|
53
42
|
printCheck("policy_secure_storage", !runtime.config.policy.requireSecureStorage || storage?.protected === true, JSON.stringify(runtime.config.policy));
|
|
54
|
-
printCheck("execution_environment",
|
|
43
|
+
printCheck("execution_environment", environment.loopbackLikelySafe, environment.risks.length === 0 ? "local" : environment.risks.join(","));
|
|
44
|
+
printCheck("login_recommendation", true, environment.recommendedLoginMode);
|
|
45
|
+
if (policyOnly) {
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
55
48
|
const apiClient = new VectorPlaneApiClient(runtime.profile.apiBaseUrl, runtime.config.requestTimeoutMs, runtime.logger);
|
|
56
49
|
try {
|
|
57
50
|
const health = await apiClient.getHealth();
|
package/dist/commands/login.js
CHANGED
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
import { getBooleanOption, getStringOption, parseArgs } from "../core/cli.js";
|
|
2
2
|
import { runLoginFlow } from "../core/auth.js";
|
|
3
3
|
import { getSessionStorageInfo, saveSession, setActiveProfile, updateProfileState, upsertProfile } from "../core/config.js";
|
|
4
|
-
import { collectMachineContext, collectRuntimeContext } from "../core/machine.js";
|
|
4
|
+
import { assessExecutionEnvironment, collectMachineContext, collectRuntimeContext } from "../core/machine.js";
|
|
5
5
|
import { loadRuntimeStatus } from "../core/runtime.js";
|
|
6
6
|
import { VectorPlaneApiClient } from "../core/api.js";
|
|
7
|
+
function resolveLoginMode(args) {
|
|
8
|
+
if (args.device || args.requireDeviceLogin) {
|
|
9
|
+
return "device";
|
|
10
|
+
}
|
|
11
|
+
if (args.manual || args.noBrowser || args.requireManualLogin) {
|
|
12
|
+
return "manual";
|
|
13
|
+
}
|
|
14
|
+
return "browser";
|
|
15
|
+
}
|
|
7
16
|
export async function runLoginCommand(cliVersion, args) {
|
|
8
17
|
const parsed = parseArgs(args);
|
|
9
18
|
const requestedProfile = getStringOption(parsed, "profile");
|
|
10
|
-
const noBrowser = getBooleanOption(parsed, "no-browser") || getBooleanOption(parsed, "manual");
|
|
11
19
|
if (requestedProfile) {
|
|
12
20
|
await upsertProfile(requestedProfile, { name: requestedProfile });
|
|
13
21
|
await setActiveProfile(requestedProfile);
|
|
14
22
|
}
|
|
15
23
|
const runtime = await loadRuntimeStatus();
|
|
24
|
+
const loginMode = resolveLoginMode({
|
|
25
|
+
noBrowser: getBooleanOption(parsed, "no-browser"),
|
|
26
|
+
manual: getBooleanOption(parsed, "manual"),
|
|
27
|
+
device: getBooleanOption(parsed, "device"),
|
|
28
|
+
requireManualLogin: runtime.config.policy.requireManualLogin,
|
|
29
|
+
requireDeviceLogin: runtime.config.policy.requireDeviceLogin,
|
|
30
|
+
});
|
|
31
|
+
const environment = assessExecutionEnvironment();
|
|
32
|
+
if (environment.recommendedLoginMode === "device" && loginMode === "browser") {
|
|
33
|
+
runtime.logger.warn("o ambiente atual parece pouco confiável para loopback. considere `vp login --device`.");
|
|
34
|
+
}
|
|
16
35
|
const machine = await collectMachineContext(runtime.device, runtime.config);
|
|
17
36
|
const runtimeContext = await collectRuntimeContext(cliVersion, "login", process.argv.slice(2));
|
|
18
37
|
const apiClient = new VectorPlaneApiClient(runtime.profile.apiBaseUrl, runtime.config.requestTimeoutMs, runtime.logger);
|
|
@@ -24,7 +43,7 @@ export async function runLoginCommand(cliVersion, args) {
|
|
|
24
43
|
device: runtime.device,
|
|
25
44
|
apiClient,
|
|
26
45
|
logger: runtime.logger,
|
|
27
|
-
|
|
46
|
+
loginMode,
|
|
28
47
|
});
|
|
29
48
|
await saveSession(session, runtime.profile.name);
|
|
30
49
|
const storage = await getSessionStorageInfo(runtime.profile.name);
|
|
@@ -34,6 +53,7 @@ export async function runLoginCommand(cliVersion, args) {
|
|
|
34
53
|
lastWorkspace: session.workspace,
|
|
35
54
|
lastError: null,
|
|
36
55
|
lastLoginAt: session.obtainedAt,
|
|
56
|
+
lastLoginMethod: loginMode,
|
|
37
57
|
lastSessionId: session.sessionId,
|
|
38
58
|
storageBackend: storage?.backend ?? null,
|
|
39
59
|
storageProtected: storage?.protected ?? null,
|
|
@@ -42,6 +62,7 @@ export async function runLoginCommand(cliVersion, args) {
|
|
|
42
62
|
});
|
|
43
63
|
runtime.logger.success("login realizado com sucesso.");
|
|
44
64
|
process.stdout.write(`Perfil: ${runtime.profile.name}\n`);
|
|
65
|
+
process.stdout.write(`Modo de login: ${loginMode}\n`);
|
|
45
66
|
process.stdout.write(`Workspace ativo: ${session.workspace}\n`);
|
|
46
67
|
return 0;
|
|
47
68
|
}
|
package/dist/commands/logout.js
CHANGED
|
@@ -15,8 +15,10 @@ export async function runLogoutCommand() {
|
|
|
15
15
|
return 0;
|
|
16
16
|
}
|
|
17
17
|
const apiClient = new VectorPlaneApiClient(runtime.profile.apiBaseUrl, runtime.config.requestTimeoutMs, runtime.logger);
|
|
18
|
+
let remoteRevokedAt = null;
|
|
18
19
|
try {
|
|
19
20
|
await revokeSession({ session, apiClient });
|
|
21
|
+
remoteRevokedAt = new Date().toISOString();
|
|
20
22
|
}
|
|
21
23
|
catch (error) {
|
|
22
24
|
runtime.logger.warn("não foi possível revogar a sessão remotamente. removendo a sessão local mesmo assim.");
|
|
@@ -29,6 +31,7 @@ export async function runLogoutCommand() {
|
|
|
29
31
|
await updateProfileState(runtime.profile.name, {
|
|
30
32
|
lastCommand: "logout",
|
|
31
33
|
lastLogoutAt: loggedOutAt,
|
|
34
|
+
lastRemoteRevokeAt: remoteRevokedAt,
|
|
32
35
|
lastSessionId: null,
|
|
33
36
|
});
|
|
34
37
|
runtime.logger.success("sessão local encerrada.");
|
package/dist/commands/status.js
CHANGED
|
@@ -35,16 +35,22 @@ export async function runStatusCommand() {
|
|
|
35
35
|
process.stdout.write(`Sessão CLI: ${session.sessionId}\n`);
|
|
36
36
|
process.stdout.write(`Expira em: ${session.expiresAt}\n`);
|
|
37
37
|
process.stdout.write(`Último refresh: ${profileState.lastRefreshAt ?? session.lastRefreshAt ?? "nunca"}\n`);
|
|
38
|
+
process.stdout.write(`Último login: ${profileState.lastLoginAt ?? "desconhecido"}\n`);
|
|
39
|
+
process.stdout.write(`Último método de login: ${profileState.lastLoginMethod ?? "desconhecido"}\n`);
|
|
40
|
+
process.stdout.write(`Último logout: ${profileState.lastLogoutAt ?? "nunca"}\n`);
|
|
41
|
+
process.stdout.write(`Última revogação remota: ${profileState.lastRemoteRevokeAt ?? "nunca"}\n`);
|
|
38
42
|
if (git.branch) {
|
|
39
43
|
process.stdout.write(`Branch: ${git.branch}\n`);
|
|
40
44
|
}
|
|
41
45
|
process.stdout.write(`Última sincronização: ${profileState.lastSyncAt ?? "nunca"}\n`);
|
|
42
46
|
process.stdout.write(`Status do último sync: ${profileState.lastSyncStatus ?? "desconhecido"}\n`);
|
|
43
|
-
process.stdout.write(`Último login: ${profileState.lastLoginAt ?? "desconhecido"}\n`);
|
|
44
|
-
process.stdout.write(`Último logout: ${profileState.lastLogoutAt ?? "nunca"}\n`);
|
|
45
47
|
if (profileState.lastSnapshotPath) {
|
|
46
48
|
process.stdout.write(`Último snapshot local: ${profileState.lastSnapshotPath}\n`);
|
|
47
49
|
}
|
|
50
|
+
if (profileState.lastAuthFailure) {
|
|
51
|
+
process.stdout.write(`Última falha de auth: ${profileState.lastAuthFailure}\n`);
|
|
52
|
+
process.stdout.write(`Falha registrada em: ${profileState.lastAuthFailureAt ?? "desconhecido"}\n`);
|
|
53
|
+
}
|
|
48
54
|
if (profileState.lastError) {
|
|
49
55
|
process.stdout.write(`Último erro: ${profileState.lastError}\n`);
|
|
50
56
|
}
|
package/dist/commands/task.js
CHANGED
|
@@ -157,6 +157,12 @@ function printObservability(payload) {
|
|
|
157
157
|
process.stdout.write(`Delegadas: ${String(payload.delegatedTasks ?? 0)}\n`);
|
|
158
158
|
process.stdout.write(`Success rate: ${String(payload.successRate ?? 0)}\n`);
|
|
159
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
|
+
}
|
|
160
166
|
async function runTaskList(cliVersion, args) {
|
|
161
167
|
const { parsed, apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
162
168
|
const tasks = await apiClient.listTasks(freshSession.accessToken, workspace);
|
|
@@ -433,6 +439,40 @@ async function runTaskObservability(cliVersion, args) {
|
|
|
433
439
|
}
|
|
434
440
|
return 0;
|
|
435
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
|
+
}
|
|
436
476
|
async function runTaskHealth(cliVersion, args) {
|
|
437
477
|
const { apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
|
|
438
478
|
const parsed = parseArgs(args);
|
|
@@ -465,7 +505,7 @@ async function runTaskApproval(cliVersion, args, approved) {
|
|
|
465
505
|
}
|
|
466
506
|
export async function runTaskCommand(cliVersion, args) {
|
|
467
507
|
const parsed = parseArgs(args);
|
|
468
|
-
const subcommand = requirePositional(parsed, 0, "Uso: vp task <run|templates|list|inspect|claim|execute|daemon|approve|reject|delegate|step-update|handoff|observability|health> [...]");
|
|
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> [...]");
|
|
469
509
|
switch (subcommand) {
|
|
470
510
|
case "run":
|
|
471
511
|
return runTaskRun(cliVersion, args.slice(1));
|
|
@@ -475,6 +515,8 @@ export async function runTaskCommand(cliVersion, args) {
|
|
|
475
515
|
return runTaskList(cliVersion, args.slice(1));
|
|
476
516
|
case "inspect":
|
|
477
517
|
return runTaskInspect(cliVersion, args.slice(1));
|
|
518
|
+
case "watch":
|
|
519
|
+
return runTaskWatch(cliVersion, args.slice(1));
|
|
478
520
|
case "claim":
|
|
479
521
|
return runTaskClaim(cliVersion, args.slice(1));
|
|
480
522
|
case "execute":
|
|
@@ -496,7 +538,7 @@ export async function runTaskCommand(cliVersion, args) {
|
|
|
496
538
|
case "health":
|
|
497
539
|
return runTaskHealth(cliVersion, args.slice(1));
|
|
498
540
|
default:
|
|
499
|
-
throw new ValidationError("Uso: vp task <run|templates|list|inspect|claim|execute|daemon|approve|reject|delegate|step-update|handoff|observability|health> [...]");
|
|
541
|
+
throw new ValidationError("Uso: vp task <run|templates|list|inspect|watch|claim|execute|daemon|approve|reject|delegate|step-update|handoff|observability|health> [...]");
|
|
500
542
|
}
|
|
501
543
|
}
|
|
502
544
|
//# sourceMappingURL=task.js.map
|
|
@@ -39,6 +39,20 @@ function printPolicyRule(rule) {
|
|
|
39
39
|
process.stdout.write(`Required capabilities: ${rule.conditions.requiredCapabilities.join(", ")}\n`);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
function printWebhook(hook) {
|
|
43
|
+
process.stdout.write(`Webhook: ${hook.id}\n`);
|
|
44
|
+
process.stdout.write(`Nome: ${hook.name}\n`);
|
|
45
|
+
process.stdout.write(`URL: ${hook.targetUrl}\n`);
|
|
46
|
+
process.stdout.write(`Enabled: ${hook.enabled ? "true" : "false"}\n`);
|
|
47
|
+
process.stdout.write(`Secret: ${hook.secretPreview}\n`);
|
|
48
|
+
process.stdout.write(`Eventos: ${hook.events.join(", ")}\n`);
|
|
49
|
+
if (hook.lastDeliveryStatus) {
|
|
50
|
+
process.stdout.write(`Última entrega: ${hook.lastDeliveryStatus}${hook.lastDeliveryAt ? ` em ${hook.lastDeliveryAt}` : ""}\n`);
|
|
51
|
+
}
|
|
52
|
+
if (hook.lastError) {
|
|
53
|
+
process.stdout.write(`Erro: ${hook.lastError}\n`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
42
56
|
function resolvePolicyConditions(parsed) {
|
|
43
57
|
const archetypes = getStringListOption(parsed, "archetypes");
|
|
44
58
|
const requiredCapabilities = getStringListOption(parsed, "required-capabilities");
|
|
@@ -155,6 +169,54 @@ async function runWorkspacePolicyCommand(args) {
|
|
|
155
169
|
}
|
|
156
170
|
throw new ValidationError("Subcomando de workspace policy não suportado.");
|
|
157
171
|
}
|
|
172
|
+
async function runWorkspaceWebhookCommand(args) {
|
|
173
|
+
const parsed = parseArgs(args);
|
|
174
|
+
const [subcommand] = parsed.positionals.slice(1);
|
|
175
|
+
const { session, apiClient, workspace } = await resolveWorkspaceApiContext();
|
|
176
|
+
if (!subcommand || subcommand === "list") {
|
|
177
|
+
const hooks = await apiClient.listWorkspaceWebhooks(session.accessToken, workspace);
|
|
178
|
+
if (printJsonIfRequested(parsed, hooks)) {
|
|
179
|
+
return 0;
|
|
180
|
+
}
|
|
181
|
+
if (hooks.length === 0) {
|
|
182
|
+
process.stdout.write("VectorPlane: nenhum webhook encontrado.\n");
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
for (const hook of hooks) {
|
|
186
|
+
process.stdout.write(`${hook.id} | ${hook.enabled ? "enabled" : "disabled"} | ${hook.name} | ${hook.targetUrl}\n`);
|
|
187
|
+
}
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
if (subcommand === "create") {
|
|
191
|
+
const name = getStringOption(parsed, "name")?.trim();
|
|
192
|
+
const targetUrl = getStringOption(parsed, "url")?.trim();
|
|
193
|
+
const secret = getStringOption(parsed, "secret")?.trim();
|
|
194
|
+
const events = getStringListOption(parsed, "events");
|
|
195
|
+
if (!name || !targetUrl || !secret || events.length === 0) {
|
|
196
|
+
throw new ValidationError("Informe `--name`, `--url`, `--secret` e `--events`.");
|
|
197
|
+
}
|
|
198
|
+
const hook = await apiClient.createWorkspaceWebhook(session.accessToken, workspace, {
|
|
199
|
+
name,
|
|
200
|
+
targetUrl,
|
|
201
|
+
secret,
|
|
202
|
+
events,
|
|
203
|
+
enabled: parsed.options.enabled === undefined ? true : getBooleanOption(parsed, "enabled"),
|
|
204
|
+
});
|
|
205
|
+
if (!printJsonIfRequested(parsed, hook)) {
|
|
206
|
+
printWebhook(hook);
|
|
207
|
+
}
|
|
208
|
+
return 0;
|
|
209
|
+
}
|
|
210
|
+
if (subcommand === "delete") {
|
|
211
|
+
const webhookId = requirePositional(parsed, 2, "Informe o identificador do webhook.");
|
|
212
|
+
const result = await apiClient.deleteWorkspaceWebhook(session.accessToken, workspace, webhookId);
|
|
213
|
+
if (!printJsonIfRequested(parsed, result)) {
|
|
214
|
+
process.stdout.write(`Webhook removido: ${result.webhookId}\n`);
|
|
215
|
+
}
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
throw new ValidationError("Subcomando de workspace webhook não suportado.");
|
|
219
|
+
}
|
|
158
220
|
export async function runWorkspaceCommand(args) {
|
|
159
221
|
const parsed = parseArgs(args);
|
|
160
222
|
const [subcommand] = parsed.positionals;
|
|
@@ -214,6 +276,9 @@ export async function runWorkspaceCommand(args) {
|
|
|
214
276
|
if (subcommand === "policy") {
|
|
215
277
|
return runWorkspacePolicyCommand(args);
|
|
216
278
|
}
|
|
279
|
+
if (subcommand === "webhook") {
|
|
280
|
+
return runWorkspaceWebhookCommand(args);
|
|
281
|
+
}
|
|
217
282
|
throw new ValidationError("Subcomando de workspace não suportado.");
|
|
218
283
|
}
|
|
219
284
|
//# sourceMappingURL=workspace.js.map
|
package/dist/core/api.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Logger } from "./logger.js";
|
|
2
2
|
import type { CurrentUserResponse, AuthCodeExchangeRequest, AuthTokenExchangeResponse, CreateLoginAttemptRequest, CreateLoginAttemptResponse, LoginAttemptStatusResponse, RefreshTokenRequest } from "../types/auth.js";
|
|
3
|
-
import type { AgentCheckoutRequest, AgentHeartbeatRequest, AgentSessionRequest, AgentSessionResponse, EventRequest, MemoryDraftCreateRequest, MemoryDraftRecord, MemoryDraftStatus, MemorySearchRecord, ClaimableTaskStepRecord, ClaimedTaskStepResponse, TaskCreateRequest, TaskHandoffRecord, TaskObservabilityRecord, TaskRecord, ResolveWorkspaceRequest, ResolveWorkspaceResponse, SyncRequest, SyncResponse, WorkspaceAgentSetup, WorkspacePolicyRuleRecord, AgentRegistryRecord, TaskTemplateRecord } from "../types/api.js";
|
|
3
|
+
import type { AgentCheckoutRequest, AgentHeartbeatRequest, AgentSessionRequest, AgentSessionResponse, EventRequest, MemoryDraftCreateRequest, MemoryDraftRecord, MemoryDraftStatus, MemorySearchRecord, ClaimableTaskStepRecord, ClaimedTaskStepResponse, TaskCreateRequest, TaskHandoffRecord, TaskObservabilityRecord, TaskRecord, ResolveWorkspaceRequest, ResolveWorkspaceResponse, SyncRequest, SyncResponse, WorkspaceAgentSetup, WorkspaceSnapshotDiffRecord, WorkspacePolicyRuleRecord, WorkspaceWebhookRecord, AgentRegistryRecord, TaskTemplateRecord, WorkspaceStreamEventRecord } from "../types/api.js";
|
|
4
4
|
export declare class VectorPlaneApiClient {
|
|
5
5
|
private readonly apiBaseUrl;
|
|
6
6
|
private readonly timeoutMs;
|
|
@@ -20,6 +20,10 @@ export declare class VectorPlaneApiClient {
|
|
|
20
20
|
resolveWorkspaceByRepo(accessToken: string, payload: ResolveWorkspaceRequest): Promise<ResolveWorkspaceResponse>;
|
|
21
21
|
getWorkspaceContext(accessToken: string, workspaceId: string): Promise<Record<string, unknown>>;
|
|
22
22
|
getWorkspaceSnapshot(accessToken: string, workspaceId: string): Promise<Record<string, unknown>>;
|
|
23
|
+
getWorkspaceSnapshotDiff(accessToken: string, workspaceId: string, params: {
|
|
24
|
+
from?: string;
|
|
25
|
+
to?: string;
|
|
26
|
+
}): Promise<WorkspaceSnapshotDiffRecord>;
|
|
23
27
|
getWorkspaceDeliveryContext(accessToken: string, workspaceId: string): Promise<Record<string, unknown>>;
|
|
24
28
|
getWorkspaceDeliveryContextSemantic(accessToken: string, workspaceId: string, params: {
|
|
25
29
|
query: string;
|
|
@@ -49,6 +53,18 @@ export declare class VectorPlaneApiClient {
|
|
|
49
53
|
approvalRole?: string | null;
|
|
50
54
|
enabled?: boolean;
|
|
51
55
|
}): Promise<WorkspacePolicyRuleRecord>;
|
|
56
|
+
listWorkspaceWebhooks(accessToken: string, workspaceRef: string): Promise<WorkspaceWebhookRecord[]>;
|
|
57
|
+
createWorkspaceWebhook(accessToken: string, workspaceRef: string, payload: {
|
|
58
|
+
name: string;
|
|
59
|
+
targetUrl: string;
|
|
60
|
+
secret: string;
|
|
61
|
+
events: WorkspaceWebhookRecord["events"];
|
|
62
|
+
enabled?: boolean;
|
|
63
|
+
}): Promise<WorkspaceWebhookRecord>;
|
|
64
|
+
deleteWorkspaceWebhook(accessToken: string, workspaceRef: string, webhookId: string): Promise<{
|
|
65
|
+
deleted: boolean;
|
|
66
|
+
webhookId: string;
|
|
67
|
+
}>;
|
|
52
68
|
listMemoryDrafts(accessToken: string, workspaceRef: string, status?: MemoryDraftStatus): Promise<MemoryDraftRecord[]>;
|
|
53
69
|
createMemoryDraft(accessToken: string, workspaceRef: string, payload: MemoryDraftCreateRequest): Promise<MemoryDraftRecord>;
|
|
54
70
|
searchWorkspaceMemory(accessToken: string, workspaceRef: string, params: {
|
|
@@ -119,4 +135,10 @@ export declare class VectorPlaneApiClient {
|
|
|
119
135
|
checkOutAgent(accessToken: string, payload: AgentCheckoutRequest): Promise<AgentSessionResponse>;
|
|
120
136
|
getHealth(): Promise<Record<string, unknown>>;
|
|
121
137
|
getReady(): Promise<Record<string, unknown>>;
|
|
138
|
+
streamWorkspaceEvents(accessToken: string, workspaceRef: string, params: {
|
|
139
|
+
taskId?: string;
|
|
140
|
+
types?: string[];
|
|
141
|
+
signal?: AbortSignal;
|
|
142
|
+
onEvent: (event: WorkspaceStreamEventRecord) => void;
|
|
143
|
+
}): Promise<void>;
|
|
122
144
|
}
|