@tomflow/proflow-execution-browser-extension 0.1.4 → 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,77 +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.4";
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<{
13
35
  result: {
14
36
  contract: "deployment.result.v1";
37
+ ok: true;
38
+ status: "SUCCEEDED";
15
39
  moduleRef: "execution-browser-extension";
16
- moduleVersion: "0.1.4";
17
- ok: boolean;
18
- status: string;
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<{
49
+ result: {
50
+ contract: "deployment.result.v1";
51
+ moduleRef: "execution-browser-extension";
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.4";
27
- ok: boolean;
28
- status: string;
29
- data: {
30
- configStatus: "INCOMPLETE" | "INVALID" | "READY";
31
- missingConfig?: string[] | undefined;
32
- runtimeStatus: "FAILED" | "RUNNING" | "STOPPED" | "UNKNOWN";
66
+ moduleVersion: "0.1.5";
67
+ ok: false;
68
+ status: "ACTION_REQUIRED";
69
+ actionRequired: {
70
+ action: string;
71
+ description: string;
33
72
  };
34
73
  };
35
74
  observedEffects: never[];
36
- };
37
- 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<{
38
86
  result: {
39
87
  contract: "deployment.result.v1";
88
+ ok: true;
89
+ status: "SUCCEEDED";
40
90
  moduleRef: "execution-browser-extension";
41
- moduleVersion: "0.1.4";
42
- ok: boolean;
43
- status: string;
44
- checks: {
45
- id: string;
46
- status: string;
47
- message: string;
48
- }[];
91
+ moduleVersion: "0.1.5";
92
+ data: {
93
+ docs: string;
94
+ setup: string;
95
+ };
49
96
  };
50
97
  observedEffects: never[];
51
- };
52
- readonly doctor: () => {
98
+ }>;
99
+ readonly start: (context: ModuleCommandContext) => Promise<{
53
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
+ } | {
54
107
  contract: "deployment.result.v1";
55
108
  moduleRef: "execution-browser-extension";
56
- moduleVersion: "0.1.4";
57
- ok: boolean;
58
- 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
+ };
59
117
  };
60
118
  observedEffects: never[];
61
- };
62
- };
63
- export declare function browserExtensionLoadDir(workspaceRoot: string): string;
64
- export declare function materializeProductionConfig(input: {
65
- moduleRef: string;
66
- config: Record<string, string>;
67
- workspaceRoot: string;
68
- }): Promise<{
69
- loadDir: string;
70
- }>;
71
- export declare function createProductionBinding(input: {
72
- moduleRef: string;
73
- config: Record<string, string>;
74
- workspaceRoot: string;
75
- }): {
76
- 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
+ }>;
77
130
  };
@@ -1,76 +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 { observeDeclaredModuleStatus } from "@tomflow/proflow-module-contract";
4
+ import { deterministicLoopbackPort, ensureModuleSecretFile, moduleWorkspaceStateDirectory, readModuleSharedFacts, writeModuleSharedFacts, } from "@tomflow/proflow-module-contract";
5
5
  import { descriptor } from "./descriptor.js";
6
6
  const base = {
7
7
  contract: "deployment.result.v1",
8
+ ok: true,
9
+ status: "SUCCEEDED",
8
10
  moduleRef: descriptor.moduleRef,
9
11
  moduleVersion: descriptor.moduleVersion,
10
12
  };
11
- export const behaviorAdapter = {
12
- describe: () => ({
13
- result: { ...base, ok: true, status: "SUCCEEDED" },
14
- observedEffects: [],
15
- }),
16
- preflight: () => ({
17
- result: { ...base, ok: true, status: "SUCCEEDED" },
18
- observedEffects: [],
19
- }),
20
- status: () => ({
21
- result: {
22
- ...base,
23
- ok: true,
24
- status: "SUCCEEDED",
25
- data: observeDeclaredModuleStatus(descriptor, undefined, "UNKNOWN"),
26
- },
27
- observedEffects: [],
28
- }),
29
- verify: () => ({
30
- result: {
31
- ...base,
32
- ok: true,
33
- status: "SUCCEEDED",
34
- checks: [
35
- {
36
- id: "extension-package",
37
- status: "PASS",
38
- message: "The MV3 package is structurally verified; real Carrier readiness remains ACTION_REQUIRED in status",
39
- },
40
- ],
41
- },
42
- observedEffects: [],
43
- }),
44
- doctor: () => ({
45
- result: { ...base, ok: true, status: "SUCCEEDED" },
46
- observedEffects: [],
47
- }),
48
- };
49
- function loopbackEndpoint(value, key) {
50
- const url = new URL(value);
51
- if (url.protocol !== "http:" ||
52
- url.hostname !== "127.0.0.1" ||
53
- url.pathname !== "/" ||
54
- url.search !== "" ||
55
- url.hash !== "") {
56
- throw new Error(`${key} must be a loopback HTTP origin`);
57
- }
58
- return value.replace(/\/$/, "");
59
- }
60
- async function credential(file, key) {
61
- const token = (await readFile(file, "utf8")).trim();
62
- if (token.length < 32)
63
- throw new Error(`${key} must contain at least 32 characters`);
64
- return token;
65
- }
13
+ const factString = (facts, name) => (typeof facts?.[name] === "string" ? String(facts[name]) : undefined);
66
14
  function packageRoot() {
67
15
  const candidate = dirname(dirname(fileURLToPath(import.meta.url)));
68
16
  return basename(candidate) === "dist" ? dirname(candidate) : candidate;
69
17
  }
