@tomflow/proflow-execution-browser-extension 0.1.12 → 0.1.13
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/CHANGELOG.md +6 -0
- package/DOCS.md +12 -4
- package/SETUP.md +10 -14
- package/dist/deployment/adapter.d.ts +42 -50
- package/dist/deployment/adapter.js +125 -103
- package/dist/deployment/descriptor.d.ts +4 -1
- package/dist/deployment/descriptor.js +5 -1
- package/dist/extension/background.js +189 -0
- package/dist/extension/provisioning-content.d.ts +1 -0
- package/dist/extension/provisioning-content.js +936 -0
- package/dist/src/configure-args.d.ts +3 -0
- package/dist/src/configure-args.js +23 -0
- package/dist/src/configure.js +8 -71
- package/dist/src/custom-gpt-editor-driver.d.ts +51 -0
- package/dist/src/custom-gpt-editor-driver.js +116 -0
- package/dist/src/custom-gpt-knowledge.d.ts +21 -0
- package/dist/src/custom-gpt-knowledge.js +148 -0
- package/dist/src/custom-gpt-provisioner.d.ts +78 -0
- package/dist/src/custom-gpt-provisioner.js +225 -0
- package/dist/src/custom-gpt-role.d.ts +66 -0
- package/dist/src/custom-gpt-role.js +98 -0
- package/dist/src/install-workflow.d.ts +29 -0
- package/dist/src/install-workflow.js +64 -0
- package/dist/src/pairing.d.ts +20 -0
- package/dist/src/pairing.js +176 -0
- package/dist/src/provisioning-bridge.d.ts +46 -0
- package/dist/src/provisioning-bridge.js +314 -0
- package/extension/background.ts +264 -3
- package/extension/provisioning-content.ts +1033 -0
- package/manifest.json +9 -1
- package/package.json +7 -5
- package/proflow.module.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @tomflow/proflow-execution-browser-extension
|
|
2
2
|
|
|
3
|
+
## 0.1.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Ship the Phase 3 browser, tunnel, model automation, platform readiness, and documentation closure as one verifiable platform snapshot.
|
|
8
|
+
|
|
3
9
|
## 0.1.12
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/DOCS.md
CHANGED
|
@@ -6,13 +6,19 @@ Execution 领域拥有的 Chrome MV3 Extension,负责 Task UI、审批提醒
|
|
|
6
6
|
|
|
7
7
|
## 主要能力
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
9
|
+
- 运行期提供 New Task 与 Side Panel UI、Task Observer、System Observer 和 Browser Effect。
|
|
10
|
+
- 运行期创建、恢复和唤醒 Custom GPT Conversation,稳定观察 workerRef/c-id。
|
|
11
|
+
- 部署期通过独立 `/gpts/editor` / `/gpts/editor/*` Provisioning Surface 物化 Custom GPT;该分支不进入 Task/Worker 状态机。
|
|
12
|
+
- 正常创建路径在单一 GPT Editor 会话中完成字段、Action Schema、API Key/Bearer、Knowledge ZIP、模型与 Capabilities 配置,再创建 Private GPT;不在 Create 后重新打开同一 GPT 做 Auth。
|
|
13
|
+
- Knowledge Bundle 在 Mac 侧先做 ZIP traversal/大小/内部类型校验,最终向 GPT Knowledge 上传 `custom-gpt-knowledge.zip` 本体;不把解包后的 `.md` 逐文件作为正常上传物。
|
|
14
|
+
- `FINALIZE_CUSTOM_GPT_AUTH` 只保留为显式 recovery/repair primitive,不属于正常 `createCustomGptRole` Golden Path。
|
|
11
15
|
- 执行 DOM-first 浏览器操作,必要时使用截图与 Vision 辅助观察。
|
|
12
16
|
|
|
13
17
|
## 提供的 API 与 Public Contract
|
|
14
18
|
|
|
15
|
-
- 提供 `execution-browser-executor` Contract,版本 `1.0.0
|
|
19
|
+
- 提供 `execution-browser-executor` Contract,版本 `1.0.0`,仅用于运行期 Browser Execution。
|
|
20
|
+
- 提供 `custom-gpt-web-provisioning` Contract,版本 `1.0.0`,仅用于部署期 Custom GPT Web Provisioning。
|
|
21
|
+
- 两条能力只共享 Extension/loopback transport 等底层设施,不共享业务状态机。
|
|
16
22
|
- 通过本地 Bridge 接收 typed Browser operation 并返回 Evidence。
|
|
17
23
|
|
|
18
24
|
## 依赖的 Module、Contract 和外部资源
|
|
@@ -26,7 +32,9 @@ Execution 领域拥有的 Chrome MV3 Extension,负责 Task UI、审批提醒
|
|
|
26
32
|
|
|
27
33
|
## 使用方式
|
|
28
34
|
|
|
29
|
-
运行 `pnpm exec -- proflow-execution-browser-extension setup
|
|
35
|
+
运行 `pnpm exec -- proflow-execution-browser-extension setup --workspace <workspace>`。ProFlow 会先准备 unpacked 目录、运行配置和本地 Bridge,并用真实 hello + heartbeat 尝试重验证已有扩展;已有扩展仍在线时不会再次打扰用户。只有未检测到在线扩展时,ProFlow 才把正确目录复制到剪贴板并打开 `chrome://extensions/`,提示用户完成安装操作:如果开发者模式尚未开启则先开启,然后点击“加载未打包的扩展程序”,在目录选择窗口中粘贴并确认目录。完成后无需回终端输入任何标识或状态;扩展身份、session、hello/heartbeat、Browser Executor 配置和 setup evidence 都由机器自动发现、生成并验证。历史 pairing evidence 不能单独代替本次 live revalidation。
|
|
36
|
+
|
|
37
|
+
若 pairing 等待超时,ProFlow 保留已经准备好的扩展文件和机器配置,不写伪 READY evidence,并输出稳定错误代码 `PAIRING_TIMEOUT`。用户确认 Chrome 已完成加载后重新执行 `platform setup` 即可。Agent setup 随后通过 `custom-gpt-web-provisioning` 使用同一 Extension package 的部署期分支。
|
|
30
38
|
|
|
31
39
|
## 职责边界与限制
|
|
32
40
|
|
package/SETUP.md
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
# execution-browser-extension Setup
|
|
2
2
|
|
|
3
|
-
## STEP-EXECUTION-BROWSER-EXTENSION-01 —
|
|
3
|
+
## STEP-EXECUTION-BROWSER-EXTENSION-01 — 加载扩展并自动配对
|
|
4
4
|
Responsible: USER
|
|
5
|
-
Interactive executable: `pnpm exec -- proflow-execution-browser-extension setup
|
|
6
|
-
Non-interactive executable: `pnpm exec -- proflow-execution-browser-extension setup
|
|
5
|
+
Interactive executable: `pnpm exec -- proflow-execution-browser-extension setup`
|
|
6
|
+
Non-interactive executable: `pnpm exec -- proflow-execution-browser-extension setup`
|
|
7
7
|
Required inputs: none
|
|
8
|
-
Verify: `pnpm exec -- proflow-execution-browser-extension setup 01`
|
|
9
|
-
Success condition: 扩展目录已生成并可选择。
|
|
10
|
-
|
|
11
|
-
## STEP-EXECUTION-BROWSER-EXTENSION-02 — 登记 Extension ID 并生成配置
|
|
12
|
-
Responsible: USER
|
|
13
|
-
Interactive executable: `pnpm exec -- proflow-execution-browser-extension setup 02`
|
|
14
|
-
Non-interactive executable: `pnpm exec -- proflow-execution-browser-extension setup 02 --extension-id <id>`
|
|
15
|
-
Required inputs: Chrome Extension ID
|
|
16
8
|
Verify: `pnpm exec -- proflow-execution-browser-extension verify`
|
|
17
|
-
Success condition: Extension
|
|
9
|
+
Success condition: 本地 pairing listener 已收到真实 Extension hello + heartbeat,并持久化部署证据。
|
|
10
|
+
|
|
11
|
+
运行命令后,ProFlow 会先准备 unpacked 目录和所有机器配置,并尝试用真实 hello + heartbeat 重验证已有扩展。若已有扩展仍在线,则无需任何人工操作,setup 直接继续;只有未检测到在线扩展时,ProFlow 才把正确目录复制到剪贴板、打开 `chrome://extensions/` 并提示用户完成安装操作:如果开发者模式尚未开启则先开启,然后点击“加载未打包的扩展程序”,在目录选择窗口中粘贴并确认刚刚复制的目录。完成后无需回终端输入任何内容;扩展身份确认、Bridge 配置、hello/heartbeat 验证和 READY 状态均由 ProFlow 自动完成。历史 `setup.json` / verification evidence 不能单独跳过本次 live revalidation。
|
|
12
|
+
|
|
13
|
+
若等待超时,已经准备好的扩展文件和机器配置会保留,且不会写入伪 READY evidence。用户只需确认 Chrome 中已经完成扩展加载,然后重新执行 `platform setup`;稳定错误代码为 `PAIRING_TIMEOUT`。
|
|
18
14
|
|
|
19
|
-
## STEP-EXECUTION-BROWSER-EXTENSION-
|
|
15
|
+
## STEP-EXECUTION-BROWSER-EXTENSION-02 — 验证部署状态
|
|
20
16
|
Responsible: AI
|
|
21
|
-
Interactive executable: `pnpm exec -- proflow-execution-browser-extension
|
|
17
|
+
Interactive executable: `pnpm exec -- proflow-execution-browser-extension verify`
|
|
22
18
|
Non-interactive executable: `pnpm exec -- proflow-execution-browser-extension verify`
|
|
23
19
|
Required inputs: none
|
|
24
20
|
Verify: `pnpm exec -- proflow-execution-browser-extension verify`
|
|
@@ -7,6 +7,16 @@ export declare function materializeProductionConfig(input: {
|
|
|
7
7
|
}): Promise<{
|
|
8
8
|
loadDir: string;
|
|
9
9
|
}>;
|
|
10
|
+
export declare function pairBrowserExtensionSetup(context: ModuleCommandContext, options?: {
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
onWaiting?: (input: {
|
|
13
|
+
loadDir: string;
|
|
14
|
+
endpoint: string;
|
|
15
|
+
}) => void | Promise<void>;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
extensionId: string;
|
|
18
|
+
extensionInstanceId: string;
|
|
19
|
+
}>;
|
|
10
20
|
export declare const behaviorAdapter: {
|
|
11
21
|
readonly install: (context: ModuleCommandContext) => Promise<{
|
|
12
22
|
result: {
|
|
@@ -14,7 +24,7 @@ export declare const behaviorAdapter: {
|
|
|
14
24
|
ok: true;
|
|
15
25
|
status: "SUCCEEDED";
|
|
16
26
|
moduleRef: "execution-browser-extension";
|
|
17
|
-
moduleVersion: "0.1.
|
|
27
|
+
moduleVersion: "0.1.13";
|
|
18
28
|
data: {
|
|
19
29
|
loadDir: string;
|
|
20
30
|
};
|
|
@@ -27,7 +37,7 @@ export declare const behaviorAdapter: {
|
|
|
27
37
|
readonly ok: true;
|
|
28
38
|
readonly status: "SUCCEEDED";
|
|
29
39
|
readonly moduleRef: "execution-browser-extension";
|
|
30
|
-
readonly moduleVersion: "0.1.
|
|
40
|
+
readonly moduleVersion: "0.1.13";
|
|
31
41
|
};
|
|
32
42
|
observedEffects: never[];
|
|
33
43
|
}>;
|
|
@@ -37,7 +47,7 @@ export declare const behaviorAdapter: {
|
|
|
37
47
|
ok: true;
|
|
38
48
|
status: "SUCCEEDED";
|
|
39
49
|
moduleRef: "execution-browser-extension";
|
|
40
|
-
moduleVersion: "0.1.
|
|
50
|
+
moduleVersion: "0.1.13";
|
|
41
51
|
data: {
|
|
42
52
|
setupStatus: "ACTION_REQUIRED" | "READY";
|
|
43
53
|
runtimeStatus: "RUNNING" | "STOPPED";
|
|
@@ -56,7 +66,7 @@ export declare const behaviorAdapter: {
|
|
|
56
66
|
result: {
|
|
57
67
|
contract: "deployment.result.v1";
|
|
58
68
|
moduleRef: "execution-browser-extension";
|
|
59
|
-
moduleVersion: "0.1.
|
|
69
|
+
moduleVersion: "0.1.13";
|
|
60
70
|
ok: false;
|
|
61
71
|
status: "FAILED";
|
|
62
72
|
error: {
|
|
@@ -66,70 +76,61 @@ export declare const behaviorAdapter: {
|
|
|
66
76
|
};
|
|
67
77
|
data: {
|
|
68
78
|
readonly steps: readonly [{
|
|
69
|
-
readonly id: "STEP-EXECUTION-BROWSER-EXTENSION-
|
|
70
|
-
readonly title: "
|
|
79
|
+
readonly id: "STEP-EXECUTION-BROWSER-EXTENSION-RECOVER";
|
|
80
|
+
readonly title: "重新加载并自动配对扩展";
|
|
71
81
|
readonly state: "BLOCKED";
|
|
72
82
|
readonly responsible: "EXTERNAL";
|
|
73
83
|
readonly execution: {
|
|
74
84
|
readonly interactive: "pnpm exec -- proflow-execution-browser-extension setup";
|
|
75
|
-
readonly nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup
|
|
85
|
+
readonly nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup";
|
|
76
86
|
};
|
|
77
87
|
readonly requiredInputs: readonly [];
|
|
78
88
|
readonly verify: "pnpm exec -- proflow-execution-browser-extension verify";
|
|
79
89
|
readonly successCondition: "配置状态变为“已就绪”";
|
|
80
|
-
readonly blockedReason: "Extension
|
|
90
|
+
readonly blockedReason: "真实 Extension heartbeat 未到达或本地 pairing 失败";
|
|
81
91
|
}];
|
|
82
92
|
};
|
|
83
93
|
};
|
|
84
94
|
observedEffects: never[];
|
|
95
|
+
} | {
|
|
96
|
+
result: {
|
|
97
|
+
readonly contract: "deployment.result.v1";
|
|
98
|
+
readonly ok: true;
|
|
99
|
+
readonly status: "SUCCEEDED";
|
|
100
|
+
readonly moduleRef: "execution-browser-extension";
|
|
101
|
+
readonly moduleVersion: "0.1.13";
|
|
102
|
+
};
|
|
103
|
+
observedEffects: string[];
|
|
85
104
|
} | {
|
|
86
105
|
result: {
|
|
87
106
|
contract: "deployment.result.v1";
|
|
88
107
|
moduleRef: "execution-browser-extension";
|
|
89
|
-
moduleVersion: "0.1.
|
|
108
|
+
moduleVersion: "0.1.13";
|
|
90
109
|
ok: false;
|
|
91
110
|
status: "ACTION_REQUIRED";
|
|
92
111
|
data: {
|
|
93
112
|
readonly steps: readonly [{
|
|
94
113
|
readonly id: "STEP-EXECUTION-BROWSER-EXTENSION-01";
|
|
95
|
-
readonly title: "
|
|
96
|
-
readonly description: "
|
|
114
|
+
readonly title: "加载扩展并自动配对";
|
|
115
|
+
readonly description: "准备 unpacked 目录和本地 Bridge,等待 Chrome Extension 的真实 hello + heartbeat。";
|
|
97
116
|
readonly state: "TODO";
|
|
98
117
|
readonly responsible: "USER";
|
|
99
118
|
readonly execution: {
|
|
100
|
-
readonly interactive: "pnpm exec -- proflow-execution-browser-extension setup
|
|
101
|
-
readonly nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup
|
|
119
|
+
readonly interactive: "pnpm exec -- proflow-execution-browser-extension setup";
|
|
120
|
+
readonly nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup";
|
|
102
121
|
};
|
|
103
122
|
readonly requiredInputs: readonly [];
|
|
104
|
-
readonly verify: "pnpm exec -- proflow-execution-browser-extension setup 01";
|
|
105
|
-
readonly successCondition: "扩展目录已生成并可选择";
|
|
106
|
-
readonly humanAction: "启用开发者模式并选择脚本复制的目录。";
|
|
107
|
-
}, {
|
|
108
|
-
readonly id: "STEP-EXECUTION-BROWSER-EXTENSION-02";
|
|
109
|
-
readonly title: "登记 Extension ID 并生成配置";
|
|
110
|
-
readonly description: "保存扩展 ID,生成 Bridge 配置并提示 Reload。";
|
|
111
|
-
readonly state: "TODO";
|
|
112
|
-
readonly responsible: "USER";
|
|
113
|
-
readonly execution: {
|
|
114
|
-
readonly interactive: "pnpm exec -- proflow-execution-browser-extension setup 02";
|
|
115
|
-
readonly nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup 02 --extension-id <id>";
|
|
116
|
-
};
|
|
117
|
-
readonly requiredInputs: readonly [{
|
|
118
|
-
readonly name: "extensionId";
|
|
119
|
-
readonly description: "Chrome Extension ID";
|
|
120
|
-
readonly sensitive: false;
|
|
121
|
-
}];
|
|
122
123
|
readonly verify: "pnpm exec -- proflow-execution-browser-extension verify";
|
|
123
|
-
readonly successCondition: "Extension
|
|
124
|
-
readonly humanAction: "
|
|
124
|
+
readonly successCondition: "Extension hello + heartbeat 已被本地 pairing listener 验证并持久化";
|
|
125
|
+
readonly humanAction: "启用开发者模式并加载脚本准备的 unpacked 目录;其余身份发现与验证自动完成。";
|
|
125
126
|
}, {
|
|
126
|
-
readonly id: "STEP-EXECUTION-BROWSER-EXTENSION-
|
|
127
|
-
readonly title: "
|
|
128
|
-
readonly description: "
|
|
127
|
+
readonly id: "STEP-EXECUTION-BROWSER-EXTENSION-02";
|
|
128
|
+
readonly title: "验证扩展部署状态";
|
|
129
|
+
readonly description: "读取 heartbeat 形成的持久化部署证据。";
|
|
129
130
|
readonly state: "TODO";
|
|
130
131
|
readonly responsible: "AI";
|
|
131
132
|
readonly execution: {
|
|
132
|
-
readonly interactive: "pnpm exec -- proflow-execution-browser-extension
|
|
133
|
+
readonly interactive: "pnpm exec -- proflow-execution-browser-extension verify";
|
|
133
134
|
readonly nonInteractive: "pnpm exec -- proflow-execution-browser-extension verify";
|
|
134
135
|
};
|
|
135
136
|
readonly requiredInputs: readonly [];
|
|
@@ -143,15 +144,6 @@ export declare const behaviorAdapter: {
|
|
|
143
144
|
};
|
|
144
145
|
};
|
|
145
146
|
observedEffects: never[];
|
|
146
|
-
} | {
|
|
147
|
-
result: {
|
|
148
|
-
readonly contract: "deployment.result.v1";
|
|
149
|
-
readonly ok: true;
|
|
150
|
-
readonly status: "SUCCEEDED";
|
|
151
|
-
readonly moduleRef: "execution-browser-extension";
|
|
152
|
-
readonly moduleVersion: "0.1.12";
|
|
153
|
-
};
|
|
154
|
-
observedEffects: string[];
|
|
155
147
|
}>;
|
|
156
148
|
readonly docs: (_context: ModuleCommandContext) => Promise<{
|
|
157
149
|
result: {
|
|
@@ -159,7 +151,7 @@ export declare const behaviorAdapter: {
|
|
|
159
151
|
ok: true;
|
|
160
152
|
status: "SUCCEEDED";
|
|
161
153
|
moduleRef: "execution-browser-extension";
|
|
162
|
-
moduleVersion: "0.1.
|
|
154
|
+
moduleVersion: "0.1.13";
|
|
163
155
|
data: {
|
|
164
156
|
docs: string;
|
|
165
157
|
};
|
|
@@ -172,11 +164,11 @@ export declare const behaviorAdapter: {
|
|
|
172
164
|
readonly ok: true;
|
|
173
165
|
readonly status: "SUCCEEDED";
|
|
174
166
|
readonly moduleRef: "execution-browser-extension";
|
|
175
|
-
readonly moduleVersion: "0.1.
|
|
167
|
+
readonly moduleVersion: "0.1.13";
|
|
176
168
|
} | {
|
|
177
169
|
contract: "deployment.result.v1";
|
|
178
170
|
moduleRef: "execution-browser-extension";
|
|
179
|
-
moduleVersion: "0.1.
|
|
171
|
+
moduleVersion: "0.1.13";
|
|
180
172
|
ok: false;
|
|
181
173
|
status: "FAILED";
|
|
182
174
|
error: {
|
|
@@ -193,7 +185,7 @@ export declare const behaviorAdapter: {
|
|
|
193
185
|
readonly ok: true;
|
|
194
186
|
readonly status: "SUCCEEDED";
|
|
195
187
|
readonly moduleRef: "execution-browser-extension";
|
|
196
|
-
readonly moduleVersion: "0.1.
|
|
188
|
+
readonly moduleVersion: "0.1.13";
|
|
197
189
|
};
|
|
198
190
|
observedEffects: never[];
|
|
199
191
|
}>;
|
|
@@ -3,6 +3,7 @@ import { cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { deterministicLoopbackPort, ensureModuleSecretFile, moduleWorkspaceStateDirectory, readModuleSharedFacts, writeModuleSharedFacts, } from "@tomflow/proflow-module-contract";
|
|
6
|
+
import { createBrowserExtensionPairingServer } from "../src/pairing.js";
|
|
6
7
|
import { descriptor } from "./descriptor.js";
|
|
7
8
|
const base = {
|
|
8
9
|
contract: "deployment.result.v1",
|
|
@@ -15,48 +16,27 @@ const setupPlan = {
|
|
|
15
16
|
steps: [
|
|
16
17
|
{
|
|
17
18
|
id: "STEP-EXECUTION-BROWSER-EXTENSION-01",
|
|
18
|
-
title: "
|
|
19
|
-
description: "
|
|
19
|
+
title: "加载扩展并自动配对",
|
|
20
|
+
description: "准备 unpacked 目录和本地 Bridge,等待 Chrome Extension 的真实 hello + heartbeat。",
|
|
20
21
|
state: "TODO",
|
|
21
22
|
responsible: "USER",
|
|
22
23
|
execution: {
|
|
23
|
-
interactive: "pnpm exec -- proflow-execution-browser-extension setup
|
|
24
|
-
nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup
|
|
24
|
+
interactive: "pnpm exec -- proflow-execution-browser-extension setup",
|
|
25
|
+
nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup",
|
|
25
26
|
},
|
|
26
27
|
requiredInputs: [],
|
|
27
|
-
verify: "pnpm exec -- proflow-execution-browser-extension setup 01",
|
|
28
|
-
successCondition: "扩展目录已生成并可选择",
|
|
29
|
-
humanAction: "启用开发者模式并选择脚本复制的目录。",
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: "STEP-EXECUTION-BROWSER-EXTENSION-02",
|
|
33
|
-
title: "登记 Extension ID 并生成配置",
|
|
34
|
-
description: "保存扩展 ID,生成 Bridge 配置并提示 Reload。",
|
|
35
|
-
state: "TODO",
|
|
36
|
-
responsible: "USER",
|
|
37
|
-
execution: {
|
|
38
|
-
interactive: "pnpm exec -- proflow-execution-browser-extension setup 02",
|
|
39
|
-
nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup 02 --extension-id <id>",
|
|
40
|
-
},
|
|
41
|
-
requiredInputs: [
|
|
42
|
-
{
|
|
43
|
-
name: "extensionId",
|
|
44
|
-
description: "Chrome Extension ID",
|
|
45
|
-
sensitive: false,
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
28
|
verify: "pnpm exec -- proflow-execution-browser-extension verify",
|
|
49
|
-
successCondition: "Extension
|
|
50
|
-
humanAction: "
|
|
29
|
+
successCondition: "Extension hello + heartbeat 已被本地 pairing listener 验证并持久化",
|
|
30
|
+
humanAction: "启用开发者模式并加载脚本准备的 unpacked 目录;其余身份发现与验证自动完成。",
|
|
51
31
|
},
|
|
52
32
|
{
|
|
53
|
-
id: "STEP-EXECUTION-BROWSER-EXTENSION-
|
|
54
|
-
title: "
|
|
55
|
-
description: "
|
|
33
|
+
id: "STEP-EXECUTION-BROWSER-EXTENSION-02",
|
|
34
|
+
title: "验证扩展部署状态",
|
|
35
|
+
description: "读取 heartbeat 形成的持久化部署证据。",
|
|
56
36
|
state: "TODO",
|
|
57
37
|
responsible: "AI",
|
|
58
38
|
execution: {
|
|
59
|
-
interactive: "pnpm exec -- proflow-execution-browser-extension
|
|
39
|
+
interactive: "pnpm exec -- proflow-execution-browser-extension verify",
|
|
60
40
|
nonInteractive: "pnpm exec -- proflow-execution-browser-extension verify",
|
|
61
41
|
},
|
|
62
42
|
requiredInputs: [],
|
|
@@ -68,18 +48,18 @@ const setupPlan = {
|
|
|
68
48
|
const blockedSetupPlan = {
|
|
69
49
|
steps: [
|
|
70
50
|
{
|
|
71
|
-
id: "STEP-EXECUTION-BROWSER-EXTENSION-
|
|
72
|
-
title: "
|
|
51
|
+
id: "STEP-EXECUTION-BROWSER-EXTENSION-RECOVER",
|
|
52
|
+
title: "重新加载并自动配对扩展",
|
|
73
53
|
state: "BLOCKED",
|
|
74
54
|
responsible: "EXTERNAL",
|
|
75
55
|
execution: {
|
|
76
56
|
interactive: "pnpm exec -- proflow-execution-browser-extension setup",
|
|
77
|
-
nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup
|
|
57
|
+
nonInteractive: "pnpm exec -- proflow-execution-browser-extension setup",
|
|
78
58
|
},
|
|
79
59
|
requiredInputs: [],
|
|
80
60
|
verify: "pnpm exec -- proflow-execution-browser-extension verify",
|
|
81
61
|
successCondition: "配置状态变为“已就绪”",
|
|
82
|
-
blockedReason: "Extension
|
|
62
|
+
blockedReason: "真实 Extension heartbeat 未到达或本地 pairing 失败",
|
|
83
63
|
},
|
|
84
64
|
],
|
|
85
65
|
};
|
|
@@ -176,7 +156,10 @@ async function readEvidence(context, loadDir) {
|
|
|
176
156
|
raw.loadDir === loadDir &&
|
|
177
157
|
typeof raw.extensionId === "string" &&
|
|
178
158
|
/^[a-z]{32}$/.test(raw.extensionId) &&
|
|
159
|
+
typeof raw.extensionInstanceId === "string" &&
|
|
160
|
+
raw.extensionInstanceId.length > 0 &&
|
|
179
161
|
raw.serviceWorker === "RUNNING" &&
|
|
162
|
+
raw.evidenceSource === "PAIRING_HEARTBEAT" &&
|
|
180
163
|
typeof raw.observedAt === "string" &&
|
|
181
164
|
!Number.isNaN(Date.parse(raw.observedAt))
|
|
182
165
|
? raw
|
|
@@ -186,28 +169,19 @@ async function readEvidence(context, loadDir) {
|
|
|
186
169
|
return undefined;
|
|
187
170
|
}
|
|
188
171
|
}
|
|
189
|
-
function input(context) {
|
|
190
|
-
const value = context.input;
|
|
191
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
192
|
-
return {};
|
|
193
|
-
const extensionId = Reflect.get(value, "extensionId"), serviceWorker = Reflect.get(value, "serviceWorker"), observedAt = Reflect.get(value, "observedAt");
|
|
194
|
-
return {
|
|
195
|
-
...(typeof extensionId === "string" ? { extensionId } : {}),
|
|
196
|
-
...(serviceWorker === "RUNNING"
|
|
197
|
-
? { serviceWorker: "RUNNING" }
|
|
198
|
-
: {}),
|
|
199
|
-
...(typeof observedAt === "string" ? { observedAt } : {}),
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
172
|
async function ownFacts(context) {
|
|
203
173
|
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
204
174
|
const loadDir = browserExtensionLoadDir(context.workspaceRoot);
|
|
205
175
|
const bridgeTokenFile = await ensureModuleSecretFile(context, descriptor.moduleRef, "bridge");
|
|
206
176
|
const bridgePort = deterministicLoopbackPort(context, descriptor.moduleRef, "bridge");
|
|
177
|
+
const provisioningBridgeTokenFile = await ensureModuleSecretFile(context, descriptor.moduleRef, "provisioning");
|
|
178
|
+
const provisioningBridgePort = deterministicLoopbackPort(context, descriptor.moduleRef, "provisioning");
|
|
207
179
|
const facts = {
|
|
208
180
|
loadDir,
|
|
209
181
|
bridgeTokenFile,
|
|
210
182
|
bridgeEndpoint: `http://127.0.0.1:${bridgePort}`,
|
|
183
|
+
provisioningBridgeTokenFile,
|
|
184
|
+
provisioningBridgeEndpoint: `http://127.0.0.1:${provisioningBridgePort}`,
|
|
211
185
|
verificationEvidenceFile: verificationFile(context),
|
|
212
186
|
};
|
|
213
187
|
const setup = await readSetup(context);
|
|
@@ -221,7 +195,7 @@ async function ownFacts(context) {
|
|
|
221
195
|
await writeModuleSharedFacts(context, descriptor.moduleRef, facts);
|
|
222
196
|
return facts;
|
|
223
197
|
}
|
|
224
|
-
async function
|
|
198
|
+
async function materializeRuntimeConfig(context) {
|
|
225
199
|
const host = await readModuleSharedFacts(context, "platform-host");
|
|
226
200
|
const endpoint = factString(host, "endpoint"), taskTokenFile = factString(host, "taskApplicationTokenFile"), approvalTokenFile = factString(host, "approvalApplicationTokenFile");
|
|
227
201
|
if (!endpoint || !taskTokenFile || !approvalTokenFile)
|
|
@@ -230,11 +204,17 @@ async function materialize(context, setup) {
|
|
|
230
204
|
const loadDir = String(facts.loadDir);
|
|
231
205
|
const bridgeTokenFile = String(facts.bridgeTokenFile);
|
|
232
206
|
const bridgeEndpoint = String(facts.bridgeEndpoint);
|
|
233
|
-
const
|
|
207
|
+
const provisioningBridgeTokenFile = String(facts.provisioningBridgeTokenFile);
|
|
208
|
+
const provisioningBridgeEndpoint = String(facts.provisioningBridgeEndpoint);
|
|
209
|
+
await writeFile(join(loadDir, "runtime-config.json"), `${JSON.stringify({
|
|
234
210
|
proflowRuntimeBridge: {
|
|
235
211
|
endpoint: bridgeEndpoint,
|
|
236
212
|
token: await credential(bridgeTokenFile),
|
|
237
213
|
},
|
|
214
|
+
proflowProvisioningBridge: {
|
|
215
|
+
endpoint: provisioningBridgeEndpoint,
|
|
216
|
+
token: await credential(provisioningBridgeTokenFile),
|
|
217
|
+
},
|
|
238
218
|
proflowTaskApplication: {
|
|
239
219
|
endpoint,
|
|
240
220
|
token: await credential(taskTokenFile),
|
|
@@ -243,15 +223,91 @@ async function materialize(context, setup) {
|
|
|
243
223
|
endpoint,
|
|
244
224
|
token: await credential(approvalTokenFile),
|
|
245
225
|
},
|
|
226
|
+
}, null, 2)}\n`, { mode: 0o600 });
|
|
227
|
+
return {
|
|
228
|
+
facts,
|
|
229
|
+
loadDir,
|
|
230
|
+
bridgeTokenFile,
|
|
231
|
+
bridgeEndpoint,
|
|
232
|
+
provisioningBridgeTokenFile,
|
|
233
|
+
provisioningBridgeEndpoint,
|
|
234
|
+
endpoint,
|
|
235
|
+
taskTokenFile,
|
|
246
236
|
};
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
}
|
|
238
|
+
async function materializeExecutorConfig(context, setup, prepared) {
|
|
239
|
+
const resolved = prepared ?? (await materializeRuntimeConfig(context));
|
|
240
|
+
await writeFile(executorConfigFile(context), `${JSON.stringify({ platformHost: { endpoint: resolved.endpoint, tokenFile: resolved.taskTokenFile }, bridge: { extensionId: setup.extensionId, tokenFile: resolved.bridgeTokenFile, host: "127.0.0.1", port: Number(new URL(resolved.bridgeEndpoint).port) } }, null, 2)}\n`, { mode: 0o600 });
|
|
249
241
|
await writeModuleSharedFacts(context, descriptor.moduleRef, {
|
|
250
|
-
...facts,
|
|
242
|
+
...resolved.facts,
|
|
251
243
|
extensionId: setup.extensionId,
|
|
252
244
|
browserExecutorConfigPath: executorConfigFile(context),
|
|
253
245
|
});
|
|
254
246
|
}
|
|
247
|
+
async function materialize(context, setup) {
|
|
248
|
+
const prepared = await materializeRuntimeConfig(context);
|
|
249
|
+
await materializeExecutorConfig(context, setup, prepared);
|
|
250
|
+
}
|
|
251
|
+
async function persistPairedBrowserExtension(context, prepared, identity) {
|
|
252
|
+
const setup = { extensionId: identity.extensionId };
|
|
253
|
+
await writeFile(setupFile(context), `${JSON.stringify(setup, null, 2)}\n`, {
|
|
254
|
+
mode: 0o600,
|
|
255
|
+
});
|
|
256
|
+
await materializeExecutorConfig(context, setup, prepared);
|
|
257
|
+
const evidence = {
|
|
258
|
+
contract: "proflow.browser-extension-verification.v1",
|
|
259
|
+
moduleVersion: descriptor.moduleVersion,
|
|
260
|
+
loadDir: prepared.loadDir,
|
|
261
|
+
extensionId: identity.extensionId,
|
|
262
|
+
extensionInstanceId: identity.extensionInstanceId,
|
|
263
|
+
serviceWorker: "RUNNING",
|
|
264
|
+
evidenceSource: "PAIRING_HEARTBEAT",
|
|
265
|
+
observedAt: new Date().toISOString(),
|
|
266
|
+
};
|
|
267
|
+
await writeFile(verificationFile(context), `${JSON.stringify(evidence, null, 2)}\n`, { mode: 0o600 });
|
|
268
|
+
return identity;
|
|
269
|
+
}
|
|
270
|
+
export async function pairBrowserExtensionSetup(context, options = {}) {
|
|
271
|
+
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
272
|
+
await installPackage(context);
|
|
273
|
+
const existingSetup = await readSetup(context);
|
|
274
|
+
const existingEvidence = await readEvidence(context, browserExtensionLoadDir(context.workspaceRoot));
|
|
275
|
+
const canRevalidateExisting = Boolean(existingSetup &&
|
|
276
|
+
existingEvidence &&
|
|
277
|
+
existingSetup.extensionId === existingEvidence.extensionId);
|
|
278
|
+
const prepared = await materializeRuntimeConfig(context);
|
|
279
|
+
const pairing = await createBrowserExtensionPairingServer({
|
|
280
|
+
token: await credential(prepared.bridgeTokenFile),
|
|
281
|
+
host: "127.0.0.1",
|
|
282
|
+
port: Number(new URL(prepared.bridgeEndpoint).port),
|
|
283
|
+
pairingTimeoutMs: options.timeoutMs ?? 120_000,
|
|
284
|
+
});
|
|
285
|
+
try {
|
|
286
|
+
const pairingPromise = pairing.waitForPairing();
|
|
287
|
+
if (canRevalidateExisting) {
|
|
288
|
+
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
289
|
+
const revalidationGraceMs = Math.min(2_000, Math.max(25, Math.floor(timeoutMs / 4)));
|
|
290
|
+
const revalidated = await Promise.race([
|
|
291
|
+
pairingPromise.then((identity) => ({
|
|
292
|
+
kind: "PAIRED",
|
|
293
|
+
identity,
|
|
294
|
+
})),
|
|
295
|
+
new Promise((resolve) => setTimeout(() => resolve({ kind: "WAITING" }), revalidationGraceMs)),
|
|
296
|
+
]);
|
|
297
|
+
if (revalidated.kind === "PAIRED") {
|
|
298
|
+
return persistPairedBrowserExtension(context, prepared, revalidated.identity);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
await options.onWaiting?.({
|
|
302
|
+
loadDir: prepared.loadDir,
|
|
303
|
+
endpoint: pairing.endpoint,
|
|
304
|
+
});
|
|
305
|
+
return persistPairedBrowserExtension(context, prepared, await pairingPromise);
|
|
306
|
+
}
|
|
307
|
+
finally {
|
|
308
|
+
await pairing.close();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
255
311
|
const failed = (code, message, retryable = true) => ({
|
|
256
312
|
...base,
|
|
257
313
|
ok: false,
|
|
@@ -274,7 +330,7 @@ export const behaviorAdapter = {
|
|
|
274
330
|
const loadDir = browserExtensionLoadDir(context.workspaceRoot);
|
|
275
331
|
const setup = await readSetup(context);
|
|
276
332
|
const evidence = await readEvidence(context, loadDir);
|
|
277
|
-
const setupReady = Boolean(setup && evidence);
|
|
333
|
+
const setupReady = Boolean(setup && evidence && setup.extensionId === evidence.extensionId);
|
|
278
334
|
return {
|
|
279
335
|
result: {
|
|
280
336
|
...base,
|
|
@@ -305,61 +361,27 @@ export const behaviorAdapter = {
|
|
|
305
361
|
},
|
|
306
362
|
setup: async (context) => {
|
|
307
363
|
await mkdir(stateDir(context), { recursive: true, mode: 0o700 });
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
364
|
+
await installPackage(context);
|
|
365
|
+
const setup = await readSetup(context);
|
|
366
|
+
const evidence = await readEvidence(context, browserExtensionLoadDir(context.workspaceRoot));
|
|
367
|
+
if (setup && evidence && setup.extensionId === evidence.extensionId) {
|
|
368
|
+
try {
|
|
369
|
+
await materialize(context, setup);
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
312
372
|
return {
|
|
313
373
|
result: {
|
|
314
|
-
...failed("SETUP_FAILED",
|
|
374
|
+
...failed("SETUP_FAILED", error instanceof Error
|
|
375
|
+
? error.message
|
|
376
|
+
: "browser extension config materialization failed"),
|
|
315
377
|
data: blockedSetupPlan,
|
|
316
378
|
},
|
|
317
379
|
observedEffects: [],
|
|
318
380
|
};
|
|
319
|
-
|
|
320
|
-
await writeFile(setupFile(context), `${JSON.stringify(setup, null, 2)}\n`, { mode: 0o600 });
|
|
321
|
-
}
|
|
322
|
-
if (!setup)
|
|
323
|
-
return {
|
|
324
|
-
result: {
|
|
325
|
-
...base,
|
|
326
|
-
ok: false,
|
|
327
|
-
status: "ACTION_REQUIRED",
|
|
328
|
-
data: setupPlan,
|
|
329
|
-
actionRequired: {
|
|
330
|
-
action: "load-unpacked-extension",
|
|
331
|
-
description: `Load ${browserExtensionLoadDir(context.workspaceRoot)} in Chrome, copy its extensionId, then run proflow-execution-browser-extension setup --extension-id <id>.`,
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
observedEffects: [],
|
|
335
|
-
};
|
|
336
|
-
try {
|
|
337
|
-
await materialize(context, setup);
|
|
338
|
-
}
|
|
339
|
-
catch (error) {
|
|
340
|
-
return {
|
|
341
|
-
result: {
|
|
342
|
-
...failed("SETUP_FAILED", error instanceof Error
|
|
343
|
-
? error.message
|
|
344
|
-
: "browser extension config materialization failed"),
|
|
345
|
-
data: blockedSetupPlan,
|
|
346
|
-
},
|
|
347
|
-
observedEffects: [],
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
if (supplied.serviceWorker === "RUNNING") {
|
|
351
|
-
const evidence = {
|
|
352
|
-
contract: "proflow.browser-extension-verification.v1",
|
|
353
|
-
moduleVersion: descriptor.moduleVersion,
|
|
354
|
-
loadDir: browserExtensionLoadDir(context.workspaceRoot),
|
|
355
|
-
extensionId: setup.extensionId,
|
|
356
|
-
serviceWorker: "RUNNING",
|
|
357
|
-
observedAt: supplied.observedAt ?? new Date().toISOString(),
|
|
358
|
-
};
|
|
359
|
-
await writeFile(verificationFile(context), `${JSON.stringify(evidence, null, 2)}\n`, { mode: 0o600 });
|
|
381
|
+
}
|
|
360
382
|
return {
|
|
361
383
|
result: base,
|
|
362
|
-
observedEffects: ["
|
|
384
|
+
observedEffects: ["Uses heartbeat-proven Chrome MV3 pairing evidence"],
|
|
363
385
|
};
|
|
364
386
|
}
|
|
365
387
|
return {
|
|
@@ -369,8 +391,8 @@ export const behaviorAdapter = {
|
|
|
369
391
|
status: "ACTION_REQUIRED",
|
|
370
392
|
data: setupPlan,
|
|
371
393
|
actionRequired: {
|
|
372
|
-
action: "
|
|
373
|
-
description:
|
|
394
|
+
action: "load-unpacked-extension",
|
|
395
|
+
description: `Run pnpm exec -- proflow-execution-browser-extension setup --workspace ${context.workspaceRoot}; then enable Developer Mode and load ${browserExtensionLoadDir(context.workspaceRoot)}. The package CLI materializes bootstrap config, discovers the extension and verifies its heartbeat automatically.`,
|
|
374
396
|
},
|
|
375
397
|
},
|
|
376
398
|
observedEffects: [],
|
|
@@ -3,7 +3,7 @@ export declare const descriptor: {
|
|
|
3
3
|
readonly contractVersion: "1.0.0";
|
|
4
4
|
readonly moduleRef: "execution-browser-extension";
|
|
5
5
|
readonly packageName: "@tomflow/proflow-execution-browser-extension";
|
|
6
|
-
readonly moduleVersion: "0.1.
|
|
6
|
+
readonly moduleVersion: "0.1.13";
|
|
7
7
|
readonly kind: "browser-extension";
|
|
8
8
|
readonly templateVersion: "1.0.0";
|
|
9
9
|
readonly platformCompatibility: ">=1.0.0 <2.0.0";
|
|
@@ -14,6 +14,9 @@ export declare const descriptor: {
|
|
|
14
14
|
readonly provides: readonly [{
|
|
15
15
|
readonly contractRef: "execution-browser-executor";
|
|
16
16
|
readonly version: "1.0.0";
|
|
17
|
+
}, {
|
|
18
|
+
readonly contractRef: "custom-gpt-web-provisioning";
|
|
19
|
+
readonly version: "1.0.0";
|
|
17
20
|
}];
|
|
18
21
|
readonly requires: readonly [{
|
|
19
22
|
readonly contractRef: "execution";
|