@zewstid/zeye-capture 0.2.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/README.md +194 -0
- package/dist/src/canonical.d.ts +3 -0
- package/dist/src/canonical.js +41 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +470 -0
- package/dist/src/fileOutbox.d.ts +22 -0
- package/dist/src/fileOutbox.js +219 -0
- package/dist/src/host.d.ts +14 -0
- package/dist/src/host.js +92 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +24 -0
- package/dist/src/localHost.d.ts +81 -0
- package/dist/src/localHost.js +289 -0
- package/dist/src/providers.d.ts +18 -0
- package/dist/src/providers.js +381 -0
- package/dist/src/redaction.d.ts +7 -0
- package/dist/src/redaction.js +80 -0
- package/dist/src/service.d.ts +28 -0
- package/dist/src/service.js +118 -0
- package/dist/src/transport.d.ts +156 -0
- package/dist/src/transport.js +368 -0
- package/dist/src/types.d.ts +125 -0
- package/dist/src/types.js +2 -0
- package/dist/test/fileOutbox.test.d.ts +1 -0
- package/dist/test/fileOutbox.test.js +104 -0
- package/dist/test/host.test.d.ts +1 -0
- package/dist/test/host.test.js +58 -0
- package/dist/test/localHost.test.d.ts +1 -0
- package/dist/test/localHost.test.js +122 -0
- package/dist/test/providers-and-transport.test.d.ts +1 -0
- package/dist/test/providers-and-transport.test.js +222 -0
- package/dist/test/transcript-and-service.test.d.ts +1 -0
- package/dist/test/transcript-and-service.test.js +50 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# @zewstid/zeye-capture
|
|
2
|
+
|
|
3
|
+
Local-first capture and continuous sync for Zeye Engineering Memory. Metadata
|
|
4
|
+
capture is the default. Complete provider-visible prompts, responses and tool
|
|
5
|
+
records are captured only after explicit Zeye consent and local secret
|
|
6
|
+
redaction. Hidden model reasoning is never captured.
|
|
7
|
+
|
|
8
|
+
## What exists now
|
|
9
|
+
|
|
10
|
+
- `FileOutbox`: a JSON-backed durable outbox with atomic state replacement.
|
|
11
|
+
- Idempotent delivery records keyed by `adapter + sessionId + sourceEventId`,
|
|
12
|
+
including immutable repository/branch/HEAD facts observed at enqueue time.
|
|
13
|
+
- Stable content hashes: replays with the same source and content collapse;
|
|
14
|
+
changed content from the same provider event is reported as a conflict.
|
|
15
|
+
- Source checkpoints for JSONL/backfill cursors.
|
|
16
|
+
- A required `RedactionBoundary`, executed before a payload reaches disk.
|
|
17
|
+
- Delivery attempt and acknowledgement state, allowing a future HTTP transport
|
|
18
|
+
to replay safely after an offline period.
|
|
19
|
+
- Claude Code hook and Codex JSONL adapters for lifecycle metadata and,
|
|
20
|
+
when explicitly consented, provider-visible messages and tool records.
|
|
21
|
+
- A one-command background service that continuously imports and retries on
|
|
22
|
+
macOS (launchd), Linux (systemd user service), and Windows (Task Scheduler).
|
|
23
|
+
- `CaptureHttpTransport`, which turns one durable local delivery into one
|
|
24
|
+
authenticated, idempotent `POST /engineering-memory/ingest` batch.
|
|
25
|
+
- A production-oriented local CLI: Claude Code hook install/removal, configured
|
|
26
|
+
Codex JSONL sync, safe Git identity discovery, durable outbox locking,
|
|
27
|
+
explicit heartbeat, and non-secret status diagnostics.
|
|
28
|
+
|
|
29
|
+
## Security and limits
|
|
30
|
+
|
|
31
|
+
- Transcript content is locally redacted before either the durable outbox or
|
|
32
|
+
network boundary. The API envelope-encrypts each segment with a separate data
|
|
33
|
+
key and keeps ciphertext apart from metadata.
|
|
34
|
+
- The one-time enrollment token is kept in an owner-only local service config,
|
|
35
|
+
never in the service definition, command line, repository, or outbox. Native
|
|
36
|
+
keychain integration remains future hardening.
|
|
37
|
+
- VS Code/GitHub Copilot currently has a safe companion-event boundary; a
|
|
38
|
+
packaged extension is not included in this release.
|
|
39
|
+
- “Complete” means all provider-visible records emitted by supported adapters.
|
|
40
|
+
It never means hidden chain-of-thought or provider-private state.
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { FileOutbox, type RedactionBoundary } from '@zewstid/zeye-capture'
|
|
46
|
+
|
|
47
|
+
const redactor: RedactionBoundary = {
|
|
48
|
+
redact(event) {
|
|
49
|
+
return { payload: event.payload, redactionCount: 0 }
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const outbox = new FileOutbox('/private/path/zeye-capture', redactor)
|
|
54
|
+
const result = await outbox.enqueue({
|
|
55
|
+
adapter: 'claude-code',
|
|
56
|
+
sessionId: 'provider-session-id',
|
|
57
|
+
sourceEventId: 'provider-event-id',
|
|
58
|
+
occurredAt: new Date().toISOString(),
|
|
59
|
+
type: 'tool.completed',
|
|
60
|
+
payload: { tool: 'Bash', exitCode: 0 },
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The included transport calls `markAttempt()` for unsuccessful uploads and
|
|
65
|
+
`acknowledge(deliveryId, receiptId)` only after a server receipt is durably
|
|
66
|
+
returned. It never regenerates a delivery ID or mutates an already queued
|
|
67
|
+
event.
|
|
68
|
+
|
|
69
|
+
## Provider adapters
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import {
|
|
73
|
+
FileOutbox, metadataRedactor, fromClaudeCodeHook,
|
|
74
|
+
fromCodexJsonl, CaptureHttpTransport,
|
|
75
|
+
} from '@zewstid/zeye-capture'
|
|
76
|
+
|
|
77
|
+
const outbox = new FileOutbox('/private/path/zeye-capture', metadataRedactor)
|
|
78
|
+
|
|
79
|
+
// Claude Code hook stdin JSON → local durable evidence.
|
|
80
|
+
const claudeEvent = fromClaudeCodeHook(hookPayload)
|
|
81
|
+
if (claudeEvent) await outbox.enqueue(claudeEvent)
|
|
82
|
+
|
|
83
|
+
// One Codex JSONL record → local durable evidence.
|
|
84
|
+
const codexEvent = fromCodexJsonl(JSON.parse(jsonlLine), 'rollout.jsonl')
|
|
85
|
+
if (codexEvent) await outbox.enqueue(codexEvent)
|
|
86
|
+
|
|
87
|
+
// A VS Code companion extension sends a deliberately metadata-only event to
|
|
88
|
+
// `zeye-capture vscode-copilot-event` over stdin. Its event shape is also
|
|
89
|
+
// available to hosts through fromVSCodeCopilotEvent().
|
|
90
|
+
|
|
91
|
+
// A host application supplies the enrollment token at runtime; it is not
|
|
92
|
+
// written into the outbox. The server still has to enable
|
|
93
|
+
// engineering-memory.session-ledger for the organization.
|
|
94
|
+
const transport = new CaptureHttpTransport(outbox, enrollmentConfiguration)
|
|
95
|
+
await transport.flush()
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The adapters intentionally treat unknown provider record shapes as
|
|
99
|
+
`capture.warning`. That keeps capture health honest while provider formats
|
|
100
|
+
change, instead of inventing a complete record.
|
|
101
|
+
|
|
102
|
+
The VS Code adapter is an integration boundary for a companion extension; it
|
|
103
|
+
does not claim to scrape Copilot chats or private extension storage. A future
|
|
104
|
+
packaged VSIX can emit these events using the same contract without changing
|
|
105
|
+
the durable outbox or server API.
|
|
106
|
+
|
|
107
|
+
## Local host workflow
|
|
108
|
+
|
|
109
|
+
Build the package first, then make the executable available on your `PATH` (or
|
|
110
|
+
use the built `dist/src/cli.js` through Node). Run all commands from the Git
|
|
111
|
+
repository being captured, or pass `--repo /absolute/path`.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Add Zeye-owned groups to the repository-local, untracked Claude settings.
|
|
115
|
+
zeye-capture install claude
|
|
116
|
+
|
|
117
|
+
# Register a known Codex rollout/session JSONL source. This does not guess a
|
|
118
|
+
# provider-private location and does not start a background process.
|
|
119
|
+
zeye-capture install codex --jsonl /absolute/path/to/rollout.jsonl
|
|
120
|
+
|
|
121
|
+
# Import configured Codex sources, upload any durable outbox records, and send
|
|
122
|
+
# a heartbeat when runtime enrollment is available.
|
|
123
|
+
zeye-capture sync
|
|
124
|
+
|
|
125
|
+
# View safe local diagnostics: queue counts, lock state, Git identity, and
|
|
126
|
+
# whether upload variables are complete. It never prints the machine token.
|
|
127
|
+
zeye-capture status
|
|
128
|
+
zeye-capture heartbeat
|
|
129
|
+
|
|
130
|
+
# With ZEYE_CAPTURE_* values present, install and immediately start continuous
|
|
131
|
+
# sync. The service survives logout/restart according to the host OS policy.
|
|
132
|
+
zeye-capture service install --repo /path/to/repo
|
|
133
|
+
|
|
134
|
+
# Stop continuous sync and remove both its definition and private enrollment.
|
|
135
|
+
zeye-capture service uninstall --repo /path/to/repo
|
|
136
|
+
|
|
137
|
+
# Remove only hook groups/sources created for Zeye.
|
|
138
|
+
zeye-capture uninstall claude
|
|
139
|
+
zeye-capture uninstall codex --jsonl /absolute/path/to/rollout.jsonl
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`install claude` defaults to `.claude/settings.local.json` and refuses to
|
|
143
|
+
modify that file if Git tracks it. It preserves other hook groups. The host
|
|
144
|
+
configuration is stored under `$XDG_CONFIG_HOME/zeye-capture/host-v1.json` (or
|
|
145
|
+
`~/.config/...`) with owner-only permissions where the OS supports POSIX modes.
|
|
146
|
+
The default outbox is under `$XDG_STATE_HOME/zeye-capture/` (or
|
|
147
|
+
`~/.local/state/...`) and is also owner-restricted. `ZEYE_CAPTURE_OUTBOX_DIR`,
|
|
148
|
+
when set, is a private base directory: Zeye creates a distinct hashed child for
|
|
149
|
+
each repository so records cannot cross repository boundaries. Neither location
|
|
150
|
+
stores the upload token.
|
|
151
|
+
|
|
152
|
+
Interactive setup supplies these variables once. `service install` persists
|
|
153
|
+
them in an owner-only local service enrollment so no scheduler is configured
|
|
154
|
+
by hand:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
ZEYE_CAPTURE_ENDPOINT=https://api.example/engineering-memory/ingest
|
|
158
|
+
ZEYE_CAPTURE_TOKEN=zem_... # runtime only; never written by CLI
|
|
159
|
+
ZEYE_CAPTURE_ORGANIZATION_ID=...
|
|
160
|
+
ZEYE_CAPTURE_INSTALLATION_ID=...
|
|
161
|
+
ZEYE_CAPTURE_ACTOR_JSON={...}
|
|
162
|
+
ZEYE_CAPTURE_REPOSITORY_ID=... # optional internal Zeye binding
|
|
163
|
+
ZEYE_CAPTURE_PROVIDER_REPOSITORY_ID=... # optional provider numeric ID
|
|
164
|
+
ZEYE_CAPTURE_CONTENT_CONSENT_ID=... # required only for transcripts
|
|
165
|
+
ZEYE_CAPTURE_TRANSCRIPT_CONSENT=yes # explicit local opt-in
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`ZEYE_CAPTURE_ENDPOINT` must be an HTTPS URL ending in
|
|
169
|
+
`/engineering-memory/ingest`. HTTP is accepted only for an explicit loopback
|
|
170
|
+
development collector. Without both content-consent variables, capture remains
|
|
171
|
+
metadata-only even if the installation is transcript-capable.
|
|
172
|
+
|
|
173
|
+
Without all required values the host remains useful offline: it captures into
|
|
174
|
+
the local outbox and reports `uploadConfigured: false`. Git discovery supplies
|
|
175
|
+
the remote's canonical `owner/repository` name, branch, and HEAD SHA when
|
|
176
|
+
available; the raw remote URL is not stored or printed. A configured internal
|
|
177
|
+
repository ID enables the server to bind those facts to existing Zeye evidence.
|
|
178
|
+
|
|
179
|
+
For continuous capture, run `zeye-capture service install --repo /path/to/repo`
|
|
180
|
+
once. The CLI
|
|
181
|
+
uses an owner-only single-process lock and waits briefly for an overlapping
|
|
182
|
+
hook/run before failing, so normal bursts are queued rather than dropped. A
|
|
183
|
+
`sync` exits non-zero if an input source or upload fails, while retaining its
|
|
184
|
+
outbox records for retry. Hook calls use the same lock.
|
|
185
|
+
|
|
186
|
+
## Build and test
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npm -w @zewstid/zeye-capture run build
|
|
190
|
+
npm -w @zewstid/zeye-capture test
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The root workspace already includes `packages/*`, so no workspace manifest
|
|
194
|
+
change is required. The package uses only Node built-ins at runtime.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.canonicalJson = canonicalJson;
|
|
4
|
+
exports.sha256 = sha256;
|
|
5
|
+
exports.newDeliveryId = newDeliveryId;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
function normalize(value) {
|
|
8
|
+
if (value === null || typeof value === 'boolean' || typeof value === 'string')
|
|
9
|
+
return value;
|
|
10
|
+
if (typeof value === 'number') {
|
|
11
|
+
if (!Number.isFinite(value))
|
|
12
|
+
throw new TypeError('Capture payload cannot contain non-finite numbers');
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === 'bigint' || typeof value === 'function' || typeof value === 'symbol' || value === undefined) {
|
|
16
|
+
throw new TypeError(`Capture payload contains unsupported value type: ${typeof value}`);
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(value))
|
|
19
|
+
return value.map(normalize);
|
|
20
|
+
if (value instanceof Date)
|
|
21
|
+
return value.toISOString();
|
|
22
|
+
if (typeof value === 'object') {
|
|
23
|
+
const object = value;
|
|
24
|
+
return Object.keys(object)
|
|
25
|
+
.sort()
|
|
26
|
+
.reduce((result, key) => {
|
|
27
|
+
result[key] = normalize(object[key]);
|
|
28
|
+
return result;
|
|
29
|
+
}, {});
|
|
30
|
+
}
|
|
31
|
+
throw new TypeError('Capture payload is not serializable');
|
|
32
|
+
}
|
|
33
|
+
function canonicalJson(value) {
|
|
34
|
+
return JSON.stringify(normalize(value));
|
|
35
|
+
}
|
|
36
|
+
function sha256(value) {
|
|
37
|
+
return (0, node_crypto_1.createHash)('sha256').update(canonicalJson(value), 'utf8').digest('hex');
|
|
38
|
+
}
|
|
39
|
+
function newDeliveryId() {
|
|
40
|
+
return (0, node_crypto_1.randomUUID)();
|
|
41
|
+
}
|