@jian-ai/remote-control 0.8.0 → 0.8.1
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/README.md +3 -1
- package/bin/computer-macos.mjs +85 -19
- package/bin/jian-remote.mjs +1 -1
- package/macos/build-helper.sh +46 -0
- package/macos/computer-helper +0 -0
- package/macos/computer-helper.swift +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,4 +43,6 @@ jian-remote configure --gateway https://gateway.example --token WORKER_TOKEN
|
|
|
43
43
|
|
|
44
44
|
Worker and gateway must share the same major.minor. See [docs/remote.md](../../docs/remote.md).
|
|
45
45
|
|
|
46
|
-
Requires Node.js 24+.
|
|
46
|
+
Requires Node.js 24+. Published builds include a prebuilt macOS `computer-helper` for Computer
|
|
47
|
+
Use (no Xcode on the worker Mac). From source, run `macos/build-helper.sh` on a Mac, or let the
|
|
48
|
+
worker compile with `swiftc` when Command Line Tools are installed.
|
package/bin/computer-macos.mjs
CHANGED
|
@@ -75,22 +75,78 @@ export function capturePrimaryScreenshotBase64() {
|
|
|
75
75
|
return { base64, widthPx, heightPx };
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
function helperPaths() {
|
|
79
|
-
const support = join(
|
|
78
|
+
function helperPaths(home = homedir()) {
|
|
79
|
+
const support = join(home, 'Library', 'Application Support', 'Jian Remote');
|
|
80
80
|
return {
|
|
81
81
|
support,
|
|
82
82
|
source: join(support, `${HELPER_NAME}.swift`),
|
|
83
83
|
binary: join(support, HELPER_NAME),
|
|
84
|
+
stamp: join(support, `${HELPER_NAME}.sha256`),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Paths inside the installed npm package (or a checkout). */
|
|
89
|
+
export function packageHelperPaths(
|
|
90
|
+
packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..'),
|
|
91
|
+
) {
|
|
92
|
+
return {
|
|
93
|
+
swift: join(packageRoot, 'macos', `${HELPER_NAME}.swift`),
|
|
94
|
+
binary: join(packageRoot, 'macos', HELPER_NAME),
|
|
84
95
|
};
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
function packageSwiftPath() {
|
|
88
|
-
return
|
|
99
|
+
return packageHelperPaths().swift;
|
|
89
100
|
}
|
|
90
101
|
|
|
102
|
+
function packageBinaryPath() {
|
|
103
|
+
return packageHelperPaths().binary;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function stampForBytes(bytes) {
|
|
107
|
+
return createHash('sha256').update(bytes).digest('hex').slice(0, 16);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Drop download quarantine so a helper from npm can exec under the LaunchAgent. */
|
|
111
|
+
function clearQuarantine(path) {
|
|
112
|
+
spawnSync('xattr', ['-d', 'com.apple.quarantine', path], { encoding: 'utf8' });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Prefer a Publish-built universal binary from the npm package; otherwise compile with swiftc.
|
|
117
|
+
* Stamp is over the installed artifact bytes so updates replace a stale helper.
|
|
118
|
+
*/
|
|
91
119
|
async function ensureHelperBinary() {
|
|
92
120
|
const paths = helperPaths();
|
|
93
121
|
await mkdir(paths.support, { recursive: true, mode: 0o755 });
|
|
122
|
+
|
|
123
|
+
let previous = '';
|
|
124
|
+
try {
|
|
125
|
+
previous = (await readFile(paths.stamp, 'utf8')).trim();
|
|
126
|
+
} catch {
|
|
127
|
+
previous = '';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const packagedBinary = packageBinaryPath();
|
|
131
|
+
if (existsSync(packagedBinary)) {
|
|
132
|
+
const bytes = await readFile(packagedBinary);
|
|
133
|
+
const hash = `bin:${stampForBytes(bytes)}`;
|
|
134
|
+
if (previous !== hash || !existsSync(paths.binary)) {
|
|
135
|
+
await copyFile(packagedBinary, paths.binary);
|
|
136
|
+
await chmod(paths.binary, 0o755);
|
|
137
|
+
clearQuarantine(paths.binary);
|
|
138
|
+
await writeFile(paths.stamp, `${hash}\n`, { mode: 0o644 });
|
|
139
|
+
}
|
|
140
|
+
// Keep source nearby for debugging / swiftc fallback after a partial install.
|
|
141
|
+
try {
|
|
142
|
+
const swift = packageSwiftPath();
|
|
143
|
+
if (existsSync(swift)) await copyFile(swift, paths.source);
|
|
144
|
+
} catch {
|
|
145
|
+
// optional
|
|
146
|
+
}
|
|
147
|
+
return paths.binary;
|
|
148
|
+
}
|
|
149
|
+
|
|
94
150
|
let sourceText;
|
|
95
151
|
try {
|
|
96
152
|
sourceText = await readFile(packageSwiftPath(), 'utf8');
|
|
@@ -99,18 +155,11 @@ async function ensureHelperBinary() {
|
|
|
99
155
|
sourceText = await readFile(paths.source, 'utf8');
|
|
100
156
|
} catch {
|
|
101
157
|
throw new Error(
|
|
102
|
-
'Computer-use helper
|
|
158
|
+
'Computer-use helper is missing from this install. Reinstall or update @jian-ai/remote-control.',
|
|
103
159
|
);
|
|
104
160
|
}
|
|
105
161
|
}
|
|
106
|
-
const hash =
|
|
107
|
-
const stamp = join(paths.support, `${HELPER_NAME}.sha256`);
|
|
108
|
-
let previous = '';
|
|
109
|
-
try {
|
|
110
|
-
previous = (await readFile(stamp, 'utf8')).trim();
|
|
111
|
-
} catch {
|
|
112
|
-
previous = '';
|
|
113
|
-
}
|
|
162
|
+
const hash = `src:${stampForBytes(Buffer.from(sourceText, 'utf8'))}`;
|
|
114
163
|
const needsBuild = previous !== hash || !existsSync(paths.binary);
|
|
115
164
|
await writeFile(paths.source, sourceText, { mode: 0o644 });
|
|
116
165
|
if (needsBuild) {
|
|
@@ -122,27 +171,44 @@ async function ensureHelperBinary() {
|
|
|
122
171
|
if (compiled.status !== 0) {
|
|
123
172
|
throw new Error(
|
|
124
173
|
(compiled.stderr || compiled.stdout || 'swiftc failed').trim() ||
|
|
125
|
-
'Could not build the Jian computer-use helper.
|
|
174
|
+
'Could not build the Jian computer-use helper. Published packages ship a prebuilt helper; for source builds install Xcode Command Line Tools, or reinstall @jian-ai/remote-control.',
|
|
126
175
|
);
|
|
127
176
|
}
|
|
128
177
|
await chmod(paths.binary, 0o755);
|
|
129
|
-
|
|
178
|
+
clearQuarantine(paths.binary);
|
|
179
|
+
await writeFile(paths.stamp, `${hash}\n`, { mode: 0o644 });
|
|
130
180
|
}
|
|
131
181
|
return paths.binary;
|
|
132
182
|
}
|
|
133
183
|
|
|
134
|
-
/** Copy Swift
|
|
184
|
+
/** Copy Swift source and any prebuilt helper next to the installed worker. */
|
|
135
185
|
export async function materializeComputerHelperSources(configDir) {
|
|
136
186
|
if (process.platform !== 'darwin') return;
|
|
137
187
|
try {
|
|
138
|
-
const
|
|
139
|
-
if (!existsSync(
|
|
188
|
+
const pkg = packageHelperPaths();
|
|
189
|
+
if (!existsSync(pkg.swift) && !existsSync(pkg.binary)) return;
|
|
140
190
|
const support = join(homedir(), 'Library', 'Application Support', 'Jian Remote');
|
|
141
191
|
await mkdir(support, { recursive: true, mode: 0o755 });
|
|
142
|
-
await copyFile(from, join(support, `${HELPER_NAME}.swift`));
|
|
143
192
|
const localMacos = join(configDir, 'macos');
|
|
144
193
|
await mkdir(localMacos, { recursive: true, mode: 0o755 });
|
|
145
|
-
|
|
194
|
+
if (existsSync(pkg.swift)) {
|
|
195
|
+
await copyFile(pkg.swift, join(support, `${HELPER_NAME}.swift`));
|
|
196
|
+
await copyFile(pkg.swift, join(localMacos, `${HELPER_NAME}.swift`));
|
|
197
|
+
}
|
|
198
|
+
if (existsSync(pkg.binary)) {
|
|
199
|
+
const destSupport = join(support, HELPER_NAME);
|
|
200
|
+
const destLocal = join(localMacos, HELPER_NAME);
|
|
201
|
+
await copyFile(pkg.binary, destSupport);
|
|
202
|
+
await copyFile(pkg.binary, destLocal);
|
|
203
|
+
await chmod(destSupport, 0o755);
|
|
204
|
+
await chmod(destLocal, 0o755);
|
|
205
|
+
clearQuarantine(destSupport);
|
|
206
|
+
clearQuarantine(destLocal);
|
|
207
|
+
const bytes = await readFile(pkg.binary);
|
|
208
|
+
await writeFile(join(support, `${HELPER_NAME}.sha256`), `bin:${stampForBytes(bytes)}\n`, {
|
|
209
|
+
mode: 0o644,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
146
212
|
} catch {
|
|
147
213
|
// Best-effort on install; computer session will surface a clear error.
|
|
148
214
|
}
|
package/bin/jian-remote.mjs
CHANGED
|
@@ -21,7 +21,7 @@ const option = (name) => {
|
|
|
21
21
|
* Stamped to the release tag on npm publish and when the gateway serves this script.
|
|
22
22
|
* Compatibility with the gateway requires the same major.minor (see versionsCompatible).
|
|
23
23
|
*/
|
|
24
|
-
export const PACKAGE_VERSION = '0.8.
|
|
24
|
+
export const PACKAGE_VERSION = '0.8.1';
|
|
25
25
|
|
|
26
26
|
/** Soft stdout/stderr caps while capturing a command (chars). Gateway schema matches these. */
|
|
27
27
|
const STDOUT_CAP = 400_000;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Build a universal (arm64 + x86_64) computer-helper for the npm package.
|
|
3
|
+
# Run on macOS with Xcode CLT. Used by Publish CI and local packaging.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
SRC="$ROOT/computer-helper.swift"
|
|
8
|
+
OUT="${1:-$ROOT/computer-helper}"
|
|
9
|
+
MIN_OS="${MACOSX_DEPLOYMENT_TARGET:-13.0}"
|
|
10
|
+
TMP="$(mktemp -d "${TMPDIR:-/tmp}/jian-computer-helper.XXXXXX")"
|
|
11
|
+
cleanup() { rm -rf "$TMP"; }
|
|
12
|
+
trap cleanup EXIT
|
|
13
|
+
|
|
14
|
+
if [[ "$(uname -s)" != "Darwin" ]]; then
|
|
15
|
+
echo "build-helper.sh must run on macOS (swiftc + lipo)." >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
if ! command -v swiftc >/dev/null 2>&1; then
|
|
19
|
+
echo "swiftc not found. Install Xcode Command Line Tools." >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
if [[ ! -f "$SRC" ]]; then
|
|
23
|
+
echo "Missing source: $SRC" >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
swiftc -O \
|
|
28
|
+
-target "arm64-apple-macosx${MIN_OS}" \
|
|
29
|
+
-framework AppKit -framework Carbon \
|
|
30
|
+
-o "$TMP/computer-helper-arm64" "$SRC"
|
|
31
|
+
|
|
32
|
+
swiftc -O \
|
|
33
|
+
-target "x86_64-apple-macosx${MIN_OS}" \
|
|
34
|
+
-framework AppKit -framework Carbon \
|
|
35
|
+
-o "$TMP/computer-helper-x86_64" "$SRC"
|
|
36
|
+
|
|
37
|
+
lipo -create -output "$OUT" "$TMP/computer-helper-arm64" "$TMP/computer-helper-x86_64"
|
|
38
|
+
chmod 755 "$OUT"
|
|
39
|
+
|
|
40
|
+
# Ad-hoc sign so Gatekeeper is quieter for LaunchAgent children; Developer ID overlays this in CI when secrets exist.
|
|
41
|
+
if command -v codesign >/dev/null 2>&1; then
|
|
42
|
+
codesign --force -s - --identifier com.jian.remote.computer-helper "$OUT" 2>/dev/null || true
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "Built universal helper: $OUT"
|
|
46
|
+
lipo -info "$OUT"
|
|
Binary file
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
Protocol: newline-delimited JSON on stdin → newline-delimited JSON on stdout (flush each).
|
|
6
6
|
Errors go to stderr in English only.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Published npm packages ship a prebuilt universal binary (Publish CI). From source on a Mac:
|
|
9
|
+
./build-helper.sh
|
|
10
|
+
# or: swiftc -O -framework AppKit -framework Carbon -o computer-helper computer-helper.swift
|
|
10
11
|
|
|
11
12
|
Run as a child of the Node worker; keep stdin/stdout pipes open for the session.
|
|
12
13
|
*/
|