@inaiii/pi-remote-helper 0.3.0 → 0.3.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/cli/lock.mjs CHANGED
@@ -2,28 +2,55 @@ import { constants } from 'node:fs';
2
2
  import { open } from 'node:fs/promises';
3
3
  import { spawn } from 'node:child_process';
4
4
 
5
+ const lockMessages = {
6
+ MAINTENANCE_LOCK_FILE: 'Maintenance lock file is unsafe or inaccessible. Check private installation permissions and storage.',
7
+ MAINTENANCE_LOCK_UNAVAILABLE: 'Maintenance lock utility is unavailable. macOS requires lockf; Linux requires flock.',
8
+ MAINTENANCE_LOCK_BUSY: 'Another maintenance operation is active. Retry after it finishes; do not delete the lock file.',
9
+ MAINTENANCE_LOCK_FAILED: 'Maintenance lock did not become ready. Check the local lock and Unix shell utilities.',
10
+ };
11
+
12
+ export class MaintenanceLockError extends Error {
13
+ constructor(code) { super(lockMessages[code]); this.code = code; }
14
+ }
15
+
5
16
  // Keep the inode: unlinking a released lock can let two callers lock different
6
17
  // files. Kernel ownership ends when the temporary holder loses its stdin pipe,
7
18
  // including when the maintenance process crashes.
