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