@ycheung5/pi-computer-use 0.0.0-stage → 0.1.0

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.
Files changed (92) hide show
  1. package/GLOSSARY.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +165 -2
  4. package/assets/accessibility.cs +208 -0
  5. package/assets/browser-indicators.cs +37 -0
  6. package/assets/capture.cs +215 -0
  7. package/assets/clipboard.cs +197 -0
  8. package/assets/controls.cs +249 -0
  9. package/assets/indicator-preview.png +0 -0
  10. package/assets/indicators.cs +252 -0
  11. package/assets/node-LICENSE.txt +2732 -0
  12. package/assets/ocr.cs +86 -0
  13. package/assets/ownership.cs +59 -0
  14. package/assets/pointer.cs +229 -0
  15. package/assets/puppeteer-LICENSE.txt +202 -0
  16. package/assets/readme-banner.svg +33 -0
  17. package/assets/stop.cs +197 -0
  18. package/assets/text.cs +249 -0
  19. package/assets/wgc.cs +167 -0
  20. package/assets/window-management.cs +217 -0
  21. package/assets/window-marker.cs +23 -0
  22. package/assets/window-references.cs +80 -0
  23. package/assets/windows.cs +127 -0
  24. package/dist/accessibility.d.ts +29 -0
  25. package/dist/accessibility.js +258 -0
  26. package/dist/browser-actions.d.ts +21 -0
  27. package/dist/browser-actions.js +213 -0
  28. package/dist/browser-indicators.d.ts +23 -0
  29. package/dist/browser-indicators.js +69 -0
  30. package/dist/browser-profiles.d.ts +10 -0
  31. package/dist/browser-profiles.js +174 -0
  32. package/dist/browser.d.ts +11 -0
  33. package/dist/browser.js +2071 -0
  34. package/dist/capture.d.ts +74 -0
  35. package/dist/capture.js +199 -0
  36. package/dist/client.d.ts +38 -0
  37. package/dist/client.js +331 -0
  38. package/dist/desktop.d.ts +20 -0
  39. package/dist/desktop.js +42 -0
  40. package/dist/extension.d.ts +4 -0
  41. package/dist/extension.js +409 -0
  42. package/dist/launcher.d.ts +6 -0
  43. package/dist/launcher.js +85 -0
  44. package/dist/native.d.ts +11 -0
  45. package/dist/native.js +145 -0
  46. package/dist/ocr.d.ts +52 -0
  47. package/dist/ocr.js +246 -0
  48. package/dist/ownership.d.ts +4 -0
  49. package/dist/ownership.js +24 -0
  50. package/dist/pointer.d.ts +108 -0
  51. package/dist/pointer.js +174 -0
  52. package/dist/protocol.d.ts +15 -0
  53. package/dist/protocol.js +1 -0
  54. package/dist/runtime.d.ts +8 -0
  55. package/dist/runtime.js +72 -0
  56. package/dist/settings.d.ts +85 -0
  57. package/dist/settings.js +449 -0
  58. package/dist/stop.d.ts +24 -0
  59. package/dist/stop.js +156 -0
  60. package/dist/text.d.ts +100 -0
  61. package/dist/text.js +183 -0
  62. package/dist/waits.d.ts +19 -0
  63. package/dist/waits.js +262 -0
  64. package/dist/window-management.d.ts +25 -0
  65. package/dist/window-management.js +167 -0
  66. package/dist/worker.d.ts +1 -0
  67. package/dist/worker.js +540 -0
  68. package/docs/README.md +54 -0
  69. package/docs/adr/0001-preserve-reference-behavior.md +9 -0
  70. package/docs/adr/0002-window-lifetime-references.md +11 -0
  71. package/docs/adr/0003-exclusive-desktop-control.md +11 -0
  72. package/docs/adr/0004-pi-first-acceptance.md +9 -0
  73. package/docs/adr/0005-default-apps-and-workspace-reuse.md +7 -0
  74. package/docs/agents/domain.md +9 -0
  75. package/docs/agents/issue-tracker.md +10 -0
  76. package/docs/agents/triage-labels.md +9 -0
  77. package/docs/browser.md +113 -0
  78. package/docs/implementation-plan.md +139 -0
  79. package/docs/indicators.md +53 -0
  80. package/docs/native-interop-research.md +17 -0
  81. package/docs/ownership.md +25 -0
  82. package/docs/pi-extension-research.md +57 -0
  83. package/docs/pointer.md +65 -0
  84. package/docs/runtime.md +42 -0
  85. package/docs/settings.md +33 -0
  86. package/docs/spec.md +161 -0
  87. package/docs/stopping.md +83 -0
  88. package/docs/text.md +79 -0
  89. package/docs/tickets.md +84 -0
  90. package/docs/validation.md +1372 -0
  91. package/docs/windows.md +171 -0
  92. package/package.json +134 -4