70
18
  export function browserExtensionLoadDir(workspaceRoot) {
71
- 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(/\/$/, "");
72
40
  }
73
41
  export async function materializeProductionConfig(input) {
42
+ const context = { workspaceRoot: input.workspaceRoot };
43
+ const loadDir = await installPackage(context);
74
44
  const required = [
75
45
  "bridge.endpoint",
76
46
  "bridge.token",
@@ -79,31 +49,31 @@ export async function materializeProductionConfig(input) {
79
49
  "approvalApplication.endpoint",
80
50
  "approvalApplication.token",
81
51
  ];
82
- for (const key of required) {
83
- if (!input.config[key])
84
- throw new Error(`missing required browser config ${key}`);
85
- }
52
+ for (const name of required)
53
+ if (!input.config[name])
54
+ throw new Error(`missing required browser config ${name}`);
86
55
  const runtimeConfig = {
87
56
  proflowRuntimeBridge: {
88
57
  endpoint: loopbackEndpoint(input.config["bridge.endpoint"] ?? "", "bridge.endpoint"),
89
- token: await credential(input.config["bridge.token"] ?? "", "bridge.token"),
58
+ token: await credential(input.config["bridge.token"] ?? ""),
90
59
  },
91
60
  proflowTaskApplication: {
92
61
  endpoint: loopbackEndpoint(input.config["taskApplication.endpoint"] ?? "", "taskApplication.endpoint"),
93
- token: await credential(input.config["taskApplication.token"] ?? "", "taskApplication.token"),
62
+ token: await credential(input.config["taskApplication.token"] ?? ""),
94
63
  },
95
64
  proflowApprovalApplication: {
96
65
  endpoint: loopbackEndpoint(input.config["approvalApplication.endpoint"] ?? "", "approvalApplication.endpoint"),
97
- token: await credential(input.config["approvalApplication.token"] ?? "", "approvalApplication.token"),
66
+ token: await credential(input.config["approvalApplication.token"] ?? ""),
98
67
  },
99
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) {
100
73
  const sourceRoot = packageRoot();
101
- const loadDir = browserExtensionLoadDir(input.workspaceRoot);
74
+ const loadDir = browserExtensionLoadDir(context.workspaceRoot);
102
75
  await mkdir(join(loadDir, "dist"), { recursive: true });
103
- await cp(join(sourceRoot, "dist", "extension"), join(loadDir, "dist", "extension"), {
104
- recursive: true,
105
- force: true,
106
- });
76
+ await cp(join(sourceRoot, "dist", "extension"), join(loadDir, "dist", "extension"), { recursive: true, force: true });
107
77
  await cp(join(sourceRoot, "extension"), join(loadDir, "extension"), {
108
78
  recursive: true,
109
79
  force: true,
@@ -111,108 +81,221 @@ export async function materializeProductionConfig(input) {
111
81
  await cp(join(sourceRoot, "manifest.json"), join(loadDir, "manifest.json"), {
112
82
  force: true,
113
83
  });
114
- await writeFile(join(loadDir, "runtime-config.json"), `${JSON.stringify(runtimeConfig, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
115
- return { loadDir };
84
+ return loadDir;
116
85
  }
117
- async function readBrowserVerificationEvidence(file, expectedLoadDir) {
118
- 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 {
119
95
  return undefined;
96
+ }
97
+ }
98
+ async function readEvidence(context, loadDir) {
120
99
  try {
121
- const raw = JSON.parse(await readFile(file, "utf8"));
122
- if (raw.contract !== "proflow.browser-extension-verification.v1" ||
123
- raw.moduleVersion !== descriptor.moduleVersion ||
124
- raw.loadDir !== expectedLoadDir ||
125
- typeof raw.extensionId !== "string" ||
126
- raw.extensionId.length < 16 ||
127
- raw.serviceWorker !== "RUNNING" ||
128
- typeof raw.observedAt !== "string" ||
129
- Number.isNaN(Date.parse(raw.observedAt)))
130
- return undefined;
131
- 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;
132
111
  }
133
112
  catch {
134
113
  return undefined;
135
114
  }
136
115
  }
137
- export function createProductionBinding(input) {
138
- const loadDir = browserExtensionLoadDir(input.workspaceRoot);
139
- const verificationEvidenceFile = input.config.verificationEvidenceFile;
140
- const boundBase = {
141
- contract: "deployment.result.v1",
142
- moduleRef: descriptor.moduleRef,
143
- 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 } : {}),
144
127
  };
145
- const configured = async () => {
146
- try {
147
- const raw = JSON.parse(await readFile(join(loadDir, "runtime-config.json"), "utf8"));
148
- return typeof raw === "object" && raw !== null;
149
- }
150
- catch {
151
- return false;
152
- }
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),
153
139
  };
154
- return {
155
- behaviorAdapter: {
156
- ...behaviorAdapter,
157
- preflight: async () => ({
158
- result: (await configured())
159
- ? { ...boundBase, ok: true, status: "SUCCEEDED" }
160
- : {
161
- ...boundBase,
162
- ok: false,
163
- status: "ACTION_REQUIRED",
164
- actionRequired: {
165
- action: "configure-browser-extension",
166
- description: `Materialize Browser Extension runtime config before loading ${loadDir}`,
167
- },
168
- },
169
- observedEffects: [],
170
- }),
171
- status: async () => {
172
- const isConfigured = await configured();
173
- const evidence = await readBrowserVerificationEvidence(verificationEvidenceFile, loadDir);
174
- return {
175
- result: {
176
- ...boundBase,
177
- ok: true,
178
- status: "SUCCEEDED",
179
- data: observeDeclaredModuleStatus(descriptor, input.config, evidence ? "RUNNING" : "STOPPED", isConfigured),
180
- },
181
- observedEffects: evidence
182
- ? ["Observes real Chrome MV3 load evidence"]
183
- : [],
184
- };
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
+ },
185
213
  },
186
- verify: async () => {
187
- const evidence = await readBrowserVerificationEvidence(verificationEvidenceFile, loadDir);
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))
188
225
  return {
189
- result: (await configured()) && evidence
190
- ? {
191
- ...boundBase,
192
- ok: true,
193
- status: "SUCCEEDED",
194
- checks: [
195
- {
196
- id: "real-carrier-e3-e4",
197
- status: "PASS",
198
- message: `Real Chrome loaded extension ${evidence.extensionId} from ${loadDir} and its MV3 Service Worker was observed running`,
199
- },
200
- ],
201
- }
202
- : {
203
- ...boundBase,
204
- ok: false,
205
- status: "ACTION_REQUIRED",
206
- actionRequired: {
207
- action: "verify-real-chrome-extension",
208
- description: `Load ${loadDir} in real Chrome and write verified evidence to ${verificationEvidenceFile ?? "the configured verificationEvidenceFile"}`,
209
- },
210
- },
211
- observedEffects: evidence
212
- ? ["Observes real Chrome MV3 load evidence"]
213
- : [],
226
+ result: failed("SETUP_FAILED", "extensionId must be a canonical 32-character Chromium extension id", false),
227
+ observedEffects: [],
214
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.`,
241
+ },
242
+ },
243
+ observedEffects: [],
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"),
253
+ observedEffects: [],
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
+ },
215
280
  },
216
- },
217
- };
218
- }
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,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.4";
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";
@@ -33,79 +33,14 @@ export declare const descriptor: {
33
33
  readonly kind: "human";
34
34
  readonly action: "Load and verify the unpacked MV3 extension in the real Chrome profile";
35
35
  }];
