@forsion/tangu-computer-use 0.5.8

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 (120) hide show
  1. package/CHANGELOG.md +223 -0
  2. package/LICENSE +26 -0
  3. package/LICENSE.upstream +21 -0
  4. package/README.md +169 -0
  5. package/UPSTREAM.md +277 -0
  6. package/check.mjs +369 -0
  7. package/icon.png +0 -0
  8. package/install.sh +112 -0
  9. package/main.js +541 -0
  10. package/manifest.json +47 -0
  11. package/native/linux/bridge-rs/Cargo.lock +1204 -0
  12. package/native/linux/bridge-rs/Cargo.toml +18 -0
  13. package/native/linux/bridge-rs/src/atspi.rs +640 -0
  14. package/native/linux/bridge-rs/src/error.rs +65 -0
  15. package/native/linux/bridge-rs/src/lib.rs +11 -0
  16. package/native/linux/bridge-rs/src/main.rs +1074 -0
  17. package/native/linux/bridge-rs/src/protocol.rs +49 -0
  18. package/native/linux/bridge-rs/src/state.rs +293 -0
  19. package/native/linux/bridge-rs/src/wayland.rs +89 -0
  20. package/native/linux/bridge-rs/src/x11.rs +909 -0
  21. package/native/linux/bridge-rs/tests/protocol_tests.rs +73 -0
  22. package/native/macos/agent_cursor.swift +184 -0
  23. package/native/macos/agent_cursor_motion.swift +252 -0
  24. package/native/macos/agent_cursor_tests.swift +128 -0
  25. package/native/macos/agent_highlight.swift +229 -0
  26. package/native/macos/agent_highlight_tests.swift +99 -0
  27. package/native/macos/bridge.swift +3852 -0
  28. package/native/macos/foreground_activity.swift +57 -0
  29. package/native/macos/foreground_activity_tests.swift +31 -0
  30. package/native/macos/live_stream.swift +289 -0
  31. package/native/windows/bridge-rs/Cargo.lock +396 -0
  32. package/native/windows/bridge-rs/Cargo.toml +30 -0
  33. package/native/windows/bridge-rs/src/capture.rs +518 -0
  34. package/native/windows/bridge-rs/src/error.rs +81 -0
  35. package/native/windows/bridge-rs/src/input.rs +501 -0
  36. package/native/windows/bridge-rs/src/lib.rs +16 -0
  37. package/native/windows/bridge-rs/src/main.rs +1683 -0
  38. package/native/windows/bridge-rs/src/protocol.rs +53 -0
  39. package/native/windows/bridge-rs/src/refs.rs +237 -0
  40. package/native/windows/bridge-rs/src/state.rs +55 -0
  41. package/native/windows/bridge-rs/src/uia.rs +1252 -0
  42. package/native/windows/bridge-rs/src/window.rs +718 -0
  43. package/native/windows/bridge-rs/tests/protocol_tests.rs +252 -0
  44. package/native/windows/bridge-rs/tests/refs_tests.rs +86 -0
  45. package/native/windows/bridge-rs/tests/state_tests.rs +33 -0
  46. package/package.json +74 -0
  47. package/prebuilt/linux/arm64/linux-bridge +0 -0
  48. package/prebuilt/linux/x64/linux-bridge +0 -0
  49. package/prebuilt/macos/arm64/bridge +0 -0
  50. package/prebuilt/macos/arm64/tangu-computer-use.app.json +10 -0
  51. package/prebuilt/macos/arm64/tangu-computer-use.app.zip +0 -0
  52. package/prebuilt/macos/x64/bridge +0 -0
  53. package/prebuilt/macos/x64/tangu-computer-use.app.json +10 -0
  54. package/prebuilt/macos/x64/tangu-computer-use.app.zip +0 -0
  55. package/prebuilt/windows/windows-bridge.exe +0 -0
  56. package/scripts/blind-click.check.mjs +128 -0
  57. package/scripts/build-native.mjs +302 -0
  58. package/scripts/build.mjs +84 -0
  59. package/scripts/calc-fixture.mjs +78 -0
  60. package/scripts/cli-exit.check.mjs +49 -0
  61. package/scripts/helper-path.check.mjs +82 -0
  62. package/scripts/helper-refresh.check.mjs +62 -0
  63. package/scripts/helper-signal.check.mjs +59 -0
  64. package/scripts/highlight.check.mjs +32 -0
  65. package/scripts/keychain-free-install.check.mjs +140 -0
  66. package/scripts/live-view.check.mjs +124 -0
  67. package/scripts/macos-bundle.d.mts +1 -0
  68. package/scripts/macos-bundle.mjs +119 -0
  69. package/scripts/make-signing-cert.sh +57 -0
  70. package/scripts/mini-foreground.check.mjs +11 -0
  71. package/scripts/no-foreground.check.mjs +81 -0
  72. package/scripts/overlay-visible.check.mjs +221 -0
  73. package/scripts/package-macos-app.mjs +39 -0
  74. package/scripts/permissions.check.mjs +199 -0
  75. package/scripts/platform-contract.check.mjs +30 -0
  76. package/scripts/setup-helper.mjs +154 -0
  77. package/scripts/tangu-computer-use.entitlements +10 -0
  78. package/scripts/verify-macos-bundles.mjs +25 -0
  79. package/scripts/verify-package.mjs +74 -0
  80. package/skills/computer-use/SKILL.md +110 -0
  81. package/src/foregroundNote.ts +43 -0
  82. package/src/helperState.ts +31 -0
  83. package/src/index.ts +63 -0
  84. package/src/onboarding.ts +198 -0
  85. package/src/pi-compat.ts +49 -0
  86. package/src/settings.ts +11 -0
  87. package/src/setup.ts +97 -0
  88. package/src/tools.ts +289 -0
  89. package/src/vendor/actions.ts +130 -0
  90. package/src/vendor/bridge.ts +2405 -0
  91. package/src/vendor/cdp.ts +658 -0
  92. package/src/vendor/config.ts +113 -0
  93. package/src/vendor/contract.ts +104 -0
  94. package/src/vendor/note.ts +195 -0
  95. package/src/vendor/outline.ts +651 -0
  96. package/src/vendor/output.ts +134 -0
  97. package/src/vendor/permissions.ts +111 -0
  98. package/src/vendor/platform/architecture.ts +23 -0
  99. package/src/vendor/platform/coerce.ts +16 -0
  100. package/src/vendor/platform/index.ts +59 -0
  101. package/src/vendor/platform/linux/backend.ts +186 -0
  102. package/src/vendor/platform/linux/helper.ts +238 -0
  103. package/src/vendor/platform/macos/backend.ts +131 -0
  104. package/src/vendor/platform/macos/browser.ts +110 -0
  105. package/src/vendor/platform/macos/helper-path.d.mts +9 -0
  106. package/src/vendor/platform/macos/helper-path.mjs +34 -0
  107. package/src/vendor/platform/macos/helper.ts +291 -0
  108. package/src/vendor/platform/macos/permissions.ts +146 -0
  109. package/src/vendor/platform/types.ts +222 -0
  110. package/src/vendor/platform/windows/backend.ts +142 -0
  111. package/src/vendor/platform/windows/helper.ts +140 -0
  112. package/src/vendor/root-selection.ts +25 -0
  113. package/src/vendor/runtime.ts +129 -0
  114. package/src/vendor/state.ts +146 -0
  115. package/src/vendor/view.ts +147 -0
  116. package/tangu-plugins/computer-use/dist/foregroundNote.js +19 -0
  117. package/tangu-plugins/computer-use/dist/index.js +5672 -0
  118. package/tangu-plugins/computer-use/tangu-plugin.json +9 -0
  119. package/tsconfig.json +22 -0
  120. package/types/tangu-agent.d.ts +142 -0
