@skrr-ai/cli 0.1.32 → 0.1.34
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 +2 -2
- package/dist/base-command.js +5 -0
- package/dist/commands/agents/list.d.ts +2 -1
- package/dist/commands/agents/list.js +2 -1
- package/dist/commands/agents/show.js +2 -1
- package/dist/commands/balance/statement.js +8 -4
- package/dist/commands/browser/install.js +6 -2
- package/dist/commands/browser/uninstall.js +6 -2
- package/dist/commands/code/acp.js +2 -1
- package/dist/commands/code/doctor.d.ts +1 -1
- package/dist/commands/code/doctor.js +6 -5
- package/dist/commands/code/handover.d.ts +2 -2
- package/dist/commands/code/handover.js +2 -2
- package/dist/commands/code/index.d.ts +2 -2
- package/dist/commands/code/index.js +18 -17
- package/dist/commands/code/install.d.ts +3 -3
- package/dist/commands/code/install.js +18 -11
- package/dist/commands/code/migrate.js +3 -2
- package/dist/commands/code/oversky-agent.d.ts +5 -4
- package/dist/commands/code/oversky-agent.js +24 -22
- package/dist/commands/code/run.js +2 -1
- package/dist/commands/harnesses/list.d.ts +9 -1
- package/dist/commands/harnesses/list.js +28 -7
- package/dist/commands/harnesses/show.js +7 -2
- package/dist/commands/instructions/install.js +16 -6
- package/dist/commands/learning/delete.d.ts +9 -0
- package/dist/commands/learning/delete.js +40 -0
- package/dist/commands/learning/export.d.ts +9 -0
- package/dist/commands/learning/export.js +56 -0
- package/dist/commands/learning/reset.d.ts +9 -0
- package/dist/commands/learning/reset.js +36 -0
- package/dist/commands/learning/set.d.ts +10 -0
- package/dist/commands/learning/set.js +52 -0
- package/dist/commands/learning/status.d.ts +8 -0
- package/dist/commands/learning/status.js +35 -0
- package/dist/commands/logout.js +10 -5
- package/dist/commands/machines/dedicated/attach.js +2 -1
- package/dist/commands/tasks/runs.js +3 -1
- package/dist/commands/workspaces/use.js +2 -1
- package/dist/lib/adaptive-learning.d.ts +37 -0
- package/dist/lib/adaptive-learning.js +39 -0
- package/dist/lib/auth-core-init.d.ts +23 -0
- package/dist/lib/auth-core-init.js +61 -0
- package/dist/lib/code-handover.d.ts +1 -1
- package/dist/lib/code-handover.js +3 -2
- package/dist/lib/config.d.ts +30 -4
- package/dist/lib/config.js +2 -1
- package/dist/lib/daemon-installer.d.ts +1 -1
- package/dist/lib/daemon-target.js +2 -2
- package/dist/lib/dedicated-machines.d.ts +2 -2
- package/dist/lib/dedicated-machines.js +11 -4
- package/dist/lib/exec-runtime-binary.d.ts +2 -2
- package/dist/lib/exec-runtime-binary.js +2 -2
- package/dist/lib/{sky-code-agent.d.ts → first-party-harness-agent.d.ts} +20 -19
- package/dist/lib/{sky-code-agent.js → first-party-harness-agent.js} +85 -47
- package/dist/lib/{sky-code-broker.d.ts → first-party-harness-broker.d.ts} +6 -6
- package/dist/lib/{sky-code-broker.js → first-party-harness-broker.js} +23 -23
- package/dist/lib/{sky-code-doctor.d.ts → first-party-harness-doctor.d.ts} +12 -2
- package/dist/lib/{sky-code-doctor.js → first-party-harness-doctor.js} +181 -94
- package/dist/lib/{sky-code-managed.d.ts → first-party-harness-managed.d.ts} +55 -30
- package/dist/lib/{sky-code-managed.js → first-party-harness-managed.js} +92 -35
- package/dist/lib/{sky-code.d.ts → first-party-harness.d.ts} +107 -36
- package/dist/lib/{sky-code.js → first-party-harness.js} +172 -73
- package/dist/lib/harness-provider-input.d.ts +35 -0
- package/dist/lib/harness-provider-input.js +57 -0
- package/dist/lib/harness-tiers.d.ts +8 -2
- package/dist/lib/harness-tiers.js +11 -6
- package/dist/lib/task-instruction-offer.js +10 -4
- package/dist/lib/worklogs.js +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/configRoot.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/configRoot.js +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +248 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +361 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +147 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +180 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.d.ts +111 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.js +314 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.d.ts +11 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +14 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +3 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +51 -14
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseManifest.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +4 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +4 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/configRoot.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/configRoot.js +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +248 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +332 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +147 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +170 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.d.ts +111 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.js +303 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.d.ts +11 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +14 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +3 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +12 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseManifest.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +4 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +4 -4
- package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4816 -4782
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/index.js +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
- package/oclif.manifest.json +34286 -33962
- package/package.json +5 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.d.ts +0 -96
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.js +0 -115
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.d.ts +0 -96
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.js +0 -107
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* WL-6.9 — skrr
|
|
3
|
+
* WL-6.9 — first-party harness diagnostics (`skrr code doctor`).
|
|
4
4
|
*
|
|
5
5
|
* The plan makes this a launch gate, phrased as: support must be able to
|
|
6
6
|
* identify the product version, upstream base, config source, and runtime mode
|
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
* actions, and collapsing them into one red line is how a user spends an
|
|
22
22
|
* hour reinstalling something that was merely absent. Hence three statuses,
|
|
23
23
|
* not two.
|
|
24
|
+
*
|
|
25
|
+
* Every product-named value — display name, binary name, project directory, env
|
|
26
|
+
* var names — comes from the identity module in `@skrr-ai/auth-core`. A variable
|
|
27
|
+
* that was read under an OLDER name is reported by that name, with the neutral
|
|
28
|
+
* one to move to: the older names are still honoured, and a diagnostic that
|
|
29
|
+
* hid which one was in effect would send an operator to edit the wrong line.
|
|
24
30
|
*/
|
|
25
31
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
32
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -31,14 +37,28 @@ exports.runDoctor = runDoctor;
|
|
|
31
37
|
exports.formatReport = formatReport;
|
|
32
38
|
const node_fs_1 = require("node:fs");
|
|
33
39
|
const node_path_1 = __importDefault(require("node:path"));
|
|
34
|
-
const
|
|
40
|
+
const first_party_harness_1 = require("./first-party-harness");
|
|
35
41
|
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
36
42
|
const harness_tiers_1 = require("./harness-tiers");
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
const first_party_harness_managed_1 = require("./first-party-harness-managed");
|
|
44
|
+
/**
|
|
45
|
+
* The variable one purpose was read from, and — when that was an older name — the
|
|
46
|
+
* sentence that says so. `undefined` when the neutral name (or nothing) was used.
|
|
47
|
+
*/
|
|
48
|
+
function legacyEnvNote(key, name) {
|
|
49
|
+
const neutral = auth_core_1.FIRST_PARTY_HARNESS_ENV[key].name;
|
|
50
|
+
if (!name || name === neutral)
|
|
51
|
+
return undefined;
|
|
52
|
+
return (`${name} is an older name for this setting and is still read; ` +
|
|
53
|
+
`rename it to ${neutral}, which wins when both are set.`);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Project-level state dir — the ENGINE's own project marker
|
|
57
|
+
* (`FIRST_PARTY_HARNESS_ENGINE.projectDirectory`). Basename deliberately differs
|
|
58
|
+
* from `~/.skrr`.
|
|
59
|
+
*/
|
|
40
60
|
function projectStateDir(cwd) {
|
|
41
|
-
return node_path_1.default.join(node_path_1.default.resolve(cwd),
|
|
61
|
+
return node_path_1.default.join(node_path_1.default.resolve(cwd), auth_core_1.FIRST_PARTY_HARNESS_ENGINE.projectDirectory);
|
|
42
62
|
}
|
|
43
63
|
/**
|
|
44
64
|
* Which release channel this machine follows, and where its manifest lives.
|
|
@@ -46,7 +66,7 @@ function projectStateDir(cwd) {
|
|
|
46
66
|
* `notInstalledMessage()` has always ended by promising that this command names
|
|
47
67
|
* "which channel this machine follows and whether that channel has a published
|
|
48
68
|
* release", and nothing here knew what a channel was — the whole vocabulary sat
|
|
49
|
-
* in `daemon/src/
|
|
69
|
+
* in `daemon/src/first-party-harness/channels.ts`, whose only importer was the daemon's own
|
|
50
70
|
* installer. So the user with no engine, who is exactly the audience for that
|
|
51
71
|
* sentence, was sent to a command that could not answer (dogfood OSK-274).
|
|
52
72
|
*
|
|
@@ -57,67 +77,98 @@ function projectStateDir(cwd) {
|
|
|
57
77
|
* the answer one copy-paste away and keeps the property — and the promise is
|
|
58
78
|
* reworded to match what is delivered rather than the other way round.
|
|
59
79
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
80
|
+
* The URL is the CANONICAL feed candidate. A manifest may also be published under
|
|
81
|
+
* each older spelling (`firstPartyHarnessFeedCandidates`), and an installer that
|
|
82
|
+
* finds nothing at the canonical URL tries those next — so the remedy names them
|
|
83
|
+
* too. Without that, "the canonical URL 404s" would read as "there is no release"
|
|
84
|
+
* on a feed an older pipeline published under the other spelling. The doctor
|
|
85
|
+
* cannot say which candidate actually holds a release, because it does not probe;
|
|
86
|
+
* it says where to look, in the order an installer looks.
|
|
87
|
+
*
|
|
88
|
+
* `warn` on an unrecognised channel, because `resolveFirstPartyHarnessChannel`
|
|
89
|
+
* falls back to the default rather than throwing: a typo must not brick updates,
|
|
90
|
+
* and a silent fallback must not look like agreement.
|
|
63
91
|
*/
|
|
64
92
|
function releaseChannelCheck(env) {
|
|
65
|
-
const { channel, invalid } = (0, auth_core_1.
|
|
66
|
-
const
|
|
67
|
-
base:
|
|
68
|
-
manifestFilename:
|
|
93
|
+
const { channel, invalid, source } = (0, auth_core_1.resolveFirstPartyHarnessChannel)(env);
|
|
94
|
+
const [canonical, ...alternates] = (0, auth_core_1.firstPartyHarnessFeedCandidates)(env).map((candidate) => (0, auth_core_1.resolveChannelManifestUrl)({
|
|
95
|
+
base: candidate.base,
|
|
96
|
+
manifestFilename: candidate.manifestFilename,
|
|
69
97
|
channel,
|
|
70
|
-
});
|
|
98
|
+
}));
|
|
99
|
+
const legacyNote = legacyEnvNote('channel', source);
|
|
71
100
|
if (invalid) {
|
|
72
101
|
return {
|
|
73
102
|
name: 'release-channel',
|
|
74
103
|
status: 'warn',
|
|
75
|
-
detail: `${
|
|
76
|
-
remedy: `Set ${auth_core_1.
|
|
104
|
+
detail: `${source}='${invalid}' is not a channel; following '${channel}' instead — ${canonical}`,
|
|
105
|
+
remedy: `Set ${auth_core_1.FIRST_PARTY_HARNESS_ENV.channel.name} to one of ${auth_core_1.FIRST_PARTY_HARNESS_CHANNELS.join(', ')}, ` +
|
|
106
|
+
'or unset it to follow the default.' +
|
|
107
|
+
(legacyNote ? ` ${legacyNote}` : ''),
|
|
77
108
|
};
|
|
78
109
|
}
|
|
110
|
+
const alsoTried = alternates.filter((url) => url !== canonical);
|
|
79
111
|
return {
|
|
80
112
|
name: 'release-channel',
|
|
81
113
|
status: 'ok',
|
|
82
|
-
detail: `${channel} — ${
|
|
83
|
-
remedy:
|
|
114
|
+
detail: `${channel} — ${canonical}`,
|
|
115
|
+
remedy: 'Fetch that URL to see whether the channel has a published release, and which version.' +
|
|
116
|
+
(alsoTried.length
|
|
117
|
+
? ` If it has none, an installer also tries ${alsoTried.join(', ')}.`
|
|
118
|
+
: '') +
|
|
119
|
+
(legacyNote ? ` ${legacyNote}` : ''),
|
|
84
120
|
};
|
|
85
121
|
}
|
|
86
122
|
/**
|
|
87
123
|
* What the pre-rename engine directory still holds, for a machine that has moved on.
|
|
88
124
|
*
|
|
89
|
-
* Round 1 gave both readers a fallback to
|
|
90
|
-
* BREAKS on a machine that installed before
|
|
91
|
-
* of finishing the move: no installer sweep,
|
|
92
|
-
* no mention anywhere. This machine converged
|
|
93
|
-
* reported one, the older holding 713 MB across six
|
|
125
|
+
* Round 1 gave both readers a fallback to the managed `bin` directory under the
|
|
126
|
+
* pre-rename `~/.oversky` root so nothing BREAKS on a machine that installed before
|
|
127
|
+
* the rename. Nothing was given the job of finishing the move: no installer sweep,
|
|
128
|
+
* and — once the current root wins — no mention anywhere. This machine converged
|
|
129
|
+
* on two engine directories and reported one, the older holding 713 MB across six
|
|
130
|
+
* binaries (dogfood OSK-278).
|
|
94
131
|
*
|
|
95
132
|
* Flat, never recursive, and failures are swallowed: this is a footnote on a
|
|
96
133
|
* diagnostic, and a doctor that throws while counting bytes it was only going to
|
|
97
134
|
* mention has made things worse than the clutter it was reporting.
|
|
98
135
|
*/
|
|
99
136
|
function strandedLegacyEngine(env) {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
137
|
+
for (const dir of (0, first_party_harness_1.legacyManagedEngineBinDirs)(env)) {
|
|
138
|
+
try {
|
|
139
|
+
const entries = (0, node_fs_1.readdirSync)(dir);
|
|
140
|
+
let bytes = 0;
|
|
141
|
+
let files = 0;
|
|
142
|
+
for (const entry of entries) {
|
|
143
|
+
const stats = (0, node_fs_1.statSync)(node_path_1.default.join(dir, entry));
|
|
144
|
+
if (!stats.isFile())
|
|
145
|
+
continue;
|
|
146
|
+
files += 1;
|
|
147
|
+
bytes += stats.size;
|
|
148
|
+
}
|
|
149
|
+
if (files > 0)
|
|
150
|
+
return { dir, files, bytes };
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Absent or unreadable — nothing to mention for this directory.
|
|
115
154
|
}
|
|
116
|
-
return files > 0 ? { dir, files, bytes } : null;
|
|
117
155
|
}
|
|
118
|
-
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Whether a managed engine resolved under an OLDER spelling of its file name.
|
|
160
|
+
*
|
|
161
|
+
* Not a fault: resolution tries every spelling on purpose, and the next
|
|
162
|
+
* `skrr code` run renames the file onto the canonical name (leaving a link at the
|
|
163
|
+
* old one). Said anyway, because a path that does not match the name everything
|
|
164
|
+
* else prints is exactly the surprise this module exists to explain.
|
|
165
|
+
*/
|
|
166
|
+
function olderBinaryName(bin) {
|
|
167
|
+
const name = node_path_1.default.basename(bin);
|
|
168
|
+
const canonical = (0, auth_core_1.firstPartyHarnessBinaryName)(undefined, process.platform);
|
|
169
|
+
if (name === canonical)
|
|
119
170
|
return null;
|
|
120
|
-
|
|
171
|
+
return (0, auth_core_1.firstPartyHarnessBinaryNames)(process.platform).includes(name) ? name : null;
|
|
121
172
|
}
|
|
122
173
|
/** Bytes as the size a human would say out loud. */
|
|
123
174
|
function humanBytes(bytes) {
|
|
@@ -125,7 +176,9 @@ function humanBytes(bytes) {
|
|
|
125
176
|
return mb >= 1024 ? `${(mb / 1024).toFixed(1)} GB` : `${Math.round(mb)} MB`;
|
|
126
177
|
}
|
|
127
178
|
function engineCheck(env) {
|
|
128
|
-
const
|
|
179
|
+
const resolution = (0, first_party_harness_1.resolveEngine)(env);
|
|
180
|
+
const { path: bin, source } = resolution;
|
|
181
|
+
const pathVar = auth_core_1.FIRST_PARTY_HARNESS_ENV.path.name;
|
|
129
182
|
if (bin && source === 'legacy-managed') {
|
|
130
183
|
// `ok`, because it runs and this is the resolution the daemon has always
|
|
131
184
|
// made. The remedy is here because the resolution is SURPRISING: the path
|
|
@@ -137,58 +190,79 @@ function engineCheck(env) {
|
|
|
137
190
|
status: 'ok',
|
|
138
191
|
detail: `${bin} (resolved via ${source} — the pre-rename location)`,
|
|
139
192
|
remedy: 'It works, and nothing has to change today. Installing the engine again ' +
|
|
140
|
-
`through the daemon writes it to ${(0,
|
|
193
|
+
`through the daemon writes it to ${(0, first_party_harness_1.managedEnginePath)(env)}; ` +
|
|
141
194
|
'the copy above can be deleted once it does.',
|
|
142
195
|
};
|
|
143
196
|
}
|
|
197
|
+
if (bin && source === 'env') {
|
|
198
|
+
// Name the variable, not just "env": two names are read for this override,
|
|
199
|
+
// and the one to edit is the one that is set.
|
|
200
|
+
const legacyNote = resolution.legacyEnv ? legacyEnvNote('path', resolution.envName) : undefined;
|
|
201
|
+
return {
|
|
202
|
+
name: 'engine',
|
|
203
|
+
status: 'ok',
|
|
204
|
+
detail: `${bin} (resolved via env ${resolution.envName})`,
|
|
205
|
+
...(legacyNote ? { remedy: legacyNote } : {}),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
144
208
|
if (bin) {
|
|
145
209
|
// The engine resolved from the current root — and the pre-rename directory
|
|
146
210
|
// may still be sitting there. Said here rather than in its own check because
|
|
147
211
|
// it is a footnote on a healthy answer, not a finding of its own.
|
|
148
212
|
const stranded = strandedLegacyEngine(env);
|
|
213
|
+
const older = source === 'managed' ? olderBinaryName(bin) : null;
|
|
214
|
+
const notes = [
|
|
215
|
+
older
|
|
216
|
+
? `Installed under the older file name ${older}; the next \`${auth_core_1.FIRST_PARTY_HARNESS.command}\` ` +
|
|
217
|
+
`run renames it to ${(0, auth_core_1.firstPartyHarnessBinaryName)(undefined, process.platform)} and leaves a link ` +
|
|
218
|
+
'at the old name, so anything still reading that path keeps working.'
|
|
219
|
+
: null,
|
|
220
|
+
stranded
|
|
221
|
+
? `${stranded.dir} still holds ${stranded.files} file(s), ${humanBytes(stranded.bytes)}, ` +
|
|
222
|
+
'from before the config root moved. Nothing reads it now that the engine above ' +
|
|
223
|
+
'resolves, and nothing removes it either — delete it when you want the space back.'
|
|
224
|
+
: null,
|
|
225
|
+
].filter((note) => Boolean(note));
|
|
149
226
|
return {
|
|
150
227
|
name: 'engine',
|
|
151
228
|
status: 'ok',
|
|
152
229
|
detail: `${bin} (resolved via ${source})`,
|
|
153
|
-
...(
|
|
154
|
-
? {
|
|
155
|
-
remedy: `${stranded.dir} still holds ${stranded.files} file(s), ${humanBytes(stranded.bytes)}, ` +
|
|
156
|
-
'from before the config root moved. Nothing reads it now that the engine above ' +
|
|
157
|
-
'resolves, and nothing removes it either — delete it when you want the space back.',
|
|
158
|
-
}
|
|
159
|
-
: {}),
|
|
230
|
+
...(notes.length ? { remedy: notes.join(' ') } : {}),
|
|
160
231
|
};
|
|
161
232
|
}
|
|
162
|
-
const override = env
|
|
163
|
-
|
|
233
|
+
const override = (0, auth_core_1.readFirstPartyHarnessEnv)(env, 'path');
|
|
234
|
+
const legacyNote = legacyEnvNote('path', override.name);
|
|
235
|
+
if (override.value && !node_path_1.default.isAbsolute(override.value)) {
|
|
164
236
|
return {
|
|
165
237
|
name: 'engine',
|
|
166
238
|
status: 'fail',
|
|
167
239
|
// A relative override is rejected rather than resolved: for an
|
|
168
240
|
// agent-invoked command the cwd is attacker-influenced, so a repo
|
|
169
|
-
// containing
|
|
170
|
-
detail: `${
|
|
171
|
-
remedy: `Set ${
|
|
241
|
+
// containing a file named like the engine could otherwise hijack it.
|
|
242
|
+
detail: `${override.name} is set to a relative path ('${override.value}') and was ignored`,
|
|
243
|
+
remedy: `Set ${pathVar} to an absolute path.${legacyNote ? ` ${legacyNote}` : ''}`,
|
|
172
244
|
};
|
|
173
245
|
}
|
|
174
|
-
if (override) {
|
|
246
|
+
if (override.value) {
|
|
175
247
|
return {
|
|
176
248
|
name: 'engine',
|
|
177
249
|
status: 'fail',
|
|
178
|
-
detail: `${
|
|
179
|
-
remedy:
|
|
250
|
+
detail: `${override.name}='${override.value}' is not an executable file`,
|
|
251
|
+
remedy: `Point it at a real ${auth_core_1.FIRST_PARTY_HARNESS.displayName} engine build, or unset it to fall back to PATH.` +
|
|
252
|
+
(legacyNote ? ` ${legacyNote}` : ''),
|
|
180
253
|
};
|
|
181
254
|
}
|
|
182
255
|
return {
|
|
183
256
|
name: 'engine',
|
|
184
257
|
status: 'fail',
|
|
185
|
-
detail:
|
|
186
|
-
|
|
258
|
+
detail: `no ${auth_core_1.FIRST_PARTY_HARNESS.displayName} engine binary ` +
|
|
259
|
+
`(${(0, auth_core_1.firstPartyHarnessBinaryNames)(process.platform).join(' or ')}) on PATH or in the managed location`,
|
|
260
|
+
remedy: `Install the engine (\`${auth_core_1.FIRST_PARTY_HARNESS.command} install\`), or set ${pathVar} to an existing build.`,
|
|
187
261
|
};
|
|
188
262
|
}
|
|
189
263
|
function stateDirCheck(cwd, env) {
|
|
190
264
|
const project = projectStateDir(cwd);
|
|
191
|
-
const user = (0,
|
|
265
|
+
const user = (0, first_party_harness_1.engineHome)(env);
|
|
192
266
|
// The collision the plan calls out: if the project dir resolved onto the
|
|
193
267
|
// daemon's auth/profile root, running with $HOME as cwd would reinterpret
|
|
194
268
|
// user credentials as project configuration.
|
|
@@ -196,13 +270,13 @@ function stateDirCheck(cwd, env) {
|
|
|
196
270
|
// holds for a user who relocated their root with OVERSKY_CONFIG_DIR. A
|
|
197
271
|
// collision check that looks at the wrong directory reports "ok" and is worse
|
|
198
272
|
// than no check.
|
|
199
|
-
const daemonRoot = node_path_1.default.dirname((0,
|
|
273
|
+
const daemonRoot = node_path_1.default.dirname((0, first_party_harness_1.engineHome)(env));
|
|
200
274
|
if (project === daemonRoot || project.startsWith(daemonRoot + node_path_1.default.sep)) {
|
|
201
275
|
return {
|
|
202
276
|
name: 'state-paths',
|
|
203
277
|
status: 'fail',
|
|
204
278
|
detail: `project state dir ${project} sits inside the skrr user root ${daemonRoot}`,
|
|
205
|
-
remedy:
|
|
279
|
+
remedy: `This is a build bug — report it. Do not run ${auth_core_1.FIRST_PARTY_HARNESS.displayName} from this directory.`,
|
|
206
280
|
};
|
|
207
281
|
}
|
|
208
282
|
const parts = [
|
|
@@ -212,14 +286,14 @@ function stateDirCheck(cwd, env) {
|
|
|
212
286
|
return { name: 'state-paths', status: 'ok', detail: parts.join('; ') };
|
|
213
287
|
}
|
|
214
288
|
function compatCheck(cwd, env) {
|
|
215
|
-
// Report both
|
|
289
|
+
// Report both the engine's own writable instruction surfaces and read-only
|
|
216
290
|
// compatibility sources so "where did this instruction come from?" has an
|
|
217
291
|
// answer — a WL-2 acceptance criterion.
|
|
218
292
|
const root = node_path_1.default.resolve(cwd);
|
|
219
293
|
const own = [];
|
|
220
294
|
for (const file of [
|
|
221
|
-
node_path_1.default.join(root, '
|
|
222
|
-
node_path_1.default.join((0,
|
|
295
|
+
node_path_1.default.join(projectStateDir(root), 'AGENTS.md'),
|
|
296
|
+
node_path_1.default.join((0, first_party_harness_1.engineInstructionHome)(env), 'AGENTS.md'),
|
|
223
297
|
]) {
|
|
224
298
|
if ((0, node_fs_1.existsSync)(file))
|
|
225
299
|
own.push(file);
|
|
@@ -233,12 +307,12 @@ function compatCheck(cwd, env) {
|
|
|
233
307
|
return {
|
|
234
308
|
name: 'compat-sources',
|
|
235
309
|
status: 'ok',
|
|
236
|
-
detail:
|
|
310
|
+
detail: `no ${auth_core_1.FIRST_PARTY_HARNESS.displayName}-owned or compatibility instruction files in this project`,
|
|
237
311
|
};
|
|
238
312
|
}
|
|
239
313
|
const details = [];
|
|
240
314
|
if (own.length > 0)
|
|
241
|
-
details.push(
|
|
315
|
+
details.push(`${auth_core_1.FIRST_PARTY_HARNESS.displayName}-owned: ${own.join(', ')}`);
|
|
242
316
|
if (compatibility.length > 0)
|
|
243
317
|
details.push(`compatibility (read-only): ${compatibility.join(', ')}`);
|
|
244
318
|
return {
|
|
@@ -256,11 +330,11 @@ function opencodeCheck(cwd) {
|
|
|
256
330
|
return {
|
|
257
331
|
name: 'migration',
|
|
258
332
|
status: 'warn',
|
|
259
|
-
// Warn, not fail: the directory being present is not an error, and
|
|
260
|
-
//
|
|
333
|
+
// Warn, not fail: the directory being present is not an error, and the
|
|
334
|
+
// engine will not read or touch it. The user simply has config they may
|
|
261
335
|
// want carried over.
|
|
262
|
-
detail: `${legacy} exists and is NOT read by
|
|
263
|
-
remedy:
|
|
336
|
+
detail: `${legacy} exists and is NOT read by ${auth_core_1.FIRST_PARTY_HARNESS.displayName}`,
|
|
337
|
+
remedy: `Run \`${auth_core_1.FIRST_PARTY_HARNESS.command} migrate opencode --dry-run\` to see what would carry over.`,
|
|
264
338
|
};
|
|
265
339
|
}
|
|
266
340
|
/**
|
|
@@ -270,26 +344,30 @@ function opencodeCheck(cwd) {
|
|
|
270
344
|
* it is this one, so the check stays. What changed is that the answer is
|
|
271
345
|
* computed from {@link MIRRORED_HARNESS_TIERS} instead of being restated as
|
|
272
346
|
* prose: this line described Tier 2 behaviour for a day after the owner
|
|
273
|
-
* decision of 2026-08-11 moved
|
|
347
|
+
* decision of 2026-08-11 moved the first-party harness to Tier 1, which is the worst
|
|
274
348
|
* possible audience to mislead — the people running `doctor` are running it
|
|
275
349
|
* because permissions already confused them.
|
|
276
350
|
*
|
|
277
|
-
* `
|
|
351
|
+
* `first-party-harness-trust-parity.spec.ts` fails if the mirror drifts from
|
|
278
352
|
* `daemon/src/harness-trust.ts` again.
|
|
279
353
|
*/
|
|
280
354
|
function trustCheck() {
|
|
281
|
-
|
|
355
|
+
// The canonical slug. The shared table carries every spelling at the same tier,
|
|
356
|
+
// so which one is asked does not change the answer — but the report prints the
|
|
357
|
+
// name the platform stores.
|
|
358
|
+
const harness = auth_core_1.FIRST_PARTY_HARNESS.provider;
|
|
359
|
+
const tier = (0, harness_tiers_1.mirroredTier)(harness);
|
|
282
360
|
if (tier == null) {
|
|
283
361
|
// Unreachable while the constant carries an entry, and deliberately not an
|
|
284
362
|
// invented tier: guessing here would put a false claim in a diagnostic.
|
|
285
363
|
return {
|
|
286
364
|
name: 'trust-tier',
|
|
287
365
|
status: 'warn',
|
|
288
|
-
detail: `no mirrored trust tier for ${
|
|
366
|
+
detail: `no mirrored trust tier for ${harness} in this CLI build`,
|
|
289
367
|
remedy: `The daemon decides; read ${harness_tiers_1.HARNESS_TIER_AUTHORITY} or run \`oversky doctor\`.`,
|
|
290
368
|
};
|
|
291
369
|
}
|
|
292
|
-
const described = (0, harness_tiers_1.describeTier)(
|
|
370
|
+
const described = (0, harness_tiers_1.describeTier)(harness, tier);
|
|
293
371
|
return {
|
|
294
372
|
name: 'trust-tier',
|
|
295
373
|
status: described.notable ? 'warn' : 'ok',
|
|
@@ -317,7 +395,7 @@ function permissionAuthorityCheck(env) {
|
|
|
317
395
|
/**
|
|
318
396
|
* Hosts the engine's WL-2.7 egress policy denies, and the one it still permits.
|
|
319
397
|
*
|
|
320
|
-
* Duplicated from `packages/core/src/egress.ts` in the
|
|
398
|
+
* Duplicated from `packages/core/src/egress.ts` in the engine's fork rather than
|
|
321
399
|
* imported: the CLI does not depend on the engine's source tree, and `doctor`
|
|
322
400
|
* must be able to answer "what will this binary refuse to talk to?" without one.
|
|
323
401
|
* The cost of the duplication is bounded because the fork's own test asserts the
|
|
@@ -334,14 +412,15 @@ const EGRESS_DENIED_HOSTS = [
|
|
|
334
412
|
'social-cards.sst.dev',
|
|
335
413
|
'opencode.internal',
|
|
336
414
|
];
|
|
337
|
-
|
|
415
|
+
/** The ENGINE reads this one under its own name; the platform only reports it. */
|
|
416
|
+
const EGRESS_OVERRIDE_ENV = auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.allowUpstreamEgress;
|
|
338
417
|
function egressCheck(env) {
|
|
339
418
|
const override = env[EGRESS_OVERRIDE_ENV];
|
|
340
419
|
if (override === '1' || override === 'true') {
|
|
341
420
|
return {
|
|
342
421
|
name: 'upstream-egress',
|
|
343
422
|
status: 'warn',
|
|
344
|
-
detail: `${EGRESS_OVERRIDE_ENV} is set —
|
|
423
|
+
detail: `${EGRESS_OVERRIDE_ENV} is set — ${auth_core_1.FIRST_PARTY_HARNESS.displayName} may reach upstream hosted services`,
|
|
345
424
|
remedy: 'Unset it unless you are deliberately debugging upstream behaviour. Session content, ' +
|
|
346
425
|
'repository names, and account tokens can leave for a third party while it is set.',
|
|
347
426
|
};
|
|
@@ -368,7 +447,7 @@ function egressCheck(env) {
|
|
|
368
447
|
* inventing a resolved credential would not be.
|
|
369
448
|
*/
|
|
370
449
|
function credentialCheck(env) {
|
|
371
|
-
// The variable `daemon/src/backends/
|
|
450
|
+
// The variable `daemon/src/backends/first-party-harness.ts` declares. Named here rather
|
|
372
451
|
// than imported because the CLI does not depend on the daemon's source.
|
|
373
452
|
const ENGINE_KEY_VAR = 'OPENROUTER_API_KEY';
|
|
374
453
|
const present = typeof env[ENGINE_KEY_VAR] === 'string' && env[ENGINE_KEY_VAR].length > 0;
|
|
@@ -388,7 +467,7 @@ function credentialCheck(env) {
|
|
|
388
467
|
// matches the one `skrr code`'s fallback line gives when it says whether it is
|
|
389
468
|
// falling back to BYOK or to nothing at all. Reporting only `OPENROUTER_API_KEY`
|
|
390
469
|
// told a user with an `ANTHROPIC_API_KEY` they had no credential.
|
|
391
|
-
const others = Object.keys((0,
|
|
470
|
+
const others = Object.keys((0, first_party_harness_1.inheritedProviderCredentials)(env)).sort();
|
|
392
471
|
if (others.length) {
|
|
393
472
|
const shown = others.slice(0, 4);
|
|
394
473
|
return {
|
|
@@ -505,7 +584,7 @@ function delegatedSurfaceCheck(env) {
|
|
|
505
584
|
* by construction never enters a child environment anyway.
|
|
506
585
|
*/
|
|
507
586
|
function managedInferenceCheck(env) {
|
|
508
|
-
const posture = (0,
|
|
587
|
+
const posture = (0, first_party_harness_managed_1.managedInferencePosture)(env);
|
|
509
588
|
return {
|
|
510
589
|
name: 'managed-inference',
|
|
511
590
|
status: posture.status,
|
|
@@ -550,7 +629,7 @@ function sessionAuthorityCheck(env) {
|
|
|
550
629
|
// "a daemon adjudicates every tool call" at a session where nothing adjudicates
|
|
551
630
|
// anything. The docstring above named that conflation and the code beneath it
|
|
552
631
|
// committed it.
|
|
553
|
-
switch ((0,
|
|
632
|
+
switch ((0, first_party_harness_managed_1.resolveTrustPlane)(env)) {
|
|
554
633
|
case 'daemon-supervised':
|
|
555
634
|
return {
|
|
556
635
|
name: 'session-authority',
|
|
@@ -585,7 +664,7 @@ function sessionAuthorityCheck(env) {
|
|
|
585
664
|
remedy: 'Intended for local use, and nothing to fix. Worth knowing because it is NOT the same ' +
|
|
586
665
|
'trust plane as a daemon-dispatched run — and neither plane is an OS-level sandbox ' +
|
|
587
666
|
'(no seccomp, container, or namespace). An untrusted autonomous workload needs a hosted ' +
|
|
588
|
-
|
|
667
|
+
`or container profile; see docs/${auth_core_1.FIRST_PARTY_HARNESS_ENGINE.appName}/threat-model.md in the engine's repository.`,
|
|
589
668
|
};
|
|
590
669
|
}
|
|
591
670
|
}
|
|
@@ -610,28 +689,36 @@ function agentAttributionCheck() {
|
|
|
610
689
|
// whose job is to tell you your config is broken.
|
|
611
690
|
let configured;
|
|
612
691
|
try {
|
|
613
|
-
configured = (0,
|
|
692
|
+
configured = (0, first_party_harness_managed_1.readConfiguredAgentSetting)();
|
|
614
693
|
}
|
|
615
694
|
catch (err) {
|
|
616
695
|
return {
|
|
617
696
|
name: 'agent',
|
|
618
697
|
status: 'warn',
|
|
619
|
-
detail: `could not read ${
|
|
698
|
+
detail: `could not read ${first_party_harness_managed_1.AGENT_CONFIG_KEY}: ${err instanceof Error ? err.message : String(err)}`,
|
|
620
699
|
remedy: 'Fix or delete ~/.skrr/cli-config.json, then sign in again.',
|
|
621
700
|
};
|
|
622
701
|
}
|
|
623
|
-
|
|
702
|
+
const { command, displayName } = auth_core_1.FIRST_PARTY_HARNESS;
|
|
703
|
+
if (configured.value) {
|
|
624
704
|
return {
|
|
625
705
|
name: 'agent',
|
|
626
706
|
status: 'ok',
|
|
627
|
-
|
|
707
|
+
// The key it was READ from, which is not always the key written today.
|
|
708
|
+
detail: `sessions are attributed to "${configured.value}" (${configured.key})`,
|
|
709
|
+
...(configured.legacy
|
|
710
|
+
? {
|
|
711
|
+
remedy: `"${configured.key}" is the older name for this setting and is still read. ` +
|
|
712
|
+
`\`${command} skrr-agent --set ${configured.value}\` rewrites it as "${first_party_harness_managed_1.AGENT_CONFIG_KEY}".`,
|
|
713
|
+
}
|
|
714
|
+
: {}),
|
|
628
715
|
};
|
|
629
716
|
}
|
|
630
717
|
return {
|
|
631
718
|
name: 'agent',
|
|
632
719
|
status: 'ok',
|
|
633
|
-
detail: `no ${
|
|
634
|
-
remedy:
|
|
720
|
+
detail: `no ${first_party_harness_managed_1.AGENT_CONFIG_KEY} set — sessions use your sole owned agent, else your default ${displayName} agent`,
|
|
721
|
+
remedy: `Run \`${command} skrr-agent\` to see which, or \`${command} skrr-agent --set <id>\` to pin one.`,
|
|
635
722
|
};
|
|
636
723
|
}
|
|
637
724
|
function runDoctor(cwd = process.cwd(), env = process.env) {
|
|
@@ -671,9 +758,9 @@ function formatReport(report) {
|
|
|
671
758
|
}
|
|
672
759
|
lines.push('');
|
|
673
760
|
lines.push(report.status === 'fail'
|
|
674
|
-
?
|
|
761
|
+
? `One or more checks failed — ${auth_core_1.FIRST_PARTY_HARNESS.displayName} will not run correctly until they are resolved.`
|
|
675
762
|
: report.status === 'warn'
|
|
676
|
-
?
|
|
763
|
+
? `${auth_core_1.FIRST_PARTY_HARNESS.displayName} is usable; the warnings above are worth reading.`
|
|
677
764
|
: 'All checks passed.');
|
|
678
765
|
return lines.join('\n');
|
|
679
766
|
}
|