@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';
|
|
@@ -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
|
-
|
|
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) => {
|