@tomflow/proflow-execution-browser-extension 0.1.11 → 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.
@@ -0,0 +1,936 @@
1
+ "use strict";
2
+ (() => {
3
+ // packages/execution-browser-extension/src/custom-gpt-editor-driver.ts
4
+ function record(value) {
5
+ if (typeof value !== "object" || value === null || Array.isArray(value))
6
+ throw new TypeError("PROVISIONING_REQUEST_INVALID");
7
+ return value;
8
+ }
9
+ function requiredString(value) {
10
+ if (typeof value !== "string" || value.trim().length === 0)
11
+ throw new TypeError("PROVISIONING_REQUEST_INVALID");
12
+ return value;
13
+ }
14
+ function stringArray(value) {
15
+ if (!Array.isArray(value) || value.length === 0 || !value.every((item) => typeof item === "string" && item.trim().length > 0))
16
+ throw new TypeError("PROVISIONING_REQUEST_INVALID");
17
+ return [...value];
18
+ }
19
+ function knowledgeFiles(value) {
20
+ if (!Array.isArray(value) || value.length === 0 || value.length > 64)
21
+ throw new TypeError("PROVISIONING_REQUEST_INVALID");
22
+ return value.map((candidate) => {
23
+ const file = record(candidate);
24
+ const name = requiredString(file.name);
25
+ const mime = requiredString(file.mime);
26
+ const sha256 = requiredString(file.sha256);
27
+ const url = requiredString(file.url);
28
+ const sizeBytes = file.sizeBytes;
29
+ if (!Number.isInteger(sizeBytes) || Number(sizeBytes) <= 0 || !/^sha256:[0-9a-f]{64}$/.test(sha256))
30
+ throw new TypeError("PROVISIONING_REQUEST_INVALID");
31
+ let parsed;
32
+ try {
33
+ parsed = new URL(url);
34
+ } catch {
35
+ throw new TypeError("PROVISIONING_REQUEST_INVALID");
36
+ }
37
+ if (parsed.protocol !== "http:" || parsed.hostname !== "127.0.0.1" || !parsed.pathname.startsWith("/v1/provisioning/files/") || parsed.username !== "" || parsed.password !== "")
38
+ throw new TypeError("PROVISIONING_REQUEST_INVALID");
39
+ return { name, mime, sizeBytes: Number(sizeBytes), sha256, url };
40
+ });
41
+ }
42
+ function parseCustomGptProvisioningRequest(input) {
43
+ const source = record(input);
44
+ const capabilities = record(source.capabilities);
45
+ for (const name of [
46
+ "webSearch",
47
+ "imageGeneration",
48
+ "codeInterpreter"
49
+ ])
50
+ if (typeof capabilities[name] !== "boolean")
51
+ throw new TypeError("PROVISIONING_REQUEST_INVALID");
52
+ return {
53
+ packageName: requiredString(source.packageName),
54
+ version: requiredString(source.version),
55
+ displayName: requiredString(source.displayName),
56
+ description: requiredString(source.description),
57
+ instructions: requiredString(source.instructions),
58
+ conversationStarters: stringArray(source.conversationStarters),
59
+ recommendedModel: requiredString(source.recommendedModel),
60
+ capabilities: {
61
+ webSearch: capabilities.webSearch,
62
+ imageGeneration: capabilities.imageGeneration,
63
+ codeInterpreter: capabilities.codeInterpreter
64
+ },
65
+ knowledgeBundle: requiredString(source.knowledgeBundle),
66
+ actionSchema: requiredString(source.actionSchema),
67
+ knowledgeFiles: knowledgeFiles(source.knowledgeFiles),
68
+ ...source.bearerCredential === void 0 ? {} : { bearerCredential: requiredString(source.bearerCredential) }
69
+ };
70
+ }
71
+ function createCustomGptEditorDriver(port) {
72
+ const configureDraft = async (material) => {
73
+ await port.setTextField("displayName", material.displayName);
74
+ await port.setTextField("description", material.description);
75
+ await port.setTextField("instructions", material.instructions);
76
+ await port.replaceConversationStarters(material.conversationStarters);
77
+ await port.selectRecommendedModel(material.recommendedModel);
78
+ for (const capability of [
79
+ "webSearch",
80
+ "imageGeneration",
81
+ "codeInterpreter"
82
+ ])
83
+ await port.setCapability(capability, material.capabilities[capability]);
84
+ await port.installActionSchema(material.actionSchema);
85
+ if (material.bearerCredential)
86
+ await port.configureBearerAuth(material.bearerCredential);
87
+ return {
88
+ status: "DRAFT_CONFIGURED",
89
+ packageName: material.packageName,
90
+ version: material.version
91
+ };
92
+ };
93
+ return Object.freeze({
94
+ configureDraft,
95
+ async provision(material) {
96
+ await configureDraft(material);
97
+ await port.uploadKnowledge(material.knowledgeFiles);
98
+ await port.verifyReady(material);
99
+ const live = await port.createPrivate();
100
+ return {
101
+ status: "LIVE_CREATED",
102
+ packageName: material.packageName,
103
+ version: material.version,
104
+ ...live
105
+ };
106
+ }
107
+ });
108
+ }
109
+
110
+ // packages/execution-browser-extension/extension/provisioning-content.ts
111
+ var provisioningSurface = Object.freeze({
112
+ kind: "CUSTOM_GPT_DEPLOYMENT_PROVISIONING",
113
+ instanceId: `provisioning:${crypto.randomUUID()}`,
114
+ url: location.href
115
+ });
116
+ var sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
117
+ function normalize(value) {
118
+ return (value ?? "").replace(/\s+/g, " ").trim().toLowerCase();
119
+ }
120
+ function editorSurfaceReady() {
121
+ return location.protocol === "https:" && location.hostname === "chatgpt.com" && location.pathname.startsWith("/gpts/editor");
122
+ }
123
+ function elementSemanticText(element) {
124
+ return normalize(
125
+ [
126
+ element.getAttribute("aria-label"),
127
+ element.getAttribute("placeholder"),
128
+ element.getAttribute("name"),
129
+ element.textContent
130
+ ].filter(Boolean).join(" ")
131
+ );
132
+ }
133
+ function matchesAny(element, candidates) {
134
+ const semantic = elementSemanticText(element);
135
+ return candidates.some(
136
+ (candidate) => semantic.includes(normalize(candidate))
137
+ );
138
+ }
139
+ function controlFromLabel(label) {
140
+ if (label.htmlFor) {
141
+ const linked = document.getElementById(label.htmlFor);
142
+ if (linked instanceof HTMLElement) return linked;
143
+ }
144
+ const selector = 'input:not([type="file"]), textarea, select, [contenteditable="true"], [role="textbox"], [role="combobox"]';
145
+ const nested = label.querySelector(selector);
146
+ if (nested) return nested;
147
+ let scope = label.parentElement;
148
+ for (let depth = 0; depth < 4 && scope; depth += 1) {
149
+ const controls = [...scope.querySelectorAll(selector)];
150
+ if (controls.length === 1) return controls[0] ?? null;
151
+ scope = scope.parentElement;
152
+ }
153
+ return null;
154
+ }
155
+ function findControl(candidates) {
156
+ for (const label of document.querySelectorAll("label"))
157
+ if (matchesAny(label, candidates)) {
158
+ const control = controlFromLabel(label);
159
+ if (control) return control;
160
+ }
161
+ for (const element of document.querySelectorAll(
162
+ 'input, textarea, select, [contenteditable="true"], [role="textbox"], [role="combobox"]'
163
+ ))
164
+ if (matchesAny(element, candidates)) return element;
165
+ throw new Error(`GPT_EDITOR_CONTROL_NOT_FOUND:${candidates[0] ?? "unknown"}`);
166
+ }
167
+ function setControlValue(element, value) {
168
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
169
+ const prototype = element instanceof HTMLInputElement ? HTMLInputElement.prototype : element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLSelectElement.prototype;
170
+ const setter = Object.getOwnPropertyDescriptor(prototype, "value")?.set;
171
+ if (setter) setter.call(element, value);
172
+ else element.value = value;
173
+ } else {
174
+ element.textContent = value;
175
+ }
176
+ element.dispatchEvent(
177
+ new InputEvent("input", { bubbles: true, data: value })
178
+ );
179
+ element.dispatchEvent(new Event("change", { bubbles: true }));
180
+ }
181
+ function controlValue(element) {
182
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)
183
+ return element.value.replaceAll("\r\n", "\n");
184
+ return (element.textContent ?? "").replaceAll("\r\n", "\n");
185
+ }
186
+ async function waitForReadback(code, predicate) {
187
+ for (let attempt = 0; attempt < 600; attempt += 1) {
188
+ if (predicate()) return;
189
+ await sleep(100);
190
+ }
191
+ throw new Error(code);
192
+ }
193
+ function clickable(candidates) {
194
+ for (const element of document.querySelectorAll(
195
+ 'button, [role="button"], [role="option"], [role="menuitem"], [role="radio"]'
196
+ ))
197
+ if (matchesAny(element, candidates)) return element;
198
+ return null;
199
+ }
200
+ async function waitForClickable(candidates, attempts = 30) {
201
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
202
+ const element = clickable(candidates);
203
+ if (element) return element;
204
+ await sleep(100);
205
+ }
206
+ throw new Error(`GPT_EDITOR_ACTION_NOT_FOUND:${candidates[0] ?? "unknown"}`);
207
+ }
208
+ var fieldLabels = {
209
+ displayName: ["Name", "\u540D\u79F0", "Name your GPT", "\u4E3A\u4F60\u7684 GPT \u547D\u540D"],
210
+ description: [
211
+ "Description",
212
+ "\u63CF\u8FF0",
213
+ "Add a short description about what this GPT does",
214
+ "\u6DFB\u52A0\u6709\u5173\u6B64 GPT \u7684\u529F\u80FD\u7684\u7B80\u77ED\u63CF\u8FF0"
215
+ ],
216
+ instructions: ["Instructions", "\u6307\u4EE4"]
217
+ };
218
+ async function ensureConfigureMode() {
219
+ try {
220
+ findControl(fieldLabels.displayName);
221
+ return;
222
+ } catch {
223
+ }
224
+ const configure = await waitForClickable(["Configure", "\u914D\u7F6E"], 60);
225
+ configure.click();
226
+ for (let attempt = 0; attempt < 80; attempt += 1) {
227
+ try {
228
+ findControl(fieldLabels.displayName);
229
+ return;
230
+ } catch {
231
+ await sleep(100);
232
+ }
233
+ }
234
+ throw new Error("GPT_EDITOR_CONFIGURE_SURFACE_NOT_READY");
235
+ }
236
+ var capabilityLabels = {
237
+ webSearch: ["Web search", "\u7F51\u9875\u641C\u7D22", "\u7F51\u7EDC\u641C\u7D22", "\u6D4F\u89C8\u7F51\u9875"],
238
+ imageGeneration: ["Image generation", "\u56FE\u50CF\u751F\u6210", "\u56FE\u7247\u751F\u6210"],
239
+ codeInterpreter: [
240
+ "Code Interpreter & Data Analysis",
241
+ "Code Interpreter",
242
+ "Data Analysis",
243
+ "\u4EE3\u7801\u89E3\u91CA\u5668",
244
+ "\u4EE3\u7801\u89E3\u8BD1\u5668"
245
+ ]
246
+ };
247
+ function conversationStarterControls(minimum) {
248
+ const label = [...document.querySelectorAll("label")].find(
249
+ (element) => matchesAny(element, ["Conversation starter", "\u5BF9\u8BDD\u5F00\u573A\u767D"])
250
+ );
251
+ if (!label) return [];
252
+ let scope = label.parentElement;
253
+ for (let depth = 0; depth < 4 && scope; depth += 1) {
254
+ const controls = [
255
+ ...scope.querySelectorAll(
256
+ 'input[type="text"], textarea:not([data-testid="gizmo-instructions-input"])'
257
+ )
258
+ ];
259
+ if (controls.length >= minimum) return controls;
260
+ scope = scope.parentElement;
261
+ }
262
+ return [];
263
+ }
264
+ async function replaceConversationStarters(values) {
265
+ const controls = conversationStarterControls(values.length);
266
+ if (controls.length < values.length)
267
+ throw new Error("GPT_EDITOR_STARTER_CONTROL_NOT_FOUND");
268
+ for (let index = 0; index < values.length; index += 1)
269
+ setControlValue(controls[index], values[index] ?? "");
270
+ }
271
+ function currentGptId() {
272
+ const match = /\/(g-[a-zA-Z0-9_-]+)(?:\/|$)/.exec(location.pathname);
273
+ return match?.[1] ?? null;
274
+ }
275
+ async function knowledgeFileInput() {
276
+ const label = [...document.querySelectorAll("label")].find(
277
+ (element) => matchesAny(element, ["Knowledge", "\u77E5\u8BC6"])
278
+ );
279
+ if (label) {
280
+ let scope = label.parentElement;
281
+ for (let depth = 0; depth < 4 && scope; depth += 1) {
282
+ const inputs = [
283
+ ...scope.querySelectorAll('input[type="file"]')
284
+ ];
285
+ if (inputs.length === 1) return inputs[0];
286
+ scope = scope.parentElement;
287
+ }
288
+ }
289
+ const upload = clickable([
290
+ "Upload files",
291
+ "Upload file",
292
+ "Add files",
293
+ "\u4E0A\u4F20\u6587\u4EF6",
294
+ "\u6DFB\u52A0\u6587\u4EF6"
295
+ ]);
296
+ if (upload) upload.click();
297
+ for (let attempt = 0; attempt < 40; attempt += 1) {
298
+ const candidates = [
299
+ ...document.querySelectorAll('input[type="file"]')
300
+ ];
301
+ if (candidates.length === 1) return candidates[0];
302
+ await sleep(100);
303
+ }
304
+ throw new Error("GPT_EDITOR_KNOWLEDGE_INPUT_NOT_FOUND");
305
+ }
306
+ async function fetchKnowledgeRelay(url) {
307
+ const result = await chrome.runtime.sendMessage({
308
+ type: "PROFLOW_PROVISIONING_RELAY_FETCH",
309
+ url
310
+ });
311
+ if (typeof result !== "object" || result === null)
312
+ throw new Error("KNOWLEDGE_RELAY_BACKGROUND_INVALID");
313
+ const record2 = result;
314
+ if (record2.ok !== true || typeof record2.base64 !== "string")
315
+ throw new Error(
316
+ typeof record2.error === "string" ? record2.error : "KNOWLEDGE_RELAY_BACKGROUND_FAILED"
317
+ );
318
+ const binary = atob(record2.base64);
319
+ const bytes = new Uint8Array(binary.length);
320
+ for (let index = 0; index < binary.length; index += 1)
321
+ bytes[index] = binary.charCodeAt(index);
322
+ return bytes.buffer;
323
+ }
324
+ async function sha256Bytes(bytes) {
325
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
326
+ return `sha256:${[...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, "0")).join("")}`;
327
+ }
328
+ async function waitForKnowledgeName(name) {
329
+ await waitForReadback(
330
+ `GPT_EDITOR_KNOWLEDGE_UPLOAD_TIMEOUT:${name}`,
331
+ () => knowledgeReadbackMatches(name)
332
+ );
333
+ }
334
+ async function uploadKnowledge(files) {
335
+ for (const descriptor of files) {
336
+ const input = await knowledgeFileInput();
337
+ const bytes = await fetchKnowledgeRelay(descriptor.url);
338
+ if (bytes.byteLength !== descriptor.sizeBytes)
339
+ throw new Error("KNOWLEDGE_RELAY_SIZE_MISMATCH");
340
+ if (await sha256Bytes(bytes) !== descriptor.sha256)
341
+ throw new Error("KNOWLEDGE_RELAY_HASH_MISMATCH");
342
+ const transfer = new DataTransfer();
343
+ transfer.items.add(
344
+ new File([bytes], descriptor.name, { type: descriptor.mime })
345
+ );
346
+ input.files = transfer.files;
347
+ input.dispatchEvent(new Event("change", { bubbles: true }));
348
+ await waitForKnowledgeName(descriptor.name);
349
+ }
350
+ }
351
+ var privateVisibilityLabels = [
352
+ "Only me",
353
+ "Private",
354
+ "\u53EA\u6709\u6211",
355
+ "\u79C1\u6709",
356
+ "\u4EC5\u81EA\u5DF1"
357
+ ];
358
+ var nonPrivateVisibilityLabels = [
359
+ "Anyone",
360
+ "Public",
361
+ "Link",
362
+ "GPT Store",
363
+ "Workspace",
364
+ "\u6240\u6709\u4EBA",
365
+ "\u516C\u5F00",
366
+ "\u94FE\u63A5",
367
+ "\u5DE5\u4F5C\u533A"
368
+ ];
369
+ function semanticValues(element) {
370
+ const referencedText = (attribute) => (element.getAttribute(attribute) ?? "").split(/\s+/).filter(Boolean).map((id) => document.getElementById(id)?.textContent ?? "").join(" ");
371
+ return [
372
+ element.getAttribute("aria-label"),
373
+ referencedText("aria-labelledby"),
374
+ referencedText("aria-describedby"),
375
+ element.getAttribute("data-value"),
376
+ element.getAttribute("value"),
377
+ element.textContent
378
+ ].filter((value) => Boolean(value)).map(normalize);
379
+ }
380
+ function matchesExactSemantic(element, candidates) {
381
+ const expected = new Set(candidates.map(normalize));
382
+ return semanticValues(element).some((value) => expected.has(value));
383
+ }
384
+ function matchesBoundedSemantic(element, candidates) {
385
+ const values = semanticValues(element);
386
+ return candidates.some((candidate) => {
387
+ const expected = normalize(candidate);
388
+ return values.some(
389
+ (value) => value === expected || value.startsWith(`${expected} `) || value.endsWith(` ${expected}`)
390
+ );
391
+ });
392
+ }
393
+ function available(element) {
394
+ const style = getComputedStyle(element);
395
+ return element.getClientRects().length > 0 && style.display !== "none" && style.visibility !== "hidden" && element.getAttribute("aria-hidden") !== "true" && !element.hasAttribute("disabled") && element.getAttribute("aria-disabled") !== "true";
396
+ }
397
+ function privateVisibilityControl() {
398
+ const selector = 'input[type="radio"], label, button, [role="button"], [role="radio"], [role="option"], [role="menuitem"], [role="menuitemradio"]';
399
+ for (const element of document.querySelectorAll(selector)) {
400
+ if (available(element) && matchesBoundedSemantic(element, privateVisibilityLabels) && !matchesBoundedSemantic(element, nonPrivateVisibilityLabels))
401
+ return element;
402
+ }
403
+ return null;
404
+ }
405
+ function selected(element) {
406
+ if (element instanceof HTMLInputElement && element.type === "radio")
407
+ return element.checked;
408
+ if (element instanceof HTMLLabelElement && element.htmlFor) {
409
+ const control = document.getElementById(element.htmlFor);
410
+ if (control instanceof HTMLInputElement && control.type === "radio")
411
+ return control.checked;
412
+ }
413
+ return element.getAttribute("aria-checked") === "true" || element.getAttribute("aria-selected") === "true" || element.getAttribute("aria-pressed") === "true" || element.getAttribute("data-state") === "checked";
414
+ }
415
+ function carrierGptId(value) {
416
+ if (typeof value !== "string") throw new Error("ROLE_CARRIER_URL_INVALID");
417
+ const url = new URL(value);
418
+ const match = /^\/g\/(g-[A-Za-z0-9_-]+)$/.exec(url.pathname);
419
+ if (url.origin !== "https://chatgpt.com" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !match?.[1])
420
+ throw new Error("ROLE_CARRIER_URL_INVALID");
421
+ return match[1];
422
+ }
423
+ async function waitForAuthSemantic(root, candidates, selector = 'button, [role="button"], [role="radio"], [role="option"], label') {
424
+ for (let attempt = 0; attempt < 120; attempt += 1) {
425
+ for (const element of root.querySelectorAll(selector))
426
+ if (available(element) && matchesBoundedSemantic(element, candidates))
427
+ return element;
428
+ await sleep(100);
429
+ }
430
+ throw new Error(
431
+ `GPT_EDITOR_AUTH_CONTROL_NOT_FOUND:${candidates[0] ?? "unknown"}`
432
+ );
433
+ }
434
+ function existingActionEditButton() {
435
+ const actionLabel = [...document.querySelectorAll("label")].find(
436
+ (element) => matchesExactSemantic(element, ["Actions", "\u64CD\u4F5C"])
437
+ );
438
+ let scope = actionLabel?.parentElement ?? null;
439
+ for (let depth = 0; depth < 5 && scope; depth += 1) {
440
+ const buttons = [
441
+ ...scope.querySelectorAll('button, [role="button"]')
442
+ ].filter(available);
443
+ const create = buttons.find(
444
+ (element) => matchesBoundedSemantic(element, [
445
+ "Create new action",
446
+ "New action",
447
+ "\u521B\u5EFA\u65B0\u64CD\u4F5C"
448
+ ])
449
+ );
450
+ if (create) {
451
+ return buttons.find(
452
+ (element) => element !== create && elementSemanticText(element) === "" && normalize(element.parentElement?.parentElement?.textContent).length > 0
453
+ ) ?? null;
454
+ }
455
+ scope = scope.parentElement;
456
+ }
457
+ return null;
458
+ }
459
+ async function openExistingActionEditor() {
460
+ const authLabels = ["Authentication", "\u8EAB\u4EFD\u9A8C\u8BC1", "\u8BA4\u8BC1"];
461
+ if (clickable(authLabels)) return;
462
+ const edit = existingActionEditButton();
463
+ if (!edit) throw new Error("GPT_EDITOR_ACTION_EDIT_NOT_FOUND");
464
+ edit.click();
465
+ for (let attempt = 0; attempt < 120; attempt += 1) {
466
+ if (clickable(authLabels)) return;
467
+ try {
468
+ findControl(["OpenAPI schema", "Schema", "OpenAPI", "\u67B6\u6784"]);
469
+ return;
470
+ } catch {
471
+ }
472
+ await sleep(100);
473
+ }
474
+ throw new Error("GPT_EDITOR_ACTION_EDITOR_NOT_READY");
475
+ }
476
+ async function waitForAuthSettingsButton() {
477
+ const labels = ["Authentication", "\u8EAB\u4EFD\u9A8C\u8BC1", "\u8BA4\u8BC1"];
478
+ for (let attempt = 0; attempt < 120; attempt += 1) {
479
+ for (const label of document.querySelectorAll("label")) {
480
+ if (!available(label) || !matchesExactSemantic(label, labels)) continue;
481
+ let container = label.parentElement;
482
+ for (let depth = 0; depth < 4 && container; depth += 1) {
483
+ const buttons = [
484
+ ...container.querySelectorAll("button")
485
+ ].filter(available);
486
+ const [button] = buttons;
487
+ if (buttons.length === 1 && button) return button;
488
+ container = container.parentElement;
489
+ }
490
+ }
491
+ await sleep(100);
492
+ }
493
+ throw new Error("GPT_EDITOR_AUTH_SETTINGS_BUTTON_NOT_FOUND");
494
+ }
495
+ async function returnFromActionEditor() {
496
+ let back;
497
+ for (let attempt = 0; attempt < 80; attempt += 1) {
498
+ back = [...document.querySelectorAll("button")].find(
499
+ (button) => {
500
+ if ((button.textContent ?? "").trim().length > 0) return false;
501
+ const context = normalize(
502
+ button.parentElement?.parentElement?.textContent ?? ""
503
+ );
504
+ return context.includes("add action") || context.includes("\u6DFB\u52A0\u64CD\u4F5C") || context.includes("edit action") || context.includes("\u7F16\u8F91\u64CD\u4F5C");
505
+ }
506
+ );
507
+ if (back) break;
508
+ await sleep(100);
509
+ }
510
+ if (!back) throw new Error("GPT_EDITOR_ACTION_BACK_NOT_FOUND");
511
+ back.click();
512
+ await waitForReadback(
513
+ "GPT_EDITOR_CONFIGURE_RETURN_TIMEOUT",
514
+ () => [...document.querySelectorAll("label")].some(
515
+ (label) => matchesAny(label, ["Knowledge", "\u77E5\u8BC6"])
516
+ )
517
+ );
518
+ }
519
+ async function configureBearerAuthDraft(credential) {
520
+ if (credential.length < 32) throw new Error("ROLE_CREDENTIAL_INVALID");
521
+ await openExistingActionEditor();
522
+ (await waitForAuthSettingsButton()).click();
523
+ let dialog = null;
524
+ for (let attempt = 0; attempt < 600; attempt += 1) {
525
+ dialog = [...document.querySelectorAll('[role="dialog"]')].find(
526
+ (candidate) => available(candidate)
527
+ ) ?? null;
528
+ if (dialog) break;
529
+ await sleep(100);
530
+ }
531
+ if (!dialog) throw new Error("GPT_EDITOR_AUTH_DIALOG_NOT_FOUND");
532
+ const authRadio = async (labels) => {
533
+ for (let attempt = 0; attempt < 120; attempt += 1) {
534
+ for (const radio of dialog.querySelectorAll(
535
+ 'input[type="radio"], [role="radio"]'
536
+ )) {
537
+ let container = radio.parentElement;
538
+ for (let depth = 0; depth < 3 && container; depth += 1) {
539
+ if (matchesBoundedSemantic(container, labels)) return radio;
540
+ container = container.parentElement;
541
+ }
542
+ }
543
+ await sleep(100);
544
+ }
545
+ throw new Error(
546
+ `GPT_EDITOR_AUTH_RADIO_NOT_FOUND:${labels[0] ?? "unknown"}`
547
+ );
548
+ };
549
+ const apiKey = await authRadio(["API Key", "API \u5BC6\u94A5"]);
550
+ if (!selected(apiKey)) apiKey.click();
551
+ const bearer = await authRadio(["Bearer"]);
552
+ if (!selected(bearer)) bearer.click();
553
+ let keyInput = null;
554
+ for (let attempt = 0; attempt < 120; attempt += 1) {
555
+ const candidate = dialog.querySelector(
556
+ 'input[type="password"]'
557
+ );
558
+ if (candidate && available(candidate)) {
559
+ keyInput = candidate;
560
+ break;
561
+ }
562
+ await sleep(100);
563
+ }
564
+ if (!keyInput) throw new Error("GPT_EDITOR_AUTH_KEY_INPUT_NOT_FOUND");
565
+ setControlValue(keyInput, credential);
566
+ if (controlValue(keyInput) !== credential)
567
+ throw new Error("GPT_EDITOR_AUTH_KEY_READBACK_MISMATCH");
568
+ (await waitForAuthSemantic(dialog, ["Save", "\u4FDD\u5B58"])).click();
569
+ await waitForReadback(
570
+ "GPT_EDITOR_AUTH_SAVE_TIMEOUT",
571
+ () => !available(dialog)
572
+ );
573
+ await returnFromActionEditor();
574
+ }
575
+ function savedConfirmationVisible() {
576
+ const labels = [
577
+ "Settings saved",
578
+ "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58",
579
+ "GPT updated",
580
+ "GPT \u5DF2\u66F4\u65B0"
581
+ ];
582
+ return [...document.querySelectorAll('[role="dialog"]')].some(
583
+ (dialog) => available(dialog) && labels.some(
584
+ (label) => normalize(dialog.textContent).includes(normalize(label))
585
+ )
586
+ );
587
+ }
588
+ async function finalizeBearerAuth(credential) {
589
+ await configureBearerAuthDraft(credential);
590
+ (await waitForAuthSemantic(document, ["Update", "\u66F4\u65B0"])).click();
591
+ await waitForReadback(
592
+ "GPT_EDITOR_AUTH_UPDATE_TIMEOUT",
593
+ savedConfirmationVisible
594
+ );
595
+ const gptId = currentGptId();
596
+ if (!gptId) throw new Error("GPT_EDITOR_GPT_ID_MISSING");
597
+ return { status: "AUTH_UPDATED", gptId };
598
+ }
599
+ async function selectPrivateVisibility() {
600
+ let control = null;
601
+ for (let attempt = 0; attempt < 80; attempt += 1) {
602
+ control = privateVisibilityControl();
603
+ if (control) break;
604
+ await sleep(100);
605
+ }
606
+ if (!control) throw new Error("GPT_EDITOR_PRIVATE_CONTROL_NOT_FOUND");
607
+ if (!selected(control)) control.click();
608
+ for (let attempt = 0; attempt < 40; attempt += 1) {
609
+ const current = privateVisibilityControl();
610
+ if (current && selected(current)) return;
611
+ await sleep(100);
612
+ }
613
+ throw new Error("GPT_EDITOR_PRIVATE_SELECTION_NOT_CONFIRMED");
614
+ }
615
+ async function waitForPrivateCreateAction(initialCreateButton) {
616
+ const control = privateVisibilityControl();
617
+ const scopedRoot = control?.closest(
618
+ '[role="dialog"], [role="menu"], [role="listbox"]'
619
+ ) ?? document;
620
+ const labelGroups = [
621
+ ["Save", "\u4FDD\u5B58"],
622
+ ["Publish", "\u53D1\u5E03"],
623
+ ["Create", "\u521B\u5EFA"]
624
+ ];
625
+ for (let attempt = 0; attempt < 40; attempt += 1) {
626
+ for (const labels of labelGroups)
627
+ for (const element of scopedRoot.querySelectorAll(
628
+ 'button, [role="button"]'
629
+ ))
630
+ if (element !== initialCreateButton && available(element) && matchesBoundedSemantic(element, labels))
631
+ return element;
632
+ await sleep(100);
633
+ }
634
+ throw new Error("GPT_EDITOR_PRIVATE_CREATE_ACTION_NOT_FOUND");
635
+ }
636
+ function publishCreateButton() {
637
+ return [...document.querySelectorAll('button, [role="button"]')].find(
638
+ (element) => available(element) && element.getAttribute("role") !== "radio" && element.closest('[role="radiogroup"]') === null && matchesExactSemantic(element, ["Create", "\u521B\u5EFA"])
639
+ ) ?? null;
640
+ }
641
+ function fieldReadbackMatches(field, expected) {
642
+ try {
643
+ return controlValue(findControl(fieldLabels[field])) === expected;
644
+ } catch {
645
+ return false;
646
+ }
647
+ }
648
+ function starterReadbackMatches(values) {
649
+ const controls = conversationStarterControls(values.length);
650
+ if (controls.length < values.length) return false;
651
+ return values.every(
652
+ (value, index) => controlValue(controls[index]) === value
653
+ );
654
+ }
655
+ function modelReadbackMatches(value) {
656
+ try {
657
+ const selector = findControl([
658
+ "Recommended model",
659
+ "\u63A8\u8350\u6A21\u578B",
660
+ "\u63A8\u8350\u7684\u6A21\u578B",
661
+ value
662
+ ]);
663
+ if (selector instanceof HTMLSelectElement) {
664
+ const option = selector.selectedOptions[0];
665
+ return Boolean(
666
+ option && (option.value === value || normalize(option.textContent).endsWith(`(${normalize(value)})`))
667
+ );
668
+ }
669
+ return semanticValues(selector).some(
670
+ (candidate) => candidate.includes(normalize(value))
671
+ );
672
+ } catch {
673
+ return false;
674
+ }
675
+ }
676
+ function capabilityReadbackMatches(capability, expected) {
677
+ try {
678
+ const control = findControl(capabilityLabels[capability]);
679
+ if (control instanceof HTMLInputElement && control.type === "checkbox")
680
+ return control.checked === expected;
681
+ return control.getAttribute("aria-checked") === "true" === expected;
682
+ } catch {
683
+ return false;
684
+ }
685
+ }
686
+ function knowledgeReadbackMatches(name) {
687
+ const expected = normalize(name);
688
+ return [
689
+ ...document.querySelectorAll('[role="group"], button')
690
+ ].some(
691
+ (element) => normalize(element.getAttribute("aria-label")) === expected || normalize(element.textContent) === expected
692
+ );
693
+ }
694
+ function actionReadbackMatches(schema) {
695
+ const match = /servers:\s*\n\s*-\s*url:\s*([^\s]+)/m.exec(schema);
696
+ if (!match?.[1]) return false;
697
+ try {
698
+ const host = new URL(match[1]).host;
699
+ return normalize(document.body.textContent).includes(normalize(host));
700
+ } catch {
701
+ return false;
702
+ }
703
+ }
704
+ async function verifyConfiguredMaterial(material) {
705
+ await waitForReadback(
706
+ "GPT_EDITOR_NAME_READBACK_MISMATCH",
707
+ () => fieldReadbackMatches("displayName", material.displayName)
708
+ );
709
+ await waitForReadback(
710
+ "GPT_EDITOR_DESCRIPTION_READBACK_MISMATCH",
711
+ () => fieldReadbackMatches("description", material.description)
712
+ );
713
+ await waitForReadback(
714
+ "GPT_EDITOR_INSTRUCTIONS_READBACK_MISMATCH",
715
+ () => fieldReadbackMatches("instructions", material.instructions)
716
+ );
717
+ await waitForReadback(
718
+ "GPT_EDITOR_STARTERS_READBACK_MISMATCH",
719
+ () => starterReadbackMatches(material.conversationStarters)
720
+ );
721
+ await waitForReadback(
722
+ "GPT_EDITOR_MODEL_READBACK_MISMATCH",
723
+ () => modelReadbackMatches(material.recommendedModel)
724
+ );
725
+ for (const capability of [
726
+ "webSearch",
727
+ "imageGeneration",
728
+ "codeInterpreter"
729
+ ])
730
+ await waitForReadback(
731
+ `GPT_EDITOR_CAPABILITY_READBACK_MISMATCH:${capability}`,
732
+ () => capabilityReadbackMatches(
733
+ capability,
734
+ material.capabilities[capability]
735
+ )
736
+ );
737
+ await waitForReadback(
738
+ "GPT_EDITOR_ACTION_READBACK_MISMATCH",
739
+ () => actionReadbackMatches(material.actionSchema)
740
+ );
741
+ for (const file of material.knowledgeFiles)
742
+ await waitForReadback(
743
+ `GPT_EDITOR_KNOWLEDGE_READBACK_MISMATCH:${file.name}`,
744
+ () => knowledgeReadbackMatches(file.name)
745
+ );
746
+ await waitForReadback(
747
+ "GPT_EDITOR_DRAFT_ID_NOT_READY",
748
+ () => currentGptId() !== null
749
+ );
750
+ await waitForReadback(
751
+ "GPT_EDITOR_CREATE_NOT_READY",
752
+ () => publishCreateButton() !== null
753
+ );
754
+ }
755
+ async function openPrivateCreateSurface(initialCreateButton) {
756
+ initialCreateButton.click();
757
+ for (let attempt = 0; attempt < 200; attempt += 1) {
758
+ if (privateVisibilityControl()) return;
759
+ await sleep(100);
760
+ }
761
+ throw new Error("GPT_EDITOR_PRIVATE_CONTROL_NOT_FOUND");
762
+ }
763
+ async function waitForLiveCreatedResult() {
764
+ for (let attempt = 0; attempt < 320; attempt += 1) {
765
+ const gptId = currentGptId();
766
+ if (gptId && savedConfirmationVisible())
767
+ return { gptId, carrierUrl: `https://chatgpt.com/g/${gptId}` };
768
+ await sleep(125);
769
+ }
770
+ throw new Error("GPT_EDITOR_PRIVATE_CREATE_TIMEOUT");
771
+ }
772
+ async function finalizePrivateCreateSurface(initialCreateButton) {
773
+ await selectPrivateVisibility();
774
+ (await waitForPrivateCreateAction(initialCreateButton)).click();
775
+ return waitForLiveCreatedResult();
776
+ }
777
+ async function createPrivateGpt() {
778
+ const createButton = publishCreateButton();
779
+ if (!createButton) throw new Error("GPT_EDITOR_FORM_READY_STATE_LOST");
780
+ await openPrivateCreateSurface(createButton);
781
+ return finalizePrivateCreateSurface(createButton);
782
+ }
783
+ var domPort = {
784
+ async setTextField(field, value) {
785
+ setControlValue(findControl(fieldLabels[field]), value);
786
+ },
787
+ async replaceConversationStarters(values) {
788
+ await replaceConversationStarters(values);
789
+ },
790
+ async selectRecommendedModel(value) {
791
+ const selector = findControl([
792
+ "Recommended model",
793
+ "\u63A8\u8350\u6A21\u578B",
794
+ "\u63A8\u8350\u7684\u6A21\u578B",
795
+ value
796
+ ]);
797
+ if (selector instanceof HTMLSelectElement) {
798
+ let option;
799
+ const normalizedValue = normalize(value);
800
+ for (let attempt = 0; attempt < 150; attempt += 1) {
801
+ option = [...selector.options].find((candidate) => {
802
+ const optionText = normalize(candidate.textContent);
803
+ return candidate.value === value || optionText === normalizedValue || optionText.endsWith(`(${normalizedValue})`);
804
+ });
805
+ if (option) break;
806
+ await sleep(100);
807
+ }
808
+ if (!option)
809
+ throw new Error(`GPT_EDITOR_MODEL_OPTION_NOT_FOUND:${value}`);
810
+ setControlValue(selector, option.value);
811
+ return;
812
+ }
813
+ selector.click();
814
+ (await waitForClickable([value])).click();
815
+ },
816
+ async setCapability(capability, enabled) {
817
+ const labels = capabilityLabels[capability];
818
+ let control;
819
+ try {
820
+ control = findControl(labels);
821
+ } catch {
822
+ control = await waitForClickable(labels);
823
+ }
824
+ if (control instanceof HTMLInputElement && control.type === "checkbox") {
825
+ if (control.checked !== enabled) control.click();
826
+ if (control.checked !== enabled)
827
+ throw new Error(
828
+ `GPT_EDITOR_CAPABILITY_READBACK_MISMATCH:${capability}`
829
+ );
830
+ return;
831
+ }
832
+ if (control.getAttribute("aria-checked") === "true" !== enabled)
833
+ control.click();
834
+ if (control.getAttribute("aria-checked") === "true" !== enabled)
835
+ throw new Error(`GPT_EDITOR_CAPABILITY_READBACK_MISMATCH:${capability}`);
836
+ },
837
+ async configureBearerAuth(value) {
838
+ await configureBearerAuthDraft(value);
839
+ },
840
+ async uploadKnowledge(files) {
841
+ await uploadKnowledge(files);
842
+ },
843
+ async verifyReady(material) {
844
+ await verifyConfiguredMaterial(material);
845
+ },
846
+ async installActionSchema(value) {
847
+ let schema = null;
848
+ try {
849
+ schema = findControl(["OpenAPI schema", "Schema", "OpenAPI", "\u67B6\u6784"]);
850
+ } catch {
851
+ const create = clickable([
852
+ "Create new action",
853
+ "New action",
854
+ "\u521B\u5EFA\u65B0\u64CD\u4F5C"
855
+ ]);
856
+ if (!create) throw new Error("GPT_EDITOR_ACTION_CREATE_NOT_FOUND");
857
+ create.click();
858
+ for (let attempt = 0; attempt < 200; attempt += 1) {
859
+ schema = document.querySelector(
860
+ 'textarea[placeholder*="OpenAPI"]'
861
+ );
862
+ if (schema) break;
863
+ try {
864
+ schema = findControl([
865
+ "\u5728\u6B64\u5904\u8F93\u5165\u4F60\u7684 OpenAPI \u67B6\u6784",
866
+ "OpenAPI schema",
867
+ "Schema",
868
+ "OpenAPI",
869
+ "\u67B6\u6784"
870
+ ]);
871
+ break;
872
+ } catch {
873
+ await sleep(100);
874
+ }
875
+ }
876
+ }
877
+ if (!schema) throw new Error("GPT_EDITOR_ACTION_SCHEMA_NOT_FOUND");
878
+ setControlValue(schema, value);
879
+ await waitForReadback(
880
+ "GPT_EDITOR_ACTION_SCHEMA_READBACK_MISMATCH",
881
+ () => controlValue(schema) === value
882
+ );
883
+ await returnFromActionEditor();
884
+ },
885
+ async createPrivate() {
886
+ return createPrivateGpt();
887
+ }
888
+ };
889
+ var editorDriver = createCustomGptEditorDriver(domPort);
890
+ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
891
+ if (message.type !== "PROFLOW_PROVISIONING_COMMAND") return;
892
+ if (!editorSurfaceReady()) {
893
+ sendResponse({ ok: false, error: "PROVISIONING_SURFACE_NOT_READY" });
894
+ return;
895
+ }
896
+ void (async () => {
897
+ try {
898
+ await ensureConfigureMode();
899
+ if (message.operation === "PROVISION_CUSTOM_GPT") {
900
+ const material = parseCustomGptProvisioningRequest(message.request);
901
+ const result = await editorDriver.provision(material);
902
+ sendResponse({
903
+ ok: true,
904
+ value: {
905
+ ...result,
906
+ provisioningInstanceId: provisioningSurface.instanceId,
907
+ url: location.href
908
+ }
909
+ });
910
+ return;
911
+ }
912
+ if (message.operation !== "FINALIZE_CUSTOM_GPT_AUTH")
913
+ throw new Error("PROVISIONING_OPERATION_UNSUPPORTED");
914
+ const expectedGptId = carrierGptId(message.request.carrierUrl);
915
+ let credential = message.request.credential;
916
+ delete message.request.credential;
917
+ if (typeof credential !== "string" || credential.length < 32)
918
+ throw new Error("ROLE_CREDENTIAL_INVALID");
919
+ try {
920
+ const result = await finalizeBearerAuth(credential);
921
+ if (result.gptId !== expectedGptId)
922
+ throw new Error("GPT_EDITOR_AUTH_TARGET_MISMATCH");
923
+ sendResponse({ ok: true, value: result });
924
+ } finally {
925
+ credential = "";
926
+ }
927
+ } catch (error) {
928
+ sendResponse({
929
+ ok: false,
930
+ error: error instanceof Error ? error.message : "GPT_EDITOR_PROVISIONING_FAILED"
931
+ });
932
+ }
933
+ })();
934
+ return true;
935
+ });
936
+ })();