@tomflow/proflow-execution-browser-extension 0.1.19 → 0.1.21
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/dist/deployment/adapter.d.ts +41 -12
- package/dist/deployment/adapter.js +134 -18
- package/dist/deployment/descriptor.d.ts +1 -1
- package/dist/deployment/descriptor.js +1 -1
- package/dist/src/bridge.js +9 -0
- package/manifest.json +1 -1
- package/package.json +3 -3
- package/proflow.module.json +1 -1
|
@@ -7,6 +7,29 @@ export declare function materializeProductionConfig(input: {
|
|
|
7
7
|
}): Promise<{
|
|
8
8
|
loadDir: string;
|
|
9
9
|
}>;
|
|
10
|
+
export type RunningBridgeProbe = {
|
|
11
|
+
kind: "ABSENT";
|
|
12
|
+
} | {
|
|
13
|
+
kind: "PRESENT";
|
|
14
|
+
identity?: {
|
|
15
|
+
extensionId: string;
|
|
16
|
+
extensionInstanceId: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export declare function classifyBrowserLiveSetup(input: {
|
|
20
|
+
baseSetupReady: boolean;
|
|
21
|
+
evidenceInstanceId?: string;
|
|
22
|
+
bridgeProbe: RunningBridgeProbe;
|
|
23
|
+
}): {
|
|
24
|
+
readonly setupReady: boolean;
|
|
25
|
+
issueCode?: never;
|
|
26
|
+
} | {
|
|
27
|
+
setupReady: boolean;
|
|
28
|
+
issueCode: "EXTENSION_SESSION_OFFLINE";
|
|
29
|
+
} | {
|
|
30
|
+
setupReady: boolean;
|
|
31
|
+
issueCode: "EXTENSION_SESSION_REVALIDATION_REQUIRED";
|
|
32
|
+
};
|
|
10
33
|
export declare function pairBrowserExtensionSetup(context: ModuleCommandContext, options?: {
|
|
11
34
|
timeoutMs?: number;
|
|
12
35
|
onWaiting?: (input: {
|
|
@@ -24,7 +47,7 @@ export declare const behaviorAdapter: {
|
|
|
24
47
|
ok: true;
|
|
25
48
|
status: "SUCCEEDED";
|
|
26
49
|
moduleRef: "execution-browser-extension";
|
|
27
|
-
moduleVersion: "0.1.
|
|
50
|
+
moduleVersion: "0.1.21";
|
|
28
51
|
data: {
|
|
29
52
|
loadDir: string;
|
|
30
53
|
};
|
|
@@ -37,7 +60,7 @@ export declare const behaviorAdapter: {
|
|
|
37
60
|
readonly ok: true;
|
|
38
61
|
readonly status: "SUCCEEDED";
|
|
39
62
|
readonly moduleRef: "execution-browser-extension";
|
|
40
|
-
readonly moduleVersion: "0.1.
|
|
63
|
+
readonly moduleVersion: "0.1.21";
|
|
41
64
|
};
|
|
42
65
|
observedEffects: never[];
|
|
43
66
|
}>;
|
|
@@ -47,17 +70,23 @@ export declare const behaviorAdapter: {
|
|
|
47
70
|
ok: true;
|
|
48
71
|
status: "SUCCEEDED";
|
|
49
72
|
moduleRef: "execution-browser-extension";
|
|
50
|
-
moduleVersion: "0.1.
|
|
73
|
+
moduleVersion: "0.1.21";
|
|
51
74
|
data: {
|
|
52
75
|
setupStatus: "ACTION_REQUIRED" | "READY";
|
|
53
76
|
runtimeStatus: "NOT_APPLICABLE";
|
|
54
|
-
issues?: {
|
|
77
|
+
issues?: ({
|
|
55
78
|
scope: "SETUP";
|
|
56
79
|
code: string;
|
|
57
80
|
message: string;
|
|
58
81
|
relatedModuleRefs: string[];
|
|
59
82
|
nextCommand: string;
|
|
60
|
-
}
|
|
83
|
+
} | {
|
|
84
|
+
scope: "SETUP";
|
|
85
|
+
code: "EXTENSION_SESSION_OFFLINE" | "EXTENSION_SESSION_REVALIDATION_REQUIRED" | undefined;
|
|
86
|
+
message: string;
|
|
87
|
+
relatedModuleRefs: string[];
|
|
88
|
+
nextCommand: string;
|
|
89
|
+
})[];
|
|
61
90
|
};
|
|
62
91
|
};
|
|
63
92
|
observedEffects: never[];
|
|
@@ -66,7 +95,7 @@ export declare const behaviorAdapter: {
|
|
|
66
95
|
result: {
|
|
67
96
|
contract: "deployment.result.v1";
|
|
68
97
|
moduleRef: "execution-browser-extension";
|
|
69
|
-
moduleVersion: "0.1.
|
|
98
|
+
moduleVersion: "0.1.21";
|
|
70
99
|
ok: false;
|
|
71
100
|
status: "ACTION_REQUIRED";
|
|
72
101
|
data: {
|
|
@@ -98,14 +127,14 @@ export declare const behaviorAdapter: {
|
|
|
98
127
|
readonly ok: true;
|
|
99
128
|
readonly status: "SUCCEEDED";
|
|
100
129
|
readonly moduleRef: "execution-browser-extension";
|
|
101
|
-
readonly moduleVersion: "0.1.
|
|
130
|
+
readonly moduleVersion: "0.1.21";
|
|
102
131
|
};
|
|
103
132
|
observedEffects: string[];
|
|
104
133
|
} | {
|
|
105
134
|
result: {
|
|
106
135
|
contract: "deployment.result.v1";
|
|
107
136
|
moduleRef: "execution-browser-extension";
|
|
108
|
-
moduleVersion: "0.1.
|
|
137
|
+
moduleVersion: "0.1.21";
|
|
109
138
|
ok: false;
|
|
110
139
|
status: "FAILED";
|
|
111
140
|
error: {
|
|
@@ -138,7 +167,7 @@ export declare const behaviorAdapter: {
|
|
|
138
167
|
ok: true;
|
|
139
168
|
status: "SUCCEEDED";
|
|
140
169
|
moduleRef: "execution-browser-extension";
|
|
141
|
-
moduleVersion: "0.1.
|
|
170
|
+
moduleVersion: "0.1.21";
|
|
142
171
|
data: {
|
|
143
172
|
docs: string;
|
|
144
173
|
};
|
|
@@ -151,11 +180,11 @@ export declare const behaviorAdapter: {
|
|
|
151
180
|
readonly ok: true;
|
|
152
181
|
readonly status: "SUCCEEDED";
|
|
153
182
|
readonly moduleRef: "execution-browser-extension";
|
|
154
|
-
readonly moduleVersion: "0.1.
|
|
183
|
+
readonly moduleVersion: "0.1.21";
|
|
155
184
|
} | {
|
|
156
185
|
contract: "deployment.result.v1";
|
|
157
186
|
moduleRef: "execution-browser-extension";
|
|
158
|
-
moduleVersion: "0.1.
|
|
187
|
+
moduleVersion: "0.1.21";
|
|
159
188
|
ok: false;
|
|
160
189
|
status: "FAILED";
|
|
161
190
|
error: {
|
|
@@ -172,7 +201,7 @@ export declare const behaviorAdapter: {
|
|
|
172
201
|
readonly ok: true;
|
|
173
202
|
readonly status: "SUCCEEDED";
|
|
174
203
|
readonly moduleRef: "execution-browser-extension";
|
|
175
|
-
readonly moduleVersion: "0.1.
|
|
204
|
+
readonly moduleVersion: "0.1.21";
|
|
176
205
|
};
|
|
177
206
|
observedEffects: never[];
|
|
178
207
|
}>;
|
|
@@ -133,11 +133,12 @@ async function persistDeveloperModeConfirmation(context) {
|
|
|
133
133
|
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
134
134
|
await writeFile(installFlowFile(context), `${JSON.stringify({ developerModeConfirmed: true })}\n`, { mode: 0o600 });
|
|
135
135
|
}
|
|
136
|
-
async function
|
|
136
|
+
async function readEvidenceCandidate(context, loadDir) {
|
|
137
137
|
try {
|
|
138
138
|
const raw = JSON.parse(await readFile(verificationFile(context), "utf8"));
|
|
139
139
|
return raw.contract === "proflow.browser-extension-verification.v1" &&
|
|
140
|
-
raw.moduleVersion ===
|
|
140
|
+
typeof raw.moduleVersion === "string" &&
|
|
141
|
+
raw.moduleVersion.length > 0 &&
|
|
141
142
|
raw.loadDir === loadDir &&
|
|
142
143
|
typeof raw.extensionId === "string" &&
|
|
143
144
|
/^[a-z]{32}$/.test(raw.extensionId) &&
|
|
@@ -154,6 +155,12 @@ async function readEvidence(context, loadDir) {
|
|
|
154
155
|
return undefined;
|
|
155
156
|
}
|
|
156
157
|
}
|
|
158
|
+
async function readEvidence(context, loadDir) {
|
|
159
|
+
const evidence = await readEvidenceCandidate(context, loadDir);
|
|
160
|
+
return evidence?.moduleVersion === descriptor.moduleVersion
|
|
161
|
+
? evidence
|
|
162
|
+
: undefined;
|
|
163
|
+
}
|
|
157
164
|
async function ownFacts(context) {
|
|
158
165
|
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
159
166
|
const loadDir = browserExtensionLoadDir(context.workspaceRoot);
|
|
@@ -248,15 +255,98 @@ async function persistPairedBrowserExtension(context, prepared, identity) {
|
|
|
248
255
|
await writeFile(verificationFile(context), `${JSON.stringify(evidence, null, 2)}\n`, { mode: 0o600 });
|
|
249
256
|
return identity;
|
|
250
257
|
}
|
|
258
|
+
export function classifyBrowserLiveSetup(input) {
|
|
259
|
+
if (!input.baseSetupReady || input.bridgeProbe.kind === "ABSENT")
|
|
260
|
+
return { setupReady: input.baseSetupReady };
|
|
261
|
+
if (!input.bridgeProbe.identity)
|
|
262
|
+
return {
|
|
263
|
+
setupReady: false,
|
|
264
|
+
issueCode: "EXTENSION_SESSION_OFFLINE",
|
|
265
|
+
};
|
|
266
|
+
if (input.bridgeProbe.identity.extensionInstanceId !== input.evidenceInstanceId)
|
|
267
|
+
return {
|
|
268
|
+
setupReady: false,
|
|
269
|
+
issueCode: "EXTENSION_SESSION_REVALIDATION_REQUIRED",
|
|
270
|
+
};
|
|
271
|
+
return { setupReady: true };
|
|
272
|
+
}
|
|
273
|
+
function connectionRefused(error) {
|
|
274
|
+
if (!(error instanceof TypeError))
|
|
275
|
+
return false;
|
|
276
|
+
const cause = Reflect.get(error, "cause");
|
|
277
|
+
return (typeof cause === "object" &&
|
|
278
|
+
cause !== null &&
|
|
279
|
+
Reflect.get(cause, "code") === "ECONNREFUSED");
|
|
280
|
+
}
|
|
281
|
+
async function probeRunningBridgeSession(prepared, extensionId, timeoutMs) {
|
|
282
|
+
try {
|
|
283
|
+
const response = await fetch(`${prepared.bridgeEndpoint}/v1/session/status`, {
|
|
284
|
+
headers: {
|
|
285
|
+
authorization: `Bearer ${await credential(prepared.bridgeTokenFile)}`,
|
|
286
|
+
origin: `chrome-extension://${extensionId}`,
|
|
287
|
+
},
|
|
288
|
+
signal: AbortSignal.timeout(Math.max(25, Math.min(500, timeoutMs))),
|
|
289
|
+
});
|
|
290
|
+
if (!response.ok)
|
|
291
|
+
throw new Error(`RUNNING_BRIDGE_STATUS_${response.status}`);
|
|
292
|
+
const body = (await response.json());
|
|
293
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
294
|
+
throw new Error("RUNNING_BRIDGE_STATUS_INVALID");
|
|
295
|
+
if (Reflect.get(body, "online") !== true)
|
|
296
|
+
return { kind: "PRESENT" };
|
|
297
|
+
const extensionInstanceId = Reflect.get(body, "extensionInstanceId");
|
|
298
|
+
if (typeof extensionInstanceId !== "string" ||
|
|
299
|
+
extensionInstanceId.length === 0)
|
|
300
|
+
throw new Error("RUNNING_BRIDGE_STATUS_INVALID");
|
|
301
|
+
return {
|
|
302
|
+
kind: "PRESENT",
|
|
303
|
+
identity: { extensionId, extensionInstanceId },
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
if (connectionRefused(error))
|
|
308
|
+
return { kind: "ABSENT" };
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async function revalidateRunningBridge(prepared, extensionId, timeoutMs, onWaiting) {
|
|
313
|
+
const startedAt = Date.now();
|
|
314
|
+
const graceMs = Math.min(2_000, Math.max(25, Math.floor(timeoutMs / 4)));
|
|
315
|
+
let waitingShown = false;
|
|
316
|
+
while (true) {
|
|
317
|
+
const remainingMs = Math.max(1, timeoutMs - (Date.now() - startedAt));
|
|
318
|
+
const probe = await probeRunningBridgeSession(prepared, extensionId, remainingMs);
|
|
319
|
+
if (probe.kind === "ABSENT")
|
|
320
|
+
return undefined;
|
|
321
|
+
if (probe.identity)
|
|
322
|
+
return probe.identity;
|
|
323
|
+
const elapsedMs = Date.now() - startedAt;
|
|
324
|
+
if (!waitingShown && elapsedMs >= graceMs) {
|
|
325
|
+
waitingShown = true;
|
|
326
|
+
await onWaiting?.({
|
|
327
|
+
loadDir: prepared.loadDir,
|
|
328
|
+
endpoint: prepared.bridgeEndpoint,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (elapsedMs >= timeoutMs)
|
|
332
|
+
throw new Error("PAIRING_TIMEOUT");
|
|
333
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(100, Math.max(10, timeoutMs - elapsedMs))));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
251
336
|
export async function pairBrowserExtensionSetup(context, options = {}) {
|
|
252
337
|
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
253
338
|
await installPackage(context);
|
|
254
339
|
const existingSetup = await readSetup(context);
|
|
255
|
-
const existingEvidence = await
|
|
340
|
+
const existingEvidence = await readEvidenceCandidate(context, browserExtensionLoadDir(context.workspaceRoot));
|
|
256
341
|
const canRevalidateExisting = Boolean(existingSetup &&
|
|
257
342
|
existingEvidence &&
|
|
258
343
|
existingSetup.extensionId === existingEvidence.extensionId);
|
|
259
344
|
const prepared = await materializeRuntimeConfig(context);
|
|
345
|
+
if (canRevalidateExisting && existingSetup) {
|
|
346
|
+
const runningIdentity = await revalidateRunningBridge(prepared, existingSetup.extensionId, options.timeoutMs ?? 120_000, options.onWaiting);
|
|
347
|
+
if (runningIdentity)
|
|
348
|
+
return persistPairedBrowserExtension(context, prepared, runningIdentity);
|
|
349
|
+
}
|
|
260
350
|
const pairing = await createBrowserExtensionPairingServer({
|
|
261
351
|
token: await credential(prepared.bridgeTokenFile),
|
|
262
352
|
host: "127.0.0.1",
|
|
@@ -317,31 +407,57 @@ export const behaviorAdapter = {
|
|
|
317
407
|
loadDir,
|
|
318
408
|
})
|
|
319
409
|
: "MISSING";
|
|
320
|
-
const
|
|
410
|
+
const baseSetupReady = Boolean(setup &&
|
|
321
411
|
evidence &&
|
|
322
412
|
setup.extensionId === evidence.extensionId &&
|
|
323
413
|
chromeState === "ENABLED");
|
|
414
|
+
let bridgeProbe = { kind: "ABSENT" };
|
|
415
|
+
if (baseSetupReady && evidence) {
|
|
416
|
+
const facts = await readModuleSharedFacts(context, descriptor.moduleRef);
|
|
417
|
+
const bridgeEndpoint = factString(facts, "bridgeEndpoint");
|
|
418
|
+
const bridgeTokenFile = factString(facts, "bridgeTokenFile");
|
|
419
|
+
if (bridgeEndpoint && bridgeTokenFile) {
|
|
420
|
+
try {
|
|
421
|
+
bridgeProbe = await probeRunningBridgeSession({ bridgeEndpoint, bridgeTokenFile }, evidence.extensionId, 500);
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
bridgeProbe = { kind: "PRESENT" };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
const live = classifyBrowserLiveSetup({
|
|
429
|
+
baseSetupReady,
|
|
430
|
+
...(evidence ? { evidenceInstanceId: evidence.extensionInstanceId } : {}),
|
|
431
|
+
bridgeProbe,
|
|
432
|
+
});
|
|
433
|
+
const issue = !baseSetupReady
|
|
434
|
+
? {
|
|
435
|
+
scope: "SETUP",
|
|
436
|
+
code: "EXTENSION_LOAD_REQUIRED",
|
|
437
|
+
message: "Chrome 扩展尚未加载或缺少可验证的运行证据",
|
|
438
|
+
relatedModuleRefs: ["chrome-runtime"],
|
|
439
|
+
nextCommand: "platform setup",
|
|
440
|
+
}
|
|
441
|
+
: live.setupReady
|
|
442
|
+
? undefined
|
|
443
|
+
: {
|
|
444
|
+
scope: "SETUP",
|
|
445
|
+
code: live.issueCode,
|
|
446
|
+
message: live.issueCode === "EXTENSION_SESSION_OFFLINE"
|
|
447
|
+
? "Chrome 扩展已加载,但当前运行会话未在线"
|
|
448
|
+
: "Chrome 扩展已重新启动,需要自动刷新当前运行会话证据",
|
|
449
|
+
relatedModuleRefs: ["execution-runtime"],
|
|
450
|
+
nextCommand: "platform setup",
|
|
451
|
+
};
|
|
324
452
|
return {
|
|
325
453
|
result: {
|
|
326
454
|
...base,
|
|
327
455
|
data: {
|
|
328
|
-
setupStatus: setupReady
|
|
456
|
+
setupStatus: live.setupReady
|
|
329
457
|
? "READY"
|
|
330
458
|
: "ACTION_REQUIRED",
|
|
331
459
|
runtimeStatus: "NOT_APPLICABLE",
|
|
332
|
-
...(
|
|
333
|
-
? {}
|
|
334
|
-
: {
|
|
335
|
-
issues: [
|
|
336
|
-
{
|
|
337
|
-
scope: "SETUP",
|
|
338
|
-
code: "EXTENSION_LOAD_REQUIRED",
|
|
339
|
-
message: "Chrome 扩展尚未加载或缺少可验证的运行证据",
|
|
340
|
-
relatedModuleRefs: ["chrome-runtime"],
|
|
341
|
-
nextCommand: "platform setup",
|
|
342
|
-
},
|
|
343
|
-
],
|
|
344
|
-
}),
|
|
460
|
+
...(issue ? { issues: [issue] } : {}),
|
|
345
461
|
},
|
|
346
462
|
},
|
|
347
463
|
observedEffects: [],
|
|
@@ -3,7 +3,7 @@ export declare const descriptor: {
|
|
|
3
3
|
readonly contractVersion: "1.0.0";
|
|
4
4
|
readonly moduleRef: "execution-browser-extension";
|
|
5
5
|
readonly packageName: "@tomflow/proflow-execution-browser-extension";
|
|
6
|
-
readonly moduleVersion: "0.1.
|
|
6
|
+
readonly moduleVersion: "0.1.21";
|
|
7
7
|
readonly kind: "browser-extension";
|
|
8
8
|
readonly templateVersion: "1.0.0";
|
|
9
9
|
readonly platformCompatibility: ">=1.0.0 <2.0.0";
|
|
@@ -3,7 +3,7 @@ export const descriptor = {
|
|
|
3
3
|
contractVersion: "1.0.0",
|
|
4
4
|
moduleRef: "execution-browser-extension",
|
|
5
5
|
packageName: "@tomflow/proflow-execution-browser-extension",
|
|
6
|
-
moduleVersion: "0.1.
|
|
6
|
+
moduleVersion: "0.1.21",
|
|
7
7
|
kind: "browser-extension",
|
|
8
8
|
templateVersion: "1.0.0",
|
|
9
9
|
platformCompatibility: ">=1.0.0 <2.0.0",
|
package/dist/src/bridge.js
CHANGED
|
@@ -129,6 +129,15 @@ export async function createBrowserRealityBridgeServer(options) {
|
|
|
129
129
|
send(response, 200, { accepted: true });
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
132
|
+
if (request.method === "GET" && url.pathname === "/v1/session/status") {
|
|
133
|
+
const online = session !== undefined &&
|
|
134
|
+
now().getTime() - session.lastHeartbeatAt <= freshnessMs;
|
|
135
|
+
send(response, 200, {
|
|
136
|
+
online,
|
|
137
|
+
extensionInstanceId: session?.extensionInstanceId ?? null,
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
132
141
|
if (!session)
|
|
133
142
|
throw new BrowserRealityBridgeError("BRIDGE_OFFLINE", "extension session has not completed hello");
|
|
134
143
|
if (url.searchParams.get("extensionInstanceId") !==
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomflow/proflow-execution-browser-extension",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Execution-owned MV3 Browser executor, evidence provider and browser application surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"@tomflow/proflow-module-contract": "^0.1.13"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@tomflow/proflow-
|
|
35
|
-
"@tomflow/proflow-
|
|
34
|
+
"@tomflow/proflow-deployment-conformance": "^0.1.13",
|
|
35
|
+
"@tomflow/proflow-execution-runtime": "^0.1.15"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
38
38
|
"proflow",
|
package/proflow.module.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"contractVersion": "1.0.0",
|
|
4
4
|
"moduleRef": "execution-browser-extension",
|
|
5
5
|
"packageName": "@tomflow/proflow-execution-browser-extension",
|
|
6
|
-
"moduleVersion": "0.1.
|
|
6
|
+
"moduleVersion": "0.1.21",
|
|
7
7
|
"kind": "browser-extension",
|
|
8
8
|
"templateVersion": "1.0.0",
|
|
9
9
|
"platformCompatibility": ">=1.0.0 <2.0.0",
|