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.
- package/DISCLOSURE +47 -0
- package/LICENSE +64 -0
- package/README.md +44 -0
- package/bin/operator.mjs +10 -0
- package/package.json +58 -0
- package/runtime/app/apps/local-agent/src/approval-store.js +342 -0
- package/runtime/app/apps/local-agent/src/bootstrap-config.js +255 -0
- package/runtime/app/apps/local-agent/src/cli.js +237 -0
- package/runtime/app/apps/local-agent/src/device-reset.js +260 -0
- package/runtime/app/apps/local-agent/src/emergency-stop.js +141 -0
- package/runtime/app/apps/local-agent/src/main.js +311 -0
- package/runtime/app/apps/local-agent/src/privacy-data.js +129 -0
- package/runtime/app/apps/local-agent/src/relay-agent.js +333 -0
- package/runtime/app/apps/local-agent/src/relay-enrollment.js +156 -0
- package/runtime/app/apps/local-agent/src/relay-session-credentials.js +436 -0
- package/runtime/app/apps/local-agent/src/remote.js +130 -0
- package/runtime/app/apps/local-agent/src/runtime-factory.js +66 -0
- package/runtime/app/apps/local-agent/src/server.js +578 -0
- package/runtime/app/package.json +4 -0
- package/runtime/app/src/capabilities/browser-cdp-connection.js +199 -0
- package/runtime/app/src/capabilities/browser-cdp-frames.js +288 -0
- package/runtime/app/src/capabilities/browser-cdp-page.js +430 -0
- package/runtime/app/src/capabilities/browser-cdp.js +384 -0
- package/runtime/app/src/capabilities/browser-managed.js +345 -0
- package/runtime/app/src/capabilities/docker.js +448 -0
- package/runtime/app/src/capabilities/filesystem.js +264 -0
- package/runtime/app/src/capabilities/git-checkpoint.js +415 -0
- package/runtime/app/src/capabilities/git-write.js +321 -0
- package/runtime/app/src/capabilities/git.js +252 -0
- package/runtime/app/src/capabilities/index.js +16 -0
- package/runtime/app/src/capabilities/path-scope.js +86 -0
- package/runtime/app/src/capabilities/postgres.js +566 -0
- package/runtime/app/src/capabilities/process.js +258 -0
- package/runtime/app/src/capabilities/project-command.js +599 -0
- package/runtime/app/src/capabilities/project-transaction.js +215 -0
- package/runtime/app/src/capabilities/project.js +67 -0
- package/runtime/app/src/capabilities/system.js +40 -0
- package/runtime/app/src/capabilities/vscode.js +312 -0
- package/runtime/app/src/capabilities/windows-uia.js +331 -0
- package/runtime/app/src/core/account-device-registry.js +604 -0
- package/runtime/app/src/core/action-identity.js +44 -0
- package/runtime/app/src/core/audit.js +295 -0
- package/runtime/app/src/core/capability-policy.js +58 -0
- package/runtime/app/src/core/child-environment.js +29 -0
- package/runtime/app/src/core/device-enrollment.js +417 -0
- package/runtime/app/src/core/device-identity.js +436 -0
- package/runtime/app/src/core/device-registry.js +477 -0
- package/runtime/app/src/core/device-reset.js +178 -0
- package/runtime/app/src/core/device-routing.js +305 -0
- package/runtime/app/src/core/durable-state.js +306 -0
- package/runtime/app/src/core/errors.js +20 -0
- package/runtime/app/src/core/evidence.js +9 -0
- package/runtime/app/src/core/index.js +17 -0
- package/runtime/app/src/core/machine-state.js +160 -0
- package/runtime/app/src/core/network-authority.js +20 -0
- package/runtime/app/src/core/policy.js +67 -0
- package/runtime/app/src/core/product-identity.js +7 -0
- package/runtime/app/src/core/provenance.js +18 -0
- package/runtime/app/src/core/provider-learning.js +274 -0
- package/runtime/app/src/core/public-plugin-surface.js +25 -0
- package/runtime/app/src/core/public-restricted-data.js +105 -0
- package/runtime/app/src/core/rate-limit.js +118 -0
- package/runtime/app/src/core/relay-client.js +568 -0
- package/runtime/app/src/core/relay-delivery-store.js +512 -0
- package/runtime/app/src/core/relay-result-store.js +321 -0
- package/runtime/app/src/core/release-update.js +253 -0
- package/runtime/app/src/core/router.js +96 -0
- package/runtime/app/src/core/runtime.js +136 -0
- package/runtime/app/src/core/scoped-path-syntax.js +16 -0
- package/runtime/app/src/core/session-token.js +498 -0
- package/runtime/app/src/core/task-orchestrator.js +1148 -0
- package/runtime/app/src/core/task-store.js +517 -0
- package/runtime/app/src/core/task.js +142 -0
- package/runtime/app/src/core/trusted-executable.js +124 -0
- package/runtime/app/src/core/types.js +83 -0
- package/runtime/app/src/core/windows-path-lease.js +138 -0
- package/runtime/native/operator-windows-dpapi.exe +0 -0
- package/runtime/native/operator-windows-path-lease.exe +0 -0
- package/runtime/native/operator-windows-uia.exe +0 -0
- package/runtime/runtime-manifest.json +380 -0
- package/src/cli.mjs +345 -0
- package/src/publish-check.mjs +22 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { OperatorError } from './errors.js';
|
|
5
|
+
|
|
6
|
+
const WINDOWS_NATIVE_EXTENSIONS = ['.exe', '.com'] ;
|
|
7
|
+
const MIN_GIT_MAJOR = 2;
|
|
8
|
+
const MIN_GIT_MINOR = 45;
|
|
9
|
+
const GIT_VERSION_CACHE = new Map ();
|
|
10
|
+
|
|
11
|
+
export function assertSupportedGitVersionText(output ) {
|
|
12
|
+
const match = /^git version (\d+)\.(\d+)(?:\.(\d+))?(?:[.\-][^\s]+)?$/i.exec(String(output ?? '').trim());
|
|
13
|
+
if (!match) {
|
|
14
|
+
throw new OperatorError('GIT_VERSION_CHECK_FAILED', 'Operator could not parse the trusted Git executable version.');
|
|
15
|
+
}
|
|
16
|
+
const major = Number(match[1]);
|
|
17
|
+
const minor = Number(match[2]);
|
|
18
|
+
const patch = Number(match[3] ?? 0);
|
|
19
|
+
if (major < MIN_GIT_MAJOR || (major === MIN_GIT_MAJOR && minor < MIN_GIT_MINOR)) {
|
|
20
|
+
throw new OperatorError(
|
|
21
|
+
'GIT_VERSION_UNSUPPORTED',
|
|
22
|
+
`Operator Git capabilities require Git ${MIN_GIT_MAJOR}.${MIN_GIT_MINOR} or newer so lazy promisor fetching can be disabled fail-closed.`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return { major, minor, patch };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resolveSupportedGitExecutable(source = process.env) {
|
|
29
|
+
const executable = resolveTrustedExecutable('git', source);
|
|
30
|
+
const stat = fs.statSync(executable);
|
|
31
|
+
const cacheKey = process.platform === 'win32' ? executable.toLowerCase() : executable;
|
|
32
|
+
const cached = GIT_VERSION_CACHE.get(cacheKey);
|
|
33
|
+
if (cached && cached.size === stat.size && cached.mtimeMs === stat.mtimeMs) return executable;
|
|
34
|
+
|
|
35
|
+
const result = spawnSync(executable, ['--version'], {
|
|
36
|
+
shell: false,
|
|
37
|
+
windowsHide: true,
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
timeout: 5_000,
|
|
40
|
+
maxBuffer: 32 * 1024,
|
|
41
|
+
env: gitVersionProbeEnvironment(source)
|
|
42
|
+
});
|
|
43
|
+
if (result.error || result.status !== 0) {
|
|
44
|
+
throw new OperatorError('GIT_VERSION_CHECK_FAILED', 'Operator could not verify the trusted Git executable version.');
|
|
45
|
+
}
|
|
46
|
+
assertSupportedGitVersionText(String(result.stdout ?? ''));
|
|
47
|
+
GIT_VERSION_CACHE.set(cacheKey, { size: stat.size, mtimeMs: stat.mtimeMs });
|
|
48
|
+
return executable;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resolveTrustedExecutable(
|
|
52
|
+
input ,
|
|
53
|
+
source = process.env
|
|
54
|
+
) {
|
|
55
|
+
const executable = String(input ?? '').trim();
|
|
56
|
+
if (!executable || executable.includes('\0')) {
|
|
57
|
+
throw new OperatorError('EXECUTABLE_INVALID', 'Executable must be a non-empty path or command name without NUL bytes.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (path.isAbsolute(executable)) return validateNativeExecutable(executable);
|
|
61
|
+
if (/[\\/]/.test(executable)) {
|
|
62
|
+
throw new OperatorError('EXECUTABLE_PATH_UNTRUSTED', 'Relative executable paths are not allowed.');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const pathValue = source.PATH ?? source.Path ?? '';
|
|
66
|
+
for (const rawDirectory of String(pathValue).split(path.delimiter)) {
|
|
67
|
+
const directory = stripOuterQuotes(rawDirectory.trim());
|
|
68
|
+
if (!directory || !path.isAbsolute(directory)) continue;
|
|
69
|
+
for (const name of candidateNames(executable, source)) {
|
|
70
|
+
const candidate = path.resolve(directory, name);
|
|
71
|
+
const resolved = tryNativeExecutable(candidate);
|
|
72
|
+
if (resolved) return resolved;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new OperatorError('EXECUTABLE_NOT_FOUND', `Trusted executable ${executable} was not found in absolute PATH directories.`);
|
|
77
|
+
}
|
|
78
|
+
function candidateNames(executable , source ) {
|
|
79
|
+
if (process.platform !== 'win32') return [executable];
|
|
80
|
+
const extension = path.win32.extname(executable).toLowerCase();
|
|
81
|
+
if (extension) {
|
|
82
|
+
if (!WINDOWS_NATIVE_EXTENSIONS.includes(extension )) {
|
|
83
|
+
throw new OperatorError('EXECUTABLE_SCRIPT_DENIED', 'Windows command scripts are not valid shell-free executable authority.');
|
|
84
|
+
}
|
|
85
|
+
return [executable];
|
|
86
|
+
}
|
|
87
|
+
void source;
|
|
88
|
+
return WINDOWS_NATIVE_EXTENSIONS.map((item) => `${executable}${item}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function validateNativeExecutable(candidate ) {
|
|
92
|
+
const resolved = tryNativeExecutable(candidate);
|
|
93
|
+
if (!resolved) throw new OperatorError('EXECUTABLE_NOT_FOUND', `Trusted executable does not exist or is not executable: ${candidate}`);
|
|
94
|
+
return resolved;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function tryNativeExecutable(candidate ) {
|
|
98
|
+
if (process.platform === 'win32') {
|
|
99
|
+
const extension = path.win32.extname(candidate).toLowerCase();
|
|
100
|
+
if (!WINDOWS_NATIVE_EXTENSIONS.includes(extension )) return undefined;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const resolved = fs.realpathSync.native(candidate);
|
|
104
|
+
if (!path.isAbsolute(resolved) || !fs.statSync(resolved).isFile()) return undefined;
|
|
105
|
+
if (process.platform !== 'win32') fs.accessSync(resolved, fs.constants.X_OK);
|
|
106
|
+
return resolved;
|
|
107
|
+
} catch {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function stripOuterQuotes(value ) {
|
|
113
|
+
return value.length >= 2 && value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function gitVersionProbeEnvironment(source ) {
|
|
117
|
+
const env = { LANG: 'C', LC_ALL: 'C' };
|
|
118
|
+
if (process.platform === 'win32') {
|
|
119
|
+
for (const key of ['SYSTEMROOT', 'WINDIR']) {
|
|
120
|
+
if (source[key] !== undefined) env[key] = source[key];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return env;
|
|
124
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { spawn, } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { OperatorError } from './errors.js';
|
|
4
|
+
import { safeChildEnvironment } from './child-environment.js';
|
|
5
|
+
|
|
6
|
+
const READY_TIMEOUT_MS = 5_000;
|
|
7
|
+
const RELEASE_TIMEOUT_MS = 5_000;
|
|
8
|
+
const MAX_HELPER_TEXT_BYTES = 4 * 1024;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export async function withWindowsPathLease (
|
|
20
|
+
options ,
|
|
21
|
+
operation
|
|
22
|
+
) {
|
|
23
|
+
if (process.platform !== 'win32') return await operation();
|
|
24
|
+
const executable = options.executable ?? process.env.OPERATOR_WINDOWS_PATH_LEASE_PATH;
|
|
25
|
+
if (!executable || !path.isAbsolute(executable)) {
|
|
26
|
+
throw new OperatorError(
|
|
27
|
+
'WINDOWS_PATH_LEASE_HELPER_REQUIRED',
|
|
28
|
+
'Windows filesystem operations require the packaged path-lease helper.'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const child = spawn(executable, ['lease', options.mode, options.root, options.target], {
|
|
32
|
+
shell: false,
|
|
33
|
+
windowsHide: true,
|
|
34
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
35
|
+
env: safeChildEnvironment('windows-native')
|
|
36
|
+
});
|
|
37
|
+
await waitForReady(child);
|
|
38
|
+
|
|
39
|
+
let value ;
|
|
40
|
+
let operationError ;
|
|
41
|
+
try {
|
|
42
|
+
value = await operation();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
operationError = error;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let releaseError ;
|
|
48
|
+
try {
|
|
49
|
+
await releaseLease(child);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
releaseError = error;
|
|
52
|
+
}
|
|
53
|
+
if (operationError) throw operationError;
|
|
54
|
+
if (releaseError) throw releaseError;
|
|
55
|
+
return value ;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function waitForReady(child ) {
|
|
59
|
+
await new Promise ((resolve, reject) => {
|
|
60
|
+
let stdout = '';
|
|
61
|
+
let stderr = '';
|
|
62
|
+
let settled = false;
|
|
63
|
+
const finish = (error ) => {
|
|
64
|
+
if (settled) return;
|
|
65
|
+
settled = true;
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
if (error) reject(error); else resolve();
|
|
68
|
+
};
|
|
69
|
+
child.once('error', () => finish(new OperatorError(
|
|
70
|
+
'WINDOWS_PATH_LEASE_HELPER_FAILED',
|
|
71
|
+
'Windows path-lease helper could not be started.'
|
|
72
|
+
)));
|
|
73
|
+
child.stderr.setEncoding('utf8');
|
|
74
|
+
child.stderr.on('data', (chunk ) => {
|
|
75
|
+
if (Buffer.byteLength(stderr, 'utf8') >= MAX_HELPER_TEXT_BYTES) return;
|
|
76
|
+
stderr = (stderr + chunk).slice(0, MAX_HELPER_TEXT_BYTES);
|
|
77
|
+
});
|
|
78
|
+
child.stdout.setEncoding('utf8');
|
|
79
|
+
child.stdout.on('data', (chunk ) => {
|
|
80
|
+
if (settled) return;
|
|
81
|
+
stdout += chunk;
|
|
82
|
+
if (Buffer.byteLength(stdout, 'utf8') > MAX_HELPER_TEXT_BYTES) {
|
|
83
|
+
finish(new OperatorError('WINDOWS_PATH_LEASE_HELPER_FAILED', 'Windows path-lease helper returned invalid output.'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const newline = stdout.indexOf('\n');
|
|
87
|
+
if (newline < 0) return;
|
|
88
|
+
if (stdout.slice(0, newline).trim() !== 'READY') {
|
|
89
|
+
finish(new OperatorError('WINDOWS_PATH_LEASE_DENIED', 'Windows path authority validation failed.'));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
finish();
|
|
93
|
+
});
|
|
94
|
+
child.once('close', (code) => {
|
|
95
|
+
if (!settled) finish(new OperatorError(
|
|
96
|
+
'WINDOWS_PATH_LEASE_DENIED',
|
|
97
|
+
code === 0
|
|
98
|
+
? 'Windows path-lease helper exited before establishing authority.'
|
|
99
|
+
: 'Windows path authority validation failed.'
|
|
100
|
+
));
|
|
101
|
+
});
|
|
102
|
+
const timer = setTimeout(() => {
|
|
103
|
+
child.kill('SIGTERM');
|
|
104
|
+
finish(new OperatorError('WINDOWS_PATH_LEASE_TIMEOUT', 'Windows path authority validation timed out.'));
|
|
105
|
+
}, READY_TIMEOUT_MS);
|
|
106
|
+
timer.unref();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function releaseLease(child ) {
|
|
111
|
+
if (child.exitCode !== null) {
|
|
112
|
+
if (child.exitCode === 0) return;
|
|
113
|
+
throw new OperatorError('WINDOWS_PATH_LEASE_HELPER_FAILED', 'Windows path-lease helper exited unexpectedly.');
|
|
114
|
+
}
|
|
115
|
+
child.stdin.end();
|
|
116
|
+
await new Promise ((resolve, reject) => {
|
|
117
|
+
let settled = false;
|
|
118
|
+
const finish = (error ) => {
|
|
119
|
+
if (settled) return;
|
|
120
|
+
settled = true;
|
|
121
|
+
clearTimeout(timer);
|
|
122
|
+
if (error) reject(error); else resolve();
|
|
123
|
+
};
|
|
124
|
+
child.once('error', () => finish(new OperatorError(
|
|
125
|
+
'WINDOWS_PATH_LEASE_HELPER_FAILED',
|
|
126
|
+
'Windows path-lease helper failed while releasing authority.'
|
|
127
|
+
)));
|
|
128
|
+
child.once('close', (code) => {
|
|
129
|
+
if (code === 0) finish();
|
|
130
|
+
else finish(new OperatorError('WINDOWS_PATH_LEASE_HELPER_FAILED', 'Windows path-lease helper failed while releasing authority.'));
|
|
131
|
+
});
|
|
132
|
+
const timer = setTimeout(() => {
|
|
133
|
+
child.kill('SIGTERM');
|
|
134
|
+
finish(new OperatorError('WINDOWS_PATH_LEASE_TIMEOUT', 'Windows path-lease helper did not release authority in time.'));
|
|
135
|
+
}, RELEASE_TIMEOUT_MS);
|
|
136
|
+
timer.unref();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|