@pdpp/local-collector 0.0.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 +48 -0
- package/dist/local-collector/bin/pdpp-local-collector.js +347 -0
- package/dist/local-collector/src/errors.d.ts +12 -0
- package/dist/local-collector/src/errors.js +20 -0
- package/dist/local-collector/src/runner.d.ts +16 -0
- package/dist/local-collector/src/runner.js +59 -0
- package/dist/polyfill-connectors/connectors/claude_code/index.js +806 -0
- package/dist/polyfill-connectors/connectors/claude_code/parsers.js +224 -0
- package/dist/polyfill-connectors/connectors/claude_code/schemas.js +120 -0
- package/dist/polyfill-connectors/connectors/claude_code/types.js +1 -0
- package/dist/polyfill-connectors/connectors/codex/index.js +880 -0
- package/dist/polyfill-connectors/connectors/codex/parsers.js +159 -0
- package/dist/polyfill-connectors/connectors/codex/schemas.js +118 -0
- package/dist/polyfill-connectors/connectors/codex/types.js +1 -0
- package/dist/polyfill-connectors/src/auth.js +76 -0
- package/dist/polyfill-connectors/src/browser-handoff.js +197 -0
- package/dist/polyfill-connectors/src/collector-protocol.d.ts +2 -0
- package/dist/polyfill-connectors/src/collector-protocol.js +2 -0
- package/dist/polyfill-connectors/src/collector-runner.d.ts +139 -0
- package/dist/polyfill-connectors/src/collector-runner.js +1084 -0
- package/dist/polyfill-connectors/src/connector-runtime-protocol.d.ts +191 -0
- package/dist/polyfill-connectors/src/connector-runtime-protocol.js +1 -0
- package/dist/polyfill-connectors/src/connector-runtime.js +879 -0
- package/dist/polyfill-connectors/src/fixture-capture.js +237 -0
- package/dist/polyfill-connectors/src/is-main-module.d.ts +1 -0
- package/dist/polyfill-connectors/src/is-main-module.js +17 -0
- package/dist/polyfill-connectors/src/local-device-client.d.ts +126 -0
- package/dist/polyfill-connectors/src/local-device-client.js +132 -0
- package/dist/polyfill-connectors/src/local-device-envelope.d.ts +26 -0
- package/dist/polyfill-connectors/src/local-device-envelope.js +43 -0
- package/dist/polyfill-connectors/src/local-device-outbox.d.ts +115 -0
- package/dist/polyfill-connectors/src/local-device-outbox.js +509 -0
- package/dist/polyfill-connectors/src/local-device-queue.d.ts +34 -0
- package/dist/polyfill-connectors/src/local-device-queue.js +133 -0
- package/dist/polyfill-connectors/src/local-source-inventory.js +119 -0
- package/dist/polyfill-connectors/src/pdpp-safe-text.js +13 -0
- package/dist/polyfill-connectors/src/runner/index.d.ts +11 -0
- package/dist/polyfill-connectors/src/runner/index.js +10 -0
- package/dist/polyfill-connectors/src/runtime-capabilities.d.ts +40 -0
- package/dist/polyfill-connectors/src/runtime-capabilities.js +59 -0
- package/dist/polyfill-connectors/src/safe-emit.d.ts +3 -0
- package/dist/polyfill-connectors/src/safe-emit.js +30 -0
- package/dist/polyfill-connectors/src/safe-text-preview.js +156 -0
- package/dist/polyfill-connectors/src/schema-registry.js +17 -0
- package/dist/polyfill-connectors/src/scope-filters.d.ts +38 -0
- package/dist/polyfill-connectors/src/scope-filters.js +80 -0
- package/dist/polyfill-connectors/src/shutdown-hook.js +51 -0
- package/dist/polyfill-connectors/src/streaming-target-registration.js +161 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @pdpp/local-collector
|
|
2
|
+
|
|
3
|
+
Publishable PDPP local collector runtime for filesystem-class connectors.
|
|
4
|
+
|
|
5
|
+
This package is intentionally narrower than `@pdpp/polyfill-connectors`: it
|
|
6
|
+
ships only the local collector runner, the device-exporter client, and bundled
|
|
7
|
+
Claude Code / Codex connector entrypoints. Browser/Patchright-backed connectors
|
|
8
|
+
stay out of this package until each has its own publishability review.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# @pdpp/local-collector package, npx-launched pdpp-local-collector binary
|
|
14
|
+
npx -y @pdpp/local-collector advertise
|
|
15
|
+
|
|
16
|
+
# @pdpp/local-collector package, npx-launched pdpp-local-collector binary
|
|
17
|
+
npx -y @pdpp/local-collector enroll \
|
|
18
|
+
--base-url https://<reference-host> \
|
|
19
|
+
--code <one-time-code>
|
|
20
|
+
|
|
21
|
+
# @pdpp/local-collector package, npx-launched pdpp-local-collector binary
|
|
22
|
+
PDPP_LOCAL_DEVICE_ID=<device_id> \
|
|
23
|
+
PDPP_LOCAL_DEVICE_TOKEN=<device_token> \
|
|
24
|
+
PDPP_CONNECTION_ID=<source_instance_id> \
|
|
25
|
+
npx -y @pdpp/local-collector run \
|
|
26
|
+
--base-url https://<reference-host> \
|
|
27
|
+
--connector claude_code
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The collector sends `X-PDPP-Collector-Protocol` on enrollment and every
|
|
31
|
+
device-exporter request. The reference server rejects incompatible versions
|
|
32
|
+
before persisting records or state.
|
|
33
|
+
|
|
34
|
+
Install globally if you prefer a persistent binary:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# @pdpp/local-collector package, installs the pdpp-local-collector binary
|
|
38
|
+
npm i -g @pdpp/local-collector
|
|
39
|
+
pdpp-local-collector advertise
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`device_token` is write-capable for its collector lane. Store it in a secret
|
|
43
|
+
manager or root-readable env file, and do not print it in logs, issues, or
|
|
44
|
+
support transcripts.
|
|
45
|
+
|
|
46
|
+
For a full operator runbook, including Docker move guidance and troubleshooting
|
|
47
|
+
for `403` after source migration and `409 collector_protocol_mismatch`, see
|
|
48
|
+
[`docs/local-collector.md`](../../docs/local-collector.md).
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, extname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { ALLOW_CUSTOM_COMMAND_ENV, CollectorCustomCommandRefusedError, CollectorUsageError, } from "../src/errors.js";
|
|
6
|
+
import { BUNDLED_CONNECTOR_IDS, COLLECTOR_PROTOCOL_VERSION, COLLECTOR_RUNTIME_CAPABILITIES, LocalDeviceOutbox, enrollCollector, getBundledConnector, isMainModule, runCollectorConnector, } from "../src/runner.js";
|
|
7
|
+
const DEFAULT_QUEUE_PATH = join(dirname(fileURLToPath(import.meta.url)), "..", ".pdpp-data", "collector-runner-queue.json");
|
|
8
|
+
const LOCAL_COLLECTOR_PACKAGE_NAME = "@pdpp/local-collector";
|
|
9
|
+
const LOCAL_COLLECTOR_PACKAGE_VERSION = "0.0.0";
|
|
10
|
+
const HELP_TEXT = `pdpp-local-collector — PDPP local collector runner.
|
|
11
|
+
|
|
12
|
+
Subcommands:
|
|
13
|
+
advertise Print runtime capabilities and protocol version.
|
|
14
|
+
status Print local durable outbox health as JSON.
|
|
15
|
+
[--queue <path>]
|
|
16
|
+
[--connection-id <id>]
|
|
17
|
+
doctor Print local durable outbox operator diagnostics as JSON.
|
|
18
|
+
[--queue <path>]
|
|
19
|
+
[--connection-id <id>]
|
|
20
|
+
enroll --base-url <url> Exchange a one-time enrollment code for a
|
|
21
|
+
--code <code> device id + device token.
|
|
22
|
+
[--device-label <label>]
|
|
23
|
+
run --base-url <url> Run a bundled filesystem-class connector
|
|
24
|
+
--connector claude_code|codex
|
|
25
|
+
--device-id <id>
|
|
26
|
+
--device-token <token>
|
|
27
|
+
--connection-id <id>
|
|
28
|
+
[--streams a,b,c]
|
|
29
|
+
[--backfill-streams attachments]
|
|
30
|
+
[--run-id <id>]
|
|
31
|
+
|
|
32
|
+
Public connectors: ${BUNDLED_CONNECTOR_IDS.join(", ")}.
|
|
33
|
+
Browser-bound connectors stay in the monorepo until each has its own
|
|
34
|
+
publishability review.
|
|
35
|
+
|
|
36
|
+
See: openspec/changes/publish-pdpp-local-collector/design.md.
|
|
37
|
+
`;
|
|
38
|
+
async function main() {
|
|
39
|
+
const options = parseArgs(process.argv.slice(2));
|
|
40
|
+
if (options.command === "advertise") {
|
|
41
|
+
process.stdout.write(`${JSON.stringify({
|
|
42
|
+
runtime: COLLECTOR_RUNTIME_CAPABILITIES.id,
|
|
43
|
+
bindings: [...COLLECTOR_RUNTIME_CAPABILITIES.bindings],
|
|
44
|
+
collector_protocol_version: COLLECTOR_PROTOCOL_VERSION,
|
|
45
|
+
bundled_connectors: BUNDLED_CONNECTOR_IDS,
|
|
46
|
+
}, null, 2)}\n`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (options.command === "status" || options.command === "doctor") {
|
|
50
|
+
const status = inspectLocalOutboxStatus(options);
|
|
51
|
+
process.stdout.write(`${JSON.stringify(options.command === "doctor" ? buildLocalOutboxDoctor(status) : status, null, 2)}\n`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (options.command === "enroll") {
|
|
55
|
+
if (!options.code) {
|
|
56
|
+
throw new CollectorUsageError("enroll requires --code <one-time-code>");
|
|
57
|
+
}
|
|
58
|
+
const response = await enrollCollector({
|
|
59
|
+
baseUrl: options.baseUrl,
|
|
60
|
+
code: options.code,
|
|
61
|
+
...(options.deviceLabel ? { deviceLabel: options.deviceLabel } : {}),
|
|
62
|
+
});
|
|
63
|
+
process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!(options.deviceId && options.deviceToken && options.sourceInstanceId)) {
|
|
67
|
+
throw new CollectorUsageError("run requires --device-id <id>, --device-token <token>, and --connection-id <id>");
|
|
68
|
+
}
|
|
69
|
+
if (!options.connector) {
|
|
70
|
+
throw new CollectorUsageError("run requires --connector <connector-id>");
|
|
71
|
+
}
|
|
72
|
+
const spec = buildConnectorSpec(options);
|
|
73
|
+
const result = await runCollectorConnector({
|
|
74
|
+
baseUrl: options.baseUrl,
|
|
75
|
+
connector: spec,
|
|
76
|
+
deviceId: options.deviceId,
|
|
77
|
+
deviceToken: options.deviceToken,
|
|
78
|
+
queuePath: scopedDefaultQueuePath(options.queuePath, DEFAULT_QUEUE_PATH, options.sourceInstanceId),
|
|
79
|
+
...(options.runId ? { runId: options.runId } : {}),
|
|
80
|
+
sourceInstanceId: options.sourceInstanceId,
|
|
81
|
+
});
|
|
82
|
+
process.stdout.write(`${JSON.stringify(summarizeRunResultForCli(result), null, 2)}\n`);
|
|
83
|
+
}
|
|
84
|
+
export function summarizeRunResultForCli(result) {
|
|
85
|
+
return {
|
|
86
|
+
...result,
|
|
87
|
+
flushedState: summarizeCollectorState(result.flushedState),
|
|
88
|
+
priorState: summarizeCollectorState(result.priorState),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function summarizeCollectorState(state) {
|
|
92
|
+
if (!state || Object.keys(state).length === 0) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const streams = {};
|
|
96
|
+
for (const [stream, cursor] of Object.entries(state).sort(([a], [b]) => a.localeCompare(b))) {
|
|
97
|
+
streams[stream] = summarizeCursor(cursor);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
stream_count: Object.keys(streams).length,
|
|
101
|
+
streams,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function summarizeCursor(cursor) {
|
|
105
|
+
if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
|
|
106
|
+
return { keys: [] };
|
|
107
|
+
}
|
|
108
|
+
const record = cursor;
|
|
109
|
+
const summary = {
|
|
110
|
+
keys: Object.keys(record).sort(),
|
|
111
|
+
};
|
|
112
|
+
if (typeof record.fetched_at === "string") {
|
|
113
|
+
summary.fetched_at = record.fetched_at;
|
|
114
|
+
}
|
|
115
|
+
if (record.file_mtimes && typeof record.file_mtimes === "object" && !Array.isArray(record.file_mtimes)) {
|
|
116
|
+
summary.file_mtimes_count = Object.keys(record.file_mtimes).length;
|
|
117
|
+
}
|
|
118
|
+
return summary;
|
|
119
|
+
}
|
|
120
|
+
export function inspectLocalOutboxStatus(options) {
|
|
121
|
+
const dbPath = resolveOutboxPath(options);
|
|
122
|
+
const exists = existsSync(dbPath);
|
|
123
|
+
const summary = exists ? readOutboxSummary(dbPath, options.sourceInstanceId) : emptyOutboxSummary();
|
|
124
|
+
return {
|
|
125
|
+
collector_protocol_version: COLLECTOR_PROTOCOL_VERSION,
|
|
126
|
+
configured_device: {
|
|
127
|
+
device_id_configured: Boolean(options.deviceId),
|
|
128
|
+
device_token_configured: Boolean(options.deviceToken),
|
|
129
|
+
},
|
|
130
|
+
db: {
|
|
131
|
+
configured: Boolean(options.queuePath),
|
|
132
|
+
exists,
|
|
133
|
+
path: dbPath,
|
|
134
|
+
},
|
|
135
|
+
outbox: {
|
|
136
|
+
counts: {
|
|
137
|
+
dead_letter: summary.deadLetter,
|
|
138
|
+
leased: summary.leased,
|
|
139
|
+
pending: summary.ready,
|
|
140
|
+
retrying: summary.retrying,
|
|
141
|
+
sent: summary.succeeded,
|
|
142
|
+
total: summary.total,
|
|
143
|
+
},
|
|
144
|
+
expired_leases: summary.staleLeases,
|
|
145
|
+
oldest_pending_at: summary.oldestReadyAt,
|
|
146
|
+
},
|
|
147
|
+
package: {
|
|
148
|
+
name: LOCAL_COLLECTOR_PACKAGE_NAME,
|
|
149
|
+
version: LOCAL_COLLECTOR_PACKAGE_VERSION,
|
|
150
|
+
},
|
|
151
|
+
source: {
|
|
152
|
+
source_instance_id: options.sourceInstanceId ?? null,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export function buildLocalOutboxDoctor(status) {
|
|
157
|
+
const checks = {
|
|
158
|
+
expired_leases: status.outbox.expired_leases > 0 ? "warn" : "ok",
|
|
159
|
+
outbox_db: status.db.exists ? "ok" : "missing",
|
|
160
|
+
outbox_failures: status.outbox.counts.dead_letter > 0 ? "fail" : "ok",
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
...status,
|
|
164
|
+
checks,
|
|
165
|
+
status: checks.outbox_failures === "fail"
|
|
166
|
+
? "critical"
|
|
167
|
+
: checks.expired_leases === "warn" || checks.outbox_db === "missing"
|
|
168
|
+
? "warning"
|
|
169
|
+
: "ok",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export function buildConnectorSpec(options) {
|
|
173
|
+
if (!options.connector) {
|
|
174
|
+
throw new CollectorUsageError("connector required");
|
|
175
|
+
}
|
|
176
|
+
const bundled = getBundledConnector(options.connector);
|
|
177
|
+
const customAllowed = process.env[ALLOW_CUSTOM_COMMAND_ENV] === "1";
|
|
178
|
+
if (options.entrypointCommand && !customAllowed) {
|
|
179
|
+
throw new CollectorCustomCommandRefusedError();
|
|
180
|
+
}
|
|
181
|
+
if (!(bundled || customAllowed)) {
|
|
182
|
+
throw new CollectorUsageError(`connector '${options.connector}' is not bundled with pdpp-local-collector. ` +
|
|
183
|
+
`Supported: ${BUNDLED_CONNECTOR_IDS.join(", ")}. ` +
|
|
184
|
+
`Set ${ALLOW_CUSTOM_COMMAND_ENV}=1 to use --command <bin> for monorepo development.`);
|
|
185
|
+
}
|
|
186
|
+
const command = options.entrypointCommand ?? bundled?.command ?? "tsx";
|
|
187
|
+
const args = options.args ?? [...(bundled?.args ?? [`connectors/${options.connector}/index.ts`])];
|
|
188
|
+
const streams = options.streams ?? [...(bundled?.streams ?? [])];
|
|
189
|
+
if (streams.length === 0) {
|
|
190
|
+
throw new CollectorUsageError(`run requires --streams <a,b,c> for connector ${options.connector}`);
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
connector_id: options.connector,
|
|
194
|
+
streams,
|
|
195
|
+
...(options.streamsToBackfill ? { streamsToBackfill: options.streamsToBackfill } : {}),
|
|
196
|
+
command,
|
|
197
|
+
args,
|
|
198
|
+
runtime_requirements: { bindings: bundled?.bindings ?? {} },
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
export function parseArgs(args) {
|
|
202
|
+
const [command, ...rest] = args;
|
|
203
|
+
if (command === "--help" || command === "-h" || command === "help" || !command) {
|
|
204
|
+
process.stdout.write(HELP_TEXT);
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
if (command !== "enroll" && command !== "run" && command !== "advertise" && command !== "status" && command !== "doctor") {
|
|
208
|
+
throw new CollectorUsageError(`usage: pdpp-local-collector <enroll|run|advertise|status|doctor> --base-url <url> [options]`);
|
|
209
|
+
}
|
|
210
|
+
const options = {
|
|
211
|
+
baseUrl: process.env.PDPP_REFERENCE_BASE_URL ?? "http://127.0.0.1:7662",
|
|
212
|
+
command,
|
|
213
|
+
queuePath: process.env.PDPP_COLLECTOR_QUEUE ?? DEFAULT_QUEUE_PATH,
|
|
214
|
+
};
|
|
215
|
+
if (process.env.PDPP_LOCAL_DEVICE_ID) {
|
|
216
|
+
options.deviceId = process.env.PDPP_LOCAL_DEVICE_ID;
|
|
217
|
+
}
|
|
218
|
+
if (process.env.PDPP_LOCAL_DEVICE_TOKEN) {
|
|
219
|
+
options.deviceToken = process.env.PDPP_LOCAL_DEVICE_TOKEN;
|
|
220
|
+
}
|
|
221
|
+
if (process.env.PDPP_COLLECTOR_CONNECTOR) {
|
|
222
|
+
options.connector = process.env.PDPP_COLLECTOR_CONNECTOR;
|
|
223
|
+
}
|
|
224
|
+
if (process.env.PDPP_SOURCE_INSTANCE_ID) {
|
|
225
|
+
options.sourceInstanceId = process.env.PDPP_SOURCE_INSTANCE_ID;
|
|
226
|
+
}
|
|
227
|
+
if (process.env.PDPP_CONNECTION_ID) {
|
|
228
|
+
options.sourceInstanceId = process.env.PDPP_CONNECTION_ID;
|
|
229
|
+
}
|
|
230
|
+
if (process.env.PDPP_RUN_ID) {
|
|
231
|
+
options.runId = process.env.PDPP_RUN_ID;
|
|
232
|
+
}
|
|
233
|
+
for (let index = 0; index < rest.length; index++) {
|
|
234
|
+
const arg = rest[index];
|
|
235
|
+
if (!arg) {
|
|
236
|
+
throw new CollectorUsageError("missing option");
|
|
237
|
+
}
|
|
238
|
+
const value = rest[index + 1];
|
|
239
|
+
applyOption(options, arg, value);
|
|
240
|
+
index++;
|
|
241
|
+
}
|
|
242
|
+
return options;
|
|
243
|
+
}
|
|
244
|
+
function applyOption(options, arg, value) {
|
|
245
|
+
if (!value) {
|
|
246
|
+
throw new CollectorUsageError(`missing option value: ${arg}`);
|
|
247
|
+
}
|
|
248
|
+
const setters = {
|
|
249
|
+
"--base-url": (next) => {
|
|
250
|
+
options.baseUrl = next;
|
|
251
|
+
},
|
|
252
|
+
"--backfill-streams": (next) => {
|
|
253
|
+
options.streamsToBackfill = parseCsv(next);
|
|
254
|
+
},
|
|
255
|
+
"--code": (next) => {
|
|
256
|
+
options.code = next;
|
|
257
|
+
},
|
|
258
|
+
"--connector": (next) => {
|
|
259
|
+
options.connector = next;
|
|
260
|
+
},
|
|
261
|
+
"--device-id": (next) => {
|
|
262
|
+
options.deviceId = next;
|
|
263
|
+
},
|
|
264
|
+
"--device-label": (next) => {
|
|
265
|
+
options.deviceLabel = next;
|
|
266
|
+
},
|
|
267
|
+
"--device-token": (next) => {
|
|
268
|
+
options.deviceToken = next;
|
|
269
|
+
},
|
|
270
|
+
"--queue": (next) => {
|
|
271
|
+
options.queuePath = next;
|
|
272
|
+
},
|
|
273
|
+
"--run-id": (next) => {
|
|
274
|
+
options.runId = next;
|
|
275
|
+
},
|
|
276
|
+
"--connection-id": (next) => {
|
|
277
|
+
options.sourceInstanceId = next;
|
|
278
|
+
},
|
|
279
|
+
"--source-instance-id": (next) => {
|
|
280
|
+
options.sourceInstanceId = next;
|
|
281
|
+
},
|
|
282
|
+
"--streams": (next) => {
|
|
283
|
+
options.streams = parseCsv(next);
|
|
284
|
+
},
|
|
285
|
+
"--command": (next) => {
|
|
286
|
+
options.entrypointCommand = next;
|
|
287
|
+
},
|
|
288
|
+
"--args": (next) => {
|
|
289
|
+
options.args = next.split(" ").filter(Boolean);
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
const set = setters[arg];
|
|
293
|
+
if (!set) {
|
|
294
|
+
throw new CollectorUsageError(`unknown option: ${arg}`);
|
|
295
|
+
}
|
|
296
|
+
set(value);
|
|
297
|
+
}
|
|
298
|
+
function parseCsv(value) {
|
|
299
|
+
return value
|
|
300
|
+
.split(",")
|
|
301
|
+
.map((s) => s.trim())
|
|
302
|
+
.filter(Boolean);
|
|
303
|
+
}
|
|
304
|
+
export function scopedDefaultQueuePath(queuePath, defaultQueuePath, connectionId) {
|
|
305
|
+
if (queuePath !== defaultQueuePath) {
|
|
306
|
+
return queuePath;
|
|
307
|
+
}
|
|
308
|
+
const extension = extname(defaultQueuePath);
|
|
309
|
+
const stem = basename(defaultQueuePath, extension);
|
|
310
|
+
return join(dirname(defaultQueuePath), `${stem}.${safeQueuePathSegment(connectionId)}${extension}`);
|
|
311
|
+
}
|
|
312
|
+
function resolveOutboxPath(options) {
|
|
313
|
+
return options.sourceInstanceId
|
|
314
|
+
? scopedDefaultQueuePath(options.queuePath, DEFAULT_QUEUE_PATH, options.sourceInstanceId)
|
|
315
|
+
: options.queuePath;
|
|
316
|
+
}
|
|
317
|
+
function readOutboxSummary(path, sourceInstanceId) {
|
|
318
|
+
const outbox = new LocalDeviceOutbox({ path });
|
|
319
|
+
try {
|
|
320
|
+
return outbox.summary(sourceInstanceId ? { sourceInstanceId } : {});
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
outbox.close();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function emptyOutboxSummary() {
|
|
327
|
+
return {
|
|
328
|
+
deadLetter: 0,
|
|
329
|
+
leased: 0,
|
|
330
|
+
oldestReadyAt: null,
|
|
331
|
+
ready: 0,
|
|
332
|
+
retrying: 0,
|
|
333
|
+
staleLeases: 0,
|
|
334
|
+
succeeded: 0,
|
|
335
|
+
total: 0,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function safeQueuePathSegment(value) {
|
|
339
|
+
return encodeURIComponent(value).replaceAll("%", "_");
|
|
340
|
+
}
|
|
341
|
+
if (isMainModule(import.meta.url)) {
|
|
342
|
+
main().catch((error) => {
|
|
343
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
344
|
+
const exitCode = error instanceof CollectorUsageError ? error.exitCode : 1;
|
|
345
|
+
process.exit(exitCode);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { CollectorStateReadError, RUNTIME_CAPABILITY_MISMATCH_CODE, RuntimeCapabilityMismatchError, } from "../../polyfill-connectors/src/runner/index.js";
|
|
2
|
+
export declare const ALLOW_CUSTOM_COMMAND_ENV = "PDPP_LOCAL_COLLECTOR_ALLOW_CUSTOM_COMMAND";
|
|
3
|
+
export declare class CollectorCustomCommandRefusedError extends Error {
|
|
4
|
+
readonly code: "custom_command_refused";
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare class CollectorUsageError extends Error {
|
|
8
|
+
readonly exitCode: number;
|
|
9
|
+
constructor(message: string, options?: {
|
|
10
|
+
exitCode?: number;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { CollectorStateReadError, RUNTIME_CAPABILITY_MISMATCH_CODE, RuntimeCapabilityMismatchError, } from "../../polyfill-connectors/src/runner/index.js";
|
|
2
|
+
export const ALLOW_CUSTOM_COMMAND_ENV = "PDPP_LOCAL_COLLECTOR_ALLOW_CUSTOM_COMMAND";
|
|
3
|
+
export class CollectorCustomCommandRefusedError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor() {
|
|
6
|
+
super(`pdpp-local-collector refuses --command <bin> by default to keep the ` +
|
|
7
|
+
`device-token supply chain narrow. Set ${ALLOW_CUSTOM_COMMAND_ENV}=1 ` +
|
|
8
|
+
`to opt in for monorepo development; see openspec/changes/publish-pdpp-local-collector/design.md §3.`);
|
|
9
|
+
this.name = "CollectorCustomCommandRefusedError";
|
|
10
|
+
this.code = "custom_command_refused";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class CollectorUsageError extends Error {
|
|
14
|
+
exitCode;
|
|
15
|
+
constructor(message, options = {}) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "CollectorUsageError";
|
|
18
|
+
this.exitCode = options.exitCode ?? 64;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RuntimeCapabilityProfile } from "../../polyfill-connectors/src/runner/index.js";
|
|
2
|
+
export { buildCollectorStartMessage, COLLECTOR_PROTOCOL_VERSION, CollectorStateReadError, drainCollectorQueue, emitToStdout, enrollCollector, evaluatePlacement, isMainModule, LocalDeviceClient, LocalDeviceHttpError, LocalDeviceOutbox, LocalDeviceQueue, PROVIDER_RUNTIME_CAPABILITIES, RUNTIME_CAPABILITY_MISMATCH_CODE, RuntimeCapabilityMismatchError, assertPlacementOrThrow, buildLocalDeviceRecordEnvelope, buildLocalDeviceOutboxId, canonicalJson, diffRequiredBindings, hashCanonicalJson, parseJsonlLine, resourceSet, runCollectorConnector, stringifyForJsonl, transformRecordsToCollectorEnvelopes, type CollectorChildContext, type CollectorConnectorSpec, type CollectorEnrollmentConfig, type CollectorRunConfig, type CollectorRunResult, type ConnectorPlacementInput, type ConnectorRuntimeRequirements, type EmittedMessage, type EnrollmentExchangeResponse, type LocalDeviceRecordEnvelope, type BuildLocalDeviceOutboxIdInput, type LocalDeviceOutboxClaimInput, type LocalDeviceOutboxDeadLetterInput, type LocalDeviceOutboxEnqueueInput, type LocalDeviceOutboxFailInput, type LocalDeviceOutboxItem, type LocalDeviceOutboxKind, type LocalDeviceOutboxLeaseInput, type LocalDeviceOutboxOptions, type LocalDeviceOutboxStatus, type LocalDeviceOutboxSummary, type PlacementDecision, type RuntimeBindingName, type RuntimeCapabilityProfile, type StartMessage, type StreamScope, } from "../../polyfill-connectors/src/runner/index.js";
|
|
3
|
+
export declare const COLLECTOR_RUNTIME_CAPABILITIES: RuntimeCapabilityProfile;
|
|
4
|
+
export interface BundledConnectorEntry {
|
|
5
|
+
readonly connector_id: string;
|
|
6
|
+
readonly args: readonly string[];
|
|
7
|
+
readonly command: string;
|
|
8
|
+
readonly bindings: Readonly<Record<string, {
|
|
9
|
+
required: boolean;
|
|
10
|
+
}>>;
|
|
11
|
+
readonly streams: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export declare const BUNDLED_CONNECTORS: Readonly<Record<string, BundledConnectorEntry>>;
|
|
14
|
+
export declare const BUNDLED_CONNECTOR_IDS: readonly string[];
|
|
15
|
+
export declare function getBundledConnector(connectorId: string): BundledConnectorEntry | null;
|
|
16
|
+
export declare const BUNDLED_CONNECTOR_VERSIONS: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { COLLECTOR_PROTOCOL_VERSION as PROTOCOL_VERSION, COLLECTOR_RUNTIME_CAPABILITIES as POLYFILL_COLLECTOR_RUNTIME_CAPABILITIES, } from "../../polyfill-connectors/src/runner/index.js";
|
|
5
|
+
export { buildCollectorStartMessage, COLLECTOR_PROTOCOL_VERSION, CollectorStateReadError, drainCollectorQueue, emitToStdout, enrollCollector, evaluatePlacement, isMainModule, LocalDeviceClient, LocalDeviceHttpError, LocalDeviceOutbox, LocalDeviceQueue, PROVIDER_RUNTIME_CAPABILITIES, RUNTIME_CAPABILITY_MISMATCH_CODE, RuntimeCapabilityMismatchError, assertPlacementOrThrow, buildLocalDeviceRecordEnvelope, buildLocalDeviceOutboxId, canonicalJson, diffRequiredBindings, hashCanonicalJson, parseJsonlLine, resourceSet, runCollectorConnector, stringifyForJsonl, transformRecordsToCollectorEnvelopes, } from "../../polyfill-connectors/src/runner/index.js";
|
|
6
|
+
export const COLLECTOR_RUNTIME_CAPABILITIES = {
|
|
7
|
+
id: POLYFILL_COLLECTOR_RUNTIME_CAPABILITIES.id,
|
|
8
|
+
bindings: new Set(["network", "filesystem", "local_device"]),
|
|
9
|
+
};
|
|
10
|
+
function bundledEntry(connectorPath) {
|
|
11
|
+
const built = fileURLToPath(new URL(`../../polyfill-connectors/connectors/${connectorPath}/index.js`, import.meta.url));
|
|
12
|
+
if (existsSync(built)) {
|
|
13
|
+
return built;
|
|
14
|
+
}
|
|
15
|
+
return fileURLToPath(new URL(`../../polyfill-connectors/connectors/${connectorPath}/index.ts`, import.meta.url));
|
|
16
|
+
}
|
|
17
|
+
function commandForEntry(entry) {
|
|
18
|
+
return extname(entry) === ".ts" ? "tsx" : "node";
|
|
19
|
+
}
|
|
20
|
+
const POLYFILL_CLAUDE_CODE_ENTRY = bundledEntry("claude_code");
|
|
21
|
+
const POLYFILL_CODEX_ENTRY = bundledEntry("codex");
|
|
22
|
+
export const BUNDLED_CONNECTORS = Object.freeze({
|
|
23
|
+
claude_code: Object.freeze({
|
|
24
|
+
connector_id: "claude_code",
|
|
25
|
+
command: commandForEntry(POLYFILL_CLAUDE_CODE_ENTRY),
|
|
26
|
+
args: Object.freeze([POLYFILL_CLAUDE_CODE_ENTRY]),
|
|
27
|
+
bindings: Object.freeze({ filesystem: Object.freeze({ required: true }) }),
|
|
28
|
+
streams: Object.freeze([
|
|
29
|
+
"sessions",
|
|
30
|
+
"messages",
|
|
31
|
+
"attachments",
|
|
32
|
+
"memory_notes",
|
|
33
|
+
"skills",
|
|
34
|
+
"slash_commands",
|
|
35
|
+
]),
|
|
36
|
+
}),
|
|
37
|
+
codex: Object.freeze({
|
|
38
|
+
connector_id: "codex",
|
|
39
|
+
command: commandForEntry(POLYFILL_CODEX_ENTRY),
|
|
40
|
+
args: Object.freeze([POLYFILL_CODEX_ENTRY]),
|
|
41
|
+
bindings: Object.freeze({ filesystem: Object.freeze({ required: true }) }),
|
|
42
|
+
streams: Object.freeze([
|
|
43
|
+
"sessions",
|
|
44
|
+
"messages",
|
|
45
|
+
"function_calls",
|
|
46
|
+
"rules",
|
|
47
|
+
"prompts",
|
|
48
|
+
"skills",
|
|
49
|
+
]),
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
export const BUNDLED_CONNECTOR_IDS = Object.freeze(Object.keys(BUNDLED_CONNECTORS));
|
|
53
|
+
export function getBundledConnector(connectorId) {
|
|
54
|
+
return BUNDLED_CONNECTORS[connectorId] ?? null;
|
|
55
|
+
}
|
|
56
|
+
export const BUNDLED_CONNECTOR_VERSIONS = Object.freeze({
|
|
57
|
+
claude_code: PROTOCOL_VERSION,
|
|
58
|
+
codex: PROTOCOL_VERSION,
|
|
59
|
+
});
|