@tomflow/proflow-execution-browser-extension 0.1.33 → 0.1.35

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.
@@ -47,7 +47,7 @@ export declare const behaviorAdapter: {
47
47
  ok: true;
48
48
  status: "SUCCEEDED";
49
49
  moduleRef: "execution-browser-extension";
50
- moduleVersion: "0.1.33";
50
+ moduleVersion: "0.1.35";
51
51
  data: {
52
52
  loadDir: string;
53
53
  };
@@ -60,7 +60,7 @@ export declare const behaviorAdapter: {
60
60
  readonly ok: true;
61
61
  readonly status: "SUCCEEDED";
62
62
  readonly moduleRef: "execution-browser-extension";
63
- readonly moduleVersion: "0.1.33";
63
+ readonly moduleVersion: "0.1.35";
64
64
  };
65
65
  observedEffects: never[];
66
66
  }>;
@@ -70,7 +70,7 @@ export declare const behaviorAdapter: {
70
70
  ok: true;
71
71
  status: "SUCCEEDED";
72
72
  moduleRef: "execution-browser-extension";
73
- moduleVersion: "0.1.33";
73
+ moduleVersion: "0.1.35";
74
74
  data: {
75
75
  setupStatus: "ACTION_REQUIRED" | "READY";
76
76
  runtimeStatus: "NOT_APPLICABLE";
@@ -95,7 +95,7 @@ export declare const behaviorAdapter: {
95
95
  result: {
96
96
  contract: "deployment.result.v1";
97
97
  moduleRef: "execution-browser-extension";
98
- moduleVersion: "0.1.33";
98
+ moduleVersion: "0.1.35";
99
99
  ok: false;
100
100
  status: "ACTION_REQUIRED";
101
101
  data: {
@@ -127,14 +127,14 @@ export declare const behaviorAdapter: {
127
127
  readonly ok: true;
128
128
  readonly status: "SUCCEEDED";
129
129
  readonly moduleRef: "execution-browser-extension";
130
- readonly moduleVersion: "0.1.33";
130
+ readonly moduleVersion: "0.1.35";
131
131
  };
132
132
  observedEffects: string[];
133
133
  } | {
134
134
  result: {
135
135
  contract: "deployment.result.v1";
136
136
  moduleRef: "execution-browser-extension";
137
- moduleVersion: "0.1.33";
137
+ moduleVersion: "0.1.35";
138
138
  ok: false;
139
139
  status: "FAILED";
140
140
  error: {
@@ -167,7 +167,7 @@ export declare const behaviorAdapter: {
167
167
  ok: true;
168
168
  status: "SUCCEEDED";
169
169
  moduleRef: "execution-browser-extension";
170
- moduleVersion: "0.1.33";
170
+ moduleVersion: "0.1.35";
171
171
  data: {
172
172
  docs: string;
173
173
  };
@@ -180,11 +180,11 @@ export declare const behaviorAdapter: {
180
180
  readonly ok: true;
181
181
  readonly status: "SUCCEEDED";
182
182
  readonly moduleRef: "execution-browser-extension";
183
- readonly moduleVersion: "0.1.33";
183
+ readonly moduleVersion: "0.1.35";
184
184
  } | {
185
185
  contract: "deployment.result.v1";
186
186
  moduleRef: "execution-browser-extension";
187
- moduleVersion: "0.1.33";
187
+ moduleVersion: "0.1.35";
188
188
  ok: false;
189
189
  status: "FAILED";
190
190
  error: {
@@ -201,7 +201,7 @@ export declare const behaviorAdapter: {
201
201
  readonly ok: true;
202
202
  readonly status: "SUCCEEDED";
203
203
  readonly moduleRef: "execution-browser-extension";
204
- readonly moduleVersion: "0.1.33";
204
+ readonly moduleVersion: "0.1.35";
205
205
  };
206
206
  observedEffects: never[];
207
207
  }>;
@@ -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.33";
6
+ readonly moduleVersion: "0.1.35";
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.33",
6
+ moduleVersion: "0.1.35",
7
7
  kind: "browser-extension",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
@@ -13878,6 +13878,7 @@ var extensionInstanceId = `extension:${crypto.randomUUID()}`;
13878
13878
  var sessions = /* @__PURE__ */ new Map();
13879
13879
  var BROWSER_CARRIER_KEEPALIVE_KEY = "proflowBrowserSnapshot";
13880
13880
  var BROWSER_CARRIER_KEEPALIVE_MS = 2e4;
13881
+ var BROWSER_BRIDGE_FETCH_TIMEOUT_MS = 5e3;
13881
13882
  var sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
13882
13883
  async function persistSnapshot() {
13883
13884
  await chrome.storage.session.set({
@@ -14491,6 +14492,7 @@ async function executeProvisioningCommand(command) {
14491
14492
  async function bridgeFetch(config2, path, init = {}) {
14492
14493
  return fetch(`${config2.endpoint}${path}`, {
14493
14494
  ...init,
14495
+ signal: init.signal ?? AbortSignal.timeout(BROWSER_BRIDGE_FETCH_TIMEOUT_MS),
14494
14496
  headers: {
14495
14497
  authorization: `Bearer ${config2.token}`,
14496
14498
  "content-type": "application/json",
@@ -14503,7 +14505,7 @@ async function runBridgeLoop() {
14503
14505
  if (bridgeLoopStarted) return;
14504
14506
  bridgeLoopStarted = true;
14505
14507
  while (true) {
14506
- const config2 = await bridgeConfig();
14508
+ const config2 = await bridgeConfig().catch(() => null);
14507
14509
  if (!config2) {
14508
14510
  await sleep(1e3);
14509
14511
  continue;
@@ -14518,8 +14520,8 @@ async function runBridgeLoop() {
14518
14520
  })
14519
14521
  });
14520
14522
  if (!hello.ok) throw new Error("BRIDGE_HELLO_REJECTED");
14521
- let lastHeartbeatAt = 0;
14522
- let lastExtensionKeepaliveAt = 0;
14523
+ let lastHeartbeatAt = Date.now();
14524
+ let lastExtensionKeepaliveAt = Date.now();
14523
14525
  while (true) {
14524
14526
  if (Date.now() - lastExtensionKeepaliveAt >= BROWSER_CARRIER_KEEPALIVE_MS) {
14525
14527
  await chrome.storage.session.get(BROWSER_CARRIER_KEEPALIVE_KEY).catch(() => ({}));
@@ -25,9 +25,14 @@ export declare function createBrowserRealityBridgeServer(options: BrowserReality
25
25
  browser: BrowserRealityPort;
26
26
  status(): {
27
27
  online: boolean;
28
+ sessionOnline: boolean;
29
+ commandConsumerReady: boolean;
28
30
  extensionInstanceId: string | null;
29
31
  queuedCommands: number;
30
32
  pendingCommands: number;
33
+ lastCommandPollAt: string | null;
34
+ lastCommandDeliveredAt: string | null;
35
+ lastCommandResultAt: string | null;
31
36
  };
32
37
  close(): Promise<void>;
33
38
  }>>;
@@ -115,6 +115,10 @@ export async function createBrowserRealityBridgeServer(options) {
115
115
  const queue = [];
116
116
  const pending = new Map();
117
117
  let session;
118
+ let lastCommandPollAt = null;
119
+ let lastCommandDeliveredAt = null;
120
+ let lastCommandResultAt = null;
121
+ let lastCommandConsumerAt = null;
118
122
  let closed = false;
119
123
  let endpoint = "";
120
124
  const taskBootstrap = new Map();
@@ -122,6 +126,13 @@ export async function createBrowserRealityBridgeServer(options) {
122
126
  const taskCookie = "proflow_tasks_session";
123
127
  const taskBootstrapTtlMs = 60_000;
124
128
  const taskSessionTtlMs = 8 * 60 * 60_000;
129
+ const sessionOnline = () => !closed &&
130
+ session !== undefined &&
131
+ now().getTime() - session.lastHeartbeatAt <= freshnessMs;
132
+ const commandConsumerReady = () => sessionOnline() &&
133
+ lastCommandConsumerAt !== null &&
134
+ now().getTime() - lastCommandConsumerAt <= freshnessMs;
135
+ const online = commandConsumerReady;
125
136
  const pruneTaskWebState = () => {
126
137
  const current = now().getTime();
127
138
  for (const [key, expiresAt] of taskBootstrap)
@@ -186,8 +197,15 @@ export async function createBrowserRealityBridgeServer(options) {
186
197
  approvalApplicationConfigured: true,
187
198
  systemObserver: null,
188
199
  browserCarrier: {
189
- online: session !== undefined && now().getTime() - session.lastHeartbeatAt <= freshnessMs,
200
+ online: commandConsumerReady(),
201
+ sessionOnline: sessionOnline(),
202
+ commandConsumerReady: commandConsumerReady(),
190
203
  extensionInstanceId: session?.extensionInstanceId ?? null,
204
+ queuedCommands: queue.length,
205
+ pendingCommands: pending.size,
206
+ lastCommandPollAt,
207
+ lastCommandDeliveredAt,
208
+ lastCommandResultAt,
191
209
  },
192
210
  },
193
211
  });
@@ -232,18 +250,25 @@ export async function createBrowserRealityBridgeServer(options) {
232
250
  if (!isRecord(body) ||
233
251
  stringField(body, "extensionId") !== options.extensionId)
234
252
  throw new BrowserRealityBridgeError("BRIDGE_AUTH_INVALID", "extension identity mismatch");
253
+ const extensionInstanceId = stringField(body, "extensionInstanceId");
254
+ if (session?.extensionInstanceId !== extensionInstanceId) {
255
+ lastCommandPollAt = null;
256
+ lastCommandDeliveredAt = null;
257
+ lastCommandResultAt = null;
258
+ lastCommandConsumerAt = null;
259
+ }
235
260
  session = {
236
- extensionInstanceId: stringField(body, "extensionInstanceId"),
261
+ extensionInstanceId,
237
262
  lastHeartbeatAt: now().getTime(),
238
263
  };
239
264
  send(response, 200, { accepted: true });
240
265
  return;
241
266
  }
242
267
  if (request.method === "GET" && url.pathname === "/v1/session/status") {
243
- const online = session !== undefined &&
244
- now().getTime() - session.lastHeartbeatAt <= freshnessMs;
245
268
  send(response, 200, {
246
- online,
269
+ online: commandConsumerReady(),
270
+ sessionOnline: sessionOnline(),
271
+ commandConsumerReady: commandConsumerReady(),
247
272
  extensionInstanceId: session?.extensionInstanceId ?? null,
248
273
  });
249
274
  return;
@@ -260,12 +285,16 @@ export async function createBrowserRealityBridgeServer(options) {
260
285
  return;
261
286
  }
262
287
  if (request.method === "GET" && url.pathname === "/v1/commands/next") {
263
- session.lastHeartbeatAt = now().getTime();
288
+ const stamp = now();
289
+ session.lastHeartbeatAt = stamp.getTime();
290
+ lastCommandConsumerAt = stamp.getTime();
291
+ lastCommandPollAt = stamp.toISOString();
264
292
  const command = queue.shift();
265
293
  if (command) {
266
294
  const tracked = pending.get(command.commandId);
267
295
  if (tracked)
268
296
  tracked.stage = "DELIVERED";
297
+ lastCommandDeliveredAt = lastCommandPollAt;
269
298
  }
270
299
  send(response, command ? 200 : 204, command);
271
300
  return;
@@ -280,6 +309,10 @@ export async function createBrowserRealityBridgeServer(options) {
280
309
  throw new BrowserRealityBridgeError("BRIDGE_INPUT_INVALID", "command result is stale or unknown");
281
310
  pending.delete(commandId);
282
311
  clearTimeout(command.timer);
312
+ const resultStamp = now();
313
+ session.lastHeartbeatAt = resultStamp.getTime();
314
+ lastCommandConsumerAt = resultStamp.getTime();
315
+ lastCommandResultAt = resultStamp.toISOString();
283
316
  if (body.ok === true)
284
317
  command.resolve(body.value);
285
318
  else
@@ -311,12 +344,9 @@ export async function createBrowserRealityBridgeServer(options) {
311
344
  if (!address || typeof address === "string")
312
345
  throw new Error("bridge address missing");
313
346
  endpoint = `http://127.0.0.1:${address.port}`;
314
- const online = () => !closed &&
315
- session !== undefined &&
316
- now().getTime() - session.lastHeartbeatAt <= freshnessMs;
317
347
  const requestCommand = (command) => {
318
348
  if (!online())
319
- return Promise.reject(new BrowserRealityBridgeError("BRIDGE_OFFLINE", "extension heartbeat is not fresh"));
349
+ return Promise.reject(new BrowserRealityBridgeError("BRIDGE_OFFLINE", "extension command consumer is not ready"));
320
350
  const commandId = `browser-command:${idFactory()}`;
321
351
  return new Promise((resolve, reject) => {
322
352
  const timer = setTimeout(() => {
@@ -388,9 +418,14 @@ export async function createBrowserRealityBridgeServer(options) {
388
418
  status() {
389
419
  return {
390
420
  online: online(),
421
+ sessionOnline: sessionOnline(),
422
+ commandConsumerReady: commandConsumerReady(),
391
423
  extensionInstanceId: session?.extensionInstanceId ?? null,
392
424
  queuedCommands: queue.length,
393
425
  pendingCommands: pending.size,
426
+ lastCommandPollAt,
427
+ lastCommandDeliveredAt,
428
+ lastCommandResultAt,
394
429
  };
395
430
  },
396
431
  async close() {
@@ -88,9 +88,14 @@ export declare function createBrowserExecutorComposition(options: BrowserExecuto
88
88
  bridgeEndpoint: string;
89
89
  bridgeStatus: () => {
90
90
  online: boolean;
91
+ sessionOnline: boolean;
92
+ commandConsumerReady: boolean;
91
93
  extensionInstanceId: string | null;
92
94
  queuedCommands: number;
93
95
  pendingCommands: number;
96
+ lastCommandPollAt: string | null;
97
+ lastCommandDeliveredAt: string | null;
98
+ lastCommandResultAt: string | null;
94
99
  };
95
100
  close: () => Promise<void>;
96
101
  }>>;
@@ -143,6 +143,7 @@ const extensionInstanceId = `extension:${crypto.randomUUID()}`;
143
143
  const sessions = new Map<number, ContentObservation>();
144
144
  const BROWSER_CARRIER_KEEPALIVE_KEY = "proflowBrowserSnapshot";
145
145
  const BROWSER_CARRIER_KEEPALIVE_MS = 20_000;
146
+ const BROWSER_BRIDGE_FETCH_TIMEOUT_MS = 5_000;
146
147
 
147
148
  const sleep = (milliseconds: number) =>
148
149
  new Promise<void>((resolve) => setTimeout(resolve, milliseconds));
@@ -978,6 +979,7 @@ async function bridgeFetch(
978
979
  ): Promise<Response> {
979
980
  return fetch(`${config.endpoint}${path}`, {
980
981
  ...init,
982
+ signal: init.signal ?? AbortSignal.timeout(BROWSER_BRIDGE_FETCH_TIMEOUT_MS),
981
983
  headers: {
982
984
  authorization: `Bearer ${config.token}`,
983
985
  "content-type": "application/json",
@@ -991,7 +993,7 @@ async function runBridgeLoop() {
991
993
  if (bridgeLoopStarted) return;
992
994
  bridgeLoopStarted = true;
993
995
  while (true) {
994
- const config = await bridgeConfig();
996
+ const config = await bridgeConfig().catch(() => null);
995
997
  if (!config) {
996
998
  await sleep(1_000);
997
999
  continue;
@@ -1006,8 +1008,10 @@ async function runBridgeLoop() {
1006
1008
  }),
1007
1009
  });
1008
1010
  if (!hello.ok) throw new Error("BRIDGE_HELLO_REJECTED");
1009
- let lastHeartbeatAt = 0;
1010
- let lastExtensionKeepaliveAt = 0;
1011
+ // Hello only establishes a session. The first inner-loop action must be
1012
+ // a real command poll so Runtime readiness cannot be granted by hello alone.
1013
+ let lastHeartbeatAt = Date.now();
1014
+ let lastExtensionKeepaliveAt = Date.now();
1011
1015
  while (true) {
1012
1016
  if (
1013
1017
  Date.now() - lastExtensionKeepaliveAt >=
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.33",
4
+ "version": "0.1.35",
5
5
  "permissions": [
6
6
  "tabs",
7
7
  "storage"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-execution-browser-extension",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "Execution-owned MV3 Browser executor, evidence provider and browser application surface.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -27,8 +27,8 @@
27
27
  "SETUP.md"
28
28
  ],
29
29
  "dependencies": {
30
- "@tomflow/proflow-execution-contracts": "^0.1.10",
31
- "@tomflow/proflow-module-contract": "^0.1.13"
30
+ "@tomflow/proflow-module-contract": "^0.1.13",
31
+ "@tomflow/proflow-execution-contracts": "^0.1.10"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@tomflow/proflow-deployment-conformance": "^0.1.13",
@@ -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.33",
6
+ "moduleVersion": "0.1.35",
7
7
  "kind": "browser-extension",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",