@bbyx0011/ghostcode 0.1.0

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.
Files changed (42) hide show
  1. package/bin/ghostcode-mcp +11 -0
  2. package/bin/ghostcode-mcp-darwin-arm64 +0 -0
  3. package/bin/ghostcode-mcp-darwin-x64 +0 -0
  4. package/bin/ghostcode-mcp-linux-x64 +0 -0
  5. package/bin/ghostcode-wrapper +11 -0
  6. package/bin/ghostcode-wrapper-darwin-arm64 +0 -0
  7. package/bin/ghostcode-wrapper-darwin-x64 +0 -0
  8. package/bin/ghostcode-wrapper-linux-x64 +0 -0
  9. package/bin/ghostcoded-darwin-arm64 +0 -0
  10. package/bin/ghostcoded-darwin-x64 +0 -0
  11. package/bin/ghostcoded-linux-x64 +0 -0
  12. package/dist/cli.d.ts +21 -0
  13. package/dist/cli.js +97 -0
  14. package/dist/daemon.d.ts +72 -0
  15. package/dist/index.d.ts +31 -0
  16. package/dist/index.js +35 -0
  17. package/dist/install.d.ts +61 -0
  18. package/dist/ipc.d.ts +121 -0
  19. package/dist/postinstall.d.ts +42 -0
  20. package/dist/session-lease.d.ts +110 -0
  21. package/dist/web.d.ts +51 -0
  22. package/hooks/hooks.json +101 -0
  23. package/package.json +29 -0
  24. package/prompts/codex-analyzer.md +64 -0
  25. package/prompts/codex-reviewer.md +73 -0
  26. package/prompts/gemini-analyzer.md +82 -0
  27. package/prompts/gemini-reviewer.md +91 -0
  28. package/scripts/hook-pre-compact.mjs +128 -0
  29. package/scripts/hook-pre-tool-use.mjs +225 -0
  30. package/scripts/hook-session-end.mjs +108 -0
  31. package/scripts/hook-session-start.mjs +243 -0
  32. package/scripts/hook-stop.mjs +143 -0
  33. package/scripts/hook-subagent-start.mjs +231 -0
  34. package/scripts/hook-subagent-stop.mjs +168 -0
  35. package/scripts/hook-user-prompt-submit.mjs +134 -0
  36. package/scripts/lib/daemon-client.mjs +165 -0
  37. package/scripts/lib/stdin.mjs +88 -0
  38. package/scripts/run.mjs +98 -0
  39. package/skills/execute/SKILL.md +481 -0
  40. package/skills/plan/SKILL.md +318 -0
  41. package/skills/research/SKILL.md +267 -0
  42. package/skills/review/SKILL.md +238 -0
