@skrr-ai/cli 0.1.8 → 0.1.10
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/base-command.d.ts +27 -0
- package/dist/base-command.js +83 -11
- package/dist/commands/agent/message/send.js +1 -1
- package/dist/commands/agents/chat.d.ts +1 -1
- package/dist/commands/agents/chat.js +1 -1
- package/dist/commands/browser/install.d.ts +1 -1
- package/dist/commands/browser/install.js +10 -2
- package/dist/commands/browser/mcp-config.js +3 -1
- package/dist/commands/browser/skill/show.js +1 -1
- package/dist/commands/browser/status.js +4 -1
- package/dist/commands/browser/uninstall.d.ts +1 -1
- package/dist/commands/browser/uninstall.js +10 -2
- package/dist/commands/browser.d.ts +1 -1
- package/dist/commands/browser.js +3 -3
- package/dist/commands/code/index.js +6 -3
- package/dist/commands/code/install.d.ts +61 -0
- package/dist/commands/code/install.js +247 -0
- package/dist/commands/code/oversky-agent.d.ts +2 -1
- package/dist/commands/code/oversky-agent.js +12 -8
- package/dist/commands/commands.js +1 -1
- package/dist/commands/context.js +1 -1
- package/dist/commands/create-token.js +1 -1
- package/dist/commands/daemon/byok.d.ts +1 -0
- package/dist/commands/daemon/byok.js +14 -1
- package/dist/commands/daemon/index.js +18 -7
- package/dist/commands/daemon/install.d.ts +1 -0
- package/dist/commands/daemon/install.js +22 -3
- package/dist/commands/daemon/logs.d.ts +1 -0
- package/dist/commands/daemon/logs.js +4 -0
- package/dist/commands/daemon/start.d.ts +1 -0
- package/dist/commands/daemon/start.js +15 -2
- package/dist/commands/daemon/status.d.ts +1 -0
- package/dist/commands/daemon/status.js +14 -1
- package/dist/commands/daemon/stop.d.ts +1 -0
- package/dist/commands/daemon/stop.js +14 -1
- package/dist/commands/daemon/uninstall.d.ts +1 -0
- package/dist/commands/daemon/uninstall.js +15 -2
- package/dist/commands/daemon/usage.d.ts +1 -0
- package/dist/commands/daemon/usage.js +5 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/github/allow-tools.d.ts +1 -0
- package/dist/commands/github/app/install.d.ts +1 -0
- package/dist/commands/github/app/status.d.ts +1 -0
- package/dist/commands/github/connect.d.ts +1 -0
- package/dist/commands/github/connections.d.ts +1 -0
- package/dist/commands/github/disconnect.d.ts +1 -0
- package/dist/commands/github/finalize.d.ts +1 -0
- package/dist/commands/github/reconnect.d.ts +1 -0
- package/dist/commands/github/run.d.ts +1 -0
- package/dist/commands/github/tools.d.ts +1 -0
- package/dist/commands/goals/create.js +2 -2
- package/dist/commands/goals/show.js +5 -0
- package/dist/commands/harnesses/list.js +14 -3
- package/dist/commands/harnesses/show.js +14 -0
- package/dist/commands/instructions/activate.js +1 -1
- package/dist/commands/instructions/archive-version.js +1 -1
- package/dist/commands/instructions/delete.js +1 -1
- package/dist/commands/instructions/diff.js +1 -1
- package/dist/commands/instructions/install.js +7 -1
- package/dist/commands/instructions/restore.js +1 -1
- package/dist/commands/instructions/show.js +17 -1
- package/dist/commands/instructions/update.js +1 -1
- package/dist/commands/integrations/connections/connect.d.ts +1 -0
- package/dist/commands/integrations/connections/disconnect.d.ts +1 -0
- package/dist/commands/integrations/connections/finalize.d.ts +1 -0
- package/dist/commands/integrations/connections/list.d.ts +1 -0
- package/dist/commands/integrations/connections/reconnect.d.ts +1 -0
- package/dist/commands/integrations/execute.d.ts +1 -0
- package/dist/commands/integrations/search.d.ts +1 -0
- package/dist/commands/integrations/toolkits.d.ts +1 -0
- package/dist/commands/integrations/tools/allow.d.ts +1 -0
- package/dist/commands/integrations/tools/list.d.ts +1 -0
- package/dist/commands/list-daemons.d.ts +2 -7
- package/dist/commands/list-daemons.js +13 -13
- package/dist/commands/login.js +78 -33
- package/dist/commands/logout.js +52 -1
- package/dist/commands/machines/hosted/start.js +1 -1
- package/dist/commands/pair.js +2 -2
- package/dist/commands/projects/create.js +6 -1
- package/dist/commands/projects/tasks.js +6 -1
- package/dist/commands/revoke-daemon.js +6 -2
- package/dist/commands/set-capabilities.js +6 -2
- package/dist/commands/skills/install.js +1 -1
- package/dist/commands/skills/library.js +2 -2
- package/dist/commands/spaces/create.js +6 -2
- package/dist/commands/tasks/complete.d.ts +1 -1
- package/dist/commands/tasks/complete.js +21 -8
- package/dist/commands/tasks/create.js +4 -1
- package/dist/commands/tasks/current.js +1 -1
- package/dist/commands/tasks/delete.d.ts +1 -0
- package/dist/commands/tasks/delete.js +9 -0
- package/dist/commands/tasks/export.js +5 -1
- package/dist/commands/tasks/follow-up.js +2 -1
- package/dist/commands/tasks/list.js +5 -1
- package/dist/commands/tasks/pr.js +1 -1
- package/dist/commands/tasks/show.js +10 -1
- package/dist/commands/tasks/triage.js +2 -1
- package/dist/commands/tasks/update.js +4 -1
- package/dist/commands/tasks/upsert.js +4 -1
- package/dist/commands/usage.js +1 -1
- package/dist/commands/whoami.js +4 -1
- package/dist/lib/agentic-stream.js +21 -1
- package/dist/lib/api-fetch.d.ts +1 -1
- package/dist/lib/api-fetch.js +1 -1
- package/dist/lib/auth-core-init.js +23 -3
- package/dist/lib/auth-storage.d.ts +0 -12
- package/dist/lib/auth-storage.js +37 -2
- package/dist/lib/config.d.ts +43 -3
- package/dist/lib/config.js +391 -31
- package/dist/lib/created-line.d.ts +22 -0
- package/dist/lib/created-line.js +25 -0
- package/dist/lib/daemon-binding.d.ts +29 -0
- package/dist/lib/daemon-binding.js +63 -0
- package/dist/lib/daemon-ref.d.ts +31 -0
- package/dist/lib/daemon-ref.js +47 -0
- package/dist/lib/daemonBroker.js +1 -1
- package/dist/lib/delegated-cli.js +3 -3
- package/dist/lib/exec-oversky.d.ts +18 -1
- package/dist/lib/exec-oversky.js +21 -4
- package/dist/lib/html-text.d.ts +15 -0
- package/dist/lib/html-text.js +40 -0
- package/dist/lib/keychain.d.ts +43 -0
- package/dist/lib/keychain.js +145 -4
- package/dist/lib/login.js +3 -3
- package/dist/lib/node-adapter.js +1 -1
- package/dist/lib/oauthLogin.js +17 -6
- package/dist/lib/sky-code-agent.d.ts +1 -1
- package/dist/lib/sky-code-agent.js +8 -8
- package/dist/lib/sky-code-broker.js +18 -2
- package/dist/lib/sky-code-doctor.js +120 -4
- package/dist/lib/sky-code-managed.d.ts +12 -0
- package/dist/lib/sky-code-managed.js +21 -7
- package/dist/lib/sky-code.d.ts +16 -1
- package/dist/lib/sky-code.js +110 -7
- package/dist/lib/task-execution.js +4 -11
- package/dist/lib/tasks.d.ts +1 -0
- package/dist/lib/tasks.js +9 -6
- package/dist/lib/update-check.js +1 -1
- package/dist/lib/usage-discovery.js +1 -1
- package/dist/lib/web-url.js +12 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.js +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +13 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/linux.js +16 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.d.ts +0 -9
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.js +18 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loginLocalhost.js +3 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +3 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/runtime.d.ts +7 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.d.ts +96 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.js +115 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.js +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/linux.js +16 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.d.ts +0 -9
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.js +18 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loginLocalhost.js +3 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +3 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/runtime.d.ts +7 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.d.ts +96 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.js +107 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4206 -4191
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +2 -2
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +2 -2
- package/oclif.manifest.json +7969 -1397
- package/package.json +22 -19
package/dist/lib/config.js
CHANGED
|
@@ -35,8 +35,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ENVIRONMENT_BASE_URLS = exports.LEGACY_PROD_BASE_URL = exports.LEGACY_DEV_BASE_URL = exports.CANONICAL_PROD_BASE_URL = exports.CANONICAL_DEV_BASE_URL = void 0;
|
|
37
37
|
exports.environmentForBaseURL = environmentForBaseURL;
|
|
38
|
+
exports.describeDeployment = describeDeployment;
|
|
38
39
|
exports.canonicalizeKnownBaseURL = canonicalizeKnownBaseURL;
|
|
39
40
|
exports.isEnvAuthOverride = isEnvAuthOverride;
|
|
41
|
+
exports.configRoot = configRoot;
|
|
40
42
|
exports.loadConfig = loadConfig;
|
|
41
43
|
exports.saveConfig = saveConfig;
|
|
42
44
|
exports.getConfigPath = getConfigPath;
|
|
@@ -48,6 +50,7 @@ exports.saveRawConfig = saveRawConfig;
|
|
|
48
50
|
const fs = __importStar(require("node:fs"));
|
|
49
51
|
const os = __importStar(require("node:os"));
|
|
50
52
|
const path = __importStar(require("node:path"));
|
|
53
|
+
const keychain_1 = require("./keychain");
|
|
51
54
|
const publicEndpoints_generated_1 = require("./publicEndpoints.generated");
|
|
52
55
|
// Both from config/public-endpoints.json, the one domain contract, rather than a
|
|
53
56
|
// fourth hand-copied pair. The CLI previously knew only about dev — there was no
|
|
@@ -96,6 +99,22 @@ function environmentForBaseURL(value) {
|
|
|
96
99
|
* display and not harmless for routing, because two spellings of prod are two
|
|
97
100
|
* different keys to anything comparing base URLs.
|
|
98
101
|
*/
|
|
102
|
+
/**
|
|
103
|
+
* A deployment, written so a human can tell which one it is.
|
|
104
|
+
*
|
|
105
|
+
* A published `npm i -g @skrr-ai/cli` defaults to the DEV deployment, and nothing
|
|
106
|
+
* in the login flow said so — you got "Signed in via browser" and no indication
|
|
107
|
+
* of which backend now holds your account (OSK-301). Naming the environment as
|
|
108
|
+
* well as the URL matters because `oversky.dev` and `skrr.ai` do not look like a
|
|
109
|
+
* dev/prod pair to anyone who has not read this file.
|
|
110
|
+
*
|
|
111
|
+
* Falls back to the bare URL for a deployment we do not recognise — a
|
|
112
|
+
* self-hosted or local server is legitimate, and labelling it would be a guess.
|
|
113
|
+
*/
|
|
114
|
+
function describeDeployment(baseURL) {
|
|
115
|
+
const env = environmentForBaseURL(baseURL);
|
|
116
|
+
return env ? `${env} (${baseURL})` : baseURL;
|
|
117
|
+
}
|
|
99
118
|
function canonicalizeKnownBaseURL(value) {
|
|
100
119
|
const trimmed = value.trim().replace(/\/+$/, '');
|
|
101
120
|
const normalized = trimmed.toLowerCase().replace(/^http:\/\//, 'https://');
|
|
@@ -112,10 +131,27 @@ function canonicalizeKnownBaseURL(value) {
|
|
|
112
131
|
return value;
|
|
113
132
|
}
|
|
114
133
|
const DEFAULT_CONFIG = {
|
|
115
|
-
//
|
|
116
|
-
// (daemon/src/config.ts → ENVIRONMENT_URLS.
|
|
117
|
-
// `skrr login --env
|
|
118
|
-
|
|
134
|
+
// PROD for an unconfigured CLI, matching the daemon
|
|
135
|
+
// (daemon/src/config.ts → ENVIRONMENT_URLS.prod). Reach dev with
|
|
136
|
+
// `skrr login --env dev`, `--base-url https://oversky.dev`, or OVERSKY_BASE_URL.
|
|
137
|
+
//
|
|
138
|
+
// This was dev, which was right when the CLI was something the team installed
|
|
139
|
+
// from the repo. `@skrr-ai/cli` is a PUBLIC npm package now, and the default
|
|
140
|
+
// decides where a stranger's first `skrr login` creates their account,
|
|
141
|
+
// workspace and agents. `oversky.dev` is the integration environment: it
|
|
142
|
+
// deploys on every push to `dev` and, by design, without waiting for the api or
|
|
143
|
+
// client suites. A reasonable place for the team to live, a poor place to send
|
|
144
|
+
// someone who typed `npm install` (OSK-301).
|
|
145
|
+
//
|
|
146
|
+
// It moves with the daemon or not at all. The two defaults are ONE decision:
|
|
147
|
+
// changing this alone would put every fresh machine into the CLI-on-prod /
|
|
148
|
+
// daemon-on-dev split that OSK-279 exists to prevent — which is exactly the
|
|
149
|
+
// state this machine was found in. `cli/src/__tests__/default-deployment.spec.ts`
|
|
150
|
+
// reads the daemon's own source and fails if they diverge.
|
|
151
|
+
//
|
|
152
|
+
// Nobody's existing setup moves: a stored `cli-config.json` wins over this, so
|
|
153
|
+
// only an install with no configuration at all is affected.
|
|
154
|
+
baseURL: exports.CANONICAL_PROD_BASE_URL,
|
|
119
155
|
};
|
|
120
156
|
/**
|
|
121
157
|
* Environment-variable overlay for headless use (CI, Secrets Manager, EC2
|
|
@@ -138,18 +174,46 @@ const ENV_VAR = {
|
|
|
138
174
|
refreshToken: 'OVERSKY_REFRESH_TOKEN',
|
|
139
175
|
cliId: 'OVERSKY_CLI_ID',
|
|
140
176
|
};
|
|
177
|
+
/**
|
|
178
|
+
* Read one overlay variable under either prefix.
|
|
179
|
+
*
|
|
180
|
+
* The ~921 `OVERSKY_*` variables are RETAINED by decision
|
|
181
|
+
* (docs/product/skrr-env-inventory.md), so this migrates nothing and changes no
|
|
182
|
+
* existing environment: the legacy spelling still wins when both are set. What
|
|
183
|
+
* it stops is the renamed product SILENTLY IGNORING the prefix its own
|
|
184
|
+
* contributor docs tell new code to use. `SKRR_BASE_URL=https://oversky.dev`
|
|
185
|
+
* previously did nothing at all — the command ran happily against production
|
|
186
|
+
* and reported success, which is the worst available outcome for a variable
|
|
187
|
+
* whose entire job is choosing the deployment.
|
|
188
|
+
*
|
|
189
|
+
* A disagreement is reported rather than resolved quietly, because two
|
|
190
|
+
* different values for one setting is a mistake either way.
|
|
191
|
+
*/
|
|
192
|
+
let warnedEnvPrefixConflict = false;
|
|
193
|
+
function envOverlayValue(key) {
|
|
194
|
+
const legacyName = ENV_VAR[key];
|
|
195
|
+
const renamedName = legacyName.replace(/^OVERSKY_/, 'SKRR_');
|
|
196
|
+
const legacy = process.env[legacyName]?.trim();
|
|
197
|
+
const renamed = process.env[renamedName]?.trim();
|
|
198
|
+
if (legacy && renamed && legacy !== renamed && !warnedEnvPrefixConflict) {
|
|
199
|
+
warnedEnvPrefixConflict = true;
|
|
200
|
+
process.stderr.write(`warning: ${legacyName} and ${renamedName} are both set and disagree. ` +
|
|
201
|
+
`Using ${legacyName}=${legacy}.\n`);
|
|
202
|
+
}
|
|
203
|
+
return legacy && legacy.length > 0 ? legacy : renamed && renamed.length > 0 ? renamed : undefined;
|
|
204
|
+
}
|
|
141
205
|
function overlayEnv(cfg) {
|
|
142
206
|
const out = { ...cfg };
|
|
143
|
-
const bu =
|
|
207
|
+
const bu = envOverlayValue('baseURL') || envOverlayValue('serverUrl');
|
|
144
208
|
if (bu && bu.length > 0)
|
|
145
209
|
out.baseURL = canonicalizeKnownBaseURL(bu);
|
|
146
|
-
const tok =
|
|
210
|
+
const tok = envOverlayValue('token');
|
|
147
211
|
if (tok && tok.length > 0)
|
|
148
212
|
out.token = tok;
|
|
149
|
-
const rt =
|
|
213
|
+
const rt = envOverlayValue('refreshToken');
|
|
150
214
|
if (rt && rt.length > 0)
|
|
151
215
|
out.refreshToken = rt;
|
|
152
|
-
const cid =
|
|
216
|
+
const cid = envOverlayValue('cliId');
|
|
153
217
|
if (cid && cid.length > 0)
|
|
154
218
|
out.cliId = cid;
|
|
155
219
|
return out;
|
|
@@ -161,8 +225,9 @@ function overlayEnv(cfg) {
|
|
|
161
225
|
* us persist a rotated pair that the next `loadConfig()` would shadow.
|
|
162
226
|
*/
|
|
163
227
|
function isEnvAuthOverride() {
|
|
164
|
-
|
|
165
|
-
|
|
228
|
+
// Both prefixes, or a credential injected as SKRR_TOKEN would be used for the
|
|
229
|
+
// request while the process still believed it could refresh and persist.
|
|
230
|
+
return !!(envOverlayValue('token') || envOverlayValue('refreshToken'));
|
|
166
231
|
}
|
|
167
232
|
/**
|
|
168
233
|
* Storage layout (Phase H+).
|
|
@@ -175,12 +240,127 @@ function isEnvAuthOverride() {
|
|
|
175
240
|
* The CLI shares the daemon's config root so `auth.lock` and
|
|
176
241
|
* `needs-reauth.json` are coordinated across binaries.
|
|
177
242
|
*/
|
|
243
|
+
/**
|
|
244
|
+
* The skrr root for this process — the ONE place every root-derived path comes
|
|
245
|
+
* from.
|
|
246
|
+
*
|
|
247
|
+
* It is exported and takes `env` because the alternative is what this replaced:
|
|
248
|
+
* two functions each deriving the root themselves, one honouring the override
|
|
249
|
+
* and one not, disagreeing only when someone relocated their root (OSK-300).
|
|
250
|
+
* Fixing that by teaching the second function the same two variable names would
|
|
251
|
+
* have re-created the split the moment a third name appeared — and did, briefly,
|
|
252
|
+
* when `SKRR_CONFIG_DIR` was added to one side only.
|
|
253
|
+
*
|
|
254
|
+
* `SKRR_CONFIG_DIR` is the spelling for anything new (root CLAUDE.md) and wins
|
|
255
|
+
* when both are set. `OVERSKY_CONFIG_DIR` keeps working: the daemon, the engine
|
|
256
|
+
* resolver and every existing install read it.
|
|
257
|
+
*/
|
|
258
|
+
function configRoot(env = process.env) {
|
|
259
|
+
const root = (env.SKRR_CONFIG_DIR || env.OVERSKY_CONFIG_DIR)?.trim();
|
|
260
|
+
return root || path.join(os.homedir(), '.skrr');
|
|
261
|
+
}
|
|
178
262
|
function newConfigDir() {
|
|
179
|
-
return
|
|
263
|
+
return configRoot();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Fields that belong to the MACHINE, not to a profile.
|
|
267
|
+
*
|
|
268
|
+
* `cliId` is the sharp one. Its own doc says it is presented at login and at
|
|
269
|
+
* every refresh rotation, and that the server binds it into the JWT `did` claim
|
|
270
|
+
* and rejects a rotation against a different one. Split it per profile and a
|
|
271
|
+
* second profile does not degrade — it fails to stay logged in.
|
|
272
|
+
*
|
|
273
|
+
* `credentials.store` is a machine posture (keychain / file / env-only), and
|
|
274
|
+
* `migrated` is a breadcrumb about this machine's DISK LAYOUT. Neither is a
|
|
275
|
+
* property of which deployment you are addressing.
|
|
276
|
+
*
|
|
277
|
+
* Everything else follows the profile, because everything else IS the
|
|
278
|
+
* deployment: `baseURL`, who you are there (`userId`, `email`), and what you
|
|
279
|
+
* chose there (`workspaceId`, `skyCodeAgentId`).
|
|
280
|
+
*/
|
|
281
|
+
const MACHINE_SCOPED_KEYS = ['cliId', 'credentials', 'migrated'];
|
|
282
|
+
/**
|
|
283
|
+
* Where this profile's config lives.
|
|
284
|
+
*
|
|
285
|
+
* The DEFAULT profile keeps `~/.skrr/cli-config.json` — the exact path it has
|
|
286
|
+
* always used. That is the whole reason this change needs no migration and
|
|
287
|
+
* carries almost no risk: profiles are ADDITIVE. An existing install is the
|
|
288
|
+
* default profile, its file does not move, and nothing about it is rewritten.
|
|
289
|
+
*
|
|
290
|
+
* A named profile gets `~/.skrr/profiles/<name>/cli-config.json`, sharing the
|
|
291
|
+
* tree the daemon already uses for the same idea. Sharing rather than inventing
|
|
292
|
+
* `cli-profiles/` is deliberate: today's worst failure is the CLI and the daemon
|
|
293
|
+
* pointing at different deployments (OSK-279), and one profile namespace makes
|
|
294
|
+
* that harder to produce, not easier.
|
|
295
|
+
*/
|
|
296
|
+
function profileConfigDir() {
|
|
297
|
+
const profile = (0, keychain_1.getActiveProfile)();
|
|
298
|
+
if (!profile || profile === keychain_1.DEFAULT_PROFILE)
|
|
299
|
+
return newConfigDir();
|
|
300
|
+
return path.join(newConfigDir(), 'profiles', profile);
|
|
180
301
|
}
|
|
181
302
|
function newConfigFile() {
|
|
303
|
+
return path.join(profileConfigDir(), 'cli-config.json');
|
|
304
|
+
}
|
|
305
|
+
/** The machine-scoped file, which never moves with the profile. */
|
|
306
|
+
function machineConfigFile() {
|
|
182
307
|
return path.join(newConfigDir(), 'cli-config.json');
|
|
183
308
|
}
|
|
309
|
+
/** Split a config into the half that follows the profile and the half that does not. */
|
|
310
|
+
function splitByScope(config) {
|
|
311
|
+
const machine = {};
|
|
312
|
+
const profile = {};
|
|
313
|
+
for (const [key, value] of Object.entries(config)) {
|
|
314
|
+
if (MACHINE_SCOPED_KEYS.includes(key))
|
|
315
|
+
machine[key] = value;
|
|
316
|
+
else
|
|
317
|
+
profile[key] = value;
|
|
318
|
+
}
|
|
319
|
+
return { machine, profile };
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Machine-scoped fields, read from the machine file regardless of profile.
|
|
323
|
+
*
|
|
324
|
+
* Returns an empty object rather than throwing: a named profile on a machine
|
|
325
|
+
* that has never run the default one has no machine file yet, and that is a
|
|
326
|
+
* first run, not a fault.
|
|
327
|
+
*/
|
|
328
|
+
function readMachineScoped() {
|
|
329
|
+
const p = machineConfigFile();
|
|
330
|
+
if (!fs.existsSync(p))
|
|
331
|
+
return {};
|
|
332
|
+
try {
|
|
333
|
+
return splitByScope(readJsonOrThrow(p)).machine;
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
return {};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* The PRE-RENAME config root — the generation between `.sky` and `.skrr`.
|
|
341
|
+
*
|
|
342
|
+
* This was missing from the fallback chain, which went straight from `.skrr` to
|
|
343
|
+
* `.sky` and skipped the directory that every current install actually has.
|
|
344
|
+
* The effect is not a missing preference: with no config found, `loadConfig`
|
|
345
|
+
* returns DEFAULT_CONFIG, whose baseURL is the DEV deployment — so a CLI that
|
|
346
|
+
* had been pointed at production silently starts addressing dev, and its
|
|
347
|
+
* `workspaceId` is gone. Nothing errors; commands just answer about a different
|
|
348
|
+
* backend.
|
|
349
|
+
*
|
|
350
|
+
* This read "the production apex", and said the drift ran the other way. It did
|
|
351
|
+
* not: DEFAULT_CONFIG has been dev since it was written, forty lines further up.
|
|
352
|
+
* The direction matters here more than most stale comments, because it is the
|
|
353
|
+
* difference between a lost session and one that silently addresses production.
|
|
354
|
+
*
|
|
355
|
+
* Note the filename differs by generation: `.sky` used `config.json`, `.oversky`
|
|
356
|
+
* and `.skrr` use `cli-config.json`.
|
|
357
|
+
*/
|
|
358
|
+
function priorConfigDir() {
|
|
359
|
+
return path.join(os.homedir(), '.oversky');
|
|
360
|
+
}
|
|
361
|
+
function priorConfigFile() {
|
|
362
|
+
return path.join(priorConfigDir(), 'cli-config.json');
|
|
363
|
+
}
|
|
184
364
|
function legacyConfigDir() {
|
|
185
365
|
return path.join(os.homedir(), '.sky');
|
|
186
366
|
}
|
|
@@ -188,41 +368,221 @@ function legacyConfigFile() {
|
|
|
188
368
|
return path.join(legacyConfigDir(), 'config.json');
|
|
189
369
|
}
|
|
190
370
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
371
|
+
* Breadcrumb proving the carry-forward already ran on this machine.
|
|
372
|
+
*
|
|
373
|
+
* Deliberately OUTSIDE the file it produces. The migration's own record used to
|
|
374
|
+
* be the config it wrote, which made it one-shot only for as long as that file
|
|
375
|
+
* survived: any moment where `cli-config.json` was absent looked exactly like a
|
|
376
|
+
* first run, and the pre-rename config was adopted again — `baseURL` included.
|
|
377
|
+
*
|
|
378
|
+
* Follows the `.migrated-*` convention already used by the daemon's
|
|
379
|
+
* profile-migration and keychain-hygiene passes.
|
|
380
|
+
*/
|
|
381
|
+
const CARRY_FORWARD_BREADCRUMB = '.migrated-2026-09-02-cli-config-carry-forward';
|
|
382
|
+
function carryForwardBreadcrumbPath() {
|
|
383
|
+
return path.join(newConfigDir(), CARRY_FORWARD_BREADCRUMB);
|
|
384
|
+
}
|
|
385
|
+
function hasCarriedForward() {
|
|
386
|
+
try {
|
|
387
|
+
return fs.existsSync(carryForwardBreadcrumbPath());
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function markCarriedForward() {
|
|
394
|
+
try {
|
|
395
|
+
const dir = newConfigDir();
|
|
396
|
+
if (!fs.existsSync(dir))
|
|
397
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
398
|
+
fs.writeFileSync(carryForwardBreadcrumbPath(), `${new Date().toISOString()}\n`, {
|
|
399
|
+
mode: 0o600,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
// Best effort. Failing to record it means the migration may run again,
|
|
404
|
+
// which is the old behaviour — not worse.
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
let announcedAdoptedDeployment = false;
|
|
408
|
+
/**
|
|
409
|
+
* Say when the CLI adopts a deployment the user did not name in this command.
|
|
410
|
+
*
|
|
411
|
+
* Whatever the source, changing which DEPLOYMENT a session addresses is not a
|
|
412
|
+
* detail: the failure it produces is an empty list, not an error, because a
|
|
413
|
+
* successful `200 {"data": []}` from the wrong deployment is indistinguishable
|
|
414
|
+
* from having no rows.
|
|
415
|
+
*/
|
|
416
|
+
function announceAdoptedDeployment(baseURL, source) {
|
|
417
|
+
if (announcedAdoptedDeployment)
|
|
418
|
+
return;
|
|
419
|
+
announcedAdoptedDeployment = true;
|
|
420
|
+
process.stderr.write(`note: no CLI config found; using ${baseURL} (from ${source}).\n` +
|
|
421
|
+
` If that is the wrong deployment: skrr login --base-url <url>\n`);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Read the config file. Returns defaults if missing. Throws on corrupt JSON.
|
|
425
|
+
* Performs a one-shot legacy → new copy when only the legacy file exists, so
|
|
426
|
+
* existing users don't have to re-login.
|
|
194
427
|
*/
|
|
195
428
|
function loadConfig() {
|
|
196
429
|
const newPath = newConfigFile();
|
|
430
|
+
const named = newPath !== machineConfigFile();
|
|
197
431
|
if (fs.existsSync(newPath)) {
|
|
198
|
-
|
|
432
|
+
const current = migrateLegacyBaseURL(readJsonOrThrow(newPath));
|
|
433
|
+
warnIfStrandedPriorConfig(current);
|
|
434
|
+
// A named profile's file holds only its own half; the machine half is read
|
|
435
|
+
// from the machine file so `cliId` is one value for this machine no matter
|
|
436
|
+
// how many profiles exist.
|
|
437
|
+
return overlayEnv(named ? { ...current, ...readMachineScoped() } : current);
|
|
199
438
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
439
|
+
// A named profile that has never been used is a FIRST RUN for that profile,
|
|
440
|
+
// not a machine without config. It must not adopt the default profile's
|
|
441
|
+
// deployment — that is how "switch to dev" would silently keep talking to
|
|
442
|
+
// prod — so it starts from the documented default, carrying only the machine
|
|
443
|
+
// half forward. The generation carry-forward below is deliberately skipped:
|
|
444
|
+
// there is no `.sky`/`.oversky` ancestor of a profile that did not exist then.
|
|
445
|
+
if (named) {
|
|
446
|
+
return overlayEnv({ ...DEFAULT_CONFIG, ...readMachineScoped() });
|
|
447
|
+
}
|
|
448
|
+
// Newest first, and ONCE per machine. Each generation is copied forward so a
|
|
449
|
+
// user never has to re-login because a directory was renamed — but only on a
|
|
450
|
+
// genuine first run. After the breadcrumb exists, a missing config means the
|
|
451
|
+
// file went away, not that this machine is pre-rename, and re-adopting the
|
|
452
|
+
// old file would silently move the session to the old DEPLOYMENT.
|
|
453
|
+
if (!hasCarriedForward()) {
|
|
454
|
+
for (const carriedPath of [priorConfigFile(), legacyConfigFile()]) {
|
|
455
|
+
if (!fs.existsSync(carriedPath))
|
|
456
|
+
continue;
|
|
457
|
+
const carried = migrateLegacyBaseURL(readJsonOrThrow(carriedPath));
|
|
458
|
+
try {
|
|
459
|
+
saveConfig(carried);
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
/* swallow — caller still has a usable config in memory */
|
|
463
|
+
}
|
|
464
|
+
markCarriedForward();
|
|
465
|
+
announceAdoptedDeployment(carried.baseURL, carriedPath);
|
|
466
|
+
return overlayEnv(carried);
|
|
208
467
|
}
|
|
209
|
-
return overlayEnv(legacy);
|
|
210
468
|
}
|
|
469
|
+
// Nothing to carry: fall through to the documented default. Deliberately NOT
|
|
470
|
+
// derived from the stored credential's origin — making config resolution read
|
|
471
|
+
// the keychain would let ambient machine state override a documented default,
|
|
472
|
+
// and a fresh install defaulting to dev is intended behaviour, not a bug.
|
|
211
473
|
return overlayEnv({ ...DEFAULT_CONFIG });
|
|
212
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Surface the one case this fix cannot repair on its own.
|
|
477
|
+
*
|
|
478
|
+
* A CLI that ran after the directory rename but before this migration existed
|
|
479
|
+
* already wrote a `.skrr` config from DEFAULT_CONFIG. The copy-forward above
|
|
480
|
+
* will not fire for it, and it must not: once a config exists, "the user chose
|
|
481
|
+
* this backend" and "the default landed here" are indistinguishable, and
|
|
482
|
+
* silently rewriting a deliberate choice is worse than the bug.
|
|
483
|
+
*
|
|
484
|
+
* So it says so instead of guessing. Warned once per process, to stderr, and
|
|
485
|
+
* only when the two disagree — agreement needs no warning.
|
|
486
|
+
*/
|
|
487
|
+
let warnedStrandedPriorConfig = false;
|
|
488
|
+
function warnIfStrandedPriorConfig(current) {
|
|
489
|
+
if (warnedStrandedPriorConfig)
|
|
490
|
+
return;
|
|
491
|
+
// On EVERY command, forever, was the complaint — and it is fair: once you have
|
|
492
|
+
// read it and meant the deployment you are on, it is pure noise burying real
|
|
493
|
+
// stderr. Deleting the old file is one exit; this is the other, for a machine
|
|
494
|
+
// that wants to keep it.
|
|
495
|
+
if ((process.env[QUIET_ENV] ?? '').trim().length > 0)
|
|
496
|
+
return;
|
|
497
|
+
const priorPath = priorConfigFile();
|
|
498
|
+
if (!fs.existsSync(priorPath))
|
|
499
|
+
return;
|
|
500
|
+
let prior;
|
|
501
|
+
try {
|
|
502
|
+
prior = readJsonOrThrow(priorPath);
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
return; // an unreadable prior config is not evidence of anything
|
|
506
|
+
}
|
|
507
|
+
const priorBase = canonicalizeKnownBaseURL(prior.baseURL);
|
|
508
|
+
if (!priorBase || priorBase === canonicalizeKnownBaseURL(current.baseURL))
|
|
509
|
+
return;
|
|
510
|
+
warnedStrandedPriorConfig = true;
|
|
511
|
+
const stranded = strandedSettings(prior);
|
|
512
|
+
const alsoHolds = stranded.length > 0 ? ` (it also holds ${stranded.join(', ')})` : '';
|
|
513
|
+
process.stderr.write(`warning: ${priorPath} points at ${priorBase}; this CLI is using ${current.baseURL}.\n` +
|
|
514
|
+
` Use it instead: skrr login --base-url ${priorBase}\n` +
|
|
515
|
+
` Or silence this: set ${QUIET_ENV}=1, or delete that file${alsoHolds}\n`);
|
|
516
|
+
}
|
|
517
|
+
/** Opt out once you have read the warning and meant the deployment you are on. */
|
|
518
|
+
const QUIET_ENV = 'SKRR_QUIET_CONFIG_WARNING';
|
|
519
|
+
/**
|
|
520
|
+
* The settings that deleting the prior config would discard.
|
|
521
|
+
*
|
|
522
|
+
* The migration did not carry these forward, so the old file is their only
|
|
523
|
+
* copy — naming them is the difference between an instruction and a trap.
|
|
524
|
+
*/
|
|
525
|
+
function strandedSettings(prior) {
|
|
526
|
+
const carried = new Set(['baseURL', 'migrated', 'cliId']);
|
|
527
|
+
return Object.keys(prior)
|
|
528
|
+
.filter((k) => !carried.has(k) && prior[k] !== undefined)
|
|
529
|
+
.sort();
|
|
530
|
+
}
|
|
531
|
+
function newPath() {
|
|
532
|
+
return newConfigFile();
|
|
533
|
+
}
|
|
213
534
|
/** Write the config file with mode 0600 inside a 0700 directory. */
|
|
214
535
|
function saveConfig(config) {
|
|
215
|
-
const dir = newConfigDir();
|
|
216
|
-
if (!fs.existsSync(dir)) {
|
|
217
|
-
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
218
|
-
}
|
|
219
|
-
const p = newConfigFile();
|
|
220
536
|
const canonical = {
|
|
221
537
|
...config,
|
|
222
538
|
baseURL: canonicalizeKnownBaseURL(config.baseURL),
|
|
223
539
|
};
|
|
224
|
-
|
|
225
|
-
|
|
540
|
+
const target = newConfigFile();
|
|
541
|
+
const machineFile = machineConfigFile();
|
|
542
|
+
// Default profile: one file, exactly as before. Nothing is split, nothing
|
|
543
|
+
// moves, and an existing install cannot notice this change.
|
|
544
|
+
if (target === machineFile) {
|
|
545
|
+
writeConfigFile(target, canonical);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
const { machine, profile } = splitByScope(canonical);
|
|
549
|
+
writeConfigFile(target, profile);
|
|
550
|
+
// Machine-scoped fields are MERGED into the machine file rather than
|
|
551
|
+
// replacing it: the default profile's deployment, workspace and account live
|
|
552
|
+
// in that same file, and a named profile writing its own half over the top
|
|
553
|
+
// would sign the default profile out.
|
|
554
|
+
const existingMachine = fs.existsSync(machineFile) ? readJsonOrThrow(machineFile) : {};
|
|
555
|
+
const merged = { ...existingMachine, ...machine };
|
|
556
|
+
if (JSON.stringify(merged) !== JSON.stringify(existingMachine)) {
|
|
557
|
+
writeConfigFile(machineFile, merged);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Warn once when a write happens before the profile was chosen.
|
|
562
|
+
*
|
|
563
|
+
* Such a write lands in the default profile's file whatever `--profile` said,
|
|
564
|
+
* because the choice had not been made yet. It is silent, it is permanent, and
|
|
565
|
+
* the only way anyone finds it is by wondering why a named profile keeps
|
|
566
|
+
* forgetting things. The daemon carries the same guard for the same reason.
|
|
567
|
+
*/
|
|
568
|
+
let warnedUnresolvedProfile = false;
|
|
569
|
+
function warnIfProfileUnresolved(target) {
|
|
570
|
+
if ((0, keychain_1.isProfileResolved)() || warnedUnresolvedProfile)
|
|
571
|
+
return;
|
|
572
|
+
warnedUnresolvedProfile = true;
|
|
573
|
+
process.stderr.write(`[profile] config written to ${target} before the profile was resolved; ` +
|
|
574
|
+
'it landed in the default profile. If this was not the crash path, it is a ' +
|
|
575
|
+
'regression — a writer is running before BaseCommand.init().\n');
|
|
576
|
+
}
|
|
577
|
+
/** Write one config file with the directory created and 0600 enforced. */
|
|
578
|
+
function writeConfigFile(target, contents) {
|
|
579
|
+
warnIfProfileUnresolved(target);
|
|
580
|
+
const dir = path.dirname(target);
|
|
581
|
+
if (!fs.existsSync(dir)) {
|
|
582
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
583
|
+
}
|
|
584
|
+
fs.writeFileSync(target, JSON.stringify(contents, null, 2), { mode: 0o600 });
|
|
585
|
+
fs.chmodSync(target, 0o600);
|
|
226
586
|
}
|
|
227
587
|
/**
|
|
228
588
|
* One-shot migration for clients installed before oversky.dev became the dev
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One shape for "you just created a thing".
|
|
3
|
+
*
|
|
4
|
+
* The four core entities each printed a different confirmation: the identifier
|
|
5
|
+
* was a human ref for a project, a raw uuid for a goal and a space, and both for
|
|
6
|
+
* a task; the separator was an em dash for two and a hyphen for the other two;
|
|
7
|
+
* and goals and spaces appended context (`in space <uuid>`,
|
|
8
|
+
* `(subspace of <uuid>)`) that the link on the next line already carries.
|
|
9
|
+
*
|
|
10
|
+
* Reading four of these in a row, you cannot tell what you are being handed —
|
|
11
|
+
* which is what makes the difference between "did this work" and "what do I
|
|
12
|
+
* paste into the next command" harder than it should be.
|
|
13
|
+
*
|
|
14
|
+
* The rule: prefer the ref a person can type, fall back to the id, and put the
|
|
15
|
+
* title after a single em dash. The `View it at <url>` line that follows is
|
|
16
|
+
* already consistent and is left alone.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createdLine(entity: string, opts: {
|
|
19
|
+
ref?: string | null;
|
|
20
|
+
id?: string | null;
|
|
21
|
+
title?: string | null;
|
|
22
|
+
}): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createdLine = createdLine;
|
|
4
|
+
/**
|
|
5
|
+
* One shape for "you just created a thing".
|
|
6
|
+
*
|
|
7
|
+
* The four core entities each printed a different confirmation: the identifier
|
|
8
|
+
* was a human ref for a project, a raw uuid for a goal and a space, and both for
|
|
9
|
+
* a task; the separator was an em dash for two and a hyphen for the other two;
|
|
10
|
+
* and goals and spaces appended context (`in space <uuid>`,
|
|
11
|
+
* `(subspace of <uuid>)`) that the link on the next line already carries.
|
|
12
|
+
*
|
|
13
|
+
* Reading four of these in a row, you cannot tell what you are being handed —
|
|
14
|
+
* which is what makes the difference between "did this work" and "what do I
|
|
15
|
+
* paste into the next command" harder than it should be.
|
|
16
|
+
*
|
|
17
|
+
* The rule: prefer the ref a person can type, fall back to the id, and put the
|
|
18
|
+
* title after a single em dash. The `View it at <url>` line that follows is
|
|
19
|
+
* already consistent and is left alone.
|
|
20
|
+
*/
|
|
21
|
+
function createdLine(entity, opts) {
|
|
22
|
+
const label = (opts.ref ?? '').trim() || (opts.id ?? '').trim() || '?';
|
|
23
|
+
const title = (opts.title ?? '').trim() || '(no title)';
|
|
24
|
+
return `Created ${entity} ${label} — ${title}`;
|
|
25
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defaults for the flags that decide WHICH deployment and WHICH profile a
|
|
3
|
+
* newly installed daemon binds to.
|
|
4
|
+
*
|
|
5
|
+
* `skrr daemon *` are pass-throughs to the `skrrd` binary, and the two
|
|
6
|
+
* programs keep SEPARATE configuration: the CLI's deployment and profile live
|
|
7
|
+
* in `cli-config.json`, the daemon's in `daemon.json`. Nothing has ever made
|
|
8
|
+
* one follow the other, so the ordinary way to end up split is to do nothing
|
|
9
|
+
* unusual at all — log the CLI into one deployment and leave the daemon on
|
|
10
|
+
* whichever it was installed against. That is OSK-279, observed on this
|
|
11
|
+
* machine as a daemon on dev serving a CLI on prod.
|
|
12
|
+
*
|
|
13
|
+
* The fix is deliberately scoped to INSTALL, the moment a daemon is created
|
|
14
|
+
* and the only moment its binding is not yet somebody's decision. `start` is
|
|
15
|
+
* left alone on purpose: it starts a daemon that already has a binding, and
|
|
16
|
+
* silently overriding it would break the operator who split the two knowingly
|
|
17
|
+
* — running a dev daemon against a prod CLI is a legitimate thing to do, and
|
|
18
|
+
* it is not this function's place to overrule it. `skrr daemon status` reports
|
|
19
|
+
* a split instead of correcting one.
|
|
20
|
+
*/
|
|
21
|
+
/** Whether `argv` already carries `--name` or `--name=value` (or `-alias`). */
|
|
22
|
+
export declare function hasFlag(argv: readonly string[], name: string, alias?: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Append `--server` / `--profile` describing the CLI's own session, for any
|
|
25
|
+
* the caller did not pass. An explicit flag always wins — this only fills a
|
|
26
|
+
* silence, so `skrr daemon install --env prod` still installs against prod
|
|
27
|
+
* while the CLI sits on dev.
|
|
28
|
+
*/
|
|
29
|
+
export declare function bindToCliSession(argv: readonly string[]): string[];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasFlag = hasFlag;
|
|
4
|
+
exports.bindToCliSession = bindToCliSession;
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
const keychain_1 = require("./keychain");
|
|
7
|
+
/**
|
|
8
|
+
* Defaults for the flags that decide WHICH deployment and WHICH profile a
|
|
9
|
+
* newly installed daemon binds to.
|
|
10
|
+
*
|
|
11
|
+
* `skrr daemon *` are pass-throughs to the `skrrd` binary, and the two
|
|
12
|
+
* programs keep SEPARATE configuration: the CLI's deployment and profile live
|
|
13
|
+
* in `cli-config.json`, the daemon's in `daemon.json`. Nothing has ever made
|
|
14
|
+
* one follow the other, so the ordinary way to end up split is to do nothing
|
|
15
|
+
* unusual at all — log the CLI into one deployment and leave the daemon on
|
|
16
|
+
* whichever it was installed against. That is OSK-279, observed on this
|
|
17
|
+
* machine as a daemon on dev serving a CLI on prod.
|
|
18
|
+
*
|
|
19
|
+
* The fix is deliberately scoped to INSTALL, the moment a daemon is created
|
|
20
|
+
* and the only moment its binding is not yet somebody's decision. `start` is
|
|
21
|
+
* left alone on purpose: it starts a daemon that already has a binding, and
|
|
22
|
+
* silently overriding it would break the operator who split the two knowingly
|
|
23
|
+
* — running a dev daemon against a prod CLI is a legitimate thing to do, and
|
|
24
|
+
* it is not this function's place to overrule it. `skrr daemon status` reports
|
|
25
|
+
* a split instead of correcting one.
|
|
26
|
+
*/
|
|
27
|
+
/** Whether `argv` already carries `--name` or `--name=value` (or `-alias`). */
|
|
28
|
+
function hasFlag(argv, name, alias) {
|
|
29
|
+
return argv.some((arg) => {
|
|
30
|
+
if (arg === '--')
|
|
31
|
+
return false;
|
|
32
|
+
return (arg === name ||
|
|
33
|
+
arg.startsWith(`${name}=`) ||
|
|
34
|
+
(alias !== undefined && (arg === alias || arg.startsWith(`${alias}=`))));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Append `--server` / `--profile` describing the CLI's own session, for any
|
|
39
|
+
* the caller did not pass. An explicit flag always wins — this only fills a
|
|
40
|
+
* silence, so `skrr daemon install --env prod` still installs against prod
|
|
41
|
+
* while the CLI sits on dev.
|
|
42
|
+
*/
|
|
43
|
+
function bindToCliSession(argv) {
|
|
44
|
+
const extra = [];
|
|
45
|
+
// `--env` resolves to a server URL inside the daemon, so passing one is
|
|
46
|
+
// passing the other. Adding `--server` beside it would be an ambiguous
|
|
47
|
+
// instruction where the daemon's own precedence, not ours, decides.
|
|
48
|
+
if (!hasFlag(argv, '--server', '-s') && !hasFlag(argv, '--env', '-e')) {
|
|
49
|
+
const { baseURL } = (0, config_1.loadConfig)();
|
|
50
|
+
if (baseURL)
|
|
51
|
+
extra.push('--server', baseURL);
|
|
52
|
+
}
|
|
53
|
+
if (!hasFlag(argv, '--profile')) {
|
|
54
|
+
// The CLI's own profile, resolved the same way `BaseCommand` resolves it.
|
|
55
|
+
// These commands extend `Command`, so `init()` never ran and the module
|
|
56
|
+
// state `getActiveProfile()` reads was never set — asking it here would
|
|
57
|
+
// report `default` no matter what the user typed.
|
|
58
|
+
const profile = (0, keychain_1.resolveProfileFromArgv)(argv) ?? process.env.OVERSKY_PROFILE ?? null;
|
|
59
|
+
if (profile && profile !== keychain_1.DEFAULT_PROFILE)
|
|
60
|
+
extra.push('--profile', profile);
|
|
61
|
+
}
|
|
62
|
+
return [...argv, ...extra];
|
|
63
|
+
}
|