@skrr-ai/cli 0.1.89 → 0.1.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/machines/dedicated/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/tasks/start.d.ts +10 -0
- package/dist/commands/tasks/start.js +59 -0
- package/dist/lib/commitments.d.ts +1 -1
- package/dist/lib/commitments.js +4 -2
- package/dist/lib/dedicated-keep-awake.d.ts +19 -0
- package/dist/lib/dedicated-keep-awake.js +64 -0
- package/dist/lib/dedicated-power.d.ts +61 -0
- package/dist/lib/dedicated-power.js +243 -0
- 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 +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +27 -3
- 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 +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +24 -0
- 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 +6393 -6278
- package/oclif.manifest.json +33192 -32826
- package/package.json +1 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setDedicatedKeepAwake = setDedicatedKeepAwake;
|
|
4
|
+
exports.clearDedicatedKeepAwake = clearDedicatedKeepAwake;
|
|
5
|
+
exports.parseKeepAwakeDuration = parseKeepAwakeDuration;
|
|
6
|
+
exports.assertKeepAwakeMinutes = assertKeepAwakeMinutes;
|
|
7
|
+
exports.parseKeepAwakeUntil = parseKeepAwakeUntil;
|
|
8
|
+
exports.describeKeepAwake = describeKeepAwake;
|
|
9
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
10
|
+
const api_fetch_1 = require("./api-fetch");
|
|
11
|
+
const format_1 = require("./format");
|
|
12
|
+
const dedicated_machines_1 = require("./dedicated-machines");
|
|
13
|
+
/**
|
|
14
|
+
* Domain lib for `skrr machines dedicated power keep-awake` (K1). Keep-awake is a
|
|
15
|
+
* temporary override that forbids AUTOMATIC stop; it never starts the machine.
|
|
16
|
+
* The web calls the same routes through `dataService.setDedicatedRuntimeKeepAwake`.
|
|
17
|
+
*/
|
|
18
|
+
function setDedicatedKeepAwake(leaseId, request, scope = {}) {
|
|
19
|
+
return (0, api_fetch_1.apiFetch)((0, dedicated_machines_1.withDedicatedScope)(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/power/keep-awake`, scope), {
|
|
20
|
+
method: 'PUT',
|
|
21
|
+
body: request,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function clearDedicatedKeepAwake(leaseId, scope = {}) {
|
|
25
|
+
return (0, api_fetch_1.apiFetch)((0, dedicated_machines_1.withDedicatedScope)(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/power/keep-awake`, scope), {
|
|
26
|
+
method: 'DELETE',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `--for` takes a whole number with a unit: `90m`, `2h`, `3d`. A bare number is
|
|
31
|
+
* minutes (the unit the API speaks). Returns minutes, or throws a plain message.
|
|
32
|
+
*/
|
|
33
|
+
function parseKeepAwakeDuration(value) {
|
|
34
|
+
const match = value.trim().match(/^(\d+)\s*(m|h|d)?$/i);
|
|
35
|
+
if (!match) {
|
|
36
|
+
throw new Error(`Invalid --for value "${value}". Use a number with m, h or d, such as 90m, 2h or 3d.`);
|
|
37
|
+
}
|
|
38
|
+
const amount = Number(match[1]);
|
|
39
|
+
const unit = (match[2] || 'm').toLowerCase();
|
|
40
|
+
const minutes = unit === 'd' ? amount * 1440 : unit === 'h' ? amount * 60 : amount;
|
|
41
|
+
assertKeepAwakeMinutes(minutes);
|
|
42
|
+
return minutes;
|
|
43
|
+
}
|
|
44
|
+
function assertKeepAwakeMinutes(minutes) {
|
|
45
|
+
if (minutes < data_provider_1.DEDICATED_KEEP_AWAKE_MINUTES_MIN) {
|
|
46
|
+
throw new Error(`Keep-awake must last at least ${data_provider_1.DEDICATED_KEEP_AWAKE_MINUTES_MIN} minutes.`);
|
|
47
|
+
}
|
|
48
|
+
if (minutes > data_provider_1.DEDICATED_KEEP_AWAKE_MINUTES_MAX) {
|
|
49
|
+
throw new Error(`Keep-awake can last at most ${data_provider_1.DEDICATED_KEEP_AWAKE_MINUTES_MAX / 1440} days. For longer, use \`power set --policy always_on\`.`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** `--until` takes any ISO 8601 date. The server re-checks the bounds. */
|
|
53
|
+
function parseKeepAwakeUntil(value) {
|
|
54
|
+
const at = new Date(value);
|
|
55
|
+
if (Number.isNaN(at.getTime())) {
|
|
56
|
+
throw new Error(`Invalid --until value "${value}". Use an ISO date such as 2026-10-10T18:00:00Z.`);
|
|
57
|
+
}
|
|
58
|
+
return at.toISOString();
|
|
59
|
+
}
|
|
60
|
+
function describeKeepAwake(state, when = {}) {
|
|
61
|
+
return state.active && state.until
|
|
62
|
+
? `is kept awake until ${(0, format_1.formatWhen)(state.until, when)}`
|
|
63
|
+
: 'has no keep-awake; it follows its power policy';
|
|
64
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type DedicatedRuntimePowerSummary, type DedicatedPowerPolicy, type DedicatedRuntimePowerPolicyState, type OnDemandIneligibleError, type SetDedicatedRuntimePowerPolicyResponse } from '@skrr-ai/data-provider';
|
|
2
|
+
import { type WhenOptions } from './format';
|
|
3
|
+
import { type DedicatedRuntimeScope } from './dedicated-machines';
|
|
4
|
+
/**
|
|
5
|
+
* Domain lib for `skrr machines dedicated power …` (W6). Like its siblings in
|
|
6
|
+
* `dedicated-machines.ts` it speaks `apiFetch` so every refusal arrives as an
|
|
7
|
+
* `ApiFetchError` the Dedicated commands already know how to word; the route is
|
|
8
|
+
* covered by the machines parity spec through this file. The web calls the same
|
|
9
|
+
* route through `dataService.setDedicatedRuntimePowerPolicy`.
|
|
10
|
+
*/
|
|
11
|
+
export type SetPowerOutcome = {
|
|
12
|
+
ok: true;
|
|
13
|
+
response: SetDedicatedRuntimePowerPolicyResponse;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
refusal: OnDemandIneligibleError;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Set the policy. An ineligible `on_demand` is a RESULT, not an exception: the
|
|
20
|
+
* refusal is the answer the caller must explain (reason + fix). Every other
|
|
21
|
+
* failure is rethrown for the command's usual error handling.
|
|
22
|
+
*/
|
|
23
|
+
export declare function setDedicatedPowerPolicy(leaseId: string, request: {
|
|
24
|
+
policy?: DedicatedPowerPolicy;
|
|
25
|
+
idleAfterMinutes?: number;
|
|
26
|
+
budgetHoursPerMonth?: number | null;
|
|
27
|
+
}, scope?: DedicatedRuntimeScope): Promise<SetPowerOutcome>;
|
|
28
|
+
/**
|
|
29
|
+
* The policy in words. `on_demand` promises a stop only when the platform stops idle machines at all
|
|
30
|
+
* (`autoStopMode`); an unknown mode is never read as on, so the sentence never promises one.
|
|
31
|
+
*/
|
|
32
|
+
export declare function describePolicyWords(policy: DedicatedPowerPolicy, idleAfterMinutes: number, autoStopMode?: 'off' | 'canary' | 'on'): string;
|
|
33
|
+
export declare function describePowerPolicy(power: DedicatedRuntimePowerPolicyState, autoStopMode?: 'off' | 'canary' | 'on'): string;
|
|
34
|
+
/** `GET /api/machines/dedicated/:leaseId/power` — the read-only power card. */
|
|
35
|
+
export declare function getDedicatedPowerSummary(leaseId: string, scope?: DedicatedRuntimeScope): Promise<DedicatedRuntimePowerSummary>;
|
|
36
|
+
type PowerDecisionView = NonNullable<DedicatedRuntimePowerSummary['decision']>;
|
|
37
|
+
/** The stay/stop/wake decision in words: the shared reason copy backs the server's detail. */
|
|
38
|
+
export declare function describePowerDecisionLines(decision: PowerDecisionView): {
|
|
39
|
+
headline: string;
|
|
40
|
+
detail: string;
|
|
41
|
+
fix: string | null;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The last automatic stop and how it was judged (A3). Every word comes from the shared copy
|
|
45
|
+
* (`describeAutoStopVerdict`, `describeMachineCheckpointOutcome`); nothing is composed here.
|
|
46
|
+
* `verdict: null` is "not judged yet", and is said so: it is never rendered as clean. A sentence
|
|
47
|
+
* the decision text already carries (the server appends it while the machine is stopped) is not
|
|
48
|
+
* repeated.
|
|
49
|
+
*/
|
|
50
|
+
export declare function describeLastAutoStopLines(last: DedicatedRuntimePowerSummary['lastAutoStop'], decisionDetail?: string, when?: WhenOptions): {
|
|
51
|
+
lines: string[];
|
|
52
|
+
fix: string | null;
|
|
53
|
+
};
|
|
54
|
+
/** The latest wake decision and who asked, as lines; empty when there is none. */
|
|
55
|
+
export declare function describeLastWakeDecisionLines(wake: DedicatedRuntimePowerSummary['lastWakeDecision'], when?: WhenOptions): string[];
|
|
56
|
+
/** The lines of `power show`. */
|
|
57
|
+
export declare function describePowerSummary(summary: DedicatedRuntimePowerSummary, when?: WhenOptions): string[];
|
|
58
|
+
/** The lines of `power explain`; the fix is always last. */
|
|
59
|
+
export declare function describePowerExplanation(summary: DedicatedRuntimePowerSummary, when?: WhenOptions): string[];
|
|
60
|
+
export declare function describeBudget(power: DedicatedRuntimePowerPolicyState): string;
|
|
61
|
+
export {};
|
|
@@ -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
|
+
}
|
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.
|
|
@@ -309,6 +309,28 @@ export declare const COMPUTER_CAPABILITIES: {
|
|
|
309
309
|
* before, and never sends the input.
|
|
310
310
|
*/
|
|
311
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";
|
|
312
334
|
};
|
|
313
335
|
export type ComputerCapabilityKey = keyof typeof COMPUTER_CAPABILITIES;
|
|
314
336
|
export type ComputerCapability = (typeof COMPUTER_CAPABILITIES)[ComputerCapabilityKey];
|
|
@@ -355,7 +377,17 @@ export interface ComputerWireAgentActor {
|
|
|
355
377
|
agentId: string;
|
|
356
378
|
sessionId: string;
|
|
357
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;
|
|
358
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;
|
|
359
391
|
export interface ComputerWireHumanActor {
|
|
360
392
|
kind: 'human';
|
|
361
393
|
userId: string;
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
* dropped every command event for a release (OSK-13605).
|
|
26
26
|
*/
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.
|
|
29
|
-
exports.
|
|
30
|
-
exports.COMPUTER_LEASE_STATE_POWER_PHASE = exports.COMPUTER_START_REFUSALS = exports.COMPUTER_PERSONAL_SIGN_IN_REASONS = exports.COMPUTER_PERSONAL_POWER_PHASES = exports.COMPUTER_POWER_PHASES = exports.COMPUTER_MACHINE_KINDS = exports.COMPUTER_DECLARED_SERVICE_PROBLEMS = exports.COMPUTER_DECLARED_SERVICE_NAME_PATTERN = exports.COMPUTER_DECLARED_SERVICE_HOST = exports.COMPUTER_DECLARED_SERVICE_PATH_MAX_LENGTH = void 0;
|
|
28
|
+
exports.COMPUTER_BROWSER_DIALOG_KINDS = exports.COMPUTER_INPUT_TEXT_MAX_LENGTH = exports.COMPUTER_BROWSER_INPUT_PLATFORMS = exports.COMPUTER_BROWSER_VIEWPORT_LIMITS = exports.COMPUTER_BROWSER_INPUT_TYPES = exports.COMPUTER_TERMINAL_CAVEATS = exports.COMPUTER_TERMINAL_HISTORY_CAUSES = exports.COMPUTER_TERMINAL_LIFETIMES = exports.COMPUTER_TERMINAL_INPUT_TYPES = exports.COMPUTER_TERMINAL_SIGNALS = exports.COMPUTER_DIAGNOSTICS_MAX_STREAMS = exports.COMPUTER_DIAGNOSTICS_INTERVAL_MS = exports.COMPUTER_DIAGNOSTIC_COUNTERS = exports.COMPUTER_FRAME_ACK_TIMEOUT_MS = exports.COMPUTER_FRAME_MAX_BYTES = exports.COMPUTER_BROWSER_VIEWPORT = exports.COMPUTER_SURFACE_END_REASONS = exports.COMPUTER_SURFACE_STATES = exports.COMPUTER_SURFACE_KINDS = exports.COMPUTER_CONTROL_ACTIVITY_CHANGES = exports.COMPUTER_CONTROL_CHANGE_REASONS = exports.COMPUTER_SURFACE_UNPIN_REASONS = exports.COMPUTER_SURFACE_PIN_ACTIONS = exports.COMPUTER_SHARE_MODES = exports.COMPUTER_SHARE_ACTIONS = exports.COMPUTER_CONTROL_ACTIONS = exports.COMPUTER_AGENT_WRITE_WAIT_MS = exports.COMPUTER_RECONNECT_GRACE_MS = exports.COMPUTER_RECLAIM_WARNING_MS = exports.COMPUTER_CONTROL_IDLE_MS = exports.COMPUTER_HANDBACK_NOTE_MAX_LENGTH = exports.COMPUTER_GRANT_TIERS = exports.COMPUTER_OPERATIONS = exports.COMPUTER_ACCESS_LEVELS = exports.COMPUTER_ACTOR_ENGINE_MAX_LENGTH = exports.COMPUTER_PLATFORM_ROUTE_SESSION_PREFIXES = exports.COMPUTER_ACTIVITY_ACTOR_KINDS = exports.COMPUTER_ACTOR_KINDS = exports.COMPUTER_REFUSAL_CODES = exports.COMPUTER_CAPABILITY_VALUES = exports.COMPUTER_CAPABILITIES = exports.COMPUTER_MANIFEST_REORDER_WAIT_MS = exports.COMPUTER_RESYNC_TIMEOUT_MS = exports.COMPUTER_API_ANSWERED_EVENT_KEYS = exports.COMPUTER_SERVER_EVENT_KEYS = exports.DAEMON_COMPUTER_REPORT_EVENT_KEYS = exports.DAEMON_COMPUTER_EVENT_NAMES = exports.COMPUTER_EVENT_NAMES = exports.DAEMON_COMPUTER_EVENTS = exports.COMPUTER_EVENTS = void 0;
|
|
29
|
+
exports.COMPUTER_DECLARED_SERVICES_MAX = exports.COMPUTER_DECLARED_SERVICE_PROTOCOLS = exports.COMPUTER_SERVICES_TOOL_ERROR_PREFIX = exports.COMPUTER_SERVICES_TOOL_NAME = exports.COMPUTER_DECLARED_SERVICE_ACTIONS = exports.COMPUTER_COMMAND_OUTPUT_STREAMS = exports.COMPUTER_COMMAND_START_REFUSALS = exports.COMPUTER_COMMAND_UNKNOWN_END_SIGNAL = exports.COMPUTER_COMMAND_NOT_STARTED_SIGNAL = exports.COMPUTER_COMMAND_OUTCOMES = exports.COMPUTER_LIFECYCLE_EVENTS = exports.COMPUTER_COMMAND_PHASES = exports.COMPUTER_ACTIVITY_WITHHELD_PLACEHOLDER = exports.COMPUTER_ACTIVITY_OUTPUT_MAX_LENGTH = exports.COMPUTER_ACTIVITY_SUMMARY_MAX_LENGTH = exports.COMPUTER_ACTIVITY_KINDS = exports.COMPUTER_FILE_CONTENTS_TAG_PATTERN = exports.COMPUTER_FILE_CONTENTS_TAG_MAX_ENTRIES = exports.COMPUTER_FILE_CHANGES = exports.COMPUTER_FILES_CHANGED_MAX_CHANGES = exports.COMPUTER_FILES_WATCH_DEGRADED_REASONS = exports.COMPUTER_FILE_WITHHELD_REASONS = exports.COMPUTER_FILE_TYPES = exports.COMPUTER_FILE_GIT_STATUS_BUDGET_MS = exports.COMPUTER_FILE_SEARCH_MAX_QUERY_LENGTH = exports.COMPUTER_FILE_SEARCH_MAX_BUDGET_MS = exports.COMPUTER_FILE_SEARCH_DEFAULT_BUDGET_MS = exports.COMPUTER_FILE_SEARCH_MAX_LIMIT = exports.COMPUTER_FILE_SEARCH_DEFAULT_LIMIT = exports.COMPUTER_FILE_GIT_MARKS = exports.COMPUTER_FILE_SEARCH_STATES = exports.COMPUTER_FILE_SEARCH_OPERATIONS = exports.COMPUTER_FILE_OPERATIONS = exports.COMPUTER_BROWSER_URL_MAX_LENGTH = exports.COMPUTER_BROWSER_TAB_ACTIONS = exports.COMPUTER_BROWSER_HISTORY_ACTIONS = exports.COMPUTER_BROWSER_COPY_MAX_LENGTH = exports.COMPUTER_TERMINAL_CWD_MAX_LENGTH = exports.COMPUTER_BROWSER_FILE_CHOOSER_PATH_MAX_LENGTH = exports.COMPUTER_BROWSER_FILE_CHOOSER_ACCEPT_MAX_LENGTH = exports.COMPUTER_BROWSER_FILE_CHOOSER_MAX_FILES = exports.COMPUTER_BROWSER_FILE_CHOOSER_OUTCOMES = exports.COMPUTER_BROWSER_FILE_CHOOSER_MODES = exports.COMPUTER_BROWSER_DOWNLOADS_MAX = exports.COMPUTER_BROWSER_DOWNLOAD_STATES = exports.COMPUTER_BROWSER_DIALOG_CREDENTIAL_MAX_LENGTH = exports.COMPUTER_BROWSER_DIALOG_PROMPT_MAX_LENGTH = exports.COMPUTER_BROWSER_DIALOG_MESSAGE_MAX_LENGTH = exports.COMPUTER_BROWSER_DIALOG_ANSWERERS = exports.COMPUTER_BROWSER_DIALOG_OUTCOMES = void 0;
|
|
30
|
+
exports.COMPUTER_LEASE_STATE_POWER_PHASE = exports.COMPUTER_START_REFUSALS = exports.COMPUTER_PERSONAL_SIGN_IN_REASONS = exports.COMPUTER_PERSONAL_POWER_PHASES = exports.COMPUTER_POWER_PHASES = exports.COMPUTER_MACHINE_KINDS = exports.COMPUTER_DECLARED_SERVICE_PROBLEMS = exports.COMPUTER_DECLARED_SERVICE_NAME_PATTERN = exports.COMPUTER_DECLARED_SERVICE_HOST = exports.COMPUTER_DECLARED_SERVICE_PATH_MAX_LENGTH = exports.COMPUTER_DECLARED_SERVICE_NAME_MAX_LENGTH = void 0;
|
|
31
31
|
exports.normalizeComputerRefusalCode = normalizeComputerRefusalCode;
|
|
32
32
|
exports.isComputerPlatformRouteSession = isComputerPlatformRouteSession;
|
|
33
33
|
exports.computerTierOperations = computerTierOperations;
|
|
@@ -350,6 +350,28 @@ exports.COMPUTER_CAPABILITIES = {
|
|
|
350
350
|
* before, and never sends the input.
|
|
351
351
|
*/
|
|
352
352
|
browserViewport: 'computer_browser_viewport_v1',
|
|
353
|
+
/**
|
|
354
|
+
* A browser surface whose browser is still starting says so (OSK-13824): its
|
|
355
|
+
* descriptor carries `starting: { since }` from the moment its Chromium
|
|
356
|
+
* begins to launch until that launch ends (running, or failed), and the
|
|
357
|
+
* window shows "Starting the browser…" instead of subscribing into a
|
|
358
|
+
* refusal. A cold start on a fresh guest takes 25–30 s. A daemon announcing
|
|
359
|
+
* this states the ABSENCE of `starting` too: a browser surface without it
|
|
360
|
+
* is not starting. Without the announcement a client cannot tell, and falls
|
|
361
|
+
* back to reading a surface it saw open moments ago as starting.
|
|
362
|
+
*/
|
|
363
|
+
browserStarting: 'computer_browser_starting_v1',
|
|
364
|
+
/**
|
|
365
|
+
* An agent actor on the machine's activity and control leases (OSK-13827)
|
|
366
|
+
* names the REAL agent: `agentId` is the agent's own id for tool-call and
|
|
367
|
+
* browser sessions too (v1 daemons put the SESSION id there for tool calls,
|
|
368
|
+
* so no reader could ever name them), and may carry `engine`, the harness
|
|
369
|
+
* the session runs on, so one agent's sessions on different engines can be
|
|
370
|
+
* told apart. A daemon announcing this states what it sends; the server
|
|
371
|
+
* withholds `engine` from every other daemon's actors, and a reader treats
|
|
372
|
+
* an actor without it as it always did.
|
|
373
|
+
*/
|
|
374
|
+
activityActorV2: 'computer_activity_actor_v2',
|
|
353
375
|
};
|
|
354
376
|
exports.COMPUTER_CAPABILITY_VALUES = Object.values(exports.COMPUTER_CAPABILITIES);
|
|
355
377
|
// ---------------------------------------------------------------------------
|
|
@@ -453,6 +475,8 @@ function isComputerPlatformRouteSession(sessionId) {
|
|
|
453
475
|
return (typeof sessionId === 'string' &&
|
|
454
476
|
exports.COMPUTER_PLATFORM_ROUTE_SESSION_PREFIXES.some((prefix) => sessionId.startsWith(prefix)));
|
|
455
477
|
}
|
|
478
|
+
/** The longest `engine` the wire carries; a longer one is not an engine name. */
|
|
479
|
+
exports.COMPUTER_ACTOR_ENGINE_MAX_LENGTH = 64;
|
|
456
480
|
/**
|
|
457
481
|
* How a person present on the machine reached it: as its owner, or through a
|
|
458
482
|
* grant at one of the two tiers. Agents carry no access level — an agent
|