36
- readonly configSlots: readonly [{
37
- readonly key: "bridge.endpoint";
38
- readonly type: "url";
39
- readonly required: true;
40
- readonly description: "Loopback Browser Reality Bridge endpoint";
41
- }, {
42
- readonly key: "bridge.token";
43
- readonly type: "path";
44
- readonly required: true;
45
- readonly sensitive: true;
46
- readonly description: "File containing the Browser Reality Bridge extension token";
47
- }, {
48
- readonly key: "taskApplication.endpoint";
49
- readonly type: "url";
50
- readonly required: true;
51
- readonly description: "Loopback Platform Host Task application endpoint";
52
- }, {
53
- readonly key: "taskApplication.token";
54
- readonly type: "path";
55
- readonly required: true;
56
- readonly sensitive: true;
57
- readonly description: "File containing the Platform Host Task application token";
58
- }, {
59
- readonly key: "approvalApplication.endpoint";
60
- readonly type: "url";
61
- readonly required: true;
62
- readonly description: "Loopback Platform Host Approval application endpoint";
63
- }, {
64
- readonly key: "approvalApplication.token";
65
- readonly type: "path";
66
- readonly required: true;
67
- readonly sensitive: true;
68
- readonly description: "File containing the Platform Host Approval application token";
69
- }, {
70
- readonly key: "verificationEvidenceFile";
71
- readonly type: "path";
72
- readonly required: true;
73
- readonly description: "JSON evidence file written after real Chrome loads the Deployment-managed MV3 extension and its Service Worker runs";
74
- }, {
75
- readonly key: "chromeRuntimeModuleRef";
76
- readonly type: "moduleRef";
77
- readonly required: false;
78
- readonly description: "External resource module governing the Chrome runtime";
79
- readonly default: "chrome-runtime";
80
- }, {
81
- readonly key: "carrierModuleRef";
82
- readonly type: "moduleRef";
83
- readonly required: false;
84
- readonly description: "External resource module governing the Custom GPT carrier";
85
- readonly default: "chatgpt-carrier";
86
- }];
87
- readonly lifecycle: {
88
- readonly supported: readonly ["describe", "preflight", "status", "verify", "doctor"];
89
- };
90
- readonly verification: {
91
- readonly checks: readonly [{
92
- readonly id: "real-carrier-e3-e4";
93
- readonly description: "Real Chrome and ChatGPT E3/E4 evidence is present";
94
- readonly lifecycle: "verify";
95
- }];
96
- };
36
+ readonly configSlots: readonly [];
97
37
  readonly effects: readonly [{
98
38
  readonly kind: "external-resource";
99
- readonly description: "Package an MV3 extension that performs Execution-authorized Browser effects";
39
+ readonly description: "Materialize the unpacked MV3 extension package";
100
40
  readonly retention: "preserve";
101
41
  }];
