@vectorplane/ctrl-cli 0.1.13 → 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 +56 -100
- 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/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 +3 -3
- 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,123 +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 run --template code-review --title "Review auth" --intention "Revisar risco e cobertura do fluxo de auth"
|
|
24
26
|
vp task watch <task-id>
|
|
25
|
-
vp task claim --capability qa.test
|
|
26
|
-
vp task execute <task-id> --step <step-id>
|
|
27
|
-
vp task daemon --capability qa.test
|
|
28
27
|
vp context --search "decisões sobre autenticação"
|
|
29
|
-
vp context --delivery --search "fluxo de login" --type decisions
|
|
30
|
-
vp context --diff --from latest-1 --to latest
|
|
31
28
|
vp workspace webhook list
|
|
32
|
-
vp
|
|
33
|
-
vp context --workspace <workspace> --delivery
|
|
34
|
-
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"
|
|
35
|
-
vp draft create --type progress --title "Entrega concluída" --content "Resumo da mudança"
|
|
29
|
+
vp session check-in --workspace <workspace> --agent codex --type codex --client openai-codex
|
|
36
30
|
```
|
|
37
31
|
|
|
38
|
-
## Comandos
|
|
39
|
-
|
|
40
|
-
### `vp init`
|
|
41
|
-
|
|
42
|
-
- autentica localmente se ainda não houver sessão
|
|
43
|
-
- 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
|
|
44
|
-
- detecta o agente local automaticamente, pede confirmação se houver ambiguidade e aceita `--agent`
|
|
45
|
-
- baixa o template oficial do backend
|
|
46
|
-
- grava o arquivo local e adiciona a entrada no `.gitignore`
|
|
47
|
-
- aceita `--force` para sobrescrever template existente
|
|
48
|
-
- 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
|
|
49
33
|
|
|
50
34
|
### `vp login`
|
|
51
35
|
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
- em ambientes remotos ou isolados, o loopback local pode exigir um fluxo alternativo
|
|
57
|
-
- 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
|
|
58
40
|
|
|
59
41
|
### `vp logout`
|
|
60
42
|
|
|
61
|
-
-
|
|
62
|
-
-
|
|
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`
|
|
63
48
|
|
|
64
|
-
|
|
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
|
|
65
52
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
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>`
|
|
69
58
|
|
|
70
59
|
### `vp status`
|
|
71
60
|
|
|
72
|
-
- mostra
|
|
73
|
-
- exibe
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
- cria drafts editoriais ligados ao workspace atual
|
|
98
|
-
- lista drafts existentes para conferência rápida
|
|
99
|
-
- suporta `ui`, `ux`, `project_skeleton`, `template_engineering`, `patterns`, `progress`, `decisions` e `architecture`
|
|
100
|
-
- aceita `--no-impact` para registrar explicitamente quando uma lane não foi afetada
|
|
101
|
-
|
|
102
|
-
### `vp session`
|
|
103
|
-
|
|
104
|
-
- suporta `check-in`, `heartbeat` e `check-out`
|
|
105
|
-
- permite declarar `feature`, `task`, `component`, `role`, `owning-path`, `need`, `provide` e `status`
|
|
106
|
-
- deve ser usado em conjunto com `vp context --delivery` para reduzir conflito entre agentes ativos
|
|
107
|
-
|
|
108
|
-
### Comandos adicionais já implementados
|
|
109
|
-
|
|
110
|
-
- `vp whoami`
|
|
111
|
-
- mostra a identidade autenticada
|
|
112
|
-
- `vp doctor`
|
|
113
|
-
- executa verificações locais e de conectividade
|
|
114
|
-
- `vp config`
|
|
115
|
-
- gerencia perfis e configuração local
|
|
116
|
-
- inclui `vp config privacy` e `vp config policy`
|
|
117
|
-
- `vp workspace`
|
|
118
|
-
- gerencia o workspace atual
|
|
119
|
-
- suporta `vp workspace webhook list|create|delete` para integrações externas por workspace
|
|
120
|
-
- `vp session`
|
|
121
|
-
- gerencia sessões de agente
|
|
122
|
-
- `vp context`
|
|
123
|
-
- carrega contexto remoto do workspace
|
|
124
|
-
- suporta `--search "QUERY"` para busca semântica na memória
|
|
125
|
-
- com `--delivery --search`, retorna delivery context com fragmentos semânticos relevantes
|
|
126
|
-
- suporta `--diff --from latest-1 --to latest` para comparar snapshots do workspace
|
|
127
|
-
- `vp bootstrap`
|
|
128
|
-
- obtém instruções de setup do workspace
|
|
129
|
-
- `vp event send`
|
|
130
|
-
- envia eventos operacionais
|
|
131
|
-
- `vp draft`
|
|
132
|
-
- envia e consulta drafts editoriais do workspace
|
|
133
|
-
- `vp task`
|
|
134
|
-
- 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`
|
|
135
85
|
|
|
136
86
|
## Persistência local
|
|
137
87
|
|
|
@@ -146,8 +96,14 @@ vp draft create --type progress --title "Entrega concluída" --content "Resumo d
|
|
|
146
96
|
## Segurança
|
|
147
97
|
|
|
148
98
|
- tokens nunca são impressos no terminal
|
|
149
|
-
-
|
|
150
|
-
- o callback valida o `state`
|
|
99
|
+
- logs mascaram chaves sensíveis e ids de sessão
|
|
151
100
|
- a sessão usa secure storage do sistema quando disponível
|
|
152
|
-
- existe fallback para arquivo local apenas quando permitido
|
|
101
|
+
- existe fallback para arquivo local apenas quando permitido pela policy
|
|
153
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)
|
|
@@ -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/core/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CliConfig, CliProfileConfig } from "../types/config.js";
|
|
1
|
+
import type { CliConfig, CliLoginMode, CliProfileConfig } from "../types/config.js";
|
|
2
2
|
import type { AuthSession } from "../types/auth.js";
|
|
3
3
|
import type { DeviceIdentity, MachineContext, RuntimeContext } from "../types/machine.js";
|
|
4
4
|
import { VectorPlaneApiClient } from "./api.js";
|
|
@@ -13,5 +13,5 @@ export declare function runLoginFlow(params: {
|
|
|
13
13
|
device: DeviceIdentity;
|
|
14
14
|
apiClient: VectorPlaneApiClient;
|
|
15
15
|
logger: Logger;
|
|
16
|
-
|
|
16
|
+
loginMode: CliLoginMode;
|
|
17
17
|
}): Promise<AuthSession>;
|
package/dist/core/auth.js
CHANGED
|
@@ -25,48 +25,118 @@ export async function openBrowser(url) {
|
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
+
function printLoginInstructions(loginMode, loginUrl, logger) {
|
|
29
|
+
if (loginMode === "browser") {
|
|
30
|
+
logger.info("abrindo navegador para autenticação...");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (loginMode === "manual") {
|
|
34
|
+
logger.info("login manual habilitado. abra a URL abaixo no navegador:");
|
|
35
|
+
process.stdout.write(`${loginUrl}\n`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
logger.info("login device-like habilitado. use a URL abaixo em qualquer navegador confiável:");
|
|
39
|
+
process.stdout.write(`${loginUrl}\n`);
|
|
40
|
+
}
|
|
41
|
+
async function waitForCallbackCode(callbackServer, timeoutMs) {
|
|
42
|
+
try {
|
|
43
|
+
const callback = await callbackServer.waitForCallback(timeoutMs);
|
|
44
|
+
return callback.code;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new AuthError("Falha no callback local do login do CLI.", error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function waitForAuthorizedAttempt(params) {
|
|
51
|
+
const startedAt = Date.now();
|
|
52
|
+
let announcedAuthorization = false;
|
|
53
|
+
while (Date.now() - startedAt < params.timeoutMs) {
|
|
54
|
+
let status;
|
|
55
|
+
try {
|
|
56
|
+
status = await params.apiClient.getLoginAttemptStatus(params.attemptId, params.pollToken);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw new AuthError("Falha ao acompanhar a autorização do login do CLI por polling.", error);
|
|
60
|
+
}
|
|
61
|
+
const code = pickAuthorizedCode(status);
|
|
62
|
+
if (code) {
|
|
63
|
+
return code;
|
|
64
|
+
}
|
|
65
|
+
if (status.status === "authorized" && !announcedAuthorization) {
|
|
66
|
+
announcedAuthorization = true;
|
|
67
|
+
params.logger.info("autorização recebida pela API. finalizando sessão...");
|
|
68
|
+
}
|
|
69
|
+
await delay(LOGIN_ATTEMPT_POLL_INTERVAL_MS);
|
|
70
|
+
}
|
|
71
|
+
throw new AuthError("Tempo esgotado aguardando a confirmação do login do CLI.");
|
|
72
|
+
}
|
|
73
|
+
function pickAuthorizedCode(status) {
|
|
74
|
+
if ((status.status === "authorized" || status.status === "completed") && typeof status.code === "string" && status.code.length > 0) {
|
|
75
|
+
return status.code;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function delay(ms) {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
setTimeout(resolve, ms);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
28
84
|
export async function runLoginFlow(params) {
|
|
29
85
|
const state = generateLoginState();
|
|
30
|
-
const
|
|
86
|
+
const useLoopback = params.loginMode !== "device";
|
|
87
|
+
const callbackServer = useLoopback
|
|
88
|
+
? await createCallbackServer(params.config.callbackHost, params.config.callbackPort, params.config.callbackPath, state)
|
|
89
|
+
: null;
|
|
31
90
|
try {
|
|
91
|
+
const redirectUri = callbackServer?.callbackUrl ?? `http://127.0.0.1/disabled-device-flow/${state}`;
|
|
32
92
|
const loginAttempt = await params.apiClient.createLoginAttempt({
|
|
33
|
-
redirectUri
|
|
93
|
+
redirectUri,
|
|
34
94
|
state,
|
|
35
95
|
client: CLI_CLIENT_ID,
|
|
36
96
|
});
|
|
37
97
|
const loginUrl = loginAttempt.loginUrl;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
process.stdout.write(`${loginUrl}\n`);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
params.logger.info("abrindo navegador para autenticação...");
|
|
98
|
+
printLoginInstructions(params.loginMode, loginUrl, params.logger);
|
|
99
|
+
if (params.loginMode === "browser") {
|
|
44
100
|
const opened = await openBrowser(loginUrl);
|
|
45
101
|
if (!opened) {
|
|
46
|
-
params.logger.warn("não foi possível abrir o navegador automaticamente.");
|
|
102
|
+
params.logger.warn("não foi possível abrir o navegador automaticamente. continue pela URL abaixo.");
|
|
47
103
|
process.stdout.write(`${loginUrl}\n`);
|
|
48
104
|
}
|
|
49
105
|
}
|
|
50
106
|
params.logger.info("aguardando confirmação...");
|
|
51
|
-
const code =
|
|
52
|
-
|
|
53
|
-
waitForAuthorizedAttempt({
|
|
107
|
+
const code = params.loginMode === "device"
|
|
108
|
+
? await waitForAuthorizedAttempt({
|
|
54
109
|
apiClient: params.apiClient,
|
|
55
110
|
attemptId: loginAttempt.attemptId,
|
|
56
111
|
pollToken: loginAttempt.pollToken,
|
|
57
112
|
timeoutMs: CALLBACK_TIMEOUT_MS,
|
|
58
113
|
logger: params.logger,
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
114
|
+
})
|
|
115
|
+
: await Promise.any([
|
|
116
|
+
waitForCallbackCode(callbackServer, CALLBACK_TIMEOUT_MS),
|
|
117
|
+
waitForAuthorizedAttempt({
|
|
118
|
+
apiClient: params.apiClient,
|
|
119
|
+
attemptId: loginAttempt.attemptId,
|
|
120
|
+
pollToken: loginAttempt.pollToken,
|
|
121
|
+
timeoutMs: CALLBACK_TIMEOUT_MS,
|
|
122
|
+
logger: params.logger,
|
|
123
|
+
}),
|
|
124
|
+
]).catch((error) => {
|
|
125
|
+
throw new AuthError("Não foi possível concluir o login do CLI.", error);
|
|
126
|
+
});
|
|
63
127
|
const payload = {
|
|
64
128
|
code,
|
|
65
129
|
client: CLI_CLIENT_ID,
|
|
66
130
|
device: params.machine,
|
|
67
131
|
runtime: params.runtime,
|
|
68
132
|
};
|
|
69
|
-
|
|
133
|
+
let tokenResponse;
|
|
134
|
+
try {
|
|
135
|
+
tokenResponse = await params.apiClient.exchangeToken(payload);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new AuthError("Falha na troca do código do CLI por sessão autenticada.", error);
|
|
139
|
+
}
|
|
70
140
|
if (!tokenResponse.access_token || !tokenResponse.refresh_token) {
|
|
71
141
|
throw new AuthError("A API retornou uma sessão inválida.");
|
|
72
142
|
}
|
|
@@ -80,38 +150,11 @@ export async function runLoginFlow(params) {
|
|
|
80
150
|
obtainedAt: new Date().toISOString(),
|
|
81
151
|
expiresAt: new Date(Date.now() + tokenResponse.expires_in * 1000).toISOString(),
|
|
82
152
|
lastRefreshAt: null,
|
|
153
|
+
deviceId: params.device.machineId,
|
|
83
154
|
};
|
|
84
155
|
}
|
|
85
156
|
finally {
|
|
86
|
-
await callbackServer
|
|
157
|
+
await callbackServer?.close();
|
|
87
158
|
}
|
|
88
159
|
}
|
|
89
|
-
async function waitForAuthorizedAttempt(params) {
|
|
90
|
-
const startedAt = Date.now();
|
|
91
|
-
let announcedAuthorization = false;
|
|
92
|
-
while (Date.now() - startedAt < params.timeoutMs) {
|
|
93
|
-
const status = await params.apiClient.getLoginAttemptStatus(params.attemptId, params.pollToken);
|
|
94
|
-
const code = pickAuthorizedCode(status);
|
|
95
|
-
if (code) {
|
|
96
|
-
return code;
|
|
97
|
-
}
|
|
98
|
-
if (status.status === "authorized" && !announcedAuthorization) {
|
|
99
|
-
announcedAuthorization = true;
|
|
100
|
-
params.logger.info("autorização recebida pela API. finalizando sessão...");
|
|
101
|
-
}
|
|
102
|
-
await delay(LOGIN_ATTEMPT_POLL_INTERVAL_MS);
|
|
103
|
-
}
|
|
104
|
-
throw new AuthError("Tempo esgotado aguardando a confirmação do login do CLI.");
|
|
105
|
-
}
|
|
106
|
-
function pickAuthorizedCode(status) {
|
|
107
|
-
if ((status.status === "authorized" || status.status === "completed") && typeof status.code === "string" && status.code.length > 0) {
|
|
108
|
-
return status.code;
|
|
109
|
-
}
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
function delay(ms) {
|
|
113
|
-
return new Promise((resolve) => {
|
|
114
|
-
setTimeout(resolve, ms);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
160
|
//# sourceMappingURL=auth.js.map
|
package/dist/core/constants.js
CHANGED
|
@@ -38,9 +38,14 @@ export const DEFAULT_CONFIG = {
|
|
|
38
38
|
requireSecureStorage: false,
|
|
39
39
|
disableFileSessionFallback: false,
|
|
40
40
|
requireManualLogin: false,
|
|
41
|
+
requireDeviceLogin: false,
|
|
41
42
|
blockPublicIpLookup: false,
|
|
42
43
|
blockMacAddressCollection: false,
|
|
43
44
|
blockUsernameCollection: false,
|
|
45
|
+
blockNetworkInterfaceCollection: false,
|
|
46
|
+
blockCurrentDirectoryCollection: false,
|
|
47
|
+
blockHomeDirectoryCollection: false,
|
|
48
|
+
blockShellCollection: false,
|
|
44
49
|
},
|
|
45
50
|
};
|
|
46
51
|
export const DEFAULT_PROFILE_STATE = {
|
|
@@ -53,8 +58,10 @@ export const DEFAULT_PROFILE_STATE = {
|
|
|
53
58
|
lastSnapshotPath: null,
|
|
54
59
|
lastSessionId: null,
|
|
55
60
|
lastLoginAt: null,
|
|
61
|
+
lastLoginMethod: null,
|
|
56
62
|
lastRefreshAt: null,
|
|
57
63
|
lastLogoutAt: null,
|
|
64
|
+
lastRemoteRevokeAt: null,
|
|
58
65
|
storageBackend: null,
|
|
59
66
|
storageProtected: null,
|
|
60
67
|
lastAuthFailure: null,
|
|
@@ -74,7 +81,20 @@ export const DEFAULT_WORKSPACE_BINDINGS = {
|
|
|
74
81
|
};
|
|
75
82
|
export const CALLBACK_TIMEOUT_MS = 120_000;
|
|
76
83
|
export const LOGIN_ATTEMPT_POLL_INTERVAL_MS = 1_000;
|
|
77
|
-
export const SENSITIVE_KEYS = [
|
|
84
|
+
export const SENSITIVE_KEYS = [
|
|
85
|
+
"token",
|
|
86
|
+
"authorization",
|
|
87
|
+
"refresh",
|
|
88
|
+
"secret",
|
|
89
|
+
"password",
|
|
90
|
+
"cookie",
|
|
91
|
+
"set-cookie",
|
|
92
|
+
"session",
|
|
93
|
+
"machineid",
|
|
94
|
+
"deviceid",
|
|
95
|
+
"polltoken",
|
|
96
|
+
"code",
|
|
97
|
+
];
|
|
78
98
|
export const SAFE_ENV_KEYS = [
|
|
79
99
|
"SHELL",
|
|
80
100
|
"TERM",
|
package/dist/core/machine.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type { CliConfig } from "../types/config.js";
|
|
1
|
+
import type { CliConfig, CliPolicyConfig, MachinePrivacyProfile } from "../types/config.js";
|
|
2
2
|
import type { DeviceIdentity, MachineContext, RuntimeContext } from "../types/machine.js";
|
|
3
|
+
export interface ExecutionEnvironmentAssessment {
|
|
4
|
+
risks: string[];
|
|
5
|
+
recommendedLoginMode: "browser" | "manual" | "device";
|
|
6
|
+
loopbackLikelySafe: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function assessExecutionEnvironment(): ExecutionEnvironmentAssessment;
|
|
9
|
+
export declare function describePrivacyEnvelope(profile: MachinePrivacyProfile, policy: CliPolicyConfig): Record<string, boolean>;
|
|
3
10
|
export declare function collectRuntimeContext(cliVersion: string, command: string, args: string[]): Promise<RuntimeContext>;
|
|
4
11
|
export declare function collectMachineContext(identity: DeviceIdentity, config: CliConfig): Promise<MachineContext>;
|
package/dist/core/machine.js
CHANGED
|
@@ -30,7 +30,7 @@ async function detectPublicIp(timeoutMs) {
|
|
|
30
30
|
clearTimeout(timer);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
function collectNetworkInterfaces() {
|
|
33
|
+
function collectNetworkInterfaces(includeMacAddress, includeAddresses) {
|
|
34
34
|
const interfaces = os.networkInterfaces();
|
|
35
35
|
const results = [];
|
|
36
36
|
for (const [name, entries] of Object.entries(interfaces)) {
|
|
@@ -38,14 +38,48 @@ function collectNetworkInterfaces() {
|
|
|
38
38
|
results.push({
|
|
39
39
|
name,
|
|
40
40
|
family: entry.family,
|
|
41
|
-
mac: entry.mac,
|
|
42
|
-
address: entry.address,
|
|
41
|
+
mac: includeMacAddress ? entry.mac : null,
|
|
42
|
+
address: includeAddresses ? entry.address : null,
|
|
43
43
|
internal: entry.internal,
|
|
44
|
-
cidr: entry.cidr ?? null,
|
|
44
|
+
cidr: includeAddresses ? (entry.cidr ?? null) : null,
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
return results.sort((left, right) => left.name.localeCompare(right.name) || left.address.localeCompare(right.address));
|
|
48
|
+
return results.sort((left, right) => left.name.localeCompare(right.name) || String(left.address).localeCompare(String(right.address)));
|
|
49
|
+
}
|
|
50
|
+
export function assessExecutionEnvironment() {
|
|
51
|
+
const risks = [];
|
|
52
|
+
if (process.env.WSL_DISTRO_NAME) {
|
|
53
|
+
risks.push("wsl");
|
|
54
|
+
}
|
|
55
|
+
if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT) {
|
|
56
|
+
risks.push("ssh");
|
|
57
|
+
}
|
|
58
|
+
if (process.env.CI === "true") {
|
|
59
|
+
risks.push("ci");
|
|
60
|
+
}
|
|
61
|
+
if (process.env.CONTAINER || process.env.DOCKER_CONTAINER || process.env.KUBERNETES_SERVICE_HOST) {
|
|
62
|
+
risks.push("container");
|
|
63
|
+
}
|
|
64
|
+
const loopbackLikelySafe = risks.length === 0;
|
|
65
|
+
const recommendedLoginMode = loopbackLikelySafe
|
|
66
|
+
? "browser"
|
|
67
|
+
: risks.includes("ssh") || risks.includes("container") || risks.includes("ci")
|
|
68
|
+
? "device"
|
|
69
|
+
: "manual";
|
|
70
|
+
return { risks, recommendedLoginMode, loopbackLikelySafe };
|
|
71
|
+
}
|
|
72
|
+
export function describePrivacyEnvelope(profile, policy) {
|
|
73
|
+
return {
|
|
74
|
+
username: !policy.blockUsernameCollection && profile !== "minimal",
|
|
75
|
+
publicIp: !policy.blockPublicIpLookup && profile !== "minimal",
|
|
76
|
+
networkInterfaces: !policy.blockNetworkInterfaceCollection && profile !== "minimal",
|
|
77
|
+
networkAddresses: profile === "enterprise" && !policy.blockNetworkInterfaceCollection,
|
|
78
|
+
macAddress: profile === "enterprise" && !policy.blockMacAddressCollection,
|
|
79
|
+
currentDirectory: !policy.blockCurrentDirectoryCollection && profile !== "minimal",
|
|
80
|
+
homeDirectory: !policy.blockHomeDirectoryCollection && profile === "enterprise",
|
|
81
|
+
shell: !policy.blockShellCollection && profile !== "minimal",
|
|
82
|
+
};
|
|
49
83
|
}
|
|
50
84
|
export async function collectRuntimeContext(cliVersion, command, args) {
|
|
51
85
|
return {
|
|
@@ -62,14 +96,11 @@ export async function collectRuntimeContext(cliVersion, command, args) {
|
|
|
62
96
|
export async function collectMachineContext(identity, config) {
|
|
63
97
|
const cpus = os.cpus();
|
|
64
98
|
const privacyProfile = config.profiles[config.activeProfile]?.machinePrivacyProfile ?? "standard";
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
mac: includeMacAddress ? entry.mac : null,
|
|
71
|
-
}));
|
|
72
|
-
const publicIp = collectPublicIp ? await detectPublicIp(config.publicIpLookupTimeoutMs) : null;
|
|
99
|
+
const envelope = describePrivacyEnvelope(privacyProfile, config.policy);
|
|
100
|
+
const publicIp = envelope.publicIp ? await detectPublicIp(config.publicIpLookupTimeoutMs) : null;
|
|
101
|
+
const networkInterfaces = envelope.networkInterfaces
|
|
102
|
+
? collectNetworkInterfaces(envelope.macAddress, envelope.networkAddresses)
|
|
103
|
+
: [];
|
|
73
104
|
return {
|
|
74
105
|
machineId: identity.machineId,
|
|
75
106
|
hostname: os.hostname(),
|
|
@@ -77,19 +108,19 @@ export async function collectMachineContext(identity, config) {
|
|
|
77
108
|
osRelease: os.release(),
|
|
78
109
|
architecture: os.arch(),
|
|
79
110
|
kernelVersion: os.version(),
|
|
80
|
-
username:
|
|
111
|
+
username: envelope.username ? os.userInfo().username : null,
|
|
81
112
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
82
113
|
locale: Intl.DateTimeFormat().resolvedOptions().locale,
|
|
83
|
-
shell: process.env.SHELL ?? null,
|
|
84
|
-
currentDirectory: process.cwd(),
|
|
85
|
-
homeDirectory: os.homedir(),
|
|
114
|
+
shell: envelope.shell ? (process.env.SHELL ?? null) : null,
|
|
115
|
+
currentDirectory: envelope.currentDirectory ? process.cwd() : null,
|
|
116
|
+
homeDirectory: envelope.homeDirectory ? os.homedir() : null,
|
|
86
117
|
cpuModel: cpus[0]?.model ?? null,
|
|
87
118
|
cpuCount: cpus.length,
|
|
88
119
|
loadAverage: os.loadavg(),
|
|
89
120
|
memoryTotal: os.totalmem(),
|
|
90
121
|
memoryFree: os.freemem(),
|
|
91
122
|
uptimeSeconds: os.uptime(),
|
|
92
|
-
networkInterfaces
|
|
123
|
+
networkInterfaces,
|
|
93
124
|
publicIp,
|
|
94
125
|
privacyProfile,
|
|
95
126
|
};
|
package/dist/index.js
CHANGED
|
@@ -31,14 +31,14 @@ function printHelp() {
|
|
|
31
31
|
process.stdout.write("\n");
|
|
32
32
|
process.stdout.write("Comandos principais:\n");
|
|
33
33
|
process.stdout.write(" init [--agent <tipo>] [--workspace <workspace>] [--force]\n");
|
|
34
|
-
process.stdout.write(" login [--no-browser|--manual]\n");
|
|
34
|
+
process.stdout.write(" login [--no-browser|--manual|--device]\n");
|
|
35
35
|
process.stdout.write(" logout\n");
|
|
36
36
|
process.stdout.write(" sync [--force]\n");
|
|
37
37
|
process.stdout.write(" status\n");
|
|
38
38
|
process.stdout.write(" task <run|templates|list|inspect|watch|claim|execute|daemon|approve|reject|delegate|step-update|handoff|observability|health>\n");
|
|
39
39
|
process.stdout.write(" registry <list|register|update|deactivate>\n");
|
|
40
40
|
process.stdout.write(" whoami\n");
|
|
41
|
-
process.stdout.write(" doctor\n");
|
|
41
|
+
process.stdout.write(" doctor [--policy]\n");
|
|
42
42
|
process.stdout.write(" draft <create|list>\n");
|
|
43
43
|
process.stdout.write(" config <profile|get|set>\n");
|
|
44
44
|
process.stdout.write(" workspace <current|use|resolve|clear|policy|webhook>\n");
|
|
@@ -73,7 +73,7 @@ export async function runCli(args) {
|
|
|
73
73
|
case "whoami":
|
|
74
74
|
return runWhoAmICommand(cliVersion);
|
|
75
75
|
case "doctor":
|
|
76
|
-
return runDoctorCommand(cliVersion);
|
|
76
|
+
return runDoctorCommand(cliVersion, rest);
|
|
77
77
|
case "draft":
|
|
78
78
|
return runDraftCommand(cliVersion, rest);
|
|
79
79
|
case "config":
|
package/dist/types/auth.d.ts
CHANGED
package/dist/types/config.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
export type VectorPlaneEnvironment = "production" | "preview" | "self-hosted";
|
|
2
2
|
export type MachinePrivacyProfile = "standard" | "minimal" | "enterprise";
|
|
3
3
|
export type SessionStorageBackend = "system" | "file";
|
|
4
|
+
export type CliLoginMode = "browser" | "manual" | "device";
|
|
4
5
|
export interface CliPolicyConfig {
|
|
5
6
|
requireSecureStorage: boolean;
|
|
6
7
|
disableFileSessionFallback: boolean;
|
|
7
8
|
requireManualLogin: boolean;
|
|
9
|
+
requireDeviceLogin: boolean;
|
|
8
10
|
blockPublicIpLookup: boolean;
|
|
9
11
|
blockMacAddressCollection: boolean;
|
|
10
12
|
blockUsernameCollection: boolean;
|
|
13
|
+
blockNetworkInterfaceCollection: boolean;
|
|
14
|
+
blockCurrentDirectoryCollection: boolean;
|
|
15
|
+
blockHomeDirectoryCollection: boolean;
|
|
16
|
+
blockShellCollection: boolean;
|
|
11
17
|
}
|
|
12
18
|
export interface CliProfileConfig {
|
|
13
19
|
name: string;
|
|
@@ -43,8 +49,10 @@ export interface CliProfileState {
|
|
|
43
49
|
lastSnapshotPath: string | null;
|
|
44
50
|
lastSessionId: string | null;
|
|
45
51
|
lastLoginAt: string | null;
|
|
52
|
+
lastLoginMethod: CliLoginMode | null;
|
|
46
53
|
lastRefreshAt: string | null;
|
|
47
54
|
lastLogoutAt: string | null;
|
|
55
|
+
lastRemoteRevokeAt: string | null;
|
|
48
56
|
storageBackend: SessionStorageBackend | null;
|
|
49
57
|
storageProtected: boolean | null;
|
|
50
58
|
lastAuthFailure: string | null;
|
package/dist/types/machine.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface NetworkInterfaceContext {
|
|
|
6
6
|
name: string;
|
|
7
7
|
family: string;
|
|
8
8
|
mac: string | null;
|
|
9
|
-
address: string;
|
|
9
|
+
address: string | null;
|
|
10
10
|
internal: boolean;
|
|
11
11
|
cidr: string | null;
|
|
12
12
|
}
|
|
@@ -36,8 +36,8 @@ export interface MachineContext {
|
|
|
36
36
|
timezone: string;
|
|
37
37
|
locale: string;
|
|
38
38
|
shell: string | null;
|
|
39
|
-
currentDirectory: string;
|
|
40
|
-
homeDirectory: string;
|
|
39
|
+
currentDirectory: string | null;
|
|
40
|
+
homeDirectory: string | null;
|
|
41
41
|
cpuModel: string | null;
|
|
42
42
|
cpuCount: number;
|
|
43
43
|
loadAverage: number[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vectorplane/ctrl-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Official VectorPlane CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -14,12 +14,17 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"bin/vp.js",
|
|
16
16
|
"dist/**/*.js",
|
|
17
|
+
"!dist/**/__tests__/**",
|
|
18
|
+
"!dist/**/*.test.js",
|
|
17
19
|
"dist/**/*.d.ts",
|
|
20
|
+
"!dist/**/__tests__/**",
|
|
21
|
+
"!dist/**/*.test.d.ts",
|
|
18
22
|
"README.md",
|
|
19
23
|
"LICENSE"
|
|
20
24
|
],
|
|
21
25
|
"publishConfig": {
|
|
22
|
-
"access": "public"
|
|
26
|
+
"access": "public",
|
|
27
|
+
"provenance": true
|
|
23
28
|
},
|
|
24
29
|
"repository": {
|
|
25
30
|
"type": "git",
|
|
@@ -43,7 +48,8 @@
|
|
|
43
48
|
"pack:dry-run": "npm pack --dry-run",
|
|
44
49
|
"release:check": "npm run check && npm run build && npm run pack:dry-run",
|
|
45
50
|
"prepublishOnly": "npm run release:check",
|
|
46
|
-
"start": "node ./dist/index.js"
|
|
51
|
+
"start": "node ./dist/index.js",
|
|
52
|
+
"test": "vitest run"
|
|
47
53
|
},
|
|
48
54
|
"keywords": [
|
|
49
55
|
"vectorplane",
|
|
@@ -56,6 +62,7 @@
|
|
|
56
62
|
"license": "MIT",
|
|
57
63
|
"devDependencies": {
|
|
58
64
|
"@types/node": "^24.5.2",
|
|
59
|
-
"typescript": "^5.9.2"
|
|
65
|
+
"typescript": "^5.9.2",
|
|
66
|
+
"vitest": "^4.1.3"
|
|
60
67
|
}
|
|
61
68
|
}
|