impel-cli 0.20.45 → 0.20.46-beta.2
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/README.md +148 -8
- package/RELEASE_NOTES.md +59 -0
- package/docs/native-agent-host-capability-matrix.md +5 -5
- package/package.json +1 -1
- package/src/agents.js +4 -4
- package/src/apps.js +84 -36
- package/src/autoReport.js +289 -0
- package/src/bugReport.js +499 -0
- package/src/cli.js +61 -5
- package/src/codexSecurity.js +0 -18
- package/src/commands/apps.js +25 -11
- package/src/commands/auth.js +5 -1
- package/src/commands/converge.js +2 -1
- package/src/commands/cursorExperimental.js +74 -2
- package/src/commands/nuke.js +2 -2
- package/src/commands/report.js +309 -0
- package/src/commands/setup.js +134 -1
- package/src/commands/status.js +28 -3
- package/src/commands/update.js +82 -1
- package/src/cursorLocal.js +8 -0
- package/src/desktopTasks.js +420 -787
- package/src/doctor.js +17 -1
- package/src/exitCodes.js +61 -0
- package/src/featureFlags.js +393 -0
- package/src/managedProfileVersion.js +3 -1
- package/src/posthog.js +833 -0
- package/src/runtimeBrand.js +2 -2
- package/src/telemetryConsent.js +334 -0
- package/src/telemetryNotice.js +147 -0
- package/src/tenants.js +48 -0
- package/src/updates.js +26 -1
package/src/commands/setup.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
redactSecretText,
|
|
12
12
|
} from "../config.js";
|
|
13
13
|
import { promptSecret, promptText } from "../prompt.js";
|
|
14
|
-
import { fetchTenants, normalizeTenantId, tenantCredential } from "../tenants.js";
|
|
14
|
+
import { applyCliUser, fetchTenants, normalizeTenantId, tenantCredential } from "../tenants.js";
|
|
15
15
|
import {
|
|
16
16
|
printReconciliationSummary,
|
|
17
17
|
reconcileAllTenants,
|
|
@@ -19,9 +19,11 @@ import {
|
|
|
19
19
|
} from "../provisioning.js";
|
|
20
20
|
import { describeCliFailure, preparePlatformClis } from "../platformSetup.js";
|
|
21
21
|
import { runInstallRecovery } from "../installRecovery/engine.js";
|
|
22
|
+
import { reportInstallFailure } from "../autoReport.js";
|
|
22
23
|
import { refreshUpdateCache, updateNoticeLine } from "../updates.js";
|
|
23
24
|
import { restoreNativeProfiles } from "./use.js";
|
|
24
25
|
import { brandedEnvironmentName, brandedText, RUNTIME_BRAND } from "../runtimeBrand.js";
|
|
26
|
+
import { markTelemetryNoticeShown } from "../telemetryNotice.js";
|
|
25
27
|
import {
|
|
26
28
|
cacheProviderReadiness,
|
|
27
29
|
probeGatewayProviderReadiness,
|
|
@@ -44,6 +46,11 @@ Usage:
|
|
|
44
46
|
impel setup --skip-apps Skip desktop apps
|
|
45
47
|
impel setup --skip-clis Do not install missing vendor CLIs
|
|
46
48
|
impel setup --no-recovery Disable automatic install recovery
|
|
49
|
+
impel setup --analytics on|off Turn anonymous usage analytics on or off
|
|
50
|
+
|
|
51
|
+
Analytics are off unless you turn them on. Separately, when setup or update
|
|
52
|
+
fails, Impel files a bug report so the failure can be fixed; see \`impel report
|
|
53
|
+
--help\`.
|
|
47
54
|
`);
|
|
48
55
|
|
|
49
56
|
/** Kept as a pure compatibility helper for callers/tests. */
|
|
@@ -119,10 +126,66 @@ function recomputeReport(report) {
|
|
|
119
126
|
return report;
|
|
120
127
|
}
|
|
121
128
|
|
|
129
|
+
/**
|
|
130
|
+
* The message for an automatic report about a tenant-convergence failure.
|
|
131
|
+
*
|
|
132
|
+
* Every failed tenant contributes, because a setup that fails two tenants for
|
|
133
|
+
* two different reasons and reports only the first has reported the wrong
|
|
134
|
+
* thing. The tenant id prefixes each error since the ids are otherwise absent
|
|
135
|
+
* from the joined string. `sanitizeInstallFailureEnvelope` bounds the result.
|
|
136
|
+
*/
|
|
137
|
+
function failedTenantMessage(report) {
|
|
138
|
+
const failed = report.tenants.filter((tenant) => tenant.status === "failed");
|
|
139
|
+
const described = failed
|
|
140
|
+
.map((tenant) => `${tenant.tenantId}: ${tenant.errors.join("; ")}`)
|
|
141
|
+
.filter((line) => !line.endsWith(": "));
|
|
142
|
+
return described.join(" | ") || "Tenant readiness verification failed.";
|
|
143
|
+
}
|
|
144
|
+
|
|
122
145
|
function confirmed(answer) {
|
|
123
146
|
return /^(?:y|yes)$/iu.test(String(answer || "").trim());
|
|
124
147
|
}
|
|
125
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Record the analytics preference `--analytics` names, and normalize the key.
|
|
151
|
+
*
|
|
152
|
+
* Setup no longer asks. A question with a default answer is not much of a
|
|
153
|
+
* question, and the one thing `impel setup` must do — get a broken machine
|
|
154
|
+
* working — is not the moment to interrupt with an unrelated one. Analytics
|
|
155
|
+
* stay opt-in and stay switchable: `--analytics on` turns them on, and
|
|
156
|
+
* `--analytics off` (or nothing at all) leaves them off.
|
|
157
|
+
*
|
|
158
|
+
* Every path that is not an explicit `on` removes the key rather than storing
|
|
159
|
+
* `false`, which keeps one meaning for "absent" across the whole codebase —
|
|
160
|
+
* `analyticsConsentGranted` tests for exactly `true`.
|
|
161
|
+
*
|
|
162
|
+
* A branded extension runs its own product and must not inherit Impel's
|
|
163
|
+
* phone-home (KTD11), so `--analytics on` is a no-op there.
|
|
164
|
+
*/
|
|
165
|
+
function applyAnalyticsPreference(config, io, preference) {
|
|
166
|
+
if (RUNTIME_BRAND.cli.packageName !== "impel-cli") return;
|
|
167
|
+
if (preference === "on") {
|
|
168
|
+
config.telemetry = { ...(config.telemetry || {}), enabled: true };
|
|
169
|
+
io.saveConfig(config);
|
|
170
|
+
console.log("✓ Analytics on. Turn them off any time with `impel setup --analytics off`.");
|
|
171
|
+
io.markTelemetryNoticeShown();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Only an explicit `off` revokes a stored yes. A plain `impel setup` — which
|
|
176
|
+
// is also the repair command — must not silently undo a decision on file.
|
|
177
|
+
if (preference === "off" || config.telemetry?.enabled !== true) {
|
|
178
|
+
if (config.telemetry) {
|
|
179
|
+
delete config.telemetry.enabled;
|
|
180
|
+
if (Object.keys(config.telemetry).length === 0) delete config.telemetry;
|
|
181
|
+
io.saveConfig(config);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Written on every path, including the ones that print nothing: having run
|
|
185
|
+
// setup at all is what the first-run notice exists to announce.
|
|
186
|
+
io.markTelemetryNoticeShown();
|
|
187
|
+
}
|
|
188
|
+
|
|
126
189
|
export async function cmdSetup(argv, overrides = {}) {
|
|
127
190
|
const io = {
|
|
128
191
|
loadConfig,
|
|
@@ -134,12 +197,14 @@ export async function cmdSetup(argv, overrides = {}) {
|
|
|
134
197
|
reconcile: reconcileAllTenants,
|
|
135
198
|
probe: probeGateway,
|
|
136
199
|
recoverInstall: runInstallRecovery,
|
|
200
|
+
reportInstallFailure,
|
|
137
201
|
restoreNativeProfiles,
|
|
138
202
|
isTTY: process.stdin.isTTY,
|
|
139
203
|
platform: process.platform,
|
|
140
204
|
environment: process.env,
|
|
141
205
|
refreshUpdateCache,
|
|
142
206
|
updateNoticeLine,
|
|
207
|
+
markTelemetryNoticeShown,
|
|
143
208
|
...overrides,
|
|
144
209
|
};
|
|
145
210
|
if (overrides.prepareWindowsClis && !overrides.preparePlatformClis) {
|
|
@@ -154,6 +219,7 @@ export async function cmdSetup(argv, overrides = {}) {
|
|
|
154
219
|
"skip-clis": { type: "boolean" },
|
|
155
220
|
repair: { type: "boolean" }, // deprecated compatibility no-op
|
|
156
221
|
"no-recovery": { type: "boolean" },
|
|
222
|
+
analytics: { type: "string" },
|
|
157
223
|
help: { type: "boolean" },
|
|
158
224
|
});
|
|
159
225
|
if (flags.help) {
|
|
@@ -165,6 +231,15 @@ export async function cmdSetup(argv, overrides = {}) {
|
|
|
165
231
|
process.exitCode = 1;
|
|
166
232
|
return;
|
|
167
233
|
}
|
|
234
|
+
// Rejected before the token prompt and any network call: a typo in a flag
|
|
235
|
+
// about analytics must not cost the user a setup run to discover.
|
|
236
|
+
// `in`, not `!== undefined`: a trailing bare `--analytics` parses to
|
|
237
|
+
// `undefined`, and silently ignoring it would look like it took effect.
|
|
238
|
+
if ("analytics" in flags && !["on", "off"].includes(flags.analytics)) {
|
|
239
|
+
console.error(`impel setup: --analytics accepts "on" or "off", not "${redactSecretText(flags.analytics)}".`);
|
|
240
|
+
process.exitCode = 1;
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
168
243
|
|
|
169
244
|
const existing = io.loadConfig();
|
|
170
245
|
const gatewayUrl = normalizeGatewayUrl(flags.gateway || existing?.gatewayUrl || resolveDefaultGateway());
|
|
@@ -203,6 +278,7 @@ export async function cmdSetup(argv, overrides = {}) {
|
|
|
203
278
|
config.tenantName = selected.name;
|
|
204
279
|
if (listing.productAccess) config.productAccess = listing.productAccess;
|
|
205
280
|
if (listing.scopes) config.scopes = listing.scopes;
|
|
281
|
+
applyCliUser(config, listing);
|
|
206
282
|
config.tenantsUpdatedAt = new Date().toISOString();
|
|
207
283
|
io.saveConfig(config);
|
|
208
284
|
|
|
@@ -212,6 +288,11 @@ export async function cmdSetup(argv, overrides = {}) {
|
|
|
212
288
|
console.log(` ${tenant.id}${tenant.id === selected.id ? " (CLI default)" : ""} — ${tenant.name}`);
|
|
213
289
|
}
|
|
214
290
|
|
|
291
|
+
// After the credential is stored and the tenant chosen, so the preference is
|
|
292
|
+
// written into a config that already exists, and before convergence starts
|
|
293
|
+
// printing per-tenant progress the confirmation would be buried in.
|
|
294
|
+
applyAnalyticsPreference(config, io, flags.analytics);
|
|
295
|
+
|
|
215
296
|
// Setup is where a stale CLI hurts most (it re-hits installer bugs newer
|
|
216
297
|
// releases already fixed), and on a first run the launch-time notice cache
|
|
217
298
|
// is still empty — so check synchronously here. The token was just verified
|
|
@@ -503,6 +584,58 @@ export async function cmdSetup(argv, overrides = {}) {
|
|
|
503
584
|
if (sharedFailure) console.error(`Shared setup failure: ${sharedFailure}`);
|
|
504
585
|
const setupCommand = io.platform === "win32" ? "impel.cmd setup" : "impel setup";
|
|
505
586
|
console.error(`Setup is incomplete. Fix the reported issue or rerun \`${setupCommand}\`.`);
|
|
587
|
+
const failedTenantIds = report.tenants
|
|
588
|
+
.filter((tenant) => tenant.status === "failed")
|
|
589
|
+
.map((tenant) => tenant.tenantId);
|
|
590
|
+
// The one dispatch point for this command (R1). It sits here rather than
|
|
591
|
+
// in either recovery block above because a failure recovery repaired is
|
|
592
|
+
// not a failure (KTD2) — this branch is reached only by a setup that ran
|
|
593
|
+
// out of ways to fix itself. One report per run, whatever the tenant count.
|
|
594
|
+
//
|
|
595
|
+
// After the two `console.error`s so the actionable "rerun `impel setup`"
|
|
596
|
+
// stays the last thing a reader scrolls back to, and before
|
|
597
|
+
// `process.exitCode` because the report must not depend on the exit path.
|
|
598
|
+
//
|
|
599
|
+
// `reportInstallFailure` swallows its own errors, but it is an injection
|
|
600
|
+
// seam: this `catch` is the promise that whatever is behind it, a setup
|
|
601
|
+
// that failed with a clear message never becomes a setup that crashed.
|
|
602
|
+
try {
|
|
603
|
+
await io.reportInstallFailure({
|
|
604
|
+
failure: sharedFailure
|
|
605
|
+
? {
|
|
606
|
+
scope: "shared",
|
|
607
|
+
platform: io.platform,
|
|
608
|
+
architecture: process.arch,
|
|
609
|
+
step: "setup.shared_vendor_clis",
|
|
610
|
+
message: sharedFailure,
|
|
611
|
+
diagnostics: { tenantId: selected.id },
|
|
612
|
+
}
|
|
613
|
+
: {
|
|
614
|
+
// Attribute the report to a tenant that actually failed, not to
|
|
615
|
+
// the CLI default: `selected.id` is whichever tenant this machine
|
|
616
|
+
// points at, which may well have converged fine. With more than one
|
|
617
|
+
// failure there is no single owner, so the report is `shared` and
|
|
618
|
+
// the ids ride along in diagnostics — the message already names
|
|
619
|
+
// every one of them.
|
|
620
|
+
...(failedTenantIds.length === 1
|
|
621
|
+
? { scope: "tenant", tenantId: failedTenantIds[0] }
|
|
622
|
+
: { scope: "shared" }),
|
|
623
|
+
platform: io.platform,
|
|
624
|
+
architecture: process.arch,
|
|
625
|
+
step: "setup.tenant_convergence",
|
|
626
|
+
message: failedTenantMessage(report),
|
|
627
|
+
diagnostics: {
|
|
628
|
+
cliTenantId: selected.id,
|
|
629
|
+
failedTenantIds: failedTenantIds.join(","),
|
|
630
|
+
failedTenants: String(failedTenantIds.length),
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
config,
|
|
634
|
+
io,
|
|
635
|
+
});
|
|
636
|
+
} catch {
|
|
637
|
+
// R10: one error message, and it is the one above.
|
|
638
|
+
}
|
|
506
639
|
process.exitCode = 1;
|
|
507
640
|
return report;
|
|
508
641
|
}
|
package/src/commands/status.js
CHANGED
|
@@ -10,10 +10,12 @@ import { findReviewedVendorCliBinary } from "../vendorCliBinaries.js";
|
|
|
10
10
|
import { windowsTenantShortcutName } from "../shellEntries.js";
|
|
11
11
|
import {
|
|
12
12
|
ensureTenantSelection,
|
|
13
|
+
normalizeCliUser,
|
|
13
14
|
PAT_SCOPE_CLAUDE,
|
|
14
15
|
PAT_SCOPE_CODEX,
|
|
15
16
|
productAccessLabel,
|
|
16
17
|
} from "../tenants.js";
|
|
18
|
+
import { telemetryNoticeLine } from "../posthog.js";
|
|
17
19
|
import { installedVersion, maybePrintUpdateNotice } from "../updates.js";
|
|
18
20
|
import { findManagedWindowsChatGPTApp, windowsClaudeUserData } from "../windowsApps.js";
|
|
19
21
|
import { RUNTIME_BRAND } from "../runtimeBrand.js";
|
|
@@ -85,6 +87,24 @@ function cliState({ supported, available = true, profileReady, binaryReady }) {
|
|
|
85
87
|
return profileReady && binaryReady ? "ready" : "missing";
|
|
86
88
|
}
|
|
87
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Everything `impel status` prints on its way out, from every exit.
|
|
92
|
+
*
|
|
93
|
+
* There are three: no PAT, an authentication check that did not come back, and
|
|
94
|
+
* the full report. The first two are precisely the states a broken install is
|
|
95
|
+
* in — a notice wired only to the tail would be invisible exactly when someone
|
|
96
|
+
* runs `status` to find out what is wrong.
|
|
97
|
+
*
|
|
98
|
+
* `doctor` deliberately does not carry this. It runs synthetic, billable
|
|
99
|
+
* end-to-end gateway checks; a local drop notice should not cost a request.
|
|
100
|
+
*/
|
|
101
|
+
function printLocalNotices(io) {
|
|
102
|
+
if (RUNTIME_BRAND.cli.packageName !== "impel-cli") return;
|
|
103
|
+
const telemetry = io.telemetryNoticeLine();
|
|
104
|
+
if (telemetry) console.log(telemetry);
|
|
105
|
+
io.maybePrintUpdateNotice();
|
|
106
|
+
}
|
|
107
|
+
|
|
88
108
|
export async function cmdStatus(overrides = {}) {
|
|
89
109
|
const io = {
|
|
90
110
|
loadConfig,
|
|
@@ -94,6 +114,7 @@ export async function cmdStatus(overrides = {}) {
|
|
|
94
114
|
installedVersion,
|
|
95
115
|
findNativeBinary: findReviewedVendorCliBinary,
|
|
96
116
|
maybePrintUpdateNotice,
|
|
117
|
+
telemetryNoticeLine,
|
|
97
118
|
platform: process.platform,
|
|
98
119
|
environment: process.env,
|
|
99
120
|
...overrides,
|
|
@@ -102,8 +123,12 @@ export async function cmdStatus(overrides = {}) {
|
|
|
102
123
|
const version = process.env.IMPEL_CLI_EXTENSION_VERSION || io.installedVersion() || "?";
|
|
103
124
|
console.log(`${RUNTIME_BRAND.cli.command}-cli: v${version}`);
|
|
104
125
|
console.log(`Authentication: ${config?.pat ? `configured (${maskSecret(config.pat)})` : "not configured - run `impel setup`"}`);
|
|
126
|
+
// Read back through the same normalizer that wrote it: config.json is a file
|
|
127
|
+
// on disk, so what it holds is untrusted input by the time it is printed.
|
|
128
|
+
const user = normalizeCliUser(config?.user, { allowMissing: true });
|
|
129
|
+
if (user) console.log(`Signed in as: ${user.displayName || user.id}`);
|
|
105
130
|
if (!config?.pat) {
|
|
106
|
-
|
|
131
|
+
printLocalNotices(io);
|
|
107
132
|
return;
|
|
108
133
|
}
|
|
109
134
|
|
|
@@ -117,7 +142,7 @@ export async function cmdStatus(overrides = {}) {
|
|
|
117
142
|
console.log(RUNTIME_BRAND.tenant.defaultId
|
|
118
143
|
? `Run \`${RUNTIME_BRAND.cli.command} setup\` to refresh authentication and repair the local tenant.`
|
|
119
144
|
: "Run `impel setup` to refresh authentication, then `impel update` to repair local tenants.");
|
|
120
|
-
|
|
145
|
+
printLocalNotices(io);
|
|
121
146
|
return;
|
|
122
147
|
}
|
|
123
148
|
|
|
@@ -162,5 +187,5 @@ export async function cmdStatus(overrides = {}) {
|
|
|
162
187
|
? `Repair or finish missing tenant surfaces with: ${RUNTIME_BRAND.cli.command} setup`
|
|
163
188
|
: "Repair or finish missing tenant surfaces with: impel update");
|
|
164
189
|
}
|
|
165
|
-
|
|
190
|
+
printLocalNotices(io);
|
|
166
191
|
}
|
package/src/commands/update.js
CHANGED
|
@@ -10,6 +10,7 @@ import { loadConfig, redactSecretText } from "../config.js";
|
|
|
10
10
|
import { nativeCommandInvocation } from "../nativeProcess.js";
|
|
11
11
|
import { withProgress } from "../progress.js";
|
|
12
12
|
import { runInstallRecovery } from "../installRecovery/engine.js";
|
|
13
|
+
import { reportInstallFailure } from "../autoReport.js";
|
|
13
14
|
import { brandedEnvironmentName, brandedText, RUNTIME_BRAND } from "../runtimeBrand.js";
|
|
14
15
|
import { IMPEL_CLI_ENTRYPOINT } from "../selfInvocation.js";
|
|
15
16
|
import {
|
|
@@ -252,6 +253,7 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
252
253
|
environment: process.env,
|
|
253
254
|
progress: withProgress,
|
|
254
255
|
recoverInstall: runInstallRecovery,
|
|
256
|
+
reportInstallFailure,
|
|
255
257
|
postInstallVersion: postInstallCliVersion,
|
|
256
258
|
postInstallRunnable: postInstallCliRunnable,
|
|
257
259
|
resolveFreshInstall: resolveFreshGlobalEntrypoint,
|
|
@@ -315,6 +317,36 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
315
317
|
return;
|
|
316
318
|
}
|
|
317
319
|
|
|
320
|
+
// Read at most once, and only when something actually needs it. All three
|
|
321
|
+
// terminal failures do, which is why this is not still buried in the npm
|
|
322
|
+
// branch — but an `impel update` that succeeds must not pay a config read it
|
|
323
|
+
// has no use for, and this whole feature is supposed to cost the success
|
|
324
|
+
// path nothing.
|
|
325
|
+
let configLoaded = false;
|
|
326
|
+
let configValue;
|
|
327
|
+
const currentConfig = () => {
|
|
328
|
+
if (!configLoaded) {
|
|
329
|
+
configValue = io.loadConfig();
|
|
330
|
+
configLoaded = true;
|
|
331
|
+
}
|
|
332
|
+
return configValue;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* File one automatic report for a terminal `impel update` failure.
|
|
337
|
+
*
|
|
338
|
+
* Each caller has already printed the message the user needs to act on, so
|
|
339
|
+
* this must not add a second one: `reportInstallFailure` swallows its own
|
|
340
|
+
* errors and the `catch` covers whatever an injected reporter does (R10).
|
|
341
|
+
*/
|
|
342
|
+
const reportUpdateFailure = async (failure) => {
|
|
343
|
+
try {
|
|
344
|
+
await io.reportInstallFailure({ failure, config: currentConfig(), io });
|
|
345
|
+
} catch {
|
|
346
|
+
// R10: one error message, and it is the one the caller already printed.
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
318
350
|
// ── CLI ────────────────────────────────────────────────────────────────
|
|
319
351
|
let cascadeEntrypoint = null;
|
|
320
352
|
if (!remote) {
|
|
@@ -340,8 +372,8 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
340
372
|
console.error(" Verify `npm --version` in PowerShell, then retry `impel update`.");
|
|
341
373
|
console.error(` Manual recovery: \`npm install --global ${installSpec}\`.`);
|
|
342
374
|
}
|
|
343
|
-
const config = io.loadConfig();
|
|
344
375
|
let recovered = false;
|
|
376
|
+
const config = currentConfig();
|
|
345
377
|
if (config?.pat && config?.tenantId) {
|
|
346
378
|
// The goal passes on the most recent reviewed retry of the npm
|
|
347
379
|
// install OR on a live disk probe of the package that owns the
|
|
@@ -399,6 +431,19 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
399
431
|
}
|
|
400
432
|
}
|
|
401
433
|
if (!recovered) {
|
|
434
|
+
// Past recovery, so this is a failure that ran out of fixes (KTD2).
|
|
435
|
+
await reportUpdateFailure({
|
|
436
|
+
scope: "shared",
|
|
437
|
+
platform: io.platform,
|
|
438
|
+
architecture: process.arch,
|
|
439
|
+
step: "install.impel_cli",
|
|
440
|
+
command: `npm install --global ${installSpec}`,
|
|
441
|
+
message: `The npm-verified global ${RUNTIME_BRAND.cli.packageName} update failed.`,
|
|
442
|
+
diagnostics: {
|
|
443
|
+
installedVersion: String(current ?? "unknown"),
|
|
444
|
+
remoteVersion: String(remote ?? "unknown"),
|
|
445
|
+
},
|
|
446
|
+
});
|
|
402
447
|
process.exitCode = 1;
|
|
403
448
|
return;
|
|
404
449
|
}
|
|
@@ -426,6 +471,22 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
426
471
|
console.error(` Running CLI: ${CLI_BIN}`);
|
|
427
472
|
console.error(" This usually means the `impel` on PATH belongs to a different npm prefix than `npm prefix -g`.");
|
|
428
473
|
console.error(` Fix: run \`npm prefix -g\`, confirm it owns the \`impel\` shim on PATH, then \`npm install --global ${installSpec}\` there.`);
|
|
474
|
+
// Its own step, not `install.impel_cli`: npm succeeded and the update
|
|
475
|
+
// landed somewhere real. The reportable fact is the prefix skew, and
|
|
476
|
+
// the two versions below are what makes a report of it actionable.
|
|
477
|
+
await reportUpdateFailure({
|
|
478
|
+
scope: "shared",
|
|
479
|
+
platform: io.platform,
|
|
480
|
+
architecture: process.arch,
|
|
481
|
+
step: "install.impel_cli_prefix_skew",
|
|
482
|
+
command: `npm install --global ${installSpec}`,
|
|
483
|
+
message: `npm reported success but the running CLI still resolves v${postInstall ?? "?"} (expected v${remote}), and the freshly installed build could not be located.`,
|
|
484
|
+
diagnostics: {
|
|
485
|
+
runningVersion: String(postInstall ?? "unknown"),
|
|
486
|
+
expectedVersion: String(remote),
|
|
487
|
+
cliBin: CLI_BIN,
|
|
488
|
+
},
|
|
489
|
+
});
|
|
429
490
|
process.exitCode = 1;
|
|
430
491
|
return;
|
|
431
492
|
}
|
|
@@ -449,6 +510,26 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
449
510
|
};
|
|
450
511
|
if (!await io.runConvergence(convergenceArgs)) {
|
|
451
512
|
console.error("impel update: tenant convergence failed; rerun `impel update` after addressing the reported issue.");
|
|
513
|
+
// The child is `impel _converge`, which files no report of its own — and
|
|
514
|
+
// this parent only sees its exit status, because the child inherits stdio
|
|
515
|
+
// so its progress reaches the terminal live. So this report carries what
|
|
516
|
+
// the parent does know: that it was an update cascade, and which flags
|
|
517
|
+
// shaped the run it failed under. Which tenants failed is visible on the
|
|
518
|
+
// user's screen but not in the report; closing that gap needs a channel
|
|
519
|
+
// between the two processes, which R2 (one report per invocation) says must
|
|
520
|
+
// not simply be a second report from the child.
|
|
521
|
+
await reportUpdateFailure({
|
|
522
|
+
scope: "shared",
|
|
523
|
+
platform: io.platform,
|
|
524
|
+
architecture: process.arch,
|
|
525
|
+
step: "update.tenant_convergence",
|
|
526
|
+
message: "Tenant convergence failed during `impel update`.",
|
|
527
|
+
diagnostics: {
|
|
528
|
+
skipApps: String(Boolean(flags["skip-apps"])),
|
|
529
|
+
skipClis: String(Boolean(flags["skip-clis"])),
|
|
530
|
+
cascadedEntrypoint: String(Boolean(cascadeEntrypoint)),
|
|
531
|
+
},
|
|
532
|
+
});
|
|
452
533
|
process.exitCode = 1;
|
|
453
534
|
return;
|
|
454
535
|
}
|
package/src/cursorLocal.js
CHANGED
|
@@ -1473,6 +1473,14 @@ export function cursorLaunchSpec({
|
|
|
1473
1473
|
}
|
|
1474
1474
|
const env = { ...environment };
|
|
1475
1475
|
for (const key of [...DIRECT_PROVIDER_ENV_KEYS, ...RUNTIME_OVERRIDE_ENV_KEYS]) delete env[key];
|
|
1476
|
+
// The positive managed marker `telemetryConsent.js` denies on. It has to be
|
|
1477
|
+
// stamped here rather than inferred: the delete loop above removes
|
|
1478
|
+
// CLAUDE_CONFIG_DIR and CODEX_HOME, and `HOME` below points at the managed
|
|
1479
|
+
// profile, so every path the guard could otherwise key on either disappears
|
|
1480
|
+
// or stops looking managed. Without it a terminal inside managed Cursor —
|
|
1481
|
+
// holding CURSOR_LOCAL_AGENT_API_KEY — would capture and flush telemetry to a
|
|
1482
|
+
// host other than the gateway, which is the P1-8 egress boundary.
|
|
1483
|
+
env[brandedEnvironmentName("MANAGED_CURSOR")] = "1";
|
|
1476
1484
|
env.HOME = paths.home;
|
|
1477
1485
|
env.XDG_CACHE_HOME = path.join(paths.home, ".cache");
|
|
1478
1486
|
env.XDG_CONFIG_HOME = path.join(paths.home, ".config");
|