@pdpp/local-collector 0.18.2 → 0.18.4
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.
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const COLLECTOR_BUILD_SOURCE_SENTINEL = "source";
|
|
2
2
|
const COLLECTOR_BUILD_INFO = {
|
|
3
|
-
builtAt: "2026-06-
|
|
4
|
-
revision: "
|
|
5
|
-
version: "0.18.
|
|
3
|
+
builtAt: "2026-06-26T14:50:36.407Z",
|
|
4
|
+
revision: "1f4bad0a8c2b",
|
|
5
|
+
version: "0.18.4",
|
|
6
6
|
};
|
|
7
7
|
function buildAgentVersion(info = COLLECTOR_BUILD_INFO) {
|
|
8
8
|
return `${info.version}+${info.revision}`;
|
|
@@ -399,10 +399,10 @@ async function runInBrowser(args) {
|
|
|
399
399
|
page,
|
|
400
400
|
});
|
|
401
401
|
await watchdog.run(() => establishSession({ ensureSession, probeSession }, {
|
|
402
|
-
assist,
|
|
402
|
+
assist: watchdog.wrapAssist(assist),
|
|
403
403
|
capture: baseCtx.capture,
|
|
404
404
|
checkpoint: watchdog.checkpoint,
|
|
405
|
-
completeAssistance,
|
|
405
|
+
completeAssistance: watchdog.wrapCompleteAssistance(completeAssistance),
|
|
406
406
|
context: ctx,
|
|
407
407
|
page: page,
|
|
408
408
|
name,
|
|
@@ -798,6 +798,7 @@ export function makeSessionEstablishWatchdog(args) {
|
|
|
798
798
|
const pollIntervalMs = args.pollIntervalMs ?? Math.max(1, Math.min(1000, Math.floor(deadlineMs / 4)));
|
|
799
799
|
let lastProgressAt = now();
|
|
800
800
|
let lastLabel = null;
|
|
801
|
+
const openAssistance = new Map();
|
|
801
802
|
let openInteractions = 0;
|
|
802
803
|
let tripped = false;
|
|
803
804
|
const markProgress = (label) => {
|
|
@@ -816,6 +817,43 @@ export function makeSessionEstablishWatchdog(args) {
|
|
|
816
817
|
process.stderr.write(`[session-watchdog] checkpoint capture failed for ${label}: ${message}\n`);
|
|
817
818
|
}
|
|
818
819
|
};
|
|
820
|
+
const assistancePausesWatchdog = (req) => req.progress_posture === "running" && req.response_contract === "none";
|
|
821
|
+
const pruneExpiredAssistance = () => {
|
|
822
|
+
const current = now();
|
|
823
|
+
let pruned = false;
|
|
824
|
+
for (const [id, expiresAt] of openAssistance) {
|
|
825
|
+
if (expiresAt > current) {
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
openAssistance.delete(id);
|
|
829
|
+
pruned = true;
|
|
830
|
+
}
|
|
831
|
+
if (pruned) {
|
|
832
|
+
markProgress(null);
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
const wrapAssist = (assist) => async (req) => {
|
|
836
|
+
markProgress(null);
|
|
837
|
+
const assistanceRequestId = await assist(req);
|
|
838
|
+
if (assistancePausesWatchdog(req)) {
|
|
839
|
+
const timeoutMs = typeof req.timeout_seconds === "number" && Number.isFinite(req.timeout_seconds) && req.timeout_seconds > 0
|
|
840
|
+
? req.timeout_seconds * 1000
|
|
841
|
+
: deadlineMs;
|
|
842
|
+
openAssistance.set(assistanceRequestId, now() + timeoutMs + deadlineMs);
|
|
843
|
+
markProgress(null);
|
|
844
|
+
}
|
|
845
|
+
return assistanceRequestId;
|
|
846
|
+
};
|
|
847
|
+
const wrapCompleteAssistance = (completeAssistance) => async (assistanceRequestId, status, extra = {}) => {
|
|
848
|
+
try {
|
|
849
|
+
await completeAssistance(assistanceRequestId, status, extra);
|
|
850
|
+
}
|
|
851
|
+
finally {
|
|
852
|
+
if (openAssistance.delete(assistanceRequestId)) {
|
|
853
|
+
markProgress(null);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
};
|
|
819
857
|
const wrapSendInteraction = (send) => async (req) => {
|
|
820
858
|
openInteractions++;
|
|
821
859
|
markProgress(null);
|
|
@@ -833,7 +871,8 @@ export function makeSessionEstablishWatchdog(args) {
|
|
|
833
871
|
const TRIP = Symbol("session-establish-trip");
|
|
834
872
|
const tripPromise = new Promise((resolve) => {
|
|
835
873
|
const onTick = () => {
|
|
836
|
-
|
|
874
|
+
pruneExpiredAssistance();
|
|
875
|
+
if (tripped || openInteractions > 0 || openAssistance.size > 0) {
|
|
837
876
|
return;
|
|
838
877
|
}
|
|
839
878
|
const sinceMs = now() - lastProgressAt;
|
|
@@ -868,7 +907,7 @@ export function makeSessionEstablishWatchdog(args) {
|
|
|
868
907
|
}
|
|
869
908
|
}
|
|
870
909
|
};
|
|
871
|
-
return { checkpoint, wrapSendInteraction, run };
|
|
910
|
+
return { checkpoint, wrapAssist, wrapCompleteAssistance, wrapSendInteraction, run };
|
|
872
911
|
}
|
|
873
912
|
async function establishSession(hooks, args) {
|
|
874
913
|
const { ensureSession, probeSession } = hooks;
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
function freezeStaticSecretDescriptor(descriptor) {
|
|
2
|
+
const freezeMapping = (mapping) => {
|
|
3
|
+
if (mapping.secretEnvVars) {
|
|
4
|
+
Object.freeze(mapping.secretEnvVars);
|
|
5
|
+
}
|
|
6
|
+
if (mapping.secretFieldEnvVars) {
|
|
7
|
+
for (const value of Object.values(mapping.secretFieldEnvVars)) {
|
|
8
|
+
Object.freeze(value);
|
|
9
|
+
}
|
|
10
|
+
Object.freeze(mapping.secretFieldEnvVars);
|
|
11
|
+
}
|
|
12
|
+
if (mapping.setupFieldEnvVars) {
|
|
13
|
+
for (const value of Object.values(mapping.setupFieldEnvVars)) {
|
|
14
|
+
Object.freeze(value);
|
|
15
|
+
}
|
|
16
|
+
Object.freeze(mapping.setupFieldEnvVars);
|
|
17
|
+
}
|
|
18
|
+
return Object.freeze(mapping);
|
|
19
|
+
};
|
|
2
20
|
if (descriptor.secretEnvVars) {
|
|
3
21
|
Object.freeze(descriptor.secretEnvVars);
|
|
4
22
|
}
|
|
@@ -14,9 +32,22 @@ function freezeStaticSecretDescriptor(descriptor) {
|
|
|
14
32
|
}
|
|
15
33
|
Object.freeze(descriptor.setupFieldEnvVars);
|
|
16
34
|
}
|
|
35
|
+
if (descriptor.acceptedCredentialVariants) {
|
|
36
|
+
for (const variant of descriptor.acceptedCredentialVariants) {
|
|
37
|
+
freezeMapping(variant);
|
|
38
|
+
}
|
|
39
|
+
Object.freeze(descriptor.acceptedCredentialVariants);
|
|
40
|
+
}
|
|
17
41
|
return Object.freeze(descriptor);
|
|
18
42
|
}
|
|
19
43
|
export const STATIC_SECRET_CONNECTOR_REGISTRY = Object.freeze({
|
|
44
|
+
amazon: freezeStaticSecretDescriptor({
|
|
45
|
+
credentialKind: "username_password",
|
|
46
|
+
secretFieldEnvVars: {
|
|
47
|
+
password: ["AMAZON_PASSWORD"],
|
|
48
|
+
username: ["AMAZON_USERNAME"],
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
20
51
|
chatgpt: freezeStaticSecretDescriptor({
|
|
21
52
|
credentialKind: "username_password",
|
|
22
53
|
secretFieldEnvVars: {
|
|
@@ -56,12 +87,33 @@ export const STATIC_SECRET_CONNECTOR_REGISTRY = Object.freeze({
|
|
|
56
87
|
secretEnvVars: ["NOTION_API_TOKEN"],
|
|
57
88
|
}),
|
|
58
89
|
reddit: freezeStaticSecretDescriptor({
|
|
59
|
-
credentialKind: "
|
|
90
|
+
credentialKind: "username_password",
|
|
91
|
+
acceptedCredentialVariants: [
|
|
92
|
+
{
|
|
93
|
+
credentialKind: "secret_bundle",
|
|
94
|
+
secretFieldEnvVars: {
|
|
95
|
+
reddit_password: ["REDDIT_PASSWORD"],
|
|
96
|
+
reddit_username: ["REDDIT_USERNAME"],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
],
|
|
60
100
|
secretFieldEnvVars: {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
101
|
+
password: ["REDDIT_PASSWORD"],
|
|
102
|
+
username: ["REDDIT_USERNAME"],
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
chase: freezeStaticSecretDescriptor({
|
|
106
|
+
credentialKind: "username_password",
|
|
107
|
+
secretFieldEnvVars: {
|
|
108
|
+
password: ["CHASE_PASSWORD"],
|
|
109
|
+
username: ["CHASE_USERNAME"],
|
|
110
|
+
},
|
|
111
|
+
}),
|
|
112
|
+
usaa: freezeStaticSecretDescriptor({
|
|
113
|
+
credentialKind: "username_password",
|
|
114
|
+
secretFieldEnvVars: {
|
|
115
|
+
password: ["USAA_PASSWORD"],
|
|
116
|
+
username: ["USAA_USERNAME"],
|
|
65
117
|
},
|
|
66
118
|
}),
|
|
67
119
|
});
|
|
@@ -111,14 +163,26 @@ function secretBundleFields(connectorId, secret) {
|
|
|
111
163
|
}
|
|
112
164
|
return fields;
|
|
113
165
|
}
|
|
114
|
-
function
|
|
166
|
+
function injectionMappingForRecoveredSecret(connectorId, descriptor, recovered) {
|
|
115
167
|
if (!recovered || typeof recovered.secret !== "string" || recovered.secret.length === 0) {
|
|
116
168
|
throw new StaticSecretInjectionError("recovered_secret_invalid", `Cannot inject an empty credential for connector '${connectorId}'.`);
|
|
117
169
|
}
|
|
118
|
-
if (recovered.credentialKind
|
|
119
|
-
|
|
170
|
+
if (recovered.credentialKind === descriptor.credentialKind) {
|
|
171
|
+
return descriptor;
|
|
172
|
+
}
|
|
173
|
+
const variant = descriptor.acceptedCredentialVariants?.find((candidate) => candidate.credentialKind === recovered.credentialKind);
|
|
174
|
+
if (variant) {
|
|
175
|
+
return variant;
|
|
176
|
+
}
|
|
177
|
+
const expectedKinds = [
|
|
178
|
+
descriptor.credentialKind,
|
|
179
|
+
...(descriptor.acceptedCredentialVariants ?? []).map((v) => v.credentialKind),
|
|
180
|
+
];
|
|
181
|
+
if (!expectedKinds.includes(recovered.credentialKind)) {
|
|
182
|
+
throw new StaticSecretInjectionError("credential_kind_mismatch", `Connector '${connectorId}' expects credential kind '${expectedKinds.join("' or '")}', ` +
|
|
120
183
|
`but the recovered credential is '${recovered.credentialKind}'.`);
|
|
121
184
|
}
|
|
185
|
+
return descriptor;
|
|
122
186
|
}
|
|
123
187
|
function injectSingleSecret(fragment, envVars, secret) {
|
|
124
188
|
for (const envVar of envVars ?? []) {
|
|
@@ -157,10 +221,10 @@ export function buildConnectionScopedSecretEnv(connectorId, recovered, sourceBin
|
|
|
157
221
|
if (!descriptor) {
|
|
158
222
|
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.`);
|
|
159
223
|
}
|
|
160
|
-
|
|
224
|
+
const mapping = injectionMappingForRecoveredSecret(connectorId, descriptor, recovered);
|
|
161
225
|
const fragment = {};
|
|
162
|
-
injectSingleSecret(fragment,
|
|
163
|
-
injectSecretBundle(fragment, connectorId, recovered.secret,
|
|
164
|
-
injectSetupFields(fragment,
|
|
226
|
+
injectSingleSecret(fragment, mapping.secretEnvVars, recovered.secret);
|
|
227
|
+
injectSecretBundle(fragment, connectorId, recovered.secret, mapping.secretFieldEnvVars);
|
|
228
|
+
injectSetupFields(fragment, mapping.setupFieldEnvVars, sourceBinding);
|
|
165
229
|
return fragment;
|
|
166
230
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pdpp/local-collector",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.4",
|
|
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,
|