@pdpp/local-collector 0.1.0-beta.7 → 0.1.0-beta.8

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.
Files changed (26) hide show
  1. package/dist/local-collector/bin/pdpp-local-collector.js +580 -22
  2. package/dist/local-collector/src/runner.d.ts +1 -1
  3. package/dist/local-collector/src/runner.js +15 -1
  4. package/dist/polyfill-connectors/connectors/claude_code/index.js +60 -37
  5. package/dist/polyfill-connectors/connectors/codex/index.js +390 -108
  6. package/dist/polyfill-connectors/connectors/codex/parsers.js +5 -3
  7. package/dist/polyfill-connectors/src/bounded-file-preview.js +76 -0
  8. package/dist/polyfill-connectors/src/browser-handoff.js +38 -5
  9. package/dist/polyfill-connectors/src/collector-build-info.d.ts +8 -0
  10. package/dist/polyfill-connectors/src/collector-build-info.js +10 -0
  11. package/dist/polyfill-connectors/src/collector-runner.d.ts +54 -0
  12. package/dist/polyfill-connectors/src/collector-runner.js +250 -18
  13. package/dist/polyfill-connectors/src/connector-exit.js +62 -0
  14. package/dist/polyfill-connectors/src/connector-runtime-protocol.d.ts +41 -21
  15. package/dist/polyfill-connectors/src/connector-runtime.js +241 -30
  16. package/dist/polyfill-connectors/src/fingerprint-cursor.js +107 -0
  17. package/dist/polyfill-connectors/src/local-device-client.d.ts +17 -0
  18. package/dist/polyfill-connectors/src/local-device-client.js +69 -9
  19. package/dist/polyfill-connectors/src/local-device-outbox.d.ts +59 -0
  20. package/dist/polyfill-connectors/src/local-device-outbox.js +394 -5
  21. package/dist/polyfill-connectors/src/local-source-inventory.js +8 -1
  22. package/dist/polyfill-connectors/src/runner/index.d.ts +4 -3
  23. package/dist/polyfill-connectors/src/runner/index.js +4 -3
  24. package/dist/polyfill-connectors/src/safe-text-preview.js +13 -0
  25. package/dist/polyfill-connectors/src/static-secret-injection.js +155 -0
  26. package/package.json +1 -1
