impel-cli 0.18.1 → 0.18.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 +24 -15
- package/package.json +1 -1
- package/src/apps.js +13 -4
- package/src/cli.js +2 -0
- package/src/macDeveloperTools.js +65 -0
package/README.md
CHANGED
|
@@ -22,6 +22,11 @@ npm install --global impel-cli
|
|
|
22
22
|
|
|
23
23
|
Node.js 18 or newer is required.
|
|
24
24
|
|
|
25
|
+
On macOS, setup and update verify Apple's Xcode Command Line Tools before any
|
|
26
|
+
profile, app, or skills work begins. If they are missing, Impel opens the
|
|
27
|
+
system installer with `xcode-select --install` and stops safely; finish that
|
|
28
|
+
installation, then rerun the same Impel command.
|
|
29
|
+
|
|
25
30
|
Then run setup and paste an Impel Personal Access Token when prompted:
|
|
26
31
|
|
|
27
32
|
```sh
|
|
@@ -35,17 +40,19 @@ surfaces for each tenant. You do not need to repeat setup tenant by tenant.
|
|
|
35
40
|
|
|
36
41
|
`impel setup`:
|
|
37
42
|
|
|
38
|
-
1.
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
1. On macOS, verifies the Xcode Command Line Tools and opens Apple's installer
|
|
44
|
+
before doing any other setup work when they are missing.
|
|
45
|
+
2. Stores the PAT in the private Impel config file.
|
|
46
|
+
3. Fetches the complete live tenant list.
|
|
47
|
+
4. Preserves the current CLI tenant when it is still accessible, or selects the
|
|
41
48
|
control-plane default.
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
5. Creates isolated Claude and Codex CLI profiles for every supported tenant.
|
|
50
|
+
6. On Windows machines without git, installs a pinned, checksum-verified
|
|
44
51
|
MinGit into `~/.config/impel/tools/git` (no admin rights, no system PATH
|
|
45
52
|
change) so Claude and Codex can install the Impel skills marketplace.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
7. Installs or repairs supported tenant desktop apps on macOS and Windows.
|
|
54
|
+
8. Registers tenant apps with Finder/Spotlight or Windows Start/Search.
|
|
55
|
+
9. Verifies every supported tenant surface and prints a tenant-sorted summary.
|
|
49
56
|
|
|
50
57
|
Use `--tenant` only to choose the default for CLI launches. It does not limit
|
|
51
58
|
which tenants setup prepares:
|
|
@@ -117,13 +124,15 @@ impel update
|
|
|
117
124
|
|
|
118
125
|
Update:
|
|
119
126
|
|
|
120
|
-
1.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
1. On macOS, verifies the Xcode Command Line Tools and opens Apple's installer
|
|
128
|
+
before doing any other update work when they are missing.
|
|
129
|
+
2. Checks and installs the latest `impel-cli` package from npm.
|
|
130
|
+
3. Continues under the freshly installed build.
|
|
131
|
+
4. Fetches the current live tenant list.
|
|
132
|
+
5. Fully prepares tenants added since the previous run.
|
|
133
|
+
6. Repairs and upgrades existing tenants in place.
|
|
134
|
+
7. Re-registers operating-system launch entries.
|
|
135
|
+
8. Reports local tenants that are no longer accessible without deleting them.
|
|
127
136
|
|
|
128
137
|
An update preserves histories, browser state, preferences, permissions, stable
|
|
129
138
|
app identity, and user-authored settings outside Impel-managed blocks. Desktop
|
package/package.json
CHANGED
package/src/apps.js
CHANGED
|
@@ -726,7 +726,12 @@ export function installManagedAppFiles({
|
|
|
726
726
|
if (resolvedVendorPaths[target] && isVendableVendorApp(target, resolvedVendorPaths[target])) {
|
|
727
727
|
assertPinnedVendorAppVersion(target, resolvedVendorPaths[target]);
|
|
728
728
|
}
|
|
729
|
-
if (target === "chatgpt") writeVendoredChatGPTBundle(
|
|
729
|
+
if (target === "chatgpt") writeVendoredChatGPTBundle(
|
|
730
|
+
paths,
|
|
731
|
+
resolvedVendorPaths.chatgpt,
|
|
732
|
+
config.gatewayUrl,
|
|
733
|
+
homeDir,
|
|
734
|
+
);
|
|
730
735
|
else writeVendoredClaudeBundle(
|
|
731
736
|
paths,
|
|
732
737
|
resolvedVendorPaths.claude,
|
|
@@ -1561,7 +1566,7 @@ function claudeUsageCompatibilityEnabled(gatewayUrl) {
|
|
|
1561
1566
|
}
|
|
1562
1567
|
}
|
|
1563
1568
|
|
|
1564
|
-
function writeVendoredChatGPTBundle(paths, vendorPath, gatewayUrl) {
|
|
1569
|
+
function writeVendoredChatGPTBundle(paths, vendorPath, gatewayUrl, homeDir) {
|
|
1565
1570
|
if (!vendorPath) throw new Error("ChatGPT/Codex vendor app is unavailable");
|
|
1566
1571
|
const definition = APP_DEFINITIONS.chatgpt;
|
|
1567
1572
|
const executableName = definition.executableNames.find((name) => (
|
|
@@ -1600,7 +1605,7 @@ function writeVendoredChatGPTBundle(paths, vendorPath, gatewayUrl) {
|
|
|
1600
1605
|
const asarHash = asarHeaderHash(runtimeAsarPath);
|
|
1601
1606
|
const vendorExecutable = path.join(vendorBundle, "Contents", "MacOS", executableName);
|
|
1602
1607
|
const signingIdentity = resolveVendoredAppSigningIdentity(vendorExecutable);
|
|
1603
|
-
const launchHost = ensureChatGPTLaunchHost();
|
|
1608
|
+
const launchHost = ensureChatGPTLaunchHost({ cacheDir: chatGPTLaunchHostCacheDir(homeDir) });
|
|
1604
1609
|
writeAtomic(
|
|
1605
1610
|
path.join(staging, "Contents", "Info.plist"),
|
|
1606
1611
|
vendoredChatGPTWrapperPlist(vendorPath, asarHash, paths.chatgpt, launchHost ? "launch-host" : "launch"),
|
|
@@ -2083,7 +2088,7 @@ export function ensureChatGPTLaunchHost({
|
|
|
2083
2088
|
platform = process.platform,
|
|
2084
2089
|
osRelease = os.release(),
|
|
2085
2090
|
compiler = "/usr/bin/cc",
|
|
2086
|
-
cacheDir =
|
|
2091
|
+
cacheDir = chatGPTLaunchHostCacheDir(),
|
|
2087
2092
|
warn = console.warn,
|
|
2088
2093
|
} = {}) {
|
|
2089
2094
|
if (platform !== "darwin") return null;
|
|
@@ -2127,6 +2132,10 @@ export function ensureChatGPTLaunchHost({
|
|
|
2127
2132
|
}
|
|
2128
2133
|
}
|
|
2129
2134
|
|
|
2135
|
+
export function chatGPTLaunchHostCacheDir(homeDir = os.homedir()) {
|
|
2136
|
+
return path.join(homeDir, ".config", "impel", "cache", "chatgpt-launch-host");
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2130
2139
|
function vendoredChatGPTWrapperPlist(vendorPath, asarHash, identity, executableName = "launch") {
|
|
2131
2140
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2132
2141
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
package/src/cli.js
CHANGED
|
@@ -20,6 +20,7 @@ import { cmdUpdate } from "./commands/update.js";
|
|
|
20
20
|
import { cmdNuke } from "./commands/nuke.js";
|
|
21
21
|
import { cmdExperimental } from "./commands/experimental.js";
|
|
22
22
|
import { cmdConverge } from "./commands/converge.js";
|
|
23
|
+
import { ensureMacDeveloperTools } from "./macDeveloperTools.js";
|
|
23
24
|
import { refuseElevatedMacExecution } from "./privileges.js";
|
|
24
25
|
|
|
25
26
|
const HELP = `impel — isolated Impel workspaces for every tenant
|
|
@@ -82,6 +83,7 @@ function normalizeTarget(token) {
|
|
|
82
83
|
|
|
83
84
|
export async function main(argv) {
|
|
84
85
|
if (refuseElevatedMacExecution(argv)) return;
|
|
86
|
+
if (!ensureMacDeveloperTools(argv)) return;
|
|
85
87
|
const [cmd, ...rest] = argv;
|
|
86
88
|
|
|
87
89
|
switch (cmd) {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
const XCODE_SELECT = "/usr/bin/xcode-select";
|
|
4
|
+
|
|
5
|
+
function hasHelpFlag(argv) {
|
|
6
|
+
return argv[0] === "help" || argv.slice(1).some((arg) => ["--help", "-h"].includes(arg));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Commands that may clone a skills marketplace or build a managed macOS app.
|
|
11
|
+
* Read-only commands deliberately stay available while Apple's installer is
|
|
12
|
+
* pending, and `update --check` keeps its documented no-mutation contract.
|
|
13
|
+
*/
|
|
14
|
+
export function commandRequiresMacDeveloperTools(argv = []) {
|
|
15
|
+
const [command, subcommand] = argv;
|
|
16
|
+
if (!command || hasHelpFlag(argv)) return false;
|
|
17
|
+
|
|
18
|
+
if (["setup", "_converge"].includes(command)) return true;
|
|
19
|
+
if (["update", "upgrade"].includes(command)) {
|
|
20
|
+
return !argv.includes("--check") && !argv.includes("--refresh-cache");
|
|
21
|
+
}
|
|
22
|
+
if (["skill", "skills"].includes(command)) return !subcommand || subcommand === "sync";
|
|
23
|
+
if (["app", "apps"].includes(command)) {
|
|
24
|
+
return ["install", "update", "open", "refresh"].includes(subcommand);
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Ensure Apple's Command Line Tools are available before Impel mutates any
|
|
31
|
+
* lifecycle state that depends on git or the native compiler. The Apple
|
|
32
|
+
* installer runs asynchronously, so a newly requested install must stop this
|
|
33
|
+
* invocation and let the operator rerun it after installation finishes.
|
|
34
|
+
*/
|
|
35
|
+
export function ensureMacDeveloperTools(argv = [], dependencies = {}) {
|
|
36
|
+
const io = {
|
|
37
|
+
platform: process.platform,
|
|
38
|
+
spawn: spawnSync,
|
|
39
|
+
log: (message) => console.log(message),
|
|
40
|
+
error: (message) => console.error(message),
|
|
41
|
+
setExitCode: (code) => {
|
|
42
|
+
process.exitCode = code;
|
|
43
|
+
},
|
|
44
|
+
...dependencies,
|
|
45
|
+
};
|
|
46
|
+
if (io.platform !== "darwin" || !commandRequiresMacDeveloperTools(argv)) return true;
|
|
47
|
+
|
|
48
|
+
const selected = io.spawn(XCODE_SELECT, ["-p"], { stdio: "ignore" });
|
|
49
|
+
if (!selected.error && selected.status === 0) return true;
|
|
50
|
+
|
|
51
|
+
io.log("Xcode Command Line Tools are required; opening Apple's installer…");
|
|
52
|
+
const requested = io.spawn(XCODE_SELECT, ["--install"], { stdio: "inherit" });
|
|
53
|
+
if (requested.error) {
|
|
54
|
+
io.error(`impel: could not start the Xcode Command Line Tools installer (${requested.error.message || requested.error}).`);
|
|
55
|
+
io.error(" Run `xcode-select --install`, finish the system installation, then rerun this Impel command.");
|
|
56
|
+
} else if (requested.status !== 0) {
|
|
57
|
+
io.error("impel: the Xcode Command Line Tools installer did not start successfully.");
|
|
58
|
+
io.error(" Run `xcode-select --install`, finish the system installation, then rerun this Impel command.");
|
|
59
|
+
} else {
|
|
60
|
+
io.error("impel: finish installing the Xcode Command Line Tools in the system dialog, then rerun this command.");
|
|
61
|
+
io.error(" No Impel profiles, apps, or skills were changed.");
|
|
62
|
+
}
|
|
63
|
+
io.setExitCode(1);
|
|
64
|
+
return false;
|
|
65
|
+
}
|