102
- readonly documentation: readonly [{
103
- readonly id: "overview";
104
- readonly path: "./README.md";
105
- readonly description: "Package-owned module overview";
106
- }, {
107
- readonly id: "configuration";
108
- readonly path: "./CONFIGURATION.md";
109
- readonly description: "Module configuration fields, sources and materialization instructions";
110
- }];
42
+ readonly documentation: {
43
+ readonly docs: "DOCS.md";
44
+ readonly setup: "SETUP.md";
45
+ };
111
46
  };
@@ -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.4",
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",
@@ -42,96 +42,16 @@ export const descriptor = {
42
42
  action: "Load and verify the unpacked MV3 extension in the real Chrome profile",
43
43
  },
44
44
  ],
45
- configSlots: [
46
- {
47
- key: "bridge.endpoint",
48
- type: "url",
49
- required: true,
50
- description: "Loopback Browser Reality Bridge endpoint",
51
- },
52
- {
53
- key: "bridge.token",
54
- type: "path",
55
- required: true,
56
- sensitive: true,
57
- description: "File containing the Browser Reality Bridge extension token",
58
- },
59
- {
60
- key: "taskApplication.endpoint",
61
- type: "url",
62
- required: true,
63
- description: "Loopback Platform Host Task application endpoint",
64
- },
65
- {
66
- key: "taskApplication.token",
67
- type: "path",
68
- required: true,
69
- sensitive: true,
70
- description: "File containing the Platform Host Task application token",
71
- },
72
- {
73
- key: "approvalApplication.endpoint",
74
- type: "url",
75
- required: true,
76
- description: "Loopback Platform Host Approval application endpoint",
77
- },
78
- {
79
- key: "approvalApplication.token",
80
- type: "path",
81
- required: true,
82
- sensitive: true,
83
- description: "File containing the Platform Host Approval application token",
84
- },
85
- {
86
- key: "verificationEvidenceFile",
87
- type: "path",
88
- required: true,
89
- description: "JSON evidence file written after real Chrome loads the Deployment-managed MV3 extension and its Service Worker runs",
90
- },
91
- {
92
- key: "chromeRuntimeModuleRef",
93
- type: "moduleRef",
94
- required: false,
95
- description: "External resource module governing the Chrome runtime",
96
- default: "chrome-runtime",
97
- },
98
- {
99
- key: "carrierModuleRef",
100
- type: "moduleRef",
101
- required: false,
102
- description: "External resource module governing the Custom GPT carrier",
103
- default: "chatgpt-carrier",
104
- },
105
- ],
106
- lifecycle: {
107
- supported: ["describe", "preflight", "status", "verify", "doctor"],
108
- },
109
- verification: {
110
- checks: [
111
- {
112
- id: "real-carrier-e3-e4",
113
- description: "Real Chrome and ChatGPT E3/E4 evidence is present",
114
- lifecycle: "verify",
115
- },
116
- ],
117
- },
45
+ configSlots: [],
118
46
  effects: [
119
47
  {
120
48
  kind: "external-resource",
121
- description: "Package an MV3 extension that performs Execution-authorized Browser effects",
49
+ description: "Materialize the unpacked MV3 extension package",
122
50
  retention: "preserve",
123
51
  },
124
52
  ],