@@ -0,0 +1,11 @@
1
+ #!/bin/sh
2
+ # ghostcode-mcp 平台检测启动器
3
+ # 自动选择当前平台对应的 MCP Server 二进制并执行
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ PLATFORM="$(uname -s)-$(uname -m)"
6
+ case "$PLATFORM" in
7
+ Darwin-arm64) exec "$SCRIPT_DIR/ghostcode-mcp-darwin-arm64" "$@" ;;
8
+ Darwin-x86_64) exec "$SCRIPT_DIR/ghostcode-mcp-darwin-x64" "$@" ;;
9
+ Linux-x86_64) exec "$SCRIPT_DIR/ghostcode-mcp-linux-x64" "$@" ;;
10
+ *) echo "[GhostCode] 不支持的平台: $PLATFORM" >&2; exit 1 ;;
11
+ esac
Binary file
Binary file
Binary file
@@ -0,0 +1,11 @@
1
+ #!/bin/sh
2
+ # ghostcode-wrapper 平台检测启动器
3
+ # 自动选择当前平台对应的 Wrapper 二进制并执行
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ PLATFORM="$(uname -s)-$(uname -m)"
6
+ case "$PLATFORM" in
7
+ Darwin-arm64) exec "$SCRIPT_DIR/ghostcode-wrapper-darwin-arm64" "$@" ;;
8
+ Darwin-x86_64) exec "$SCRIPT_DIR/ghostcode-wrapper-darwin-x64" "$@" ;;
9
+ Linux-x86_64) exec "$SCRIPT_DIR/ghostcode-wrapper-linux-x64" "$@" ;;
10
+ *) echo "[GhostCode] 不支持的平台: $PLATFORM" >&2; exit 1 ;;
11
+ esac
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/cli.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @file cli.ts
3
+ * @description GhostCode CLI 入口
4
+ * CLI 命令分发:init / 其他子命令
5
+ * 通过 process.argv 手动解析参数,不引入额外 CLI 框架依赖
6
+ * @author Atlas.oi
7
+ * @date 2026-03-04
8
+ */
9
+ /**
10
+ * CLI 主入口函数
11
+ *
12
+ * 业务逻辑:
13
+ * 1. 解析 argv 获取子命令名称
14
+ * 2. 根据命令名分发到对应处理函数
15
+ * 3. 未知命令时输出帮助信息并以非零退出码退出
16
+ *
17
+ * @param argv 命令行参数(默认为 process.argv)
18
+ */
19
+ declare function main(argv?: string[]): Promise<void>;
20
+
21
+ export { main };
package/dist/cli.js ADDED
@@ -0,0 +1,97 @@
1
+ import { runInitCommand } from "./cli/init.js";
2
+ import { runDoctor, formatDoctorReport } from "./cli/doctor.js";
3
+ const VERSION = "0.1.0";
4
+ function printHelp() {
5
+ console.log(`ghostcode v${VERSION}
6
+
7
+ \u7528\u6CD5\uFF1A
8
+ ghostcode <\u547D\u4EE4> [\u9009\u9879]
9
+
10
+ \u547D\u4EE4\uFF1A
11
+ init \u521D\u59CB\u5316 GhostCode \u8FD0\u884C\u73AF\u5883\uFF08\u76EE\u5F55\u3001\u4E8C\u8FDB\u5236\u3001MCP \u914D\u7F6E\uFF09
12
+ doctor \u8BCA\u65AD GhostCode \u8FD0\u884C\u73AF\u5883\u5065\u5EB7\u72B6\u6001
13
+ help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
14
+ version \u663E\u793A\u7248\u672C\u4FE1\u606F
15
+
16
+ \u793A\u4F8B\uFF1A
17
+ ghostcode init # \u521D\u59CB\u5316\u73AF\u5883
18
+ ghostcode init --dry-run # \u6A21\u62DF\u8FD0\u884C\uFF0C\u4E0D\u5B9E\u9645\u4FEE\u6539\u6587\u4EF6
19
+ ghostcode doctor # \u8FD0\u884C\u73AF\u5883\u8BCA\u65AD
20
+ `);
21
+ }
22
+ async function handleInit(args) {
23
+ const dryRun = args.includes("--dry-run");
24
+ let mcpJsonPath;
25
+ const mcpJsonIdx = args.indexOf("--mcp-json");
26
+ if (mcpJsonIdx >= 0) {
27
+ mcpJsonPath = args[mcpJsonIdx + 1];
28
+ }
29
+ console.log("[GhostCode] \u5F00\u59CB\u521D\u59CB\u5316...");
30
+ if (dryRun) {
31
+ console.log("[GhostCode] \u6A21\u62DF\u8FD0\u884C\u6A21\u5F0F\uFF08--dry-run\uFF09\uFF0C\u4E0D\u4F1A\u4FEE\u6539\u6587\u4EF6");
32
+ }
33
+ const result = await runInitCommand({
34
+ dryRun,
35
+ ...mcpJsonPath !== void 0 ? { mcpJsonPath } : {}
36
+ });
37
+ if (!result.success) {
38
+ console.error(`[GhostCode] \u521D\u59CB\u5316\u5931\u8D25\uFF1A${result.error ?? "\u672A\u77E5\u9519\u8BEF"}`);
39
+ process.exit(1);
40
+ }
41
+ if (result.dirsCreated) {
42
+ console.log("[GhostCode] \u76EE\u5F55\u7ED3\u6784\u5DF2\u521B\u5EFA\uFF1A~/.ghostcode/ \u548C ~/.ghostcode/bin/");
43
+ } else {
44
+ console.log("[GhostCode] \u76EE\u5F55\u7ED3\u6784\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA");
45
+ }
46
+ if (result.binInstalled) {
47
+ console.log("[GhostCode] \u4E8C\u8FDB\u5236\u6587\u4EF6\u5B89\u88C5\u5B8C\u6210");
48
+ } else {
49
+ console.log("[GhostCode] \u4E8C\u8FDB\u5236\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5B89\u88C5");
50
+ }
51
+ if (result.mcpJsonWritten) {
52
+ console.log("[GhostCode] .mcp.json \u914D\u7F6E\u5DF2\u66F4\u65B0");
53
+ }
54
+ console.log("[GhostCode] \u521D\u59CB\u5316\u5B8C\u6210\uFF01");
55
+ }
56
+ async function handleDoctor() {
57
+ const report = await runDoctor();
58
+ console.log(formatDoctorReport(report));
59
+ if (report.overallStatus === "FAIL") {
60
+ process.exit(1);
61
+ }
62
+ }
63
+ async function main(argv = process.argv) {
64
+ const args = argv.slice(2);
65
+ const command = args[0];
66
+ if (!command || command === "help" || command === "--help" || command === "-h") {
67
+ printHelp();
68
+ return;
69
+ }
70
+ if (command === "version" || command === "--version" || command === "-v") {
71
+ console.log(`ghostcode v${VERSION}`);
72
+ return;
73
+ }
74
+ if (command === "init") {
75
+ await handleInit(args.slice(1));
76
+ return;
77
+ }
78
+ if (command === "doctor") {
79
+ await handleDoctor();
80
+ return;
81
+ }
82
+ console.error(`[GhostCode] \u672A\u77E5\u547D\u4EE4\uFF1A${command}`);
83
+ printHelp();
84
+ process.exit(1);
85
+ }
86
+ const isMainModule = process.argv[1] !== void 0 && (process.argv[1].endsWith("cli.js") || process.argv[1].endsWith("cli.ts"));
87
+ if (isMainModule) {
88
+ main().catch((err) => {
89
+ const errMsg = err instanceof Error ? err.message : String(err);
90
+ console.error(`[GhostCode] CLI \u53D1\u751F\u672A\u9884\u671F\u9519\u8BEF\uFF1A${errMsg}`);
91
+ process.exit(1);
92
+ });
93
+ }
94
+ export {
95
+ main
96
+ };
97
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @file Daemon 生命周期管理
3
+ * @description 管理 GhostCode Rust Daemon 进程的启动、停止、健康检查和心跳监控。
4
+ * 负责读取 ~/.ghostcode/daemon/ghostcoded.addr.json 获取 Daemon 连接信息,
5
+ * 在 Daemon 未运行时自动 spawn 新进程,并通过心跳维持连接存活。
6
+ *
7
+ * 核心流程:
8
+ * 1. 读取 addr.json -> 检测 Daemon 是否在运行
9
+ * 2. 不存在或 ping 失败 -> spawn ghostcoded 二进制
10
+ * 3. 等待 addr.json 出现(最多 5s)-> ping 确认
11
+ * 4. 心跳每 10s 一次,连续失败 3 次触发重启
12
+ *
13
+ * 参考: crates/ghostcode-types/src/addr.rs - AddrDescriptor 数据结构
14
+ * 参考: crates/ghostcode-types/src/ipc.rs - IPC 协议格式
15
+ * 参考: crates/ghostcode-daemon/src/paths.rs - addr.json 文件路径
16
+ * 参考: oh-my-claudecode/src/features/rate-limit-wait/daemon.ts - spawn 模式
17
+ * @author Atlas.oi
18
+ * @date 2026-03-01
19
+ */
20
+ /**
21
+ * 端点描述符
22
+ *
23
+ * Daemon 启动后写入 ghostcoded.addr.json 的连接信息
24
+ * 参考: crates/ghostcode-types/src/addr.rs:25-53
25
+ */
26
+ interface AddrDescriptor {
27
+ /** 协议版本号,固定为 1 */
28
+ v: number;
29
+ /** 传输协议,固定为 "unix" */
30
+ transport: string;
31
+ /** Unix socket 文件路径 */
32
+ path: string;
33
+ /** Daemon 进程 ID */
34
+ pid: number;
35
+ /** GhostCode 版本号 */
36
+ version: string;
37
+ /** 启动时间 ISO 8601 UTC */
38
+ ts: string;
39
+ }
40
+ /**
41
+ * 确保 Daemon 在运行,返回连接地址描述符
42
+ *
43
+ * 并发安全:多个调用者同时调用时,只会 spawn 一次 Daemon。
44
+ * 成功缓存:Daemon 启动成功后缓存 addr,后续调用直接复用(检查进程存活)。
45
+ *
46
+ * @returns Daemon 连接地址描述符
47
+ * @throws 如果 Daemon 启动失败或超时
48
+ */
49
+ declare function ensureDaemon(): Promise<AddrDescriptor>;
50
+ /**
51
+ * 停止 GhostCode Daemon
52
+ *
53
+ * 通过 IPC 发送 shutdown 操作。如果 Daemon 未运行,静默返回(幂等)。
54
+ */
55
+ declare function stopDaemon(): Promise<void>;
56
+ /**
57
+ * 启动心跳监控
58
+ *
59
+ * 每 10s 向 Daemon 发送 ping,连续失败 3 次触发重启。
60
+ *
61
+ * @param addr - 当前 Daemon 连接地址
62
+ * @returns 停止心跳的函数
63
+ */
64
+ declare function startHeartbeat(addr: AddrDescriptor): () => void;
65
+ /**
66
+ * 获取 Daemon 二进制文件路径
67
+ *
68
+ * @returns Daemon 二进制文件的绝对路径
69
+ */
70
+ declare function getDaemonBinaryPath(): string;
71
+
72
+ export { type AddrDescriptor, ensureDaemon, getDaemonBinaryPath, startHeartbeat, stopDaemon };
@@ -0,0 +1,31 @@
1
+ export { AddrDescriptor, ensureDaemon, getDaemonBinaryPath, startHeartbeat, stopDaemon } from './daemon.js';
2
+ export { DaemonError, DaemonRequest, DaemonResponse, IpcConnectionError, IpcProtocolError, IpcTimeoutError, callDaemon, createConnection, resetClient } from './ipc.js';
3
+ export { HookEventType, HookHandler, clearHooks, getHooks, registerHook } from './hooks/registry.js';
4
+ export { initializeHooks, preToolUseHandler, stopHandler } from './hooks/handlers.js';
5
+ import 'node:net';
6
+ import './router/streaming.js';
7
+
8
+ /**
9
+ * @file GhostCode Plugin 主入口
10
+ * @description GhostCode Claude Code Plugin 的公开 API 导出入口。
11
+ * 作为 TypeScript 薄壳,本文件聚合三个核心模块的导出:
12
+ * - daemon: Daemon 进程管理(T17 实现)
13
+ * - ipc: IPC 通信层(T18 实现)
14
+ * - hooks: Claude Code Hook 注册(后续任务实现)
15
+ *
16
+ * Plugin 架构设计:
17
+ * Claude Code 宿主 → Plugin (index.ts) → IPC → Rust Daemon
18
+ *
19
+ * 启动顺序:
20
+ * 1. installGhostcode():平台检测 + 二进制部署到 ~/.ghostcode/bin/ghostcoded
21
+ * 2. MCP server 初始化(由宿主调用)
22
+ * @author Atlas.oi
23
+ * @date 2026-03-01
24
+ */
25
+
26
+ /** GhostCode Plugin 版本号 */
27
+ declare const VERSION = "0.1.0";
28
+ /** GhostCode Plugin 名称 */
29
+ declare const PLUGIN_NAME = "ghostcode";
30
+
31
+ export { PLUGIN_NAME, VERSION };
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ import { installGhostcode } from "./install.js";
2
+ await installGhostcode();
3
+ import { ensureDaemon, stopDaemon, startHeartbeat, getDaemonBinaryPath } from "./daemon.js";
4
+ import {
5
+ callDaemon,
6
+ createConnection,
7
+ resetClient,
8
+ IpcTimeoutError,
9
+ IpcConnectionError,
10
+ IpcProtocolError
11
+ } from "./ipc.js";
12
+ import { registerHook, getHooks, clearHooks, initializeHooks, preToolUseHandler, stopHandler } from "./hooks/index.js";
13
+ const VERSION = "0.1.0";
14
+ const PLUGIN_NAME = "ghostcode";
15
+ export {
16
+ IpcConnectionError,
17
+ IpcProtocolError,
18
+ IpcTimeoutError,
19
+ PLUGIN_NAME,
20
+ VERSION,
21
+ callDaemon,
22
+ clearHooks,
23
+ createConnection,
24
+ ensureDaemon,
25
+ getDaemonBinaryPath,
26
+ getHooks,
27
+ initializeHooks,
28
+ preToolUseHandler,
29
+ registerHook,
30
+ resetClient,
31
+ startHeartbeat,
32
+ stopDaemon,
33
+ stopHandler
34
+ };
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @file install.ts
3
+ * @description GhostCode Plugin 首次运行安装逻辑
4
+ * 支持两种安装模式:
5
+ * 1. 本地复制模式:从 Plugin 包内的 bin/ 目录复制预编译二进制(离线/快速路径)
6
+ * 2. Release 下载模式:从 GitHub Release 下载平台 bundle,验证 checksum 后解压安装
7
+ * 通过 ~/.ghostcode/.installed 标记文件避免重复安装
8
+ * @author Atlas.oi
9
+ * @date 2026-03-04
10
+ */
11
+ /**
12
+ * 根据版本和平台信息生成 GitHub Release 资产的下载 URL
13
+ *
14
+ * URL 格式:
15
+ * https://github.com/{repo}/releases/download/v{version}/ghostcode-{platform}.tar.gz
16
+ *
17
+ * @param version 版本号(不含 v 前缀,如 "0.2.0")
18
+ * @param platform Node.js process.platform 值("darwin" | "linux")
19
+ * @param arch Node.js process.arch 值("arm64" | "x64")
20
+ * @returns 完整的 tar.gz 下载 URL
21
+ * @throws Error 不支持的平台组合
22
+ */
23
+ declare function buildReleaseAssetUrl(version: string, platform: string, arch: string): string;
24
+ /**
25
+ * 从 GitHub Release 下载并安装 GhostCode 二进制
26
+ *
27
+ * 业务逻辑:
28
+ * 1. 检查是否已安装且版本匹配(快速路径,跳过下载)
29
+ * 2. 生成 bundle 下载 URL 和 SHA256SUMS URL
30
+ * 3. 下载 bundle tar.gz 到临时目录
31
+ * 4. 下载 SHA256SUMS 文件
32
+ * 5. 解析 SHA256SUMS,获取对应 bundle 的期望 checksum
33
+ * 6. 验证下载文件 checksum(不匹配则中止,不降级!)
34
+ * 7. 解压 bundle 到目标目录
35
+ * 8. 验证 ghostcoded 和 ghostcode-mcp 均已安装
36
+ * 9. 写入安装标记文件
37
+ *
38
+ * @param version 要安装的版本号(不含 v 前缀,如 "0.2.0")
39
+ * @param platform Node.js process.platform 值("darwin" | "linux")
40
+ * @param arch Node.js process.arch 值("arm64" | "x64")
41
+ * @param targetDir 安装目标目录(默认为 ~/.ghostcode/bin)
42
+ * @throws Error checksum 不匹配、下载失败或解压失败时(禁止静默降级)
43
+ */
44
+ declare function installFromRelease(version: string, platform: string, arch: string, targetDir?: string): Promise<void>;
45
+ /**
46
+ * 执行 GhostCode Plugin 安装(本地复制模式)
47
+ *
48
+ * 业务逻辑:
49
+ * 1. 读取当前 Plugin 版本
50
+ * 2. 检查 bin/ 目录是否已安装且版本匹配(快速路径,跳过安装)
51
+ * 3. 检测当前平台
52
+ * 4. 定位 Plugin 包内对应平台的 ghostcoded 和 ghostcode-mcp 二进制文件
53
+ * 5. 创建目标目录(~/.ghostcode/bin/)
54
+ * 6. 复制双二进制并设置可执行权限
55
+ * 7. 写入安装标记文件到 bin/ 目录(与 installFromRelease 统一路径)
56
+ *
57
+ * @throws Error 当平台不受支持或二进制文件不存在时
58
+ */
59
+ declare function installGhostcode(): Promise<void>;
60
+
61
+ export { buildReleaseAssetUrl, installFromRelease, installGhostcode };
package/dist/ipc.d.ts ADDED
@@ -0,0 +1,121 @@
1
+ import * as net from 'node:net';
2
+ import { StreamEvent, StreamCallbacks } from './router/streaming.js';
3
+
4
+ /**
5
+ * @file IPC 桥接层
6
+ * @description GhostCode Plugin 与 Rust Daemon 之间的 Unix socket IPC 通信层。
7
+ * 协议:换行符分隔的 JSON(newline-delimited JSON),每条消息为一行 JSON + \n。
8
+ * 连接策略:维护 1 个持久连接,断开后懒重连(下次请求时自动重连)。
9
+ * 超时:单次请求 10s,重连最多 3 次,指数退避(100ms / 200ms / 400ms)。
10
+ *
11
+ * 类型设计与 Rust 侧 DaemonRequest/DaemonResponse 完全对齐:
12
+ * 参考: crates/ghostcode-types/src/ipc.rs
13
+ * 参考: schemas/daemon-request.json, schemas/daemon-response.json
14
+ * @author Atlas.oi
15
+ * @date 2026-03-02
16
+ */
17
+
18
+ /** Daemon 请求结构 - 与 DaemonRequest Rust struct 字段一一对应 */
19
+ interface DaemonRequest {
20
+ /** 协议版本,固定为整数 1(与 Rust v: u8 = 1 对应) */
21
+ v: 1;
22
+ /** 操作名称,如 "ping"、"actor.add" */
23
+ op: string;
24
+ /** 操作参数,任意 JSON 对象(空参数时传 {},不传 null) */
25
+ args: Record<string, unknown>;
26
+ }
27
+ /** Daemon 错误结构 - 与 DaemonError Rust struct 字段一一对应 */
28
+ interface DaemonError {
29
+ /** 错误码,如 "NOT_FOUND"、"INVALID_ARGS" */
30
+ code: string;
31
+ /** 可读的错误描述 */
32
+ message: string;
33
+ }
34
+ /** Daemon 响应结构 - 与 DaemonResponse Rust struct 字段一一对应 */
35
+ interface DaemonResponse {
36
+ /** 协议版本,固定为整数 1 */
37
+ v: 1;
38
+ /** 操作是否成功 */
39
+ ok: boolean;
40
+ /** 成功时的返回值(对应 Rust serde_json::Value,可为任意 JSON) */
41
+ result: unknown;
42
+ /** 失败时的错误信息 */
43
+ error?: DaemonError;
44
+ }
45
+ /** IPC 请求超时(单次请求超过 10s 未收到响应) */
46
+ declare class IpcTimeoutError extends Error {
47
+ constructor(op: string, timeoutMs: number);
48
+ }
49
+ /** IPC 连接失败(无法建立或恢复 Unix socket 连接) */
50
+ declare class IpcConnectionError extends Error {
51
+ constructor(socketPath: string, cause?: Error);
52
+ }
53
+ /** IPC 协议错误(收到无效 JSON 或响应结构不符合 Schema) */
54
+ declare class IpcProtocolError extends Error {
55
+ constructor(detail: string);
56
+ }
57
+ /**
58
+ * 创建到 Daemon Unix socket 的原始连接
59
+ *
60
+ * 参考: cccc/src/cccc/daemon/client_ops.py:40-55 - AF_UNIX connect 逻辑
61
+ *
62
+ * @param socketPath - Unix socket 文件路径
63
+ * @returns 已建立连接的 net.Socket 实例
64
+ * @throws {IpcConnectionError} 无法连接时抛出
65
+ */
66
+ declare function createConnection(socketPath: string): Promise<net.Socket>;
67
+ /**
68
+ * 流式请求的聚合响应
69
+ * 收集流结束时所有已到达的事件和锁定的 sessionId
70
+ */
71
+ interface StreamResponse {
72
+ /** 流过程中收到的所有事件,按到达顺序排列 */
73
+ events: StreamEvent[];
74
+ /** 从首个携带 session_id 的事件中锁定的会话 ID,若流中无 session_id 则为 null */
75
+ sessionId: string | null;
76
+ }
77
+ /**
78
+ * 解析 Socket 路径(三级回退策略)
79
+ *
80
+ * 优先级规则:
81
+ * 1. 显式参数 - 调用方直接传入的路径,优先级最高
82
+ * 2. 环境变量 GHOSTCODE_SOCKET_PATH - Daemon 启动后由 preToolUseHandler 注入
83
+ * 3. addr.json 文件 - 从 ~/.ghostcode/daemon/ghostcoded.addr.json 读取 path 字段
84
+ * 4. 全部失败 - 返回 null,由调用方决定如何处理
85
+ *
86
+ * @param explicit - 显式指定的 socket 路径(可选)
87
+ * @returns 解析到的 socket 路径,若三级均无法找到则返回 null
88
+ */
89
+ declare function resolveSocketPath(explicit?: string): string | null;
90
+ /**
91
+ * 向 Daemon 发起一次 RPC 调用(公共 API)
92
+ *
93
+ * 参考: cccc/src/cccc/daemon/server.py:968-988 - call_daemon() 函数设计
94
+ *
95
+ * @param op - 操作名称
96
+ * @param args - 操作参数(默认为 {})
97
+ * @param socketPath - Unix socket 路径(可选,未传入时通过 resolveSocketPath 自动发现)
98
+ * @returns Daemon 响应
99
+ * @throws {IpcConnectionError} 无法解析 Socket 路径时抛出
100
+ */
101
+ declare function callDaemon(op: string, args?: Record<string, unknown>, socketPath?: string): Promise<DaemonResponse>;
102
+ /**
103
+ * 重置模块级单例(主要用于测试场景)
104
+ */
105
+ declare function resetClient(): Promise<void>;
106
+ /**
107
+ * 向 Daemon 发起流式 RPC 调用(公共 API)
108
+ *
109
+ * 在 callDaemon() 的基础上增加多行 JSON 流式支持。
110
+ * 已有的 callDaemon() 接口不受影响,完全向后兼容。
111
+ *
112
+ * @param op - 操作名称,如 "task.route"
113
+ * @param args - 操作参数(默认为 {})
114
+ * @param callbacks - 流式事件回调集合
115
+ * @param socketPath - Unix socket 路径(可选,未传入时通过 resolveSocketPath 自动发现)
116
+ * @returns 流结束后的 StreamResponse(含所有事件和 sessionId)
117
+ * @throws {IpcConnectionError} 无法解析 Socket 路径时抛出
118
+ */
119
+ declare function callDaemonStream(op: string, args: Record<string, unknown> | undefined, callbacks: StreamCallbacks, socketPath?: string): Promise<StreamResponse>;
120
+
121
+ export { type DaemonError, type DaemonRequest, type DaemonResponse, IpcConnectionError, IpcProtocolError, IpcTimeoutError, StreamCallbacks, StreamEvent, type StreamResponse, callDaemon, callDaemonStream, createConnection, resetClient, resolveSocketPath };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @file postinstall.ts
3
+ * @description npm/pnpm install 后自动触发的二进制安装脚本
4
+ * 支持 CI 环境检测、GitHub Release 网络下载
5
+ *
6
+ * 业务流程:
7
+ * 1. 检测 CI 环境 -> 跳过所有下载操作,仅输出提示
8
+ * 2. 尝试 installFromRelease 下载最新版本
9
+ * 3. 下载失败 -> 输出明确错误信息、失败原因和修复建议
10
+ * 4. 权限错误 -> 输出 chmod/sudo 相关提示
11
+ *
12
+ * @author Atlas.oi
13
+ * @date 2026-03-04
14
+ */
15
+ /**
16
+ * 检测是否为 CI 环境
17
+ *
18
+ * 业务逻辑:
19
+ * 检查常见 CI 平台的环境变量标识
20
+ * - CI: GitHub Actions, CircleCI, Travis CI 等通用标识
21
+ * - GITHUB_ACTIONS: GitHub Actions 专用
22
+ * - JENKINS_URL: Jenkins CI
23
+ * - GITLAB_CI: GitLab CI
24
+ *
25
+ * @returns true 表示当前运行在 CI 环境中
26
+ */
27
+ declare function isCIEnvironment(): boolean;
28
+ /**
29
+ * postinstall 主入口函数
30
+ *
31
+ * 在 npm/pnpm install 完成后自动执行。
32
+ * 输出保持简洁,不干扰包管理器的主控制台输出。
33
+ *
34
+ * 业务逻辑:
35
+ * 1. CI 环境检测 -> 跳过下载
36
+ * 2. installFromRelease 下载最新 bundle
37
+ * 3. 下载失败 -> 直接报错,输出失败原因和修复建议(禁止降级回退)
38
+ * 4. 权限错误 -> 输出 chmod/sudo 建议
39
+ */
40
+ declare function runPostinstall(): Promise<void>;
41
+
42
+ export { isCIEnvironment, runPostinstall };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @file Session Lease 引用计数管理器
3
+ * @description 管理多会话共享单 Daemon 时的引用计数。
4
+ * 每个会话 acquire 一个 lease,只有最后一个会话 release 时才真正关闭 Daemon。
5
+ * Lease 信息存储在文件中,支持跨进程安全读写(mkdir 互斥锁 + atomic write)。
6
+ *
7
+ * 文件格式(sessions.json):
8
+ * {
9
+ * "sessions": [
10
+ * { "leaseId": "uuid-1", "pid": 12345, "acquiredAt": "ISO-timestamp" },
11
+ * { "leaseId": "uuid-2", "pid": 12346, "acquiredAt": "ISO-timestamp" }
12
+ * ]
13
+ * }
14
+ *
15
+ * @author Atlas.oi
16
+ * @date 2026-03-04
17
+ */
18
+ /**
19
+ * Lease 获取结果
20
+ */
21
+ interface LeaseInfo {
22
+ /** 唯一的 Lease 标识符(UUID v4 格式) */
23
+ leaseId: string;
24
+ /** 当前总引用计数(包含本次 acquire) */
25
+ refcount: number;
26
+ }
27
+ /**
28
+ * Lease 释放结果
29
+ */
30
+ interface ReleaseResult {
31
+ /** 释放后的剩余引用计数 */
32
+ refcount: number;
33
+ /** 是否为最后一个 lease(true 表示可以关闭 Daemon) */
34
+ isLast: boolean;
35
+ }
36
+ /**
37
+ * Session Lease 引用计数管理器
38
+ *
39
+ * 使用文件存储实现跨进程安全的引用计数:
40
+ * 1. 每次 acquire/release 操作都包裹在 withFileLock 跨进程互斥锁中
41
+ * 2. 锁内执行 read-modify-write:读文件 → 修改内存 → atomic write 回文件
42
+ * 3. atomic write = 写临时文件 + rename,确保其他进程看到的始终是完整 JSON
43
+ * 4. 如果文件不存在,视为 0 个 session
44
+ */
45
+ declare class SessionLeaseManager {
46
+ /** sessions.json 文件的绝对路径 */
47
+ private readonly filePath;
48
+ /** 互斥锁目录路径(与 sessions.json 同目录下的 .sessions.lock/) */
49
+ private readonly lockPath;
50
+ /**
51
+ * 创建 SessionLeaseManager 实例
52
+ *
53
+ * @param sessionsFilePath - sessions.json 文件路径(便于测试时使用临时路径)
54
+ */
55
+ constructor(sessionsFilePath: string);
56
+ /**
57
+ * 获取一个 session lease(引用计数 +1)
58
+ *
59
+ * 业务逻辑:
60
+ * 1. 读取当前 sessions.json 文件(不存在则视为空列表)
61
+ * 2. 生成新 UUID 作为 leaseId
62
+ * 3. 将新 session 追加到列表
63
+ * 4. Atomic write 回文件
64
+ * 5. 返回新 leaseId 和最新 refcount
65
+ *
66
+ * @returns LeaseInfo - 包含 leaseId 和当前 refcount
67
+ */
68
+ acquireLease(): LeaseInfo;
69
+ /**
70
+ * 释放一个 session lease(引用计数 -1)
71
+ *
72
+ * 业务逻辑:
73
+ * 1. 读取当前 sessions.json 文件
74
+ * 2. 从列表中移除对应 leaseId 的条目(找不到则安全跳过)
75
+ * 3. Atomic write 回文件
76
+ * 4. 返回剩余 refcount 和 isLast 标志
77
+ *
78
+ * @param leaseId - 要释放的 lease 标识符
79
+ * @returns ReleaseResult - 包含剩余 refcount 和 isLast 标志
80
+ */
81
+ releaseLease(leaseId: string): ReleaseResult;
82
+ /**
83
+ * 获取当前引用计数
84
+ *
85
+ * 直接从文件读取,反映最新的跨进程状态。
86
+ *
87
+ * @returns 当前 session 数量(引用计数)
88
+ */
89
+ getRefcount(): number;
90
+ /**
91
+ * 读取 sessions.json 文件
92
+ *
93
+ * 文件不存在或解析失败时返回空的 sessions 列表,而不是抛出异常。
94
+ *
95
+ * @returns SessionsFile - 解析后的文件内容
96
+ */
97
+ private readFile;
98
+ /**
99
+ * Atomic write sessions.json 文件
100
+ *
101
+ * 使用"写临时文件 + rename"策略确保跨进程安全:
102
+ * - 直接写目标文件可能导致其他进程读到中间状态(半写入的 JSON)
103
+ * - rename 是原子操作,确保其他进程看到的始终是完整的 JSON
104
+ *
105
+ * @param data - 要写入的文件内容
106
+ */
107
+ private writeFile;
108
+ }
109
+
110
+ export { type LeaseInfo, type ReleaseResult, SessionLeaseManager };