impel-cli 0.18.15-beta.0 → 0.18.15-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/package.json +1 -1
- package/src/commands/launch.js +11 -2
- package/src/commands/status.js +3 -2
- package/src/macSetup.js +21 -39
- package/src/provisioning.js +10 -4
- package/src/skills.js +24 -8
- package/src/vendorCliBinaries.js +121 -0
- package/src/vendorCliVersions.js +7 -0
- package/src/windowsSetup.js +7 -4
package/package.json
CHANGED
package/src/commands/launch.js
CHANGED
|
@@ -20,8 +20,9 @@ import { assertProviderScopes, ensureTenantSelection, tenantCredential } from ".
|
|
|
20
20
|
import { maybePrintUpdateNotice } from "../updates.js";
|
|
21
21
|
import {
|
|
22
22
|
nativeSpawnInvocation,
|
|
23
|
-
resolveNativeBinary,
|
|
24
23
|
} from "../nativeProcess.js";
|
|
24
|
+
import { resolveReviewedVendorCliBinary } from "../vendorCliBinaries.js";
|
|
25
|
+
import { PINNED_VENDOR_CLI_VERSIONS } from "../vendorCliVersions.js";
|
|
25
26
|
import { RUNTIME_BRAND } from "../runtimeBrand.js";
|
|
26
27
|
import {
|
|
27
28
|
cacheProviderReadiness,
|
|
@@ -115,7 +116,15 @@ async function assertLiveProviderReadiness({ config, gatewayUrl, credential, ten
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
function runNativeCli(tool, argv, environment) {
|
|
118
|
-
const binary =
|
|
119
|
+
const binary = resolveReviewedVendorCliBinary(tool, environment);
|
|
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
|
+
}
|
|
119
128
|
let invocation;
|
|
120
129
|
try {
|
|
121
130
|
invocation = nativeSpawnInvocation(binary, argv, environment);
|
package/src/commands/status.js
CHANGED
|
@@ -4,8 +4,9 @@ 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
|
|
7
|
+
import { environmentValue } from "../nativeProcess.js";
|
|
8
8
|
import { tenantCliClientReadiness } from "../provisioning.js";
|
|
9
|
+
import { findReviewedVendorCliBinary } from "../vendorCliBinaries.js";
|
|
9
10
|
import { windowsTenantShortcutName } from "../shellEntries.js";
|
|
10
11
|
import {
|
|
11
12
|
ensureTenantSelection,
|
|
@@ -86,7 +87,7 @@ export async function cmdStatus(overrides = {}) {
|
|
|
86
87
|
cliReadiness: tenantCliClientReadiness,
|
|
87
88
|
desktopReadiness,
|
|
88
89
|
installedVersion,
|
|
89
|
-
findNativeBinary,
|
|
90
|
+
findNativeBinary: findReviewedVendorCliBinary,
|
|
90
91
|
maybePrintUpdateNotice,
|
|
91
92
|
platform: process.platform,
|
|
92
93
|
environment: process.env,
|
package/src/macSetup.js
CHANGED
|
@@ -7,11 +7,12 @@ 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";
|
|
10
12
|
|
|
11
13
|
const MAX_INSTALLER_BYTES = 512 * 1024;
|
|
12
14
|
const INSTALLER_DOWNLOAD_TIMEOUT_MS = 30_000;
|
|
13
15
|
const INSTALLER_RUN_TIMEOUT_MS = 10 * 60 * 1_000;
|
|
14
|
-
const MAC_SIGNING_TEAMS = Object.freeze({ claude: "Q6L2SF6YDW", codex: "2DC432GLL2" });
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* The script bytes are pinned per impel-cli release. Both vendor scripts then
|
|
@@ -19,59 +20,34 @@ const MAC_SIGNING_TEAMS = Object.freeze({ claude: "Q6L2SF6YDW", codex: "2DC432GL
|
|
|
19
20
|
*/
|
|
20
21
|
export const MAC_CLI_INSTALLERS = Object.freeze({
|
|
21
22
|
claude: Object.freeze({
|
|
23
|
+
version: PINNED_VENDOR_CLI_VERSIONS.claude,
|
|
22
24
|
url: "https://claude.ai/install.sh",
|
|
23
25
|
trustedOrigins: Object.freeze([
|
|
24
26
|
"https://claude.ai",
|
|
25
27
|
"https://downloads.claude.ai",
|
|
26
28
|
]),
|
|
27
29
|
sha256: "cde4f1702d3b1695f92b73d26888364e17bca476e17f0fd676484c951d36c125",
|
|
28
|
-
command:
|
|
29
|
-
args: Object.freeze([
|
|
30
|
+
command: `curl -fsSL https://claude.ai/install.sh | bash -s ${PINNED_VENDOR_CLI_VERSIONS.claude}`,
|
|
31
|
+
args: Object.freeze([PINNED_VENDOR_CLI_VERSIONS.claude]),
|
|
30
32
|
environment: Object.freeze({}),
|
|
31
33
|
}),
|
|
32
34
|
codex: Object.freeze({
|
|
35
|
+
version: PINNED_VENDOR_CLI_VERSIONS.codex,
|
|
33
36
|
url: "https://chatgpt.com/codex/install.sh",
|
|
34
37
|
trustedOrigins: Object.freeze([
|
|
35
38
|
"https://chatgpt.com",
|
|
36
39
|
"https://releases.openai.com",
|
|
37
40
|
]),
|
|
38
|
-
sha256: "
|
|
39
|
-
command:
|
|
41
|
+
sha256: "ba92dd27e5c06f0d3bbc58bfa4b9cfb6599cd2742fbb1f92a2765e6c07dedb5a",
|
|
42
|
+
command: `curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 CODEX_RELEASE=${PINNED_VENDOR_CLI_VERSIONS.codex} sh`,
|
|
40
43
|
args: Object.freeze([]),
|
|
41
|
-
environment: Object.freeze({
|
|
44
|
+
environment: Object.freeze({
|
|
45
|
+
CODEX_NON_INTERACTIVE: "1",
|
|
46
|
+
CODEX_RELEASE: PINNED_VENDOR_CLI_VERSIONS.codex,
|
|
47
|
+
}),
|
|
42
48
|
}),
|
|
43
49
|
});
|
|
44
50
|
|
|
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
|
-
|
|
75
51
|
function detectMacClis(find, environment, verify) {
|
|
76
52
|
const detect = (tool) => find(
|
|
77
53
|
tool,
|
|
@@ -200,7 +176,7 @@ export async function prepareMacClis({
|
|
|
200
176
|
const io = {
|
|
201
177
|
environment: process.env,
|
|
202
178
|
find: findNativeBinary,
|
|
203
|
-
verify:
|
|
179
|
+
verify: verifyReviewedMacVendorCli,
|
|
204
180
|
run: spawnSync,
|
|
205
181
|
fetchImpl: fetch,
|
|
206
182
|
fsImpl: fs,
|
|
@@ -252,7 +228,10 @@ export async function prepareMacClis({
|
|
|
252
228
|
await io.syncSkills({
|
|
253
229
|
client: "claude",
|
|
254
230
|
gatewayUrl,
|
|
255
|
-
env: {
|
|
231
|
+
env: {
|
|
232
|
+
CLAUDE_CONFIG_DIR: claudeProfile.configDir,
|
|
233
|
+
IMPEL_CLAUDE_BIN: binaries.claude,
|
|
234
|
+
},
|
|
256
235
|
label: "Impel isolated Claude (impel claude)",
|
|
257
236
|
// Lets Windows spawns use a git-safe cwd (see skillSyncSpawnDirectory).
|
|
258
237
|
homeDir: os.homedir(),
|
|
@@ -262,7 +241,10 @@ export async function prepareMacClis({
|
|
|
262
241
|
await io.syncSkills({
|
|
263
242
|
client: "codex",
|
|
264
243
|
gatewayUrl,
|
|
265
|
-
env: {
|
|
244
|
+
env: {
|
|
245
|
+
CODEX_HOME: codexProfile.codexHome,
|
|
246
|
+
IMPEL_CODEX_BIN: binaries.codex,
|
|
247
|
+
},
|
|
266
248
|
label: "Impel isolated Codex (impel codex)",
|
|
267
249
|
homeDir: os.homedir(),
|
|
268
250
|
});
|
package/src/provisioning.js
CHANGED
|
@@ -5,7 +5,6 @@ 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";
|
|
9
8
|
import { mapWithConcurrency } from "./progress.js";
|
|
10
9
|
import { registerTenantShellEntries } from "./shellEntries.js";
|
|
11
10
|
import { resolveSkillsGateway, syncSkillsSafe } from "./skills.js";
|
|
@@ -17,6 +16,7 @@ import {
|
|
|
17
16
|
} from "./tenants.js";
|
|
18
17
|
import { reconcileTenantApps } from "./commands/apps.js";
|
|
19
18
|
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,13 +93,19 @@ async function prepareTenantCli(config, tenant, io, binaries) {
|
|
|
93
93
|
crossAppModels: crossAppModelsEnabled(config),
|
|
94
94
|
}),
|
|
95
95
|
root: (profile) => profile.configDir,
|
|
96
|
-
env: (profile) => ({
|
|
96
|
+
env: (profile) => ({
|
|
97
|
+
CLAUDE_CONFIG_DIR: profile.configDir,
|
|
98
|
+
IMPEL_CLAUDE_BIN: binaries.claude,
|
|
99
|
+
}),
|
|
97
100
|
},
|
|
98
101
|
codex: {
|
|
99
102
|
supported: providerSupported(config.scopes, PAT_SCOPE_CODEX),
|
|
100
103
|
ensure: () => io.ensureCodex(config.gatewayUrl, tenant.id),
|
|
101
104
|
root: (profile) => profile.codexHome,
|
|
102
|
-
env: (profile) => ({
|
|
105
|
+
env: (profile) => ({
|
|
106
|
+
CODEX_HOME: profile.codexHome,
|
|
107
|
+
IMPEL_CODEX_BIN: binaries.codex,
|
|
108
|
+
}),
|
|
103
109
|
},
|
|
104
110
|
};
|
|
105
111
|
const clients = {};
|
|
@@ -181,7 +187,7 @@ export async function reconcileAllTenants({
|
|
|
181
187
|
ensureCodex: ensureImpelCodexProfile,
|
|
182
188
|
syncSkills: syncSkillsSafe,
|
|
183
189
|
syncAgents: syncAgentProfilesSafe,
|
|
184
|
-
findBinary:
|
|
190
|
+
findBinary: findReviewedVendorCliBinary,
|
|
185
191
|
reconcileApps: reconcileTenantApps,
|
|
186
192
|
registerShellEntries: registerTenantShellEntries,
|
|
187
193
|
knownTenantIds: locallyKnownTenantIds,
|
package/src/skills.js
CHANGED
|
@@ -25,6 +25,8 @@ 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";
|
|
28
30
|
|
|
29
31
|
/** The bundled plugin the Bifrost registry publishes; contains every served skill. */
|
|
30
32
|
export const SKILL_PLUGIN_NAME = "bifrost-all-skills";
|
|
@@ -499,6 +501,7 @@ export async function syncSkills({
|
|
|
499
501
|
platform = process.platform,
|
|
500
502
|
findGit = findNativeBinary,
|
|
501
503
|
homeDir = null,
|
|
504
|
+
findVendorBinary = findReviewedVendorCliBinary,
|
|
502
505
|
} = {}) {
|
|
503
506
|
const spec = CLIENT_SPECS[client];
|
|
504
507
|
if (!spec) {
|
|
@@ -512,6 +515,18 @@ export async function syncSkills({
|
|
|
512
515
|
return { client, label: displayLabel, skipped: true, reason: "disabled" };
|
|
513
516
|
}
|
|
514
517
|
|
|
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
|
+
|
|
515
530
|
// Callers that manage real profiles pass their home directory so Windows
|
|
516
531
|
// spawns happen from a git-safe cwd (see skillSyncSpawnDirectory).
|
|
517
532
|
const spawnDirectory = skillSyncSpawnDirectory({ platform, homeDir });
|
|
@@ -521,9 +536,10 @@ export async function syncSkills({
|
|
|
521
536
|
|
|
522
537
|
try {
|
|
523
538
|
// Confirm the binary and its `plugin` subcommand exist before doing anything.
|
|
524
|
-
const help = await runCommand(
|
|
539
|
+
const help = await runCommand(commandBinary, ["plugin", "--help"], env);
|
|
525
540
|
if (help.missing) {
|
|
526
|
-
|
|
541
|
+
const location = commandBinary === spec.bin ? " on PATH" : "";
|
|
542
|
+
logger.warn(`impel: skipping skill sync for ${displayLabel} — \`${commandBinary}\` CLI not found${location}.`);
|
|
527
543
|
return { client, label: displayLabel, skipped: true, reason: "binary-missing" };
|
|
528
544
|
}
|
|
529
545
|
if (!help.ok) {
|
|
@@ -555,12 +571,12 @@ export async function syncSkills({
|
|
|
555
571
|
})[0];
|
|
556
572
|
const registerResult = await runSkillCommandWithRetry(
|
|
557
573
|
runCommand,
|
|
558
|
-
|
|
574
|
+
commandBinary,
|
|
559
575
|
registerCommand.args,
|
|
560
576
|
env,
|
|
561
577
|
);
|
|
562
578
|
if (registerResult.missing) {
|
|
563
|
-
failures.push({ phase: registerCommand.phase, reason: `\`${
|
|
579
|
+
failures.push({ phase: registerCommand.phase, reason: `\`${commandBinary}\` disappeared mid-sync` });
|
|
564
580
|
} else if (!isBenign(registerResult)) {
|
|
565
581
|
failures.push({
|
|
566
582
|
phase: registerCommand.phase,
|
|
@@ -573,7 +589,7 @@ export async function syncSkills({
|
|
|
573
589
|
// gives us the same dynamic name so refreshes can still use
|
|
574
590
|
// PLUGIN@MARKETPLACE.
|
|
575
591
|
if (!marketplaceName && !registerResult.missing) {
|
|
576
|
-
const listed = await runCommand(
|
|
592
|
+
const listed = await runCommand(commandBinary, ["plugin", "marketplace", "list", "--json"], env);
|
|
577
593
|
if (listed.ok) marketplaceName = resolveConfiguredMarketplaceName(listed.stdout, sourceUrl);
|
|
578
594
|
}
|
|
579
595
|
|
|
@@ -585,12 +601,12 @@ export async function syncSkills({
|
|
|
585
601
|
for (const command of commands) {
|
|
586
602
|
const result = await runSkillCommandWithRetry(
|
|
587
603
|
runCommand,
|
|
588
|
-
|
|
604
|
+
commandBinary,
|
|
589
605
|
command.args,
|
|
590
606
|
env,
|
|
591
607
|
);
|
|
592
608
|
if (result.missing) {
|
|
593
|
-
failures.push({ phase: command.phase, reason: `\`${
|
|
609
|
+
failures.push({ phase: command.phase, reason: `\`${commandBinary}\` disappeared mid-sync` });
|
|
594
610
|
break;
|
|
595
611
|
}
|
|
596
612
|
if (isBenign(result)) continue;
|
|
@@ -603,7 +619,7 @@ export async function syncSkills({
|
|
|
603
619
|
) {
|
|
604
620
|
const repair = await reregisterCodexMarketplace({
|
|
605
621
|
run: runCommand,
|
|
606
|
-
bin:
|
|
622
|
+
bin: commandBinary,
|
|
607
623
|
env,
|
|
608
624
|
marketplaceName,
|
|
609
625
|
sourceUrl,
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
environmentValue,
|
|
7
|
+
findNativeBinary,
|
|
8
|
+
resolveNativeBinary,
|
|
9
|
+
} from "./nativeProcess.js";
|
|
10
|
+
import { PINNED_VENDOR_CLI_VERSIONS } from "./vendorCliVersions.js";
|
|
11
|
+
|
|
12
|
+
export const MAC_VENDOR_SIGNING_TEAMS = Object.freeze({
|
|
13
|
+
claude: "Q6L2SF6YDW",
|
|
14
|
+
codex: "2DC432GLL2",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const VERSION_RE = /\b\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\b/u;
|
|
18
|
+
|
|
19
|
+
function vendorOverrideName(tool, environmentPrefix = "IMPEL") {
|
|
20
|
+
if (tool === "claude") return `${environmentPrefix}_CLAUDE_BIN`;
|
|
21
|
+
if (tool === "codex") return `${environmentPrefix}_CODEX_BIN`;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Extract the vendor release from `claude --version` or `codex --version`. */
|
|
26
|
+
export function parseVendorCliVersion(output) {
|
|
27
|
+
return String(output || "").match(VERSION_RE)?.[0] || null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Accept only the exact reviewed macOS release and its vendor signature.
|
|
32
|
+
* Explicit binary overrides remain authoritative for development and recovery.
|
|
33
|
+
*/
|
|
34
|
+
export function verifyReviewedMacVendorCli(
|
|
35
|
+
tool,
|
|
36
|
+
binary,
|
|
37
|
+
environment = process.env,
|
|
38
|
+
{
|
|
39
|
+
run = spawnSync,
|
|
40
|
+
realpath = fs.realpathSync,
|
|
41
|
+
versions = PINNED_VENDOR_CLI_VERSIONS,
|
|
42
|
+
signingTeams = MAC_VENDOR_SIGNING_TEAMS,
|
|
43
|
+
environmentPrefix = "IMPEL",
|
|
44
|
+
} = {},
|
|
45
|
+
) {
|
|
46
|
+
if (!versions[tool] || !signingTeams[tool]) return false;
|
|
47
|
+
try {
|
|
48
|
+
const override = vendorOverrideName(tool, environmentPrefix);
|
|
49
|
+
const overriddenBinary = override ? environmentValue(environment, override) : null;
|
|
50
|
+
if (overriddenBinary && path.resolve(overriddenBinary) === path.resolve(binary)) return true;
|
|
51
|
+
|
|
52
|
+
const result = run(binary, ["--version"], {
|
|
53
|
+
encoding: "utf8",
|
|
54
|
+
env: environment,
|
|
55
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
56
|
+
timeout: 15_000,
|
|
57
|
+
});
|
|
58
|
+
if (result?.status !== 0 || result?.error) return false;
|
|
59
|
+
|
|
60
|
+
const version = parseVendorCliVersion(`${result.stdout || ""}\n${result.stderr || ""}`);
|
|
61
|
+
if (version !== versions[tool]) return false;
|
|
62
|
+
|
|
63
|
+
const resolved = realpath(binary);
|
|
64
|
+
const verified = run("/usr/bin/codesign", ["--verify", "--strict", resolved], {
|
|
65
|
+
encoding: "utf8",
|
|
66
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
67
|
+
timeout: 15_000,
|
|
68
|
+
});
|
|
69
|
+
if (verified?.status !== 0 || verified?.error) return false;
|
|
70
|
+
const details = run("/usr/bin/codesign", ["-dv", "--verbose=4", resolved], {
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
73
|
+
timeout: 15_000,
|
|
74
|
+
});
|
|
75
|
+
return details?.status === 0
|
|
76
|
+
&& String(details.stderr || details.stdout || "").includes(`TeamIdentifier=${signingTeams[tool]}`);
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Locate the reviewed macOS vendor CLI. On other platforms this intentionally
|
|
84
|
+
* preserves the existing native resolution behavior.
|
|
85
|
+
*/
|
|
86
|
+
export function findReviewedVendorCliBinary(
|
|
87
|
+
tool,
|
|
88
|
+
environment = process.env,
|
|
89
|
+
platform = process.platform,
|
|
90
|
+
environmentPrefix = "IMPEL",
|
|
91
|
+
{
|
|
92
|
+
find = findNativeBinary,
|
|
93
|
+
verify = verifyReviewedMacVendorCli,
|
|
94
|
+
} = {},
|
|
95
|
+
) {
|
|
96
|
+
if (platform !== "darwin") return find(tool, environment, platform, environmentPrefix);
|
|
97
|
+
return find(
|
|
98
|
+
tool,
|
|
99
|
+
environment,
|
|
100
|
+
platform,
|
|
101
|
+
environmentPrefix,
|
|
102
|
+
(binary) => verify(tool, binary, environment, { environmentPrefix }),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Resolve a launch target. macOS deliberately returns null when only an
|
|
108
|
+
* unreviewed or version-drifted binary exists so callers can request repair
|
|
109
|
+
* instead of silently launching it.
|
|
110
|
+
*/
|
|
111
|
+
export function resolveReviewedVendorCliBinary(
|
|
112
|
+
tool,
|
|
113
|
+
environment = process.env,
|
|
114
|
+
platform = process.platform,
|
|
115
|
+
environmentPrefix = "IMPEL",
|
|
116
|
+
) {
|
|
117
|
+
if (platform !== "darwin") {
|
|
118
|
+
return resolveNativeBinary(tool, environment, platform, environmentPrefix);
|
|
119
|
+
}
|
|
120
|
+
return findReviewedVendorCliBinary(tool, environment, platform, environmentPrefix);
|
|
121
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Exact vendor CLI releases reviewed for this impel-cli build. Keep macOS and
|
|
2
|
+
// Windows installers on the same versions; do not replace these with moving
|
|
3
|
+
// channels such as "stable" or "latest".
|
|
4
|
+
export const PINNED_VENDOR_CLI_VERSIONS = Object.freeze({
|
|
5
|
+
claude: "2.1.212",
|
|
6
|
+
codex: "0.146.0",
|
|
7
|
+
});
|
package/src/windowsSetup.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
nativeSpawnInvocation,
|
|
9
9
|
} from "./nativeProcess.js";
|
|
10
10
|
import { syncSkillsSafe } from "./skills.js";
|
|
11
|
+
import { PINNED_VENDOR_CLI_VERSIONS } from "./vendorCliVersions.js";
|
|
11
12
|
import { provisionWindowsGit } from "./windowsGit.js";
|
|
12
13
|
|
|
13
14
|
const POWERSHELL_ARGS = [
|
|
@@ -27,12 +28,14 @@ const POWERSHELL_ARGS = [
|
|
|
27
28
|
*/
|
|
28
29
|
export const WINDOWS_CLI_INSTALLERS = Object.freeze({
|
|
29
30
|
claude: Object.freeze({
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
version: PINNED_VENDOR_CLI_VERSIONS.claude,
|
|
32
|
+
command: `powershell -ExecutionPolicy Bypass -NoProfile -Command "& ([scriptblock]::Create((irm https://claude.ai/install.ps1))) '${PINNED_VENDOR_CLI_VERSIONS.claude}'"`,
|
|
33
|
+
script: `& ([scriptblock]::Create((Invoke-RestMethod -Uri 'https://claude.ai/install.ps1'))) '${PINNED_VENDOR_CLI_VERSIONS.claude}'`,
|
|
32
34
|
}),
|
|
33
35
|
codex: Object.freeze({
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
version: PINNED_VENDOR_CLI_VERSIONS.codex,
|
|
37
|
+
command: `powershell -ExecutionPolicy Bypass -NoProfile -Command "[Environment]::SetEnvironmentVariable('CODEX_NON_INTERACTIVE','1','Process'); & ([scriptblock]::Create((irm https://chatgpt.com/codex/install.ps1))) -Release '${PINNED_VENDOR_CLI_VERSIONS.codex}'"`,
|
|
38
|
+
script: `[Environment]::SetEnvironmentVariable('CODEX_NON_INTERACTIVE', '1', 'Process'); & ([scriptblock]::Create((Invoke-RestMethod -Uri 'https://chatgpt.com/codex/install.ps1'))) -Release '${PINNED_VENDOR_CLI_VERSIONS.codex}'`,
|
|
36
39
|
}),
|
|
37
40
|
});
|
|
38
41
|
|