@skrr-ai/cli 0.1.86 → 0.1.88
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/bin/run.js +11 -0
- package/dist/base-command.js +1 -0
- package/dist/commands/agents/chat.d.ts +58 -1
- package/dist/commands/agents/chat.js +206 -4
- package/dist/commands/computer/index.d.ts +19 -0
- package/dist/commands/computer/index.js +37 -0
- package/dist/commands/labels/create.d.ts +1 -0
- package/dist/commands/labels/create.js +2 -0
- package/dist/commands/labels/restore.d.ts +1 -0
- package/dist/commands/labels/restore.js +6 -3
- package/dist/commands/labels/update.d.ts +1 -0
- package/dist/commands/labels/update.js +2 -0
- package/dist/commands/machines/computer-adoption.d.ts +15 -0
- package/dist/commands/machines/computer-adoption.js +33 -0
- package/dist/commands/machines/dedicated/archive.d.ts +26 -0
- package/dist/commands/machines/dedicated/archive.js +113 -0
- package/dist/commands/machines/dedicated/audit.d.ts +25 -0
- package/dist/commands/machines/dedicated/audit.js +113 -0
- package/dist/commands/machines/dedicated/computer.d.ts +23 -0
- package/dist/commands/machines/dedicated/computer.js +64 -0
- package/dist/commands/machines/dedicated/cp.d.ts +2 -0
- package/dist/commands/machines/dedicated/cp.js +74 -2
- package/dist/commands/machines/dedicated/git-status.d.ts +15 -0
- package/dist/commands/machines/dedicated/git-status.js +56 -0
- package/dist/commands/machines/dedicated/index.js +15 -1
- package/dist/commands/machines/dedicated/ls.d.ts +27 -0
- package/dist/commands/machines/dedicated/ls.js +100 -0
- package/dist/commands/machines/dedicated/mkdir.d.ts +18 -0
- package/dist/commands/machines/dedicated/mkdir.js +57 -0
- package/dist/commands/machines/dedicated/mv.d.ts +24 -0
- package/dist/commands/machines/dedicated/mv.js +83 -0
- package/dist/commands/machines/dedicated/rm.d.ts +26 -0
- package/dist/commands/machines/dedicated/rm.js +100 -0
- package/dist/commands/machines/dedicated/search.d.ts +24 -0
- package/dist/commands/machines/dedicated/search.js +78 -0
- package/dist/commands/machines/dedicated/stat.d.ts +22 -0
- package/dist/commands/machines/dedicated/stat.js +92 -0
- package/dist/commands/machines/hosted/index.js +1 -1
- package/dist/commands/machines/hosted/list.js +1 -1
- package/dist/commands/machines/hosted/start.js +1 -1
- package/dist/commands/machines/services/declare.d.ts +24 -0
- package/dist/commands/machines/services/declare.js +73 -0
- package/dist/commands/machines/services/index.d.ts +15 -0
- package/dist/commands/machines/services/index.js +31 -0
- package/dist/commands/machines/services/ls.d.ts +17 -0
- package/dist/commands/machines/services/ls.js +60 -0
- package/dist/commands/machines/services/withdraw.d.ts +19 -0
- package/dist/commands/machines/services/withdraw.js +47 -0
- package/dist/commands/machines/share.d.ts +29 -0
- package/dist/commands/machines/share.js +100 -0
- package/dist/commands/machines/shared.d.ts +19 -0
- package/dist/commands/machines/shared.js +62 -0
- package/dist/commands/machines/shares.d.ts +16 -0
- package/dist/commands/machines/shares.js +69 -0
- package/dist/commands/machines/unshare.d.ts +19 -0
- package/dist/commands/machines/unshare.js +61 -0
- package/dist/commands/tasks/labels/create.d.ts +1 -0
- package/dist/commands/tasks/labels/create.js +3 -0
- package/dist/commands/views/create.d.ts +1 -0
- package/dist/commands/views/create.js +5 -0
- package/dist/lib/agent-home-workspace.d.ts +26 -0
- package/dist/lib/agent-home-workspace.js +174 -0
- package/dist/lib/agentic-stream.d.ts +135 -0
- package/dist/lib/agentic-stream.js +341 -6
- package/dist/lib/auth-storage.d.ts +4 -0
- package/dist/lib/auth-storage.js +33 -0
- package/dist/lib/computer-adoption.d.ts +55 -0
- package/dist/lib/computer-adoption.js +67 -0
- package/dist/lib/computer-consent.d.ts +13 -0
- package/dist/lib/computer-consent.js +33 -0
- package/dist/lib/computer-files.d.ts +239 -0
- package/dist/lib/computer-files.js +707 -0
- package/dist/lib/dedicated-copy.d.ts +45 -9
- package/dist/lib/dedicated-copy.js +141 -44
- package/dist/lib/dedicated-machines.d.ts +49 -0
- package/dist/lib/dedicated-machines.js +97 -6
- package/dist/lib/keychain.d.ts +1 -0
- package/dist/lib/keychain.js +67 -14
- package/dist/lib/label-scope.d.ts +12 -0
- package/dist/lib/label-scope.js +15 -1
- package/dist/lib/machine-audit.d.ts +34 -0
- package/dist/lib/machine-audit.js +50 -0
- package/dist/lib/machine-grants.d.ts +70 -0
- package/dist/lib/machine-grants.js +47 -0
- package/dist/lib/machine-services.d.ts +64 -0
- package/dist/lib/machine-services.js +60 -0
- package/dist/lib/refresh.d.ts +7 -0
- package/dist/lib/refresh.js +2 -1
- package/dist/lib/task-view-render.d.ts +2 -0
- package/dist/lib/task-view-render.js +1 -1
- package/dist/lib/views/vocabulary.d.ts +1 -1
- package/dist/lib/views/vocabulary.js +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +955 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +1070 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +7 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +955 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +1051 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +7 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +22143 -20728
- package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
- package/oclif.manifest.json +18855 -17260
- package/package.json +2 -2
package/bin/run.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Reuse V8's compiled code for this CLI's modules across runs. Every command
|
|
4
|
+
// compiles oclif plus a 2.4MB vendored data-provider bundle before it can do
|
|
5
|
+
// anything; with the cache that compile step is read back instead (Node >= 22.1;
|
|
6
|
+
// a no-op on older Node and on Bun, and NODE_DISABLE_COMPILE_CACHE=1 turns it
|
|
7
|
+
// off). The cache is keyed by file content, so a stale entry is never used.
|
|
8
|
+
try {
|
|
9
|
+
require('node:module').enableCompileCache?.();
|
|
10
|
+
} catch {
|
|
11
|
+
/* an optimisation must never be the reason a command cannot start */
|
|
12
|
+
}
|
|
13
|
+
|
|
3
14
|
const path = require('node:path');
|
|
4
15
|
const { spawnSync } = require('node:child_process');
|
|
5
16
|
const { findAlternateCli, missingLocalCommandBuild } = require('./dev-fallback');
|
package/dist/base-command.js
CHANGED
|
@@ -512,6 +512,7 @@ class BaseCommand extends core_1.Command {
|
|
|
512
512
|
const fresh = await (0, refresh_1.ensureFreshCliCredential)({
|
|
513
513
|
credential: this.resolvedCredential,
|
|
514
514
|
bareMode: this.bareMode,
|
|
515
|
+
reuseResolvedRead: true,
|
|
515
516
|
});
|
|
516
517
|
if (fresh.credential.token) {
|
|
517
518
|
this.resolvedCredential = fresh.credential;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dataService } from '@skrr-ai/data-provider';
|
|
2
2
|
import { BaseCommand } from '../../base-command';
|
|
3
|
-
import { type AgenticStreamResult, type ReconciledTerminal } from '../../lib/agentic-stream';
|
|
3
|
+
import { type AgenticStreamResult, type HandoverReading, type ReconciledTerminal } from '../../lib/agentic-stream';
|
|
4
4
|
/**
|
|
5
5
|
* Exit code for a run the USER stopped watching, by pressing Ctrl-C.
|
|
6
6
|
*
|
|
@@ -73,6 +73,62 @@ export declare function interruptedDetachMessage(input: {
|
|
|
73
73
|
* because a long tool call is silence, not a failure.
|
|
74
74
|
*/
|
|
75
75
|
export declare function reconcileTerminalFromMessages(messages: ReadonlyArray<Record<string, unknown>>, responseMessageId?: string): ReconciledTerminal | null;
|
|
76
|
+
/**
|
|
77
|
+
* Where a handed-over turn stands, read off its response message (OSK-13543).
|
|
78
|
+
*
|
|
79
|
+
* The server converges such a row to exactly one of two states, and only
|
|
80
|
+
* those two end the wait:
|
|
81
|
+
*
|
|
82
|
+
* - REPAIRED — `error` cleared and `metadata.agentic.deliveryRepair` written,
|
|
83
|
+
* by `sessionMessagePersister.repairDeliveryFailedMessage` on the machine's
|
|
84
|
+
* completed turn-end. The repair record is required, not just `error:
|
|
85
|
+
* false`: before the failure is saved a row can read `unfinished: false,
|
|
86
|
+
* error: false` from a daemon flush that never writes `error`, and calling
|
|
87
|
+
* that a success is the lie `recordHandedOverTurn` exists to prevent.
|
|
88
|
+
* - SETTLED FAILED — still `error: true`, but no longer carrying the
|
|
89
|
+
* hand-over reason (`lateDeliveryRepair.settleHandedOverFailure` moves it to
|
|
90
|
+
* `worker_replaced_daemon_failed` with the machine's own reason).
|
|
91
|
+
*
|
|
92
|
+
* Everything else — still failed with the hand-over reason, not yet written,
|
|
93
|
+
* not found — is `pending`. Pure, so the mapping is testable without a socket.
|
|
94
|
+
*/
|
|
95
|
+
export declare function handoverStateFromMessages(messages: ReadonlyArray<Record<string, unknown>>, responseMessageId?: string): HandoverReading;
|
|
96
|
+
/**
|
|
97
|
+
* How long `agents chat` waits for a handed-over turn by default (OSK-13543).
|
|
98
|
+
*
|
|
99
|
+
* Bounded above by the server's own hold: it quarantines the conversation for
|
|
100
|
+
* the worker job timeout plus a margin (30m + 5m, `conversationAdmissionClaim`)
|
|
101
|
+
* and after that stops treating the turn as possibly running, so waiting longer
|
|
102
|
+
* cannot observe anything the server would still act on. The default is well
|
|
103
|
+
* below that, because the cost of expiring is small and recoverable — the
|
|
104
|
+
* repair still lands on the record and `skrr messages list` shows it — while
|
|
105
|
+
* the cost of holding a terminal or a script is paid every minute. Ten minutes
|
|
106
|
+
* covers the repair observed in production (~90s after the failure) many times
|
|
107
|
+
* over and the long tail of a turn that was mid tool-call when the deploy hit.
|
|
108
|
+
*/
|
|
109
|
+
export declare const DEFAULT_HANDOVER_WAIT_SECONDS = 600;
|
|
110
|
+
/** The server's hold for a handed-over turn: 30m job timeout + 5m lease margin. */
|
|
111
|
+
export declare const MAX_HANDOVER_WAIT_SECONDS: number;
|
|
112
|
+
/**
|
|
113
|
+
* What to say when Ctrl-C stops a hand-over wait. There is nothing to cancel
|
|
114
|
+
* through — the server relaying the turn is gone — so the honest statement is
|
|
115
|
+
* that the CLI stopped watching and the machine may still finish.
|
|
116
|
+
*/
|
|
117
|
+
export declare function handoverInterruptMessage(input: {
|
|
118
|
+
sessionId: string;
|
|
119
|
+
conversationId?: string | null;
|
|
120
|
+
}): string;
|
|
121
|
+
/**
|
|
122
|
+
* What one Ctrl-C does, by state. Exported so the decision is testable
|
|
123
|
+
* without delivering a signal:
|
|
124
|
+
* - waiting on a handed-over turn → stop waiting at once (no cancel exists);
|
|
125
|
+
* - first interrupt of a live run → request a cancel, wait the grace;
|
|
126
|
+
* - any later interrupt → detach now.
|
|
127
|
+
*/
|
|
128
|
+
export declare function interruptAction(input: {
|
|
129
|
+
interrupts: number;
|
|
130
|
+
awaitingHandover: boolean;
|
|
131
|
+
}): 'stop-handover-wait' | 'cancel' | 'detach';
|
|
76
132
|
/**
|
|
77
133
|
* How to look at a turn the CLI is no longer watching.
|
|
78
134
|
*
|
|
@@ -168,6 +224,7 @@ export default class AgentsChat extends BaseCommand {
|
|
|
168
224
|
quick: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
169
225
|
jsonl: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
170
226
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
227
|
+
'repair-wait': import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
171
228
|
'approve-tools': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
172
229
|
'permission-mode': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
173
230
|
'local-skill': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.STREAM_LOST_EXIT_CODE = exports.DETACH_EXIT_CODE = void 0;
|
|
3
|
+
exports.MAX_HANDOVER_WAIT_SECONDS = exports.DEFAULT_HANDOVER_WAIT_SECONDS = exports.STREAM_LOST_EXIT_CODE = exports.DETACH_EXIT_CODE = void 0;
|
|
4
4
|
exports.streamedResultExitCode = streamedResultExitCode;
|
|
5
5
|
exports.interruptedDetachMessage = interruptedDetachMessage;
|
|
6
6
|
exports.reconcileTerminalFromMessages = reconcileTerminalFromMessages;
|
|
7
|
+
exports.handoverStateFromMessages = handoverStateFromMessages;
|
|
8
|
+
exports.handoverInterruptMessage = handoverInterruptMessage;
|
|
9
|
+
exports.interruptAction = interruptAction;
|
|
7
10
|
exports.inspectTurnCommand = inspectTurnCommand;
|
|
8
11
|
exports.noOutputFailureMessage = noOutputFailureMessage;
|
|
9
12
|
exports.noStreamJsonPayload = noStreamJsonPayload;
|
|
@@ -110,6 +113,9 @@ function reconcileTerminalFromMessages(messages, responseMessageId) {
|
|
|
110
113
|
// The reason exists on the row; it simply never reached the surface. Say
|
|
111
114
|
// it, rather than the transport error that masked it (OSK-7595).
|
|
112
115
|
error: failure.reason ?? 'The run failed. No reason was recorded on the message.',
|
|
116
|
+
// Provisional, not final: the server rewrites this row from the
|
|
117
|
+
// machine's own turn-end (OSK-13543).
|
|
118
|
+
...(agenticErrorSubtype(row) === agentic_stream_1.DAEMON_HANDOVER_REASON ? { handedOver: true } : {}),
|
|
113
119
|
};
|
|
114
120
|
}
|
|
115
121
|
const text = typeof row.text === 'string' ? row.text : '';
|
|
@@ -120,6 +126,101 @@ function reconcileTerminalFromMessages(messages, responseMessageId) {
|
|
|
120
126
|
return { status: 'completed', text };
|
|
121
127
|
return null;
|
|
122
128
|
}
|
|
129
|
+
function agenticRecord(row) {
|
|
130
|
+
const metadata = row.metadata;
|
|
131
|
+
if (!metadata || typeof metadata !== 'object')
|
|
132
|
+
return null;
|
|
133
|
+
const agentic = metadata.agentic;
|
|
134
|
+
return agentic && typeof agentic === 'object' ? agentic : null;
|
|
135
|
+
}
|
|
136
|
+
function agenticErrorSubtype(row) {
|
|
137
|
+
const subtype = agenticRecord(row)?.errorSubtype;
|
|
138
|
+
return typeof subtype === 'string' ? subtype : undefined;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Where a handed-over turn stands, read off its response message (OSK-13543).
|
|
142
|
+
*
|
|
143
|
+
* The server converges such a row to exactly one of two states, and only
|
|
144
|
+
* those two end the wait:
|
|
145
|
+
*
|
|
146
|
+
* - REPAIRED — `error` cleared and `metadata.agentic.deliveryRepair` written,
|
|
147
|
+
* by `sessionMessagePersister.repairDeliveryFailedMessage` on the machine's
|
|
148
|
+
* completed turn-end. The repair record is required, not just `error:
|
|
149
|
+
* false`: before the failure is saved a row can read `unfinished: false,
|
|
150
|
+
* error: false` from a daemon flush that never writes `error`, and calling
|
|
151
|
+
* that a success is the lie `recordHandedOverTurn` exists to prevent.
|
|
152
|
+
* - SETTLED FAILED — still `error: true`, but no longer carrying the
|
|
153
|
+
* hand-over reason (`lateDeliveryRepair.settleHandedOverFailure` moves it to
|
|
154
|
+
* `worker_replaced_daemon_failed` with the machine's own reason).
|
|
155
|
+
*
|
|
156
|
+
* Everything else — still failed with the hand-over reason, not yet written,
|
|
157
|
+
* not found — is `pending`. Pure, so the mapping is testable without a socket.
|
|
158
|
+
*/
|
|
159
|
+
function handoverStateFromMessages(messages, responseMessageId) {
|
|
160
|
+
const row = (responseMessageId && messages.find((m) => m?.messageId === responseMessageId)) ||
|
|
161
|
+
(!responseMessageId && [...messages].reverse().find((m) => m && m.isCreatedByUser === false));
|
|
162
|
+
if (!row)
|
|
163
|
+
return { state: 'pending' };
|
|
164
|
+
const failure = (0, data_provider_1.resolveMessageFailure)(row);
|
|
165
|
+
if (failure) {
|
|
166
|
+
if (agenticErrorSubtype(row) === agentic_stream_1.DAEMON_HANDOVER_REASON)
|
|
167
|
+
return { state: 'pending' };
|
|
168
|
+
return {
|
|
169
|
+
state: 'failed',
|
|
170
|
+
error: failure.reason ?? 'The run failed. No reason was recorded on the message.',
|
|
171
|
+
...(failure.answer ? { text: failure.answer } : {}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const repair = agenticRecord(row)?.deliveryRepair;
|
|
175
|
+
if (!repair || typeof repair !== 'object')
|
|
176
|
+
return { state: 'pending' };
|
|
177
|
+
// JSON over the wire makes this an ISO string; anything else is not shown.
|
|
178
|
+
const repairedAt = typeof repair.repairedAt === 'string' ? repair.repairedAt : undefined;
|
|
179
|
+
return {
|
|
180
|
+
state: 'repaired',
|
|
181
|
+
text: typeof row.text === 'string' ? row.text : '',
|
|
182
|
+
...(repairedAt ? { repairedAt } : {}),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* How long `agents chat` waits for a handed-over turn by default (OSK-13543).
|
|
187
|
+
*
|
|
188
|
+
* Bounded above by the server's own hold: it quarantines the conversation for
|
|
189
|
+
* the worker job timeout plus a margin (30m + 5m, `conversationAdmissionClaim`)
|
|
190
|
+
* and after that stops treating the turn as possibly running, so waiting longer
|
|
191
|
+
* cannot observe anything the server would still act on. The default is well
|
|
192
|
+
* below that, because the cost of expiring is small and recoverable — the
|
|
193
|
+
* repair still lands on the record and `skrr messages list` shows it — while
|
|
194
|
+
* the cost of holding a terminal or a script is paid every minute. Ten minutes
|
|
195
|
+
* covers the repair observed in production (~90s after the failure) many times
|
|
196
|
+
* over and the long tail of a turn that was mid tool-call when the deploy hit.
|
|
197
|
+
*/
|
|
198
|
+
exports.DEFAULT_HANDOVER_WAIT_SECONDS = 600;
|
|
199
|
+
/** The server's hold for a handed-over turn: 30m job timeout + 5m lease margin. */
|
|
200
|
+
exports.MAX_HANDOVER_WAIT_SECONDS = 35 * 60;
|
|
201
|
+
/**
|
|
202
|
+
* What to say when Ctrl-C stops a hand-over wait. There is nothing to cancel
|
|
203
|
+
* through — the server relaying the turn is gone — so the honest statement is
|
|
204
|
+
* that the CLI stopped watching and the machine may still finish.
|
|
205
|
+
*/
|
|
206
|
+
function handoverInterruptMessage(input) {
|
|
207
|
+
return (`Stopped waiting for run ${input.sessionId}. A platform update had restarted the server ` +
|
|
208
|
+
'relaying it, and your machine MAY STILL BE WORKING on it: if it finishes, its reply is ' +
|
|
209
|
+
'saved to the conversation. Nothing was cancelled. ' +
|
|
210
|
+
`Check with: ${inspectTurnCommand(input.conversationId)}`);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* What one Ctrl-C does, by state. Exported so the decision is testable
|
|
214
|
+
* without delivering a signal:
|
|
215
|
+
* - waiting on a handed-over turn → stop waiting at once (no cancel exists);
|
|
216
|
+
* - first interrupt of a live run → request a cancel, wait the grace;
|
|
217
|
+
* - any later interrupt → detach now.
|
|
218
|
+
*/
|
|
219
|
+
function interruptAction(input) {
|
|
220
|
+
if (input.awaitingHandover)
|
|
221
|
+
return 'stop-handover-wait';
|
|
222
|
+
return input.interrupts === 1 ? 'cancel' : 'detach';
|
|
223
|
+
}
|
|
123
224
|
/**
|
|
124
225
|
* How to look at a turn the CLI is no longer watching.
|
|
125
226
|
*
|
|
@@ -158,6 +259,9 @@ function noStreamJsonPayload(input) {
|
|
|
158
259
|
conversationId,
|
|
159
260
|
responseMessageId: result.responseMessageId,
|
|
160
261
|
status: result.status,
|
|
262
|
+
// The run first failed as handed over and the machine's reply was then
|
|
263
|
+
// repaired onto the record (OSK-13543).
|
|
264
|
+
...(result.repaired ? { repaired: true } : {}),
|
|
161
265
|
response: result.text,
|
|
162
266
|
// The model a local harness reported running (OSK-12111). Absent on a
|
|
163
267
|
// cloud turn and on a harness that reports none.
|
|
@@ -279,11 +383,30 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
279
383
|
'conversation; `message` says why. A `permission.resolved` says ' +
|
|
280
384
|
'how a local harness permission request was decided: `decidedBy` in words, and ' +
|
|
281
385
|
'`basis` — `person` (with `surface`), `mode` (with `mode`), `policy` or ' +
|
|
282
|
-
'`session-grant` — beside the wire `by`.'
|
|
386
|
+
'`session-grant` — beside the wire `by`. When a platform update restarts the server ' +
|
|
387
|
+
'relaying a turn your machine is still running, the run does not end at that failure: ' +
|
|
388
|
+
'`run.handed_over` (`code: "AGENT_WORKER_REPLACED"`, `reason: ' +
|
|
389
|
+
'"worker_replaced_daemon_running"`, `waitMs`, `deadlineAt`) says the CLI is waiting, ' +
|
|
390
|
+
'then exactly one terminal event follows — `run.completed` with `repaired: true` and ' +
|
|
391
|
+
'`result` (then `run.result` with `status: "completed"`, `repaired: true`, exit 0) when ' +
|
|
392
|
+
'the machine finished; `run.failed` with `handover.outcome: "machine_failed"` and the ' +
|
|
393
|
+
'machine\'s reason when it did not; or `run.failed` with `handover.outcome: "expired"` ' +
|
|
394
|
+
'and the original failure when --repair-wait ran out (exit 1 for both). Ctrl-C during ' +
|
|
395
|
+
'the wait emits `run.detached` with `handedOver: true` and exits 130.',
|
|
283
396
|
}),
|
|
284
397
|
json: core_1.Flags.boolean({
|
|
285
398
|
description: 'Emit the final one-shot response as JSON (use with --no-stream)',
|
|
286
399
|
}),
|
|
400
|
+
'repair-wait': core_1.Flags.integer({
|
|
401
|
+
description: 'Seconds to keep waiting when a platform update restarts the server relaying a turn ' +
|
|
402
|
+
'your machine is still running. The server repairs the reply onto the conversation ' +
|
|
403
|
+
'when the machine finishes, and the CLI prints it and exits 0; if the wait runs out ' +
|
|
404
|
+
'the original failure is reported. 0 ends at the failure immediately. At most the ' +
|
|
405
|
+
`server's own hold for such a turn (${exports.MAX_HANDOVER_WAIT_SECONDS}s).`,
|
|
406
|
+
default: exports.DEFAULT_HANDOVER_WAIT_SECONDS,
|
|
407
|
+
min: 0,
|
|
408
|
+
max: exports.MAX_HANDOVER_WAIT_SECONDS,
|
|
409
|
+
}),
|
|
287
410
|
'approve-tools': core_1.Flags.boolean({
|
|
288
411
|
description: 'Allow this turn to use permission-gated tools. In a terminal it asks per call; ' +
|
|
289
412
|
'with no terminal the flag itself is the approval for this turn, and each one is ' +
|
|
@@ -409,6 +532,21 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
409
532
|
if (flags.conversation && flags['thread-root']) {
|
|
410
533
|
this.error('--conversation and --thread-root are mutually exclusive.', { exit: 2 });
|
|
411
534
|
}
|
|
535
|
+
// The agent read that picks the turn's default workspace depends on nothing
|
|
536
|
+
// below, so it starts now and runs beside the `--thread-root`, `--task` and
|
|
537
|
+
// `--local-skill` reads instead of after them (each is a full round trip).
|
|
538
|
+
// Only the LOOKUP starts here: the default is committed just before the
|
|
539
|
+
// turn, so those reads still go out without a workspace header, as before.
|
|
540
|
+
// `--quick` never consulted it. Never rejects.
|
|
541
|
+
//
|
|
542
|
+
// OSK-13599 — and a server already observed to choose the agent's home
|
|
543
|
+
// workspace itself is not asked at all: that read existed only to pick the
|
|
544
|
+
// header, and it is a full round trip before every turn. Only the SERVER's
|
|
545
|
+
// capability is remembered, never the agent's workspace (agents move).
|
|
546
|
+
const serverChoosesWorkspace = !flags.quick && (0, agent_home_workspace_1.serverAppliesAgentHomeDefault)(this.config?.cacheDir, this.cliConfig.baseURL);
|
|
547
|
+
const homeWorkspace = flags.quick || serverChoosesWorkspace
|
|
548
|
+
? null
|
|
549
|
+
: (0, agent_home_workspace_1.lookupAgentHomeWorkspaceDefault)(args.agentId, (agentId) => data_provider_1.dataService.getAgentById({ agent_id: agentId }));
|
|
412
550
|
let threadConversationId = flags.conversation;
|
|
413
551
|
if (flags['thread-root']) {
|
|
414
552
|
try {
|
|
@@ -522,7 +660,7 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
522
660
|
const prompt = localSkills
|
|
523
661
|
? (0, local_skills_1.withSkillInvocations)(flags.prompt, localSkills.refs)
|
|
524
662
|
: flags.prompt;
|
|
525
|
-
|
|
663
|
+
(0, agent_home_workspace_1.commitAgentHomeWorkspaceDefault)(await homeWorkspace);
|
|
526
664
|
let started;
|
|
527
665
|
try {
|
|
528
666
|
started = await data_provider_1.dataService.startAgenticChat({
|
|
@@ -547,6 +685,18 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
547
685
|
// model is not supported."). The reader had to guess (OSK-4897).
|
|
548
686
|
this.handleApiError(err);
|
|
549
687
|
}
|
|
688
|
+
// OSK-13599 — the acceptance names the workspace the turn runs in. Adopt it
|
|
689
|
+
// for the rest of this turn (the stream's header, the message reads that
|
|
690
|
+
// reconcile its end, envelope replay) exactly as the pre-flight read's
|
|
691
|
+
// answer used to be adopted; a workspace the caller chose still wins, and
|
|
692
|
+
// an autonomous process still never takes one. It also tells us whether
|
|
693
|
+
// this server chooses the workspace itself, for the next turn.
|
|
694
|
+
const workspaceDefault = (0, agent_home_workspace_1.recordAgentHomeDefaultObservation)(this.config?.cacheDir, this.cliConfig.baseURL, started);
|
|
695
|
+
if (serverChoosesWorkspace && workspaceDefault === 'unsupported') {
|
|
696
|
+
this.warn('This server no longer chooses the agent’s home workspace, so this turn ran without ' +
|
|
697
|
+
'one. The next turn will select it again; pass --workspace to choose one now.');
|
|
698
|
+
}
|
|
699
|
+
(0, agent_home_workspace_1.commitAgentHomeWorkspaceDefault)(typeof started.workspaceId === 'string' && started.workspaceId ? started.workspaceId : null);
|
|
550
700
|
if (!started.sessionId) {
|
|
551
701
|
this.error('Agentic chat was accepted without a session ID; cannot attach the stream.', {
|
|
552
702
|
exit: 1,
|
|
@@ -564,6 +714,14 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
564
714
|
conversationId: started.conversationId,
|
|
565
715
|
...(flags['thread-root'] ? { threadRootMessageId: flags['thread-root'] } : {}),
|
|
566
716
|
responseMessageId: started.responseMessageId,
|
|
717
|
+
// OSK-13599 — where the turn runs and who chose it, when the server
|
|
718
|
+
// says (`request` | `agent_home`); absent against an older server.
|
|
719
|
+
...(workspaceDefault === 'supported'
|
|
720
|
+
? {
|
|
721
|
+
workspaceId: started.workspaceId ?? null,
|
|
722
|
+
workspaceSource: started.workspaceSource ?? null,
|
|
723
|
+
}
|
|
724
|
+
: {}),
|
|
567
725
|
agentId: args.agentId,
|
|
568
726
|
url: (0, web_url_1.agentUrlFrom)(this.cliConfig.baseURL, { id: args.agentId }),
|
|
569
727
|
});
|
|
@@ -593,6 +751,21 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
593
751
|
return reconcileTerminalFromMessages(messages, started.responseMessageId);
|
|
594
752
|
}
|
|
595
753
|
: undefined;
|
|
754
|
+
// OSK-13543 — a turn handed over on a deploy is waited out against the same
|
|
755
|
+
// record. Without a conversation there is no record to read, so no wait.
|
|
756
|
+
const awaitHandover = conversationId
|
|
757
|
+
? {
|
|
758
|
+
read: async () => {
|
|
759
|
+
const messages = (await data_provider_1.dataService.getMessagesByConvoId(conversationId));
|
|
760
|
+
if (!Array.isArray(messages))
|
|
761
|
+
return null;
|
|
762
|
+
return handoverStateFromMessages(messages, started.responseMessageId);
|
|
763
|
+
},
|
|
764
|
+
responseMessageId: started.responseMessageId,
|
|
765
|
+
conversationId,
|
|
766
|
+
maxWaitMs: (flags['repair-wait'] ?? exports.DEFAULT_HANDOVER_WAIT_SECONDS) * 1000,
|
|
767
|
+
}
|
|
768
|
+
: undefined;
|
|
596
769
|
const client = new agentic_stream_1.AgenticStreamClient({
|
|
597
770
|
baseURL: this.cliConfig.baseURL,
|
|
598
771
|
token: this.resolvedCredential.token,
|
|
@@ -602,6 +775,7 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
602
775
|
quiet,
|
|
603
776
|
approveTools: flags['approve-tools'],
|
|
604
777
|
reconcileTerminal,
|
|
778
|
+
awaitHandover,
|
|
605
779
|
replayEnvelopes: (from) => (0, api_fetch_1.apiFetch)(`/api/agentic/sessions/${encodeURIComponent(started.sessionId)}/envelopes?from=${from}&limit=500`, { credential: this.resolvedCredential, signal: AbortSignal.timeout(5000) }),
|
|
606
780
|
});
|
|
607
781
|
// OSK-2938 — Ctrl-C must be bounded, visible, and honest about what it
|
|
@@ -636,7 +810,35 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
636
810
|
let graceTimer = null;
|
|
637
811
|
const onInterrupt = () => {
|
|
638
812
|
interrupts += 1;
|
|
639
|
-
|
|
813
|
+
const action = interruptAction({ interrupts, awaitingHandover: client.awaitingHandover() });
|
|
814
|
+
if (action === 'stop-handover-wait') {
|
|
815
|
+
// Nothing to cancel and nothing to grace-wait for: stop at once.
|
|
816
|
+
if (!forcedDetachAnnounced) {
|
|
817
|
+
forcedDetachAnnounced = true;
|
|
818
|
+
const message = handoverInterruptMessage({
|
|
819
|
+
sessionId: started.sessionId,
|
|
820
|
+
conversationId: started.conversationId,
|
|
821
|
+
});
|
|
822
|
+
if (flags.jsonl) {
|
|
823
|
+
(0, node_fs_1.writeSync)(process.stdout.fd, `${JSON.stringify({
|
|
824
|
+
type: 'run.detached',
|
|
825
|
+
sessionId: started.sessionId,
|
|
826
|
+
conversationId: started.conversationId,
|
|
827
|
+
interrupted: true,
|
|
828
|
+
handedOver: true,
|
|
829
|
+
message,
|
|
830
|
+
})}\n`);
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
(0, node_fs_1.writeSync)(process.stderr.fd, `\n${message}\n`);
|
|
834
|
+
}
|
|
835
|
+
process.exitCode = exports.DETACH_EXIT_CODE;
|
|
836
|
+
}
|
|
837
|
+
client.close();
|
|
838
|
+
detach?.();
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
if (action === 'cancel') {
|
|
640
842
|
client.cancel();
|
|
641
843
|
if (!flags.jsonl) {
|
|
642
844
|
this.log(`\nCancelling run ${started.sessionId}… (waiting up to ${CANCEL_GRACE_MS / 1000}s; ` +
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr computer allow|deny|status` — the machine-local consent for the
|
|
4
|
+
* computer's new surfaces (§18/D11): the file surface and a person's own
|
|
5
|
+
* browser windows. Off by default; the daemon announces the answer as
|
|
6
|
+
* capabilities so the shell can say "this machine has not allowed the
|
|
7
|
+
* computer" instead of failing later.
|
|
8
|
+
*/
|
|
9
|
+
export default class Computer extends Command {
|
|
10
|
+
static description: string;
|
|
11
|
+
static args: {
|
|
12
|
+
action: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
13
|
+
};
|
|
14
|
+
static flags: {
|
|
15
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
static examples: string[];
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const computer_consent_1 = require("../../lib/computer-consent");
|
|
5
|
+
/**
|
|
6
|
+
* `skrr computer allow|deny|status` — the machine-local consent for the
|
|
7
|
+
* computer's new surfaces (§18/D11): the file surface and a person's own
|
|
8
|
+
* browser windows. Off by default; the daemon announces the answer as
|
|
9
|
+
* capabilities so the shell can say "this machine has not allowed the
|
|
10
|
+
* computer" instead of failing later.
|
|
11
|
+
*/
|
|
12
|
+
class Computer extends core_1.Command {
|
|
13
|
+
static description = 'Allow, deny, or show whether the computer may reach this machine — ' +
|
|
14
|
+
'its file surface and your own browser windows (off by default)';
|
|
15
|
+
static args = {
|
|
16
|
+
action: core_1.Args.string({
|
|
17
|
+
description: 'allow, deny, or status',
|
|
18
|
+
required: true,
|
|
19
|
+
options: ['allow', 'deny', 'status'],
|
|
20
|
+
ignoreStdin: true,
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
static flags = {
|
|
24
|
+
json: core_1.Flags.boolean({ description: 'Output JSON' }),
|
|
25
|
+
};
|
|
26
|
+
static examples = [
|
|
27
|
+
'<%= config.bin %> computer status',
|
|
28
|
+
'<%= config.bin %> computer allow',
|
|
29
|
+
'<%= config.bin %> computer deny',
|
|
30
|
+
'<%= config.bin %> computer status --json',
|
|
31
|
+
];
|
|
32
|
+
async run() {
|
|
33
|
+
const { args, flags } = await this.parse(Computer);
|
|
34
|
+
await (0, computer_consent_1.forwardComputerConsent)(this, [args.action, ...(flags.json ? ['--json'] : [])]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.default = Computer;
|
|
@@ -3,6 +3,7 @@ export default class LabelsCreate extends BaseCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
+
'allow-duplicate-name': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
6
7
|
name: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
8
|
color: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
9
|
description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -30,6 +30,7 @@ class LabelsCreate extends base_command_1.BaseCommand {
|
|
|
30
30
|
'group-exclusive': core_1.Flags.boolean({
|
|
31
31
|
description: 'Make this label an exclusive group: at most one of ITS children may be attached to an entity at a time. Attaching a second child auto-detaches the first.',
|
|
32
32
|
}),
|
|
33
|
+
...label_scope_1.allowDuplicateNameFlags,
|
|
33
34
|
};
|
|
34
35
|
async run() {
|
|
35
36
|
this.requireAuth();
|
|
@@ -58,6 +59,7 @@ class LabelsCreate extends base_command_1.BaseCommand {
|
|
|
58
59
|
...(applicableTo ? { applicableTo } : {}),
|
|
59
60
|
...(flags.parent ? { parentId: flags.parent } : {}),
|
|
60
61
|
...(flags['group-exclusive'] ? { groupExclusive: true } : {}),
|
|
62
|
+
...(flags['allow-duplicate-name'] ? { allowDuplicateName: true } : {}),
|
|
61
63
|
});
|
|
62
64
|
}
|
|
63
65
|
catch (err) {
|
|
@@ -6,6 +6,7 @@ export default class LabelsRestore extends BaseCommand {
|
|
|
6
6
|
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
7
|
};
|
|
8
8
|
static flags: {
|
|
9
|
+
'allow-duplicate-name': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
};
|
|
11
12
|
run(): Promise<void>;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
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
|
+
const label_scope_1 = require("../../lib/label-scope");
|
|
6
7
|
class LabelsRestore extends base_command_1.BaseCommand {
|
|
7
8
|
static description = 'Restore an archived label';
|
|
8
9
|
static examples = [
|
|
@@ -14,16 +15,18 @@ class LabelsRestore extends base_command_1.BaseCommand {
|
|
|
14
15
|
};
|
|
15
16
|
static flags = {
|
|
16
17
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
18
|
+
...label_scope_1.allowDuplicateNameFlags,
|
|
17
19
|
};
|
|
18
20
|
async run() {
|
|
19
21
|
this.requireAuth();
|
|
20
22
|
const { args, flags } = await this.parse(LabelsRestore);
|
|
21
23
|
// A live label with the same name may have been created while this one was
|
|
22
|
-
// archived; the server answers 409 `duplicate_name`
|
|
23
|
-
// `
|
|
24
|
+
// archived; the server answers 409 `duplicate_name` (same scope) or
|
|
25
|
+
// `duplicate_name_other_scope` in that case, which `handleApiError`
|
|
26
|
+
// surfaces verbatim.
|
|
24
27
|
let result;
|
|
25
28
|
try {
|
|
26
|
-
result = await data_provider_1.dataService.restoreLabel(args.id);
|
|
29
|
+
result = await data_provider_1.dataService.restoreLabel(args.id, flags['allow-duplicate-name'] ? { allowDuplicateName: true } : {});
|
|
27
30
|
}
|
|
28
31
|
catch (err) {
|
|
29
32
|
this.handleApiError(err);
|
|
@@ -6,6 +6,7 @@ export default class LabelsUpdate extends BaseCommand {
|
|
|
6
6
|
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
7
|
};
|
|
8
8
|
static flags: {
|
|
9
|
+
'allow-duplicate-name': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
12
|
color: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -36,6 +36,7 @@ class LabelsUpdate extends base_command_1.BaseCommand {
|
|
|
36
36
|
'expected-version': core_1.Flags.integer({
|
|
37
37
|
description: 'Optimistic-concurrency guard — fails with 409 if the label changed since you read it',
|
|
38
38
|
}),
|
|
39
|
+
...label_scope_1.allowDuplicateNameFlags,
|
|
39
40
|
};
|
|
40
41
|
async run() {
|
|
41
42
|
this.requireAuth();
|
|
@@ -78,6 +79,7 @@ class LabelsUpdate extends base_command_1.BaseCommand {
|
|
|
78
79
|
...(flags['expected-version'] !== undefined
|
|
79
80
|
? { expectedVersion: flags['expected-version'] }
|
|
80
81
|
: {}),
|
|
82
|
+
...(flags['allow-duplicate-name'] ? { allowDuplicateName: true } : {}),
|
|
81
83
|
});
|
|
82
84
|
}
|
|
83
85
|
catch (err) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr machines computer-adoption` — the share of daemons still on the old
|
|
4
|
+
* event families, which is the number retiring `browser:liveview:*` and
|
|
5
|
+
* `agent:terminal:*` waits on (OSK-13578). Admin only on the server: it
|
|
6
|
+
* describes the fleet, not the caller.
|
|
7
|
+
*/
|
|
8
|
+
export default class MachinesComputerAdoption extends BaseCommand {
|
|
9
|
+
static description: string;
|
|
10
|
+
static examples: string[];
|
|
11
|
+
static flags: {
|
|
12
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_command_1 = require("../../base-command");
|
|
5
|
+
const computer_adoption_1 = require("../../lib/computer-adoption");
|
|
6
|
+
/**
|
|
7
|
+
* `skrr machines computer-adoption` — the share of daemons still on the old
|
|
8
|
+
* event families, which is the number retiring `browser:liveview:*` and
|
|
9
|
+
* `agent:terminal:*` waits on (OSK-13578). Admin only on the server: it
|
|
10
|
+
* describes the fleet, not the caller.
|
|
11
|
+
*/
|
|
12
|
+
class MachinesComputerAdoption extends base_command_1.BaseCommand {
|
|
13
|
+
static description = 'Show how many daemons are still on the old browser live view / agent terminal event families instead of the computer protocol, and whether the retirement criterion is met (admin only)';
|
|
14
|
+
static examples = [
|
|
15
|
+
'<%= config.bin %> machines computer-adoption',
|
|
16
|
+
'<%= config.bin %> machines computer-adoption --json',
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
this.requireAuth();
|
|
23
|
+
const { flags } = await this.parse(MachinesComputerAdoption);
|
|
24
|
+
const read = await (0, computer_adoption_1.getComputerAdoption)();
|
|
25
|
+
if (flags.json) {
|
|
26
|
+
this.log(JSON.stringify(read, null, 2));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
for (const line of (0, computer_adoption_1.computerAdoptionLines)(read))
|
|
30
|
+
this.log(line);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.default = MachinesComputerAdoption;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr machines dedicated archive` — download a folder from the machine as a
|
|
4
|
+
* tar file.
|
|
5
|
+
*
|
|
6
|
+
* The machine builds one staged archive of the folder (prepare), the CLI reads
|
|
7
|
+
* it in chunks (read) and frees it (release — always, including on a failed or
|
|
8
|
+
* cancelled download). The local file is written beside its destination and
|
|
9
|
+
* renamed into place, the same discipline `cp` keeps, so a cancelled download
|
|
10
|
+
* never leaves a partial `<name>.tar` — only the machine's staged copy, which
|
|
11
|
+
* release then removes.
|
|
12
|
+
*/
|
|
13
|
+
export default class DedicatedMachinesArchive extends DedicatedLeaseCommand {
|
|
14
|
+
static summary: string;
|
|
15
|
+
static description: string;
|
|
16
|
+
static examples: string[];
|
|
17
|
+
static args: {
|
|
18
|
+
lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
19
|
+
path: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
20
|
+
destination: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
21
|
+
};
|
|
22
|
+
static flags: {
|
|
23
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
24
|
+
};
|
|
25
|
+
run(): Promise<void>;
|
|
26
|
+
}
|