@tomflow/proflow-execution-browser-extension 0.1.37 → 0.1.39
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/dist/deployment/adapter.d.ts +10 -10
- package/dist/deployment/descriptor.d.ts +1 -1
- package/dist/deployment/descriptor.js +1 -1
- package/dist/extension/background.js +687 -19
- package/dist/extension/content.js +267 -28
- package/dist/extension/tasks.js +74 -3
- package/dist/src/bridge.js +59 -6
- package/dist/src/carrier-attention-view.d.ts +13 -0
- package/dist/src/carrier-attention-view.js +54 -0
- package/dist/src/carrier-attention.d.ts +26 -0
- package/dist/src/carrier-attention.js +44 -0
- package/dist/src/carrier-continuation-control.d.ts +44 -0
- package/dist/src/carrier-continuation-control.js +114 -0
- package/dist/src/carrier-permission-attempt.d.ts +18 -0
- package/dist/src/carrier-permission-attempt.js +68 -0
- package/dist/src/carrier-permission-lifecycle.d.ts +34 -0
- package/dist/src/carrier-permission-lifecycle.js +73 -0
- package/dist/src/carrier-permission.d.ts +16 -0
- package/dist/src/carrier-permission.js +65 -0
- package/dist/src/chatgpt-runtime-adapter.d.ts +16 -0
- package/dist/src/chatgpt-runtime-adapter.js +173 -0
- package/dist/src/composer-submit.d.ts +12 -0
- package/dist/src/composer-submit.js +22 -0
- package/dist/src/pairing.js +13 -0
- package/extension/background.ts +622 -17
- package/extension/content.ts +40 -39
- package/extension/tasks.html +6 -0
- package/extension/tasks.ts +93 -5
- package/manifest.json +1 -1
- package/package.json +3 -3
- package/proflow.module.json +1 -1
package/extension/content.ts
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
|
+
import type { PermissionSemanticAction } from "../src/carrier-permission.js";
|
|
2
|
+
import {
|
|
3
|
+
observeChatGptPage,
|
|
4
|
+
performChatGptPermissionAction,
|
|
5
|
+
submitChatGptComposer,
|
|
6
|
+
writeChatGptInput,
|
|
7
|
+
} from "../src/chatgpt-runtime-adapter.js";
|
|
1
8
|
import { containsSubmittedFingerprint } from "../src/submitted-message.js";
|
|
2
9
|
|
|
3
|
-
type PageState = "IDLE" | "BUSY" | "BLOCKED" | "UNKNOWN";
|
|
4
10
|
type ContentCommand = {
|
|
5
11
|
type: "PROFLOW_PAGE_COMMAND";
|
|
6
12
|
contentInstanceId: string;
|
|
7
13
|
expectedUrl: string;
|
|
8
|
-
operation:
|
|
14
|
+
operation:
|
|
15
|
+
| "observe"
|
|
16
|
+
| "input"
|
|
17
|
+
| "click"
|
|
18
|
+
| "submit"
|
|
19
|
+
| "verify"
|
|
20
|
+
| "permissionAction";
|
|
9
21
|
selector?: string;
|
|
10
22
|
value?: string;
|
|
11
23
|
fingerprint?: string;
|
|
24
|
+
permissionFingerprint?: string;
|
|
25
|
+
permissionAction?: PermissionSemanticAction;
|
|
12
26
|
};
|
|
27
|
+
type ContentSnapshotRequest = { type: "PROFLOW_PAGE_SNAPSHOT_REQUEST" };
|
|
13
28
|
type ChromeContent = {
|
|
14
29
|
runtime: {
|
|
15
30
|
sendMessage(message: unknown): Promise<unknown>;
|
|
16
31
|
onMessage: {
|
|
17
32
|
addListener(
|
|
18
33
|
listener: (
|
|
19
|
-
message: ContentCommand,
|
|
34
|
+
message: ContentCommand | ContentSnapshotRequest,
|
|
20
35
|
sender: unknown,
|
|
21
36
|
sendResponse: (value: unknown) => void,
|
|
22
37
|
) => boolean | undefined,
|
|
@@ -28,22 +43,8 @@ declare const chrome: ChromeContent;
|
|
|
28
43
|
|
|
29
44
|
const contentInstanceId = `content:${crypto.randomUUID()}`;
|
|
30
45
|
|
|
31
|
-
function pageState():
|
|
32
|
-
|
|
33
|
-
return { pageState: "BLOCKED", activityKind: "ACTION_PERMISSION" };
|
|
34
|
-
if (
|
|
35
|
-
document.querySelector(
|
|
36
|
-
'[data-testid="stop-button"], button[aria-label*="Stop"]',
|
|
37
|
-
)
|
|
38
|
-
)
|
|
39
|
-
return { pageState: "BUSY", activityKind: "GENERATING" };
|
|
40
|
-
if (
|
|
41
|
-
document.querySelector(
|
|
42
|
-
'#prompt-textarea, textarea, [contenteditable="true"]',
|
|
43
|
-
)
|
|
44
|
-
)
|
|
45
|
-
return { pageState: "IDLE", activityKind: null };
|
|
46
|
-
return { pageState: "UNKNOWN", activityKind: null };
|
|
46
|
+
function pageState(): ReturnType<typeof observeChatGptPage> {
|
|
47
|
+
return observeChatGptPage(document);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
function observation() {
|
|
@@ -76,6 +77,7 @@ function hasFingerprint(fingerprint: string | undefined): boolean {
|
|
|
76
77
|
|
|
77
78
|
chrome.runtime.onMessage.addListener((command, _sender, sendResponse) => {
|
|
78
79
|
void (async () => {
|
|
80
|
+
if (command.type === "PROFLOW_PAGE_SNAPSHOT_REQUEST") return observation();
|
|
79
81
|
if (
|
|
80
82
|
command.type !== "PROFLOW_PAGE_COMMAND" ||
|
|
81
83
|
command.contentInstanceId !== contentInstanceId ||
|
|
@@ -88,33 +90,32 @@ chrome.runtime.onMessage.addListener((command, _sender, sendResponse) => {
|
|
|
88
90
|
...observation(),
|
|
89
91
|
verified: hasFingerprint(command.fingerprint),
|
|
90
92
|
};
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
+
if (command.operation === "permissionAction") {
|
|
94
|
+
if (!command.permissionFingerprint || !command.permissionAction)
|
|
95
|
+
throw new Error("PERMISSION_ACTION_INVALID");
|
|
96
|
+
performChatGptPermissionAction(
|
|
97
|
+
document,
|
|
98
|
+
command.permissionFingerprint,
|
|
99
|
+
command.permissionAction,
|
|
100
|
+
);
|
|
101
|
+
return observation();
|
|
102
|
+
}
|
|
103
|
+
if (pageState().pageState === "BLOCKED") throw new Error("PAGE_BLOCKED");
|
|
93
104
|
if (command.operation === "click") {
|
|
94
105
|
safeElement(command.selector).click();
|
|
95
106
|
return observation();
|
|
96
107
|
}
|
|
97
|
-
const input = safeElement(command.selector ?? "#prompt-textarea");
|
|
98
108
|
if (command.value === undefined || command.value.length > 4_096)
|
|
99
109
|
throw new Error("INPUT_BUDGET_EXCEEDED");
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
new InputEvent("input", {
|
|
109
|
-
bubbles: true,
|
|
110
|
-
inputType: "insertText",
|
|
111
|
-
data: command.value,
|
|
112
|
-
}),
|
|
110
|
+
if (command.operation === "submit") {
|
|
111
|
+
await submitChatGptComposer(document, command.value);
|
|
112
|
+
return observation();
|
|
113
|
+
}
|
|
114
|
+
writeChatGptInput(
|
|
115
|
+
document,
|
|
116
|
+
command.selector ?? "#prompt-textarea",
|
|
117
|
+
command.value,
|
|
113
118
|
);
|
|
114
|
-
if (command.operation === "submit")
|
|
115
|
-
safeElement(
|
|
116
|
-
'button[data-testid="send-button"], button[aria-label*="Send"]',
|
|
117
|
-
).click();
|
|
118
119
|
return observation();
|
|
119
120
|
})().then(
|
|
120
121
|
(value) => sendResponse({ ok: true, value }),
|
package/extension/tasks.html
CHANGED
|
@@ -55,6 +55,12 @@
|
|
|
55
55
|
<div id="nodes"></div>
|
|
56
56
|
</section>
|
|
57
57
|
|
|
58
|
+
<section>
|
|
59
|
+
<h2>Carrier Attention</h2>
|
|
60
|
+
<p class="meta">Transient Browser reality requiring a human decision. This is not an Execution Approval fact; Allow uses one-time permission only.</p>
|
|
61
|
+
<div id="carrier-attentions" class="meta">No carrier attention.</div>
|
|
62
|
+
</section>
|
|
63
|
+
|
|
58
64
|
<section>
|
|
59
65
|
<h2>Execution Approval</h2>
|
|
60
66
|
<p class="meta">Approval is an Execution-owned durable fact. This page only sends authenticated human decisions to the Execution Owner.</p>
|
package/extension/tasks.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { parseCarrierAttentionViews } from "../src/carrier-attention-view.js";
|
|
2
2
|
|
|
3
3
|
type ChromePanel = {
|
|
4
4
|
runtime: { sendMessage(message: unknown): Promise<unknown> };
|
|
5
5
|
};
|
|
6
6
|
declare const chrome: ChromePanel;
|
|
7
7
|
|
|
8
|
-
const extensionRuntime =
|
|
9
|
-
typeof chrome !== "undefined" && chrome.runtime ? chrome.runtime : null;
|
|
8
|
+
const extensionRuntime = typeof chrome === "undefined" ? null : chrome.runtime;
|
|
10
9
|
|
|
11
10
|
type TaskSummary = {
|
|
12
11
|
taskId: string;
|
|
@@ -65,6 +64,7 @@ const startButton = element<HTMLButtonElement>("#start-task");
|
|
|
65
64
|
const ensureWorkersButton = element<HTMLButtonElement>("#ensure-workers");
|
|
66
65
|
const newTaskForm = element<HTMLFormElement>("#new-task-form");
|
|
67
66
|
const approvalsTarget = element<HTMLElement>("#approvals");
|
|
67
|
+
const carrierAttentionsTarget = element<HTMLElement>("#carrier-attentions");
|
|
68
68
|
const systemAssessmentTarget = element<HTMLElement>("#system-assessment");
|
|
69
69
|
|
|
70
70
|
let selected: TaskView | null = null;
|
|
@@ -101,7 +101,8 @@ async function taskApplication(
|
|
|
101
101
|
operation: string,
|
|
102
102
|
input: Record<string, unknown>,
|
|
103
103
|
): Promise<unknown> {
|
|
104
|
-
if (!extensionRuntime)
|
|
104
|
+
if (!extensionRuntime)
|
|
105
|
+
return webApplication("/tasks/api/task", operation, input);
|
|
105
106
|
const raw = await extensionRuntime.sendMessage({
|
|
106
107
|
type: "PROFLOW_TASK_APPLICATION",
|
|
107
108
|
operation,
|
|
@@ -138,6 +139,90 @@ async function approvalApplication(
|
|
|
138
139
|
return response.value;
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
async function carrierAttentionAction(
|
|
143
|
+
attentionRef: string,
|
|
144
|
+
action: "allowOnce" | "deny",
|
|
145
|
+
): Promise<void> {
|
|
146
|
+
if (!extensionRuntime) {
|
|
147
|
+
const response = await fetch("/tasks/api/carrier-attention", {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: { "content-type": "application/json" },
|
|
150
|
+
body: JSON.stringify({ attentionRef, action }),
|
|
151
|
+
});
|
|
152
|
+
const body = record(await response.json());
|
|
153
|
+
if (!response.ok || body.ok !== true)
|
|
154
|
+
throw new Error(
|
|
155
|
+
typeof body.error === "string"
|
|
156
|
+
? body.error
|
|
157
|
+
: "CARRIER_ATTENTION_ACTION_FAILED",
|
|
158
|
+
);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const raw = await extensionRuntime.sendMessage({
|
|
162
|
+
type: "PROFLOW_CARRIER_ATTENTION_ACTION",
|
|
163
|
+
input: { attentionRef, action },
|
|
164
|
+
});
|
|
165
|
+
const response = record(raw);
|
|
166
|
+
if (response.ok !== true)
|
|
167
|
+
throw new Error(
|
|
168
|
+
typeof response.error === "string"
|
|
169
|
+
? response.error
|
|
170
|
+
: "CARRIER_ATTENTION_ACTION_FAILED",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function renderCarrierAttentions(snapshot: Record<string, unknown>): void {
|
|
175
|
+
const attentions = parseCarrierAttentionViews(snapshot.carrierAttentions);
|
|
176
|
+
carrierAttentionsTarget.replaceChildren();
|
|
177
|
+
if (attentions.length === 0) {
|
|
178
|
+
carrierAttentionsTarget.textContent = "No carrier attention.";
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
for (const attention of attentions) {
|
|
182
|
+
const row = document.createElement("div");
|
|
183
|
+
row.className = "task";
|
|
184
|
+
const label = document.createElement("div");
|
|
185
|
+
label.textContent = `${attention.operationId} · ${attention.targetHost ?? "unknown target"}`;
|
|
186
|
+
const detail = document.createElement("div");
|
|
187
|
+
detail.className = "meta";
|
|
188
|
+
detail.textContent = [
|
|
189
|
+
attention.reason,
|
|
190
|
+
attention.taskId ? `task ${attention.taskId}` : "task unknown",
|
|
191
|
+
attention.roleRef ? `role ${attention.roleRef}` : "role unknown",
|
|
192
|
+
].join(" · ");
|
|
193
|
+
row.append(label, detail);
|
|
194
|
+
if (attention.actions.includes("allowOnce")) {
|
|
195
|
+
const allow = document.createElement("button");
|
|
196
|
+
allow.type = "button";
|
|
197
|
+
allow.textContent = "Allow once";
|
|
198
|
+
allow.addEventListener(
|
|
199
|
+
"click",
|
|
200
|
+
() =>
|
|
201
|
+
void run(async () => {
|
|
202
|
+
await carrierAttentionAction(attention.attentionRef, "allowOnce");
|
|
203
|
+
await refreshBrowserStatus();
|
|
204
|
+
}),
|
|
205
|
+
);
|
|
206
|
+
row.append(allow);
|
|
207
|
+
}
|
|
208
|
+
if (attention.actions.includes("deny")) {
|
|
209
|
+
const deny = document.createElement("button");
|
|
210
|
+
deny.type = "button";
|
|
211
|
+
deny.textContent = "Deny";
|
|
212
|
+
deny.addEventListener(
|
|
213
|
+
"click",
|
|
214
|
+
() =>
|
|
215
|
+
void run(async () => {
|
|
216
|
+
await carrierAttentionAction(attention.attentionRef, "deny");
|
|
217
|
+
await refreshBrowserStatus();
|
|
218
|
+
}),
|
|
219
|
+
);
|
|
220
|
+
row.append(deny);
|
|
221
|
+
}
|
|
222
|
+
carrierAttentionsTarget.append(row);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
141
226
|
async function refreshApprovals() {
|
|
142
227
|
const value = record(
|
|
143
228
|
await approvalApplication("approval.list", { status: "PENDING" }),
|
|
@@ -254,7 +339,9 @@ async function refreshTasks() {
|
|
|
254
339
|
async function pageStatus(): Promise<Record<string, unknown>> {
|
|
255
340
|
if (extensionRuntime)
|
|
256
341
|
return record(
|
|
257
|
-
await extensionRuntime.sendMessage({
|
|
342
|
+
await extensionRuntime.sendMessage({
|
|
343
|
+
type: "PROFLOW_SIDE_PANEL_SNAPSHOT",
|
|
344
|
+
}),
|
|
258
345
|
);
|
|
259
346
|
const response = await fetch("/tasks/api/status", { cache: "no-store" });
|
|
260
347
|
const body = record(await response.json());
|
|
@@ -267,6 +354,7 @@ async function pageStatus(): Promise<Record<string, unknown>> {
|
|
|
267
354
|
|
|
268
355
|
async function refreshBrowserStatus() {
|
|
269
356
|
const snapshot = await pageStatus();
|
|
357
|
+
renderCarrierAttentions(snapshot);
|
|
270
358
|
connection.textContent =
|
|
271
359
|
snapshot.taskApplicationConfigured === true &&
|
|
272
360
|
snapshot.approvalApplicationConfigured === true
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomflow/proflow-execution-browser-extension",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"description": "Execution-owned MV3 Browser executor, evidence provider and browser application surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"@tomflow/proflow-module-contract": "^0.1.13"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@tomflow/proflow-
|
|
35
|
-
"@tomflow/proflow-
|
|
34
|
+
"@tomflow/proflow-execution-runtime": "^0.1.17",
|
|
35
|
+
"@tomflow/proflow-deployment-conformance": "^0.1.13"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
38
38
|
"proflow",
|
package/proflow.module.json
CHANGED
|
@@ -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.
|
|
6
|
+
"moduleVersion": "0.1.39",
|
|
7
7
|
"kind": "browser-extension",
|
|
8
8
|
"templateVersion": "1.0.0",
|
|
9
9
|
"platformCompatibility": ">=1.0.0 <2.0.0",
|