@cortexkit/opencode-antigravity-auth 3.0.3 → 3.0.5

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 (99) hide show
  1. package/README.md +7 -7
  2. package/dist/cli.js +1722 -484
  3. package/dist/cli.js.map +4 -4
  4. package/dist/cli.meta.json +440 -120
  5. package/dist/index.js +2157 -803
  6. package/dist/index.js.map +4 -4
  7. package/dist/index.meta.json +476 -141
  8. package/dist/server.js +2248 -841
  9. package/dist/server.js.map +4 -4
  10. package/dist/server.meta.json +1 -1
  11. package/dist/src/common-auth-embedded/claustrum/enrollment.d.ts +3 -2
  12. package/dist/src/common-auth-embedded/claustrum/enrollment.js +84 -42
  13. package/dist/src/common-auth-embedded/claustrum/roster.js +2 -2
  14. package/dist/src/common-auth-embedded/fs/atomic-write.d.ts +5 -0
  15. package/dist/src/common-auth-embedded/fs/atomic-write.js +21 -0
  16. package/dist/src/common-auth-embedded/fs/index.d.ts +2 -0
  17. package/dist/src/common-auth-embedded/fs/index.js +1 -0
  18. package/dist/src/common-auth-embedded/fs/private-files.d.ts +66 -0
  19. package/dist/src/common-auth-embedded/fs/private-files.js +302 -0
  20. package/dist/src/common-auth-embedded/fs/refresh-file-lock.d.ts +1 -1
  21. package/dist/src/common-auth-embedded/fs/refresh-file-lock.js +254 -37
  22. package/dist/src/common-auth-embedded/fs/with-lock.d.ts +3 -0
  23. package/dist/src/common-auth-embedded/fs/with-lock.js +4 -0
  24. package/dist/src/common-auth-embedded/logger/engine.d.ts +6 -8
  25. package/dist/src/common-auth-embedded/logger/engine.js +126 -11
  26. package/dist/src/common-auth-embedded/pi-slot/slot.d.ts +2 -0
  27. package/dist/src/common-auth-embedded/pi-slot/slot.js +34 -6
  28. package/dist/src/common-auth-embedded/rpc/port-file.js +30 -8
  29. package/dist/src/common-auth-embedded/rpc/rpc-client.js +15 -1
  30. package/dist/src/common-auth-embedded/rpc/rpc-server.js +8 -3
  31. package/dist/src/common-auth-embedded/source-output.json +92 -76
  32. package/dist/src/common-auth-embedded/store/mutate.js +2 -2
  33. package/dist/src/common-auth-embedded/store/pool.d.ts +1 -0
  34. package/dist/src/common-auth-embedded/store/pool.js +3 -0
  35. package/dist/src/common-auth-embedded/store/refresh-lock.d.ts +4 -0
  36. package/dist/src/common-auth-embedded/store/refresh-lock.js +5 -0
  37. package/dist/src/ga/rpc/protocol.d.ts +2 -3
  38. package/dist/src/ga/rpc/protocol.d.ts.map +1 -1
  39. package/dist/src/ga/rpc/protocol.js +60 -0
  40. package/dist/src/ga/rpc/protocol.js.map +1 -1
  41. package/dist/src/ga/server/index.d.ts +3 -2
  42. package/dist/src/ga/server/index.d.ts.map +1 -1
  43. package/dist/src/ga/server/index.js +23 -7
  44. package/dist/src/ga/server/index.js.map +1 -1
  45. package/dist/src/plugin/request.d.ts.map +1 -1
  46. package/dist/src/plugin/request.js +8 -14
  47. package/dist/src/plugin/request.js.map +1 -1
  48. package/dist/src/plugin/vault-custody.d.ts +6 -0
  49. package/dist/src/plugin/vault-custody.d.ts.map +1 -1
  50. package/dist/src/plugin/vault-custody.js +81 -2
  51. package/dist/src/plugin/vault-custody.js.map +1 -1
  52. package/dist/tui-build-map.json +29 -13
  53. package/dist/tui-ga-build-map.json +1 -1
  54. package/dist/types/src/common-auth-embedded/fs/refresh-file-lock.d.ts +1 -1
  55. package/dist/types/src/common-auth-embedded/logger/engine.d.ts +6 -8
  56. package/dist/types/src/common-auth-embedded/store/pool.d.ts +1 -0
  57. package/dist/types/src/common-auth-embedded/store/refresh-lock.d.ts +4 -0
  58. package/dist/types/src/ga/rpc/protocol.d.ts +2 -3
  59. package/dist/types/src/ga/rpc/protocol.d.ts.map +1 -1
  60. package/dist/types/src/ga/server/index.d.ts +3 -2
  61. package/dist/types/src/ga/server/index.d.ts.map +1 -1
  62. package/dist/types/src/plugin/request.d.ts.map +1 -1
  63. package/dist/types/src/plugin/vault-custody.d.ts +6 -0
  64. package/dist/types/src/plugin/vault-custody.d.ts.map +1 -1
  65. package/package.json +5 -4
  66. package/src/common-auth-embedded/claustrum/enrollment.d.ts +3 -2
  67. package/src/common-auth-embedded/claustrum/enrollment.js +84 -42
  68. package/src/common-auth-embedded/claustrum/roster.js +2 -2
  69. package/src/common-auth-embedded/fs/atomic-write.d.ts +5 -0
  70. package/src/common-auth-embedded/fs/atomic-write.js +21 -0
  71. package/src/common-auth-embedded/fs/index.d.ts +2 -0
  72. package/src/common-auth-embedded/fs/index.js +1 -0
  73. package/src/common-auth-embedded/fs/private-files.d.ts +66 -0
  74. package/src/common-auth-embedded/fs/private-files.js +302 -0
  75. package/src/common-auth-embedded/fs/refresh-file-lock.d.ts +1 -1
  76. package/src/common-auth-embedded/fs/refresh-file-lock.js +254 -37
  77. package/src/common-auth-embedded/fs/with-lock.d.ts +3 -0
  78. package/src/common-auth-embedded/fs/with-lock.js +4 -0
  79. package/src/common-auth-embedded/logger/engine.d.ts +6 -8
  80. package/src/common-auth-embedded/logger/engine.js +126 -11
  81. package/src/common-auth-embedded/pi-slot/slot.d.ts +2 -0
  82. package/src/common-auth-embedded/pi-slot/slot.js +34 -6
  83. package/src/common-auth-embedded/rpc/port-file.js +30 -8
  84. package/src/common-auth-embedded/rpc/rpc-client.js +15 -1
  85. package/src/common-auth-embedded/rpc/rpc-server.js +8 -3
  86. package/src/common-auth-embedded/source-output.json +92 -76
  87. package/src/common-auth-embedded/store/mutate.js +2 -2
  88. package/src/common-auth-embedded/store/pool.d.ts +1 -0
  89. package/src/common-auth-embedded/store/pool.js +3 -0
  90. package/src/common-auth-embedded/store/refresh-lock.d.ts +4 -0
  91. package/src/common-auth-embedded/store/refresh-lock.js +5 -0
  92. package/src/tui-compiled/common-auth-embedded/fs/private-files.js +302 -0
  93. package/src/tui-compiled/common-auth-embedded/logger/engine.js +126 -11
  94. package/src/tui-compiled/common-auth-embedded/rpc/port-file.js +30 -8
  95. package/src/tui-compiled/common-auth-embedded/rpc/rpc-client.js +15 -1
  96. package/src/tui-raw/common-auth-embedded/fs/private-files.js +302 -0
  97. package/src/tui-raw/common-auth-embedded/logger/engine.js +126 -11
  98. package/src/tui-raw/common-auth-embedded/rpc/port-file.js +30 -8
  99. package/src/tui-raw/common-auth-embedded/rpc/rpc-client.js +15 -1
