impel-cli 0.18.15-beta.8 → 0.18.15
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 +0 -49
- package/package.json +1 -2
- package/src/agents.js +173 -8
- package/src/apps.js +43 -211
- package/src/commands/apps.js +8 -43
- package/src/commands/converge.js +3 -18
- package/src/commands/experimental.js +2 -8
- package/src/commands/launch.js +2 -11
- package/src/commands/status.js +2 -3
- package/src/commands/update.js +11 -23
- package/src/macSetup.js +38 -20
- package/src/provisioning.js +4 -10
- package/src/skills.js +8 -24
- package/src/updates.js +10 -57
- package/src/windowsApps.js +17 -61
- package/src/windowsSetup.js +4 -7
- package/docs/experimental-managed-cursor.md +0 -205
- package/src/commands/cursorExperimental.js +0 -192
- package/src/cursorLocal.js +0 -1549
- package/src/vendorCliBinaries.js +0 -121
- package/src/vendorCliVersions.js +0 -7
package/src/commands/launch.js
CHANGED
|
@@ -20,9 +20,8 @@ import { assertProviderScopes, ensureTenantSelection, tenantCredential } from ".
|
|
|
20
20
|
import { maybePrintUpdateNotice } from "../updates.js";
|
|
21
21
|
import {
|
|
22
22
|
nativeSpawnInvocation,
|
|
23
|
+
resolveNativeBinary,
|
|
23
24
|
} from "../nativeProcess.js";
|
|
24
|
-
import { resolveReviewedVendorCliBinary } from "../vendorCliBinaries.js";
|
|
25
|
-
import { PINNED_VENDOR_CLI_VERSIONS } from "../vendorCliVersions.js";
|
|
26
25
|
import { RUNTIME_BRAND } from "../runtimeBrand.js";
|
|
27
26
|
import {
|
|
28
27
|
cacheProviderReadiness,
|
|
@@ -116,15 +115,7 @@ async function assertLiveProviderReadiness({ config, gatewayUrl, credential, ten
|
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
function runNativeCli(tool, argv, environment) {
|
|
119
|
-
const binary =
|
|
120
|
-
if (!binary) {
|
|
121
|
-
const version = PINNED_VENDOR_CLI_VERSIONS[tool];
|
|
122
|
-
console.error(
|
|
123
|
-
`impel ${tool}: reviewed ${tool === "claude" ? "Claude Code" : "Codex"} CLI `
|
|
124
|
-
+ `v${version} is not installed. Run \`impel update\` to repair it.`,
|
|
125
|
-
);
|
|
126
|
-
return Promise.resolve(127);
|
|
127
|
-
}
|
|
118
|
+
const binary = resolveNativeBinary(tool, environment);
|
|
128
119
|
let invocation;
|
|
129
120
|
try {
|
|
130
121
|
invocation = nativeSpawnInvocation(binary, argv, environment);
|
package/src/commands/status.js
CHANGED
|
@@ -4,9 +4,8 @@ import path from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { appPaths, CLAUDE_CONFIG_ID, readTenantManifest } from "../apps.js";
|
|
6
6
|
import { crossAppModelsEnabled, loadConfig, maskSecret } from "../config.js";
|
|
7
|
-
import { environmentValue } from "../nativeProcess.js";
|
|
7
|
+
import { environmentValue, findNativeBinary } from "../nativeProcess.js";
|
|
8
8
|
import { tenantCliClientReadiness } from "../provisioning.js";
|
|
9
|
-
import { findReviewedVendorCliBinary } from "../vendorCliBinaries.js";
|
|
10
9
|
import { windowsTenantShortcutName } from "../shellEntries.js";
|
|
11
10
|
import {
|
|
12
11
|
ensureTenantSelection,
|
|
@@ -87,7 +86,7 @@ export async function cmdStatus(overrides = {}) {
|
|
|
87
86
|
cliReadiness: tenantCliClientReadiness,
|
|
88
87
|
desktopReadiness,
|
|
89
88
|
installedVersion,
|
|
90
|
-
findNativeBinary
|
|
89
|
+
findNativeBinary,
|
|
91
90
|
maybePrintUpdateNotice,
|
|
92
91
|
platform: process.platform,
|
|
93
92
|
environment: process.env,
|
package/src/commands/update.js
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
isNewerVersion,
|
|
19
19
|
refreshUpdateCache,
|
|
20
20
|
updateInstallSpec,
|
|
21
|
-
updateTagForVersion,
|
|
22
21
|
writeUpdateCache,
|
|
23
22
|
} from "../updates.js";
|
|
24
23
|
|
|
@@ -201,41 +200,30 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
201
200
|
}
|
|
202
201
|
|
|
203
202
|
const current = io.installedVersion();
|
|
204
|
-
const
|
|
205
|
-
const installSpec = updateInstallSpec(updateTag);
|
|
206
|
-
const remote = await io.progress(
|
|
207
|
-
"Checking npm for impel-cli updates",
|
|
208
|
-
() => io.fetchRemoteVersion({ tag: updateTag }),
|
|
209
|
-
);
|
|
203
|
+
const remote = await io.progress("Checking npm for impel-cli updates", () => io.fetchRemoteVersion());
|
|
210
204
|
if (remote) io.writeCache({ remoteVersion: remote, checkedAt: Date.now() });
|
|
211
205
|
|
|
212
206
|
console.log(`impel-cli v${current ?? "?"}`);
|
|
213
|
-
console.log(`npm
|
|
207
|
+
console.log(`npm latest: ${remote ? `v${remote}` : "unknown — registry check failed"}`);
|
|
214
208
|
|
|
215
209
|
const updateAvailable = Boolean(current && remote && isNewerVersion(remote, current));
|
|
216
210
|
const upToDate = Boolean(current && remote && !updateAvailable);
|
|
217
211
|
if (flags.check) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
process.exitCode = 1;
|
|
221
|
-
} else {
|
|
222
|
-
console.log(upToDate ? "Up to date." : "Update available: run `impel update`.");
|
|
223
|
-
}
|
|
212
|
+
console.log(upToDate ? "Up to date." : "Update available: run `impel update`.");
|
|
213
|
+
if (!remote) process.exitCode = 1;
|
|
224
214
|
return;
|
|
225
215
|
}
|
|
226
216
|
|
|
227
217
|
// ── CLI ────────────────────────────────────────────────────────────────
|
|
228
|
-
if (
|
|
229
|
-
console.warn("CLI: npm update check unavailable; keeping the installed build.");
|
|
230
|
-
} else if (upToDate) {
|
|
218
|
+
if (upToDate) {
|
|
231
219
|
console.log("CLI: already up to date.");
|
|
232
220
|
} else {
|
|
233
|
-
console.log(
|
|
234
|
-
if (!await io.progress(
|
|
221
|
+
console.log("CLI: installing the latest build…");
|
|
222
|
+
if (!await io.progress("Installing the latest impel-cli build", () => io.selfUpdate(updateInstallSpec()))) {
|
|
235
223
|
console.error("impel update: `npm install -g` failed; the CLI was not updated.");
|
|
236
224
|
if (io.platform === "win32") {
|
|
237
225
|
console.error(" Verify `npm --version` in PowerShell, then retry `impel update`.");
|
|
238
|
-
console.error(
|
|
226
|
+
console.error(" Manual recovery: `npm install --global impel-cli@latest`.");
|
|
239
227
|
}
|
|
240
228
|
const config = io.loadConfig();
|
|
241
229
|
let recovered = false;
|
|
@@ -251,7 +239,7 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
251
239
|
platform: io.platform,
|
|
252
240
|
architecture: process.arch,
|
|
253
241
|
step: "install.impel_cli",
|
|
254
|
-
command: `npm install --global ${
|
|
242
|
+
command: `npm install --global ${RUNTIME_BRAND.cli.packageName}@latest`,
|
|
255
243
|
message: `The npm-verified global ${RUNTIME_BRAND.cli.packageName} update failed.`,
|
|
256
244
|
},
|
|
257
245
|
config,
|
|
@@ -277,7 +265,7 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
277
265
|
}
|
|
278
266
|
},
|
|
279
267
|
retryStep: async () => {
|
|
280
|
-
updateState.ok = io.selfUpdate(
|
|
268
|
+
updateState.ok = io.selfUpdate(updateInstallSpec()) === true;
|
|
281
269
|
return updateState.ok;
|
|
282
270
|
},
|
|
283
271
|
},
|
|
@@ -306,7 +294,7 @@ export async function cmdUpdate(argv, overrides = {}) {
|
|
|
306
294
|
console.error(`impel update: npm reported success but the running CLI still resolves v${postInstall ?? "?"} (expected v${remote}).`);
|
|
307
295
|
console.error(` Running CLI: ${CLI_BIN}`);
|
|
308
296
|
console.error(" This usually means the `impel` on PATH belongs to a different npm prefix than `npm prefix -g`.");
|
|
309
|
-
console.error(
|
|
297
|
+
console.error(" Fix: run `npm prefix -g`, confirm it owns the `impel` shim on PATH, then `npm install --global impel-cli@latest` there.");
|
|
310
298
|
process.exitCode = 1;
|
|
311
299
|
return;
|
|
312
300
|
}
|
package/src/macSetup.js
CHANGED
|
@@ -7,12 +7,11 @@ import { spawnSync } from "node:child_process";
|
|
|
7
7
|
import { ensureImpelClaudeProfile, ensureImpelCodexProfile } from "./cliProfiles.js";
|
|
8
8
|
import { findNativeBinary } from "./nativeProcess.js";
|
|
9
9
|
import { syncSkillsSafe } from "./skills.js";
|
|
10
|
-
import { verifyReviewedMacVendorCli } from "./vendorCliBinaries.js";
|
|
11
|
-
import { PINNED_VENDOR_CLI_VERSIONS } from "./vendorCliVersions.js";
|
|
12
10
|
|
|
13
11
|
const MAX_INSTALLER_BYTES = 512 * 1024;
|
|
14
12
|
const INSTALLER_DOWNLOAD_TIMEOUT_MS = 30_000;
|
|
15
13
|
const INSTALLER_RUN_TIMEOUT_MS = 10 * 60 * 1_000;
|
|
14
|
+
const MAC_SIGNING_TEAMS = Object.freeze({ claude: "Q6L2SF6YDW", codex: "2DC432GLL2" });
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* The script bytes are pinned per impel-cli release. Both vendor scripts then
|
|
@@ -20,34 +19,59 @@ const INSTALLER_RUN_TIMEOUT_MS = 10 * 60 * 1_000;
|
|
|
20
19
|
*/
|
|
21
20
|
export const MAC_CLI_INSTALLERS = Object.freeze({
|
|
22
21
|
claude: Object.freeze({
|
|
23
|
-
version: PINNED_VENDOR_CLI_VERSIONS.claude,
|
|
24
22
|
url: "https://claude.ai/install.sh",
|
|
25
23
|
trustedOrigins: Object.freeze([
|
|
26
24
|
"https://claude.ai",
|
|
27
25
|
"https://downloads.claude.ai",
|
|
28
26
|
]),
|
|
29
27
|
sha256: "cde4f1702d3b1695f92b73d26888364e17bca476e17f0fd676484c951d36c125",
|
|
30
|
-
command:
|
|
31
|
-
args: Object.freeze([
|
|
28
|
+
command: "curl -fsSL https://claude.ai/install.sh | bash -s stable",
|
|
29
|
+
args: Object.freeze(["stable"]),
|
|
32
30
|
environment: Object.freeze({}),
|
|
33
31
|
}),
|
|
34
32
|
codex: Object.freeze({
|
|
35
|
-
version: PINNED_VENDOR_CLI_VERSIONS.codex,
|
|
36
33
|
url: "https://chatgpt.com/codex/install.sh",
|
|
37
34
|
trustedOrigins: Object.freeze([
|
|
38
35
|
"https://chatgpt.com",
|
|
39
36
|
"https://releases.openai.com",
|
|
40
37
|
]),
|
|
41
38
|
sha256: "ba92dd27e5c06f0d3bbc58bfa4b9cfb6599cd2742fbb1f92a2765e6c07dedb5a",
|
|
42
|
-
command:
|
|
39
|
+
command: "curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 CODEX_RELEASE=0.144.6 sh",
|
|
43
40
|
args: Object.freeze([]),
|
|
44
|
-
environment: Object.freeze({
|
|
45
|
-
CODEX_NON_INTERACTIVE: "1",
|
|
46
|
-
CODEX_RELEASE: PINNED_VENDOR_CLI_VERSIONS.codex,
|
|
47
|
-
}),
|
|
41
|
+
environment: Object.freeze({ CODEX_NON_INTERACTIVE: "1", CODEX_RELEASE: "0.144.6" }),
|
|
48
42
|
}),
|
|
49
43
|
});
|
|
50
44
|
|
|
45
|
+
function verifyMacCli(tool, binary, environment, run = spawnSync) {
|
|
46
|
+
try {
|
|
47
|
+
const result = run(binary, ["--version"], {
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
env: environment,
|
|
50
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
51
|
+
timeout: 15_000,
|
|
52
|
+
});
|
|
53
|
+
if (result?.status !== 0 || result?.error) return false;
|
|
54
|
+
const explicitOverride = tool === "claude" ? environment.IMPEL_CLAUDE_BIN : environment.IMPEL_CODEX_BIN;
|
|
55
|
+
if (explicitOverride && path.resolve(explicitOverride) === path.resolve(binary)) return true;
|
|
56
|
+
const resolved = fs.realpathSync(binary);
|
|
57
|
+
const verified = run("/usr/bin/codesign", ["--verify", "--strict", resolved], {
|
|
58
|
+
encoding: "utf8",
|
|
59
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
60
|
+
timeout: 15_000,
|
|
61
|
+
});
|
|
62
|
+
if (verified?.status !== 0 || verified?.error) return false;
|
|
63
|
+
const details = run("/usr/bin/codesign", ["-dv", "--verbose=4", resolved], {
|
|
64
|
+
encoding: "utf8",
|
|
65
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
+
timeout: 15_000,
|
|
67
|
+
});
|
|
68
|
+
return details?.status === 0
|
|
69
|
+
&& String(details.stderr || details.stdout || "").includes(`TeamIdentifier=${MAC_SIGNING_TEAMS[tool]}`);
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
51
75
|
function detectMacClis(find, environment, verify) {
|
|
52
76
|
const detect = (tool) => find(
|
|
53
77
|
tool,
|
|
@@ -176,7 +200,7 @@ export async function prepareMacClis({
|
|
|
176
200
|
const io = {
|
|
177
201
|
environment: process.env,
|
|
178
202
|
find: findNativeBinary,
|
|
179
|
-
verify:
|
|
203
|
+
verify: verifyMacCli,
|
|
180
204
|
run: spawnSync,
|
|
181
205
|
fetchImpl: fetch,
|
|
182
206
|
fsImpl: fs,
|
|
@@ -228,10 +252,7 @@ export async function prepareMacClis({
|
|
|
228
252
|
await io.syncSkills({
|
|
229
253
|
client: "claude",
|
|
230
254
|
gatewayUrl,
|
|
231
|
-
env: {
|
|
232
|
-
CLAUDE_CONFIG_DIR: claudeProfile.configDir,
|
|
233
|
-
IMPEL_CLAUDE_BIN: binaries.claude,
|
|
234
|
-
},
|
|
255
|
+
env: { CLAUDE_CONFIG_DIR: claudeProfile.configDir },
|
|
235
256
|
label: "Impel isolated Claude (impel claude)",
|
|
236
257
|
// Lets Windows spawns use a git-safe cwd (see skillSyncSpawnDirectory).
|
|
237
258
|
homeDir: os.homedir(),
|
|
@@ -241,10 +262,7 @@ export async function prepareMacClis({
|
|
|
241
262
|
await io.syncSkills({
|
|
242
263
|
client: "codex",
|
|
243
264
|
gatewayUrl,
|
|
244
|
-
env: {
|
|
245
|
-
CODEX_HOME: codexProfile.codexHome,
|
|
246
|
-
IMPEL_CODEX_BIN: binaries.codex,
|
|
247
|
-
},
|
|
265
|
+
env: { CODEX_HOME: codexProfile.codexHome },
|
|
248
266
|
label: "Impel isolated Codex (impel codex)",
|
|
249
267
|
homeDir: os.homedir(),
|
|
250
268
|
});
|
package/src/provisioning.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { syncAgentProfilesSafe } from "./agents.js";
|
|
6
6
|
import { crossAppModelsEnabled, redactSecretText } from "./config.js";
|
|
7
7
|
import { ensureImpelClaudeProfile, ensureImpelCodexProfile, tenantCliProfilePaths } from "./cliProfiles.js";
|
|
8
|
+
import { findNativeBinary } from "./nativeProcess.js";
|
|
8
9
|
import { mapWithConcurrency } from "./progress.js";
|
|
9
10
|
import { registerTenantShellEntries } from "./shellEntries.js";
|
|
10
11
|
import { resolveSkillsGateway, syncSkillsSafe } from "./skills.js";
|
|
@@ -16,7 +17,6 @@ import {
|
|
|
16
17
|
} from "./tenants.js";
|
|
17
18
|
import { reconcileTenantApps } from "./commands/apps.js";
|
|
18
19
|
import { RUNTIME_BRAND } from "./runtimeBrand.js";
|
|
19
|
-
import { findReviewedVendorCliBinary } from "./vendorCliBinaries.js";
|
|
20
20
|
|
|
21
21
|
export function selectDefaultTenant(listing, { requested = null, currentTenantId = null } = {}) {
|
|
22
22
|
const normalized = requested ? normalizeTenantId(requested) : null;
|
|
@@ -93,19 +93,13 @@ async function prepareTenantCli(config, tenant, io, binaries) {
|
|
|
93
93
|
crossAppModels: crossAppModelsEnabled(config),
|
|
94
94
|
}),
|
|
95
95
|
root: (profile) => profile.configDir,
|
|
96
|
-
env: (profile) => ({
|
|
97
|
-
CLAUDE_CONFIG_DIR: profile.configDir,
|
|
98
|
-
IMPEL_CLAUDE_BIN: binaries.claude,
|
|
99
|
-
}),
|
|
96
|
+
env: (profile) => ({ CLAUDE_CONFIG_DIR: profile.configDir }),
|
|
100
97
|
},
|
|
101
98
|
codex: {
|
|
102
99
|
supported: providerSupported(config.scopes, PAT_SCOPE_CODEX),
|
|
103
100
|
ensure: () => io.ensureCodex(config.gatewayUrl, tenant.id),
|
|
104
101
|
root: (profile) => profile.codexHome,
|
|
105
|
-
env: (profile) => ({
|
|
106
|
-
CODEX_HOME: profile.codexHome,
|
|
107
|
-
IMPEL_CODEX_BIN: binaries.codex,
|
|
108
|
-
}),
|
|
102
|
+
env: (profile) => ({ CODEX_HOME: profile.codexHome }),
|
|
109
103
|
},
|
|
110
104
|
};
|
|
111
105
|
const clients = {};
|
|
@@ -187,7 +181,7 @@ export async function reconcileAllTenants({
|
|
|
187
181
|
ensureCodex: ensureImpelCodexProfile,
|
|
188
182
|
syncSkills: syncSkillsSafe,
|
|
189
183
|
syncAgents: syncAgentProfilesSafe,
|
|
190
|
-
findBinary:
|
|
184
|
+
findBinary: findNativeBinary,
|
|
191
185
|
reconcileApps: reconcileTenantApps,
|
|
192
186
|
registerShellEntries: registerTenantShellEntries,
|
|
193
187
|
knownTenantIds: locallyKnownTenantIds,
|
package/src/skills.js
CHANGED
|
@@ -25,8 +25,6 @@ import path from "node:path";
|
|
|
25
25
|
|
|
26
26
|
import { resolveDefaultGateway, normalizeGatewayUrl } from "./config.js";
|
|
27
27
|
import { findNativeBinary, nativeCommandInvocation } from "./nativeProcess.js";
|
|
28
|
-
import { findReviewedVendorCliBinary } from "./vendorCliBinaries.js";
|
|
29
|
-
import { PINNED_VENDOR_CLI_VERSIONS } from "./vendorCliVersions.js";
|
|
30
28
|
|
|
31
29
|
/** The bundled plugin the Bifrost registry publishes; contains every served skill. */
|
|
32
30
|
export const SKILL_PLUGIN_NAME = "bifrost-all-skills";
|
|
@@ -501,7 +499,6 @@ export async function syncSkills({
|
|
|
501
499
|
platform = process.platform,
|
|
502
500
|
findGit = findNativeBinary,
|
|
503
501
|
homeDir = null,
|
|
504
|
-
findVendorBinary = findReviewedVendorCliBinary,
|
|
505
502
|
} = {}) {
|
|
506
503
|
const spec = CLIENT_SPECS[client];
|
|
507
504
|
if (!spec) {
|
|
@@ -515,18 +512,6 @@ export async function syncSkills({
|
|
|
515
512
|
return { client, label: displayLabel, skipped: true, reason: "disabled" };
|
|
516
513
|
}
|
|
517
514
|
|
|
518
|
-
let commandBinary = spec.bin;
|
|
519
|
-
if (platform === "darwin" && run === runSkillCommand) {
|
|
520
|
-
commandBinary = findVendorBinary(client, { ...process.env, ...env }, platform);
|
|
521
|
-
if (!commandBinary) {
|
|
522
|
-
logger.warn(
|
|
523
|
-
`impel: skipping skill sync for ${displayLabel} — reviewed ${spec.label} `
|
|
524
|
-
+ `v${PINNED_VENDOR_CLI_VERSIONS[client]} is not installed; run \`impel update\`.`,
|
|
525
|
-
);
|
|
526
|
-
return { client, label: displayLabel, skipped: true, reason: "binary-version-mismatch" };
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
515
|
// Callers that manage real profiles pass their home directory so Windows
|
|
531
516
|
// spawns happen from a git-safe cwd (see skillSyncSpawnDirectory).
|
|
532
517
|
const spawnDirectory = skillSyncSpawnDirectory({ platform, homeDir });
|
|
@@ -536,10 +521,9 @@ export async function syncSkills({
|
|
|
536
521
|
|
|
537
522
|
try {
|
|
538
523
|
// Confirm the binary and its `plugin` subcommand exist before doing anything.
|
|
539
|
-
const help = await runCommand(
|
|
524
|
+
const help = await runCommand(spec.bin, ["plugin", "--help"], env);
|
|
540
525
|
if (help.missing) {
|
|
541
|
-
|
|
542
|
-
logger.warn(`impel: skipping skill sync for ${displayLabel} — \`${commandBinary}\` CLI not found${location}.`);
|
|
526
|
+
logger.warn(`impel: skipping skill sync for ${displayLabel} — \`${spec.bin}\` CLI not found on PATH.`);
|
|
543
527
|
return { client, label: displayLabel, skipped: true, reason: "binary-missing" };
|
|
544
528
|
}
|
|
545
529
|
if (!help.ok) {
|
|
@@ -571,12 +555,12 @@ export async function syncSkills({
|
|
|
571
555
|
})[0];
|
|
572
556
|
const registerResult = await runSkillCommandWithRetry(
|
|
573
557
|
runCommand,
|
|
574
|
-
|
|
558
|
+
spec.bin,
|
|
575
559
|
registerCommand.args,
|
|
576
560
|
env,
|
|
577
561
|
);
|
|
578
562
|
if (registerResult.missing) {
|
|
579
|
-
failures.push({ phase: registerCommand.phase, reason: `\`${
|
|
563
|
+
failures.push({ phase: registerCommand.phase, reason: `\`${spec.bin}\` disappeared mid-sync` });
|
|
580
564
|
} else if (!isBenign(registerResult)) {
|
|
581
565
|
failures.push({
|
|
582
566
|
phase: registerCommand.phase,
|
|
@@ -589,7 +573,7 @@ export async function syncSkills({
|
|
|
589
573
|
// gives us the same dynamic name so refreshes can still use
|
|
590
574
|
// PLUGIN@MARKETPLACE.
|
|
591
575
|
if (!marketplaceName && !registerResult.missing) {
|
|
592
|
-
const listed = await runCommand(
|
|
576
|
+
const listed = await runCommand(spec.bin, ["plugin", "marketplace", "list", "--json"], env);
|
|
593
577
|
if (listed.ok) marketplaceName = resolveConfiguredMarketplaceName(listed.stdout, sourceUrl);
|
|
594
578
|
}
|
|
595
579
|
|
|
@@ -601,12 +585,12 @@ export async function syncSkills({
|
|
|
601
585
|
for (const command of commands) {
|
|
602
586
|
const result = await runSkillCommandWithRetry(
|
|
603
587
|
runCommand,
|
|
604
|
-
|
|
588
|
+
spec.bin,
|
|
605
589
|
command.args,
|
|
606
590
|
env,
|
|
607
591
|
);
|
|
608
592
|
if (result.missing) {
|
|
609
|
-
failures.push({ phase: command.phase, reason: `\`${
|
|
593
|
+
failures.push({ phase: command.phase, reason: `\`${spec.bin}\` disappeared mid-sync` });
|
|
610
594
|
break;
|
|
611
595
|
}
|
|
612
596
|
if (isBenign(result)) continue;
|
|
@@ -619,7 +603,7 @@ export async function syncSkills({
|
|
|
619
603
|
) {
|
|
620
604
|
const repair = await reregisterCodexMarketplace({
|
|
621
605
|
run: runCommand,
|
|
622
|
-
bin:
|
|
606
|
+
bin: spec.bin,
|
|
623
607
|
env,
|
|
624
608
|
marketplaceName,
|
|
625
609
|
sourceUrl,
|
package/src/updates.js
CHANGED
|
@@ -36,19 +36,8 @@ export function updateRegistry() {
|
|
|
36
36
|
).replace(/\/+$/u, "");
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
throw new Error(`unsupported npm update tag "${tag}"`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Keep prerelease installs on npm's prerelease channel. */
|
|
45
|
-
export function updateTagForVersion(version) {
|
|
46
|
-
const parsed = validVersion(version) ? version.match(VERSION_RE) : null;
|
|
47
|
-
return parsed?.[4] ? "next" : "latest";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function updateInstallSpec(tag = "latest") {
|
|
51
|
-
return `${updatePackage()}@${normalizeUpdateTag(tag)}`;
|
|
39
|
+
export function updateInstallSpec() {
|
|
40
|
+
return `${updatePackage()}@latest`;
|
|
52
41
|
}
|
|
53
42
|
|
|
54
43
|
export function installedVersion() {
|
|
@@ -99,17 +88,15 @@ export function isNewerVersion(candidate, current) {
|
|
|
99
88
|
function fetchRemoteVersionWithNpm({
|
|
100
89
|
packageName,
|
|
101
90
|
registry,
|
|
102
|
-
tag,
|
|
103
91
|
timeoutMs,
|
|
104
92
|
spawnSyncImpl = spawnSync,
|
|
105
93
|
environment = process.env,
|
|
106
94
|
platform = process.platform,
|
|
107
95
|
}) {
|
|
108
96
|
try {
|
|
109
|
-
const target = tag === "latest" ? packageName : `${packageName}@${tag}`;
|
|
110
97
|
const invocation = nativeCommandInvocation(
|
|
111
98
|
"npm",
|
|
112
|
-
["view",
|
|
99
|
+
["view", packageName, "version", "--json", "--registry", registry],
|
|
113
100
|
environment,
|
|
114
101
|
platform,
|
|
115
102
|
);
|
|
@@ -135,71 +122,39 @@ function fetchRemoteVersionWithNpm({
|
|
|
135
122
|
export async function fetchRemoteVersion({
|
|
136
123
|
packageName = updatePackage(),
|
|
137
124
|
registry = updateRegistry(),
|
|
138
|
-
tag = "latest",
|
|
139
125
|
timeoutMs = 10_000,
|
|
140
126
|
fetchImpl = null,
|
|
141
|
-
fallbackToNpm = fetchImpl === null,
|
|
142
127
|
spawnSyncImpl = spawnSync,
|
|
143
128
|
environment = process.env,
|
|
144
129
|
platform = process.platform,
|
|
145
130
|
} = {}) {
|
|
146
|
-
const normalizedTag = normalizeUpdateTag(tag);
|
|
147
131
|
const normalizedRegistry = String(registry).replace(/\/+$/u, "");
|
|
148
132
|
if (!fetchImpl && normalizedRegistry !== DEFAULT_UPDATE_REGISTRY) {
|
|
149
133
|
return fetchRemoteVersionWithNpm({
|
|
150
134
|
packageName,
|
|
151
135
|
registry: normalizedRegistry,
|
|
152
|
-
tag: normalizedTag,
|
|
153
|
-
timeoutMs,
|
|
154
|
-
spawnSyncImpl,
|
|
155
|
-
environment,
|
|
156
|
-
platform,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
// Windows npm commonly has proxy, certificate, or registry settings that
|
|
160
|
-
// Node's bare fetch does not inherit. Prefer the already-working npm client
|
|
161
|
-
// there, then retain the direct registry request as a fallback.
|
|
162
|
-
if (!fetchImpl && platform === "win32") {
|
|
163
|
-
const npmVersion = fetchRemoteVersionWithNpm({
|
|
164
|
-
packageName,
|
|
165
|
-
registry: normalizedRegistry,
|
|
166
|
-
tag: normalizedTag,
|
|
167
136
|
timeoutMs,
|
|
168
137
|
spawnSyncImpl,
|
|
169
138
|
environment,
|
|
170
139
|
platform,
|
|
171
140
|
});
|
|
172
|
-
if (npmVersion) return npmVersion;
|
|
173
141
|
}
|
|
174
142
|
const controller = new AbortController();
|
|
175
143
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
176
|
-
let remoteVersion = null;
|
|
177
144
|
try {
|
|
178
145
|
const encodedName = encodeURIComponent(packageName);
|
|
179
|
-
const response = await (fetchImpl || globalThis.fetch)(`${normalizedRegistry}/${encodedName}
|
|
146
|
+
const response = await (fetchImpl || globalThis.fetch)(`${normalizedRegistry}/${encodedName}/latest`, {
|
|
180
147
|
headers: { accept: "application/json" },
|
|
181
148
|
signal: controller.signal,
|
|
182
149
|
});
|
|
183
|
-
if (response.ok)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
150
|
+
if (!response.ok) return null;
|
|
151
|
+
const metadata = await response.json();
|
|
152
|
+
return validVersion(metadata?.version) ? metadata.version : null;
|
|
187
153
|
} catch {
|
|
188
|
-
|
|
189
|
-
// registry configuration, which is especially important on Windows.
|
|
154
|
+
return null;
|
|
190
155
|
} finally {
|
|
191
156
|
clearTimeout(timeout);
|
|
192
157
|
}
|
|
193
|
-
if (remoteVersion || !fallbackToNpm) return remoteVersion;
|
|
194
|
-
return fetchRemoteVersionWithNpm({
|
|
195
|
-
packageName,
|
|
196
|
-
registry: normalizedRegistry,
|
|
197
|
-
tag: normalizedTag,
|
|
198
|
-
timeoutMs,
|
|
199
|
-
spawnSyncImpl,
|
|
200
|
-
environment,
|
|
201
|
-
platform,
|
|
202
|
-
});
|
|
203
158
|
}
|
|
204
159
|
|
|
205
160
|
export function readUpdateCache() {
|
|
@@ -243,9 +198,7 @@ export function cacheIsFresh(cache, now = Date.now()) {
|
|
|
243
198
|
export async function refreshUpdateCache(dependencies = {}) {
|
|
244
199
|
const fetchLatest = dependencies.fetchRemoteVersion || fetchRemoteVersion;
|
|
245
200
|
const writeCache = dependencies.writeCache || writeUpdateCache;
|
|
246
|
-
const
|
|
247
|
-
const tag = updateTagForVersion(currentVersion);
|
|
248
|
-
const remoteVersion = await fetchLatest({ tag });
|
|
201
|
+
const remoteVersion = await fetchLatest();
|
|
249
202
|
if (!remoteVersion) {
|
|
250
203
|
try {
|
|
251
204
|
writeCache({ lastFailedAt: Date.now() });
|
|
@@ -254,7 +207,7 @@ export async function refreshUpdateCache(dependencies = {}) {
|
|
|
254
207
|
}
|
|
255
208
|
return null;
|
|
256
209
|
}
|
|
257
|
-
return writeCache({ remoteVersion,
|
|
210
|
+
return writeCache({ remoteVersion, checkedAt: Date.now() });
|
|
258
211
|
}
|
|
259
212
|
|
|
260
213
|
/** One-line human notice when npm has a newer stable package. */
|