@skrr-ai/cli 0.1.75 → 0.1.77
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/agents/releases/ci-publish.js +16 -0
- package/dist/commands/store/install.js +6 -6
- 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 +4 -3
- package/dist/lib/dedicated-file-retry.d.ts +4 -0
- package/dist/lib/dedicated-file-retry.js +45 -0
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4 -0
- package/oclif.manifest.json +34208 -34146
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../../../base-command");
|
|
6
6
|
const github_oidc_1 = require("../../../lib/github-oidc");
|
|
7
|
+
const http_error_1 = require("../../../lib/http-error");
|
|
7
8
|
/**
|
|
8
9
|
* Publish a release from CI using GitHub OIDC trusted publishing (OSK-12311).
|
|
9
10
|
*
|
|
@@ -84,6 +85,21 @@ class AgentsReleasesCiPublish extends base_command_1.BaseCommand {
|
|
|
84
85
|
});
|
|
85
86
|
}
|
|
86
87
|
catch (err) {
|
|
88
|
+
const body = (0, http_error_1.httpBodyOf)(err);
|
|
89
|
+
if ((0, http_error_1.httpStatusOf)(err) === 401 && body?.code === 'CI_OIDC_TOKEN_INVALID') {
|
|
90
|
+
const reason = typeof body.error === 'string' ? body.error : 'OIDC token was rejected';
|
|
91
|
+
const requestId = err instanceof Error ? err.requestId : undefined;
|
|
92
|
+
this.failWithCliError({
|
|
93
|
+
message: `${reason}. Request a fresh GitHub Actions OIDC token with ` +
|
|
94
|
+
'`permissions: id-token: write` and the expected audience.',
|
|
95
|
+
code: 'CI_OIDC_TOKEN_INVALID',
|
|
96
|
+
status: 401,
|
|
97
|
+
exit: 2,
|
|
98
|
+
retryable: false,
|
|
99
|
+
details: body,
|
|
100
|
+
requestId: typeof requestId === 'string' ? requestId : undefined,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
87
103
|
this.handleApiError(err);
|
|
88
104
|
}
|
|
89
105
|
if (flags.json) {
|
|
@@ -6,12 +6,12 @@ const base_command_1 = require("../../base-command");
|
|
|
6
6
|
const prompt_1 = require("../../lib/prompt");
|
|
7
7
|
/**
|
|
8
8
|
* One line per placement, said the same way the web install rail says it: the
|
|
9
|
-
* difference is whether a stranger's instructions
|
|
10
|
-
*
|
|
9
|
+
* difference is whether a stranger's instructions may reach a connected
|
|
10
|
+
* machine in the buyer's account. The model loop stays on Agent Worker.
|
|
11
11
|
*/
|
|
12
12
|
const PLACEMENT_NOTE = {
|
|
13
|
-
cloud: "It runs in the cloud, so its tools
|
|
14
|
-
local: 'It
|
|
13
|
+
cloud: "It runs in the cloud, so its tools cannot reach your connected machines. You can change that in the agent's Runtime settings.",
|
|
14
|
+
local: 'It still runs in the cloud. Its tools may use the shell, files and browser on an available connected machine in your account, selected when the agent runs.',
|
|
15
15
|
};
|
|
16
16
|
class StoreInstall extends base_command_1.BaseCommand {
|
|
17
17
|
static description = 'Install a published agent — you get your own copy of it. A paid agent shows its price and refund terms and asks you to confirm first.';
|
|
@@ -44,8 +44,8 @@ class StoreInstall extends base_command_1.BaseCommand {
|
|
|
44
44
|
description: 'Confirm a paid purchase without prompting. The price and refund terms are still printed.',
|
|
45
45
|
}),
|
|
46
46
|
'run-on': core_1.Flags.string({
|
|
47
|
-
description: 'Where
|
|
48
|
-
'local lets
|
|
47
|
+
description: 'Where its tools may run. cloud (the default) keeps tools off connected machines; ' +
|
|
48
|
+
'local lets tools use an available connected machine in your account, selected at run time. The agent runs in the cloud either way',
|
|
49
49
|
options: [...data_provider_1.STORE_INSTALL_EXECUTION_PLACEMENTS],
|
|
50
50
|
}),
|
|
51
51
|
'share-outcomes': core_1.Flags.boolean({
|
|
@@ -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,7 +213,7 @@ 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,
|
|
@@ -220,7 +221,7 @@ async function uploadFile(localPath, leaseId, remotePath, scope, io) {
|
|
|
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
223
|
...(final ? { final: true, totalSize: info.size } : {}),
|
|
223
|
-
}, scope);
|
|
224
|
+
}, scope), signal);
|
|
224
225
|
offset += bytesRead;
|
|
225
226
|
onProgress?.(offset, info.size);
|
|
226
227
|
if (final) {
|
|
@@ -280,7 +281,7 @@ async function downloadFromDedicatedRuntime(leaseId, remotePath, localPath, scop
|
|
|
280
281
|
throwIfCancelled(signal);
|
|
281
282
|
// Nothing on the machine needs undoing, so an in-flight read is abandoned
|
|
282
283
|
// at once rather than waited out.
|
|
283
|
-
const chunk = await readChunk(leaseId, { path: remotePath, offset, length: chunkBytes }, scope, signal);
|
|
284
|
+
const chunk = await (0, dedicated_file_retry_1.withFileAdmissionRetry)(() => readChunk(leaseId, { path: remotePath, offset, length: chunkBytes }, scope, signal), signal);
|
|
284
285
|
if (size === -1) {
|
|
285
286
|
size = chunk.size;
|
|
286
287
|
mtimeMs = chunk.mtimeMs;
|
|
@@ -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
|
+
}
|
|
@@ -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) {
|