8
19
  export async function acquireMaintenanceLock(path) {
9
- const file = await open(path, constants.O_CREAT | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
10
20
  try {
11
- const info = await file.stat();
12
- if (!info.isFile() || info.nlink !== 1 || info.uid !== process.getuid() || info.mode & 0o077) throw new Error('Invalid maintenance lock.');
13
- } finally { await file.close(); }
14
- const script = 'printf "ready\\n"; /bin/cat >/dev/null';
21
+ const file = await open(path, constants.O_CREAT | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
22
+ try {
23
+ const info = await file.stat();
24
+ if (!info.isFile() || info.nlink !== 1 || typeof process.getuid !== 'function' || info.uid !== process.getuid() || info.mode & 0o077) throw new MaintenanceLockError('MAINTENANCE_LOCK_FILE');
25
+ } finally { await file.close(); }
26
+ } catch { throw new MaintenanceLockError('MAINTENANCE_LOCK_FILE'); }
27
+ const script = '[ -x /bin/cat ] || exit 69; printf "ready\\n"; exec /bin/cat >/dev/null';
15
28
  const holder = process.platform === 'darwin'
16
29
  ? spawn('/usr/bin/lockf', ['-k', '-t', '0', path, '/bin/sh', '-c', script], { stdio: ['pipe', 'pipe', 'ignore'] })
17
- : spawn('flock', ['-n', path, '/bin/sh', '-c', script], { stdio: ['pipe', 'pipe', 'ignore'] });
30
+ : spawn('flock', ['-n', '-E', '75', path, '/bin/sh', '-c', script], { stdio: ['pipe', 'pipe', 'ignore'] });
18
31
  holder.stdin.on('error', () => {});
19
32
  const exited = new Promise(resolve => { holder.once('close', resolve); holder.once('error', resolve); });
20
33
  try {
21
34
  await new Promise((resolve, reject) => {
22
- const timer = setTimeout(() => reject(new Error('Maintenance lock did not become ready.')), 5000);
23
- const finish = (error) => { clearTimeout(timer); error ? reject(error) : resolve(); };
24
- holder.stdout.once('data', () => finish());
25
- holder.once('error', () => finish(new Error('Maintenance lock utility is unavailable.')));
26
- holder.once('close', () => finish(new Error('Another maintenance operation is active.')));
35
+ const timer = setTimeout(() => finish(new MaintenanceLockError('MAINTENANCE_LOCK_FAILED')), 5000);
36
+ const finish = (error) => {
37
+ clearTimeout(timer);
38
+ holder.stdout.off('data', ready); holder.off('error', failed); holder.off('close', closed);
39
+ error ? reject(error) : resolve();
40
+ };
41
+ let output = '';
42
+ const ready = chunk => {
43
+ output += chunk.toString();
44
+ if (output === 'ready\n') finish();
45
+ else if (!'ready\n'.startsWith(output)) finish(new MaintenanceLockError('MAINTENANCE_LOCK_FAILED'));
46
+ };
47
+ const failed = error => finish(new MaintenanceLockError(error.code === 'ENOENT' ? 'MAINTENANCE_LOCK_UNAVAILABLE' : 'MAINTENANCE_LOCK_FAILED'));
48
+ // lockf uses EX_TEMPFAIL for contention; flock's conflict exit is set to
49
+ // the same value. Other early exits do not prove another owner exists.
50
+ const closed = code => finish(new MaintenanceLockError(code === 75 ? 'MAINTENANCE_LOCK_BUSY' : 'MAINTENANCE_LOCK_FAILED'));
51
+ holder.stdout.on('data', ready);
52
+ holder.once('error', failed);
53
+ holder.once('close', closed);
27
54
  });
28
55
  } catch (error) {
29
56
  holder.stdin.end();
@@ -1,11 +1,11 @@
1
1
  {
2
- "version": "0.3.0",
2
+ "version": "0.3.1",
3
3
  "platform": "darwin",
4
4
  "arch": "arm64",
5
- "gitCommit": "3b297d59d2392fa4f87a446ca217bb2ef6ec6a4e",
6
- "builtAt": "2026-10-04T13:35:02.000Z",
5
+ "gitCommit": "100e480b2e58ee1e68fed4c1d34bc6db5b9f471f",
6
+ "builtAt": "2026-10-06T06:10:23.000Z",
7
7
  "sha256": {
8
- "pi-remote-helper": "70f7190e16d959b2608b4d4db00e5d386e43ce50a087190398dcadab18083fd2",
9
- "pi-remote-launch-wrapper": "234b635ecc752a5020506fdc91c2bb10adb188e3318200848f7a19ec1adc9c83"
8
+ "pi-remote-helper": "6724620b1b6055770448171ea2db67a9e841bd3b86dc5c17fa4f9f0f4a967860",
9
+ "pi-remote-launch-wrapper": "372b62c9c7f9e70b3d8575b13dc5b959187761486ce81acae8cacd0df6f35f8b"
10
10
  }
11
11
  }
@@ -1,11 +1,11 @@
1
1
  {
2
- "version": "0.3.0",
2
+ "version": "0.3.1",
3
3
  "platform": "darwin",
4
4
  "arch": "x64",
5
- "gitCommit": "3b297d59d2392fa4f87a446ca217bb2ef6ec6a4e",
6
- "builtAt": "2026-10-04T13:35:02.000Z",
5
+ "gitCommit": "100e480b2e58ee1e68fed4c1d34bc6db5b9f471f",
6
+ "builtAt": "2026-10-06T06:10:23.000Z",
7
7
  "sha256": {
8
- "pi-remote-helper": "2e828d75410c5dad3f1dba4aef15137d13ab40281cb730826065ee65f14046a5",
9
- "pi-remote-launch-wrapper": "977e56b0ed52fecfb5a28e534bdbdc69dca692ce7781dd18b8808324d8f25202"
8
+ "pi-remote-helper": "236d7bf79b775879ec873f2f24b6e8ba4201da31d0e2699602c8302f257e336e",
9
+ "pi-remote-launch-wrapper": "b3203f811753ab32ea92cad68fb4fed1cf3265355099bbcacff215d964524729"
10
10
  }
11
11
  }
Binary file
package/package.json CHANGED
@@ -31,6 +31,6 @@
31
31
  "access": "public",
32
32
  "registry": "https://registry.npmjs.org/"
33
33
  },
34
- "version": "0.3.0",
35
- "gitHead": "3b297d59d2392fa4f87a446ca217bb2ef6ec6a4e"
34
+ "version": "0.3.1",
35
+ "gitHead": "100e480b2e58ee1e68fed4c1d34bc6db5b9f471f"
36
36
  }