mecord-connect 1.0.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 (82) hide show
  1. package/DISCLOSURE +47 -0
  2. package/LICENSE +64 -0
  3. package/README.md +44 -0
  4. package/bin/operator.mjs +10 -0
  5. package/package.json +58 -0
  6. package/runtime/app/apps/local-agent/src/approval-store.js +342 -0
  7. package/runtime/app/apps/local-agent/src/bootstrap-config.js +255 -0
  8. package/runtime/app/apps/local-agent/src/cli.js +237 -0
  9. package/runtime/app/apps/local-agent/src/device-reset.js +260 -0
  10. package/runtime/app/apps/local-agent/src/emergency-stop.js +141 -0
  11. package/runtime/app/apps/local-agent/src/main.js +311 -0
  12. package/runtime/app/apps/local-agent/src/privacy-data.js +129 -0
  13. package/runtime/app/apps/local-agent/src/relay-agent.js +333 -0
  14. package/runtime/app/apps/local-agent/src/relay-enrollment.js +156 -0
  15. package/runtime/app/apps/local-agent/src/relay-session-credentials.js +436 -0
  16. package/runtime/app/apps/local-agent/src/remote.js +130 -0
  17. package/runtime/app/apps/local-agent/src/runtime-factory.js +66 -0
  18. package/runtime/app/apps/local-agent/src/server.js +578 -0
  19. package/runtime/app/package.json +4 -0
  20. package/runtime/app/src/capabilities/browser-cdp-connection.js +199 -0
  21. package/runtime/app/src/capabilities/browser-cdp-frames.js +288 -0
  22. package/runtime/app/src/capabilities/browser-cdp-page.js +430 -0
  23. package/runtime/app/src/capabilities/browser-cdp.js +384 -0
  24. package/runtime/app/src/capabilities/browser-managed.js +345 -0
  25. package/runtime/app/src/capabilities/docker.js +448 -0
  26. package/runtime/app/src/capabilities/filesystem.js +264 -0
  27. package/runtime/app/src/capabilities/git-checkpoint.js +415 -0
  28. package/runtime/app/src/capabilities/git-write.js +321 -0
  29. package/runtime/app/src/capabilities/git.js +252 -0
  30. package/runtime/app/src/capabilities/index.js +16 -0
  31. package/runtime/app/src/capabilities/path-scope.js +86 -0
  32. package/runtime/app/src/capabilities/postgres.js +566 -0
  33. package/runtime/app/src/capabilities/process.js +258 -0
  34. package/runtime/app/src/capabilities/project-command.js +599 -0
  35. package/runtime/app/src/capabilities/project-transaction.js +215 -0
  36. package/runtime/app/src/capabilities/project.js +67 -0
  37. package/runtime/app/src/capabilities/system.js +40 -0
  38. package/runtime/app/src/capabilities/vscode.js +312 -0
  39. package/runtime/app/src/capabilities/windows-uia.js +331 -0
  40. package/runtime/app/src/core/account-device-registry.js +604 -0
  41. package/runtime/app/src/core/action-identity.js +44 -0
  42. package/runtime/app/src/core/audit.js +295 -0
  43. package/runtime/app/src/core/capability-policy.js +58 -0
  44. package/runtime/app/src/core/child-environment.js +29 -0
  45. package/runtime/app/src/core/device-enrollment.js +417 -0
  46. package/runtime/app/src/core/device-identity.js +436 -0
  47. package/runtime/app/src/core/device-registry.js +477 -0
  48. package/runtime/app/src/core/device-reset.js +178 -0
  49. package/runtime/app/src/core/device-routing.js +305 -0
  50. package/runtime/app/src/core/durable-state.js +306 -0
  51. package/runtime/app/src/core/errors.js +20 -0
  52. package/runtime/app/src/core/evidence.js +9 -0
  53. package/runtime/app/src/core/index.js +17 -0
  54. package/runtime/app/src/core/machine-state.js +160 -0
  55. package/runtime/app/src/core/network-authority.js +20 -0
  56. package/runtime/app/src/core/policy.js +67 -0
  57. package/runtime/app/src/core/product-identity.js +7 -0
  58. package/runtime/app/src/core/provenance.js +18 -0
  59. package/runtime/app/src/core/provider-learning.js +274 -0
  60. package/runtime/app/src/core/public-plugin-surface.js +25 -0
  61. package/runtime/app/src/core/public-restricted-data.js +105 -0
  62. package/runtime/app/src/core/rate-limit.js +118 -0
  63. package/runtime/app/src/core/relay-client.js +568 -0
  64. package/runtime/app/src/core/relay-delivery-store.js +512 -0
  65. package/runtime/app/src/core/relay-result-store.js +321 -0
  66. package/runtime/app/src/core/release-update.js +253 -0
  67. package/runtime/app/src/core/router.js +96 -0
  68. package/runtime/app/src/core/runtime.js +136 -0
  69. package/runtime/app/src/core/scoped-path-syntax.js +16 -0
  70. package/runtime/app/src/core/session-token.js +498 -0
  71. package/runtime/app/src/core/task-orchestrator.js +1148 -0
  72. package/runtime/app/src/core/task-store.js +517 -0
  73. package/runtime/app/src/core/task.js +142 -0
  74. package/runtime/app/src/core/trusted-executable.js +124 -0
  75. package/runtime/app/src/core/types.js +83 -0
  76. package/runtime/app/src/core/windows-path-lease.js +138 -0
  77. package/runtime/native/operator-windows-dpapi.exe +0 -0
  78. package/runtime/native/operator-windows-path-lease.exe +0 -0
  79. package/runtime/native/operator-windows-uia.exe +0 -0
  80. package/runtime/runtime-manifest.json +380 -0
  81. package/src/cli.mjs +345 -0
  82. package/src/publish-check.mjs +22 -0
