@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.
@@ -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.19";
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.19";
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.19";
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.19";
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.19";
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.19";
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.19";
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.19";
183
+ readonly moduleVersion: "0.1.21";
155
184
  } | {
156
185
  contract: "deployment.result.v1";
157
186
  moduleRef: "execution-browser-extension";
158
- moduleVersion: "0.1.19";
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.19";
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 readEvidence(context, loadDir) {
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 === descriptor.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 readEvidence(context, browserExtensionLoadDir(context.workspaceRoot));
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 setupReady = Boolean(setup &&
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
- ...(setupReady
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.19";
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.19",
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",
@@ -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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "ProFlow Execution Browser",
4
- "version": "0.1.19",
4
+ "version": "0.1.21",
5
5
  "permissions": ["tabs", "storage", "sidePanel"],
6
6
  "host_permissions": ["https://chatgpt.com/*", "http://127.0.0.1/*"],
7
7
  "background": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-execution-browser-extension",
3
- "version": "0.1.19",
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-execution-runtime": "^0.1.15",
35
- "@tomflow/proflow-deployment-conformance": "^0.1.13"
34
+ "@tomflow/proflow-deployment-conformance": "^0.1.13",
35
+ "@tomflow/proflow-execution-runtime": "^0.1.15"
36
36
  },
37
37
  "keywords": [
38
38
  "proflow",
@@ -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.19",
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",