@skrr-ai/cli 0.1.76 → 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.
- package/dist/commands/machines/dedicated/cp.js +2 -0
- package/dist/commands/store/publishers/cancel.d.ts +11 -0
- package/dist/commands/store/publishers/cancel.js +17 -0
- package/dist/lib/dedicated-copy.js +18 -5
- package/dist/lib/dedicated-file-retry.d.ts +4 -0
- package/dist/lib/dedicated-file-retry.js +45 -0
- package/dist/lib/dedicated-machines.d.ts +2 -0
- package/dist/lib/dedicated-ssh.d.ts +1 -0
- package/dist/lib/dedicated-ssh.js +15 -2
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4 -0
- package/oclif.manifest.json +22872 -22810
- package/package.json +1 -1
|
@@ -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 ' +
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
export default class StorePublishersCancel extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
approvalId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static flags: {
|
|
8
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../../base-command");
|
|
6
|
+
class StorePublishersCancel extends base_command_1.BaseCommand {
|
|
7
|
+
static description = 'Withdraw your own pending org publish approval request';
|
|
8
|
+
static args = { approvalId: core_1.Args.string({ required: true, ignoreStdin: true }) };
|
|
9
|
+
static flags = { json: core_1.Flags.boolean({ description: 'Output JSON' }) };
|
|
10
|
+
async run() {
|
|
11
|
+
this.requireAuth();
|
|
12
|
+
const { args } = await this.parse(StorePublishersCancel);
|
|
13
|
+
const result = await data_provider_1.dataService.cancelStoreOrgPublishApproval(args.approvalId);
|
|
14
|
+
this.log(JSON.stringify(result, null, 2));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.default = StorePublishersCancel;
|
|
@@ -12,6 +12,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
12
12
|
const node_fs_1 = require("node:fs");
|
|
13
13
|
const promises_1 = require("node:fs/promises");
|
|
14
14
|
const node_path_1 = require("node:path");
|
|
15
|
+
const dedicated_file_retry_1 = require("./dedicated-file-retry");
|
|
15
16
|
const dedicated_machines_1 = require("./dedicated-machines");
|
|
16
17
|
/**
|
|
17
18
|
* A refusal the CLI makes itself, with the code the server uses for the same
|
|
@@ -212,15 +213,15 @@ async function uploadFile(localPath, leaseId, remotePath, scope, io) {
|
|
|
212
213
|
if (!final && bytesRead === 0) {
|
|
213
214
|
throw changedWhileCopying(localPath, 'here');
|
|
214
215
|
}
|
|
215
|
-
const result = await writeChunk(leaseId, {
|
|
216
|
+
const result = await (0, dedicated_file_retry_1.withFileAdmissionRetry)(() => writeChunk(leaseId, {
|
|
216
217
|
path: remotePath,
|
|
217
218
|
transferId,
|
|
218
219
|
offset,
|
|
219
220
|
dataBase64: buffer.subarray(0, bytesRead).toString('base64'),
|
|
220
221
|
// A file that GREW is caught here: the machine refuses to commit
|
|
221
222
|
// when what it staged is not the size declared at the start.
|
|
222
|
-
...(final ? { final: true, totalSize: info.size } : {}),
|
|
223
|
-
}, scope);
|
|
223
|
+
...(final ? { final: true, totalSize: info.size, executable: (info.mode & 0o111) !== 0 } : {}),
|
|
224
|
+
}, scope), signal);
|
|
224
225
|
offset += bytesRead;
|
|
225
226
|
onProgress?.(offset, info.size);
|
|
226
227
|
if (final) {
|
|
@@ -275,20 +276,27 @@ async function downloadFromDedicatedRuntime(leaseId, remotePath, localPath, scop
|
|
|
275
276
|
let offset = 0;
|
|
276
277
|
let size = -1;
|
|
277
278
|
let mtimeMs = -1;
|
|
279
|
+
let executable;
|
|
278
280
|
try {
|
|
279
281
|
for (;;) {
|
|
280
282
|
throwIfCancelled(signal);
|
|
281
283
|
// Nothing on the machine needs undoing, so an in-flight read is abandoned
|
|
282
284
|
// at once rather than waited out.
|
|
283
|
-
const chunk = await readChunk(leaseId, { path: remotePath, offset, length: chunkBytes }, scope, signal);
|
|
285
|
+
const chunk = await (0, dedicated_file_retry_1.withFileAdmissionRetry)(() => readChunk(leaseId, { path: remotePath, offset, length: chunkBytes }, scope, signal), signal);
|
|
284
286
|
if (size === -1) {
|
|
285
287
|
size = chunk.size;
|
|
286
288
|
mtimeMs = chunk.mtimeMs;
|
|
289
|
+
executable = chunk.executable;
|
|
287
290
|
}
|
|
288
|
-
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)) {
|
|
289
294
|
// Chunks from two versions of a file splice into one that never existed.
|
|
290
295
|
throw changedWhileCopying(remotePath, 'on the machine');
|
|
291
296
|
}
|
|
297
|
+
// Older API replicas omit this additive metadata during a rollout.
|
|
298
|
+
if (typeof chunk.executable === 'boolean')
|
|
299
|
+
executable = chunk.executable;
|
|
292
300
|
const bytes = Buffer.from(chunk.dataBase64, 'base64');
|
|
293
301
|
if (bytes.byteLength !== chunk.bytesRead) {
|
|
294
302
|
throw new DedicatedCopyError('DEDICATED_RUNTIME_FILE_TRANSFER_FAILED', 'A chunk arrived damaged; nothing was written to the destination.');
|
|
@@ -303,6 +311,11 @@ async function downloadFromDedicatedRuntime(leaseId, remotePath, localPath, scop
|
|
|
303
311
|
throw changedWhileCopying(remotePath, 'on the machine');
|
|
304
312
|
}
|
|
305
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
|
+
}
|
|
306
319
|
await handle.close();
|
|
307
320
|
closed = true;
|
|
308
321
|
await (0, promises_1.rename)(temporary, target);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Only admission refusals prove the pending file request never reached the daemon. */
|
|
2
|
+
export declare function isFileAdmissionReconnect(error: unknown): boolean;
|
|
3
|
+
/** Retry the same pending chunk, never an ambiguous transport failure or lost commit reply. */
|
|
4
|
+
export declare function withFileAdmissionRetry<T>(request: () => Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isFileAdmissionReconnect = isFileAdmissionReconnect;
|
|
4
|
+
exports.withFileAdmissionRetry = withFileAdmissionRetry;
|
|
5
|
+
const promises_1 = require("node:timers/promises");
|
|
6
|
+
const api_fetch_1 = require("./api-fetch");
|
|
7
|
+
const RECONNECT_BACKOFF_MS = [500, 1000, 2000, 2000];
|
|
8
|
+
/** Only admission refusals prove the pending file request never reached the daemon. */
|
|
9
|
+
function isFileAdmissionReconnect(error) {
|
|
10
|
+
if (!(error instanceof api_fetch_1.ApiFetchError) ||
|
|
11
|
+
error.status !== 503 ||
|
|
12
|
+
error.code !== 'DEDICATED_RUNTIME_NOT_ASSIGNABLE')
|
|
13
|
+
return false;
|
|
14
|
+
try {
|
|
15
|
+
const body = JSON.parse(error.body ?? 'null');
|
|
16
|
+
return (body !== null &&
|
|
17
|
+
typeof body === 'object' &&
|
|
18
|
+
!Array.isArray(body) &&
|
|
19
|
+
body.code === 'DEDICATED_RUNTIME_NOT_ASSIGNABLE' &&
|
|
20
|
+
body.retryable === true);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Retry the same pending chunk, never an ambiguous transport failure or lost commit reply. */
|
|
27
|
+
async function withFileAdmissionRetry(request, signal) {
|
|
28
|
+
for (let attempt = 0;; attempt += 1) {
|
|
29
|
+
if (signal?.aborted)
|
|
30
|
+
throw signal.reason ?? new Error('Copy cancelled');
|
|
31
|
+
try {
|
|
32
|
+
return await request();
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (attempt >= RECONNECT_BACKOFF_MS.length || !isFileAdmissionReconnect(error))
|
|
36
|
+
throw error;
|
|
37
|
+
try {
|
|
38
|
+
await (0, promises_1.setTimeout)(RECONNECT_BACKOFF_MS[attempt], undefined, { signal });
|
|
39
|
+
}
|
|
40
|
+
catch (waitError) {
|
|
41
|
+
throw signal?.aborted ? (signal.reason ?? waitError) : waitError;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -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) => {
|
|
@@ -42881,6 +42881,10 @@ var gR = Object.freeze({ __proto__: null, acceptAgentIntuition: function(e2, t2,
|
|
|
42881
42881
|
return oR.post((function(e3, t3) {
|
|
42882
42882
|
return "".concat(ia(e3, t3), "/cancel");
|
|
42883
42883
|
})(e2, t2), {});
|
|
42884
|
+
}, cancelStoreOrgPublishApproval: function(e2) {
|
|
42885
|
+
return oR.delete((function(e3) {
|
|
42886
|
+
return "".concat(er(), "/publishers/approvals/").concat(encodeURIComponent(e3));
|
|
42887
|
+
})(e2));
|
|
42884
42888
|
}, cancelSubscription: function(e2) {
|
|
42885
42889
|
return oR.post("".concat(Lr(), "/cancel"), e2);
|
|
42886
42890
|
}, cancelTaskAssetUpload: function(e2) {
|