@tomflow/proflow-execution-browser-extension 0.1.3 → 0.1.5

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/DOCS.md ADDED
@@ -0,0 +1,29 @@
1
+ # @tomflow/proflow-execution-browser-extension — Module Docs
2
+
3
+ Module ref: `execution-browser-extension`
4
+ Domain: `execution`
5
+ Kind: `browser-extension`
6
+
7
+ Execution-owned MV3 Browser executor, evidence provider and browser application surface.
8
+
9
+ ## Standard management surface
10
+
11
+ `install / uninstall / status / setup / docs / start / stop`
12
+
13
+ ## Provides
14
+
15
+ - `execution-browser-executor` `1.0.0`
16
+
17
+ ## Requires
18
+
19
+ - `execution` `>=1.0.0 <2.0.0`
20
+ - `task-orchestration` `>=1.0.0 <2.0.0`
21
+ - `agent-runtime` `>=1.0.0 <2.0.0`
22
+
23
+ ## Public setup inputs
24
+
25
+ - None. Deterministic/private values and cross-Module shared facts are not public configuration.
26
+
27
+ ## Ownership boundary
28
+
29
+ Module business APIs and any module-specific extra commands remain package-owned. Platform does not proxy or interpret them. Current human/external preparation is documented in `SETUP.md`.
package/SETUP.md ADDED
@@ -0,0 +1,7 @@
1
+ # @tomflow/proflow-execution-browser-extension — Module Setup
2
+
3
+ After Module.install materializes the deployment directory and deterministic local configuration, load the unpacked extension in Chrome when requested. Confirm the real extension Service Worker is running, then rerun Module.setup so it can re-observe Chrome reality and record evidence. Endpoints, tokens and evidence paths are not user configuration.
4
+
5
+ ## Completion
6
+
7
+ Setup is complete only when the Module's own `status` reports `setupStatus=READY`. Re-running setup must re-observe reality; it must not resume from a blind historical step index.
@@ -1,76 +1,130 @@
1
+ import { type ModuleCommandContext } from "@tomflow/proflow-module-contract";
2
+ export declare function browserExtensionLoadDir(workspaceRoot: string): string;
3
+ export declare function materializeProductionConfig(input: {
4
+ moduleRef: string;
5
+ config: Record<string, string>;
6
+ workspaceRoot: string;
7
+ }): Promise<{
8
+ loadDir: string;
9
+ }>;
1
10
  export declare const behaviorAdapter: {
2
- readonly describe: () => {
11
+ readonly install: (context: ModuleCommandContext) => Promise<{
3
12
  result: {
4
13
  contract: "deployment.result.v1";
14
+ ok: true;
15
+ status: "SUCCEEDED";
5
16
  moduleRef: "execution-browser-extension";
6
- moduleVersion: "0.1.3";
7
- ok: boolean;
8
- status: string;
17
+ moduleVersion: "0.1.5";
18
+ data: {
19
+ loadDir: string;
20
+ };
21
+ };
22
+ observedEffects: string[];
23
+ }>;
24
+ readonly uninstall: (_context: ModuleCommandContext) => Promise<{
25
+ result: {
26
+ readonly contract: "deployment.result.v1";
27
+ readonly ok: true;
28
+ readonly status: "SUCCEEDED";
29
+ readonly moduleRef: "execution-browser-extension";
30
+ readonly moduleVersion: "0.1.5";
9
31
  };
10
32
  observedEffects: never[];
11
- };
12
- readonly preflight: () => {
33
+ }>;
34
+ readonly status: (context: ModuleCommandContext) => Promise<{
35
+ result: {
36
+ contract: "deployment.result.v1";
37
+ ok: true;
38
+ status: "SUCCEEDED";
39
+ moduleRef: "execution-browser-extension";
40
+ moduleVersion: "0.1.5";
41
+ data: {
42
+ setupStatus: "ACTION_REQUIRED" | "READY";
43
+ runtimeStatus: "RUNNING" | "STOPPED";
44
+ };
45
+ };
46
+ observedEffects: string[];
47
+ }>;
48
+ readonly setup: (context: ModuleCommandContext) => Promise<{
13
49
  result: {
14
50
  contract: "deployment.result.v1";
15
51
  moduleRef: "execution-browser-extension";
16
- moduleVersion: "0.1.3";
17
- ok: boolean;
18
- status: string;
52
+ moduleVersion: "0.1.5";
53
+ ok: false;
54
+ status: "FAILED";
55
+ error: {
56
+ code: "SETUP_FAILED" | "START_FAILED";
57
+ message: string;
58
+ retryable: boolean;
59
+ };
19
60
  };
20
61
  observedEffects: never[];
21
- };
22
- readonly status: () => {
62
+ } | {
23
63
  result: {
24
64
  contract: "deployment.result.v1";
25
65
  moduleRef: "execution-browser-extension";
26
- moduleVersion: "0.1.3";
27
- ok: boolean;
28
- status: string;
66
+ moduleVersion: "0.1.5";
67
+ ok: false;
68
+ status: "ACTION_REQUIRED";
29
69
  actionRequired: {
30
70
  action: string;
31
71
  description: string;
32
72
  };
33
73
  };
34
74
  observedEffects: never[];
35
- };
36
- readonly verify: () => {
75
+ } | {
76
+ result: {
77
+ readonly contract: "deployment.result.v1";
78
+ readonly ok: true;
79
+ readonly status: "SUCCEEDED";
80
+ readonly moduleRef: "execution-browser-extension";
81
+ readonly moduleVersion: "0.1.5";
82
+ };
83
+ observedEffects: string[];
84
+ }>;
85
+ readonly docs: (_context: ModuleCommandContext) => Promise<{
37
86
  result: {
38
87
  contract: "deployment.result.v1";
88
+ ok: true;
89
+ status: "SUCCEEDED";
39
90
  moduleRef: "execution-browser-extension";
40
- moduleVersion: "0.1.3";
41
- ok: boolean;
42
- status: string;
43
- checks: {
44
- id: string;
45
- status: string;
46
- message: string;
47
- }[];
91
+ moduleVersion: "0.1.5";
92
+ data: {
93
+ docs: string;
94
+ setup: string;
95
+ };
48
96
  };
49
97
  observedEffects: never[];
50
- };
51
- readonly doctor: () => {
98
+ }>;
99
+ readonly start: (context: ModuleCommandContext) => Promise<{
52
100
  result: {
101
+ readonly contract: "deployment.result.v1";
102
+ readonly ok: true;
103
+ readonly status: "SUCCEEDED";
104
+ readonly moduleRef: "execution-browser-extension";
105
+ readonly moduleVersion: "0.1.5";
106
+ } | {
53
107
  contract: "deployment.result.v1";
54
108
  moduleRef: "execution-browser-extension";
55
- moduleVersion: "0.1.3";
56
- ok: boolean;
57
- status: string;
109
+ moduleVersion: "0.1.5";
110
+ ok: false;
111
+ status: "FAILED";
112
+ error: {
113
+ code: "SETUP_FAILED" | "START_FAILED";
114
+ message: string;
115
+ retryable: boolean;
116
+ };
58
117
  };
59
118
  observedEffects: never[];
60
- };
61
- };
62
- export declare function browserExtensionLoadDir(workspaceRoot: string): string;
63
- export declare function materializeProductionConfig(input: {
64
- moduleRef: string;
65
- config: Record<string, string>;
66
- workspaceRoot: string;
67
- }): Promise<{
68
- loadDir: string;
69
- }>;
70
- export declare function createProductionBinding(input: {
71
- moduleRef: string;
72
- config: Record<string, string>;
73
- workspaceRoot: string;
74
- }): {
75
- behaviorAdapter: Record<string, unknown>;
119
+ }>;
120
+ readonly stop: (_context: ModuleCommandContext) => Promise<{
121
+ result: {
122
+ readonly contract: "deployment.result.v1";
123
+ readonly ok: true;
124
+ readonly status: "SUCCEEDED";
125
+ readonly moduleRef: "execution-browser-extension";
126
+ readonly moduleVersion: "0.1.5";
127
+ };
128
+ observedEffects: never[];
129
+ }>;
76
130
  };