125
- documentation: [
126
- {
127
- id: "overview",
128
- path: "./README.md",
129
- description: "Package-owned module overview",
130
- },
131
- {
132
- id: "configuration",
133
- path: "./CONFIGURATION.md",
134
- description: "Module configuration fields, sources and materialization instructions",
135
- },
136
- ],
53
+ documentation: {
54
+ docs: "DOCS.md",
55
+ setup: "SETUP.md",
56
+ },
137
57
  };
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.4",
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.4",
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": {
@@ -22,15 +22,16 @@
22
22
  "proflow.module.json",
23
23
  "conformance.json",
24
24
  "README.md",
25
- "CONFIGURATION.md"
25
+ "DOCS.md",
26
+ "SETUP.md"
26
27
  ],
27
28
  "dependencies": {
28
- "@tomflow/proflow-module-contract": "^0.1.2",
29
- "@tomflow/proflow-execution-contracts": "^0.1.3"
29
+ "@tomflow/proflow-execution-contracts": "^0.1.4",
30
+ "@tomflow/proflow-module-contract": "^0.1.4"
30
31
  },
31
32
  "devDependencies": {
32
- "@tomflow/proflow-deployment-conformance": "^0.1.3",
33
- "@tomflow/proflow-execution-runtime": "^0.1.5"
33
+ "@tomflow/proflow-execution-runtime": "^0.1.7",
34
+ "@tomflow/proflow-deployment-conformance": "^0.1.4"
34
35
  },
35
36
  "keywords": [
36
37
  "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.4",
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",
@@ -42,96 +42,16 @@
42
42
  "action": "Load and verify the unpacked MV3 extension in the real Chrome profile"
43
43
  }
44
44
  ],
45
- "configSlots": [
46
- {
47
- "key": "bridge.endpoint",
48
- "type": "url",
49
- "required": true,
50
- "description": "Loopback Browser Reality Bridge endpoint"
51
- },
52
- {
53
- "key": "bridge.token",
54
- "type": "path",
55
- "required": true,
56
- "sensitive": true,
57
- "description": "File containing the Browser Reality Bridge extension token"
58
- },
59
- {
60
- "key": "taskApplication.endpoint",
61
- "type": "url",
62
- "required": true,
63
- "description": "Loopback Platform Host Task application endpoint"
64
- },
65
- {
66
- "key": "taskApplication.token",
67
- "type": "path",
68
- "required": true,
69
- "sensitive": true,
70
- "description": "File containing the Platform Host Task application token"
71
- },
72
- {
73
- "key": "approvalApplication.endpoint",
74
- "type": "url",
75
- "required": true,
76
- "description": "Loopback Platform Host Approval application endpoint"
77
- },
78
- {
79
- "key": "approvalApplication.token",
80
- "type": "path",
81
- "required": true,
82
- "sensitive": true,
83
- "description": "File containing the Platform Host Approval application token"
84
- },
85
- {
86
- "key": "verificationEvidenceFile",
87
- "type": "path",
88
- "required": true,
89
- "description": "JSON evidence file written after real Chrome loads the Deployment-managed MV3 extension and its Service Worker runs"
90
- },
91
- {
92
- "key": "chromeRuntimeModuleRef",
93
- "type": "moduleRef",
94
- "required": false,
95
- "description": "External resource module governing the Chrome runtime",
96
- "default": "chrome-runtime"
97
- },
98
- {
99
- "key": "carrierModuleRef",
100
- "type": "moduleRef",
101
- "required": false,
102
- "description": "External resource module governing the Custom GPT carrier",
103
- "default": "chatgpt-carrier"
104
- }
105
- ],
106
- "lifecycle": {
107
- "supported": ["describe", "preflight", "status", "verify", "doctor"]
108
- },
109
- "verification": {
110
- "checks": [
111
- {
112
- "id": "real-carrier-e3-e4",
113
- "description": "Real Chrome and ChatGPT E3/E4 evidence is present",
114
- "lifecycle": "verify"
115
- }
116
- ]
117
- },
45
+ "configSlots": [],
118
46
  "effects": [
119
47
  {
120
48
  "kind": "external-resource",
121
- "description": "Package an MV3 extension that performs Execution-authorized Browser effects",
49
+ "description": "Materialize the unpacked MV3 extension package",
122
50
  "retention": "preserve"
123
51
  }
124
52
  ],
