wendkeep 0.87.0 → 0.89.0
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/CHANGELOG.md +35 -0
- package/README.en.md +3 -2
- package/README.md +3 -2
- package/bin/wendkeep.mjs +1 -0
- package/docs/en/commands/ecosystem-bridges.md +172 -0
- package/docs/en/commands/observer-security.md +154 -0
- package/docs/en/commands/observer.md +30 -12
- package/docs/en/commands/verify.md +6 -0
- package/docs/pt-BR/commands/ecosystem-bridges.md +169 -0
- package/docs/pt-BR/commands/observer-security.md +154 -0
- package/docs/pt-BR/commands/observer.md +30 -12
- package/docs/pt-BR/commands/verify.md +6 -0
- package/hooks/observer-publish.mjs +3 -1
- package/package.json +2 -1
- package/packages/cli/src/index.mjs +10 -1
- package/packages/harness/src/sensors-core.mjs +49 -3
- package/packages/integrations/src/bridge-config.mjs +139 -0
- package/packages/integrations/src/bridge-contract.mjs +316 -0
- package/packages/integrations/src/bridge-diagnostics.mjs +45 -0
- package/packages/integrations/src/canonical-bridge-authority.mjs +32 -0
- package/packages/integrations/src/capabilities.mjs +34 -0
- package/packages/integrations/src/ecosystem-bridge.mjs +82 -0
- package/packages/integrations/src/index.mjs +6 -0
- package/packages/integrations/src/spec-kit-adapter.mjs +259 -0
- package/packages/integrations/src/superpowers-adapter.mjs +269 -0
- package/packages/mcp/src/executor.mjs +35 -2
- package/packages/observer/package.json +16 -0
- package/packages/observer/src/audit.mjs +1 -0
- package/packages/observer/src/authz.mjs +38 -0
- package/packages/observer/src/encryption.mjs +75 -0
- package/packages/observer/src/index.mjs +7 -0
- package/packages/observer/src/policy.mjs +305 -0
- package/packages/observer/src/purge.mjs +100 -0
- package/packages/observer/src/redaction.mjs +54 -0
- package/packages/observer/src/retention.mjs +39 -0
- package/packages/observer/src/token-registry.mjs +122 -0
- package/schema/ecosystem-bridge-artifact-manifest-v1.schema.json +30 -0
- package/schema/ecosystem-bridge-v1.schema.json +65 -0
- package/schema/observer/006-observer-security.sql +64 -0
- package/schema/observer-policy-v1.schema.json +63 -0
- package/schema/sync-event-v1.schema.json +10 -0
- package/schema/wendkeep.evidence-envelope-v2.schema.json +39 -0
- package/schema/wendkeep.sensors.schema.json +14 -0
- package/src/doctor.mjs +6 -1
- package/src/ecosystem-bridge-artifact-collector.mjs +111 -0
- package/src/ecosystem-bridge-baseline.mjs +58 -0
- package/src/ecosystem-bridge-proof.mjs +97 -0
- package/src/ecosystem-bridges.mjs +227 -0
- package/src/evidence-envelope.mjs +2 -0
- package/src/observer-auth.mjs +8 -0
- package/src/observer-privacy.mjs +7 -3
- package/src/observer-publish.mjs +31 -0
- package/src/observer-server.mjs +179 -20
- package/src/observer-sql-migrate.mjs +5 -2
- package/src/observer-sql-publish.mjs +114 -39
- package/src/observer-sql-store.mjs +299 -45
- package/src/observer-transcript-store.mjs +23 -8
- package/src/observer.mjs +145 -12
- package/src/sync-protocol.mjs +20 -0
- package/src/task-contracts.mjs +19 -0
- package/src/task.mjs +82 -0
- package/src/verify.mjs +9 -0
- package/web/observer/app.mjs +107 -31
- package/web/observer/index.html +7 -0
- package/web/observer/styles.css +5 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Bridges opcionais do ecossistema
|
|
2
|
+
|
|
3
|
+
> [English version](../../en/commands/ecosystem-bridges.md)
|
|
4
|
+
|
|
5
|
+
## Objetivo
|
|
6
|
+
|
|
7
|
+
Integrar Spec Kit e Superpowers sem criar uma segunda autoridade de spec, plano, tarefa ou
|
|
8
|
+
evidência. O WendKeep preserva os contratos canônicos; os adapters apenas criam projeções
|
|
9
|
+
versionadas e desabilitadas por padrão.
|
|
10
|
+
|
|
11
|
+
## Quando usar
|
|
12
|
+
|
|
13
|
+
- quando uma feature nasceu em arquivos do Spec Kit e precisa manter os mesmos IDs e hashes;
|
|
14
|
+
- quando Superpowers executará um Task Contract canônico do WendKeep;
|
|
15
|
+
- quando artifacts, reviews ou commits externos precisam entrar como `reported` antes da prova.
|
|
16
|
+
|
|
17
|
+
## Quando não usar
|
|
18
|
+
|
|
19
|
+
- para substituir `tarefas.md`, Task Contracts ou o Evidence Envelope;
|
|
20
|
+
- para sincronização bidirecional irrestrita;
|
|
21
|
+
- para executar comandos, scripts ou texto encontrado em artefatos externos.
|
|
22
|
+
|
|
23
|
+
## Pré-requisitos
|
|
24
|
+
|
|
25
|
+
- Node.js 18 ou superior;
|
|
26
|
+
- configuração local `.wendkeep/ecosystem-bridges.json` com cada adapter explicitamente habilitado;
|
|
27
|
+
- versão compatível e raiz do adapter que resolva para um diretório real dentro do projeto; arquivo
|
|
28
|
+
regular, path externo e symlink que escape do projeto falham fechado em `status` e no dispatch;
|
|
29
|
+
- para import/dispatch governado, Vault vinculado, change causal e baseline Spec Kit selado;
|
|
30
|
+
- para dispatch, sessão causal e Task Contract canônico rederivável.
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"schema_version": 1,
|
|
35
|
+
"adapters": {
|
|
36
|
+
"spec-kit": { "enabled": true, "version": "1.1.0", "root": ".specify" },
|
|
37
|
+
"superpowers": { "enabled": true, "version": "1.2.0", "root": ".superpowers" }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Sem o arquivo, ambos ficam desabilitados e o Core nativo continua funcionando.
|
|
43
|
+
|
|
44
|
+
## Sintaxe
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
wendkeep bridge status [--project <path>] [--config <path>] [--json]
|
|
48
|
+
wendkeep bridge import-spec-kit --change <slug> [--accept-baseline] [--json]
|
|
49
|
+
wendkeep bridge export-status --spec-projection <projection.json> [--task-contract <task.json>] [--input <artifacts.json>] [--json]
|
|
50
|
+
wendkeep bridge dispatch-superpowers --task-id <id> --change <slug> [--task-contract <task.json>] [--session <id>] --spec-projection <projection.json> [--json]
|
|
51
|
+
wendkeep bridge verify-artifacts --input <artifacts.json> --proofs <proofs.json> --change <slug> [--session <id>] [--json]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`import-spec-kit` lê Markdown sob `memory/` e `specs/`, classifica constitution/spec/plan/task,
|
|
55
|
+
preserva IDs e SHA-256, cria mappings explícitos `story|requirement → capability → change → task`
|
|
56
|
+
e nunca escreve na origem. IDs repetidos em arquivos diferentes bloqueiam a projeção.
|
|
57
|
+
O primeiro import exige `--accept-baseline` e ancora a projeção verde na change do Vault; imports
|
|
58
|
+
seguintes rederivam a origem e comparam path, kind, hash e mapping contra esse baseline imutável.
|
|
59
|
+
`dispatch-superpowers` contém somente o contexto estrutural
|
|
60
|
+
mínimo derivado do Task Contract; transcript, conteúdo privado e ownership externo não entram.
|
|
61
|
+
O dispatch rederiva o contrato e o active context do Vault/checkout; um JSON submetido é apenas
|
|
62
|
+
uma cópia para comparação e nunca valida o próprio `binding`. Com Spec Kit ativo, baseline e
|
|
63
|
+
`--spec-projection` são obrigatórios e a origem é reimportada antes do dispatch.
|
|
64
|
+
O contrato `spec-projection` pertence exclusivamente ao adapter `spec-kit`: versão incompatível,
|
|
65
|
+
kind fora do schema ou projeção re-selada por outro adapter é rejeitada antes de produzir `spec_refs`.
|
|
66
|
+
Uma decisão `ok: false` exige ao menos um diagnóstico bloqueante, e `ok: true` não pode coexistir
|
|
67
|
+
com diagnóstico bloqueante; qualquer incoerência bloqueia o dispatch sem expor referências.
|
|
68
|
+
`export-status` recalcula e valida `projection_id`, devolve apenas uma projeção `reported` e não
|
|
69
|
+
grava nos arquivos do Spec Kit.
|
|
70
|
+
|
|
71
|
+
## Opções e códigos de saída
|
|
72
|
+
|
|
73
|
+
| Opção | Efeito |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `--project <path>` | Seleciona a raiz do consumidor. |
|
|
76
|
+
| `--config <path>` | Substitui `.wendkeep/ecosystem-bridges.json`. |
|
|
77
|
+
| `--change <slug>` | Seleciona a change que guarda baseline e Evidence Envelope canônicos. |
|
|
78
|
+
| `--accept-baseline` | Ancora somente o primeiro baseline Spec Kit verde; não sobrescreve drift. |
|
|
79
|
+
| `--task-id <id>` | Seleciona a tarefa no contexto causal e rederiva o contrato canônico. |
|
|
80
|
+
| `--task-contract <path>` | Cópia opcional que deve coincidir com o contrato rederivado. |
|
|
81
|
+
| `--spec-projection <path>` | Liga referências Spec Kit ao dispatch sem copiar o conteúdo. |
|
|
82
|
+
| `--input` / `--proofs` | Classifica artifacts externos e suas provas Git/CI/Envelope. |
|
|
83
|
+
| `--json` | Emite o contrato tipado em uma linha JSON. |
|
|
84
|
+
|
|
85
|
+
- `0`: operação válida; adapters opcionais desabilitados também são saudáveis;
|
|
86
|
+
- `1`: adapter habilitado bloqueado, drift, incompatibilidade ou prova ausente;
|
|
87
|
+
- `2`: argumento, configuração ou arquivo de entrada inválido.
|
|
88
|
+
|
|
89
|
+
## Exemplos
|
|
90
|
+
|
|
91
|
+
Fluxo pequeno, sem adapters:
|
|
92
|
+
|
|
93
|
+
```powershell
|
|
94
|
+
node ./bin/wendkeep.mjs bridge status --json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Fluxo médio, Spec Kit somente leitura:
|
|
98
|
+
|
|
99
|
+
```powershell
|
|
100
|
+
node ./bin/wendkeep.mjs bridge import-spec-kit --change ecosystem-bridges --accept-baseline --json > spec-projection.json
|
|
101
|
+
node ./bin/wendkeep.mjs bridge dispatch-superpowers --task-id 3.1 --change ecosystem-bridges --session "$env:CODEX_THREAD_ID" --spec-projection spec-projection.json --json
|
|
102
|
+
node ./bin/wendkeep.mjs bridge export-status --spec-projection spec-projection.json --task-contract task-contract.json --json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Fluxo grande, ingestão de relato e prova externa:
|
|
106
|
+
|
|
107
|
+
```powershell
|
|
108
|
+
node ./bin/wendkeep.mjs verify --change ecosystem-bridges
|
|
109
|
+
node ./bin/wendkeep.mjs bridge verify-artifacts --input artifacts.json --proofs ci-proofs.json --change ecosystem-bridges --json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Antes de `verify`, versione no índice Git o artefato e `.wendkeep/bridge-artifacts.json`. O manifest
|
|
113
|
+
v1 liga cada item a `source`, `external_id`, `kind`, `path`, `sensor_id` e `task_id`. O sensor
|
|
114
|
+
correspondente em `wendkeep.sensors.json` declara `artifact_results` v1 com `external_id`, `path` e
|
|
115
|
+
`algorithm: sha256`; o runner calcula o digest dos bytes, inclusive binários, somente depois de uma
|
|
116
|
+
execução verde. O resultado explícito não contém conteúdo, transcript nem output tail do artefato.
|
|
117
|
+
O collector consulta primeiro o índice e então exige a mesma cópia presente na worktree; apagar ou
|
|
118
|
+
alterar somente a cópia de trabalho falha fechado, enquanto a ausência simultânea no índice e na
|
|
119
|
+
worktree continua significando que o bridge opcional não declarou artifacts.
|
|
120
|
+
|
|
121
|
+
Cada referência em `ci-proofs.json` usa apenas
|
|
122
|
+
`{"type":"evidence-envelope","external_id":"review-1"}`. Path, task, sensor, digests e blobs Git
|
|
123
|
+
são rederivados do manifest e do Envelope canônicos; `state`, SHA ou autoridade autodeclarados são
|
|
124
|
+
ignorados para promoção. `artifacts.json` continua sendo somente o relato externo a comparar.
|
|
125
|
+
|
|
126
|
+
O artifact começa como `reported`. JSON externo que apenas declare `state: verified` continua
|
|
127
|
+
`reported`. A promoção para `verified` exige, em conjunto: arquivo dentro do projeto e igual ao
|
|
128
|
+
blob do índice Git, sensor CI verde com `artifact_results.digest` explícito, Evidence Envelope v2
|
|
129
|
+
canônico e bound ao checkout, e entrada correspondente em `external_artifacts` do Envelope.
|
|
130
|
+
`output_sha256` não prova artefatos.
|
|
131
|
+
O resultado expõe uma proof selada ligada ao `evidence_envelope_id`, sem copiar transcript ou Vault.
|
|
132
|
+
|
|
133
|
+
## Resultado esperado
|
|
134
|
+
|
|
135
|
+
- Spec Kit permanece uma fonte externa somente leitura;
|
|
136
|
+
- plano, tarefa e evidência canônicos continuam pertencendo ao WendKeep;
|
|
137
|
+
- Superpowers recebe um dispatch mínimo sem poder reescrever o escopo;
|
|
138
|
+
- criação/reuso e finalização de worktree permanecem nos comandos `wendkeep worktree` derivados no dispatch;
|
|
139
|
+
- cleanup pós-merge usa `wendkeep worktree finish <slug> --pr <número-ou-url>`;
|
|
140
|
+
- drift e ownership concorrente bloqueiam antes da execução;
|
|
141
|
+
- remover ou desabilitar um adapter não degrada o Core.
|
|
142
|
+
|
|
143
|
+
## Erros comuns e diagnóstico
|
|
144
|
+
|
|
145
|
+
| Código | Diagnóstico |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `BRIDGE_ADAPTER_DISABLED` | Estado opcional normal; habilite explicitamente se necessário. |
|
|
148
|
+
| `BRIDGE_ADAPTER_MISSING` | Adapter habilitado, mas sua raiz não existe. |
|
|
149
|
+
| `BRIDGE_VERSION_INCOMPATIBLE` | Versão fora do compatibility range publicado. |
|
|
150
|
+
| `BRIDGE_OWNERSHIP_CONFLICT` | Ferramenta externa tentou possuir plano/tarefa/evidência. |
|
|
151
|
+
| `BRIDGE_SOURCE_DRIFT` | Hash mudou, plano ficou obsoleto ou uma referência apareceu/desapareceu. |
|
|
152
|
+
| `BRIDGE_SOURCE_ID_DUPLICATE` | O mesmo story/requirement ID apareceu em arquivos diferentes. |
|
|
153
|
+
| `BRIDGE_BASELINE_MISSING` | Spec Kit ativo sem baseline canônico ou projeção obrigatória. |
|
|
154
|
+
| `BRIDGE_BASELINE_STALE` | Origem/projeção divergiu do baseline selado no Vault. |
|
|
155
|
+
| `BRIDGE_PROJECTION_INVALID` | Conteúdo e `projection_id` não coincidem ou o schema está incompleto. |
|
|
156
|
+
| `BRIDGE_SCHEMA_INVALID` | Envelope runtime não cumpre o contrato publicado. |
|
|
157
|
+
| `BRIDGE_ARTIFACT_MANIFEST_UNTRACKED` | Manifest bridge ausente em apenas um lado ou diferente entre índice e worktree. |
|
|
158
|
+
| `BRIDGE_ARTIFACT_FORGED` | Path/bytes do artefato não correspondem ao arquivo versionado. |
|
|
159
|
+
| `BRIDGE_ARTIFACT_RESULT_MISSING` | Sensor verde não produziu o digest explícito ligado ao manifest. |
|
|
160
|
+
| `BRIDGE_PROOF_MISSING` | Relato externo ainda não possui prova independente vinculada. |
|
|
161
|
+
| `BRIDGE_PROOF_UNVERIFIED` | Prova autodeclarada foi mantida como `reported`. |
|
|
162
|
+
|
|
163
|
+
`wendkeep doctor` mostra a seção `[bridges]` sem fazer import, dispatch ou escrita.
|
|
164
|
+
|
|
165
|
+
## Próximos passos
|
|
166
|
+
|
|
167
|
+
Revise a projeção antes do dispatch, mantenha os arquivos gerados fora do controle canônico e
|
|
168
|
+
valide artifacts pelo CI ou Evidence Envelope. Consulte também [Changes e verificação](changes-and-verification.md)
|
|
169
|
+
e [Worktrees gerenciadas](worktrees.md).
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Segurança do Observer
|
|
2
|
+
|
|
3
|
+
**PT-BR** · [English](../../en/commands/observer-security.md)
|
|
4
|
+
|
|
5
|
+
## Objetivo
|
|
6
|
+
|
|
7
|
+
O Observer é um read model local ou de equipe, nunca uma nova autoridade sobre Vault, spec, memória
|
|
8
|
+
ou sync. O modelo de ameaça considera host remoto comprometido, token roubado, operador curioso,
|
|
9
|
+
banco/outbox copiados, payload adversarial e purge interrompido. Host/Origin continuam validados;
|
|
10
|
+
mutações e leituras sensíveis falham fechadas, inclusive no loopback.
|
|
11
|
+
|
|
12
|
+
| Classe | Padrão | Risco principal |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| documento | `metadata` | memória/decisões integrais |
|
|
15
|
+
| transcript | `metadata` | conversa e ferramentas |
|
|
16
|
+
| prompt/resposta | `redacted` | PII e segredos |
|
|
17
|
+
| uso | `aggregate` | custo e identidade operacional |
|
|
18
|
+
| audit/receipt | metadados mínimos | apagar a própria prova |
|
|
19
|
+
|
|
20
|
+
A policy v1 restringe por classe, `project_id`, glob de path e `entity_type`. Regras mais tardias
|
|
21
|
+
vencem apenas no projeto correspondente. Redaction cobre Bearer, credenciais em URL/connection
|
|
22
|
+
string, access keys, e-mail, telefone e regexes configuráveis seguras. O schema é
|
|
23
|
+
`schema/observer-policy-v1.schema.json`.
|
|
24
|
+
Em `transcript_capture: messages`, arrays, JSONL e o envelope canônico `{messages:[...]}` preservam
|
|
25
|
+
somente mensagens `user|assistant|system` com `role`/`content` string após redaction; campos extras,
|
|
26
|
+
tools e entradas malformadas são descartados ou falham fechados.
|
|
27
|
+
A policy explícita é a única autoridade de captura do publisher; `WENDKEEP_OBSERVER_CAPTURE_LEVEL`
|
|
28
|
+
é apenas compatibilidade traduzida para policy quando nenhum arquivo de policy foi fornecido e
|
|
29
|
+
nunca eleva nem suprime `none|metadata|messages|full` ou documentos `selected` explícitos.
|
|
30
|
+
Nos upserts de documento/transcript, `content_hash` sempre representa o conteúdo final após captura
|
|
31
|
+
e redaction; captura metadata/selected usa o SHA-256 do conteúdo vazio. Exclusões de documento
|
|
32
|
+
continuam efetivas mesmo com captura `none`, preservam path/revision/operação e nunca transportam
|
|
33
|
+
conteúdo ou hash obsoleto.
|
|
34
|
+
A redaction nunca reescreve campos validados de identidade estrutural, como IDs de projeto/evento/
|
|
35
|
+
entidade, paths lógicos, revisions ou operações. A privacidade do path é aplicada de modo fail-closed
|
|
36
|
+
pelas regras de captura por projeto/path, não pela renomeação da chave de storage por uma regra de
|
|
37
|
+
redaction de conteúdo.
|
|
38
|
+
O contrato estrutural por evento também preserva aliases snake/camel aceitos, chaves de documento/
|
|
39
|
+
sessão/agente/call/transcript/rollup, timestamps, roles, status, coverage, dimensões de modelo/preço,
|
|
40
|
+
workflow e proveniência de source. `title`, `summary`, `agent_name`, conteúdo, prompt/resposta e
|
|
41
|
+
metadata continuam como campos de display/conteúdo sujeitos a redaction.
|
|
42
|
+
Na publicação incremental, timestamps de turn ausentes ou vazios herdam o instante canônico do
|
|
43
|
+
lote, epoch numérico em milissegundos é normalizado para ISO 8601 e valor não vazio inválido falha
|
|
44
|
+
fechado antes da policy/store; evento e payload usam o mesmo instante.
|
|
45
|
+
|
|
46
|
+
## Quando usar
|
|
47
|
+
|
|
48
|
+
Use ao habilitar o Observer para dados reais, cadastrar ou revogar credenciais, restringir captura,
|
|
49
|
+
proteger SQLite/outbox, definir retenção ou eliminar dados com prova verificável.
|
|
50
|
+
|
|
51
|
+
## Quando não usar
|
|
52
|
+
|
|
53
|
+
Não use como KMS/secret manager corporativo, para publicar Vault/runtime, para substituir a
|
|
54
|
+
autoridade local ou para apagar manualmente tabelas e índices. Captura `full` continua opt-in e
|
|
55
|
+
sujeita à policy/redaction.
|
|
56
|
+
|
|
57
|
+
## Pré-requisitos
|
|
58
|
+
|
|
59
|
+
Use Node.js 22.13+, mantenha o bind no loopback e injete tokens/chaves somente por variáveis de
|
|
60
|
+
ambiente. O token de bootstrap é registrado somente pelo hash, exige projetos explícitos e
|
|
61
|
+
expiração finita; não é um admin wildcard fora do registry. Para Docker, defina também
|
|
62
|
+
`WENDKEEP_OBSERVER_BOOTSTRAP_PROJECTS`, `WENDKEEP_OBSERVER_BOOTSTRAP_EXPIRES_AT` e uma chave de
|
|
63
|
+
32 bytes em hex/base64 em `WENDKEEP_OBSERVER_ENCRYPTION_KEY`. O operador guarda a chave e receipts externos.
|
|
64
|
+
|
|
65
|
+
## Sintaxe
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx wendkeep observer serve --token <token> --bootstrap-projects <p1,p2> --bootstrap-expires-at <ISO> [--bootstrap-token-id <id>] [--require-loopback-auth] [--require-encryption]
|
|
69
|
+
npx wendkeep observer security token create --project-id <projeto> --role <role> --scopes <scopes> --token-env <env> --expires-at <ISO>
|
|
70
|
+
npx wendkeep observer security token rotate --project-id <projeto> --token-id <id> --token-env <env> --expires-at <ISO> [--new-token-id <id>]
|
|
71
|
+
npx wendkeep observer security token revoke --project-id <projeto> --token-id <id>
|
|
72
|
+
npx wendkeep observer security policy set --project-id <projeto> --file <policy.json>
|
|
73
|
+
npx wendkeep observer security policy show --project-id <projeto>
|
|
74
|
+
npx wendkeep observer security purge --project-id <projeto> --before <ISO> --classes <classes> [--dry-run]
|
|
75
|
+
npx wendkeep observer security retention run --project-id <projeto> [--dry-run] [--operation-id <id>]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Opções e códigos de saída
|
|
79
|
+
|
|
80
|
+
- `viewer` lê metadata/agregados; `auditor` pode receber scopes sensíveis; `publisher` ingere;
|
|
81
|
+
`admin` administra policy, purge e recovery. Role, scope e projeto precisam autorizar juntos.
|
|
82
|
+
- Tokens são persistidos somente como SHA-256; expiração, rotação e revogação valem sem restart.
|
|
83
|
+
- Após rotacionar o bootstrap, atualize token e token ID no ambiente; reiniciar nunca reativa a
|
|
84
|
+
credencial antiga revogada ou expirada.
|
|
85
|
+
- `--token-env` nomeia a variável com o segredo; o comando nunca imprime o valor.
|
|
86
|
+
- `--require-loopback-auth` protege toda a API; reads sensíveis exigem token mesmo sem a flag.
|
|
87
|
+
- `--require-encryption` falha se a chave externa estiver ausente ou inválida.
|
|
88
|
+
- `WENDKEEP_OBSERVER_REQUIRE_ENCRYPTION=1` aplica a mesma falha fechada a `status`, `security`,
|
|
89
|
+
`register`, `publish` e `reconcile`; com chave configurada, todo primeiro upgrade v5 usa apenas
|
|
90
|
+
`.bak.enc` + manifest antes de qualquer leitura/backfill.
|
|
91
|
+
- Exit `0` indica operação concluída; exit `1` indica configuração, autorização, policy, chave ou
|
|
92
|
+
operação inválida. O hook mantém exit `0` fail-open para o fluxo local, mas aborta antes de
|
|
93
|
+
persistir conteúdo inseguro.
|
|
94
|
+
|
|
95
|
+
O audit guarda capability, resultado, rota e horário, nunca Bearer, prompt, resposta ou payload.
|
|
96
|
+
|
|
97
|
+
## Exemplos
|
|
98
|
+
|
|
99
|
+
Recovery offline explícito e auditado:
|
|
100
|
+
|
|
101
|
+
```powershell
|
|
102
|
+
$env:OBSERVER_RECOVERY_TOKEN = '<segredo-forte-temporário>'
|
|
103
|
+
npx wendkeep observer security token create --data-dir C:\WendKeepObserver `
|
|
104
|
+
--project-id project-a --role admin --scopes '*' --token-env OBSERVER_RECOVERY_TOKEN `
|
|
105
|
+
--expires-at 2026-09-29T12:00:00Z --reason 'offline recovery' --json
|
|
106
|
+
npx wendkeep observer security token revoke --data-dir C:\WendKeepObserver `
|
|
107
|
+
--project-id project-a --token-id <id> --reason 'recovery complete' --json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Sempre faça dry-run antes do purge. O runner de retenção é explícito/idempotente (CLI ou
|
|
111
|
+
`POST /v1/projects/:id/security/retention`), sem timer oculto:
|
|
112
|
+
|
|
113
|
+
```powershell
|
|
114
|
+
npx wendkeep observer security purge --data-dir C:\WendKeepObserver `
|
|
115
|
+
--project-id project-a --before 2026-08-01T00:00:00Z `
|
|
116
|
+
--classes documents,calls,transcripts --dry-run --json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```powershell
|
|
120
|
+
npx wendkeep observer security retention run --data-dir C:\WendKeepObserver `
|
|
121
|
+
--project-id project-a --operation-id scheduled-2026-08-29 --dry-run --json
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Resultado esperado
|
|
125
|
+
|
|
126
|
+
TTL é independente para documentos, calls e transcripts. Contagens, remoção de projeções/FTS,
|
|
127
|
+
eventos e receipt usam a mesma transação; retry é idempotente e dado antigo tardio gera nova prova.
|
|
128
|
+
|
|
129
|
+
AES-256-GCM usa AAD por projeto/classe/registro/campo e `keyProvider` externo. O backfill v6 remove
|
|
130
|
+
plaintext e índices derivados antes de liberar leituras; chave errada falha sem revelar conteúdo.
|
|
131
|
+
A migration estrutural `006-observer-security.sql` cria backup, valida checksum, faz rollback e
|
|
132
|
+
permite retry. Em modo at-rest obrigatório, o backup é `.bak.enc`, tem manifest/key ID/permissão
|
|
133
|
+
restrita e restauração falha com chave errada; nenhum `.bak` plaintext permanece.
|
|
134
|
+
|
|
135
|
+
O hook aplica policy metadata/redacted por padrão; `WENDKEEP_OBSERVER_POLICY_FILE` seleciona policy
|
|
136
|
+
explícita. `WENDKEEP_OBSERVER_OUTBOX_KEY_ENV` nomeia a variável da chave da outbox e
|
|
137
|
+
`WENDKEEP_OBSERVER_OUTBOX_KEY_ID` identifica a chave. O Compose exige autenticação e criptografia.
|
|
138
|
+
O painel guarda Bearer somente em memória, exporta cópia sanitizada e expõe Segurança. MCP exige
|
|
139
|
+
scope para calls/busca integral. Sync leva apenas `policy_ref`, sem duplicar tokens ou autoridade.
|
|
140
|
+
|
|
141
|
+
## Erros comuns e diagnóstico
|
|
142
|
+
|
|
143
|
+
- `observer_token_missing|expired|revoked`: crie/rotacione um token escopado ou faça recovery offline.
|
|
144
|
+
- `observer_project_forbidden|role_forbidden|scope_forbidden`: confira a interseção projeto/role/scope.
|
|
145
|
+
- `observer_encryption_key_unavailable|observer_decryption_failed`: confira key ID e material externo;
|
|
146
|
+
nunca enfraqueça o modo obrigatório.
|
|
147
|
+
- `observer_policy_invalid`: valide campos/captures e remova regex inválida ou explosiva.
|
|
148
|
+
- Falha de migration v6: preserve `.pre-006-*.bak.enc` e seu manifest, corrija a causa e repita.
|
|
149
|
+
|
|
150
|
+
## Próximos passos
|
|
151
|
+
|
|
152
|
+
Leia [Observer local](observer.md), faça um dry-run de retenção, valide token revogado/expirado e
|
|
153
|
+
guarde o receipt fora do banco quando precisar de prova externa. Nunca publique banco, backup,
|
|
154
|
+
outbox, chave, token ou `/data`.
|
|
@@ -26,7 +26,9 @@ pelos hooks e pelo WendKeep local.
|
|
|
26
26
|
|
|
27
27
|
Tenha Node.js 22.13 ou mais recente para executar o Observer SQL. O Keep Core e os demais comandos
|
|
28
28
|
continuam compatíveis com Node.js 18 ou mais recente. Registre explicitamente cada projeto e defina
|
|
29
|
-
`WENDKEEP_OBSERVER_TOKEN
|
|
29
|
+
`WENDKEEP_OBSERVER_TOKEN`. Toda mutação e toda leitura de conteúdo sensível exigem Bearer, inclusive
|
|
30
|
+
no loopback; metadados e agregados podem permanecer abertos localmente quando
|
|
31
|
+
`--require-loopback-auth` não é usado. Veja [Segurança do Observer](observer-security.md).
|
|
30
32
|
|
|
31
33
|
## Sintaxe
|
|
32
34
|
|
|
@@ -36,7 +38,7 @@ npx wendkeep observer register --project <projeto> --vault <vault> --data-dir <d
|
|
|
36
38
|
npx wendkeep observer publish --project <projeto> --vault <vault> --data-dir <diretório>
|
|
37
39
|
npx wendkeep observer reconcile --project <projeto> --vault <vault> --data-dir <diretório> [--url http://127.0.0.1:8787]
|
|
38
40
|
npx wendkeep observer memory import --project <projeto> --vault <vault> --url http://127.0.0.1:8787 --token <token> --json
|
|
39
|
-
npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório> --token <token>
|
|
41
|
+
npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório> --token <token> --bootstrap-projects <p1,p2> --bootstrap-expires-at <ISO> [--require-loopback-auth] [--require-encryption]
|
|
40
42
|
```
|
|
41
43
|
|
|
42
44
|
## Opções e códigos de saída
|
|
@@ -46,7 +48,13 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
|
|
|
46
48
|
- `--project` e `--vault` identificam o projeto nos comandos `register`, `publish`, `reconcile` e `memory import`.
|
|
47
49
|
- `--host` aceita somente `127.0.0.1`, `localhost` ou `::1`; outros hosts são recusados antes do
|
|
48
50
|
listen.
|
|
49
|
-
- `--token` ou `WENDKEEP_OBSERVER_TOKEN`
|
|
51
|
+
- `--token` ou `WENDKEEP_OBSERVER_TOKEN` fornece o segredo de bootstrap hash-only; projetos
|
|
52
|
+
explícitos e expiração finita são obrigatórios, e toda mutação/leitura sensível passa pelo registry;
|
|
53
|
+
`--allow-non-loopback` falha sem token.
|
|
54
|
+
- `--require-loopback-auth` exige Bearer também para metadados e agregados locais e ativa a policy
|
|
55
|
+
segura do projeto na ingestão.
|
|
56
|
+
- `--require-encryption` exige `WENDKEEP_OBSERVER_ENCRYPTION_KEY` com 32 bytes em hex/base64; use
|
|
57
|
+
`WENDKEEP_OBSERVER_ENCRYPTION_KEY_ID` para identificar a chave externa.
|
|
50
58
|
- `WENDKEEP_OBSERVER_CAPTURE_LEVEL` aceita `metadata` (padrão, sem mensagens), `messages` ou
|
|
51
59
|
`full-transcript`. Caminhos locais absolutos nunca são publicados.
|
|
52
60
|
- Exit `0` indica sucesso; exit `1` indica falha de configuração ou operação; o hook publisher
|
|
@@ -57,7 +65,10 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
|
|
|
57
65
|
```powershell
|
|
58
66
|
npx wendkeep observer register --project C:\GitHub\WendKeep --vault C:\GitHub\WendKeep\.WendKeep-vault --data-dir C:\WendKeepObserver
|
|
59
67
|
$env:WENDKEEP_OBSERVER_TOKEN = '<token-local-forte>'
|
|
60
|
-
|
|
68
|
+
$env:WENDKEEP_OBSERVER_BOOTSTRAP_PROJECTS = 'project-a'
|
|
69
|
+
$env:WENDKEEP_OBSERVER_BOOTSTRAP_EXPIRES_AT = '2026-09-29T12:00:00Z'
|
|
70
|
+
$env:WENDKEEP_OBSERVER_ENCRYPTION_KEY = '<32-bytes-em-hex-ou-base64>'
|
|
71
|
+
npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver --token $env:WENDKEEP_OBSERVER_TOKEN --require-loopback-auth
|
|
61
72
|
$env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
|
|
62
73
|
```
|
|
63
74
|
|
|
@@ -67,15 +78,19 @@ Para Docker local:
|
|
|
67
78
|
docker compose -f docker/wendkeep-observer/compose.yaml up -d --build
|
|
68
79
|
```
|
|
69
80
|
|
|
81
|
+
O Compose exige token, allowlist/expiração do bootstrap e chave; inicia com autenticação integral
|
|
82
|
+
e criptografia obrigatória. A policy `encryption_required` recusa ingestão e outbox plaintext.
|
|
83
|
+
|
|
70
84
|
## Painel web local
|
|
71
85
|
|
|
72
86
|
Com o servidor em execução, abra [http://127.0.0.1:8787/](http://127.0.0.1:8787/) no navegador.
|
|
73
|
-
O painel é servido pelo mesmo processo
|
|
74
|
-
|
|
87
|
+
O painel é servido pelo mesmo processo. Informe o token no formulário local: ele fica somente na
|
|
88
|
+
memória da página, segue como Bearer nas consultas e é descartado ao recarregar. A porta fica presa
|
|
89
|
+
ao loopback do computador; não coloque o endereço em uma interface de rede.
|
|
75
90
|
|
|
76
91
|
O painel mostra a lista multi-projeto, versão, saúde, sessão mais recente, change ativa, contagem
|
|
77
92
|
de changes e data da última captura. Ao abrir um projeto, o workspace oferece Overview, Consumo,
|
|
78
|
-
Sessões, Memória, Changes e
|
|
93
|
+
Sessões, Memória, Changes, Sincronização e Segurança. A aba Consumo mostra custo total, tokens por categoria,
|
|
79
94
|
agentes principais, subagentes, provedores, modelos, tendência diária, cobertura histórica e
|
|
80
95
|
chamadas conforme o nível de captura escolhido. Os estados de carregamento, vazio, servidor
|
|
81
96
|
indisponível, conflito, modelo sem tarifa e dados desatualizados ficam visíveis, e a atualização
|
|
@@ -160,18 +175,21 @@ corte. As telas do Observer não concluem, arquivam, reparam ou promovem estado.
|
|
|
160
175
|
- `POST /v1/projects/:project_id/ingest` — lote idempotente de documentos, sessões, agentes, rollups,
|
|
161
176
|
chamadas e transcripts.
|
|
162
177
|
- `GET /v1/projects/:project_id/memory/tree` — árvore e metadados dos documentos.
|
|
163
|
-
- `GET /v1/projects/:project_id/memory/document?path=...` — conteúdo Markdown integral.
|
|
178
|
+
- `GET /v1/projects/:project_id/memory/document?path=...` — conteúdo Markdown integral; exige Bearer.
|
|
164
179
|
- `GET /v1/projects/:project_id/memory/search?q=...` — busca ranqueada por chunks, com trecho do
|
|
165
|
-
match e proveniência; usa fallback lexical quando FTS5 não está disponível.
|
|
180
|
+
match e proveniência; usa fallback lexical quando FTS5 não está disponível e exige Bearer.
|
|
166
181
|
- `GET /v1/projects/:project_id/sync` — modo, contagem, conflitos e último evento.
|
|
167
182
|
- `PUT /v1/projects/:project_id/sync` — compatibilidade de configuração; a autoridade continua SQL.
|
|
168
|
-
- `GET /v1/projects/:project_id/memory/export` — exportação read-only
|
|
183
|
+
- `GET /v1/projects/:project_id/memory/export` — exportação read-only sanitizada por padrão; exige Bearer.
|
|
169
184
|
- `POST /v1/projects/:project_id/memory/events` — ingestão idempotente em lote.
|
|
170
185
|
- `GET /v1/projects/:project_id/usage/summary` — totais filtráveis por período, change, sessão,
|
|
171
186
|
agente, provedor, modelo e papel.
|
|
172
187
|
- `GET /v1/projects/:project_id/usage/breakdown` — hierarquia de agentes, subagentes e modelos.
|
|
173
|
-
- `GET /v1/projects/:project_id/usage/calls` — chamadas individuais com prompt e resposta.
|
|
174
|
-
- `GET /v1/projects/:project_id/transcripts/:transcript_id` — transcript comprimido, validado por hash.
|
|
188
|
+
- `GET /v1/projects/:project_id/usage/calls` — chamadas individuais com prompt e resposta; exige Bearer.
|
|
189
|
+
- `GET /v1/projects/:project_id/transcripts/:transcript_id` — transcript comprimido, validado por hash; exige Bearer.
|
|
190
|
+
- `GET /v1/projects/:project_id/security` — policy, contagens de tokens e audit sanitizado; exige admin.
|
|
191
|
+
- `PUT /v1/projects/:project_id/security/policy` — atualiza a policy efetiva sem restart; exige admin.
|
|
192
|
+
- `POST /v1/projects/:project_id/security/purge` — dry-run/purge transacional com receipt; exige admin.
|
|
175
193
|
|
|
176
194
|
As rotas `/v1` rejeitam corpo transportado ou expandido acima do limite e validam projeto, caminho,
|
|
177
195
|
revisão, hash, idempotência e isolamento antes de gravar o conteúdo no SQLite. Para preservar uma
|
|
@@ -88,6 +88,12 @@ mesmo diretório e rename atômico. No deep, `verificacao.json` e `verdict.json`
|
|
|
88
88
|
`evidenceEnvelopeId` e `evidenceBinding` completo; o verificador independente deve preservar ambos
|
|
89
89
|
no verdict.
|
|
90
90
|
|
|
91
|
+
Para artefatos de bridge, `.wendkeep/bridge-artifacts.json` e os arquivos referenciados precisam
|
|
92
|
+
estar versionados e iguais ao índice Git. O sensor ligado à mesma task declara `artifact_results`
|
|
93
|
+
v1 em `wendkeep.sensors.json`; após GREEN, o runner calcula o SHA-256 diretamente dos bytes e grava
|
|
94
|
+
o resultado estruturado no sensor. `verify` cruza manifest, task, sensor, digest e blobs Git antes
|
|
95
|
+
de preencher `external_artifacts`; stdout, transcript e `output_tail` nunca são prova do arquivo.
|
|
96
|
+
|
|
91
97
|
O envelope carrega `tdd_attestations`, e `verificacao.json`, `tddAttestations`. Em GOVERN, uma tarefa marcada
|
|
92
98
|
`[tdd]` precisa de GREEN atual ou waiver explícito; em ASSURE, isso vale para comportamento
|
|
93
99
|
testável. Refactor/commit posterior ou mutante sobrevivente invalida o GREEN no Task Contract.
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
3
|
import { debugLog, readHookInput, resolveVault } from './obsidian-common.mjs';
|
|
4
|
-
import { publishObserverSnapshot } from '../src/observer-publish.mjs';
|
|
4
|
+
import { publishObserverSnapshot, resolveObserverPublisherSecurity } from '../src/observer-publish.mjs';
|
|
5
5
|
|
|
6
6
|
async function main() {
|
|
7
7
|
const input = readHookInput();
|
|
8
8
|
const resolved = resolveVault(input);
|
|
9
|
+
const publisherSecurity = resolveObserverPublisherSecurity();
|
|
9
10
|
const result = await publishObserverSnapshot({
|
|
10
11
|
vaultBase: resolved.base,
|
|
11
12
|
projectRoot: resolved.projectRoot,
|
|
12
13
|
input,
|
|
14
|
+
...publisherSecurity,
|
|
13
15
|
});
|
|
14
16
|
if (!result.ok && result.error) debugLog('Observer publish fail-open:', result.error);
|
|
15
17
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wendkeep",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.89.0",
|
|
4
4
|
"description": "Vault-first persistent memory for AI coding agents, with an optional profile-aware governance runtime: OFF, FLOW, GUIDE, GOVERN, or ASSURE. Local-first and agent-agnostic (Claude Code, Codex, Cursor…).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"scripts": {
|
|
47
47
|
"precheck": "node --check src/capabilities.mjs && node --check src/host-capabilities.mjs && node --check src/task-contracts.mjs && node --check src/task-leases.mjs && node --check src/task.mjs && node --check src/change.mjs && node --check src/archive-operation-lock.mjs && node --check src/worktree.mjs && node --check src/worktree-cleanup.mjs && node --check src/provenance-gate.mjs && node --check src/provenance-sources.mjs && node --check src/receipt-ledger.mjs && node --check src/evidence-envelope.mjs && node --check src/context.mjs && node --check src/active-context-health.mjs && node --check src/active-context-runtime.mjs && node --check hooks/active-context-store.mjs && node --check hooks/change-core.mjs && node --check hooks/brain-inject.mjs && node --check hooks/change-context.mjs && node --check hooks/session-stop.mjs && node --check packages/vault/src/worktree-metadata.mjs && node --check packages/vault/src/evidence-envelope.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/integrations/src/capabilities.mjs && node --check packages/pi/src/index.mjs && node --check src/sync-protocol.mjs && node --check src/sync-outbox.mjs && node --check src/sync-adapters.mjs && node --check src/sync-protocol-cli.mjs && node --check packages/mcp/src/sync.mjs && node --check src/portable.mjs && node --check src/tdd.mjs && node --check src/tdd-attestation.mjs && node --check src/tdd-attestation-store.mjs",
|
|
48
48
|
"check": "node --check scripts/validate-commit-range.mjs && node --check packages/commit/src/index.mjs && node --check packages/commit/src/cli.mjs && node --check packages/commit/src/git-runtime.mjs && node --check src/git-commit-hooks.mjs && node --check scripts/release.mjs && node --check scripts/release-plan.mjs && node --check scripts/release-provenance.mjs && node --check scripts/run-scope.mjs && node --check src/release-provenance.mjs && node --check bin/wendkeep.mjs && node --check packages/cli/src/index.mjs && node --check src/mcp.mjs && node --check src/init.mjs && node --check src/doctor.mjs && node --check src/active-context-health.mjs && node --check src/project-vault.mjs && node --check src/observer-auth.mjs && node --check src/observer-privacy.mjs && node --check src/observer-snapshot.mjs && node --check src/observer-store.mjs && node --check src/observer-memory.mjs && node --check src/observer-memory-publish.mjs && node --check src/observer-sql-store.mjs && node --check src/observer-sql-migrate.mjs && node --check src/observer-sql-publish.mjs && node --check src/observer-transcript-store.mjs && node --check src/observer-server.mjs && node --check src/observer.mjs && node --check src/observer-publish.mjs && node --check src/operating-profile.mjs && node --check src/profile.mjs && node --check src/flow.mjs && node --check src/work-kind.mjs && node --check src/delivery.mjs && node --check web/observer/app.mjs && node --check hooks/observer-publish.mjs && node --check hooks/evidence-context.mjs && node --check hooks/active-context-handoff-evidence.mjs && node --check hooks/evidence-recall.mjs && node --check hooks/memory-scope.mjs && node --check hooks/operating-profile-runtime.mjs && node --check hooks/operating-profile-task-store.mjs && node --check hooks/flow-core.mjs && node --check hooks/flow-protected-policy.mjs && node --check hooks/git-snapshot.mjs && node --check hooks/vault-path-safety.mjs && node --check hooks/vault-runtime-store.mjs && node --check packages/harness/src/index.mjs && node --check packages/harness/src/flow-store.mjs && node --check packages/harness/src/operating-profile.mjs && node --check packages/harness/src/sensors-core.mjs && node --check packages/integrations/src/host-hooks.mjs && node --check packages/integrations/src/hook-envelope.mjs && node --check packages/integrations/src/prompt-content.mjs && node --check packages/integrations/src/transcript-usage.mjs && node --check packages/integrations/src/transcripts.mjs && node --check packages/integrations/src/session-identity.mjs && node --check packages/integrations/src/index.mjs && node --check packages/mcp/src/audit.mjs && node --check packages/mcp/src/cli.mjs && node --check packages/mcp/src/config.mjs && node --check packages/mcp/src/effects.mjs && node --check packages/mcp/src/executor.mjs && node --check packages/mcp/src/server.mjs && node --check packages/mcp/src/stdio.mjs && node --check packages/mcp/src/index.mjs && node --check packages/vault/src/index.mjs && node --check packages/vault/src/project-vault.mjs && node --check packages/vault/src/vault-path-safety.mjs && node --check packages/vault/src/locale.mjs && node --check packages/vault/src/memory-schema.mjs && node --check packages/vault/src/memory-mode.mjs && node --check packages/vault/src/memory-scope.mjs && node --check packages/vault/src/memory-candidate-policy.mjs && node --check packages/vault/src/evidence-recall.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/vault/src/memory-store.mjs && node --check packages/vault/src/validate-core.mjs && node --check packages/vault/src/validate-memory.mjs",
|
|
49
|
+
"postcheck": "node --check packages/observer/src/index.mjs && node --check packages/observer/src/policy.mjs && node --check packages/observer/src/redaction.mjs && node --check packages/observer/src/authz.mjs && node --check packages/observer/src/token-registry.mjs && node --check packages/observer/src/encryption.mjs && node --check packages/observer/src/retention.mjs && node --check packages/observer/src/purge.mjs && node --check packages/observer/src/audit.mjs && node --check src/ecosystem-bridges.mjs && node --check src/ecosystem-bridge-artifact-collector.mjs && node --check src/ecosystem-bridge-baseline.mjs && node --check src/ecosystem-bridge-proof.mjs && node --check packages/integrations/src/bridge-config.mjs && node --check packages/integrations/src/bridge-contract.mjs && node --check packages/integrations/src/bridge-diagnostics.mjs && node --check packages/integrations/src/canonical-bridge-authority.mjs && node --check packages/integrations/src/ecosystem-bridge.mjs && node --check packages/integrations/src/spec-kit-adapter.mjs && node --check packages/integrations/src/superpowers-adapter.mjs",
|
|
49
50
|
"test": "node --test --test-concurrency=2",
|
|
50
51
|
"test:core": "node scripts/run-scope.mjs core",
|
|
51
52
|
"release": "node scripts/release.mjs",
|
|
@@ -61,6 +61,7 @@ Usage:
|
|
|
61
61
|
serve: --vault P · --timeout-ms N.
|
|
62
62
|
config: --client generic|claude|codex|cursor · --vault P.
|
|
63
63
|
wendkeep capabilities [...] Show declared lifecycle/effect coverage by host. --host <id> · --host-version <v> · --json.
|
|
64
|
+
wendkeep bridge <sub> Optional ecosystem adapters: status | import-spec-kit | export-status | dispatch-superpowers | verify-artifacts.
|
|
64
65
|
wendkeep observer <sub> Local multi-project Observer: serve | register | publish | reconcile | status.
|
|
65
66
|
wendkeep worktree create <slug> [--base ref] [--branch name] [--open vscode|none] [--json]
|
|
66
67
|
wendkeep worktree list [--json]
|
|
@@ -237,6 +238,9 @@ async function main(argv) {
|
|
|
237
238
|
} else if (cmd === 'commit') {
|
|
238
239
|
const { COMMIT_HELP } = await import('../../commit/src/cli.mjs');
|
|
239
240
|
process.stdout.write(COMMIT_HELP);
|
|
241
|
+
} else if (cmd === 'bridge') {
|
|
242
|
+
const { BRIDGE_HELP } = await import('../../../src/ecosystem-bridges.mjs');
|
|
243
|
+
process.stdout.write(BRIDGE_HELP);
|
|
240
244
|
} else {
|
|
241
245
|
process.stdout.write(HELP);
|
|
242
246
|
}
|
|
@@ -250,7 +254,7 @@ async function main(argv) {
|
|
|
250
254
|
// `sync` starts with `init` and resolves the freshly bound Vault itself. Pre-resolving
|
|
251
255
|
// here would prevent that repair step from reporting a corrupt binding as its own
|
|
252
256
|
// first-stage failure (and could never make it as far as the guarded init).
|
|
253
|
-
&& !['init', 'sync', 'worktree', 'hook', 'observer', 'mcp', 'capabilities', 'commit', '--version', '-v', '--help', '-h', 'help'].includes(cmd)) {
|
|
257
|
+
&& !['init', 'sync', 'worktree', 'hook', 'observer', 'mcp', 'capabilities', 'commit', 'bridge', '--version', '-v', '--help', '-h', 'help'].includes(cmd)) {
|
|
254
258
|
await preferProjectVault(rest);
|
|
255
259
|
}
|
|
256
260
|
switch (cmd) {
|
|
@@ -294,6 +298,11 @@ async function main(argv) {
|
|
|
294
298
|
process.exit(runCapabilities(rest));
|
|
295
299
|
break;
|
|
296
300
|
}
|
|
301
|
+
case 'bridge': {
|
|
302
|
+
const { runEcosystemBridge } = await import('../../../src/ecosystem-bridges.mjs');
|
|
303
|
+
process.exit(runEcosystemBridge(rest));
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
297
306
|
case 'worktree': {
|
|
298
307
|
const { runWorktree } = await import('../../../src/worktree.mjs');
|
|
299
308
|
process.exit(await runWorktree(rest));
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
// at the PROJECT ROOT (wendkeep.sensors.json); evidence lives per-change in the vault.
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
5
|
import { createHash } from 'node:crypto';
|
|
6
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
7
|
-
import { dirname, join, resolve } from 'node:path';
|
|
6
|
+
import { existsSync, lstatSync, readFileSync, realpathSync } from 'node:fs';
|
|
7
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
8
8
|
|
|
9
9
|
export const SENSOR_VAULT_ENV = 'WENDKEEP_SENSOR_VAULT';
|
|
10
10
|
const SENSOR_OUTPUT_MAX_BUFFER = 8 * 1024 * 1024;
|
|
11
11
|
const SENSOR_DIAGNOSTIC_MAX_LENGTH = 2000;
|
|
12
|
+
const SENSOR_ARTIFACT_MAX_BYTES = 1024 * 1024;
|
|
12
13
|
|
|
13
14
|
function sanitizeSensorDiagnostic(value) {
|
|
14
15
|
return String(value || '')
|
|
@@ -27,6 +28,41 @@ function sha256(value) {
|
|
|
27
28
|
return `sha256:${createHash('sha256').update(String(value || '')).digest('hex')}`;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
function containedPath(root, target) {
|
|
32
|
+
const rel = relative(root, target);
|
|
33
|
+
return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function collectSensorArtifactResults(sensor, cwd) {
|
|
37
|
+
const declarations = sensor?.artifact_results;
|
|
38
|
+
if (declarations === undefined) return [];
|
|
39
|
+
if (!Array.isArray(declarations)) throw new Error('artifact_results must be an array');
|
|
40
|
+
const root = realpathSync(resolve(cwd || '.'));
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
return declarations.map((declaration) => {
|
|
43
|
+
const externalId = String(declaration?.external_id || '').trim();
|
|
44
|
+
const configuredPath = String(declaration?.path || '').trim();
|
|
45
|
+
if (Object.keys(declaration || {}).some((key) => ![
|
|
46
|
+
'schema_version', 'external_id', 'path', 'algorithm',
|
|
47
|
+
].includes(key)) || declaration?.schema_version !== 1 || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(externalId)
|
|
48
|
+
|| !configuredPath || declaration?.algorithm !== 'sha256' || seen.has(externalId)) {
|
|
49
|
+
throw new Error('artifact result declaration is invalid');
|
|
50
|
+
}
|
|
51
|
+
seen.add(externalId);
|
|
52
|
+
const candidate = resolve(root, configuredPath);
|
|
53
|
+
if (!containedPath(root, candidate) || !existsSync(candidate) || !lstatSync(candidate).isFile()) {
|
|
54
|
+
throw new Error(`artifact result path is unavailable: ${externalId}`);
|
|
55
|
+
}
|
|
56
|
+
const file = realpathSync(candidate);
|
|
57
|
+
if (!containedPath(root, file) || lstatSync(file).size > SENSOR_ARTIFACT_MAX_BYTES) {
|
|
58
|
+
throw new Error(`artifact result path is unsafe: ${externalId}`);
|
|
59
|
+
}
|
|
60
|
+
const path = relative(root, file).replaceAll('\\', '/');
|
|
61
|
+
const digest = createHash('sha256').update(readFileSync(file)).digest('hex');
|
|
62
|
+
return { schema_version: 1, external_id: externalId, path, algorithm: 'sha256', digest };
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
30
66
|
function sensorNow(now) {
|
|
31
67
|
const value = typeof now === 'function' ? now() : (now || new Date().toISOString());
|
|
32
68
|
if (value instanceof Date) return value.toISOString();
|
|
@@ -167,7 +203,17 @@ export function runSensors(sensors, ids, { spawn = spawnSync, cwd, env, now } =
|
|
|
167
203
|
output_sha256: sha256(rawOutput),
|
|
168
204
|
output_tail: boundedOutputTail,
|
|
169
205
|
};
|
|
170
|
-
if (entry.status === '
|
|
206
|
+
if (entry.status === 'green' && s.artifact_results !== undefined) {
|
|
207
|
+
try {
|
|
208
|
+
entry.artifact_results = collectSensorArtifactResults(s, cwd);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
entry.status = 'red';
|
|
211
|
+
entry.exit_code = 1;
|
|
212
|
+
entry.artifact_results = [];
|
|
213
|
+
entry.note = sanitizeSensorDiagnostic(error?.message || 'artifact result collection failed');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (entry.status === 'red' && !entry.note) entry.note = sensorFailureNote(r);
|
|
171
217
|
if (s.type === 'mutation' && s.report) {
|
|
172
218
|
// Delegated mutation (Wave B): read the tool's mutation-testing-elements report and
|
|
173
219
|
// attach surviving mutants so verify can turn them into fix tasks.
|