@@ -1,4 +1,7 @@
1
1
  import { appendFileSync, chmodSync, existsSync, renameSync, statSync, } from 'node:fs';
2
+ import { open } from 'node:fs/promises';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { createPrivateBytes, ensurePrivateDirectory, privateOperation, replaceWindowsFile, verifyPrivate, windowsPrivateFiles, } from '../fs/private-files.js';
2
5
  import { createRedactor } from './redact.js';
3
6
  const ORDER = {
4
7
  error: 0,
@@ -36,6 +39,75 @@ function rotateIfNeeded(f) {
36
39
  /* never throw */
37
40
  }
38
41
  }
42
+ async function closeWindowsFile(sink) {
43
+ const { file, handle } = sink;
44
+ sink.file = undefined;
45
+ sink.handle = undefined;
46
+ if (file && handle)
47
+ await privateOperation(file, 'log-close', () => handle.close());
48
+ }
49
+ async function openWindowsFile(sink, file) {
50
+ await ensurePrivateDirectory(dirname(file));
51
+ try {
52
+ await verifyPrivate(file, 'file', true);
53
+ }
54
+ catch (error) {
55
+ if (error.code !== 'ENOENT')
56
+ throw error;
57
+ try {
58
+ await createPrivateBytes(file, '');
59
+ }
60
+ catch (creationError) {
61
+ if (creationError.code !== 'EEXIST')
62
+ throw creationError;
63
+ await verifyPrivate(file, 'file', true);
64
+ }
65
+ }
66
+ // The safe-parent policy denies other ordinary users the ability to create, replace or link entries between verification and fs.open.
67
+ // This protects against other ordinary users, not the owner or administrators, whose ability to change that namespace is outside our threat model.
68
+ sink.handle = await privateOperation(file, 'log-open', () => open(file, 'a'));
69
+ sink.file = file;
70
+ return sink.handle;
71
+ }
72
+ async function flushWindowsFile(sink, file, text) {
73
+ file = resolve(file);
74
+ try {
75
+ if (sink.file !== file)
76
+ await closeWindowsFile(sink);
77
+ let handle = sink.handle ?? (await openWindowsFile(sink, file));
78
+ const info = await privateOperation(file, 'log-stat', () => handle.stat());
79
+ if (info.size >= MAX_BYTES) {
80
+ await closeWindowsFile(sink);
81
+ for (let i = ROTATE_KEEP - 1; i >= 0; i--) {
82
+ const source = i === 0 ? file : `${file}.${i}`;
83
+ const target = `${file}.${i + 1}`;
84
+ try {
85
+ await verifyPrivate(source, 'file', true);
86
+ }
87
+ catch (error) {
88
+ if (error.code === 'ENOENT')
89
+ continue;
90
+ throw error;
91
+ }
92
+ try {
93
+ await verifyPrivate(target, 'file', true);
94
+ }
95
+ catch (error) {
96
+ if (error.code !== 'ENOENT')
97
+ throw error;
98
+ }
99
+ await replaceWindowsFile(source, target);
100
+ await verifyPrivate(target, 'file');
101
+ }
102
+ handle = await openWindowsFile(sink, file);
103
+ }
104
+ await privateOperation(file, 'log-append', () => handle.appendFile(text, 'utf8'));
105
+ }
106
+ catch (error) {
107
+ await closeWindowsFile(sink).catch(() => { });
108
+ throw error;
109
+ }
110
+ }
39
111
  function safeSerialize(data) {
40
112
  try {
41
113
  return ` ${JSON.stringify(data)}`;
@@ -63,7 +135,19 @@ function createEngine(options) {
63
135
  let captureSink;
64
136
  let buffer = [];
65
137
  let timer;
138
+ let orderedSink = Promise.resolve();
139
+ let sinkError;
140
+ const windowsFile = {};
141
+ function queueWindows(operation) {
142
+ const next = orderedSink.then(operation);
143
+ orderedSink = next.catch((error) => {
144
+ sinkError = error;
145
+ });
146
+ return next;
147
+ }
66
148
  function configure(next) {
149
+ if (windowsPrivateFiles && next.file !== logFileSource)
150
+ void queueWindows(() => closeWindowsFile(windowsFile)).catch(() => { });
67
151
  logFileSource = next.file;
68
152
  initLevelSource = next.level;
69
153
  redactor = createRedactor(next);
@@ -89,25 +173,49 @@ function createEngine(options) {
89
173
  return floor;
90
174
  return 'info';
91
175
  }
92
- function flushLogs() {
176
+ function flushLogs(close = true) {
93
177
  if (timer) {
94
178
  clearTimeout(timer);
95
179
  timer = undefined;
96
180
  }
97
- if (!buffer.length)
181
+ if (!buffer.length) {
182
+ if (windowsPrivateFiles)
183
+ return queueWindows(async () => {
184
+ if (close)
185
+ await closeWindowsFile(windowsFile);
186
+ if (sinkError)
187
+ throw sinkError;
188
+ });
98
189
  return;
190
+ }
99
191
  let file;
100
192
  try {
101
193
  file = logFilePath();
102
194
  }
103
195
  catch {
104
196
  buffer = [];
197
+ if (windowsPrivateFiles && close)
198
+ return queueWindows(() => closeWindowsFile(windowsFile));
105
199
  return;
106
200
  }
107
201
  const text = buffer.join('');
108
202
  buffer = [];
109
- if (!file)
203
+ if (!file) {
204
+ if (windowsPrivateFiles && close)
205
+ return queueWindows(() => closeWindowsFile(windowsFile));
110
206
  return;
207
+ }
208
+ if (windowsPrivateFiles) {
209
+ return queueWindows(async () => {
210
+ try {
211
+ await flushWindowsFile(windowsFile, file, text);
212
+ }
213
+ finally {
214
+ if (close)
215
+ await closeWindowsFile(windowsFile);
216
+ }
217
+ });
218
+ }
111
219
  try {
112
220
  rotateIfNeeded(file);
113
221
  if (existsSync(file))
@@ -122,7 +230,9 @@ function createEngine(options) {
122
230
  if (!timer)
123
231
  timer = setTimeout(() => {
124
232
  timer = undefined;
125
- flushLogs();
233
+ const flushed = flushLogs(false);
234
+ if (flushed instanceof Promise)
235
+ void flushed.catch(() => { });
126
236
  }, 500);
127
237
  }
128
238
  function emit(channel, level, message, data) {
@@ -157,8 +267,11 @@ function createEngine(options) {
157
267
  if (logFileSource === undefined)
158
268
  return;
159
269
  buffer.push(line);
160
- if (buffer.length >= 50)
161
- flushLogs();
270
+ if (buffer.length >= 50) {
271
+ const flushed = flushLogs(false);
272
+ if (flushed instanceof Promise)
273
+ void flushed.catch(() => { });
274
+ }
162
275
  else
163
276
  schedule();
164
277
  }
@@ -176,6 +289,9 @@ function createEngine(options) {
176
289
  };
177
290
  }
178
291
  function reset() {
292
+ if (windowsPrivateFiles)
293
+ void queueWindows(() => closeWindowsFile(windowsFile)).catch(() => { });
294
+ sinkError = undefined;
179
295
  buffer = [];
180
296
  if (timer) {
181
297
  clearTimeout(timer);
@@ -222,18 +338,17 @@ export function setLogLevel(l) {
222
338
  defaultEngine.setLogLevel(l);
223
339
  }
224
340
  /**
225
- * Write whatever the default logger has buffered. Safe to call synchronously
226
- * from a process-exit handler, which is how each host drains the buffer on
227
- * shutdown.
341
+ * Write whatever the default logger has buffered. On Windows await the
342
+ * promise before shutdown; POSIX keeps synchronous process-exit flushing.
228
343
  */
229
344
  export function flushLogs() {
230
- defaultEngine.flushLogs();
345
+ return defaultEngine.flushLogs();
231
346
  }
232
347
  export function createLogger(channel) {
233
348
  return defaultEngine.createLogger(channel);
234
349
  }
235
350
  export async function flushForTest() {
236
- defaultEngine.flushLogs();
351
+ return defaultEngine.flushLogs();
237
352
  }
238
353
  /**
239
354
  * Return the default logger to its uninitialised state. Only a test needs
@@ -5,6 +5,8 @@ export declare class PiSlotError extends Error {
5
5
  constructor(code: PiSlotErrorCode);
6
6
  }
7
7
  export interface PiSlotOptions {
8
+ /** Windows checks safe ancestry and creates missing parents owner-only, regardless of this option. POSIX keeps its existing directory policy. */
9
+ privateParents?: boolean;
8
10
  authPath: string;
9
11
  provider: string;
10
12
  /**
@@ -4,6 +4,7 @@ import { lstat, mkdir, open, unlink } from 'node:fs/promises';
4
4
  import { dirname, resolve } from 'node:path';
5
5
  import { setTimeout as sleep } from 'node:timers/promises';
6
6
  import { writeJsonAtomic } from '../fs/atomic-write.js';
7
+ import { ensurePrivateDirectory, PrivateFileError, readPrivateBuffer, windowsPrivateFiles, } from '../fs/private-files.js';
7
8
  import { acquirePiLock } from './lock.js';
8
9
  /** Errors deliberately contain neither paths nor credentials nor underlying error messages. */
9
10
  export class PiSlotError extends Error {
@@ -96,6 +97,21 @@ function code(error) {
96
97
  return object(error) ? error.code : undefined;
97
98
  }
98
99
  async function safeRead(path, privateFile = false, afterReadCheck) {
100
+ if (windowsPrivateFiles) {
101
+ try {
102
+ await afterReadCheck?.(path);
103
+ const bytes = await readPrivateBuffer(path, true);
104
+ const text = bytes.toString('utf8');
105
+ if (!bytes.equals(Buffer.from(text)))
106
+ throw new PiSlotError(privateFile ? 'invalid-stash' : 'invalid-auth');
107
+ return text;
108
+ }
109
+ catch (error) {
110
+ if (code(error) === 'ENOENT')
111
+ return undefined;
112
+ throw error;
113
+ }
114
+ }
99
115
  let file;
100
116
  try {
101
117
  file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
@@ -188,6 +204,10 @@ function replaceEntry(auth, provider, next) {
188
204
  return `${text.slice(0, offset)}${last ? ',' : ''}${JSON.stringify(provider)}:${next}${text.slice(offset)}`;
189
205
  }
190
206
  async function syncDir(path) {
207
+ // Windows flushes file bytes before rename but cannot promise persisted
208
+ // directory metadata, so this Pi transition deliberately skips directory sync.
209
+ if (windowsPrivateFiles)
210
+ return;
191
211
  const dir = await open(path, 'r');
192
212
  try {
193
213
  await dir.sync();
@@ -200,6 +220,7 @@ async function atomicWrite(path, text, assertLock, beforeRename) {
200
220
  await writeJsonAtomic(path, null, {
201
221
  serialize: () => text,
202
222
  durable: true,
223
+ durability: windowsPrivateFiles ? 'file-only-on-windows' : undefined,
203
224
  beforeRename: async () => {
204
225
  await beforeRename?.(path);
205
226
  assertLock();
@@ -274,7 +295,10 @@ export function createPiSlot(options, onStep, hooks) {
274
295
  throw new PiSlotError('lock-compromised');
275
296
  };
276
297
  try {
277
- await mkdir(dirname(authPath), { recursive: true, mode: 0o700 });
298
+ if (windowsPrivateFiles)
299
+ await ensurePrivateDirectory(dirname(authPath));
300
+ else
301
+ await mkdir(dirname(authPath), { recursive: true, mode: 0o700 });
278
302
  const deadline = Date.now() + timeout;
279
303
  let retry = 0;
280
304
  while (!release) {
@@ -300,7 +324,7 @@ export function createPiSlot(options, onStep, hooks) {
300
324
  return result;
301
325
  }
302
326
  catch (error) {
303
- if (error instanceof PiSlotError)
327
+ if (error instanceof PiSlotError || error instanceof PrivateFileError)
304
328
  throw error;
305
329
  throw new PiSlotError('io');
306
330
  }
@@ -342,10 +366,14 @@ export function createPiSlot(options, onStep, hooks) {
342
366
  if (stash && digest(auth.entry) !== stash.sha256)
343
367
  throw new PiSlotError('conflict');
344
368
  if (!stash) {
345
- await mkdir(dirname(stashPath), { recursive: true, mode: 0o700 });
346
- const dir = await lstat(dirname(stashPath));
347
- if (!dir.isDirectory() || (dir.mode & 0o777) !== 0o700)
348
- throw new PiSlotError('unsafe-path');
369
+ if (windowsPrivateFiles)
370
+ await ensurePrivateDirectory(dirname(stashPath));
371
+ else {
372
+ await mkdir(dirname(stashPath), { recursive: true, mode: 0o700 });
373
+ const dir = await lstat(dirname(stashPath));
374
+ if (!dir.isDirectory() || (dir.mode & 0o777) !== 0o700)
375
+ throw new PiSlotError('unsafe-path');
376
+ }
349
377
  const saved = {
350
378
  version: 1,
351
379
  provider,
@@ -1,6 +1,7 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
- import { chmod, mkdir, open, readdir, readFile, rename, rmdir, stat, unlink, } from 'node:fs/promises';
2
+ import { chmod, mkdir, open, readdir, rename, rmdir, stat, unlink, } from 'node:fs/promises';
3
3
  import { join, resolve } from 'node:path';
4
+ import { ensurePrivateDirectory, PrivateFileError, readPrivateText, verifyPrivate, windowsPrivateFiles, writePrivateAtomic, } from '../fs/private-files.js';
4
5
  /**
5
6
  * A PID a liveness probe may address. `process.kill(0 | negative, 0)` signals
6
7
  * a process group rather than a process, and an unsafe integer cannot name a
@@ -68,6 +69,15 @@ export async function writePortFile(dir, entry, options = {}) {
68
69
  // persistent ENOENT (e.g. permission, read-only parent) will surface on
69
70
  // the second attempt — failing fast beats an unbounded loop.
70
71
  const writeOnce = async () => {
72
+ const target = join(dir, `port-${entry.pid}.json`);
73
+ if (windowsPrivateFiles) {
74
+ await ensurePrivateDirectory(dir, options.secureDir ?? false);
75
+ await options.beforeWrite?.();
76
+ await writePrivateAtomic(target, JSON.stringify({ ...entry, startedAt: Date.now() }), {
77
+ stagePath: `${target}.${(options.stageName ?? randomUUID)()}.tmp`,
78
+ });
79
+ return target;
80
+ }
71
81
  await mkdir(dir, {
72
82
  recursive: true,
73
83
  mode: options.secureDir ? 0o700 : undefined,
@@ -76,7 +86,6 @@ export async function writePortFile(dir, entry, options = {}) {
76
86
  await chmod(dir, 0o700);
77
87
  await options.beforeWrite?.();
78
88
  const full = { ...entry, startedAt: Date.now() };
79
- const target = join(dir, `port-${entry.pid}.json`);
80
89
  const tmp = `${target}.${(options.stageName ?? randomUUID)()}.tmp`;
81
90
  let handle;
82
91
  let created = false;
@@ -139,9 +148,11 @@ export async function sweepRpcState(root, activeDir, isManagedDir, log) {
139
148
  const portFile = join(dir, name);
140
149
  let raw;
141
150
  try {
142
- raw = await readFile(portFile, 'utf8');
151
+ raw = await readPrivateText(portFile);
143
152
  }
144
- catch {
153
+ catch (error) {
154
+ if (error instanceof PrivateFileError && error.code !== 'ENOENT')
155
+ throw error;
145
156
  continue;
146
157
  }
147
158
  if (raw === undefined)
@@ -172,19 +183,27 @@ export async function portFileIdentity(dir, entry) {
172
183
  return null;
173
184
  const path = join(resolve(dir), `port-${entry.pid}.json`);
174
185
  try {
186
+ if (windowsPrivateFiles)
187
+ await verifyPrivate(path, 'file');
175
188
  const info = await stat(path);
176
189
  return `${path}:${info.dev}:${info.ino}:${info.mtimeMs}:${info.size}`;
177
190
  }
178
- catch {
191
+ catch (error) {
192
+ if (error instanceof PrivateFileError && error.code !== 'ENOENT')
193
+ throw error;
179
194
  return null;
180
195
  }
181
196
  }
182
197
  export async function discoverPortFile(dir, expectedPid, options = {}) {
183
198
  let names;
184
199
  try {
200
+ if (windowsPrivateFiles)
201
+ await verifyPrivate(dir, 'directory');
185
202
  names = await readdir(dir);
186
203
  }
187
- catch {
204
+ catch (error) {
205
+ if (error instanceof PrivateFileError && error.code !== 'ENOENT')
206
+ throw error;
188
207
  return null;
189
208
  }
190
209
  const live = [];
@@ -192,7 +211,7 @@ export async function discoverPortFile(dir, expectedPid, options = {}) {
192
211
  if (!name.startsWith('port-') || !name.endsWith('.json'))
193
212
  continue;
194
213
  try {
195
- const parsed = JSON.parse(await readFile(join(dir, name), 'utf8'));
214
+ const parsed = JSON.parse(await readPrivateText(join(dir, name)));
196
215
  if (isUsablePortFileEntry(parsed, name)) {
197
216
  if (pidAlive(parsed.pid))
198
217
  live.push(parsed);
@@ -200,7 +219,10 @@ export async function discoverPortFile(dir, expectedPid, options = {}) {
200
219
  await unlink(join(dir, name)).catch(() => { });
201
220
  }
202
221
  }
203
- catch { }
222
+ catch (error) {
223
+ if (error instanceof PrivateFileError && error.code !== 'ENOENT')
224
+ throw error;
225
+ }
204
226
  }
205
227
  if (live.length === 0)
206
228
  return null;
@@ -1,6 +1,7 @@
1
1
  import { open } from 'node:fs/promises';
2
2
  import { connect } from 'node:net';
3
3
  import { join, resolve } from 'node:path';
4
+ import { readPrivateText, windowsPrivateFiles } from '../fs/private-files.js';
4
5
  import { discoverPortFile, portFileIdentity, } from './port-file.js';
5
6
  export const DEFAULT_RPC_TIMEOUT_MS = 2_000;
6
7
  const selections = new Map();
@@ -12,6 +13,17 @@ const discoveries = new Map();
12
13
  // those bytes still name the discovered entry.
13
14
  async function boundIdentity(dir, entry) {
14
15
  const path = join(resolve(dir), `port-${entry.pid}.json`);
16
+ if (windowsPrivateFiles) {
17
+ const current = JSON.parse(await readPrivateText(path));
18
+ if (current.pid !== entry.pid ||
19
+ current.port !== entry.port ||
20
+ current.token !== entry.token)
21
+ return null;
22
+ // private-files does not expose the read handle's identity, so a separate
23
+ // stat cannot bind an RPC-server cache entry to the file that supplied its
24
+ // token. On Windows, rediscover the port file on every call.
25
+ return null;
26
+ }
15
27
  let handle;
16
28
  try {
17
29
  handle = await open(path, 'r');
@@ -32,7 +44,9 @@ async function boundIdentity(dir, entry) {
32
44
  }
33
45
  async function select(key, dir, expectedPid, options) {
34
46
  const cached = selections.get(key);
35
- if (cached && (await portFileIdentity(dir, cached.entry)) === cached.identity)
47
+ if (!windowsPrivateFiles &&
48
+ cached &&
49
+ (await portFileIdentity(dir, cached.entry)) === cached.identity)
36
50
  return cached;
37
51
  selections.delete(key);
38
52
  const pending = discoveries.get(key);
@@ -1,7 +1,8 @@
1
1
  import { randomBytes, timingSafeEqual } from 'node:crypto';
2
- import { readFile, unlink } from 'node:fs/promises';
2
+ import { unlink } from 'node:fs/promises';
3
3
  import { createServer, } from 'node:http';
4
4
  import { join } from 'node:path';
5
+ import { PrivateFileError, readPrivateText } from '../fs/private-files.js';
5
6
  import { isSessionId, } from './notifications.js';
6
7
  import { sweepRpcState, writePortFile } from './port-file.js';
7
8
  /**
@@ -301,9 +302,13 @@ export async function startRpcServer(options) {
301
302
  socket.destroy();
302
303
  });
303
304
  const portFile = join(options.dir, `port-${process.pid}.json`);
304
- const current = await readFile(portFile, 'utf8')
305
+ const current = await readPrivateText(portFile)
305
306
  .then((raw) => JSON.parse(raw))
306
- .catch(() => undefined);
307
+ .catch((error) => {
308
+ if (error instanceof PrivateFileError && error.code !== 'ENOENT')
309
+ throw error;
310
+ return undefined;
311
+ });
307
312
  if (current?.port === port && current.token === token)
308
313
  await unlink(portFile).catch(() => { });
309
314
  },