@@ -1,78 +1,46 @@
1
1
  import { cp, mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { basename, dirname, join } from "node:path";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { deterministicLoopbackPort, ensureModuleSecretFile, moduleWorkspaceStateDirectory, readModuleSharedFacts, writeModuleSharedFacts, } from "@tomflow/proflow-module-contract";
4
5
  import { descriptor } from "./descriptor.js";
5
6
  const base = {
6
7
  contract: "deployment.result.v1",
8
+ ok: true,
9
+ status: "SUCCEEDED",
7
10
  moduleRef: descriptor.moduleRef,
8
11
  moduleVersion: descriptor.moduleVersion,
9
12
  };
10
- export const behaviorAdapter = {
11
- describe: () => ({
12
- result: { ...base, ok: true, status: "SUCCEEDED" },
13
- observedEffects: [],
14
- }),
15
- preflight: () => ({
16
- result: { ...base, ok: true, status: "SUCCEEDED" },
17
- observedEffects: [],
18
- }),
19
- status: () => ({
20
- result: {
21
- ...base,
22
- ok: false,
23
- status: "ACTION_REQUIRED",
24
- actionRequired: {
25
- action: "verify-real-carrier",
26
- description: "Load in real Chrome and collect ChatGPT E3/E4 evidence",
27
- },
28
- },
29
- observedEffects: [],
30
- }),
31
- verify: () => ({
32
- result: {
33
- ...base,
34
- ok: true,
35
- status: "SUCCEEDED",
36
- checks: [
37
- {
38
- id: "extension-package",
39
- status: "PASS",
40
- message: "The MV3 package is structurally verified; real Carrier readiness remains ACTION_REQUIRED in status",
41
- },
42
- ],
43
- },
44
- observedEffects: [],
45
- }),
46
- doctor: () => ({
47
- result: { ...base, ok: true, status: "SUCCEEDED" },
48
- observedEffects: [],
49
- }),
50
- };
51
- function loopbackEndpoint(value, key) {
52
- const url = new URL(value);
53
- if (url.protocol !== "http:" ||
54
- url.hostname !== "127.0.0.1" ||
55
- url.pathname !== "/" ||
56
- url.search !== "" ||
57
- url.hash !== "") {
58
- throw new Error(`${key} must be a loopback HTTP origin`);
59
- }
60
- return value.replace(/\/$/, "");
61
- }
62
- async function credential(file, key) {
63
- const token = (await readFile(file, "utf8")).trim();
64
- if (token.length < 32)
65
- throw new Error(`${key} must contain at least 32 characters`);
66
- return token;
67
- }
13
+ const factString = (facts, name) => (typeof facts?.[name] === "string" ? String(facts[name]) : undefined);
68
14
  function packageRoot() {
69
15
  const candidate = dirname(dirname(fileURLToPath(import.meta.url)));
70
16
  return basename(candidate) === "dist" ? dirname(candidate) : candidate;
71
17
  }
72
18
  export function browserExtensionLoadDir(workspaceRoot) {
73
- return join(workspaceRoot, ".proflow", "deployment", "browser-extension", "execution-browser-extension");
19
+ return join(resolve(workspaceRoot), ".proflow", "deployment", "browser-extension", "execution-browser-extension");
20
+ }
21
+ const stateDir = (context) => moduleWorkspaceStateDirectory(context, descriptor.moduleRef);
22
+ const setupFile = (context) => join(stateDir(context), "setup.json");
23
+ const verificationFile = (context) => join(stateDir(context), "verification.json");
24
+ const executorConfigFile = (context) => join(stateDir(context), "browser-executor.json");
25
+ async function credential(file) {
26
+ const value = (await readFile(file, "utf8")).trim();
27
+ if (value.length < 32)
28
+ throw new Error(`credential ${file} is invalid`);
29
+ return value;
30
+ }
31
+ function loopbackEndpoint(value, name) {
32
+ const url = new URL(value);
33
+ if (url.protocol !== "http:" ||
34
+ url.hostname !== "127.0.0.1" ||
35
+ url.pathname !== "/" ||
36
+ url.search ||
37
+ url.hash)
38
+ throw new Error(`${name} must be a loopback HTTP origin`);
39
+ return value.replace(/\/$/, "");
74
40
  }
75
41
  export async function materializeProductionConfig(input) {
42
+ const context = { workspaceRoot: input.workspaceRoot };
43
+ const loadDir = await installPackage(context);
76
44
  const required = [
77
45
  "bridge.endpoint",
78
46
  "bridge.token",
@@ -81,31 +49,31 @@ export async function materializeProductionConfig(input) {
81
49
  "approvalApplication.endpoint",
82
50
  "approvalApplication.token",
83
51
  ];
84
- for (const key of required) {
85
- if (!input.config[key])
86
- throw new Error(`missing required browser config ${key}`);
87
- }
52
+ for (const name of required)
53
+ if (!input.config[name])
54
+ throw new Error(`missing required browser config ${name}`);
88
55
  const runtimeConfig = {
89
56
  proflowRuntimeBridge: {
90
57
  endpoint: loopbackEndpoint(input.config["bridge.endpoint"] ?? "", "bridge.endpoint"),
91
- token: await credential(input.config["bridge.token"] ?? "", "bridge.token"),
58
+ token: await credential(input.config["bridge.token"] ?? ""),
92
59
  },
93
60
  proflowTaskApplication: {
94
61
  endpoint: loopbackEndpoint(input.config["taskApplication.endpoint"] ?? "", "taskApplication.endpoint"),
95
- token: await credential(input.config["taskApplication.token"] ?? "", "taskApplication.token"),
62
+ token: await credential(input.config["taskApplication.token"] ?? ""),
96
63
  },
97
64
  proflowApprovalApplication: {
98
65
  endpoint: loopbackEndpoint(input.config["approvalApplication.endpoint"] ?? "", "approvalApplication.endpoint"),
99
- token: await credential(input.config["approvalApplication.token"] ?? "", "approvalApplication.token"),
66
+ token: await credential(input.config["approvalApplication.token"] ?? ""),
100
67
  },
101
68
  };
69
+ await writeFile(join(loadDir, "runtime-config.json"), `${JSON.stringify(runtimeConfig, null, 2)}\n`, { mode: 0o600 });
70
+ return { loadDir };
71
+ }
72
+ async function installPackage(context) {
102
73
  const sourceRoot = packageRoot();
103
- const loadDir = browserExtensionLoadDir(input.workspaceRoot);
74
+ const loadDir = browserExtensionLoadDir(context.workspaceRoot);
104
75
  await mkdir(join(loadDir, "dist"), { recursive: true });
105
- await cp(join(sourceRoot, "dist", "extension"), join(loadDir, "dist", "extension"), {
106
- recursive: true,
107
- force: true,
108
- });
76
+ await cp(join(sourceRoot, "dist", "extension"), join(loadDir, "dist", "extension"), { recursive: true, force: true });
109
77
  await cp(join(sourceRoot, "extension"), join(loadDir, "extension"), {
110
78
  recursive: true,
111
79
  force: true,
@@ -113,112 +81,221 @@ export async function materializeProductionConfig(input) {
113
81
  await cp(join(sourceRoot, "manifest.json"), join(loadDir, "manifest.json"), {
114
82
  force: true,
115
83
  });
116
- await writeFile(join(loadDir, "runtime-config.json"), `${JSON.stringify(runtimeConfig, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
117
- return { loadDir };
84
+ return loadDir;
118
85
  }
119
- async function readBrowserVerificationEvidence(file, expectedLoadDir) {
120
- if (!file)
86
+ async function readSetup(context) {
87
+ try {
88
+ const raw = JSON.parse(await readFile(setupFile(context), "utf8"));
89
+ return typeof raw.extensionId === "string" &&
90
+ /^[a-z]{32}$/.test(raw.extensionId)
91
+ ? raw
92
+ : undefined;
93
+ }
94
+ catch {
121
95
  return undefined;
96
+ }
97
+ }
98
+ async function readEvidence(context, loadDir) {
122
99
  try {
123
- const raw = JSON.parse(await readFile(file, "utf8"));
124
- if (raw.contract !== "proflow.browser-extension-verification.v1" ||
125
- raw.moduleVersion !== descriptor.moduleVersion ||
126
- raw.loadDir !== expectedLoadDir ||
127
- typeof raw.extensionId !== "string" ||
128
- raw.extensionId.length < 16 ||
129
- raw.serviceWorker !== "RUNNING" ||
130
- typeof raw.observedAt !== "string" ||
131
- Number.isNaN(Date.parse(raw.observedAt)))
132
- return undefined;
133
- return raw;
100
+ const raw = JSON.parse(await readFile(verificationFile(context), "utf8"));
101
+ return raw.contract === "proflow.browser-extension-verification.v1" &&
102
+ raw.moduleVersion === descriptor.moduleVersion &&
103
+ raw.loadDir === loadDir &&
104
+ typeof raw.extensionId === "string" &&
105
+ /^[a-z]{32}$/.test(raw.extensionId) &&
106
+ raw.serviceWorker === "RUNNING" &&
107
+ typeof raw.observedAt === "string" &&
108
+ !Number.isNaN(Date.parse(raw.observedAt))
109
+ ? raw
110
+ : undefined;
134
111
  }
135
112
  catch {
136
113
  return undefined;
137
114
  }
138
115
  }
139
- export function createProductionBinding(input) {
140
- const loadDir = browserExtensionLoadDir(input.workspaceRoot);
141
- const verificationEvidenceFile = input.config.verificationEvidenceFile;
142
- const boundBase = {
143
- contract: "deployment.result.v1",
144
- moduleRef: descriptor.moduleRef,
145
- moduleVersion: descriptor.moduleVersion,
116
+ function input(context) {
117
+ const value = context.input;
118
+ if (typeof value !== "object" || value === null || Array.isArray(value))
119
+ return {};
120
+ const extensionId = Reflect.get(value, "extensionId"), serviceWorker = Reflect.get(value, "serviceWorker"), observedAt = Reflect.get(value, "observedAt");
121
+ return {
122
+ ...(typeof extensionId === "string" ? { extensionId } : {}),
123
+ ...(serviceWorker === "RUNNING"
124
+ ? { serviceWorker: "RUNNING" }
125
+ : {}),
126
+ ...(typeof observedAt === "string" ? { observedAt } : {}),
146
127
  };
147
- const configured = async () => {
148
- try {
149
- const raw = JSON.parse(await readFile(join(loadDir, "runtime-config.json"), "utf8"));
150
- return typeof raw === "object" && raw !== null;
151
- }
152
- catch {
153
- return false;
154
- }
128
+ }
129
+ async function ownFacts(context) {
130
+ await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
131
+ const loadDir = browserExtensionLoadDir(context.workspaceRoot);
132
+ const bridgeTokenFile = await ensureModuleSecretFile(context, descriptor.moduleRef, "bridge");
133
+ const bridgePort = deterministicLoopbackPort(context, descriptor.moduleRef, "bridge");
134
+ const facts = {
135
+ loadDir,
136
+ bridgeTokenFile,
137
+ bridgeEndpoint: `http://127.0.0.1:${bridgePort}`,
138
+ verificationEvidenceFile: verificationFile(context),
155
139
  };
156
- return {
157
- behaviorAdapter: {
158
- ...behaviorAdapter,
159
- preflight: async () => ({
160
- result: (await configured())
161
- ? { ...boundBase, ok: true, status: "SUCCEEDED" }
162
- : {
163
- ...boundBase,
164
- ok: false,
165
- status: "ACTION_REQUIRED",
166
- actionRequired: {
167
- action: "configure-browser-extension",
168
- description: `Materialize Browser Extension runtime config before loading ${loadDir}`,
169
- },
140
+ const setup = await readSetup(context);
141
+ if (setup)
142
+ facts.extensionId = setup.extensionId;
143
+ try {
144
+ await readFile(executorConfigFile(context), "utf8");
145
+ facts.browserExecutorConfigPath = executorConfigFile(context);
146
+ }
147
+ catch { }
148
+ await writeModuleSharedFacts(context, descriptor.moduleRef, facts);
149
+ return facts;
150
+ }
151
+ async function materialize(context, setup) {
152
+ const host = await readModuleSharedFacts(context, "platform-host");
153
+ const endpoint = factString(host, "endpoint"), taskTokenFile = factString(host, "taskApplicationTokenFile"), approvalTokenFile = factString(host, "approvalApplicationTokenFile");
154
+ if (!endpoint || !taskTokenFile || !approvalTokenFile)
155
+ throw new Error("platform-host application shared facts are unavailable");
156
+ const facts = await ownFacts(context);
157
+ const loadDir = String(facts.loadDir);
158
+ const bridgeTokenFile = String(facts.bridgeTokenFile);
159
+ const bridgeEndpoint = String(facts.bridgeEndpoint);
160
+ const runtimeConfig = {
161
+ proflowRuntimeBridge: {
162
+ endpoint: bridgeEndpoint,
163
+ token: await credential(bridgeTokenFile),
164
+ },
165
+ proflowTaskApplication: {
166
+ endpoint,
167
+ token: await credential(taskTokenFile),
168
+ },
169
+ proflowApprovalApplication: {
170
+ endpoint,
171
+ token: await credential(approvalTokenFile),
172
+ },
173
+ };
174
+ await writeFile(join(loadDir, "runtime-config.json"), `${JSON.stringify(runtimeConfig, null, 2)}\n`, { mode: 0o600 });
175
+ await writeFile(executorConfigFile(context), `${JSON.stringify({ platformHost: { endpoint, tokenFile: taskTokenFile }, bridge: { extensionId: setup.extensionId, tokenFile: bridgeTokenFile, host: "127.0.0.1", port: Number(new URL(bridgeEndpoint).port) } }, null, 2)}\n`, { mode: 0o600 });
176
+ await writeModuleSharedFacts(context, descriptor.moduleRef, {
177
+ ...facts,
178
+ extensionId: setup.extensionId,
179
+ browserExecutorConfigPath: executorConfigFile(context),
180
+ });
181
+ }
182
+ const failed = (code, message, retryable = true) => ({
183
+ ...base,
184
+ ok: false,
185
+ status: "FAILED",
186
+ error: { code, message, retryable },
187
+ });
188
+ export const behaviorAdapter = {
189
+ install: async (context) => {
190
+ const loadDir = await installPackage(context);
191
+ return {
192
+ result: { ...base, data: { ...(await ownFacts(context)), loadDir } },
193
+ observedEffects: ["Materialize the unpacked MV3 extension package"],
194
+ };
195
+ },
196
+ uninstall: async (_context) => ({
197
+ result: base,
198
+ observedEffects: [],
199
+ }),
200
+ status: async (context) => {
201
+ const loadDir = browserExtensionLoadDir(context.workspaceRoot);
202
+ const setup = await readSetup(context);
203
+ const evidence = await readEvidence(context, loadDir);
204
+ return {
205
+ result: {
206
+ ...base,
207
+ data: {
208
+ setupStatus: setup && evidence
209
+ ? "READY"
210
+ : "ACTION_REQUIRED",
211
+ runtimeStatus: evidence ? "RUNNING" : "STOPPED",
212
+ },
213
+ },
214
+ observedEffects: evidence
215
+ ? ["Observes real Chrome MV3 load evidence"]
216
+ : [],
217
+ };
218
+ },
219
+ setup: async (context) => {
220
+ await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
221
+ const supplied = input(context);
222
+ let setup = await readSetup(context);
223
+ if (supplied.extensionId) {
224
+ if (!/^[a-z]{32}$/.test(supplied.extensionId))
225
+ return {
226
+ result: failed("SETUP_FAILED", "extensionId must be a canonical 32-character Chromium extension id", false),
227
+ observedEffects: [],
228
+ };
229
+ setup = { extensionId: supplied.extensionId };
230
+ await writeFile(setupFile(context), `${JSON.stringify(setup, null, 2)}\n`, { mode: 0o600 });
231
+ }
232
+ if (!setup)
233
+ return {
234
+ result: {
235
+ ...base,
236
+ ok: false,
237
+ status: "ACTION_REQUIRED",
238
+ actionRequired: {
239
+ action: "load-unpacked-extension",
240
+ description: `Load ${browserExtensionLoadDir(context.workspaceRoot)} in Chrome, copy its extensionId, then rerun Module.setup with extensionId.`,
170
241
  },
242
+ },
171
243
  observedEffects: [],
172
- }),
173
- status: async () => ({
174
- result: (await configured())
175
- ? {
176
- ...boundBase,
177
- ok: true,
178
- status: "SUCCEEDED",
179
- data: { loadDir, configMaterialized: true },
180
- }
181
- : {
182
- ...boundBase,
183
- ok: false,
184
- status: "ACTION_REQUIRED",
185
- actionRequired: {
186
- action: "configure-browser-extension",
187
- description: "Browser Extension runtime config is not materialized",
188
- },
189
- },
244
+ };
245
+ try {
246
+ await materialize(context, setup);
247
+ }
248
+ catch (error) {
249
+ return {
250
+ result: failed("SETUP_FAILED", error instanceof Error
251
+ ? error.message
252
+ : "browser extension config materialization failed"),
190
253
  observedEffects: [],
191
- }),
192
- verify: async () => {
193
- const evidence = await readBrowserVerificationEvidence(verificationEvidenceFile, loadDir);
194
- return {
195
- result: (await configured()) && evidence
196
- ? {
197
- ...boundBase,
198
- ok: true,
199
- status: "SUCCEEDED",
200
- checks: [
201
- {
202
- id: "real-carrier-e3-e4",
203
- status: "PASS",
204
- message: `Real Chrome loaded extension ${evidence.extensionId} from ${loadDir} and its MV3 Service Worker was observed running`,
205
- },
206
- ],
207
- }
208
- : {
209
- ...boundBase,
210
- ok: false,
211
- status: "ACTION_REQUIRED",
212
- actionRequired: {
213
- action: "verify-real-chrome-extension",
214
- description: `Load ${loadDir} in real Chrome and write verified evidence to ${verificationEvidenceFile ?? "the configured verificationEvidenceFile"}`,
215
- },
216
- },
217
- observedEffects: evidence
218
- ? ["Observes real Chrome MV3 load evidence"]
219
- : [],
220
- };
254
+ };
255
+ }
256
+ if (supplied.serviceWorker === "RUNNING") {
257
+ const evidence = {
258
+ contract: "proflow.browser-extension-verification.v1",
259
+ moduleVersion: descriptor.moduleVersion,
260
+ loadDir: browserExtensionLoadDir(context.workspaceRoot),
261
+ extensionId: setup.extensionId,
262
+ serviceWorker: "RUNNING",
263
+ observedAt: supplied.observedAt ?? new Date().toISOString(),
264
+ };
265
+ await writeFile(verificationFile(context), `${JSON.stringify(evidence, null, 2)}\n`, { mode: 0o600 });
266
+ return {
267
+ result: base,
268
+ observedEffects: ["Records real Chrome MV3 load evidence"],
269
+ };
270
+ }
271
+ return {
272
+ result: {
273
+ ...base,
274
+ ok: false,
275
+ status: "ACTION_REQUIRED",
276
+ actionRequired: {
277
+ action: "reload-and-verify-extension",
278
+ description: "Reload the unpacked extension after runtime config materialization, confirm its MV3 service worker is RUNNING, then rerun Module.setup with serviceWorker=RUNNING.",
279
+ },
221
280
  },
222
- },
223
- };
224
- }
281
+ observedEffects: [],
282
+ };
283
+ },
284
+ docs: async (_context) => ({
285
+ result: { ...base, data: { docs: "DOCS.md", setup: "SETUP.md" } },
286
+ observedEffects: [],
287
+ }),
288
+ start: async (context) => {
289
+ const loadDir = browserExtensionLoadDir(context.workspaceRoot);
290
+ return {
291
+ result: (await readEvidence(context, loadDir))
292
+ ? base
293
+ : failed("START_FAILED", "browser extension setup is not READY"),
294
+ observedEffects: [],
295
+ };
296
+ },
297
+ stop: async (_context) => ({
298
+ result: base,
299
+ observedEffects: [],
300
+ }),
301
+ };
@@ -3,11 +3,10 @@ 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.3";
6
+ readonly moduleVersion: "0.1.5";
7
7
  readonly kind: "browser-extension";
8
8
  readonly templateVersion: "1.0.0";
9
9
  readonly platformCompatibility: ">=1.0.0 <2.0.0";
10
- readonly installClass: "core";
11
10
  readonly identity: {
12
11
  readonly domain: "execution";
13
12
  readonly summary: "Execution-owned MV3 Browser executor, evidence provider and browser application surface.";
@@ -34,75 +33,14 @@ export declare const descriptor: {
34
33
  readonly kind: "human";
35
34
  readonly action: "Load and verify the unpacked MV3 extension in the real Chrome profile";
36
35
  }];
37
- readonly configSlots: readonly [{
38
- readonly key: "bridge.endpoint";
39
- readonly type: "url";
40
- readonly required: true;
41
- readonly description: "Loopback Browser Reality Bridge endpoint";
42
- }, {
43
- readonly key: "bridge.token";
44
- readonly type: "path";
45
- readonly required: true;
46
- readonly sensitive: true;
47
- readonly description: "File containing the Browser Reality Bridge extension token";
48
- }, {
49
- readonly key: "taskApplication.endpoint";
50
- readonly type: "url";
51
- readonly required: true;
52
- readonly description: "Loopback Platform Host Task application endpoint";
53
- }, {
54
- readonly key: "taskApplication.token";
55
- readonly type: "path";
56
- readonly required: true;
57
- readonly sensitive: true;
58
- readonly description: "File containing the Platform Host Task application token";
59
- }, {
60
- readonly key: "approvalApplication.endpoint";
61
- readonly type: "url";
62
- readonly required: true;
63
- readonly description: "Loopback Platform Host Approval application endpoint";
64
- }, {
65
- readonly key: "approvalApplication.token";
66
- readonly type: "path";
67
- readonly required: true;
68
- readonly sensitive: true;
69
- readonly description: "File containing the Platform Host Approval application token";
70
- }, {
71
- readonly key: "verificationEvidenceFile";
72
- readonly type: "path";
73
- readonly required: true;
74
- readonly description: "JSON evidence file written after real Chrome loads the Deployment-managed MV3 extension and its Service Worker runs";
75
- }, {
76
- readonly key: "chromeRuntimeModuleRef";
77
- readonly type: "moduleRef";
78
- readonly required: false;
79
- readonly description: "External resource module governing the Chrome runtime";
80
- readonly default: "chrome-runtime";
81
- }, {
82
- readonly key: "carrierModuleRef";
83
- readonly type: "moduleRef";
84
- readonly required: false;
85
- readonly description: "External resource module governing the Custom GPT carrier";
86
- readonly default: "chatgpt-carrier";
87
- }];
88
- readonly lifecycle: {
89
- readonly supported: readonly ["describe", "preflight", "status", "verify", "doctor"];
90
- };
91
- readonly verification: {
92
- readonly checks: readonly [{
93
- readonly id: "real-carrier-e3-e4";
94
- readonly description: "Real Chrome and ChatGPT E3/E4 evidence is present";
95
- readonly lifecycle: "verify";
96
- }];
97
- };
36
+ readonly configSlots: readonly [];
98
37
  readonly effects: readonly [{
99
38
  readonly kind: "external-resource";
100
- readonly description: "Package an MV3 extension that performs Execution-authorized Browser effects";
39
+ readonly description: "Materialize the unpacked MV3 extension package";
101
40
  readonly retention: "preserve";
102
41
  }];
103
- readonly documentation: readonly [{
104
- readonly id: "overview";
105
- readonly path: "./README.md";
106
- readonly description: "Package-owned module overview";
107
- }];
42
+ readonly documentation: {
43
+ readonly docs: "DOCS.md";
44
+ readonly setup: "SETUP.md";
45
+ };
108
46
  };
@@ -3,113 +3,55 @@ 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.3",
6
+ moduleVersion: "0.1.5",
7
7
  kind: "browser-extension",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
10
- installClass: "core",
11
10
  identity: {
12
11
  domain: "execution",
13
12
  summary: "Execution-owned MV3 Browser executor, evidence provider and browser application surface.",
14
13
  },
15
- provides: [{ contractRef: "execution-browser-executor", version: "1.0.0" }],
16
- requires: [
17
- { contractRef: "execution", versionRange: ">=1.0.0 <2.0.0" },
18
- { contractRef: "task-orchestration", versionRange: ">=1.0.0 <2.0.0" },
19
- { contractRef: "agent-runtime", versionRange: ">=1.0.0 <2.0.0" },
20
- ],
21
- requirements: [
22
- { kind: "runtime", runtime: "browser", versionRange: ">=1" },
14
+ provides: [
23
15
  {
24
- kind: "human",
25
- action: "Load and verify the unpacked MV3 extension in the real Chrome profile",
16
+ contractRef: "execution-browser-executor",
17
+ version: "1.0.0",
26
18
  },
27
19
  ],
28
- configSlots: [
29
- {
30
- key: "bridge.endpoint",
31
- type: "url",
32
- required: true,
33
- description: "Loopback Browser Reality Bridge endpoint",
34
- },
35
- {
36
- key: "bridge.token",
37
- type: "path",
38
- required: true,
39
- sensitive: true,
40
- description: "File containing the Browser Reality Bridge extension token",
41
- },
42
- {
43
- key: "taskApplication.endpoint",
44
- type: "url",
45
- required: true,
46
- description: "Loopback Platform Host Task application endpoint",
47
- },
48
- {
49
- key: "taskApplication.token",
50
- type: "path",
51
- required: true,
52
- sensitive: true,
53
- description: "File containing the Platform Host Task application token",
54
- },
20
+ requires: [
55
21
  {
56
- key: "approvalApplication.endpoint",
57
- type: "url",
58
- required: true,
59
- description: "Loopback Platform Host Approval application endpoint",
22
+ contractRef: "execution",
23
+ versionRange: ">=1.0.0 <2.0.0",
60
24
  },
61
25
  {
62
- key: "approvalApplication.token",
63
- type: "path",
64
- required: true,
65
- sensitive: true,
66
- description: "File containing the Platform Host Approval application token",
26
+ contractRef: "task-orchestration",
27
+ versionRange: ">=1.0.0 <2.0.0",
67
28
  },
68
29
  {
69
- key: "verificationEvidenceFile",
70
- type: "path",
71
- required: true,
72
- description: "JSON evidence file written after real Chrome loads the Deployment-managed MV3 extension and its Service Worker runs",
30
+ contractRef: "agent-runtime",
31
+ versionRange: ">=1.0.0 <2.0.0",
73
32
  },
33
+ ],
34
+ requirements: [
74
35
  {
75
- key: "chromeRuntimeModuleRef",
76
- type: "moduleRef",
77
- required: false,
78
- description: "External resource module governing the Chrome runtime",
79
- default: "chrome-runtime",
36
+ kind: "runtime",
37
+ runtime: "browser",
38
+ versionRange: ">=1",
80
39
  },
81
40
  {
82
- key: "carrierModuleRef",
83
- type: "moduleRef",
84
- required: false,
85
- description: "External resource module governing the Custom GPT carrier",
86
- default: "chatgpt-carrier",
41
+ kind: "human",
42
+ action: "Load and verify the unpacked MV3 extension in the real Chrome profile",
87
43
  },
88
44
  ],
89
- lifecycle: {
90
- supported: ["describe", "preflight", "status", "verify", "doctor"],
91
- },
92
- verification: {
93
- checks: [
94
- {
95
- id: "real-carrier-e3-e4",
96
- description: "Real Chrome and ChatGPT E3/E4 evidence is present",
97
- lifecycle: "verify",
98
- },
99
- ],
100
- },
45
+ configSlots: [],
101
46
  effects: [
102
47
  {
103
48
  kind: "external-resource",
104
- description: "Package an MV3 extension that performs Execution-authorized Browser effects",
49
+ description: "Materialize the unpacked MV3 extension package",
105
50
  retention: "preserve",
106
51
  },
107
52
  ],
108
- documentation: [
109
- {
110
- id: "overview",
111
- path: "./README.md",
112
- description: "Package-owned module overview",
113
- },
114
- ],
53
+ documentation: {
54
+ docs: "DOCS.md",
55
+ setup: "SETUP.md",
56
+ },
115
57
  };
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ export declare function materializeBrowserExtensionConfig(workspaceRoot: string): Promise<{
3
+ loadDir: string;
4
+ }>;
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { materializeProductionConfig } from "../deployment/adapter.js";
5
+ function workspaceFromArgs(args) {
6
+ if (args[0] !== "materialize-config") {
7
+ throw new Error("Usage: proflow-execution-browser-extension materialize-config [--workspace /absolute/path]");
8
+ }
9
+ const workspaceIndex = args.indexOf("--workspace");
10
+ if (workspaceIndex < 0)
11
+ return process.cwd();
12
+ const value = args[workspaceIndex + 1];
13
+ if (!value || args.length !== 3) {
14
+ throw new Error("Usage: proflow-execution-browser-extension materialize-config [--workspace /absolute/path]");
15
+ }
16
+ return resolve(value);
17
+ }
18
+ export async function materializeBrowserExtensionConfig(workspaceRoot) {
19
+ const configPath = resolve(workspaceRoot, ".proflow", "config", "execution-browser-extension.json");
20
+ const parsed = JSON.parse(await readFile(configPath, "utf8"));
21
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
22
+ throw new Error("execution-browser-extension config must be a JSON object");
23
+ }
24
+ const config = Object.fromEntries(Object.entries(parsed).map(([key, value]) => {
25
+ if (typeof value !== "string") {
26
+ throw new Error(`browser extension config ${key} must be a string`);
27
+ }
28
+ return [key, value];
29
+ }));
30
+ return materializeProductionConfig({
31
+ moduleRef: "execution-browser-extension",
32
+ config,
33
+ workspaceRoot,
34
+ });
35
+ }
36
+ async function main() {
37
+ const workspaceRoot = workspaceFromArgs(process.argv.slice(2));
38
+ const result = await materializeBrowserExtensionConfig(workspaceRoot);
39
+ process.stdout.write(`${JSON.stringify({ status: "MATERIALIZED", ...result })}\n`);
40
+ }
41
+ if (import.meta.main)
42
+ await main();
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.3",
4
+ "version": "0.1.5",
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.3",
3
+ "version": "0.1.5",
4
4
  "description": "Execution-owned MV3 Browser executor, evidence provider and browser application surface.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -11,7 +11,8 @@
11
11
  "./bridge": "./dist/src/bridge.js",
12
12
  "./deployment/adapter": "./dist/deployment/adapter.js",
13
13
  "./deployment/descriptor": "./dist/deployment/descriptor.js",
14
- "./runtime-composition": "./dist/src/runtime-composition.js"
14
+ "./runtime-composition": "./dist/src/runtime-composition.js",
15
+ "./configure": "./dist/src/configure.js"
15
16
  },
16
17
  "files": [
17
18
  "dist",
@@ -21,15 +22,16 @@
21
22
  "proflow.module.json",
22
23
  "conformance.json",
23
24
  "README.md",
24
- "self-install.mjs"
25
+ "DOCS.md",
26
+ "SETUP.md"
25
27
  ],
26
28
  "dependencies": {
27
- "@tomflow/proflow-execution-contracts": "^0.1.2"
29
+ "@tomflow/proflow-execution-contracts": "^0.1.4",
30
+ "@tomflow/proflow-module-contract": "^0.1.4"
28
31
  },
29
32
  "devDependencies": {
30
- "@tomflow/proflow-module-contract": "^0.1.1",
31
- "@tomflow/proflow-deployment-conformance": "^0.1.2",
32
- "@tomflow/proflow-execution-runtime": "^0.1.2"
33
+ "@tomflow/proflow-execution-runtime": "^0.1.7",
34
+ "@tomflow/proflow-deployment-conformance": "^0.1.4"
33
35
  },
34
36
  "keywords": [
35
37
  "proflow",
@@ -38,20 +40,11 @@
38
40
  ],
39
41
  "proflow": {
40
42
  "module": true,
41
- "installClass": "core",
42
- "installRequires": [
43
- "@tomflow/proflow-agent-runtime",
44
- "@tomflow/proflow-chatgpt-carrier",
45
- "@tomflow/proflow-chrome-runtime",
46
- "@tomflow/proflow-execution-contracts",
47
- "@tomflow/proflow-execution-runtime",
48
- "@tomflow/proflow-task-orchestration"
49
- ],
50
43
  "descriptor": "./dist/deployment/descriptor.js",
51
44
  "manifest": "./proflow.module.json"
52
45
  },
53
46
  "bin": {
54
- "proflow-execution-browser-extension": "./self-install.mjs"
47
+ "proflow-execution-browser-extension": "./dist/src/configure.js"
55
48
  },
56
49
  "scripts": {
57
50
  "test": "node --test tests/**/*.test.ts",
@@ -3,11 +3,10 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "execution-browser-extension",
5
5
  "packageName": "@tomflow/proflow-execution-browser-extension",
6
- "moduleVersion": "0.1.3",
6
+ "moduleVersion": "0.1.5",
7
7
  "kind": "browser-extension",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",
10
- "installClass": "core",
11
10
  "identity": {
12
11
  "domain": "execution",
13
12
  "summary": "Execution-owned MV3 Browser executor, evidence provider and browser application surface."
@@ -43,91 +42,16 @@
43
42
  "action": "Load and verify the unpacked MV3 extension in the real Chrome profile"
44
43
  }
45
44
  ],
46
- "configSlots": [
47
- {
48
- "key": "bridge.endpoint",
49
- "type": "url",
50
- "required": true,
51
- "description": "Loopback Browser Reality Bridge endpoint"
52
- },
53
- {
54
- "key": "bridge.token",
55
- "type": "path",
56
- "required": true,
57
- "sensitive": true,
58
- "description": "File containing the Browser Reality Bridge extension token"
59
- },
60
- {
61
- "key": "taskApplication.endpoint",
62
- "type": "url",
63
- "required": true,
64
- "description": "Loopback Platform Host Task application endpoint"
65
- },
66
- {
67
- "key": "taskApplication.token",
68
- "type": "path",
69
- "required": true,
70
- "sensitive": true,
71
- "description": "File containing the Platform Host Task application token"
72
- },
73
- {
74
- "key": "approvalApplication.endpoint",
75
- "type": "url",
76
- "required": true,
77
- "description": "Loopback Platform Host Approval application endpoint"
78
- },
79
- {
80
- "key": "approvalApplication.token",
81
- "type": "path",
82
- "required": true,
83
- "sensitive": true,
84
- "description": "File containing the Platform Host Approval application token"
85
- },
86
- {
87
- "key": "verificationEvidenceFile",
88
- "type": "path",
89
- "required": true,
90
- "description": "JSON evidence file written after real Chrome loads the Deployment-managed MV3 extension and its Service Worker runs"
91
- },
92
- {
93
- "key": "chromeRuntimeModuleRef",
94
- "type": "moduleRef",
95
- "required": false,
96
- "description": "External resource module governing the Chrome runtime",
97
- "default": "chrome-runtime"
98
- },
99
- {
100
- "key": "carrierModuleRef",
101
- "type": "moduleRef",
102
- "required": false,
103
- "description": "External resource module governing the Custom GPT carrier",
104
- "default": "chatgpt-carrier"
105
- }
106
- ],
107
- "lifecycle": {
108
- "supported": ["describe", "preflight", "status", "verify", "doctor"]
109
- },
110
- "verification": {
111
- "checks": [
112
- {
113
- "id": "real-carrier-e3-e4",
114
- "description": "Real Chrome and ChatGPT E3/E4 evidence is present",
115
- "lifecycle": "verify"
116
- }
117
- ]
118
- },
45
+ "configSlots": [],
119
46
  "effects": [
120
47
  {
121
48
  "kind": "external-resource",
122
- "description": "Package an MV3 extension that performs Execution-authorized Browser effects",
49
+ "description": "Materialize the unpacked MV3 extension package",
123
50
  "retention": "preserve"
124
51
  }
125
52
  ],
126
- "documentation": [
127
- {
128
- "id": "overview",
129
- "path": "./README.md",
130
- "description": "Package-owned module overview"
131
- }
132
- ]
53
+ "documentation": {
54
+ "docs": "DOCS.md",
55
+ "setup": "SETUP.md"
56
+ }
133
57
  }
package/CHANGELOG.md DELETED
@@ -1,19 +0,0 @@
1
- # @tomflow/proflow-execution-browser-extension
2
-
3
- ## 0.1.3
4
-
5
- ### Patch Changes
6
-
7
- - Real-1 final remediation and black-box release closure
8
-
9
- ## 0.1.2
10
-
11
- ### Patch Changes
12
-
13
- - Close the Real-1 global Platform control-plane contract across every published ProFlow package. Platform CLI now owns one durable global Workspace binding with canonical identity, cross-process operation locking, cwd/`--workspace` install targeting, cross-directory instance commands, whole-instance uninstall/rebind, and deterministic npm/yarn/pnpm Workspace package-manager selection. Every package-owned install entry, including newly generated Module Template packages, delegates to the Shell-global `platform` command and fails closed when that global CLI is unavailable; Deployment Conformance and the formal test plans mechanically enforce the same rule across all 24 packages.
14
-
15
- ## 0.1.1
16
-
17
- ### Patch Changes
18
-
19
- - Close Real-1 deployment graph and Fresh Workspace bootstrap gaps: remove Contract-library runtime-provider aliases, correct Execution/Model dependency direction, materialize logical/moduleRef providers through `installRequires`, enforce repository graph validation, and keep self-install executables stable without package-manager worktree mutation.
package/self-install.mjs DELETED
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
- import { spawnSync } from "node:child_process";
3
-
4
- const [command, ...rest] = process.argv.slice(2);
5
- const usage =
6
- "Usage: npx @tomflow/proflow-execution-browser-extension install\n";
7
- if (command === "--help" || command === "-h") {
8
- process.stdout.write(usage);
9
- process.exit(0);
10
- }
11
- if (command !== "install" || rest.length > 0) {
12
- process.stderr.write(usage);
13
- process.exit(2);
14
- }
15
- const executable = process.platform === "win32" ? "platform.cmd" : "platform";
16
- const result = spawnSync(
17
- executable,
18
- [
19
- "install",
20
- "@tomflow/proflow-execution-browser-extension",
21
- "--workspace",
22
- process.cwd(),
23
- ],
24
- { cwd: process.cwd(), env: process.env, stdio: "inherit" },
25
- );
26
- if (result.error) {
27
- if (result.error.code === "ENOENT") {
28
- process.stderr.write(
29
- "GLOBAL_PLATFORM_CLI_REQUIRED: install @tomflow/proflow-platform-cli globally before package-owned install\n",
30
- );
31
- process.exit(127);
32
- }
33
- throw result.error;
34
- }
35
- process.exit(result.status ?? 1);