@@ -0,0 +1,155 @@
1
+ function freezeStaticSecretDescriptor(descriptor) {
2
+ if (descriptor.secretEnvVars) {
3
+ Object.freeze(descriptor.secretEnvVars);
4
+ }
5
+ if (descriptor.secretFieldEnvVars) {
6
+ for (const value of Object.values(descriptor.secretFieldEnvVars)) {
7
+ Object.freeze(value);
8
+ }
9
+ Object.freeze(descriptor.secretFieldEnvVars);
10
+ }
11
+ if (descriptor.setupFieldEnvVars) {
12
+ for (const value of Object.values(descriptor.setupFieldEnvVars)) {
13
+ Object.freeze(value);
14
+ }
15
+ Object.freeze(descriptor.setupFieldEnvVars);
16
+ }
17
+ return Object.freeze(descriptor);
18
+ }
19
+ export const STATIC_SECRET_CONNECTOR_REGISTRY = Object.freeze({
20
+ gmail: freezeStaticSecretDescriptor({
21
+ credentialKind: "app_password",
22
+ secretEnvVars: ["GOOGLE_APP_PASSWORD_PDPP", "GMAIL_APP_PASSWORD"],
23
+ setupFieldEnvVars: {
24
+ account_email: ["GMAIL_ADDRESS", "GMAIL_USER"],
25
+ },
26
+ }),
27
+ github: freezeStaticSecretDescriptor({
28
+ credentialKind: "personal_access_token",
29
+ secretEnvVars: ["GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_TOKEN"],
30
+ }),
31
+ ynab: freezeStaticSecretDescriptor({
32
+ credentialKind: "personal_access_token",
33
+ secretEnvVars: ["YNAB_PERSONAL_ACCESS_TOKEN", "YNAB_PAT"],
34
+ }),
35
+ slack: freezeStaticSecretDescriptor({
36
+ credentialKind: "secret_bundle",
37
+ secretFieldEnvVars: {
38
+ slack_token: ["SLACK_TOKEN"],
39
+ slack_cookie: ["SLACK_COOKIE"],
40
+ },
41
+ setupFieldEnvVars: {
42
+ slack_workspace: ["SLACK_WORKSPACE"],
43
+ },
44
+ }),
45
+ reddit: freezeStaticSecretDescriptor({
46
+ credentialKind: "secret_bundle",
47
+ secretFieldEnvVars: {
48
+ reddit_password: ["REDDIT_PASSWORD"],
49
+ reddit_client_secret: ["REDDIT_CLIENT_SECRET"],
50
+ },
51
+ setupFieldEnvVars: {
52
+ reddit_username: ["REDDIT_USERNAME"],
53
+ reddit_client_id: ["REDDIT_CLIENT_ID"],
54
+ },
55
+ }),
56
+ });
57
+ export class StaticSecretInjectionError extends Error {
58
+ code;
59
+ constructor(code, message) {
60
+ super(message);
61
+ this.name = "StaticSecretInjectionError";
62
+ this.code = code;
63
+ }
64
+ }
65
+ export function isStaticSecretConnector(connectorId) {
66
+ return Object.hasOwn(STATIC_SECRET_CONNECTOR_REGISTRY, connectorId);
67
+ }
68
+ function setupFieldsFromSourceBinding(sourceBinding) {
69
+ if (!sourceBinding || typeof sourceBinding !== "object" || Array.isArray(sourceBinding)) {
70
+ return {};
71
+ }
72
+ const raw = sourceBinding.setup_fields;
73
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
74
+ return {};
75
+ }
76
+ const fields = {};
77
+ for (const [key, value] of Object.entries(raw)) {
78
+ if (typeof value === "string" && value.trim().length > 0) {
79
+ fields[key] = value.trim();
80
+ }
81
+ }
82
+ return fields;
83
+ }
84
+ function secretBundleFields(connectorId, secret) {
85
+ let parsed;
86
+ try {
87
+ parsed = JSON.parse(secret);
88
+ }
89
+ catch {
90
+ throw new StaticSecretInjectionError("recovered_secret_bundle_invalid", `Connector '${connectorId}' expects a sealed JSON credential bundle; recovered secret was not valid JSON.`);
91
+ }
92
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
93
+ throw new StaticSecretInjectionError("recovered_secret_bundle_invalid", `Connector '${connectorId}' expects a sealed JSON credential bundle object.`);
94
+ }
95
+ const fields = {};
96
+ for (const [key, value] of Object.entries(parsed)) {
97
+ if (typeof value === "string" && value.trim().length > 0) {
98
+ fields[key] = value.trim();
99
+ }
100
+ }
101
+ return fields;
102
+ }
103
+ function assertRecoveredSecretMatches(connectorId, descriptor, recovered) {
104
+ if (!recovered || typeof recovered.secret !== "string" || recovered.secret.length === 0) {
105
+ throw new StaticSecretInjectionError("recovered_secret_invalid", `Cannot inject an empty credential for connector '${connectorId}'.`);
106
+ }
107
+ if (recovered.credentialKind !== descriptor.credentialKind) {
108
+ throw new StaticSecretInjectionError("credential_kind_mismatch", `Connector '${connectorId}' expects credential kind '${descriptor.credentialKind}', ` +
109
+ `but the recovered credential is '${recovered.credentialKind}'.`);
110
+ }
111
+ }
112
+ function injectSingleSecret(fragment, envVars, secret) {
113
+ for (const envVar of envVars ?? []) {
114
+ fragment[envVar] = secret;
115
+ }
116
+ }
117
+ function injectSecretBundle(fragment, connectorId, secret, secretFieldEnvVars) {
118
+ if (!secretFieldEnvVars) {
119
+ return;
120
+ }
121
+ const bundle = secretBundleFields(connectorId, secret);
122
+ for (const [fieldName, envVars] of Object.entries(secretFieldEnvVars)) {
123
+ const value = bundle[fieldName];
124
+ if (!value) {
125
+ throw new StaticSecretInjectionError("recovered_secret_bundle_field_missing", `Connector '${connectorId}' credential bundle is missing required field '${fieldName}'.`);
126
+ }
127
+ for (const envVar of envVars) {
128
+ fragment[envVar] = value;
129
+ }
130
+ }
131
+ }
132
+ function injectSetupFields(fragment, setupFieldEnvVars, sourceBinding) {
133
+ const setupFields = setupFieldsFromSourceBinding(sourceBinding);
134
+ for (const [fieldName, envVars] of Object.entries(setupFieldEnvVars ?? {})) {
135
+ const value = setupFields[fieldName];
136
+ if (!value) {
137
+ continue;
138
+ }
139
+ for (const envVar of envVars) {
140
+ fragment[envVar] = value;
141
+ }
142
+ }
143
+ }
144
+ export function buildConnectionScopedSecretEnv(connectorId, recovered, sourceBinding) {
145
+ const descriptor = STATIC_SECRET_CONNECTOR_REGISTRY[connectorId];
146
+ if (!descriptor) {
147
+ throw new StaticSecretInjectionError("not_a_static_secret_connector", `Connector '${connectorId}' is not a known static-secret connector; refusing to invent secret env vars for it.`);
148
+ }
149
+ assertRecoveredSecretMatches(connectorId, descriptor, recovered);
150
+ const fragment = {};
151
+ injectSingleSecret(fragment, descriptor.secretEnvVars, recovered.secret);
152
+ injectSecretBundle(fragment, connectorId, recovered.secret, descriptor.secretFieldEnvVars);
153
+ injectSetupFields(fragment, descriptor.setupFieldEnvVars, sourceBinding);
154
+ return fragment;
155
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdpp/local-collector",
3
- "version": "0.1.0-beta.7",
3
+ "version": "0.1.0-beta.8",
4
4
  "description": "Publishable local collector runtime for PDPP: filesystem-class connectors (Claude Code, Codex) plus the device-exporter ingest client.",
5
5
  "type": "module",
6
6
  "private": false,