125
- "documentation": [
126
- {
127
- "id": "overview",
128
- "path": "./README.md",
129
- "description": "Package-owned module overview"
130
- },
131
- {
132
- "id": "configuration",
133
- "path": "./CONFIGURATION.md",
134
- "description": "Module configuration fields, sources and materialization instructions"
135
- }
136
- ]
53
+ "documentation": {
54
+ "docs": "DOCS.md",
55
+ "setup": "SETUP.md"
56
+ }
137
57
  }
package/CONFIGURATION.md DELETED
@@ -1,33 +0,0 @@
1
- # execution-browser-extension configuration
2
-
3
- Canonical public config: `.proflow/config/execution-browser-extension.json`
4
-
5
- This file is Workspace-owned data. `platform uninstall` does not delete it. Values are JSON strings. Raw secrets must not be stored here; `secretRef` fields contain opaque references only.
6
-
7
- ## Fields
8
-
9
- | Key | Type | Required | Sensitive | Source | Meaning |
10
- |---|---|---:|---:|---|---|
11
- | `bridge.endpoint` | `url` | yes | no | User or owning external/runtime Module supplies the reachable endpoint URL. | Loopback Browser Reality Bridge endpoint |
12
- | `bridge.token` | `path` | yes | yes | User or owning Module supplies an absolute/local path that exists on this Workspace. | File containing the Browser Reality Bridge extension token |
13
- | `taskApplication.endpoint` | `url` | yes | no | User or owning external/runtime Module supplies the reachable endpoint URL. | Loopback Platform Host Task application endpoint |
14
- | `taskApplication.token` | `path` | yes | yes | User or owning Module supplies an absolute/local path that exists on this Workspace. | File containing the Platform Host Task application token |
15
- | `approvalApplication.endpoint` | `url` | yes | no | User or owning external/runtime Module supplies the reachable endpoint URL. | Loopback Platform Host Approval application endpoint |
16
- | `approvalApplication.token` | `path` | yes | yes | User or owning Module supplies an absolute/local path that exists on this Workspace. | File containing the Platform Host Approval application token |
17
- | `verificationEvidenceFile` | `path` | yes | no | User or owning Module supplies an absolute/local path that exists on this Workspace. | JSON evidence file written after real Chrome loads the Deployment-managed MV3 extension and its Service Worker runs |
18
- | `chromeRuntimeModuleRef` | `moduleRef` | no | no | Another installed ProFlow Module reference selected from `platform docs` / Module topology. | External resource module governing the Chrome runtime |
19
- | `carrierModuleRef` | `moduleRef` | no | no | Another installed ProFlow Module reference selected from `platform docs` / Module topology. | External resource module governing the Custom GPT carrier |
20
-
21
- ## Materialize
22
-
23
- Create or update the canonical JSON object at:
24
-
25
- `.proflow/config/execution-browser-extension.json`
26
-
27
- Use only declared keys and string values. Do not create a second Platform configuration database.
28
-
29
- The Browser Extension also requires a package-owned physical runtime config artifact. After the canonical Module config is present, run `proflow-execution-browser-extension materialize-config --workspace <path>`. This package-owned command writes the private runtime artifact under `.proflow/deployment/browser-extension/execution-browser-extension/`; `platform start` must not perform this materialization implicitly.
30
-
31
- ## Base completion check
32
-
33
- Run `platform modules`. This Module should no longer report required keys in `missingConfig`. `platform start` performs the authoritative Module-owned validation before starting anything.