@skrr-ai/cli 0.1.88 → 0.1.90
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/computer-adoption.d.ts +3 -2
- package/dist/commands/machines/computer-adoption.js +4 -3
- package/dist/commands/machines/dedicated/agent-browser.d.ts +25 -0
- package/dist/commands/machines/dedicated/agent-browser.js +65 -0
- package/dist/commands/machines/dedicated/exec.js +60 -14
- package/dist/commands/machines/dedicated/index.js +2 -0
- package/dist/commands/machines/dedicated/power/explain.d.ts +13 -0
- package/dist/commands/machines/dedicated/power/explain.js +36 -0
- package/dist/commands/machines/dedicated/power/index.d.ts +10 -0
- package/dist/commands/machines/dedicated/power/index.js +24 -0
- package/dist/commands/machines/dedicated/power/keep-awake.d.ts +20 -0
- package/dist/commands/machines/dedicated/power/keep-awake.js +69 -0
- package/dist/commands/machines/dedicated/power/set.d.ts +23 -0
- package/dist/commands/machines/dedicated/power/set.js +106 -0
- package/dist/commands/machines/dedicated/power/show.d.ts +13 -0
- package/dist/commands/machines/dedicated/power/show.js +36 -0
- package/dist/commands/machines/dedicated/show.js +17 -2
- package/dist/lib/commitments.d.ts +1 -1
- package/dist/lib/commitments.js +4 -2
- package/dist/lib/computer-adoption.d.ts +5 -0
- package/dist/lib/computer-adoption.js +6 -1
- package/dist/lib/computer-consent.d.ts +43 -1
- package/dist/lib/computer-consent.js +125 -4
- package/dist/lib/dedicated-keep-awake.d.ts +19 -0
- package/dist/lib/dedicated-keep-awake.js +64 -0
- package/dist/lib/dedicated-machines.d.ts +31 -0
- package/dist/lib/dedicated-machines.js +40 -0
- package/dist/lib/dedicated-power.d.ts +61 -0
- package/dist/lib/dedicated-power.js +243 -0
- package/dist/lib/exec-runtime-binary.d.ts +3 -1
- package/dist/lib/exec-runtime-binary.js +16 -2
- package/dist/lib/format.d.ts +13 -0
- package/dist/lib/format.js +51 -0
- package/dist/lib/keychain.js +3 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +135 -8
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +166 -16
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dedicatedRuntimeBusyWire.d.ts +107 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dedicatedRuntimeBusyWire.js +151 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dedicatedRuntimeWakeWire.d.ts +115 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dedicatedRuntimeWakeWire.js +117 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +8 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +31 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +149 -9
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +135 -8
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +161 -13
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dedicatedRuntimeBusyWire.d.ts +107 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dedicatedRuntimeBusyWire.js +147 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dedicatedRuntimeWakeWire.d.ts +115 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dedicatedRuntimeWakeWire.js +113 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +31 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +144 -8
- package/dist/node_modules/@skrr-ai/auth-core/package.json +21 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +6730 -6366
- package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
- package/oclif.manifest.json +6303 -5852
- package/package.json +2 -2
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setDedicatedPowerPolicy = setDedicatedPowerPolicy;
|
|
4
|
+
exports.describePolicyWords = describePolicyWords;
|
|
5
|
+
exports.describePowerPolicy = describePowerPolicy;
|
|
6
|
+
exports.getDedicatedPowerSummary = getDedicatedPowerSummary;
|
|
7
|
+
exports.describePowerDecisionLines = describePowerDecisionLines;
|
|
8
|
+
exports.describeLastAutoStopLines = describeLastAutoStopLines;
|
|
9
|
+
exports.describeLastWakeDecisionLines = describeLastWakeDecisionLines;
|
|
10
|
+
exports.describePowerSummary = describePowerSummary;
|
|
11
|
+
exports.describePowerExplanation = describePowerExplanation;
|
|
12
|
+
exports.describeBudget = describeBudget;
|
|
13
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
14
|
+
const api_fetch_1 = require("./api-fetch");
|
|
15
|
+
const format_1 = require("./format");
|
|
16
|
+
const dedicated_machines_1 = require("./dedicated-machines");
|
|
17
|
+
function bodyOf(err) {
|
|
18
|
+
const raw = err?.body;
|
|
19
|
+
if (typeof raw !== 'string')
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Set the policy. An ineligible `on_demand` is a RESULT, not an exception: the
|
|
31
|
+
* refusal is the answer the caller must explain (reason + fix). Every other
|
|
32
|
+
* failure is rethrown for the command's usual error handling.
|
|
33
|
+
*/
|
|
34
|
+
async function setDedicatedPowerPolicy(leaseId, request, scope = {}) {
|
|
35
|
+
try {
|
|
36
|
+
const response = await (0, api_fetch_1.apiFetch)((0, dedicated_machines_1.withDedicatedScope)(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/power`, scope), { method: 'PUT', body: request });
|
|
37
|
+
return { ok: true, response };
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const body = bodyOf(err);
|
|
41
|
+
if (err?.status === 409 && body?.code === 'ON_DEMAND_INELIGIBLE') {
|
|
42
|
+
const reason = String(body.reason ?? '');
|
|
43
|
+
// The vocabulary is shared; the server's words win, the shared copy backs them up.
|
|
44
|
+
const copy = (0, data_provider_1.describeOnDemandIneligibility)(reason);
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
refusal: {
|
|
48
|
+
error: String(body.error ?? 'on_demand is not available for this machine yet.'),
|
|
49
|
+
code: 'ON_DEMAND_INELIGIBLE',
|
|
50
|
+
reason: reason,
|
|
51
|
+
detail: String(body.detail ?? copy?.detail ?? ''),
|
|
52
|
+
fix: String(body.fix ?? copy?.fix ?? ''),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The policy in words. `on_demand` promises a stop only when the platform stops idle machines at all
|
|
61
|
+
* (`autoStopMode`); an unknown mode is never read as on, so the sentence never promises one.
|
|
62
|
+
*/
|
|
63
|
+
function describePolicyWords(policy, idleAfterMinutes, autoStopMode) {
|
|
64
|
+
if (policy === 'on_demand') {
|
|
65
|
+
if (autoStopMode === 'on' || autoStopMode === 'canary') {
|
|
66
|
+
return `on_demand (stops after ${idleAfterMinutes} minutes idle)`;
|
|
67
|
+
}
|
|
68
|
+
// Unknown is neutral, never a claim in either direction.
|
|
69
|
+
return autoStopMode === 'off'
|
|
70
|
+
? `on_demand (idle limit ${idleAfterMinutes} minutes; automatic stop is not turned on yet, so nothing stops it)`
|
|
71
|
+
: `on_demand (stops after ${idleAfterMinutes} minutes idle when automatic stop is on)`;
|
|
72
|
+
}
|
|
73
|
+
if (policy === 'manual')
|
|
74
|
+
return 'manual (stops and starts only when you do)';
|
|
75
|
+
return policy;
|
|
76
|
+
}
|
|
77
|
+
function describePowerPolicy(power, autoStopMode) {
|
|
78
|
+
const base = describePolicyWords(power.policy, power.idleAfterMinutes, autoStopMode);
|
|
79
|
+
return power.policyStored ? base : `${base} (default; never set)`;
|
|
80
|
+
}
|
|
81
|
+
/* ---- power show / explain (V5) ---------------------------------------------- */
|
|
82
|
+
/** `GET /api/machines/dedicated/:leaseId/power` — the read-only power card. */
|
|
83
|
+
function getDedicatedPowerSummary(leaseId, scope = {}) {
|
|
84
|
+
return (0, api_fetch_1.apiFetch)((0, dedicated_machines_1.withDedicatedScope)(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/power`, scope));
|
|
85
|
+
}
|
|
86
|
+
/** The stay/stop/wake decision in words: the shared reason copy backs the server's detail. */
|
|
87
|
+
function describePowerDecisionLines(decision) {
|
|
88
|
+
const copy = (0, data_provider_1.describePowerReason)(decision.reason);
|
|
89
|
+
const VERBS = { stay: 'Staying on', stop: 'Stopped', wake: 'Wake' };
|
|
90
|
+
const verb = VERBS[decision.question] ?? decision.question;
|
|
91
|
+
return {
|
|
92
|
+
// The headline is the human label only; the reason token lives in `--json` and `power explain`.
|
|
93
|
+
// With no shared copy and no server detail there is no sentence, so the token is the last resort.
|
|
94
|
+
headline: decision.detail || copy?.detail ? verb : `${verb}: ${decision.reason}`,
|
|
95
|
+
detail: decision.detail || copy?.detail || '',
|
|
96
|
+
fix: decision.fix ?? copy?.fix ?? null,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The last automatic stop and how it was judged (A3). Every word comes from the shared copy
|
|
101
|
+
* (`describeAutoStopVerdict`, `describeMachineCheckpointOutcome`); nothing is composed here.
|
|
102
|
+
* `verdict: null` is "not judged yet", and is said so: it is never rendered as clean. A sentence
|
|
103
|
+
* the decision text already carries (the server appends it while the machine is stopped) is not
|
|
104
|
+
* repeated.
|
|
105
|
+
*/
|
|
106
|
+
function describeLastAutoStopLines(last, decisionDetail = '', when = {}) {
|
|
107
|
+
if (!last)
|
|
108
|
+
return { lines: [], fix: null };
|
|
109
|
+
const lines = [];
|
|
110
|
+
const verdict = (0, data_provider_1.describeAutoStopVerdict)(last.verdict, { evidence: last.evidence });
|
|
111
|
+
const atText = last.at ? ` at ${(0, format_1.formatWhen)(last.at, when)}` : '';
|
|
112
|
+
lines.push(`Auto stop: ${verdict ? `${verdict.label} (${verdict.verdict})` : 'not checked yet'}${atText}`);
|
|
113
|
+
const checkpoint = (0, data_provider_1.describeMachineCheckpointOutcome)(last.checkpointOutcome, { action: 'stop' });
|
|
114
|
+
if (checkpoint && !decisionDetail.includes(checkpoint.sentence)) {
|
|
115
|
+
lines.push(` ${checkpoint.sentence}`);
|
|
116
|
+
}
|
|
117
|
+
if (verdict && last.verdict !== 'clean') {
|
|
118
|
+
if (!decisionDetail.includes(verdict.detail))
|
|
119
|
+
lines.push(` ${verdict.detail}`);
|
|
120
|
+
return { lines, fix: verdict.fix };
|
|
121
|
+
}
|
|
122
|
+
return { lines, fix: null };
|
|
123
|
+
}
|
|
124
|
+
function formatHours(hours) {
|
|
125
|
+
return `${Math.round(hours * 100) / 100}h`;
|
|
126
|
+
}
|
|
127
|
+
function formatUsage(usage) {
|
|
128
|
+
if (!usage.available)
|
|
129
|
+
return `unavailable (${usage.reason})`;
|
|
130
|
+
const cost = String(usage.currency).toLowerCase() === 'usd'
|
|
131
|
+
? (0, dedicated_machines_1.formatUsdCents)(usage.costCents)
|
|
132
|
+
: `${usage.cost.toFixed(2)} ${String(usage.currency).toUpperCase()}`;
|
|
133
|
+
return `${formatHours(usage.hours)}, ${cost} in ${usage.month}${usage.openInterval ? ' (still running)' : ''}`;
|
|
134
|
+
}
|
|
135
|
+
function formatHolder(h, when) {
|
|
136
|
+
if (h.kind === 'unreadable') {
|
|
137
|
+
return `cannot see — staying on${h.detail ? ` (${h.detail})` : ''}`;
|
|
138
|
+
}
|
|
139
|
+
return [h.kind, h.ref, h.since ? `since ${(0, format_1.formatWhen)(h.since, when)}` : null]
|
|
140
|
+
.filter(Boolean)
|
|
141
|
+
.join(' · ');
|
|
142
|
+
}
|
|
143
|
+
/** The latest wake decision and who asked, as lines; empty when there is none. */
|
|
144
|
+
function describeLastWakeDecisionLines(wake, when = {}) {
|
|
145
|
+
if (!wake)
|
|
146
|
+
return [];
|
|
147
|
+
// The sentence is the answer. The reason token and the transport ("http") are internals; who asked is
|
|
148
|
+
// named only when it is not the person reading this.
|
|
149
|
+
const asked = wake.source && wake.source !== 'person'
|
|
150
|
+
? ` Asked by ${wake.source}${wake.sourceRef ? ` ${wake.sourceRef}` : ''}.`
|
|
151
|
+
: '';
|
|
152
|
+
const copy = (0, data_provider_1.describePowerReason)(wake.reason);
|
|
153
|
+
const sentence = wake.detail || copy?.detail || wake.reason;
|
|
154
|
+
const lines = [
|
|
155
|
+
`Wake: ${sentence}${asked}${wake.at ? ` (${(0, format_1.formatWhen)(wake.at, when)})` : ''}`,
|
|
156
|
+
];
|
|
157
|
+
if (wake.fix)
|
|
158
|
+
lines.push(` ${wake.fix}`);
|
|
159
|
+
return lines;
|
|
160
|
+
}
|
|
161
|
+
/** The lines of `power show`. */
|
|
162
|
+
function describePowerSummary(summary, when = {}) {
|
|
163
|
+
const { power } = summary;
|
|
164
|
+
const lines = [];
|
|
165
|
+
lines.push(`Dedicated Runtime ${summary.leaseId}`);
|
|
166
|
+
lines.push(`State: ${summary.state}${summary.stateSince ? ` since ${(0, format_1.formatWhen)(summary.stateSince, when)}` : ''}`);
|
|
167
|
+
const policy = describePolicyWords(power.policy, power.idleAfterMinutes, summary.autoStopMode);
|
|
168
|
+
lines.push(`Policy: ${policy}`);
|
|
169
|
+
if (power.keepAwakeUntil)
|
|
170
|
+
lines.push(`Keep awake until: ${(0, format_1.formatWhen)(power.keepAwakeUntil, when)}`);
|
|
171
|
+
if (power.budgetHoursPerMonth !== null)
|
|
172
|
+
lines.push(`Budget: ${power.budgetHoursPerMonth} hours per month`);
|
|
173
|
+
if (summary.decision) {
|
|
174
|
+
const d = describePowerDecisionLines(summary.decision);
|
|
175
|
+
lines.push(`Decision: ${d.headline}`);
|
|
176
|
+
if (d.detail)
|
|
177
|
+
lines.push(` ${d.detail}`);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const none = (0, data_provider_1.describeNoPowerDecision)(summary);
|
|
181
|
+
if (none) {
|
|
182
|
+
lines.push(`Decision: ${none.headline}`);
|
|
183
|
+
lines.push(` ${none.detail}`);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
lines.push('Decision: nothing to explain for this state');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
lines.push(...describeLastAutoStopLines(summary.lastAutoStop, summary.decision?.detail, when).lines);
|
|
190
|
+
lines.push(...describeLastWakeDecisionLines(summary.lastWakeDecision, when));
|
|
191
|
+
if (summary.holders === null) {
|
|
192
|
+
lines.push('Holders: not checked (the machine is not running)');
|
|
193
|
+
}
|
|
194
|
+
else if (summary.holders.length === 0) {
|
|
195
|
+
lines.push('Holders: none — idle');
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
lines.push('Holders:');
|
|
199
|
+
for (const h of summary.holders)
|
|
200
|
+
lines.push(` - ${formatHolder(h, when)}`);
|
|
201
|
+
}
|
|
202
|
+
lines.push(`This month: ${formatUsage(summary.usage)}`);
|
|
203
|
+
lines.push(`Next expected wake: ${(summary.nextExpectedWake ? (0, format_1.formatWhen)(summary.nextExpectedWake, when) : null) ??
|
|
204
|
+
(summary.nextExpectedWakeUnavailable ? 'unavailable' : 'none scheduled')}`);
|
|
205
|
+
lines.push(`Last wake: ${summary.lastWake
|
|
206
|
+
? `${summary.lastWake.at ? (0, format_1.formatWhen)(summary.lastWake.at, when) : 'unknown time'}${summary.lastWake.source ? ` (${summary.lastWake.source})` : ''}`
|
|
207
|
+
: 'none'}`);
|
|
208
|
+
return lines;
|
|
209
|
+
}
|
|
210
|
+
/** The lines of `power explain`; the fix is always last. */
|
|
211
|
+
function describePowerExplanation(summary, when = {}) {
|
|
212
|
+
const lines = [];
|
|
213
|
+
if (!summary.decision) {
|
|
214
|
+
const none = (0, data_provider_1.describeNoPowerDecision)(summary);
|
|
215
|
+
lines.push(none ? `${none.headline}. ${none.detail}` : `Nothing to explain for ${summary.leaseId}.`);
|
|
216
|
+
lines.push(...describeLastWakeDecisionLines(summary.lastWakeDecision, when));
|
|
217
|
+
return lines;
|
|
218
|
+
}
|
|
219
|
+
const d = summary.decision;
|
|
220
|
+
const text = describePowerDecisionLines(d);
|
|
221
|
+
const wakeLines = describeLastWakeDecisionLines(summary.lastWakeDecision, when);
|
|
222
|
+
lines.push(`Question: ${d.question}`);
|
|
223
|
+
lines.push(`Gate: ${d.gate}`);
|
|
224
|
+
lines.push(`Reason: ${d.reason}`);
|
|
225
|
+
if (d.at)
|
|
226
|
+
lines.push(`At: ${(0, format_1.formatWhen)(d.at, when)}`);
|
|
227
|
+
if (text.detail)
|
|
228
|
+
lines.push(`Detail: ${text.detail}`);
|
|
229
|
+
const stop = describeLastAutoStopLines(summary.lastAutoStop, d.detail, when);
|
|
230
|
+
lines.push(...stop.lines);
|
|
231
|
+
lines.push(...wakeLines);
|
|
232
|
+
// A false stop's fix replaces nothing: the decision's own fix is still last-but-one.
|
|
233
|
+
if (text.fix && text.fix !== stop.fix)
|
|
234
|
+
lines.push(`Fix: ${text.fix}`);
|
|
235
|
+
if (stop.fix)
|
|
236
|
+
lines.push(`Fix: ${stop.fix}`);
|
|
237
|
+
return lines;
|
|
238
|
+
}
|
|
239
|
+
function describeBudget(power) {
|
|
240
|
+
return power.budgetHoursPerMonth == null
|
|
241
|
+
? 'Monthly compute-hour budget: none.'
|
|
242
|
+
: `Monthly compute-hour budget: ${power.budgetHoursPerMonth} hours. At the budget, automatic starts are refused; a running machine is never stopped by it and you can still start it yourself.`;
|
|
243
|
+
}
|
|
@@ -86,7 +86,9 @@ export type RuntimeBinaryOutput = {
|
|
|
86
86
|
* post-condition probes, where output is evidence rather than user-facing
|
|
87
87
|
* command output.
|
|
88
88
|
*/
|
|
89
|
-
export declare function execRuntimeBinaryCapture(args: string[]
|
|
89
|
+
export declare function execRuntimeBinaryCapture(args: string[], opts?: {
|
|
90
|
+
timeoutMs?: number;
|
|
91
|
+
}): Promise<RuntimeBinaryOutput>;
|
|
90
92
|
/**
|
|
91
93
|
* Check an installed daemon's top-level help before forwarding a command that
|
|
92
94
|
* was added after older daemon releases shipped. A false result is useful to
|
|
@@ -251,7 +251,7 @@ function execRuntimeBinary(args, opts = {}) {
|
|
|
251
251
|
* post-condition probes, where output is evidence rather than user-facing
|
|
252
252
|
* command output.
|
|
253
253
|
*/
|
|
254
|
-
function execRuntimeBinaryCapture(args) {
|
|
254
|
+
function execRuntimeBinaryCapture(args, opts = {}) {
|
|
255
255
|
return new Promise((resolve, reject) => {
|
|
256
256
|
const bin = findRuntimeBinary();
|
|
257
257
|
if (!bin) {
|
|
@@ -279,8 +279,22 @@ function execRuntimeBinaryCapture(args) {
|
|
|
279
279
|
child.stderr?.on('data', (chunk) => {
|
|
280
280
|
stderr = append(stderr, chunk);
|
|
281
281
|
});
|
|
282
|
-
|
|
282
|
+
// A probe is a read: past its bound it is abandoned (and the child
|
|
283
|
+
// stopped) rather than allowed to stall the command that asked.
|
|
284
|
+
const timer = opts.timeoutMs && opts.timeoutMs > 0
|
|
285
|
+
? setTimeout(() => {
|
|
286
|
+
child.kill('SIGTERM');
|
|
287
|
+
reject(new Error('runtime-binary-probe-timeout'));
|
|
288
|
+
}, opts.timeoutMs)
|
|
289
|
+
: null;
|
|
290
|
+
child.on('error', (error) => {
|
|
291
|
+
if (timer)
|
|
292
|
+
clearTimeout(timer);
|
|
293
|
+
reject(error);
|
|
294
|
+
});
|
|
283
295
|
child.on('exit', (code, signal) => {
|
|
296
|
+
if (timer)
|
|
297
|
+
clearTimeout(timer);
|
|
284
298
|
resolve({
|
|
285
299
|
code: signal ? 128 + signalNumber(signal) : (code ?? 0),
|
|
286
300
|
stdout,
|
package/dist/lib/format.d.ts
CHANGED
|
@@ -98,3 +98,16 @@ export declare function localTime(value: unknown): string;
|
|
|
98
98
|
* rather than print an empty parenthesis.
|
|
99
99
|
*/
|
|
100
100
|
export declare function localZone(at?: Date): string;
|
|
101
|
+
export type WhenOptions = {
|
|
102
|
+
/** The reference instant; injectable so output is deterministic in tests. */
|
|
103
|
+
now?: Date;
|
|
104
|
+
/** IANA zone to render in; defaults to the reader's own. */
|
|
105
|
+
timeZone?: string;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* A timestamp for a person: local time plus how far away it is, `07:38 (2h ago)`
|
|
109
|
+
* or `09:39 (in 1h 59m)`. The date is added only when it is not today in the
|
|
110
|
+
* rendered zone (`2026-10-07 22:10 (11h ago)`). Machine output (`--json`) keeps
|
|
111
|
+
* the raw ISO string; this is for text only. Unparseable input is returned as is.
|
|
112
|
+
*/
|
|
113
|
+
export declare function formatWhen(value: unknown, options?: WhenOptions): string;
|
package/dist/lib/format.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.renderTable = renderTable;
|
|
|
9
9
|
exports.localStamp = localStamp;
|
|
10
10
|
exports.localTime = localTime;
|
|
11
11
|
exports.localZone = localZone;
|
|
12
|
+
exports.formatWhen = formatWhen;
|
|
12
13
|
/**
|
|
13
14
|
* Code points that occupy TWO terminal columns (Unicode East Asian Wide and
|
|
14
15
|
* Fullwidth), plus the emoji blocks terminals render double-width.
|
|
@@ -259,3 +260,53 @@ function localZone(at = new Date()) {
|
|
|
259
260
|
const abs = Math.abs(offset);
|
|
260
261
|
return `UTC${sign}${`${Math.floor(abs / 60)}`.padStart(2, '0')}:${`${abs % 60}`.padStart(2, '0')}`;
|
|
261
262
|
}
|
|
263
|
+
function zonedParts(date, timeZone) {
|
|
264
|
+
const parts = new Intl.DateTimeFormat('en-CA', {
|
|
265
|
+
timeZone,
|
|
266
|
+
year: 'numeric',
|
|
267
|
+
month: '2-digit',
|
|
268
|
+
day: '2-digit',
|
|
269
|
+
hour: '2-digit',
|
|
270
|
+
minute: '2-digit',
|
|
271
|
+
hourCycle: 'h23',
|
|
272
|
+
}).formatToParts(date);
|
|
273
|
+
const out = {};
|
|
274
|
+
for (const p of parts)
|
|
275
|
+
out[p.type] = p.value;
|
|
276
|
+
return out;
|
|
277
|
+
}
|
|
278
|
+
function relativeSpan(ms) {
|
|
279
|
+
const minutes = Math.floor(ms / 60_000);
|
|
280
|
+
if (minutes < 1)
|
|
281
|
+
return 'under a minute';
|
|
282
|
+
if (minutes < 60)
|
|
283
|
+
return `${minutes}m`;
|
|
284
|
+
const hours = Math.floor(minutes / 60);
|
|
285
|
+
if (hours < 24)
|
|
286
|
+
return minutes % 60 ? `${hours}h ${minutes % 60}m` : `${hours}h`;
|
|
287
|
+
const days = Math.floor(hours / 24);
|
|
288
|
+
return hours % 24 ? `${days}d ${hours % 24}h` : `${days}d`;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* A timestamp for a person: local time plus how far away it is, `07:38 (2h ago)`
|
|
292
|
+
* or `09:39 (in 1h 59m)`. The date is added only when it is not today in the
|
|
293
|
+
* rendered zone (`2026-10-07 22:10 (11h ago)`). Machine output (`--json`) keeps
|
|
294
|
+
* the raw ISO string; this is for text only. Unparseable input is returned as is.
|
|
295
|
+
*/
|
|
296
|
+
function formatWhen(value, options = {}) {
|
|
297
|
+
if (value === null || value === undefined || value === '')
|
|
298
|
+
return '-';
|
|
299
|
+
const date = value instanceof Date ? value : new Date(typeof value === 'number' ? value : String(value));
|
|
300
|
+
if (Number.isNaN(date.getTime()))
|
|
301
|
+
return String(value);
|
|
302
|
+
const now = options.now ?? new Date();
|
|
303
|
+
const at = zonedParts(date, options.timeZone);
|
|
304
|
+
const ref = zonedParts(now, options.timeZone);
|
|
305
|
+
const sameDay = at.year === ref.year && at.month === ref.month && at.day === ref.day;
|
|
306
|
+
const clock = `${at.hour}:${at.minute}`;
|
|
307
|
+
const stamp = sameDay ? clock : `${at.year}-${at.month}-${at.day} ${clock}`;
|
|
308
|
+
const delta = date.getTime() - now.getTime();
|
|
309
|
+
const span = relativeSpan(Math.abs(delta));
|
|
310
|
+
const relative = span === 'under a minute' ? 'just now' : delta >= 0 ? `in ${span}` : `${span} ago`;
|
|
311
|
+
return `${stamp} (${relative})`;
|
|
312
|
+
}
|
package/dist/lib/keychain.js
CHANGED
|
@@ -150,6 +150,9 @@ function setActiveProfile(profile) {
|
|
|
150
150
|
_profileResolved = true;
|
|
151
151
|
if (profile && profile.length > 0)
|
|
152
152
|
_activeProfile = profile;
|
|
153
|
+
// The machineUuid the CLI sends (first-party harness broker) is per profile,
|
|
154
|
+
// exactly as the daemon's is (OSK-13845); both read the same persisted choice.
|
|
155
|
+
(0, auth_core_1.setMachineUuidProfile)(_activeProfile);
|
|
153
156
|
}
|
|
154
157
|
/**
|
|
155
158
|
* Whether the profile has been chosen for this process.
|
|
@@ -33,6 +33,22 @@ export declare const COMPUTER_EVENTS: {
|
|
|
33
33
|
readonly attach: "computer:attach";
|
|
34
34
|
/** client → API: leave. */
|
|
35
35
|
readonly detach: "computer:detach";
|
|
36
|
+
/**
|
|
37
|
+
* client → API: read the whole manifest again ON THE SAME ATTACHMENT, after
|
|
38
|
+
* a missed delta or a `stale_epoch` (OSK-13815). The ack carries what an
|
|
39
|
+
* attach's does — the attachment id (unchanged), the manifest and the
|
|
40
|
+
* operations — or a typed refusal; `not_found` means the attachment is gone
|
|
41
|
+
* and only then does the client attach afresh. Re-reading state must never
|
|
42
|
+
* change who the reader is: a control lease is bound to the attachment
|
|
43
|
+
* (`holderAttachmentId`), and a resync that detached and attached again
|
|
44
|
+
* orphaned the hold the person had just taken. Answered by the API, which
|
|
45
|
+
* renews the binding with the daemon exactly as its periodic re-check does
|
|
46
|
+
* (a repeated `daemon:computer:attach` with the same id), so it has no
|
|
47
|
+
* daemon mirror and needs no daemon capability. An API that predates it
|
|
48
|
+
* never answers; the client bounds the wait and falls back to attaching
|
|
49
|
+
* afresh.
|
|
50
|
+
*/
|
|
51
|
+
readonly resync: "computer:resync";
|
|
36
52
|
/** API → client: this attachment was detached, with the reason (revoked, stale generation, …). */
|
|
37
53
|
readonly detached: "computer:detached";
|
|
38
54
|
/** API → client: a whole manifest, or a revisioned delta. */
|
|
@@ -162,6 +178,22 @@ export declare const DAEMON_COMPUTER_EVENT_NAMES: readonly DaemonComputerEventNa
|
|
|
162
178
|
export declare const DAEMON_COMPUTER_REPORT_EVENT_KEYS: readonly ["ack", "manifest", "data", "activity", "detached", "filesChanged", "diagnostics"];
|
|
163
179
|
/** The API → client events; every other `computer:*` event flows client → API. */
|
|
164
180
|
export declare const COMPUTER_SERVER_EVENT_KEYS: readonly ["detached", "manifest", "data", "activity", "filesChanged"];
|
|
181
|
+
/**
|
|
182
|
+
* Client → API requests the API answers itself, with no `daemon:computer:*`
|
|
183
|
+
* mirror: every other client request is relayed to the daemon under the same
|
|
184
|
+
* key. `resync` renews the binding through the daemon's existing attach
|
|
185
|
+
* (OSK-13815).
|
|
186
|
+
*/
|
|
187
|
+
export declare const COMPUTER_API_ANSWERED_EVENT_KEYS: readonly ["resync"];
|
|
188
|
+
/** How long the client waits for a `computer:resync` answer before attaching afresh (an API that predates it never answers). */
|
|
189
|
+
export declare const COMPUTER_RESYNC_TIMEOUT_MS = 12000;
|
|
190
|
+
/**
|
|
191
|
+
* How long a client holds a manifest delta that arrived AHEAD of its
|
|
192
|
+
* predecessor before reading the hole as a loss (OSK-13815). Deltas fan out
|
|
193
|
+
* through a relay of several replicas; a reorder is milliseconds, a loss is
|
|
194
|
+
* forever, and only a loss needs the whole manifest again.
|
|
195
|
+
*/
|
|
196
|
+
export declare const COMPUTER_MANIFEST_REORDER_WAIT_MS = 300;
|
|
165
197
|
/**
|
|
166
198
|
* One capability per wire addition, each withheld positively at dispatch: the
|
|
167
199
|
* API never sends a computer event to a daemon that did not announce the
|
|
@@ -263,6 +295,42 @@ export declare const COMPUTER_CAPABILITIES: {
|
|
|
263
295
|
* browser; nothing is proxied to it. Announced by the relay that serves it.
|
|
264
296
|
*/
|
|
265
297
|
readonly declaredServices: "computer_declared_services_v1";
|
|
298
|
+
/**
|
|
299
|
+
* The page follows the controlling window (§6.2 "a stable viewport", §7.1,
|
|
300
|
+
* OSK-13808): a browser `viewport` input, sent only by the attachment that
|
|
301
|
+
* holds the browser's lease at the current epoch, sizes the streamed page
|
|
302
|
+
* to that window's content box (`COMPUTER_BROWSER_VIEWPORT_LIMITS`). Only
|
|
303
|
+
* while a person holds control: handing back — or the hold lapsing, being
|
|
304
|
+
* revoked or moving to the agent any other way — restores the browser's own
|
|
305
|
+
* viewport before the agent can act, so an agent never inherits a person's
|
|
306
|
+
* size. Watchers keep seeing the page at whatever size it is, scaled to
|
|
307
|
+
* fit. Announced only where the browser driver can override and restore
|
|
308
|
+
* the page's metrics; without it the window scales the fixed page, as
|
|
309
|
+
* before, and never sends the input.
|
|
310
|
+
*/
|
|
311
|
+
readonly browserViewport: "computer_browser_viewport_v1";
|
|
312
|
+
/**
|
|
313
|
+
* A browser surface whose browser is still starting says so (OSK-13824): its
|
|
314
|
+
* descriptor carries `starting: { since }` from the moment its Chromium
|
|
315
|
+
* begins to launch until that launch ends (running, or failed), and the
|
|
316
|
+
* window shows "Starting the browser…" instead of subscribing into a
|
|
317
|
+
* refusal. A cold start on a fresh guest takes 25–30 s. A daemon announcing
|
|
318
|
+
* this states the ABSENCE of `starting` too: a browser surface without it
|
|
319
|
+
* is not starting. Without the announcement a client cannot tell, and falls
|
|
320
|
+
* back to reading a surface it saw open moments ago as starting.
|
|
321
|
+
*/
|
|
322
|
+
readonly browserStarting: "computer_browser_starting_v1";
|
|
323
|
+
/**
|
|
324
|
+
* An agent actor on the machine's activity and control leases (OSK-13827)
|
|
325
|
+
* names the REAL agent: `agentId` is the agent's own id for tool-call and
|
|
326
|
+
* browser sessions too (v1 daemons put the SESSION id there for tool calls,
|
|
327
|
+
* so no reader could ever name them), and may carry `engine`, the harness
|
|
328
|
+
* the session runs on, so one agent's sessions on different engines can be
|
|
329
|
+
* told apart. A daemon announcing this states what it sends; the server
|
|
330
|
+
* withholds `engine` from every other daemon's actors, and a reader treats
|
|
331
|
+
* an actor without it as it always did.
|
|
332
|
+
*/
|
|
333
|
+
readonly activityActorV2: "computer_activity_actor_v2";
|
|
266
334
|
};
|
|
267
335
|
export type ComputerCapabilityKey = keyof typeof COMPUTER_CAPABILITIES;
|
|
268
336
|
export type ComputerCapability = (typeof COMPUTER_CAPABILITIES)[ComputerCapabilityKey];
|
|
@@ -271,14 +339,13 @@ export declare const COMPUTER_CAPABILITY_VALUES: [ComputerCapability, ...Compute
|
|
|
271
339
|
* Every refusal and limit the computer protocol can answer with. Callers branch
|
|
272
340
|
* on the code; a message beside it is free text and may change.
|
|
273
341
|
*/
|
|
274
|
-
export declare const COMPUTER_REFUSAL_CODES: readonly ["human_control", "stale_epoch", "stale_generation", "file_changed", "capability_missing", "unauthorized", "control_denied", "not_holder", "attachment_limit", "observer_limit", "watch_limit", "grant_limit", "browser_limit", "rate_limited", "not_found", "surface_ended", "machine_unavailable", "daemon_unreachable", "timeout", "invalid_request", "consent_missing", "path_withheld", "too_large", "service_limit"];
|
|
342
|
+
export declare const COMPUTER_REFUSAL_CODES: readonly ["human_control", "stale_epoch", "stale_generation", "file_changed", "capability_missing", "unauthorized", "control_denied", "not_holder", "attachment_limit", "observer_limit", "watch_limit", "grant_limit", "browser_limit", "rate_limited", "not_found", "surface_ended", "machine_unavailable", "daemon_unreachable", "relay_error", "timeout", "invalid_request", "consent_missing", "path_withheld", "too_large", "service_limit"];
|
|
275
343
|
export type ComputerRefusalCode = (typeof COMPUTER_REFUSAL_CODES)[number];
|
|
276
344
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
345
|
+
* The code when it is a computer refusal, else `null`. There is one spelling
|
|
346
|
+
* per code: the browser's older spelling of `human_control` was accepted for
|
|
347
|
+
* one release and retired with the old browser events (§5.2, OSK-13509).
|
|
279
348
|
*/
|
|
280
|
-
export declare const COMPUTER_LEGACY_REFUSAL_SPELLINGS: Readonly<Record<string, ComputerRefusalCode>>;
|
|
281
|
-
/** The canonical code for a received spelling, or `null` when it is not a computer refusal. */
|
|
282
349
|
export declare function normalizeComputerRefusalCode(code: unknown): ComputerRefusalCode | null;
|
|
283
350
|
export declare const COMPUTER_ACTOR_KINDS: readonly ["agent", "human"];
|
|
284
351
|
export type ComputerActorKind = (typeof COMPUTER_ACTOR_KINDS)[number];
|
|
@@ -291,13 +358,36 @@ export type ComputerActorKind = (typeof COMPUTER_ACTOR_KINDS)[number];
|
|
|
291
358
|
*/
|
|
292
359
|
export declare const COMPUTER_ACTIVITY_ACTOR_KINDS: readonly ["agent", "human", "system"];
|
|
293
360
|
export type ComputerActivityActorKind = (typeof COMPUTER_ACTIVITY_ACTOR_KINDS)[number];
|
|
361
|
+
/**
|
|
362
|
+
* The session ids the PLATFORM's own routes run daemon tools under — the web
|
|
363
|
+
* Files surface (`computer-files:` on a personal machine, `dedicated-runtime:`
|
|
364
|
+
* on a Dedicated Runtime) and the owner's `skrr machines dedicated` commands.
|
|
365
|
+
* No agent and no model is behind such a session: the person who asked is
|
|
366
|
+
* known to the API (which audits it), not to the daemon. So an activity event
|
|
367
|
+
* produced under one names no agent (OSK-13790: a person's upload used to read
|
|
368
|
+
* "agent created …"). Built by `ComputerFileService.runPersonalMachineTool`
|
|
369
|
+
* and `DedicatedRuntimeMachineAccess.runDedicatedRuntimeTool`.
|
|
370
|
+
*/
|
|
371
|
+
export declare const COMPUTER_PLATFORM_ROUTE_SESSION_PREFIXES: readonly ["dedicated-runtime:", "computer-files:"];
|
|
372
|
+
/** Whether a tool call's session is one of the platform's own routes, not an agent's. */
|
|
373
|
+
export declare function isComputerPlatformRouteSession(sessionId: unknown): boolean;
|
|
294
374
|
/** An agent session, as the wire names it. Same session = same `agentId` AND `sessionId`. */
|
|
295
375
|
export interface ComputerWireAgentActor {
|
|
296
376
|
kind: 'agent';
|
|
297
377
|
agentId: string;
|
|
298
378
|
sessionId: string;
|
|
299
379
|
label: string;
|
|
380
|
+
/**
|
|
381
|
+
* The engine the session runs on (`claude-code`, `codex`, …), as the daemon
|
|
382
|
+
* names its backend. Present only from a daemon that announced
|
|
383
|
+
* `computer_activity_actor_v2` (`COMPUTER_CAPABILITIES.activityActorV2`).
|
|
384
|
+
* A display distinction between one agent's sessions, never an identity:
|
|
385
|
+
* `sameComputerActor` still compares the agent id and the session id.
|
|
386
|
+
*/
|
|
387
|
+
engine?: string;
|
|
300
388
|
}
|
|
389
|
+
/** The longest `engine` the wire carries; a longer one is not an engine name. */
|
|
390
|
+
export declare const COMPUTER_ACTOR_ENGINE_MAX_LENGTH = 64;
|
|
301
391
|
export interface ComputerWireHumanActor {
|
|
302
392
|
kind: 'human';
|
|
303
393
|
userId: string;
|
|
@@ -420,7 +510,11 @@ export type ComputerSurfaceState = (typeof COMPUTER_SURFACE_STATES)[number];
|
|
|
420
510
|
*/
|
|
421
511
|
export declare const COMPUTER_SURFACE_END_REASONS: readonly ["machine_stopped", "closed_by_owner", "closed", "process_exited", "daemon_restarted", "browser_crashed", "expired", "browser_reset"];
|
|
422
512
|
export type ComputerSurfaceEndReason = (typeof COMPUTER_SURFACE_END_REASONS)[number];
|
|
423
|
-
/**
|
|
513
|
+
/**
|
|
514
|
+
* The browser's logical viewport whoever is looking (§6.2): what the agent
|
|
515
|
+
* always works at. A person holding control may size the page to their window
|
|
516
|
+
* (`computer_browser_viewport_v1`); handing back restores this.
|
|
517
|
+
*/
|
|
424
518
|
export declare const COMPUTER_BROWSER_VIEWPORT: Readonly<{
|
|
425
519
|
width: 1280;
|
|
426
520
|
height: 800;
|
|
@@ -492,8 +586,41 @@ export interface ComputerTerminalInfo {
|
|
|
492
586
|
};
|
|
493
587
|
caveats?: ComputerTerminalCaveat[];
|
|
494
588
|
}
|
|
495
|
-
/**
|
|
496
|
-
|
|
589
|
+
/**
|
|
590
|
+
* Input a browser surface takes. `tab` needs `computer_browser_tabs_v1`;
|
|
591
|
+
* `viewport` needs `computer_browser_viewport_v1`.
|
|
592
|
+
*/
|
|
593
|
+
export declare const COMPUTER_BROWSER_INPUT_TYPES: readonly ["pointer", "key", "text", "navigate", "history", "tab", "viewport"];
|
|
594
|
+
/**
|
|
595
|
+
* What a `viewport` input may ask of the page (OSK-13808), in CSS pixels: the
|
|
596
|
+
* controlling window's content box, bounded so no window can make the page
|
|
597
|
+
* absurd for the agent that gets it back, and aspect-clamped (`minAspect` and
|
|
598
|
+
* `maxAspect` are width ÷ height) so a sliver of a window cannot either.
|
|
599
|
+
* Device pixel ratio is not part of it: the page keeps the browser's own, so a
|
|
600
|
+
* high-density screen changes no layout and no picture size.
|
|
601
|
+
*/
|
|
602
|
+
export declare const COMPUTER_BROWSER_VIEWPORT_LIMITS: Readonly<{
|
|
603
|
+
minWidth: 320;
|
|
604
|
+
maxWidth: 2560;
|
|
605
|
+
minHeight: 240;
|
|
606
|
+
maxHeight: 1600;
|
|
607
|
+
minAspect: 0.4;
|
|
608
|
+
maxAspect: 3.2;
|
|
609
|
+
}>;
|
|
610
|
+
/**
|
|
611
|
+
* The size a `viewport` input asks for, made legal: whole CSS pixels inside
|
|
612
|
+
* `COMPUTER_BROWSER_VIEWPORT_LIMITS`, its aspect inside the clamp. Total and
|
|
613
|
+
* idempotent: the window sends what this returns and the daemon applies what
|
|
614
|
+
* this returns, so both ends agree on the page a size produces. Null for a size
|
|
615
|
+
* with no area (a hidden or collapsed window asks for nothing).
|
|
616
|
+
*/
|
|
617
|
+
export declare function clampComputerBrowserViewport(size: {
|
|
618
|
+
width: number;
|
|
619
|
+
height: number;
|
|
620
|
+
}): {
|
|
621
|
+
width: number;
|
|
622
|
+
height: number;
|
|
623
|
+
} | null;
|
|
497
624
|
/**
|
|
498
625
|
* The client OS a browser `key` input comes from. A person's shortcuts are
|
|
499
626
|
* mapped onto the Linux page's by the daemon: on `mac`, Command is Control,
|