impel-cli 0.20.63-canary.6 → 0.20.63-canary.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impel-cli",
3
- "version": "0.20.63-canary.6",
3
+ "version": "0.20.63-canary.7",
4
4
  "description": "Prepare isolated Claude and Codex workspaces for every accessible Impel tenant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,14 +2,27 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { spawn } from "node:child_process";
5
+ import { RUNTIME_BRAND } from "./runtimeBrand.js";
5
6
 
6
7
  const TENANT = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/u;
7
8
  const APPLICATIONS_BY_TOOL = Object.freeze({
8
9
  claude: ["claude"],
9
10
  codex: ["codex", "chatgpt"],
10
11
  });
11
- const MAC_RUNNER = "/Applications/Impel Managed App Runner.app/Contents/Library/LaunchServices/impel-managed-runner";
12
- const MAC_DESCRIPTOR_ROOT = "/Library/Application Support/Impel/Managed App Runner/descriptors";
12
+ // Managed-app discovery is channel scoped. The canary installer may replace
13
+ // the machine-global runner, but its descriptors and credentials must remain
14
+ // disjoint from stable state so reinstall/recovery never crosses channels.
15
+ const BRAND_DISPLAY_PREFIX = RUNTIME_BRAND.channel === "canary"
16
+ && RUNTIME_BRAND.apps.displayPrefix === "Impel"
17
+ ? "Impel Canary"
18
+ : RUNTIME_BRAND.apps.displayPrefix;
19
+ const MAC_RUNNER = `/Applications/${BRAND_DISPLAY_PREFIX} Managed App Runner.app/Contents/Library/LaunchServices/impel-managed-runner`;
20
+ const MAC_DESCRIPTOR_ROOT = `/Library/Application Support/${BRAND_DISPLAY_PREFIX}/Managed App Runner/descriptors`;
21
+ const CONFIG_ROOT_SEGMENT = RUNTIME_BRAND.cli.configNamespace;
22
+
23
+ function credentialPath(home, tenantId) {
24
+ return path.join(home, ".config", CONFIG_ROOT_SEGMENT, "apps", "credentials", `${tenantId}.json`);
25
+ }
13
26
 
