copilot-hub 0.1.28 → 0.1.29

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.
@@ -1,12 +1,16 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
- import dotenv from "dotenv";
5
4
  import { createWorkspaceBoundaryPolicy, assertWorkspaceAllowed, parseWorkspaceAllowedRoots, } from "@copilot-hub/core/workspace-policy";
6
5
  import { parseTurnActivityTimeoutSetting } from "@copilot-hub/core/codex-app-utils";
7
- import { resolveConfigBaseDir, resolveOptionalPathFromBase, resolvePathFromBase, } from "@copilot-hub/core/config-paths";
6
+ import { loadCopilotHubEnvironment } from "@copilot-hub/core";
7
+ import { resolveOptionalPathFromBase, resolvePathFromBase } from "@copilot-hub/core/config-paths";
8
8
  import { getDefaultExternalWorkspaceBasePath, getKernelRootPath, } from "@copilot-hub/core/workspace-paths";
9
- const envBaseDir = loadEnvironment();
9
+ const loadedEnvironment = loadEnvironment();
10
+ const envBaseDir = loadedEnvironment.baseDir;
11
+ const envFilePath = loadedEnvironment.envPath;
12
+ const envFileValues = loadedEnvironment.fileValues;
13
+ const envOverrideKeys = loadedEnvironment.overriddenKeys;
10
14
  const kernelRootPath = getKernelRootPath();
11
15
  const configuredDefaultWorkspaceRoot = String(process.env.DEFAULT_WORKSPACE_ROOT ?? "").trim();
12
16
  const defaultWorkspaceRoot = resolveWorkspaceRoot(configuredDefaultWorkspaceRoot || getDefaultExternalWorkspaceBasePath(kernelRootPath));
