@skrr-ai/cli 0.1.77 → 0.1.78

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.
@@ -26,6 +26,8 @@ class DedicatedMachinesCp extends base_command_1.BaseCommand {
26
26
  'machine is created.',
27
27
  'As with cp and scp, a destination that ends in / or is an existing directory receives the file ' +
28
28
  'under its source name.',
29
+ 'Files marked executable keep owner execute permission on the copy. Other permissions use ' +
30
+ 'destination defaults; source special permission bits are not copied.',
29
31
  'Neither side is left half-written: the file is staged beside its destination and renamed into ' +
30
32
  'place at the end. A file that changes while it is copied is refused, not spliced. Ctrl-C removes ' +
31
33
  'the partial copy and exits 130 (143 for SIGTERM, 129 when the terminal closes). Progress is ' +
@@ -220,7 +220,7 @@ async function uploadFile(localPath, leaseId, remotePath, scope, io) {
220
220
  dataBase64: buffer.subarray(0, bytesRead).toString('base64'),
221
221
  // A file that GREW is caught here: the machine refuses to commit
222
222
  // when what it staged is not the size declared at the start.
223
- ...(final ? { final: true, totalSize: info.size } : {}),
223
+ ...(final ? { final: true, totalSize: info.size, executable: (info.mode & 0o111) !== 0 } : {}),
224
224
  }, scope), signal);
225
225
  offset += bytesRead;
226
226
  onProgress?.(offset, info.size);
@@ -276,6 +276,7 @@ async function downloadFromDedicatedRuntime(leaseId, remotePath, localPath, scop
276
276
  let offset = 0;
277
277
  let size = -1;
278
278
  let mtimeMs = -1;
279
+ let executable;
279
280
  try {
280
281
  for (;;) {
281
282
  throwIfCancelled(signal);
@@ -285,11 +286,17 @@ async function downloadFromDedicatedRuntime(leaseId, remotePath, localPath, scop
285
286
  if (size === -1) {
286
287
  size = chunk.size;
287
288
  mtimeMs = chunk.mtimeMs;
289
+ executable = chunk.executable;
288
290
  }
289
- else if (chunk.size !== size || chunk.mtimeMs !== mtimeMs) {
291
+ else if (chunk.size !== size || chunk.mtimeMs !== mtimeMs ||
292
+ (typeof chunk.executable === 'boolean' && typeof executable === 'boolean' &&
293
+ chunk.executable !== executable)) {
290
294
  // Chunks from two versions of a file splice into one that never existed.
291
295
  throw changedWhileCopying(remotePath, 'on the machine');
292
296
  }
297
+ // Older API replicas omit this additive metadata during a rollout.
298
+ if (typeof chunk.executable === 'boolean')
299
+ executable = chunk.executable;
293
300
  const bytes = Buffer.from(chunk.dataBase64, 'base64');
294
301
  if (bytes.byteLength !== chunk.bytesRead) {
295
302
  throw new DedicatedCopyError('DEDICATED_RUNTIME_FILE_TRANSFER_FAILED', 'A chunk arrived damaged; nothing was written to the destination.');
@@ -304,6 +311,11 @@ async function downloadFromDedicatedRuntime(leaseId, remotePath, localPath, scop
304
311
  throw changedWhileCopying(remotePath, 'on the machine');
305
312
  }
306
313
  }
314
+ // Set only the owner execute bit on the pinned staging inode. Other
315
+ // permissions keep destination defaults; source special bits never travel.
316
+ if (executable === true) {
317
+ await handle.chmod(((await handle.stat()).mode & 0o777) | 0o100);
318
+ }
307
319
  await handle.close();
308
320
  closed = true;
309
321
  await (0, promises_1.rename)(temporary, target);
@@ -466,6 +466,7 @@ export interface DedicatedFileChunk {
466
466
  path: string;
467
467
  size: number;
468
468
  mtimeMs: number;
469
+ executable?: boolean;
469
470
  offset: number;
470
471
  bytesRead: number;
471
472
  dataBase64: string;
@@ -491,6 +492,7 @@ export declare function writeDedicatedRuntimeFileChunk(leaseId: string, input: {
491
492
  final?: boolean;
492
493
  abort?: boolean;
493
494
  totalSize?: number;
495
+ executable?: boolean;
494
496
  }, scope?: DedicatedRuntimeScope): Promise<DedicatedFileWriteResult>;
495
497
  export type DedicatedCopyPlan = {
496
498
  direction: 'upload';
@@ -112,6 +112,7 @@ export interface SshStartAck {
112
112
  type: string;
113
113
  key: string;
114
114
  }[];
115
+ receiverAckV1?: boolean;
115
116
  }
116
117
  export interface SshProxyOptions {
117
118
  baseURL: string;
@@ -343,6 +343,7 @@ function runSshProxy(options) {
343
343
  channelId,
344
344
  keyType: identity.keyType,
345
345
  keyBlob: identity.keyBlob,
346
+ receiverAckV1: true,
346
347
  });
347
348
  });
348
349
  socket.on('dedicated:ssh:start:ack', (ack) => {
@@ -366,11 +367,23 @@ function runSshProxy(options) {
366
367
  input.resume();
367
368
  input.on('data', onInput);
368
369
  });
369
- socket.on('dedicated:ssh:data', (frame) => {
370
+ socket.on('dedicated:ssh:data', (frame, receipt) => {
370
371
  if (!frame || frame.channelId !== channelId || finished)
371
372
  return;
372
373
  if (typeof frame.data === 'string' && frame.data.length > 0) {
373
- output.write(Buffer.from(frame.data, 'base64'));
374
+ const bytes = Buffer.from(frame.data, 'base64');
375
+ if (typeof receipt === 'function') {
376
+ // A frame can precede start-ack (sshd's banner). The owned channel
377
+ // already advertised support; confirm only when stdout flushed.
378
+ output.write(bytes, (error) => {
379
+ receipt({ channelId, ok: !error && !finished });
380
+ if (error)
381
+ finish(1, 'Could not write SSH output.');
382
+ });
383
+ }
384
+ else {
385
+ output.write(bytes);
386
+ }
374
387
  }
375
388
  });
376
389
  socket.on('dedicated:ssh:closed', (frame) => {