14
27
  function regularFile(file) {
15
28
  try {
@@ -44,7 +57,7 @@ function parseCandidate(manifestPath, launcher, applicationId, platform, home) {
44
57
  || String(tenantId).includes("..")
45
58
  || manifest?.logicalApplicationId !== `${tenantId}.${applicationId}`
46
59
  ) return null;
47
- const credential = path.join(home, ".config", "impel", "apps", "credentials", `${tenantId}.json`);
60
+ const credential = credentialPath(home, tenantId);
48
61
  if (!regularFile(credential)) return null;
49
62
  return {
50
63
  tenantId,
@@ -101,7 +114,7 @@ function runnerEnvelopeCandidate(envelopePath, launcher, applicationId, home) {
101
114
  || String(tenantId).includes("..")
102
115
  || payload?.logicalApplicationId !== `${tenantId}.${applicationId}`
103
116
  ) return null;
104
- const credential = path.join(home, ".config", "impel", "apps", "credentials", `${tenantId}.json`);
117
+ const credential = credentialPath(home, tenantId);
105
118
  if (!regularFile(credential)) return null;
106
119
  return {
107
120
  tenantId,
@@ -185,7 +198,7 @@ export function installedManagedAppCandidates(tool, {
185
198
  platform = process.platform,
186
199
  home = os.homedir(),
187
200
  applicationsRoot = platform === "win32"
188
- ? path.join(process.env.ProgramFiles || "C:\\Program Files", "Impel", "Apps")
201
+ ? path.join(process.env.ProgramFiles || "C:\\Program Files", BRAND_DISPLAY_PREFIX, "Apps")
189
202
  : "/Applications",
190
203
  descriptorRoot = MAC_DESCRIPTOR_ROOT,
191
204
  runnerLauncher = MAC_RUNNER,
@@ -5,6 +5,7 @@ import { tenantCliProfilePaths } from "../cliProfiles.js";
5
5
  import { redactSecretText } from "../config.js";
6
6
  import { runBuffer, runCapture, runInteractive, sleep } from "./process.js";
7
7
  import { runPaths } from "./state.js";
8
+ import { RUNTIME_BRAND } from "../runtimeBrand.js";
8
9
 
9
10
  // LEGACY ONLY: this module's SSH upload and PAT-bearing remote configuration
10
11
  // are isolated to portable-legacy. Broker-backed modes use immutable workspace
@@ -15,6 +16,7 @@ const REMOTE_REPO = "/workspace/repo";
15
16
  const REMOTE_BUNDLE = "/tmp/impel-remote-repo.bundle";
16
17
  const SESSION_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
17
18
  const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/u;
19
+ export const REMOTE_CONFIG_ROOT = `/home/agent/.config/${RUNTIME_BRAND.cli.configNamespace}`;
18
20
 
19
21
  function git(args, options = {}) {
20
22
  return runCapture(process.env.IMPEL_REMOTE_GIT_BIN || "git", args, options).stdout.trim();
@@ -190,16 +192,16 @@ export function uploadRemoteConfiguration(state, {
190
192
  const environmentFile = Object.entries(selectedEnvironment)
191
193
  .map(([name, value]) => `export ${name}=${shellQuote(value)}`)
192
194
  .join("\n") + "\n";
193
- const profileRoot = `/home/agent/.config/impel/cli/tenants/${tenant.tenantId}`;
195
+ const profileRoot = `${REMOTE_CONFIG_ROOT}/cli/tenants/${tenant.tenantId}`;
194
196
  const claudeSettings = {
195
197
  permissions: { defaultMode: "bypassPermissions" },
196
198
  sandbox: { enabled: false },
197
199
  };
198
200
  const files = [
199
- { path: "/home/agent/.config/impel/config.json", contents: `${JSON.stringify(remoteConfig, null, 2)}\n` },
200
- { path: "/home/agent/.config/impel/remote/env.sh", contents: environmentFile },
201
+ { path: `${REMOTE_CONFIG_ROOT}/config.json`, contents: `${JSON.stringify(remoteConfig, null, 2)}\n` },
202
+ { path: `${REMOTE_CONFIG_ROOT}/remote/env.sh`, contents: environmentFile },
201
203
  {
202
- path: "/home/agent/.config/impel/remote/run.json",
204
+ path: `${REMOTE_CONFIG_ROOT}/remote/run.json`,
203
205
  contents: `${JSON.stringify({
204
206
  runId: state.runId,
205
207
  credentialTokenId: credential.tokenId,
@@ -214,12 +216,12 @@ const path = require("node:path");
214
216
  const payload = JSON.parse(fs.readFileSync(0, "utf8"));
215
217
  for (const file of payload.files) {
216
218
  const target = path.resolve(file.path);
217
- if (!target.startsWith("/home/agent/.config/impel/")) throw new Error("refusing remote path");
219
+ if (!target.startsWith(payload.root + "/")) throw new Error("refusing remote path");
218
220
  fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
219
221
  fs.writeFileSync(target, file.contents, { mode: 0o600 });
220
222
  fs.chmodSync(target, 0o600);
221
223
  }
222
- `, { files });
224
+ `, { files, root: REMOTE_CONFIG_ROOT });
223
225
  }
224
226
 
225
227
  export function uploadRepository(state, repository) {
@@ -294,8 +296,8 @@ export function transferSessionCheckpoint(state, { provider, sessionId, tenantId
294
296
  const profiles = tenantCliProfilePaths(tenantId);
295
297
  const localRoot = provider === "claude" ? profiles.claudeConfigDir : profiles.codexHome;
296
298
  const remoteRoot = provider === "claude"
297
- ? `/home/agent/.config/impel/cli/tenants/${tenantId}/claude`
298
- : `/home/agent/.config/impel/cli/tenants/${tenantId}/codex`;
299
+ ? `${REMOTE_CONFIG_ROOT}/cli/tenants/${tenantId}/claude`
300
+ : `${REMOTE_CONFIG_ROOT}/cli/tenants/${tenantId}/codex`;
299
301
  const files = collectSessionFiles(localRoot, sessionId);
300
302
  if (files.length === 0) {
301
303
  throw new Error(`could not find ${provider} session ${sessionId} in the selected Impel CLI profile`);
package/src/windowsGit.js CHANGED
@@ -15,6 +15,7 @@ import path from "node:path";
15
15
 
16
16
  import { nativeCommandInvocation, nativeSpawnInvocation } from "./nativeProcess.js";
17
17
  import { renameWithWindowsRetry } from "./windowsFs.js";
18
+ import { RUNTIME_BRAND } from "./runtimeBrand.js";
18
19
 
19
20
  /**
20
21
  * The exact MinGit release the CLI may install. Never use `latest` or an
@@ -52,7 +53,7 @@ const POWERSHELL_ARGS = [
52
53
 
53
54
  /** Root of the Impel-managed MinGit install. */
54
55
  export function managedGitRoot(homeDir = os.homedir()) {
55
- return path.join(homeDir, ".config", "impel", "tools", "git");
56
+ return path.join(homeDir, ".config", RUNTIME_BRAND.cli.configNamespace, "tools", "git");
56
57
  }
57
58
 
58
59
  /** The PATH-safe git.exe inside a MinGit root (MinGit unpacks `cmd/` at top level). */