@@ -0,0 +1,258 @@
1
+ import { screenshot } from "./capture.js";
2
+ import { nativeDesktop } from "./native.js";
3
+ import { ocrDesktop } from "./ocr.js";
4
+ const target = {
5
+ windowRef: { type: "string", minLength: 1 },
6
+ windowTitle: { type: "string" },
7
+ processId: { type: "integer", minimum: 1 },
8
+ nativeWindowHandle: { type: "integer", minimum: 1 },
9
+ activate: { type: "boolean" },
10
+ };
11
+ const view = {
12
+ viewMode: {
13
+ type: "string",
14
+ enum: ["control", "content", "raw"],
15
+ default: "control",
16
+ },
17
+ includeOffscreen: { type: "boolean", default: false },
18
+ };
19
+ const browser = {
20
+ browserTab: { type: "string", minLength: 1 },
21
+ };
22
+ const detail = {
23
+ detailLevel: {
24
+ type: "string",
25
+ enum: ["compact", "full"],
26
+ default: "compact",
27
+ },
28
+ };
29
+ const tree = {
30
+ scope: {
31
+ type: "string",
32
+ enum: ["active_window", "desktop"],
33
+ default: "active_window",
34
+ },
35
+ treeTimeoutMs: {
36
+ type: "integer",
37
+ minimum: 100,
38
+ maximum: 30000,
39
+ default: 5000,
40
+ },
41
+ maxDepth: { type: "integer", minimum: 0, maximum: 12, default: 6 },
42
+ maxNodes: { type: "integer", minimum: 1, maximum: 3000, default: 500 },
43
+ };
44
+ const definitions = {
45
+ accessibility_tree: {
46
+ properties: { ...target, ...browser, ...view, ...detail, ...tree },
47
+ },
48
+ snapshot: {
49
+ properties: {
50
+ ...target,
51
+ ...browser,
52
+ ...view,
53
+ ...detail,
54
+ ...tree,
55
+ maxDepth: { ...tree.maxDepth, default: 5 },
56
+ maxNodes: { ...tree.maxNodes, maximum: 2000, default: 250 },
57
+ includeScreenshot: { type: "boolean", default: true },
58
+ captureWindow: { type: "boolean", default: false },
59
+ maxWidth: { type: "integer", minimum: 0, maximum: 7680, default: 1600 },
60
+ },
61
+ },
62
+ find: {
63
+ properties: {
64
+ ...target,
65
+ ...browser,
66
+ ...view,
67
+ ...tree,
68
+ query: { type: "string", minLength: 1 },
69
+ controlType: { type: "string" },
70
+ ocrFallback: { type: "boolean", default: true },
71
+ maxDepth: { ...tree.maxDepth, default: 8 },
72
+ maxNodes: { ...tree.maxNodes, maximum: 5000, default: 1200 },
73
+ maxResults: { type: "integer", minimum: 1, maximum: 200, default: 25 },
74
+ },
75
+ required: ["query"],
76
+ },
77
+ element_info: {
78
+ properties: {
79
+ ...target,
80
+ ...view,
81
+ ...detail,
82
+ elementId: { type: "string", minLength: 1 },
83
+ x: { type: "integer" },
84
+ y: { type: "integer" },
85
+ },
86
+ },
87
+ focus: {
88
+ properties: {
89
+ ...target,
90
+ ...view,
91
+ elementId: { type: "string", minLength: 1 },
92
+ },
93
+ required: ["elementId"],
94
+ },
95
+ invoke: {
96
+ properties: {
97
+ ...target,
98
+ ...view,
99
+ elementId: { type: "string", minLength: 1 },
100
+ action: {
101
+ type: "string",
102
+ enum: ["default", "expand", "collapse", "scroll_into_view"],
103
+ default: "default",
104
+ },
105
+ fallbackClick: { type: "boolean", default: true },
106
+ },
107
+ required: ["elementId"],
108
+ },
109
+ set_value: {
110
+ properties: {
111
+ ...target,
112
+ ...view,
113
+ elementId: { type: "string", minLength: 1 },
114
+ value: { type: "string" },
115
+ fallbackType: { type: "boolean", default: true },
116
+ restoreClipboard: { type: "boolean", default: true },
117
+ },
118
+ required: ["elementId", "value"],
119
+ },
120
+ scroll: {
121
+ properties: {
122
+ ...target,
123
+ ...view,
124
+ ...browser,
125
+ elementId: { type: "string", minLength: 1 },
126
+ x: { type: "integer" },
127
+ y: { type: "integer" },
128
+ deltaY: { type: "integer", default: 480 },
129
+ deltaX: { type: "integer", default: 0 },
130
+ },
131
+ },
132
+ };
133
+ const toolDefinitions = Object.entries(definitions).map(([action, definition]) => ({
134
+ action,
135
+ parameters: { type: "object", additionalProperties: false, ...definition },
136
+ }));
137
+ export const observationTools = toolDefinitions.filter((tool) => ["snapshot", "accessibility_tree", "find", "element_info"].includes(tool.action));
138
+ export const controlTools = toolDefinitions.filter((tool) => ["focus", "invoke", "set_value", "scroll"].includes(tool.action));
139
+ export async function executeAccessibility(action, args, signal, execute) {
140
+ const definition = definitions[action];
141
+ if (!definition)
142
+ return { ok: false, action, error: "UNKNOWN_ACTION" };
143
+ const normalized = {};
144
+ const invalid = (message) => ({
145
+ ok: false,
146
+ action,
147
+ error: `INVALID_ARGUMENTS: ${message}`,
148
+ });
149
+ for (const key of Object.keys(args))
150
+ if (!(key in definition.properties))
151
+ return invalid(`unknown option ${key}`);
152
+ for (const [key, rule] of Object.entries(definition.properties)) {
153
+ const value = Object.hasOwn(args, key) ? args[key] : rule.default;
154
+ if (value === undefined) {
155
+ if (definition.required?.includes(key))
156
+ return invalid(`${key} is required`);
157
+ continue;
158
+ }
159
+ if (rule.type === "integer"
160
+ ? !Number.isSafeInteger(value)
161
+ : typeof value !== rule.type)
162
+ return invalid(`${key} must be ${rule.type}`);
163
+ if (typeof value === "number" &&
164
+ ((rule.minimum !== undefined && value < rule.minimum) ||
165
+ (rule.maximum !== undefined && value > rule.maximum)))
166
+ return invalid(`${key} is outside supported bounds`);
167
+ if (typeof value === "string" &&
168
+ ((rule.enum && !rule.enum.includes(value)) ||
169
+ (rule.minLength && value.length < rule.minLength)))
170
+ return invalid(`${key} has an unsupported value`);
171
+ normalized[key] = value;
172
+ }
173
+ if (action === "element_info" &&
174
+ !normalized.elementId &&
175
+ (normalized.x === undefined || normalized.y === undefined))
176
+ return invalid("provide elementId or both x and y");
177
+ if (action === "scroll" && !normalized.elementId) {
178
+ if (normalized.x === undefined || normalized.y === undefined)
179
+ return invalid("provide elementId or both x and y");
180
+ return {
181
+ ok: false,
182
+ action,
183
+ error: "INPUT_SAFETY_NOT_READY: coordinate scrolling requires guarded physical input",
184
+ inputRequired: true,
185
+ };
186
+ }
187
+ signal?.throwIfAborted();
188
+ let result;
189
+ // Guarded mutation failures retain the cleanup lease; do not translate them
190
+ // into ordinary observation errors or clear the outer input state.
191
+ if (execute)
192
+ return execute(normalized);
193
+ try {
194
+ result = await nativeDesktop.execute(action, normalized, signal);
195
+ }
196
+ catch (error) {
197
+ result = {
198
+ ok: false,
199
+ action,
200
+ error: error instanceof Error ? error.message : String(error),
201
+ };
202
+ }
203
+ if (action === "snapshot" && normalized.includeScreenshot) {
204
+ delete result.screenshotPending;
205
+ const windowCapture = normalized.captureWindow && normalized.scope !== "desktop";
206
+ const captureArgs = {
207
+ scope: windowCapture ? "active_window" : "desktop",
208
+ maxWidth: normalized.maxWidth,
209
+ };
210
+ if (windowCapture) {
211
+ for (const key of [
212
+ "windowRef",
213
+ "windowTitle",
214
+ "processId",
215
+ "nativeWindowHandle",
216
+ "activate",
217
+ ]) {
218
+ const value = result[key] ?? normalized[key];
219
+ if (value !== undefined)
220
+ captureArgs[key] = value;
221
+ }
222
+ }
223
+ const captured = await screenshot(captureArgs, signal);
224
+ if (captured.ok && captured.screenshot)
225
+ result.screenshot = captured.screenshot;
226
+ else {
227
+ result.partial = true;
228
+ result.screenshotError = String(captured.error ?? "SCREENSHOT_UNAVAILABLE");
229
+ }
230
+ }
231
+ if (action === "find" && result.ocrFallbackPending) {
232
+ delete result.ocrFallbackPending;
233
+ const ocrArgs = {
234
+ query: normalized.query,
235
+ scope: normalized.scope,
236
+ };
237
+ for (const key of [
238
+ "windowRef",
239
+ "windowTitle",
240
+ "processId",
241
+ "nativeWindowHandle",
242
+ "activate",
243
+ ]) {
244
+ const value = result[key] ?? normalized[key];
245
+ if (value !== undefined)
246
+ ocrArgs[key] = value;
247
+ }
248
+ const recognized = await ocrDesktop(ocrArgs, signal);
249
+ result.ocr = recognized;
250
+ if (recognized.ok)
251
+ result.ocrCandidates = recognized.matched ?? [];
252
+ else {
253
+ result.ocrError = recognized.error;
254
+ result.conclusive = false;
255
+ }
256
+ }
257
+ return result;
258
+ }
@@ -0,0 +1,21 @@
1
+ type Rule = {
2
+ type: string;
3
+ default?: unknown;
4
+ enum?: string[];
5
+ minimum?: number;
6
+ maximum?: number;
7
+ minLength?: number;
8
+ minItems?: number;
9
+ items?: Rule;
10
+ };
11
+ export declare const browserActionTools: {
12
+ action: string;
13
+ parameters: {
14
+ properties: Record<string, Rule>;
15
+ required?: string[];
16
+ type: string;
17
+ additionalProperties: boolean;
18
+ };
19
+ }[];
20
+ export declare function normalizeBrowserAction(action: string, args: Record<string, unknown>): Record<string, unknown> | undefined;
21
+ export {};
@@ -0,0 +1,213 @@
1
+ // Shared reference-compatible tool schemas; browser execution stays in browser.ts.
2
+ const target = {
3
+ windowRef: { type: "string", minLength: 1 },
4
+ windowTitle: { type: "string" },
5
+ processId: { type: "integer", minimum: 1 },
6
+ nativeWindowHandle: { type: "integer", minimum: 1 },
7
+ activate: { type: "boolean", default: false },
8
+ };
9
+ const view = {
10
+ viewMode: {
11
+ type: "string",
12
+ enum: ["control", "content", "raw"],
13
+ default: "control",
14
+ },
15
+ includeOffscreen: { type: "boolean", default: false },
16
+ };
17
+ const tab = { browserTab: { type: "string", minLength: 1 } };
18
+ const element = { elementId: { type: "string", minLength: 1 } };
19
+ const point = { x: { type: "integer" }, y: { type: "integer" } };
20
+ const button = {
21
+ type: "string",
22
+ enum: ["left", "right", "middle"],
23
+ default: "left",
24
+ };
25
+ const dispatch = {
26
+ type: "string",
27
+ enum: ["auto", "background", "foreground"],
28
+ default: "auto",
29
+ };
30
+ const definitions = {
31
+ click: {
32
+ properties: { ...target, ...view, ...element, ...point, button, dispatch },
33
+ },
34
+ double_click: {
35
+ properties: { ...target, ...view, ...element, ...point, button, dispatch },
36
+ },
37
+ move: { properties: { ...target, ...view, ...element, ...point } },
38
+ scroll: {
39
+ properties: {
40
+ ...target,
41
+ ...view,
42
+ ...tab,
43
+ ...element,
44
+ ...point,
45
+ deltaY: { type: "integer", default: 480 },
46
+ deltaX: { type: "integer", default: 0 },
47
+ },
48
+ },
49
+ type_text: {
50
+ required: ["text"],
51
+ properties: {
52
+ ...target,
53
+ ...tab,
54
+ text: { type: "string" },
55
+ method: {
56
+ type: "string",
57
+ enum: ["auto", "clipboard", "sendinput", "background"],
58
+ default: "auto",
59
+ },
60
+ focusMode: {
61
+ type: "string",
62
+ enum: ["preserve", "main"],
63
+ default: "preserve",
64
+ },
65
+ restoreClipboard: { type: "boolean", default: true },
66
+ },
67
+ },
68
+ keypress: {
69
+ required: ["keys"],
70
+ properties: {
71
+ ...target,
72
+ ...tab,
73
+ keys: {
74
+ type: "array",
75
+ minItems: 1,
76
+ items: { type: "string", minLength: 1 },
77
+ },
78
+ dispatch: {
79
+ type: "string",
80
+ enum: ["foreground", "background"],
81
+ default: "foreground",
82
+ },
83
+ },
84
+ },
85
+ focus: {
86
+ required: ["elementId"],
87
+ properties: { ...target, ...view, ...element },
88
+ },
89
+ invoke: {
90
+ required: ["elementId"],
91
+ properties: {
92
+ ...target,
93
+ ...view,
94
+ ...element,
95
+ action: {
96
+ type: "string",
97
+ enum: ["default", "expand", "collapse", "scroll_into_view"],
98
+ default: "default",
99
+ },
100
+ fallbackClick: { type: "boolean", default: true },
101
+ },
102
+ },
103
+ set_value: {
104
+ required: ["elementId", "value"],
105
+ properties: {
106
+ ...target,
107
+ ...view,
108
+ ...element,
109
+ value: { type: "string" },
110
+ fallbackType: { type: "boolean", default: true },
111
+ restoreClipboard: { type: "boolean", default: true },
112
+ },
113
+ },
114
+ activate_window: { properties: { ...target, ...tab } },
115
+ close_window: { properties: { ...target, ...tab } },
116
+ wait_for: {
117
+ properties: {
118
+ ...target,
119
+ ...tab,
120
+ ...view,
121
+ condition: {
122
+ type: "string",
123
+ enum: ["window", "element", "text"],
124
+ default: "window",
125
+ },
126
+ query: { type: "string" },
127
+ controlType: { type: "string" },
128
+ scope: {
129
+ type: "string",
130
+ enum: ["active_window", "desktop"],
131
+ default: "active_window",
132
+ },
133
+ treeTimeoutMs: {
134
+ type: "integer",
135
+ minimum: 100,
136
+ maximum: 30000,
137
+ default: 1000,
138
+ },
139
+ maxDepth: { type: "integer", minimum: 0, maximum: 12, default: 8 },
140
+ maxNodes: { type: "integer", minimum: 1, maximum: 5000, default: 1200 },
141
+ appear: { type: "boolean", default: true },
142
+ timeoutMs: {
143
+ type: "integer",
144
+ minimum: 100,
145
+ maximum: 60000,
146
+ default: 5000,
147
+ },
148
+ intervalMs: { type: "integer", minimum: 50, maximum: 5000, default: 250 },
149
+ },
150
+ },
151
+ };
152
+ export const browserActionTools = Object.entries(definitions).map(([action, definition]) => ({
153
+ action,
154
+ parameters: { type: "object", additionalProperties: false, ...definition },
155
+ }));
156
+ export function normalizeBrowserAction(action, args) {
157
+ const definition = definitions[action];
158
+ if (!definition)
159
+ return undefined;
160
+ const properties = { ...definition.properties, ...tab };
161
+ for (const key of Object.keys(args))
162
+ if (!Object.hasOwn(properties, key))
163
+ throw new Error(`INVALID_ARGUMENTS: unknown option ${key}`);
164
+ const normalized = {};
165
+ const check = (name, value, rule) => {
166
+ if (rule.type === "array") {
167
+ if (!Array.isArray(value) ||
168
+ value.length < (rule.minItems ?? 0) ||
169
+ value.length > 10000)
170
+ throw new Error(`INVALID_ARGUMENTS: ${name} must be an array with supported length`);
171
+ if (rule.items)
172
+ for (const child of value)
173
+ check(name, child, rule.items);
174
+ }
175
+ else if (rule.type === "integer"
176
+ ? !Number.isSafeInteger(value)
177
+ : typeof value !== rule.type)
178
+ throw new Error(`INVALID_ARGUMENTS: ${name} must be ${rule.type}`);
179
+ if ((typeof value === "number" &&
180
+ ((rule.minimum !== undefined && value < rule.minimum) ||
181
+ (rule.maximum !== undefined && value > rule.maximum))) ||
182
+ (typeof value === "string" &&
183
+ ((rule.enum && !rule.enum.includes(value)) ||
184
+ (rule.minLength !== undefined && value.length < rule.minLength))))
185
+ throw new Error(`INVALID_ARGUMENTS: ${name} has an unsupported value`);
186
+ };
187
+ for (const [key, rule] of Object.entries(properties)) {
188
+ const value = Object.hasOwn(args, key) ? args[key] : rule.default;
189
+ if (value === undefined) {
190
+ if (definition.required?.includes(key))
191
+ throw new Error(`INVALID_ARGUMENTS: ${key} is required`);
192
+ continue;
193
+ }
194
+ check(key, value, rule);
195
+ normalized[key] = value;
196
+ }
197
+ if (action === "keypress") {
198
+ const keys = normalized.keys;
199
+ if (keys.some((key) => /^(win|windows|meta|cmd|command)$/i.test(key)))
200
+ throw new Error("INVALID_ARGUMENTS: Windows key is blocked");
201
+ if (keys.filter((key) => !/^(ctrl|control|alt|option|shift)$/i.test(key))
202
+ .length !== 1)
203
+ throw new Error("INVALID_ARGUMENTS: keypress needs exactly one non-modifier key");
204
+ }
205
+ if (action === "invoke" && normalized.action !== "default")
206
+ throw new Error("INVALID_ARGUMENTS: non-default invoke actions apply only to UI Automation elements");
207
+ if (action === "wait_for" &&
208
+ (normalized.condition === "window" ||
209
+ typeof normalized.query !== "string" ||
210
+ !normalized.query.trim()))
211
+ throw new Error("INVALID_ARGUMENTS: browser wait_for requires element/text condition and query");
212
+ return normalized;
213
+ }
@@ -0,0 +1,23 @@
1
+ export declare function updateBrowserIndicator(input: {
2
+ mode: "mark" | "hide" | "show" | "clear";
3
+ point?: {
4
+ x: number;
5
+ y: number;
6
+ };
7
+ }): {
8
+ active: boolean;
9
+ tabBadge: boolean;
10
+ clickMarker: boolean;
11
+ hidden?: undefined;
12
+ ariaHidden?: undefined;
13
+ pointerEvents?: undefined;
14
+ shadowClosed?: undefined;
15
+ } | {
16
+ active: boolean;
17
+ tabBadge: boolean;
18
+ clickMarker: boolean;
19
+ hidden: boolean;
20
+ ariaHidden: boolean;
21
+ pointerEvents: string;
22
+ shadowClosed: boolean;
23
+ };
@@ -0,0 +1,69 @@
1
+ // Runs only in this extension's isolated browser realm. Keep the function self-contained.
2
+ export function updateBrowserIndicator(input) {
3
+ const world = globalThis;
4
+ let cue = world.__piComputerUseCue;
5
+ if (input.mode === "clear") {
6
+ cue?.host.remove();
7
+ cue?.icon.remove();
8
+ delete world.__piComputerUseCue;
9
+ return { active: false, tabBadge: false, clickMarker: false };
10
+ }
11
+ if (!cue?.host.isConnected &&
12
+ input.mode === "mark" &&
13
+ document.documentElement) {
14
+ cue?.icon.remove();
15
+ const host = document.createElement("div");
16
+ host.setAttribute("aria-hidden", "true");
17
+ host.setAttribute("role", "presentation");
18
+ host.style.cssText =
19
+ "all:initial!important;position:fixed!important;left:0!important;top:0!important;width:0!important;height:0!important;overflow:visible!important;pointer-events:none!important;z-index:2147483647!important";
20
+ const root = host.attachShadow({ mode: "closed" });
21
+ const marker = document.createElementNS("http://www.w3.org/2000/svg", "svg");
22
+ marker.setAttribute("viewBox", "0 0 28 34");
23
+ marker.setAttribute("aria-hidden", "true");
24
+ marker.style.cssText =
25
+ "position:fixed;width:28px;height:34px;pointer-events:none;display:none;filter:drop-shadow(0 0 3px #2d9cff)";
26
+ const arrow = document.createElementNS("http://www.w3.org/2000/svg", "path");
27
+ arrow.setAttribute("d", "M2 2 L2 25 L8 20 L13 31 L18 28 L13 18 L23 18 Z");
28
+ arrow.setAttribute("fill", "#2d9cff");
29
+ arrow.setAttribute("stroke", "white");
30
+ arrow.setAttribute("stroke-width", "1.5");
31
+ marker.append(arrow);
32
+ root.append(marker);
33
+ document.documentElement.append(host);
34
+ const icon = document.createElement("link");
35
+ icon.rel = "icon";
36
+ icon.type = "image/svg+xml";
37
+ icon.href =
38
+ "data:image/svg+xml," +
39
+ encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="6" fill="#247fd9"/><text x="16" y="23" font-family="sans-serif" font-size="23" text-anchor="middle" fill="white">π</text></svg>');
40
+ (document.head ?? document.documentElement).append(icon);
41
+ cue = { host, marker, icon };
42
+ world.__piComputerUseCue = cue;
43
+ }
44
+ if (!cue)
45
+ return { active: false, tabBadge: false, clickMarker: false };
46
+ if (input.mode === "hide")
47
+ cue.host.style.setProperty("display", "none", "important");
48
+ else if (input.mode === "show")
49
+ cue.host.style.removeProperty("display");
50
+ else {
51
+ cue.host.style.removeProperty("display");
52
+ if (input.point &&
53
+ Number.isFinite(input.point.x) &&
54
+ Number.isFinite(input.point.y)) {
55
+ cue.marker.style.left = `${input.point.x}px`;
56
+ cue.marker.style.top = `${input.point.y}px`;
57
+ cue.marker.style.display = "block";
58
+ }
59
+ }
60
+ return {
61
+ active: cue.host.isConnected,
62
+ tabBadge: cue.icon.isConnected,
63
+ clickMarker: cue.marker.style.display !== "none",
64
+ hidden: cue.host.style.display === "none",
65
+ ariaHidden: cue.host.getAttribute("aria-hidden") === "true",
66
+ pointerEvents: cue.host.style.pointerEvents,
67
+ shadowClosed: cue.host.shadowRoot === null,
68
+ };
69
+ }
@@ -0,0 +1,10 @@
1
+ export interface BrowserProfile {
2
+ path: string;
3
+ kept: boolean;
4
+ lock: string;
5
+ token: string;
6
+ }
7
+ export declare function acquireBrowserProfile(app: string, executable: string, kept: boolean): Promise<BrowserProfile>;
8
+ export declare function recordProfileBrowser(profile: BrowserProfile, pid: number | undefined): Promise<void>;
9
+ export declare function releaseBrowserProfile(profile: BrowserProfile, browserPid?: number): Promise<void>;
10
+ export declare function browserIdleMs(): number;