@@ -0,0 +1,147 @@
1
+ import { serializeOutlineNode, type Outline, type OutlineChange, type OutlineDiff, type OutlineNode, type SerializedOutlineNode } from "./outline.ts";
2
+
3
+ function numericRef(ref: string): number {
4
+ const match = /^@e(\d+)$/.exec(ref);
5
+ return match ? Number(match[1]) : 0;
6
+ }
7
+
8
+ function rebuildIndexes(outline: Outline): void {
9
+ outline.nodes = [];
10
+ outline.refToWireRef = new Map();
11
+ outline.wireRefToRef = new Map();
12
+ const queue = [outline.root];
13
+ while (queue.length > 0) {
14
+ const node = queue.shift()!;
15
+ outline.nodes.push(node);
16
+ if (node.wireRef) {
17
+ outline.refToWireRef.set(node.ref, node.wireRef);
18
+ outline.wireRefToRef.set(node.wireRef, node.ref);
19
+ }
20
+ queue.push(...node.children);
21
+ }
22
+ }
23
+
24
+ function structuralToken(node: OutlineNode): string {
25
+ return [node.role, node.subrole, node.identifier, node.title, node.description].map((value) => value.trim().toLowerCase()).join("|");
26
+ }
27
+
28
+ function structuralKey(node: OutlineNode): string {
29
+ const parts: string[] = [];
30
+ let current: OutlineNode | undefined = node;
31
+ while (current) {
32
+ const token = structuralToken(current);
33
+ const siblings = current.parent?.children ?? [current];
34
+ const peers = siblings.filter((candidate) => structuralToken(candidate) === token);
35
+ parts.unshift(`${token}#${Math.max(0, peers.indexOf(current))}`);
36
+ current = current.parent;
37
+ }
38
+ return parts.join(">");
39
+ }
40
+
41
+ /** Preserve public refs only when native or structural identity is unambiguous. */
42
+ export function stabilizeRefs(base: Outline | undefined, next: Outline): Outline {
43
+ if (!base) return next;
44
+ const reserved = new Set<string>();
45
+ const assigned = new Set<OutlineNode>();
46
+ const byWireRef = new Map(base.nodes.filter((node) => node.wireRef).map((node) => [node.wireRef!, node.ref]));
47
+ const structuralGroups = new Map<string, OutlineNode[]>();
48
+ for (const node of base.nodes) {
49
+ const key = structuralKey(node);
50
+ structuralGroups.set(key, [...(structuralGroups.get(key) ?? []), node]);
51
+ }
52
+ let nextIndex = Math.max(0, ...base.nodes.map((node) => numericRef(node.ref))) + 1;
53
+ for (const node of next.nodes) {
54
+ const wireStable = node.wireRef ? byWireRef.get(node.wireRef) : undefined;
55
+ const structuralMatches = structuralGroups.get(structuralKey(node)) ?? [];
56
+ const stable = wireStable ?? (structuralMatches.length === 1 ? structuralMatches[0].ref : undefined);
57
+ if (stable && !reserved.has(stable)) {
58
+ node.ref = stable;
59
+ reserved.add(stable);
60
+ assigned.add(node);
61
+ }
62
+ }
63
+ for (const node of next.nodes) {
64
+ if (assigned.has(node)) continue;
65
+ while (reserved.has(`@e${nextIndex}`)) nextIndex += 1;
66
+ node.ref = `@e${nextIndex++}`;
67
+ reserved.add(node.ref);
68
+ }
69
+ rebuildIndexes(next);
70
+ return next;
71
+ }
72
+
73
+ function comparable(node: OutlineNode): Omit<SerializedOutlineNode, "children"> {
74
+ const { children: _children, ...fields } = serializeOutlineNode(node);
75
+ return {
76
+ ...fields,
77
+ rect: fields.rect ? { x: Math.round(fields.rect.x), y: Math.round(fields.rect.y), w: Math.round(fields.rect.w), h: Math.round(fields.rect.h) } : undefined,
78
+ text: fields.text.map((item) => ({ string: item.string, confidence: Math.round(item.confidence * 100) / 100 })),
79
+ };
80
+ }
81
+
82
+ function changedFields(base: OutlineNode, next: OutlineNode): Partial<Omit<SerializedOutlineNode, "children">> {
83
+ const before = comparable(base);
84
+ const after = comparable(next);
85
+ const fields: Partial<Omit<SerializedOutlineNode, "children">> = {};
86
+ for (const key of Object.keys(after) as Array<keyof typeof after>) {
87
+ if (key === "ref" || key === "wireRef") continue;
88
+ if (JSON.stringify(before[key]) !== JSON.stringify(after[key])) (fields as Record<string, unknown>)[key] = after[key];
89
+ }
90
+ return fields;
91
+ }
92
+
93
+ function refPath(node: OutlineNode): string[] {
94
+ const refs: string[] = [];
95
+ let current: OutlineNode | undefined = node;
96
+ while (current) {
97
+ refs.unshift(current.ref);
98
+ current = current.parent;
99
+ }
100
+ return refs;
101
+ }
102
+
103
+ export function changesBetween(base: Outline, next: Outline): OutlineDiff {
104
+ if (base.root.role !== next.root.role || base.root.subrole !== next.root.subrole) {
105
+ return { changes: [], changedNodeCount: next.nodes.length, fullNodeCount: next.nodes.length, useFullView: true, reason: "root_replaced" };
106
+ }
107
+ const before = new Map(base.nodes.map((node) => [node.ref, node]));
108
+ const after = new Map(next.nodes.map((node) => [node.ref, node]));
109
+ const changes: OutlineChange[] = [];
110
+ for (const node of next.nodes) {
111
+ const previous = before.get(node.ref);
112
+ if (!previous) changes.push({ type: "added", ref: node.ref, parent: node.parent?.ref, node: { ...serializeOutlineNode(node), children: [] } });
113
+ else {
114
+ const fields = changedFields(previous, node);
115
+ if (Object.keys(fields).length > 0) changes.push({ type: "updated", ref: node.ref, path: refPath(node), fields });
116
+ }
117
+ }
118
+ for (const node of base.nodes) if (!after.has(node.ref)) changes.push({ type: "removed", ref: node.ref, parent: node.parent?.ref });
119
+ const identityConfidence = next.nodes.length === 0 ? 1 : next.nodes.filter((node) => before.has(node.ref)).length / next.nodes.length;
120
+ const changeRatio = changes.length / Math.max(1, Math.max(base.nodes.length, next.nodes.length));
121
+ const identityLow = next.nodes.length > 8 && identityConfidence < 0.4;
122
+ const overBudget = changes.length > 100 || (changes.length > 20 && changeRatio > 0.65);
123
+ return {
124
+ changes,
125
+ changedNodeCount: changes.length,
126
+ fullNodeCount: next.nodes.length,
127
+ useFullView: identityLow || overBudget,
128
+ reason: identityLow ? "identity_confidence_low" : overBudget ? "change_budget_exceeded" : undefined,
129
+ };
130
+ }
131
+
132
+ function label(node: SerializedOutlineNode): string {
133
+ return node.title || node.description || node.value || node.identifier || node.text.map((item) => item.string).join(" ").trim() || node.role || "node";
134
+ }
135
+
136
+ export function renderChanges(changes: OutlineChange[]): string {
137
+ return changes.map((change) => {
138
+ if (change.type === "added") return `+ ${change.ref}${change.parent ? ` under ${change.parent}` : ""} ${JSON.stringify(label(change.node))}`;
139
+ if (change.type === "removed") return `- ${change.ref}${change.parent ? ` from ${change.parent}` : ""}`;
140
+ const fields = Object.entries(change.fields).filter(([key]) => !["rect", "text", "actions"].includes(key)).slice(0, 6).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(", ");
141
+ const supplemental = [
142
+ change.fields.text ? `text=${JSON.stringify(change.fields.text.map((item) => item.string))}` : undefined,
143
+ change.fields.actions ? `actions=${JSON.stringify(change.fields.actions)}` : undefined,
144
+ ].filter(Boolean).join(", ");
145
+ return `~ ${change.ref} (${change.path.join(" > ")}) ${[fields, supplemental].filter(Boolean).join(", ") || "changed"}`;
146
+ }).join("\n");
147
+ }
@@ -0,0 +1,19 @@
1
+ // src/foregroundNote.ts
2
+ function tookForeground(step) {
3
+ if (!step) return false;
4
+ if (step.escalatedToForeground === true) return true;
5
+ if (step.delivery === "ax") return false;
6
+ return step.delivery === "hid" || step.deliveryPolicy === "foreground";
7
+ }
8
+ function foregroundNote(execution) {
9
+ const exec = execution;
10
+ if (!exec || typeof exec !== "object") return "";
11
+ const steps = Array.isArray(exec.steps) && exec.steps.length ? exec.steps : [exec];
12
+ if (!steps.some(tookForeground)) return "";
13
+ const why = steps.map((s) => s?.escalationReason).find(Boolean);
14
+ return `
15
+ [foreground] This action took the foreground (focus/pointer was moved to the target)${why ? ` \u2014 ${why}` : ""}. To keep actions in the background, prefer setText to fill fields; strict background-only is the plugin's "strict background" setting (act_ui no longer takes a headless parameter).`;
16
+ }
17
+ export {
18
+ foregroundNote
19
+ };