@@ -85,6 +89,9 @@ const defaultAllowedChatIds = new Set((process.env.TELEGRAM_ALLOWED_CHAT_IDS ??
85
89
  fs.mkdirSync(dataDir, { recursive: true });
86
90
  export const config = {
87
91
  envBaseDir,
92
+ envFilePath,
93
+ envFileValues,
94
+ envOverrideKeys,
88
95
  defaultProviderKind,
89
96
  providerDefaults: {
90
97
  defaultKind: defaultProviderKind,
@@ -125,22 +132,9 @@ export const config = {
125
132
  defaultAllowedChatIds,
126
133
  };
127
134
  function loadEnvironment() {
128
- const configuredEnvPath = String(process.env.COPILOT_HUB_ENV_PATH ?? "").trim();
129
- const resolvedEnvPath = configuredEnvPath ? path.resolve(configuredEnvPath) : "";
130
- const baseDir = resolveConfigBaseDir({
131
- configuredBaseDir: process.env.COPILOT_HUB_ENV_BASE_DIR,
132
- configuredEnvPath: resolvedEnvPath,
135
+ return loadCopilotHubEnvironment({
133
136
  cwd: process.cwd(),
134
137
  });
135
- if (configuredEnvPath) {
136
- process.env.COPILOT_HUB_ENV_PATH = resolvedEnvPath;
137
- dotenv.config({ path: resolvedEnvPath });
138
- }
139
- else {
140
- dotenv.config();
141
- }
142
- process.env.COPILOT_HUB_ENV_BASE_DIR = baseDir;
143
- return baseDir;
144
138
  }
145
139
  function resolveCodexBin(rawValue) {
146
140
  const value = String(rawValue ?? "").trim();
@@ -1,5 +1,5 @@
1
- HUB_TELEGRAM_TOKEN_ENV=HUB_TELEGRAM_TOKEN
2
- HUB_TELEGRAM_TOKEN=123456:replace_me
1
+ HUB_TELEGRAM_TOKEN_ENV=HUB_TELEGRAM_TOKEN_FILE
2
+ HUB_TELEGRAM_TOKEN_FILE=123456:replace_me
3
3
  HUB_ID=copilot_hub
4
4
  HUB_NAME=Copilot Hub
5
5
  # Optional. If empty, defaults to Desktop/copilot_workspaces (Windows/macOS/Linux)
@@ -21,4 +21,3 @@ CODEX_SANDBOX=danger-full-access
21
21
  CODEX_APPROVAL_POLICY=never
22
22
  TURN_ACTIVITY_TIMEOUT_MS=0
23
23
  MAX_THREAD_MESSAGES=200
24
-
@@ -1,13 +1,17 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
- import dotenv from "dotenv";
4
+ import { loadCopilotHubEnvironment } from "@copilot-hub/core";
5
5
  import { parseTurnActivityTimeoutSetting } from "@copilot-hub/core/codex-app-utils";
6
- import { resolveConfigBaseDir, resolveOptionalPathFromBase, resolvePathFromBase, } from "@copilot-hub/core/config-paths";
6
+ import { resolveOptionalPathFromBase, resolvePathFromBase } from "@copilot-hub/core/config-paths";
7
7
  import { createWorkspaceBoundaryPolicy, assertWorkspaceAllowed, parseWorkspaceAllowedRoots, } from "@copilot-hub/core/workspace-policy";
8
8
  import { normalizeAdminBotId, normalizeAdminTokenEnv } from "./kernel/admin-contract.js";
9
9
  import { getDefaultExternalWorkspaceBasePath, getKernelRootPath, } from "@copilot-hub/core/workspace-paths";
10
- const envBaseDir = loadEnvironment();
10
+ const loadedEnvironment = loadEnvironment();
11
+ const envBaseDir = loadedEnvironment.baseDir;
12
+ const envFilePath = loadedEnvironment.envPath;
13
+ const envFileValues = loadedEnvironment.fileValues;
14
+ const envOverrideKeys = loadedEnvironment.overriddenKeys;
11
15
  const kernelRootPath = getKernelRootPath();
12
16
  const configuredDefaultWorkspaceRoot = String(process.env.DEFAULT_WORKSPACE_ROOT ?? "").trim();
13
17
  const defaultWorkspaceRoot = resolveWorkspaceRoot(configuredDefaultWorkspaceRoot || getDefaultExternalWorkspaceBasePath(kernelRootPath));
@@ -89,6 +93,9 @@ const defaultAllowedChatIds = new Set((process.env.TELEGRAM_ALLOWED_CHAT_IDS ??
89
93
  fs.mkdirSync(dataDir, { recursive: true });
90
94
  export const config = {
91
95
  envBaseDir,
96
+ envFilePath,
97
+ envFileValues,
98
+ envOverrideKeys,
92
99
  defaultProviderKind,
93
100
  providerDefaults: {
94
101
  defaultKind: defaultProviderKind,
@@ -132,22 +139,9 @@ export const config = {
132
139
  defaultAllowedChatIds,
133
140
  };
134
141
  function loadEnvironment() {
135
- const configuredEnvPath = String(process.env.COPILOT_HUB_ENV_PATH ?? "").trim();
136
- const resolvedEnvPath = configuredEnvPath ? path.resolve(configuredEnvPath) : "";
137
- const baseDir = resolveConfigBaseDir({
138
- configuredBaseDir: process.env.COPILOT_HUB_ENV_BASE_DIR,
139
- configuredEnvPath: resolvedEnvPath,
142
+ return loadCopilotHubEnvironment({
140
143
  cwd: process.cwd(),
141
144
  });
142
- if (configuredEnvPath) {
143
- process.env.COPILOT_HUB_ENV_PATH = resolvedEnvPath;
144
- dotenv.config({ path: resolvedEnvPath });
145
- }
146
- else {
147
- dotenv.config();
148
- }
149
- process.env.COPILOT_HUB_ENV_BASE_DIR = baseDir;
150
- return baseDir;
151
145
  }
152
146
  function resolveCodexBin(rawValue) {
153
147
  const value = String(rawValue ?? "").trim();
@@ -3,16 +3,20 @@ import { BotRuntime } from "@copilot-hub/core/bot-runtime";
3
3
  import { config } from "./config.js";
4
4
  import { assertWorkspaceAllowed } from "@copilot-hub/core/workspace-policy";
5
5
  import { createChannelAdapter } from "./channels/channel-factory.js";
6
- const TELEGRAM_TOKEN_PATTERN = /^\d{5,}:[A-Za-z0-9_-]{20,}$/;
7
- const tokenEnvName = String(process.env.HUB_TELEGRAM_TOKEN_ENV ?? "HUB_TELEGRAM_TOKEN").trim() || "HUB_TELEGRAM_TOKEN";
8
- const hubToken = String(process.env[tokenEnvName] ?? "").trim();
6
+ import { isUsableTelegramToken, resolveHubTelegramToken } from "./hub-token-config.js";
7
+ const tokenResolution = resolveHubTelegramToken({
8
+ env: process.env,
9
+ envFileValues: config.envFileValues,
10
+ });
11
+ const tokenEnvName = tokenResolution.tokenEnvName;
12
+ const hubToken = tokenResolution.token;
9
13
  if (!hubToken) {
10
14
  throw new Error([
11
15
  `Hub Telegram token is missing (${tokenEnvName}).`,
12
16
  "Set this token to start copilot-hub.",
13
17
  ].join("\n"));
14
18
  }
15
- if (!TELEGRAM_TOKEN_PATTERN.test(hubToken) || hubToken.toLowerCase().includes("replace_me")) {
19
+ if (!isUsableTelegramToken(hubToken)) {
16
20
  throw new Error([
17
21
  `Hub Telegram token in ${tokenEnvName} is invalid.`,
18
22
  "Run 'copilot-hub configure' and paste a real BotFather token.",
@@ -73,6 +77,14 @@ const runtime = new BotRuntime({
73
77
  let shuttingDown = false;
74
78
  await bootstrap();
75
79
  async function bootstrap() {
80
+ for (const warning of tokenResolution.warnings) {
81
+ console.warn(`[copilot_hub] ${warning}`);
82
+ }
83
+ if (config.envOverrideKeys.includes("HUB_TELEGRAM_TOKEN_ENV") ||
84
+ config.envOverrideKeys.includes(tokenEnvName)) {
85
+ console.warn(`[copilot_hub] Config file '${config.envFilePath ?? ".env"}' overrode a pre-existing process environment value for the hub token settings.`);
86
+ }
87
+ console.log(`[copilot_hub] using hub Telegram token variable '${tokenEnvName}' from ${tokenResolution.source === "env_file" ? "config file" : "process environment"}.`);
76
88
  await runtime.startChannels();
77
89
  console.log(`[copilot_hub] online as '${hubId}' on workspace '${hubWorkspaceRoot}'.`);
78
90
  registerSignals();
@@ -0,0 +1,37 @@
1
+ const DEFAULT_HUB_TELEGRAM_TOKEN_ENV = "HUB_TELEGRAM_TOKEN_FILE";
2
+ const LEGACY_HUB_TELEGRAM_TOKEN_ENV = "HUB_TELEGRAM_TOKEN";
3
+ const TELEGRAM_TOKEN_PATTERN = /^\d{5,}:[A-Za-z0-9_-]{20,}$/;
4
+ export function resolveHubTelegramToken({ env = process.env, envFileValues = {}, } = {}) {
5
+ const fileTokenEnvName = String(envFileValues.HUB_TELEGRAM_TOKEN_ENV ?? "").trim();
6
+ const processTokenEnvName = String(env.HUB_TELEGRAM_TOKEN_ENV ?? "").trim();
7
+ const tokenEnvName = fileTokenEnvName || processTokenEnvName || DEFAULT_HUB_TELEGRAM_TOKEN_ENV;
8
+ const fileToken = String(envFileValues[tokenEnvName] ?? "").trim();
9
+ const processToken = String(env[tokenEnvName] ?? "").trim();
10
+ const token = fileToken || processToken;
11
+ const warnings = [];
12
+ if (fileTokenEnvName && processTokenEnvName && fileTokenEnvName !== processTokenEnvName) {
13
+ warnings.push(`Config file selects hub token variable '${fileTokenEnvName}', overriding pre-existing process env selector '${processTokenEnvName}'.`);
14
+ }
15
+ if (tokenEnvName === LEGACY_HUB_TELEGRAM_TOKEN_ENV) {
16
+ warnings.push(`Hub token variable '${LEGACY_HUB_TELEGRAM_TOKEN_ENV}' is deprecated. Run 'copilot-hub configure' to migrate to '${DEFAULT_HUB_TELEGRAM_TOKEN_ENV}'.`);
17
+ }
18
+ if (fileToken && processToken && fileToken !== processToken) {
19
+ const detail = isUsableTelegramToken(fileToken)
20
+ ? `Using the token saved in the config file for '${tokenEnvName}' instead of the conflicting process environment value.`
21
+ : `Config file value for '${tokenEnvName}' overrides the conflicting process environment value.`;
22
+ warnings.push(detail);
23
+ }
24
+ return {
25
+ tokenEnvName,
26
+ token,
27
+ source: fileToken ? "env_file" : "process_env",
28
+ warnings,
29
+ };
30
+ }
31
+ export function isUsableTelegramToken(value) {
32
+ const token = String(value ?? "").trim();
33
+ if (!token || token.toLowerCase().includes("replace_me")) {
34
+ return false;
35
+ }
36
+ return TELEGRAM_TOKEN_PATTERN.test(token);
37
+ }
@@ -0,0 +1,46 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ let configPromise = null;
4
+ async function loadConfig() {
5
+ if (!configPromise) {
6
+ const specifier = ["..", "hub-token-config.js"].join("/");
7
+ configPromise = import(specifier);
8
+ }
9
+ return configPromise;
10
+ }
11
+ test("resolveHubTelegramToken prefers the config file token over a conflicting process env token", async () => {
12
+ const { resolveHubTelegramToken } = await loadConfig();
13
+ const result = resolveHubTelegramToken({
14
+ env: {
15
+ HUB_TELEGRAM_TOKEN_ENV: "HUB_TELEGRAM_TOKEN",
16
+ HUB_TELEGRAM_TOKEN: "123456:invalid_process_value_ABCDEFGHIJKLM",
17
+ },
18
+ envFileValues: {
19
+ HUB_TELEGRAM_TOKEN_ENV: "HUB_TELEGRAM_TOKEN",
20
+ HUB_TELEGRAM_TOKEN: "123456:valid_file_value_ABCDEFGHIJKLMNOPQRSTUVWXYZ",
21
+ },
22
+ });
23
+ assert.equal(result.tokenEnvName, "HUB_TELEGRAM_TOKEN");
24
+ assert.equal(result.source, "env_file");
25
+ assert.equal(result.token, "123456:valid_file_value_ABCDEFGHIJKLMNOPQRSTUVWXYZ");
26
+ assert.match(result.warnings.join("\n"), /deprecated/i);
27
+ });
28
+ test("resolveHubTelegramToken uses the dedicated file token variable by default", async () => {
29
+ const { resolveHubTelegramToken } = await loadConfig();
30
+ const result = resolveHubTelegramToken({
31
+ env: {},
32
+ envFileValues: {
33
+ HUB_TELEGRAM_TOKEN_ENV: "HUB_TELEGRAM_TOKEN_FILE",
34
+ HUB_TELEGRAM_TOKEN_FILE: "123456:valid_file_value_ABCDEFGHIJKLMNOPQRSTUVWXYZ",
35
+ },
36
+ });
37
+ assert.equal(result.tokenEnvName, "HUB_TELEGRAM_TOKEN_FILE");
38
+ assert.equal(result.source, "env_file");
39
+ assert.equal(result.warnings.length, 0);
40
+ });
41
+ test("isUsableTelegramToken recognizes valid telegram token formats", async () => {
42
+ const { isUsableTelegramToken } = await loadConfig();
43
+ assert.equal(isUsableTelegramToken("123456:valid_file_value_ABCDEFGHIJKLMNOPQRSTUVWXYZ"), true);
44
+ assert.equal(isUsableTelegramToken("123456:replace_me"), false);
45
+ assert.equal(isUsableTelegramToken(""), false);
46
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copilot-hub",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "Copilot Hub CLI and runtime bundle",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,10 @@
1
+ export interface LoadedCopilotHubEnvironment {
2
+ baseDir: string;
3
+ envPath: string | null;
4
+ fileValues: Record<string, string>;
5
+ overriddenKeys: string[];
6
+ }
7
+ export declare function loadCopilotHubEnvironment({ env, cwd, }?: {
8
+ env?: NodeJS.ProcessEnv;
9
+ cwd?: string;
10
+ }): LoadedCopilotHubEnvironment;
@@ -0,0 +1,66 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+ import dotenv from "dotenv";
5
+ import { resolveConfigBaseDir } from "./config-paths.js";
6
+ export function loadCopilotHubEnvironment({ env = process.env, cwd = process.cwd(), } = {}) {
7
+ const configuredEnvPath = String(env.COPILOT_HUB_ENV_PATH ?? "").trim();
8
+ const resolvedEnvPath = configuredEnvPath ? path.resolve(configuredEnvPath) : "";
9
+ const discoveredEnvPath = resolvedEnvPath || resolveDefaultEnvPath(cwd);
10
+ const baseDir = resolveConfigBaseDir({
11
+ configuredBaseDir: env.COPILOT_HUB_ENV_BASE_DIR,
12
+ configuredEnvPath: discoveredEnvPath,
13
+ cwd,
14
+ });
15
+ const fileValues = loadEnvFileValues(discoveredEnvPath);
16
+ const overriddenKeys = applyEnvFileValues(env, fileValues);
17
+ if (resolvedEnvPath) {
18
+ env.COPILOT_HUB_ENV_PATH = resolvedEnvPath;
19
+ }
20
+ env.COPILOT_HUB_ENV_BASE_DIR = baseDir;
21
+ return {
22
+ baseDir,
23
+ envPath: discoveredEnvPath || null,
24
+ fileValues,
25
+ overriddenKeys,
26
+ };
27
+ }
28
+ function resolveDefaultEnvPath(cwd) {
29
+ const candidate = path.resolve(String(cwd ?? process.cwd()), ".env");
30
+ return fs.existsSync(candidate) ? candidate : "";
31
+ }
32
+ function loadEnvFileValues(filePath) {
33
+ if (!filePath || !fs.existsSync(filePath)) {
34
+ return {};
35
+ }
36
+ try {
37
+ const raw = fs.readFileSync(filePath, "utf8");
38
+ return normalizeEnvMap(dotenv.parse(raw));
39
+ }
40
+ catch {
41
+ return {};
42
+ }
43
+ }
44
+ function applyEnvFileValues(env, fileValues) {
45
+ const overriddenKeys = [];
46
+ for (const [key, value] of Object.entries(fileValues)) {
47
+ const previousValue = String(env[key] ?? "");
48
+ if (previousValue && previousValue !== value) {
49
+ overriddenKeys.push(key);
50
+ }
51
+ env[key] = value;
52
+ }
53
+ return overriddenKeys.sort();
54
+ }
55
+ function normalizeEnvMap(value) {
56
+ const output = {};
57
+ for (const [key, entry] of Object.entries(value ?? {})) {
58
+ const normalizedKey = String(key ?? "").trim();
59
+ if (!normalizedKey) {
60
+ continue;
61
+ }
62
+ output[normalizedKey] = String(entry ?? "").trim();
63
+ }
64
+ return output;
65
+ }
66
+ //# sourceMappingURL=env-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-config.js","sourceRoot":"","sources":["../src/env-config.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  export { createWorkspaceBoundaryPolicy, assertWorkspaceAllowed, isPathInside, normalizeAbsolutePath, parseWorkspaceAllowedRoots, } from "./workspace-policy.js";
2
2
  export { normalizeThreadId } from "./thread-id.js";
3
3
  export { createProjectFingerprint } from "./project-fingerprint.js";
4
- export { DEFAULT_EXTERNAL_WORKSPACES_DIRNAME, getKernelRootPath, getDefaultExternalWorkspaceBasePath, resolveDefaultWorkspaceForBot, isPathInside as isWorkspacePathInside, } from "./workspace-paths.js";
4
+ export { DEFAULT_EXTERNAL_WORKSPACES_DIRNAME, getKernelRootPath, resolveKernelRootPath, getDefaultExternalWorkspaceBasePath, resolveExternalWorkspaceBasePath, resolveDefaultWorkspaceForBot, isPathInside as isWorkspacePathInside, } from "./workspace-paths.js";
5
5
  export { resolveConfigBaseDir, resolveProcessConfigBaseDir, resolvePathFromBase, resolveOptionalPathFromBase, } from "./config-paths.js";
6
+ export { loadCopilotHubEnvironment } from "./env-config.js";
6
7
  export { InstanceLock } from "./instance-lock.js";
7
8
  export { JsonStateStore } from "./state-store.js";
8
9
  export { assertControlPermission } from "./control-permission.js";
@@ -1,8 +1,9 @@
1
1
  export { createWorkspaceBoundaryPolicy, assertWorkspaceAllowed, isPathInside, normalizeAbsolutePath, parseWorkspaceAllowedRoots, } from "./workspace-policy.js";
2
2
  export { normalizeThreadId } from "./thread-id.js";
3
3
  export { createProjectFingerprint } from "./project-fingerprint.js";
4
- export { DEFAULT_EXTERNAL_WORKSPACES_DIRNAME, getKernelRootPath, getDefaultExternalWorkspaceBasePath, resolveDefaultWorkspaceForBot, isPathInside as isWorkspacePathInside, } from "./workspace-paths.js";
4
+ export { DEFAULT_EXTERNAL_WORKSPACES_DIRNAME, getKernelRootPath, resolveKernelRootPath, getDefaultExternalWorkspaceBasePath, resolveExternalWorkspaceBasePath, resolveDefaultWorkspaceForBot, isPathInside as isWorkspacePathInside, } from "./workspace-paths.js";
5
5
  export { resolveConfigBaseDir, resolveProcessConfigBaseDir, resolvePathFromBase, resolveOptionalPathFromBase, } from "./config-paths.js";
6
+ export { loadCopilotHubEnvironment } from "./env-config.js";
6
7
  export { InstanceLock } from "./instance-lock.js";
7
8
  export { JsonStateStore } from "./state-store.js";
8
9
  export { assertControlPermission } from "./control-permission.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACtB,YAAY,EACZ,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EACL,mCAAmC,EACnC,iBAAiB,EACjB,mCAAmC,EACnC,6BAA6B,EAC7B,YAAY,IAAI,qBAAqB,GACtC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACtB,YAAY,EACZ,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EACL,mCAAmC,EACnC,iBAAiB,EACjB,qBAAqB,EACrB,mCAAmC,EACnC,gCAAgC,EAChC,6BAA6B,EAC7B,YAAY,IAAI,qBAAqB,GACtC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -1,5 +1,16 @@
1
1
  export declare const DEFAULT_EXTERNAL_WORKSPACES_DIRNAME = "copilot_workspaces";
2
2
  export declare function getKernelRootPath(): string;
3
- export declare function getDefaultExternalWorkspaceBasePath(_kernelRoot?: string): string;
3
+ export declare function resolveKernelRootPath({ env, moduleUrl, cwd, }?: {
4
+ env?: NodeJS.ProcessEnv;
5
+ moduleUrl?: string;
6
+ cwd?: string;
7
+ }): string;
8
+ export declare function getDefaultExternalWorkspaceBasePath(kernelRoot?: string): string;
9
+ export declare function resolveExternalWorkspaceBasePath({ kernelRootPath, desktopCandidates, homeDir, tempDir, }: {
10
+ kernelRootPath: string;
11
+ desktopCandidates?: readonly string[];
12
+ homeDir?: string;
13
+ tempDir?: string;
14
+ }): string;
4
15
  export declare function resolveDefaultWorkspaceForBot(botId: unknown, kernelRoot?: string): string;
5
16
  export declare function isPathInside(parentPath: unknown, candidatePath: unknown): boolean;
@@ -1,18 +1,42 @@
1
1
  import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
+ import process from "node:process";
5
+ import { fileURLToPath } from "node:url";
4
6
  export const DEFAULT_EXTERNAL_WORKSPACES_DIRNAME = "copilot_workspaces";
5
7
  const DESKTOP_DIRNAME = "Desktop";
6
8
  export function getKernelRootPath() {
7
- return path.resolve(process.cwd());
9
+ return resolveKernelRootPath();
8
10
  }
9
- export function getDefaultExternalWorkspaceBasePath(_kernelRoot = getKernelRootPath()) {
10
- const desktopCandidates = getDesktopCandidates();
11
- const fallbackDesktop = path.resolve(process.cwd(), DESKTOP_DIRNAME);
11
+ export function resolveKernelRootPath({ env = process.env, moduleUrl = import.meta.url, cwd = process.cwd(), } = {}) {
12
+ const configuredRoot = String(env.COPILOT_HUB_KERNEL_ROOT ?? "").trim();
13
+ if (configuredRoot) {
14
+ return path.resolve(configuredRoot);
15
+ }
16
+ const moduleFilePath = String(moduleUrl ?? "").trim();
17
+ if (moduleFilePath) {
18
+ return path.resolve(path.dirname(fileURLToPath(moduleFilePath)), "..", "..", "..");
19
+ }
20
+ return path.resolve(String(cwd ?? process.cwd()));
21
+ }
22
+ export function getDefaultExternalWorkspaceBasePath(kernelRoot = getKernelRootPath()) {
23
+ return resolveExternalWorkspaceBasePath({
24
+ kernelRootPath: kernelRoot,
25
+ });
26
+ }
27
+ export function resolveExternalWorkspaceBasePath({ kernelRootPath, desktopCandidates = getDesktopCandidates(), homeDir = os.homedir(), tempDir = os.tmpdir(), }) {
12
28
  const desktopRoot = desktopCandidates.find((candidate) => directoryExists(candidate)) ??
13
29
  desktopCandidates[0] ??
14
- fallbackDesktop;
15
- return path.resolve(desktopRoot, DEFAULT_EXTERNAL_WORKSPACES_DIRNAME);
30
+ path.resolve(String(homeDir ?? "").trim() || process.cwd(), DESKTOP_DIRNAME);
31
+ const preferredRoot = path.resolve(desktopRoot, DEFAULT_EXTERNAL_WORKSPACES_DIRNAME);
32
+ if (!isPathInside(kernelRootPath, preferredRoot)) {
33
+ return preferredRoot;
34
+ }
35
+ const homeFallbackRoot = path.resolve(String(homeDir ?? "").trim() || process.cwd(), DEFAULT_EXTERNAL_WORKSPACES_DIRNAME);
36
+ if (!isPathInside(kernelRootPath, homeFallbackRoot)) {
37
+ return homeFallbackRoot;
38
+ }
39
+ return path.resolve(String(tempDir ?? "").trim() || process.cwd(), DEFAULT_EXTERNAL_WORKSPACES_DIRNAME);
16
40
  }
17
41
  export function resolveDefaultWorkspaceForBot(botId, kernelRoot = getKernelRootPath()) {
18
42
  const id = String(botId ?? "").trim();
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-paths.js","sourceRoot":"","sources":["../src/workspace-paths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace-paths.js","sourceRoot":"","sources":["../src/workspace-paths.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,10 @@
34
34
  "types": "./dist/config-paths.d.ts",
35
35
  "import": "./dist/config-paths.js"
36
36
  },
37
+ "./env-config": {
38
+ "types": "./dist/env-config.d.ts",
39
+ "import": "./dist/env-config.js"
40
+ },
37
41
  "./instance-lock": {
38
42
  "types": "./dist/instance-lock.d.ts",
39
43
  "import": "./dist/instance-lock.js"
@@ -15,7 +15,8 @@ const engineExamplePath = path.join(repoRoot, "apps", "agent-engine", ".env.exam
15
15
  const controlPlaneEnvPath = layout.controlPlaneEnvPath;
16
16
  const controlPlaneExamplePath = path.join(repoRoot, "apps", "control-plane", ".env.example");
17
17
  const TELEGRAM_TOKEN_PATTERN = /^\d{5,}:[A-Za-z0-9_-]{20,}$/;
18
- const DEFAULT_CONTROL_PLANE_TOKEN_ENV = "HUB_TELEGRAM_TOKEN";
18
+ const DEFAULT_CONTROL_PLANE_TOKEN_ENV = "HUB_TELEGRAM_TOKEN_FILE";
19
+ const LEGACY_CONTROL_PLANE_TOKEN_ENV = "HUB_TELEGRAM_TOKEN";
19
20
  const args = new Set(process.argv.slice(2));
20
21
  const requiredOnly = args.has("--required-only");
21
22
  await main();
@@ -44,9 +45,7 @@ async function main() {
44
45
  writeLines(controlPlaneEnvPath, controlPlaneLines);
45
46
  }
46
47
  async function configureRequiredTokens({ rl, controlPlaneLines }) {
47
- const controlPlaneMap = parseEnvMap(controlPlaneLines);
48
- const controlPlaneTokenEnvName = nonEmpty(controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV, DEFAULT_CONTROL_PLANE_TOKEN_ENV);
49
- setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvName);
48
+ const controlPlaneTokenEnvName = migrateControlPlaneTokenEnv(controlPlaneLines);
50
49
  const postControlPlaneMap = parseEnvMap(controlPlaneLines);
51
50
  const currentToken = String(postControlPlaneMap[controlPlaneTokenEnvName] ?? "").trim();
52
51
  if (isUsableTelegramToken(currentToken)) {
@@ -62,10 +61,8 @@ async function configureRequiredTokens({ rl, controlPlaneLines }) {
62
61
  console.log("Required token saved.");
63
62
  }
64
63
  async function configureAll({ rl, controlPlaneLines }) {
65
- const controlPlaneMap = parseEnvMap(controlPlaneLines);
66
64
  console.log("\nCopilot Hub control-plane configuration\n");
67
- const controlPlaneTokenEnvDefault = nonEmpty(controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV, DEFAULT_CONTROL_PLANE_TOKEN_ENV);
68
- setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvDefault);
65
+ const controlPlaneTokenEnvDefault = migrateControlPlaneTokenEnv(controlPlaneLines);
69
66
  const currentControlPlaneToken = String(parseEnvMap(controlPlaneLines)[controlPlaneTokenEnvDefault] ?? "").trim();
70
67
  const newControlPlaneToken = currentControlPlaneToken
71
68
  ? await askTelegramToken(rl, "Control-plane Telegram token (press Enter to keep current)", true)
@@ -77,6 +74,23 @@ async function configureAll({ rl, controlPlaneLines }) {
77
74
  console.log("- Control-plane token left unchanged.");
78
75
  }
79
76
  }
77
+ function migrateControlPlaneTokenEnv(lines) {
78
+ const controlPlaneMap = parseEnvMap(lines);
79
+ const configuredTokenEnvName = nonEmpty(controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV, DEFAULT_CONTROL_PLANE_TOKEN_ENV);
80
+ const shouldMigrateLegacyName = configuredTokenEnvName === LEGACY_CONTROL_PLANE_TOKEN_ENV;
81
+ const nextTokenEnvName = shouldMigrateLegacyName
82
+ ? DEFAULT_CONTROL_PLANE_TOKEN_ENV
83
+ : configuredTokenEnvName;
84
+ setEnvValue(lines, "HUB_TELEGRAM_TOKEN_ENV", nextTokenEnvName);
85
+ if (shouldMigrateLegacyName) {
86
+ const legacyToken = String(controlPlaneMap[LEGACY_CONTROL_PLANE_TOKEN_ENV] ?? "").trim();
87
+ const dedicatedToken = String(controlPlaneMap[DEFAULT_CONTROL_PLANE_TOKEN_ENV] ?? "").trim();
88
+ if (legacyToken && !dedicatedToken) {
89
+ setEnvValue(lines, DEFAULT_CONTROL_PLANE_TOKEN_ENV, legacyToken);
90
+ }
91
+ }
92
+ return nextTokenEnvName;
93
+ }
80
94
  function ensureEnvFile(envPath, examplePath) {
81
95
  fs.mkdirSync(path.dirname(envPath), { recursive: true });
82
96
  if (fs.existsSync(envPath)) {
@@ -508,6 +508,23 @@ function detectFatalStartupError(ensureResult) {
508
508
  detectedAt: new Date().toISOString(),
509
509
  };
510
510
  }
511
+ const invalidHubTokenLine = findLineContaining(evidenceChunks, (line) => line.includes("hub telegram token in") && line.includes("is invalid"));
512
+ if (invalidHubTokenLine) {
513
+ return {
514
+ reason: invalidHubTokenLine,
515
+ action: "Run 'copilot-hub configure' to save a valid hub token in the control-plane config, then retry service.",
516
+ detectedAt: new Date().toISOString(),
517
+ };
518
+ }
519
+ const workspaceRootLine = findLineContaining(evidenceChunks, (line) => line.includes("default_workspace_root must be outside kernel directory") ||
520
+ line.includes("hub_workspace_root must be outside kernel directory"));
521
+ if (workspaceRootLine) {
522
+ return {
523
+ reason: workspaceRootLine,
524
+ action: "Set DEFAULT_WORKSPACE_ROOT to a folder outside the copilot-hub installation, then retry service.",
525
+ detectedAt: new Date().toISOString(),
526
+ };
527
+ }
511
528
  return null;
512
529
  }
513
530
  function readLogTail(filePath, maxLines = 120) {
@@ -18,7 +18,8 @@ const engineExamplePath = path.join(repoRoot, "apps", "agent-engine", ".env.exam
18
18
  const controlPlaneEnvPath = layout.controlPlaneEnvPath;
19
19
  const controlPlaneExamplePath = path.join(repoRoot, "apps", "control-plane", ".env.example");
20
20
  const TELEGRAM_TOKEN_PATTERN = /^\d{5,}:[A-Za-z0-9_-]{20,}$/;
21
- const DEFAULT_CONTROL_PLANE_TOKEN_ENV = "HUB_TELEGRAM_TOKEN";
21
+ const DEFAULT_CONTROL_PLANE_TOKEN_ENV = "HUB_TELEGRAM_TOKEN_FILE";
22
+ const LEGACY_CONTROL_PLANE_TOKEN_ENV = "HUB_TELEGRAM_TOKEN";
22
23
 
23
24
  const args = new Set(process.argv.slice(2));
24
25
  const requiredOnly = args.has("--required-only");
@@ -53,13 +54,7 @@ async function main() {
53
54
  }
54
55
 
55
56
  async function configureRequiredTokens({ rl, controlPlaneLines }) {
56
- const controlPlaneMap = parseEnvMap(controlPlaneLines);
57
-
58
- const controlPlaneTokenEnvName = nonEmpty(
59
- controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV,
60
- DEFAULT_CONTROL_PLANE_TOKEN_ENV,
61
- );
62
- setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvName);
57
+ const controlPlaneTokenEnvName = migrateControlPlaneTokenEnv(controlPlaneLines);
63
58
 
64
59
  const postControlPlaneMap = parseEnvMap(controlPlaneLines);
65
60
  const currentToken = String(postControlPlaneMap[controlPlaneTokenEnvName] ?? "").trim();
@@ -82,15 +77,9 @@ async function configureRequiredTokens({ rl, controlPlaneLines }) {
82
77
  }
83
78
 
84
79
  async function configureAll({ rl, controlPlaneLines }) {
85
- const controlPlaneMap = parseEnvMap(controlPlaneLines);
86
-
87
80
  console.log("\nCopilot Hub control-plane configuration\n");
88
81
 
89
- const controlPlaneTokenEnvDefault = nonEmpty(
90
- controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV,
91
- DEFAULT_CONTROL_PLANE_TOKEN_ENV,
92
- );
93
- setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvDefault);
82
+ const controlPlaneTokenEnvDefault = migrateControlPlaneTokenEnv(controlPlaneLines);
94
83
  const currentControlPlaneToken = String(
95
84
  parseEnvMap(controlPlaneLines)[controlPlaneTokenEnvDefault] ?? "",
96
85
  ).trim();
@@ -106,6 +95,29 @@ async function configureAll({ rl, controlPlaneLines }) {
106
95
  }
107
96
  }
108
97
 
98
+ function migrateControlPlaneTokenEnv(lines) {
99
+ const controlPlaneMap = parseEnvMap(lines);
100
+ const configuredTokenEnvName = nonEmpty(
101
+ controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV,
102
+ DEFAULT_CONTROL_PLANE_TOKEN_ENV,
103
+ );
104
+ const shouldMigrateLegacyName = configuredTokenEnvName === LEGACY_CONTROL_PLANE_TOKEN_ENV;
105
+ const nextTokenEnvName = shouldMigrateLegacyName
106
+ ? DEFAULT_CONTROL_PLANE_TOKEN_ENV
107
+ : configuredTokenEnvName;
108
+ setEnvValue(lines, "HUB_TELEGRAM_TOKEN_ENV", nextTokenEnvName);
109
+
110
+ if (shouldMigrateLegacyName) {
111
+ const legacyToken = String(controlPlaneMap[LEGACY_CONTROL_PLANE_TOKEN_ENV] ?? "").trim();
112
+ const dedicatedToken = String(controlPlaneMap[DEFAULT_CONTROL_PLANE_TOKEN_ENV] ?? "").trim();
113
+ if (legacyToken && !dedicatedToken) {
114
+ setEnvValue(lines, DEFAULT_CONTROL_PLANE_TOKEN_ENV, legacyToken);
115
+ }
116
+ }
117
+
118
+ return nextTokenEnvName;
119
+ }
120
+
109
121
  function ensureEnvFile(envPath, examplePath) {
110
122
  fs.mkdirSync(path.dirname(envPath), { recursive: true });
111
123
  if (fs.existsSync(envPath)) {
@@ -600,6 +600,34 @@ function detectFatalStartupError(ensureResult) {
600
600
  };
601
601
  }
602
602
 
603
+ const invalidHubTokenLine = findLineContaining(
604
+ evidenceChunks,
605
+ (line) => line.includes("hub telegram token in") && line.includes("is invalid"),
606
+ );
607
+ if (invalidHubTokenLine) {
608
+ return {
609
+ reason: invalidHubTokenLine,
610
+ action:
611
+ "Run 'copilot-hub configure' to save a valid hub token in the control-plane config, then retry service.",
612
+ detectedAt: new Date().toISOString(),
613
+ };
614
+ }
615
+
616
+ const workspaceRootLine = findLineContaining(
617
+ evidenceChunks,
618
+ (line) =>
619
+ line.includes("default_workspace_root must be outside kernel directory") ||
620
+ line.includes("hub_workspace_root must be outside kernel directory"),
621
+ );
622
+ if (workspaceRootLine) {
623
+ return {
624
+ reason: workspaceRootLine,
625
+ action:
626
+ "Set DEFAULT_WORKSPACE_ROOT to a folder outside the copilot-hub installation, then retry service.",
627
+ detectedAt: new Date().toISOString(),
628
+ };
629
+ }
630
+
603
631
  return null;
604
632
  }
605
633