@keyou007/agent-bot 0.1.4 → 0.1.6

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/src/cli.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import fs from "node:fs";
2
3
  import { spawnSync } from "node:child_process";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import qrcode from "qrcode-terminal";
5
- import { loadConfig } from "./config/loadConfig.js";
6
+ import { loadConfig, loadConfigWithoutEnvironmentMutation } from "./config/loadConfig.js";
6
7
  import { sendControlRequest, isServerReachable } from "./cli/LocalControlClient.js";
7
8
  import {
8
9
  controlEndpoint,
@@ -19,6 +20,7 @@ import {
19
20
  type InitializationResult,
20
21
  type InitializationStatus,
21
22
  } from "./cli/Initializer.js";
23
+ import { parseInitCommandOptions, type InitCommandOptions } from "./cli/initOptions.js";
22
24
  import {
23
25
  registerFeishuApp,
24
26
  type FeishuAppCredentials,
@@ -47,6 +49,7 @@ import {
47
49
  prepareCrashReportDirectory,
48
50
  resolveSupervisorDiagnosticsPaths,
49
51
  } from "./supervision/SupervisorDiagnostics.js";
52
+ import { agentBotHome, defaultSqlitePath } from "./config/paths.js";
50
53
 
51
54
  const args = process.argv.slice(2);
52
55
 
@@ -69,7 +72,7 @@ async function main(input: string[]): Promise<void> {
69
72
  return;
70
73
  }
71
74
  if (command === "init") {
72
- await initCommand(rest, parsed.configPath);
75
+ await initCommand(rest, parsed.configPath, Boolean(parsed.profilePath));
73
76
  return;
74
77
  }
75
78
  if (command === "console") {
@@ -102,44 +105,43 @@ interface InitCommandResult extends InitializationResult {
102
105
  server: InitializationServerResult;
103
106
  }
104
107
 
105
- async function initCommand(input: string[], configPath?: string): Promise<void> {
106
- const supported = new Set(["--json", "--skip-feishu", "--reconfigure-feishu"]);
107
- const unsupported = input.filter((value) => !supported.has(value));
108
- if (unsupported.length > 0) throw new Error(`Unsupported init options: ${unsupported.join(" ")}`);
109
- const json = input.includes("--json");
110
- const skipFeishu = input.includes("--skip-feishu");
111
- const reconfigureFeishu = input.includes("--reconfigure-feishu");
112
- if (skipFeishu && reconfigureFeishu) {
113
- throw new Error("--skip-feishu and --reconfigure-feishu cannot be used together.");
114
- }
115
-
116
- const paths = initializeAgentBot({ configPath });
117
- if (!json) printInitializationPaths(paths);
118
- const initializationLock = acquireInitializationLock(paths.home.path);
119
-
108
+ async function initCommand(
109
+ input: string[],
110
+ configPath: string | undefined,
111
+ explicitProfile: boolean,
112
+ ): Promise<void> {
113
+ const options = parseInitCommandOptions(input, explicitProfile);
114
+ let initializationLock = options.reset
115
+ ? acquireInitializationLock(agentBotHome())
116
+ : undefined;
117
+ let paths: InitializationResult;
120
118
  let initialized: Omit<InitCommandResult, "server">;
121
119
  try {
120
+ if (options.reset) await assertResetProfileServerStopped(configPath);
121
+ paths = initializeAgentBot({ configPath, reset: options.reset });
122
+ if (!options.json) printInitializationPaths(paths);
123
+ initializationLock ??= acquireInitializationLock(paths.home.path);
122
124
  cleanupFeishuCredentialTemporaryFiles(paths.env.path);
123
125
  const feishu = await initializeFeishu(
124
126
  paths,
125
- { json, skipFeishu, reconfigureFeishu },
127
+ options,
126
128
  );
127
129
  initialized = { ...paths, feishu };
128
130
  } finally {
129
- initializationLock.release();
131
+ initializationLock?.release();
130
132
  }
131
133
 
132
- if (!json) printInitializationResult(initialized);
133
- if (!json && !skipFeishu) process.stdout.write("\nStarting Agent Bot server...\n");
134
- const server = await startInitializedServer({ skipFeishu, configPath });
134
+ if (!options.json) printInitializationResult(initialized);
135
+ if (!options.json && !options.skipFeishu) process.stdout.write("\nStarting Agent Bot server...\n");
136
+ const server = await startInitializedServer({ skipFeishu: options.skipFeishu, configPath });
135
137
  const result: InitCommandResult = { ...initialized, server };
136
- if (json) printJson(result);
138
+ if (options.json) printJson(result);
137
139
  else printInitializationServerResult(server);
138
140
  }
139
141
 
140
142
  async function initializeFeishu(
141
143
  paths: InitializationResult,
142
- options: { json: boolean; skipFeishu: boolean; reconfigureFeishu: boolean },
144
+ options: InitCommandOptions,
143
145
  ): Promise<InitCommandResult["feishu"]> {
144
146
  const existing = readFeishuCredentials(paths.env.path);
145
147
  if (options.skipFeishu) {
@@ -191,6 +193,27 @@ async function initializeFeishu(
191
193
  }
192
194
  }
193
195
 
196
+ async function assertResetProfileServerStopped(configPath?: string): Promise<void> {
197
+ const endpoints = new Set<string>([controlEndpoint(defaultSqlitePath())]);
198
+ const selectedConfigPath = configPath ?? process.env.AGENT_BOT_CONFIG;
199
+ if (selectedConfigPath && fs.existsSync(selectedConfigPath)) {
200
+ try {
201
+ endpoints.add(controlEndpoint(
202
+ loadConfigWithoutEnvironmentMutation(selectedConfigPath).storage.sqlitePath,
203
+ ));
204
+ } catch {
205
+ // Reset must remain available when the existing configuration is invalid.
206
+ }
207
+ }
208
+ for (const endpoint of endpoints) {
209
+ if (await isServerReachable(endpoint)) {
210
+ throw new Error(
211
+ "Cannot reset a running profile. Stop it with agent-bot --profile <directory> server stop, then try again.",
212
+ );
213
+ }
214
+ }
215
+ }
216
+
194
217
  function skillsCommand(input: string[]): void {
195
218
  const [action = "status", ...rest] = input;
196
219
  const sourcePath = fileURLToPath(new URL("../skills/agent-bot/", import.meta.url));
@@ -462,6 +485,9 @@ function printSkillStatus(status: SkillRegistrationStatus): void {
462
485
 
463
486
  function printInitializationPaths(result: InitializationResult): void {
464
487
  process.stdout.write("Preparing the Agent Bot user environment...\n");
488
+ if (result.reset) {
489
+ process.stdout.write(`Reset backup: ${result.reset.backupPath}\n`);
490
+ }
465
491
  process.stdout.write(`Home directory: ${result.home.path} (${initializationStatusLabel(result.home.status)})\n`);
466
492
  process.stdout.write(`Config file: ${result.config.path} (${initializationStatusLabel(result.config.status)})\n`);
467
493
  process.stdout.write(`Environment file: ${result.env.path} (${initializationStatusLabel(result.env.status)})\n`);
@@ -603,6 +629,7 @@ function optionalFeishuFeatureWarnings(missing: FeishuConfigurationChallenge["mi
603
629
 
604
630
  function initializationStatusLabel(status: InitializationStatus): string {
605
631
  if (status === "created") return "created";
632
+ if (status === "reset") return "reset from template";
606
633
  return "already exists; unchanged";
607
634
  }
608
635
 
@@ -114,6 +114,20 @@ export function loadConfig(
114
114
  };
115
115
  }
116
116
 
117
+ export function loadConfigWithoutEnvironmentMutation(configPath?: string): AppConfig {
118
+ const environment = new Map(Object.entries(process.env));
119
+ try {
120
+ return loadConfig(configPath);
121
+ } finally {
122
+ for (const name of Object.keys(process.env)) {
123
+ if (!environment.has(name)) delete process.env[name];
124
+ }
125
+ for (const [name, value] of environment) {
126
+ process.env[name] = value;
127
+ }
128
+ }
129
+ }
130
+
117
131
  function expandEnv(raw: string): string {
118
132
  return raw.replace(ENV_PATTERN, (_match, name: string) => process.env[name] ?? "");
119
133
  }
@@ -16,6 +16,7 @@ import { localCardImage } from "./LocalCardImage.js";
16
16
  export interface StartupStatusView {
17
17
  startedAt: Date;
18
18
  restartReason: string;
19
+ agentBotVersion: string;
19
20
  defaultAgentName: string;
20
21
  defaultAgentTitle: string;
21
22
  cwd: string;
@@ -340,6 +341,7 @@ export class CardRenderer {
340
341
  `**默认 Agent**:${view.defaultAgentTitle} (${inlineCode(view.defaultAgentName)})`,
341
342
  ];
342
343
  lines.push(
344
+ `**Agent Bot 版本**:${inlineCode(view.agentBotVersion)}`,
343
345
  `**服务状态 / 启动时间**:🟢 在线 / ${formatStartupTime(view.startedAt)}`,
344
346
  `**重启原因**:${inlineCode(view.restartReason)}`,
345
347
  "> 发送消息即可开始对话;发送 `/new` 创建新任务;发送 `/help` 查看帮助。",
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ import { CodexProcessManager } from "./codex/CodexProcessManager.js";
7
7
  import { CodexRuntime } from "./codex/CodexRuntime.js";
8
8
  import { LocalControlServer } from "./cli/LocalControlServer.js";
9
9
  import { controlEndpoint, type ControlRequest, type ControlResponse } from "./cli/controlProtocol.js";
10
+ import { readPackageVersion } from "./cli/packageVersion.js";
10
11
  import { loadConfig } from "./config/loadConfig.js";
11
12
  import { ConsoleConnector } from "./console/ConsoleConnector.js";
12
13
  import { ConsoleTurnPresenter } from "./console/ConsoleTurnPresenter.js";
@@ -36,6 +37,7 @@ import {
36
37
  } from "./supervision/SupervisorDiagnostics.js";
37
38
 
38
39
  const processStartedAt = new Date();
40
+ const agentBotVersion = readPackageVersion();
39
41
  const supervised = process.env.AGENT_BOT_SUPERVISED === "1";
40
42
  const startupReason = process.env.AGENT_BOT_RESTART_REASON?.trim()
41
43
  || (supervised ? "Supervisor 启动" : "直接启动");
@@ -79,6 +81,7 @@ if (feishuOutbound) {
79
81
  const defaultAgent = config.agents[defaultAgentName];
80
82
  const metadataHydrator = new SessionMetadataHydrator(store, runtimes);
81
83
  startupNotifier = new StartupNotifier(store, feishuOutbound, renderer, logger, {
84
+ agentBotVersion,
82
85
  defaultAgentName,
83
86
  defaultAgentTitle: defaultAgent?.title ?? defaultAgentName,
84
87
  cwd: path.resolve(config.defaults.cwd),
@@ -5,6 +5,7 @@ import type { FeishuOutbound } from "../feishu/types.js";
5
5
  import type { SessionRecord, StateStore, UserContextRecord } from "../state/StateStore.js";
6
6
 
7
7
  export interface StartupNotificationOptions {
8
+ agentBotVersion: string;
8
9
  defaultAgentName: string;
9
10
  defaultAgentTitle: string;
10
11
  cwd: string;
@@ -74,6 +75,7 @@ export class StartupNotifier {
74
75
  const card = this.renderer.renderStartupStatus({
75
76
  startedAt,
76
77
  restartReason,
78
+ agentBotVersion: this.options.agentBotVersion,
77
79
  defaultAgentName: this.options.defaultAgentName,
78
80
  defaultAgentTitle: this.options.defaultAgentTitle,
79
81
  cwd,