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,215 @@
1
+
2
+ import { evidence } from '../core/evidence.js';
3
+ import { OperatorError } from '../core/errors.js';
4
+ import { GitCheckpointProvider } from './git-checkpoint.js';
5
+ import { ProjectCommandProvider } from './project-command.js';
6
+
7
+ const SCORE = {
8
+ reliability: 0.99,
9
+ latency: 0.88,
10
+ determinism: 0.99,
11
+ security: 0.99,
12
+ reversibility: 0.99,
13
+ informationQuality: 0.99,
14
+ interactionCost: 0.02
15
+ };
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+ export class ProjectTransactionProvider {
27
+ name = 'project.transaction.git';
28
+ #commands ;
29
+ #checkpoints ;
30
+
31
+ constructor(options ) {
32
+ this.#commands = new ProjectCommandProvider(options);
33
+ this.#checkpoints = new GitCheckpointProvider({ allowedRoots: options.allowedRoots });
34
+ }
35
+
36
+ supports(action ) { return action.capability === 'project.transaction.run'; }
37
+ score() { return SCORE; }
38
+
39
+ async execute(action ) {
40
+ const started = performance.now();
41
+ try {
42
+ if (action.risk !== 'destructive') {
43
+ throw new OperatorError('TRANSACTION_DESTRUCTIVE_RISK_REQUIRED', 'Automatic rollback transactions must be authorized as destructive before execution.');
44
+ }
45
+ const projectRoot = String(action.input.path ?? '');
46
+ const commandId = String(action.input.commandId ?? '');
47
+ const expectedRisk = String(action.input.expectedRisk ?? '');
48
+ if (!['read', 'write'].includes(expectedRisk)) {
49
+ throw new OperatorError('TRANSACTION_RISK_UNSUPPORTED', 'Automatic rollback supports only trusted read/write commands; external effects are not reversible.');
50
+ }
51
+
52
+ const inspected = await this.#commands.execute({
53
+ id: `${action.id}:inspect-command`,
54
+ capability: 'project.command.inspect',
55
+ risk: 'read',
56
+ input: { path: projectRoot },
57
+ provenance: { kind: 'trusted_policy', source: action.id }
58
+ });
59
+ if (!inspected.ok) throw childError(inspected, 'TRANSACTION_COMMAND_INSPECT_FAILED');
60
+ const command = ((inspected.output )?.commands ?? [])
61
+ .find((candidate) => candidate.id === commandId);
62
+ if (!command) throw new OperatorError('PROJECT_COMMAND_NOT_REGISTERED', `Trusted command ${commandId} is not registered for this project.`);
63
+ if (command.risk === 'external') {
64
+ throw new OperatorError('TRANSACTION_EXTERNAL_UNSUPPORTED', 'External trusted commands cannot run inside automatic rollback transactions.');
65
+ }
66
+ if (command.risk !== expectedRisk) {
67
+ throw new OperatorError('PROJECT_COMMAND_RISK_MISMATCH', 'Transaction expectedRisk must match the trusted registry risk.', {
68
+ details: { expectedRisk, registeredRisk: command.risk }
69
+ });
70
+ }
71
+
72
+ const checkpointResult = await this.#checkpoints.execute({
73
+ id: `${action.id}:checkpoint`,
74
+ capability: 'git.checkpoint.create',
75
+ risk: 'write',
76
+ input: { cwd: projectRoot, label: `transaction before ${commandId}` },
77
+ provenance: { kind: 'trusted_policy', source: action.id }
78
+ });
79
+ if (!checkpointResult.ok) throw childError(checkpointResult, 'TRANSACTION_CHECKPOINT_FAILED');
80
+ const checkpoint = checkpointResult.output ;
81
+
82
+ const commandResult = await this.#commands.execute({
83
+ id: `${action.id}:command`,
84
+ capability: 'project.command.run',
85
+ risk: command.risk,
86
+ input: { path: projectRoot, commandId, expectedRisk: command.risk },
87
+ provenance: { kind: 'trusted_policy', source: action.id }
88
+ });
89
+
90
+ if (commandResult.ok) {
91
+ return {
92
+ ok: true,
93
+ capability: action.capability,
94
+ provider: this.name,
95
+ output: {
96
+ commandId,
97
+ commandRisk: command.risk,
98
+ checkpointId: checkpoint.id,
99
+ checkpointFingerprint: checkpoint.fingerprint,
100
+ rollbackPerformed: false,
101
+ rollbackCoverage: 'Git index + non-ignored working-tree state captured by git.checkpoint',
102
+ command: commandResult
103
+ },
104
+ evidence: [
105
+ evidence('transaction_checkpoint', 'pass', 'Created a non-mutating Git checkpoint before the trusted command.', { checkpointId: checkpoint.id }),
106
+ evidence('transaction_command', 'pass', 'Trusted command and declared postconditions verified; rollback was not required.', { commandId }),
107
+ evidence('recovery_checkpoint', 'info', 'Pre-command checkpoint remains available for explicit recovery.', { checkpointId: checkpoint.id })
108
+ ],
109
+ durationMs: Math.round(performance.now() - started)
110
+ };
111
+ }
112
+
113
+ const currentResult = await this.#checkpoints.execute({
114
+ id: `${action.id}:inspect-after-failure`,
115
+ capability: 'git.checkpoint.inspect',
116
+ risk: 'read',
117
+ input: { cwd: projectRoot },
118
+ provenance: { kind: 'trusted_policy', source: action.id }
119
+ });
120
+ if (!currentResult.ok) {
121
+ return rollbackFailure(action, started, checkpoint.id, commandResult, currentResult, 'Unable to inspect repository state before rollback.');
122
+ }
123
+ const current = (currentResult.output )?.current;
124
+ if (!current?.fingerprint) {
125
+ return rollbackFailure(action, started, checkpoint.id, commandResult, currentResult, 'Repository fingerprint was unavailable before rollback.');
126
+ }
127
+
128
+ const restoreResult = await this.#checkpoints.execute({
129
+ id: `${action.id}:rollback`,
130
+ capability: 'git.checkpoint.restore',
131
+ risk: 'destructive',
132
+ input: {
133
+ cwd: projectRoot,
134
+ checkpointId: checkpoint.id,
135
+ expectedCurrentFingerprint: current.fingerprint
136
+ },
137
+ provenance: { kind: 'trusted_policy', source: action.id }
138
+ });
139
+
140
+ if (!restoreResult.ok) {
141
+ return rollbackFailure(action, started, checkpoint.id, commandResult, restoreResult, 'Trusted command failed and automatic Git rollback could not be verified.');
142
+ }
143
+
144
+ return {
145
+ ok: false,
146
+ capability: action.capability,
147
+ provider: this.name,
148
+ output: {
149
+ commandId,
150
+ commandRisk: command.risk,
151
+ checkpointId: checkpoint.id,
152
+ rollbackPerformed: true,
153
+ rollbackCoverage: 'Git index + non-ignored working-tree state captured by git.checkpoint',
154
+ command: commandResult,
155
+ rollback: restoreResult
156
+ },
157
+ evidence: [
158
+ evidence('transaction_checkpoint', 'pass', 'Created a non-mutating Git checkpoint before the trusted command.', { checkpointId: checkpoint.id }),
159
+ evidence('transaction_command', 'fail', commandResult.error?.message ?? 'Trusted command verification failed.', { commandId, code: commandResult.error?.code }),
160
+ evidence('transaction_rollback', 'pass', 'Trusted command failed; Git checkpoint restore completed and verified.', { checkpointId: checkpoint.id })
161
+ ],
162
+ error: {
163
+ code: 'TRANSACTION_FAILED_ROLLED_BACK',
164
+ message: `Trusted command ${commandId} failed verification and repository state was rolled back to the pre-command checkpoint.`,
165
+ retryable: true
166
+ },
167
+ durationMs: Math.round(performance.now() - started)
168
+ };
169
+ } catch (error) {
170
+ const op = error instanceof OperatorError
171
+ ? error
172
+ : new OperatorError('PROJECT_TRANSACTION_ERROR', error instanceof Error ? error.message : String(error));
173
+ return {
174
+ ok: false,
175
+ capability: action.capability,
176
+ provider: this.name,
177
+ evidence: [evidence('project_transaction', 'fail', op.message, { code: op.code })],
178
+ error: { code: op.code, message: op.message, retryable: op.retryable },
179
+ durationMs: Math.round(performance.now() - started)
180
+ };
181
+ }
182
+ }
183
+ }
184
+
185
+ function childError(result , fallbackCode ) {
186
+ return new OperatorError(result.error?.code ?? fallbackCode, result.error?.message ?? fallbackCode, { retryable: result.error?.retryable });
187
+ }
188
+
189
+ function rollbackFailure(
190
+ action ,
191
+ started ,
192
+ checkpointId ,
193
+ commandResult ,
194
+ rollbackResult ,
195
+ message
196
+ ) {
197
+ return {
198
+ ok: false,
199
+ capability: action.capability,
200
+ provider: 'project.transaction.git',
201
+ output: {
202
+ checkpointId,
203
+ rollbackPerformed: false,
204
+ rollbackCoverage: 'Git index + non-ignored working-tree state captured by git.checkpoint',
205
+ command: commandResult,
206
+ rollbackAttempt: rollbackResult
207
+ },
208
+ evidence: [
209
+ evidence('transaction_command', 'fail', commandResult.error?.message ?? 'Trusted command verification failed.', { code: commandResult.error?.code }),
210
+ evidence('transaction_rollback', 'fail', message, { checkpointId, code: rollbackResult.error?.code })
211
+ ],
212
+ error: { code: 'TRANSACTION_FAILED_ROLLBACK_FAILED', message, retryable: false },
213
+ durationMs: Math.round(performance.now() - started)
214
+ };
215
+ }
@@ -0,0 +1,67 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import { evidence } from '../core/evidence.js';
5
+ import { PathScope } from './path-scope.js';
6
+
7
+ const SCORE = {
8
+ reliability: 0.97,
9
+ latency: 0.96,
10
+ determinism: 0.98,
11
+ security: 0.96,
12
+ reversibility: 1,
13
+ informationQuality: 0.94,
14
+ interactionCost: 0.01
15
+ };
16
+
17
+ export class ProjectInspectProvider {
18
+ name = 'project.semantic';
19
+ #scope ;
20
+
21
+ constructor(options ) { this.#scope = new PathScope(options.allowedRoots); }
22
+ supports(action ) { return action.capability === 'project.inspect'; }
23
+ score() { return SCORE; }
24
+
25
+ async execute(action ) {
26
+ const started = performance.now();
27
+ const root = await this.#scope.resolveExisting(String(action.input.path ?? ''));
28
+ const names = await fs.readdir(root);
29
+ const has = (name ) => names.includes(name);
30
+ const packageJson = await maybeJson(path.join(root, 'package.json'));
31
+ const pyproject = await maybeText(path.join(root, 'pyproject.toml'), 32 * 1024);
32
+
33
+ const output = {
34
+ root,
35
+ repository: has('.git'),
36
+ buildSystems: [
37
+ has('package.json') ? 'node' : null,
38
+ has('Cargo.toml') ? 'cargo' : null,
39
+ has('pyproject.toml') ? 'python' : null,
40
+ has('CMakeLists.txt') ? 'cmake' : null,
41
+ has('pom.xml') ? 'maven' : null,
42
+ has('build.gradle') || has('build.gradle.kts') ? 'gradle' : null
43
+ ].filter(Boolean),
44
+ packageManager: has('pnpm-lock.yaml') ? 'pnpm' : has('yarn.lock') ? 'yarn' : has('package-lock.json') ? 'npm' : null,
45
+ scripts: packageJson && typeof packageJson === 'object' ? (packageJson ).scripts ?? null : null,
46
+ manifests: names.filter((name) => ['package.json', 'Cargo.toml', 'pyproject.toml', 'pom.xml', 'CMakeLists.txt', 'build.gradle', 'build.gradle.kts'].includes(name)),
47
+ pyprojectDetected: pyproject !== null,
48
+ observedAt: new Date().toISOString()
49
+ };
50
+
51
+ return {
52
+ ok: true,
53
+ capability: action.capability,
54
+ provider: this.name,
55
+ output,
56
+ evidence: [evidence('project_model', 'pass', 'Project metadata derived from files in authorized scope.', { root })],
57
+ durationMs: Math.round(performance.now() - started)
58
+ };
59
+ }
60
+ }
61
+
62
+ async function maybeJson(filePath ) {
63
+ try { return JSON.parse(await fs.readFile(filePath, 'utf8')); } catch { return null; }
64
+ }
65
+ async function maybeText(filePath , max ) {
66
+ try { return (await fs.readFile(filePath, 'utf8')).slice(0, max); } catch { return null; }
67
+ }
@@ -0,0 +1,40 @@
1
+ import os from 'node:os';
2
+
3
+ import { evidence } from '../core/evidence.js';
4
+
5
+ const SCORE = {
6
+ reliability: 1,
7
+ latency: 1,
8
+ determinism: 1,
9
+ security: 0.99,
10
+ reversibility: 1,
11
+ informationQuality: 0.95,
12
+ interactionCost: 0
13
+ };
14
+
15
+ export class SystemInspectProvider {
16
+ name = 'system.native';
17
+ supports(action ) { return action.capability === 'computer.inspect'; }
18
+ score() { return SCORE; }
19
+
20
+ async execute(action ) {
21
+ const started = performance.now();
22
+ const output = {
23
+ hostname: os.hostname(),
24
+ platform: os.platform(),
25
+ release: os.release(),
26
+ arch: os.arch(),
27
+ cpuCount: os.cpus().length,
28
+ totalMemoryBytes: os.totalmem(),
29
+ node: process.version
30
+ };
31
+ return {
32
+ ok: true,
33
+ capability: action.capability,
34
+ provider: this.name,
35
+ output,
36
+ evidence: [evidence('computer_state', 'pass', 'Computer state gathered using native APIs.')],
37
+ durationMs: Math.round(performance.now() - started)
38
+ };
39
+ }
40
+ }
@@ -0,0 +1,312 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+
6
+ import { evidence } from '../core/evidence.js';
7
+ import { OperatorError } from '../core/errors.js';
8
+ import { resolveTrustedExecutable } from '../core/trusted-executable.js';
9
+ import { PathScope } from './path-scope.js';
10
+
11
+ const SCORE = {
12
+ reliability: 0.94,
13
+ latency: 0.88,
14
+ determinism: 0.93,
15
+ security: 0.96,
16
+ reversibility: 0.96,
17
+ informationQuality: 0.9,
18
+ interactionCost: 0.04
19
+ };
20
+
21
+ const MAX_OUTPUT_BYTES = 512 * 1024;
22
+ const MAX_EXTENSIONS = 1000;
23
+
24
+ export class VsCodeProvider {
25
+ name = 'vscode.cli.isolated';
26
+ #scope ;
27
+ #allowedRoots ;
28
+ #codeExecutable ;
29
+ #codeArgsPrefix ;
30
+ #dataDir ;
31
+
32
+ constructor(options ) {
33
+ this.#scope = new PathScope(options.allowedRoots);
34
+ this.#allowedRoots = options.allowedRoots.map((root) => path.resolve(root));
35
+ this.#codeExecutable = options.codeExecutable ?? 'code';
36
+ this.#codeArgsPrefix = options.codeArgsPrefix ?? [];
37
+ this.#dataDir = path.resolve(options.dataDir ?? path.join(os.homedir(), '.operator', 'vscode-safe'));
38
+ }
39
+
40
+ supports(action ) {
41
+ return action.capability === 'vscode.inspect' || action.capability === 'vscode.open';
42
+ }
43
+
44
+ score() { return SCORE; }
45
+
46
+ async execute(action ) {
47
+ const started = performance.now();
48
+ try {
49
+ await this.#assertDataDirOutsideProjects();
50
+ if (action.capability === 'vscode.inspect') {
51
+ const operation = String(action.input.operation ?? 'status');
52
+ if (operation === 'version') {
53
+ const result = await this.#run(['--version'], process.cwd(), 10_000);
54
+ return ok(action, started, { operation, version: result.stdout.trim().split(/\r?\n/)[0] ?? '' }, [
55
+ evidence('vscode_cli', 'pass', 'Read VS Code CLI version without opening a workspace.')
56
+ ]);
57
+ }
58
+ if (operation === 'extensions') {
59
+ const result = await this.#run(['--list-extensions', '--show-versions'], process.cwd(), 15_000);
60
+ const extensions = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(0, MAX_EXTENSIONS).map((line) => {
61
+ const at = line.lastIndexOf('@');
62
+ return at > 0 ? { id: line.slice(0, at), version: line.slice(at + 1) } : { id: line };
63
+ });
64
+ return ok(action, started, { operation, extensions, truncated: extensions.length >= MAX_EXTENSIONS }, [
65
+ evidence('vscode_extensions', 'pass', 'Listed installed VS Code extension identifiers and versions without opening a project.', { count: extensions.length })
66
+ ]);
67
+ }
68
+ if (operation === 'status') {
69
+ const result = await this.#run(['--status'], process.cwd(), 15_000);
70
+ return ok(action, started, { operation, status: result.stdout.slice(0, MAX_OUTPUT_BYTES), truncated: result.truncated }, [
71
+ evidence('vscode_status', 'pass', 'Read bounded VS Code process/status diagnostics through the official CLI.')
72
+ ]);
73
+ }
74
+ throw new OperatorError('INVALID_VSCODE_INSPECT_OPERATION', 'operation must be version, extensions, or status.');
75
+ }
76
+
77
+ const mode = String(action.input.mode ?? 'folder');
78
+ const timeoutMs = boundedInteger(action.input.timeoutMs, 15_000, 1_000, 60_000);
79
+ await fs.mkdir(this.#dataDir, { recursive: true, mode: 0o700 });
80
+ const args = ['--new-window', '--disable-extensions', `--user-data-dir=${this.#dataDir}`];
81
+ let targetSummary ;
82
+ let cwd ;
83
+
84
+ if (mode === 'folder') {
85
+ const folder = await this.#scope.resolveExisting(String(action.input.path ?? ''));
86
+ const stat = await fs.stat(folder);
87
+ if (!stat.isDirectory()) throw new OperatorError('VSCODE_FOLDER_REQUIRED', 'folder mode requires an authorized directory.');
88
+ args.push(folder);
89
+ cwd = folder;
90
+ targetSummary = { mode, path: folder };
91
+ } else if (mode === 'file' || mode === 'goto') {
92
+ const file = await this.#scope.resolveExisting(String(action.input.path ?? ''));
93
+ const stat = await fs.stat(file);
94
+ if (!stat.isFile()) throw new OperatorError('VSCODE_FILE_REQUIRED', `${mode} mode requires an authorized regular file.`);
95
+ cwd = path.dirname(file);
96
+ if (mode === 'goto') {
97
+ const line = boundedPositiveInt(action.input.line, 1, 1_000_000, 'line');
98
+ const column = boundedPositiveInt(action.input.column ?? 1, 1, 1_000_000, 'column');
99
+ args.push('--goto', `${file}:${line}:${column}`);
100
+ targetSummary = { mode, path: file, line, column };
101
+ } else {
102
+ args.push(file);
103
+ targetSummary = { mode, path: file };
104
+ }
105
+ } else if (mode === 'diff') {
106
+ const left = await this.#scope.resolveExisting(String(action.input.leftPath ?? ''));
107
+ const right = await this.#scope.resolveExisting(String(action.input.rightPath ?? ''));
108
+ const [leftStat, rightStat] = await Promise.all([fs.stat(left), fs.stat(right)]);
109
+ if (!leftStat.isFile() || !rightStat.isFile()) throw new OperatorError('VSCODE_DIFF_FILES_REQUIRED', 'diff mode requires two authorized regular files.');
110
+ args.push('--diff', left, right);
111
+ cwd = path.dirname(left);
112
+ targetSummary = { mode, leftPath: left, rightPath: right };
113
+ } else {
114
+ throw new OperatorError('INVALID_VSCODE_OPEN_MODE', 'mode must be folder, file, goto, or diff.');
115
+ }
116
+
117
+ const result = await this.#run(args, cwd, timeoutMs);
118
+ return ok(action, started, {
119
+ ...targetSummary,
120
+ isolated: true,
121
+ extensionsDisabled: true,
122
+ accepted: true,
123
+ stdout: result.stdout.slice(0, 32_000),
124
+ stderr: result.stderr.slice(0, 32_000)
125
+ }, [
126
+ evidence('vscode_isolation', 'pass', 'Opened target in a new Operator-isolated VS Code window with extensions disabled.', {
127
+ userDataDir: this.#dataDir,
128
+ reusedExistingWindow: false
129
+ }),
130
+ evidence('vscode_cli', 'pass', 'VS Code CLI accepted the bounded semantic open request.', { mode })
131
+ ]);
132
+ } catch (error) {
133
+ const op = error instanceof OperatorError
134
+ ? error
135
+ : new OperatorError('VSCODE_ERROR', error instanceof Error ? error.message : String(error), { retryable: true });
136
+ return {
137
+ ok: false,
138
+ capability: action.capability,
139
+ provider: this.name,
140
+ evidence: [evidence('vscode', 'fail', op.message, { code: op.code })],
141
+ error: { code: op.code, message: op.message, retryable: op.retryable },
142
+ durationMs: Math.round(performance.now() - started)
143
+ };
144
+ }
145
+ }
146
+
147
+ async #assertDataDirOutsideProjects() {
148
+ const lexicalDataDir = path.resolve(this.#dataDir);
149
+ for (const configuredRoot of this.#allowedRoots) {
150
+ const lexicalRoot = path.resolve(configuredRoot);
151
+ if (inside(lexicalDataDir, lexicalRoot)) {
152
+ throw new OperatorError('VSCODE_DATA_DIR_INSIDE_PROJECT_DENIED', 'Operator VS Code user-data directory must live outside all authorized project roots.');
153
+ }
154
+ }
155
+ await fs.mkdir(path.dirname(this.#dataDir), { recursive: true, mode: 0o700 });
156
+ const parent = await fs.realpath(path.dirname(this.#dataDir));
157
+ const resolvedDataDir = path.join(parent, path.basename(this.#dataDir));
158
+ for (const configuredRoot of this.#allowedRoots) {
159
+ let root = configuredRoot;
160
+ try { root = await fs.realpath(configuredRoot); } catch { /* lexical fallback */ }
161
+ if (inside(resolvedDataDir, root)) {
162
+ throw new OperatorError('VSCODE_DATA_DIR_INSIDE_PROJECT_DENIED', 'Operator VS Code user-data directory must live outside all authorized project roots.');
163
+ }
164
+ }
165
+ }
166
+
167
+ async #run(args , cwd , timeoutMs ) {
168
+ const output = await runCode(this.#codeExecutable, [...this.#codeArgsPrefix, ...args], cwd, timeoutMs);
169
+ if (output.code !== 0) throw new OperatorError('VSCODE_CLI_FAILED', `VS Code CLI exited with code ${output.code}.`, { details: { stderr: output.stderr.slice(-4000) } });
170
+ return output;
171
+ }
172
+ }
173
+
174
+ function boundedInteger(input , fallback , min , max ) {
175
+ const value = Number(input ?? fallback);
176
+ if (!Number.isFinite(value)) return fallback;
177
+ return Math.min(Math.max(Math.trunc(value), min), max);
178
+ }
179
+
180
+ function boundedPositiveInt(input , min , max , label ) {
181
+ const value = Number(input);
182
+ if (!Number.isInteger(value) || value < min || value > max) throw new OperatorError('VSCODE_POSITION_INVALID', `${label} must be an integer between ${min} and ${max}.`);
183
+ return value;
184
+ }
185
+
186
+ function inside(candidate , root ) {
187
+ const rel = path.relative(root, candidate);
188
+ return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
189
+ }
190
+
191
+ function safeEnvironment() {
192
+ const env = {};
193
+ for (const key of [
194
+ 'PATH', 'Path', 'PATHEXT', 'SYSTEMROOT', 'WINDIR', 'COMSPEC',
195
+ 'HOME', 'USERPROFILE', 'HOMEDRIVE', 'HOMEPATH', 'USER', 'USERNAME', 'LOGNAME',
196
+ 'TMP', 'TEMP', 'TMPDIR', 'LANG', 'LC_ALL', 'LC_CTYPE', 'TERM', 'COLORTERM', 'SHELL',
197
+ 'PROGRAMFILES', 'PROGRAMFILES(X86)', 'PROGRAMW6432', 'PROGRAMDATA', 'LOCALAPPDATA', 'APPDATA',
198
+ 'DISPLAY', 'WAYLAND_DISPLAY', 'XDG_RUNTIME_DIR', 'XDG_DATA_HOME', 'XDG_CONFIG_HOME', 'DBUS_SESSION_BUS_ADDRESS'
199
+ ]) {
200
+ if (process.env[key] !== undefined) env[key] = process.env[key];
201
+ }
202
+ return env;
203
+ }
204
+
205
+ export function candidateVsCodeExecutablePaths(source ) {
206
+ const candidates = [];
207
+ const add = (value ) => {
208
+ if (!value || !path.win32.isAbsolute(value)) return;
209
+ const normalized = path.win32.normalize(value);
210
+ if (!candidates.some((item) => item.toLowerCase() === normalized.toLowerCase())) candidates.push(normalized);
211
+ };
212
+ if (source.LOCALAPPDATA) add(path.win32.join(source.LOCALAPPDATA, 'Programs', 'Microsoft VS Code', 'Code.exe'));
213
+ for (const root of [source.PROGRAMFILES, source['PROGRAMFILES(X86)'], source.PROGRAMW6432]) {
214
+ if (root) add(path.win32.join(root, 'Microsoft VS Code', 'Code.exe'));
215
+ }
216
+ return candidates;
217
+ }
218
+
219
+ function resolveVsCodeExecutable(executable , environment ) {
220
+ if (process.platform === 'win32' && executable.trim().toLowerCase() === 'code') {
221
+ for (const candidate of candidateVsCodeExecutablePaths(environment)) {
222
+ try {
223
+ return resolveTrustedExecutable(candidate, environment);
224
+ } catch (error) {
225
+ if (!(error instanceof OperatorError) || error.code !== 'EXECUTABLE_NOT_FOUND') throw error;
226
+ }
227
+ }
228
+ }
229
+ return resolveTrustedExecutable(executable, environment);
230
+ }
231
+
232
+ export async function resolveVsCodeCliScript(codeExecutable ) {
233
+ const realExecutable = await fs.realpath(codeExecutable);
234
+ const installRoot = path.dirname(realExecutable);
235
+ const realRoot = await fs.realpath(installRoot);
236
+ const entries = await fs.readdir(installRoot, { withFileTypes: true });
237
+ const candidates = [];
238
+ for (const entry of entries) {
239
+ if (!entry.isDirectory() || !/^[0-9a-f]{10,40}$/i.test(entry.name)) continue;
240
+ const candidate = path.join(installRoot, entry.name, 'resources', 'app', 'out', 'cli.js');
241
+ try {
242
+ const stat = await fs.lstat(candidate);
243
+ if (!stat.isFile() || stat.isSymbolicLink()) continue;
244
+ const realCandidate = await fs.realpath(candidate);
245
+ if (!inside(realCandidate, realRoot)) continue;
246
+ candidates.push(realCandidate);
247
+ } catch { /* stale or incomplete installation directory */ }
248
+ }
249
+ if (candidates.length === 0) throw new OperatorError('VSCODE_CLI_NOT_FOUND', 'VS Code native executable was found but its bounded CLI entrypoint was not.');
250
+ if (candidates.length > 1) throw new OperatorError('VSCODE_CLI_AMBIGUOUS', 'Multiple VS Code CLI entrypoints were found; complete or clean up the VS Code update first.');
251
+ return candidates[0] ;
252
+ }
253
+
254
+ async function runCode(executable , args , cwd , timeoutMs ) {
255
+ const baseEnvironment = safeEnvironment();
256
+ const codeExecutable = resolveVsCodeExecutable(executable, baseEnvironment);
257
+ const nativeCli = process.platform === 'win32' && path.win32.basename(codeExecutable).toLowerCase() === 'code.exe'
258
+ ? await resolveVsCodeCliScript(codeExecutable)
259
+ : undefined;
260
+ const environment = nativeCli ? { ...baseEnvironment, ELECTRON_RUN_AS_NODE: '1' } : baseEnvironment;
261
+ const invocationArgs = nativeCli ? [nativeCli, ...args] : args;
262
+ return await new Promise((resolve, reject) => {
263
+ const child = spawn(codeExecutable, invocationArgs, {
264
+ cwd,
265
+ shell: false,
266
+ windowsHide: true,
267
+ stdio: ['ignore', 'pipe', 'pipe'],
268
+ env: environment
269
+ });
270
+ const stdout = [];
271
+ const stderr = [];
272
+ let bytes = 0;
273
+ let truncated = false;
274
+ let timedOut = false;
275
+ const capture = (bucket ) => (chunk ) => {
276
+ if (bytes >= MAX_OUTPUT_BYTES) { truncated = true; return; }
277
+ const remaining = MAX_OUTPUT_BYTES - bytes;
278
+ const piece = chunk.subarray(0, remaining);
279
+ bucket.push(piece);
280
+ bytes += piece.byteLength;
281
+ if (piece.byteLength < chunk.byteLength) truncated = true;
282
+ };
283
+ child.stdout.on('data', capture(stdout));
284
+ child.stderr.on('data', capture(stderr));
285
+ child.once('error', reject);
286
+ const timer = setTimeout(() => {
287
+ timedOut = true;
288
+ child.kill('SIGTERM');
289
+ setTimeout(() => child.kill('SIGKILL'), 1000).unref();
290
+ }, timeoutMs);
291
+ timer.unref();
292
+ child.once('close', (code) => {
293
+ clearTimeout(timer);
294
+ if (timedOut) {
295
+ reject(new OperatorError('VSCODE_TIMEOUT', `VS Code CLI exceeded ${timeoutMs}ms timeout.`, { retryable: true }));
296
+ return;
297
+ }
298
+ resolve({ code: code ?? -1, stdout: Buffer.concat(stdout).toString('utf8'), stderr: Buffer.concat(stderr).toString('utf8'), truncated });
299
+ });
300
+ });
301
+ }
302
+
303
+ function ok(action , started , output , evidenceItems ) {
304
+ return {
305
+ ok: true,
306
+ capability: action.capability,
307
+ provider: 'vscode.cli.isolated',
308
+ output,
309
+ evidence: evidenceItems,
310
+ durationMs: Math.round(performance.now() - started)
311
+ };
312
+ }