@@ -0,0 +1,331 @@
1
+ import crypto from 'node:crypto';
2
+ import { spawn, } from 'node:child_process';
3
+ import path from 'node:path';
4
+ import readline from 'node:readline';
5
+
6
+ import { evidence } from '../core/evidence.js';
7
+ import { OperatorError } from '../core/errors.js';
8
+ import { safeChildEnvironment } from '../core/child-environment.js';
9
+
10
+ const SCORE = {
11
+ reliability: 0.95,
12
+ latency: 0.91,
13
+ determinism: 0.98,
14
+ security: 0.97,
15
+ reversibility: 0.88,
16
+ informationQuality: 0.98,
17
+ interactionCost: 0.01
18
+ };
19
+
20
+ const MAX_RESPONSE_LINE_BYTES = 4 * 1024 * 1024;
21
+ const DEFAULT_TIMEOUT_MS = 20_000;
22
+ const MAX_OBSERVE_MS = 5_000;
23
+ const MAX_WAIT_MS = 10_000;
24
+ const DEFAULT_MAX_WINDOWS = 50;
25
+ const MAX_WINDOWS = 200;
26
+ const UIA_OPERATIONS = ['invoke', 'set_value', 'focus', 'select', 'expand', 'collapse', 'scroll', 'activate_window'] ;
27
+ const SCROLL_AMOUNTS = ['large_decrement', 'small_decrement', 'none', 'large_increment', 'small_increment'] ;
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+ class WindowsUiaSidecarClient {
45
+ #binaryPath ;
46
+ #timeoutMs ;
47
+ #process ;
48
+ #pending = new Map ();
49
+ #starting ;
50
+ #stderrTail = [];
51
+
52
+ constructor(binaryPath , timeoutMs = DEFAULT_TIMEOUT_MS) {
53
+ this.#binaryPath = path.resolve(binaryPath);
54
+ this.#timeoutMs = Math.min(Math.max(timeoutMs, 1_000), 120_000);
55
+ }
56
+
57
+ async call(method , params , signal ) {
58
+ if (signal?.aborted) throw new OperatorError('EXECUTION_ABORTED', 'Windows UIA execution was cancelled.', { retryable: false });
59
+ await this.#ensureStarted();
60
+ if (signal?.aborted) throw new OperatorError('EXECUTION_ABORTED', 'Windows UIA execution was cancelled.', { retryable: false });
61
+ const child = this.#process;
62
+ if (!child || child.exitCode !== null || child.stdin.destroyed) {
63
+ throw new OperatorError('UIA_SIDECAR_UNAVAILABLE', 'Windows UIA sidecar is not running.', { retryable: true });
64
+ }
65
+
66
+ const id = crypto.randomUUID();
67
+ const payload = JSON.stringify({ id, method, params });
68
+ if (Buffer.byteLength(payload) > 256 * 1024) {
69
+ throw new OperatorError('UIA_REQUEST_TOO_LARGE', 'Windows UIA request exceeds 256 KiB.');
70
+ }
71
+
72
+ const response = await new Promise ((resolve, reject) => {
73
+ let cleanupAbort = () => {};
74
+ const timer = setTimeout(() => {
75
+ this.#pending.delete(id);
76
+ cleanupAbort();
77
+ reject(new OperatorError('UIA_SIDECAR_TIMEOUT', `${method} timed out.`, { retryable: true }));
78
+ }, this.#timeoutMs);
79
+ const onAbort = () => {
80
+ clearTimeout(timer);
81
+ this.#pending.delete(id);
82
+ cleanupAbort();
83
+ reject(new OperatorError('EXECUTION_ABORTED', 'Windows UIA execution was cancelled.', { retryable: false }));
84
+ this.close();
85
+ };
86
+ cleanupAbort = () => signal?.removeEventListener('abort', onAbort);
87
+ this.#pending.set(id, { resolve, reject, timer, cleanup: cleanupAbort });
88
+ signal?.addEventListener('abort', onAbort, { once: true });
89
+ if (signal?.aborted) {
90
+ onAbort();
91
+ return;
92
+ }
93
+ child.stdin.write(`${payload}\n`, (error) => {
94
+ if (!error) return;
95
+ clearTimeout(timer);
96
+ this.#pending.delete(id);
97
+ cleanupAbort();
98
+ reject(new OperatorError('UIA_SIDECAR_WRITE_FAILED', error.message, { retryable: true }));
99
+ });
100
+ });
101
+
102
+ if (!response.ok) {
103
+ throw new OperatorError(
104
+ response.error?.code ?? 'UIA_OPERATION_FAILED',
105
+ response.error?.message ?? 'Windows UIA operation failed.',
106
+ { retryable: response.error?.retryable === true }
107
+ );
108
+ }
109
+ return response.result;
110
+ }
111
+
112
+ close() {
113
+ const child = this.#process;
114
+ this.#process = undefined;
115
+ this.#starting = undefined;
116
+ const error = new OperatorError('UIA_SIDECAR_CLOSED', 'Windows UIA sidecar closed.', { retryable: true });
117
+ for (const pending of this.#pending.values()) {
118
+ clearTimeout(pending.timer);
119
+ pending.cleanup?.();
120
+ pending.reject(error);
121
+ }
122
+ this.#pending.clear();
123
+ if (child && child.exitCode === null && !child.killed) {
124
+ try { child.kill('SIGTERM'); } catch { /* noop */ }
125
+ }
126
+ }
127
+
128
+ async #ensureStarted() {
129
+ if (this.#process && this.#process.exitCode === null) return;
130
+ if (this.#starting) return this.#starting;
131
+ this.#starting = this.#start();
132
+ try { await this.#starting; } finally { this.#starting = undefined; }
133
+ }
134
+
135
+ async #start() {
136
+ const child = spawn(this.#binaryPath, [], {
137
+ shell: false,
138
+ stdio: ['pipe', 'pipe', 'pipe'],
139
+ windowsHide: true,
140
+ env: safeChildEnvironment('windows-native')
141
+ });
142
+ this.#process = child;
143
+
144
+ child.stderr.setEncoding('utf8');
145
+ child.stderr.on('data', (chunk ) => {
146
+ for (const line of chunk.split(/\r?\n/).filter(Boolean)) {
147
+ this.#stderrTail.push(line.slice(0, 1000));
148
+ if (this.#stderrTail.length > 20) this.#stderrTail.shift();
149
+ }
150
+ });
151
+
152
+ const lines = readline.createInterface({ input: child.stdout, crlfDelay: Infinity });
153
+ lines.on('line', (line) => {
154
+ if (Buffer.byteLength(line) > MAX_RESPONSE_LINE_BYTES) {
155
+ this.close();
156
+ return;
157
+ }
158
+ let response ;
159
+ try { response = JSON.parse(line) ; } catch { return; }
160
+ if (!response?.id) return;
161
+ const pending = this.#pending.get(response.id);
162
+ if (!pending) return;
163
+ clearTimeout(pending.timer);
164
+ pending.cleanup?.();
165
+ this.#pending.delete(response.id);
166
+ pending.resolve(response);
167
+ });
168
+
169
+ child.once('exit', (code, signal) => {
170
+ if (this.#process === child) this.#process = undefined;
171
+ const details = this.#stderrTail.slice(-5).join(' | ');
172
+ const error = new OperatorError(
173
+ 'UIA_SIDECAR_EXITED',
174
+ `Windows UIA sidecar exited (${code ?? signal ?? 'unknown'}).${details ? ` ${details}` : ''}`,
175
+ { retryable: true }
176
+ );
177
+ for (const pending of this.#pending.values()) {
178
+ clearTimeout(pending.timer);
179
+ pending.cleanup?.();
180
+ pending.reject(error);
181
+ }
182
+ this.#pending.clear();
183
+ });
184
+
185
+ await new Promise ((resolve, reject) => {
186
+ const timer = setTimeout(() => reject(new OperatorError('UIA_SIDECAR_START_TIMEOUT', 'Windows UIA sidecar did not become ready.', { retryable: true })), 5_000);
187
+ child.once('spawn', () => {
188
+ clearTimeout(timer);
189
+ resolve();
190
+ });
191
+ child.once('error', (error) => {
192
+ clearTimeout(timer);
193
+ reject(new OperatorError('UIA_SIDECAR_START_FAILED', error.message, { retryable: false }));
194
+ });
195
+ });
196
+
197
+ try {
198
+ await this.call('health', {});
199
+ } catch (error) {
200
+ this.close();
201
+ throw error;
202
+ }
203
+ }
204
+ }
205
+
206
+ export class WindowsUiaProvider {
207
+ name = 'windows.uia';
208
+ #platform ;
209
+ #client ;
210
+
211
+ constructor(options = {}) {
212
+ this.#platform = options.platform ?? process.platform;
213
+ const binary = options.binaryPath
214
+ ?? process.env.OPERATOR_WINDOWS_UIA_PATH
215
+ ?? path.join(process.cwd(), 'native', 'windows-uia', 'target', 'release', 'operator-windows-uia.exe');
216
+ this.#client = new WindowsUiaSidecarClient(binary, options.timeoutMs);
217
+ }
218
+
219
+ supports(action ) {
220
+ return this.#platform === 'win32' && ['app.inspect', 'app.operate'].includes(action.capability);
221
+ }
222
+
223
+ score() { return SCORE; }
224
+
225
+ async execute(action , context = {}) {
226
+ const started = performance.now();
227
+ try {
228
+ const output = action.capability === 'app.inspect'
229
+ ? await this.#client.call('inspect', normalizeInspectInput(action.input), context.signal)
230
+ : await this.#client.call('operate', normalizeOperateInput(action.input), context.signal);
231
+ const postcondition = action.capability === 'app.operate'
232
+ ? evidence('postcondition', 'pass', 'Windows UIA/Win32 operation returned verified semantic postcondition evidence.', { operation: action.input.operation, waitMs: normalizeWaitMs(action.input.waitMs) })
233
+ : evidence('data_minimization', 'pass', 'Windows UIA returned a bounded semantic control tree, optional scoped events, and opt-in top-level window metadata instead of screenshots or process internals.', {
234
+ observeMs: normalizeObserveMs(action.input.observeMs),
235
+ waitMs: normalizeWaitMs(action.input.waitMs),
236
+ includeWindows: action.input.includeWindows === true,
237
+ maxWindows: normalizeMaxWindows(action.input.maxWindows)
238
+ });
239
+ return {
240
+ ok: true,
241
+ capability: action.capability,
242
+ provider: this.name,
243
+ output,
244
+ evidence: [
245
+ evidence('windows_uia', 'pass', action.capability === 'app.inspect' ? 'Inspected Windows controls through Microsoft UI Automation and optional bounded Win32 discovery.' : 'Operated Windows control through a verified UI Automation or semantic Win32 operation.', {}),
246
+ postcondition
247
+ ],
248
+ durationMs: Math.round(performance.now() - started)
249
+ };
250
+ } catch (error) {
251
+ const op = error instanceof OperatorError
252
+ ? error
253
+ : new OperatorError('UIA_PROVIDER_FAILED', error instanceof Error ? error.message : String(error), { retryable: true });
254
+ return {
255
+ ok: false,
256
+ capability: action.capability,
257
+ provider: this.name,
258
+ evidence: [evidence('windows_uia', 'fail', op.message, { code: op.code })],
259
+ error: { code: op.code, message: op.message, retryable: op.retryable },
260
+ durationMs: Math.round(performance.now() - started)
261
+ };
262
+ }
263
+ }
264
+
265
+ close() { this.#client.close(); }
266
+ }
267
+
268
+ function normalizeSelector(input ) {
269
+ const raw = input && typeof input === 'object' ? input : {};
270
+ const text = (key ) => typeof raw[key] === 'string' ? String(raw[key]).trim().slice(0, 512) || undefined : undefined;
271
+ const processId = Number.isInteger(raw.processId) && Number(raw.processId) > 0 ? Number(raw.processId) : undefined;
272
+ return {
273
+ name: text('name'),
274
+ automation_id: text('automationId'),
275
+ class_name: text('className'),
276
+ control_type: text('controlType'),
277
+ process_id: processId
278
+ };
279
+ }
280
+
281
+ function normalizeObserveMs(value ) {
282
+ return Number.isInteger(value) ? Math.min(Math.max(Number(value), 0), MAX_OBSERVE_MS) : 0;
283
+ }
284
+
285
+ function normalizeWaitMs(value ) {
286
+ return Number.isInteger(value) ? Math.min(Math.max(Number(value), 0), MAX_WAIT_MS) : 0;
287
+ }
288
+
289
+ function normalizeMaxWindows(value ) {
290
+ return Number.isInteger(value) ? Math.min(Math.max(Number(value), 1), MAX_WINDOWS) : DEFAULT_MAX_WINDOWS;
291
+ }
292
+
293
+ function normalizeInspectInput(input ) {
294
+ return {
295
+ selector: input.selector ? normalizeSelector(input.selector) : undefined,
296
+ max_nodes: Number.isInteger(input.maxNodes) ? Number(input.maxNodes) : undefined,
297
+ max_depth: Number.isInteger(input.maxDepth) ? Number(input.maxDepth) : undefined,
298
+ observe_ms: normalizeObserveMs(input.observeMs),
299
+ wait_ms: normalizeWaitMs(input.waitMs),
300
+ include_windows: input.includeWindows === true,
301
+ max_windows: normalizeMaxWindows(input.maxWindows)
302
+ };
303
+ }
304
+
305
+ function normalizeScrollAmount(value , field ) {
306
+ if (value === undefined) return undefined;
307
+ if (typeof value !== 'string' || !(SCROLL_AMOUNTS ).includes(value)) {
308
+ throw new OperatorError('INVALID_UIA_SCROLL_AMOUNT', `${field} must be one of ${SCROLL_AMOUNTS.join(', ')}.`);
309
+ }
310
+ return value;
311
+ }
312
+
313
+ function normalizeOperateInput(input ) {
314
+ const operation = String(input.operation ?? '');
315
+ if (!(UIA_OPERATIONS ).includes(operation)) {
316
+ throw new OperatorError('INVALID_UIA_OPERATION', `operation must be ${UIA_OPERATIONS.join(', ')}.`);
317
+ }
318
+ const horizontalAmount = normalizeScrollAmount(input.horizontalAmount, 'horizontalAmount');
319
+ const verticalAmount = normalizeScrollAmount(input.verticalAmount, 'verticalAmount');
320
+ if (operation === 'scroll' && horizontalAmount === undefined && verticalAmount === undefined) {
321
+ throw new OperatorError('INVALID_UIA_SCROLL_AMOUNT', 'scroll requires horizontalAmount or verticalAmount.');
322
+ }
323
+ return {
324
+ operation,
325
+ selector: normalizeSelector(input.selector),
326
+ value: input.value === undefined ? undefined : String(input.value),
327
+ horizontal_amount: horizontalAmount,
328
+ vertical_amount: verticalAmount,
329
+ wait_ms: normalizeWaitMs(input.waitMs)
330
+ };
331
+ }