@actiondock/core 2.10.0 → 2.11.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 (252) hide show
  1. package/README.md +1 -2
  2. package/dist/catalog/action-catalog.js +3 -33
  3. package/dist/catalog/discovery.d.ts +0 -3
  4. package/dist/catalog/discovery.js +4 -4
  5. package/dist/catalog/graph.d.ts +0 -4
  6. package/dist/catalog/graph.js +8 -14
  7. package/dist/{profile/client-actions.d.ts → client/actions.d.ts} +1 -1
  8. package/dist/{profile/client-actions.js → client/actions.js} +2 -8
  9. package/dist/{profile/client-config.d.ts → client/config.d.ts} +1 -1
  10. package/dist/{profile/client-config.js → client/config.js} +1 -1
  11. package/dist/client/dispatcher.d.ts +4 -0
  12. package/dist/client/dispatcher.js +21 -0
  13. package/dist/client/health.d.ts +38 -0
  14. package/dist/{profile/client-health.js → client/health.js} +9 -6
  15. package/dist/client/index.d.ts +16 -0
  16. package/dist/client/index.js +20 -0
  17. package/dist/{profile/client-playbooks.d.ts → client/playbooks.d.ts} +1 -1
  18. package/dist/{profile/client-playbooks.js → client/playbooks.js} +1 -1
  19. package/dist/{profile/client-runs.d.ts → client/runs.d.ts} +4 -2
  20. package/dist/{profile/client-runs.js → client/runs.js} +7 -1
  21. package/dist/{profile/client-state.d.ts → client/state.d.ts} +1 -1
  22. package/dist/{profile/client-state.js → client/state.js} +1 -1
  23. package/dist/{profile/client-transport.d.ts → client/transport.d.ts} +29 -10
  24. package/dist/{profile/client-transport.js → client/transport.js} +125 -35
  25. package/dist/doctor/checks/index.d.ts +1 -1
  26. package/dist/doctor/checks/index.js +1 -1
  27. package/dist/doctor/checks/project.js +20 -88
  28. package/dist/doctor/checks/runtime.d.ts +0 -4
  29. package/dist/doctor/checks/runtime.js +0 -28
  30. package/dist/doctor/doctor.js +1 -2
  31. package/dist/errors.d.ts +53 -3
  32. package/dist/errors.js +53 -3
  33. package/dist/execution/service.d.ts +55 -37
  34. package/dist/execution/service.js +601 -212
  35. package/dist/execution/types.d.ts +24 -5
  36. package/dist/host/host.d.ts +42 -22
  37. package/dist/host/host.js +562 -149
  38. package/dist/host/routing.js +6 -6
  39. package/dist/host/types.d.ts +25 -13
  40. package/dist/index.d.ts +9 -4
  41. package/dist/index.js +5 -2
  42. package/dist/input/advice.d.ts +0 -44
  43. package/dist/input/advice.js +7 -141
  44. package/dist/input/flat-materializer.js +1 -1
  45. package/dist/input/flat-parser.js +1 -1
  46. package/dist/input/index.d.ts +0 -1
  47. package/dist/input/index.js +0 -1
  48. package/dist/input/input-resolver.js +1 -1
  49. package/dist/input/validation-mapper.d.ts +1 -1
  50. package/dist/ipc/diagnostic.d.ts +8 -21
  51. package/dist/ipc/diagnostic.js +24 -57
  52. package/dist/ipc/host.js +21 -7
  53. package/dist/ipc/service.d.ts +14 -0
  54. package/dist/ipc/service.js +132 -26
  55. package/dist/ipc/types.d.ts +0 -4
  56. package/dist/package/index.d.ts +4 -5
  57. package/dist/package/index.js +1 -2
  58. package/dist/package/runtime.d.ts +94 -23
  59. package/dist/package/runtime.js +628 -274
  60. package/dist/package/static-index.d.ts +3 -6
  61. package/dist/package/static-index.js +11 -40
  62. package/dist/package/types.d.ts +19 -4
  63. package/dist/platform/index.d.ts +2 -1
  64. package/dist/platform/index.js +2 -1
  65. package/dist/platform/loader-hook.d.ts +32 -0
  66. package/dist/platform/loader-hook.js +90 -0
  67. package/dist/{node → platform}/module-loader.d.ts +13 -0
  68. package/dist/{node → platform}/module-loader.js +51 -4
  69. package/dist/platform/node.d.ts +19 -8
  70. package/dist/platform/node.js +50 -20
  71. package/dist/platform/types.d.ts +7 -47
  72. package/dist/process/context-process.d.ts +3 -18
  73. package/dist/process/context-process.js +3 -44
  74. package/dist/process/control.d.ts +21 -0
  75. package/dist/process/control.js +34 -0
  76. package/dist/process/converters.d.ts +23 -0
  77. package/dist/process/converters.js +92 -0
  78. package/dist/process/cursor.js +2 -22
  79. package/dist/process/{diagnostics-sink.d.ts → diagnostics.d.ts} +0 -7
  80. package/dist/process/{diagnostics-sink.js → diagnostics.js} +6 -8
  81. package/dist/process/index.d.ts +1 -3
  82. package/dist/process/index.js +1 -3
  83. package/dist/process/input-dispatcher.d.ts +4 -13
  84. package/dist/process/input-dispatcher.js +48 -69
  85. package/dist/process/managed-record.d.ts +3 -31
  86. package/dist/process/metadata-store.d.ts +0 -41
  87. package/dist/process/metadata-store.js +2 -341
  88. package/dist/process/process-driver.js +2 -2
  89. package/dist/process/process-manager.d.ts +8 -50
  90. package/dist/process/process-manager.js +28 -417
  91. package/dist/process/quotas.d.ts +14 -0
  92. package/dist/process/quotas.js +29 -0
  93. package/dist/process/run-executor.js +0 -1
  94. package/dist/process/terminal-output-cache.d.ts +20 -34
  95. package/dist/process/terminal-output-cache.js +43 -64
  96. package/dist/profile/index.d.ts +1 -2
  97. package/dist/profile/index.js +1 -2
  98. package/dist/profile/manager.d.ts +2 -4
  99. package/dist/profile/manager.js +10 -40
  100. package/dist/profile/types.d.ts +1 -17
  101. package/dist/project/digest.d.ts +5 -1
  102. package/dist/project/digest.js +81 -219
  103. package/dist/project/index.d.ts +4 -5
  104. package/dist/project/index.js +4 -5
  105. package/dist/project/init.js +2 -2
  106. package/dist/project/loader.d.ts +3 -3
  107. package/dist/project/loader.js +72 -50
  108. package/dist/project/lockfile.js +6 -5
  109. package/dist/project/manifest.js +5 -4
  110. package/dist/project/transactions.d.ts +4 -42
  111. package/dist/project/transactions.js +83 -138
  112. package/dist/project/types-generator.js +2 -1
  113. package/dist/project/types.d.ts +2 -0
  114. package/dist/registry/index.d.ts +0 -1
  115. package/dist/registry/index.js +0 -1
  116. package/dist/registry/lock.d.ts +2 -11
  117. package/dist/registry/lock.js +14 -183
  118. package/dist/registry/registry.d.ts +1 -6
  119. package/dist/registry/registry.js +5 -17
  120. package/dist/runtime/action-registry.d.ts +1 -5
  121. package/dist/runtime/action-registry.js +0 -12
  122. package/dist/runtime/context.js +2 -2
  123. package/dist/runtime/events.d.ts +11 -50
  124. package/dist/runtime/events.js +88 -368
  125. package/dist/runtime/index.d.ts +0 -4
  126. package/dist/runtime/index.js +0 -4
  127. package/dist/runtime/run-persistence.d.ts +2 -0
  128. package/dist/runtime/run-persistence.js +2 -1
  129. package/dist/runtime/standalone-commands.d.ts +18 -0
  130. package/dist/runtime/standalone-commands.js +466 -0
  131. package/dist/runtime/standalone-help.d.ts +20 -0
  132. package/dist/runtime/standalone-help.js +72 -0
  133. package/dist/runtime/standalone.d.ts +1 -44
  134. package/dist/runtime/standalone.js +32 -573
  135. package/dist/schema/validator.d.ts +2 -2
  136. package/dist/schema/validator.js +2 -9
  137. package/dist/server/index.d.ts +0 -2
  138. package/dist/server/index.js +0 -2
  139. package/dist/server/routes/actions.js +1 -1
  140. package/dist/server/routes/info.js +1 -1
  141. package/dist/server/routes/playbooks.js +1 -1
  142. package/dist/server/routes/runs.js +123 -16
  143. package/dist/server/routes/state.js +43 -18
  144. package/dist/server/security.d.ts +1 -1
  145. package/dist/server/security.js +2 -2
  146. package/dist/server/server.js +3 -7
  147. package/dist/service/factory.js +4 -3
  148. package/dist/service/index.d.ts +1 -1
  149. package/dist/service/index.js +1 -1
  150. package/dist/service/remote-errors.d.ts +1 -1
  151. package/dist/service/remote-errors.js +6 -6
  152. package/dist/service/remote-polling.d.ts +3 -0
  153. package/dist/service/remote-polling.js +10 -5
  154. package/dist/service/remote.d.ts +5 -1
  155. package/dist/service/remote.js +108 -17
  156. package/dist/service/sse-stream.js +2 -2
  157. package/dist/service/types.d.ts +22 -19
  158. package/dist/service/types.js +1 -24
  159. package/dist/storage/clock.d.ts +1 -1
  160. package/dist/storage/clock.js +1 -1
  161. package/dist/storage/data-dir-lock.d.ts +10 -22
  162. package/dist/storage/data-dir-lock.js +28 -56
  163. package/dist/storage/file-lock.d.ts +64 -0
  164. package/dist/storage/file-lock.js +447 -0
  165. package/dist/storage/index.d.ts +2 -2
  166. package/dist/storage/index.js +5 -4
  167. package/dist/storage/run-liveness.d.ts +48 -0
  168. package/dist/storage/run-liveness.js +44 -0
  169. package/dist/storage/sqlite-config.d.ts +16 -0
  170. package/dist/storage/sqlite-config.js +59 -0
  171. package/dist/storage/sqlite-driver.d.ts +3 -1
  172. package/dist/storage/sqlite-driver.js +14 -4
  173. package/dist/storage/sqlite-events.d.ts +13 -0
  174. package/dist/storage/sqlite-events.js +48 -0
  175. package/dist/storage/sqlite-runs.d.ts +107 -0
  176. package/dist/storage/sqlite-runs.js +622 -0
  177. package/dist/storage/sqlite-schema.d.ts +5 -0
  178. package/dist/storage/sqlite-schema.js +143 -0
  179. package/dist/storage/sqlite-state.d.ts +33 -0
  180. package/dist/storage/sqlite-state.js +270 -0
  181. package/dist/storage/sqlite.d.ts +30 -57
  182. package/dist/storage/sqlite.js +84 -741
  183. package/dist/storage/types.d.ts +35 -2
  184. package/dist/utils/index.d.ts +18 -14
  185. package/dist/utils/index.js +37 -29
  186. package/dist/utils/semver.d.ts +18 -0
  187. package/dist/utils/semver.js +19 -0
  188. package/dist/utils/warning.d.ts +1 -0
  189. package/dist/utils/warning.js +52 -0
  190. package/dist/{json/value-validator.d.ts → value-validator.d.ts} +9 -0
  191. package/dist/value-validator.js +330 -0
  192. package/dist/version.d.ts +1 -1
  193. package/dist/version.js +1 -1
  194. package/package.json +10 -12
  195. package/dist/execution/index.d.ts +0 -2
  196. package/dist/execution/index.js +0 -2
  197. package/dist/filter/index.d.ts +0 -1
  198. package/dist/filter/index.js +0 -1
  199. package/dist/graph/index.d.ts +0 -4
  200. package/dist/graph/index.js +0 -4
  201. package/dist/input/metadata.d.ts +0 -99
  202. package/dist/input/metadata.js +0 -99
  203. package/dist/invocation/index.d.ts +0 -2
  204. package/dist/invocation/index.js +0 -2
  205. package/dist/ipc/index.d.ts +0 -4
  206. package/dist/ipc/index.js +0 -4
  207. package/dist/json/index.d.ts +0 -1
  208. package/dist/json/index.js +0 -1
  209. package/dist/json/value-validator.js +0 -330
  210. package/dist/node/index.d.ts +0 -1
  211. package/dist/node/index.js +0 -1
  212. package/dist/platform/node-fs.d.ts +0 -37
  213. package/dist/platform/node-fs.js +0 -81
  214. package/dist/process/control-arbiter.d.ts +0 -108
  215. package/dist/process/control-arbiter.js +0 -250
  216. package/dist/process/process-executor.d.ts +0 -47
  217. package/dist/process/process-executor.js +0 -296
  218. package/dist/profile/client-health.d.ts +0 -14
  219. package/dist/profile/client-query.d.ts +0 -30
  220. package/dist/profile/client-query.js +0 -69
  221. package/dist/profile/client.d.ts +0 -26
  222. package/dist/profile/client.js +0 -31
  223. package/dist/project/closure.d.ts +0 -25
  224. package/dist/project/closure.js +0 -74
  225. package/dist/registry/store.d.ts +0 -42
  226. package/dist/registry/store.js +0 -43
  227. package/dist/runtime/clock.d.ts +0 -7
  228. package/dist/runtime/clock.js +0 -7
  229. package/dist/runtime/module-loader.d.ts +0 -2
  230. package/dist/runtime/module-loader.js +0 -1
  231. package/dist/runtime/process.d.ts +0 -8
  232. package/dist/runtime/process.js +0 -1
  233. package/dist/runtime/runner.d.ts +0 -234
  234. package/dist/runtime/runner.js +0 -609
  235. package/dist/schema/index.d.ts +0 -1
  236. package/dist/schema/index.js +0 -1
  237. package/dist/server/dispatcher.d.ts +0 -25
  238. package/dist/server/dispatcher.js +0 -54
  239. package/dist/service/local.d.ts +0 -27
  240. package/dist/service/local.js +0 -141
  241. package/dist/storage/driver.d.ts +0 -10
  242. package/dist/storage/driver.js +0 -10
  243. package/dist/storage/lazy.d.ts +0 -8
  244. package/dist/storage/lazy.js +0 -45
  245. package/dist/storage/lock-core.d.ts +0 -158
  246. package/dist/storage/lock-core.js +0 -872
  247. package/dist/utils/net.d.ts +0 -10
  248. package/dist/utils/net.js +0 -16
  249. package/dist/utils/process.d.ts +0 -10
  250. package/dist/utils/process.js +0 -21
  251. /package/dist/{filter → utils}/intent.d.ts +0 -0
  252. /package/dist/{filter → utils}/intent.js +0 -0
@@ -0,0 +1,29 @@
1
+ import { QUOTA_EXCEEDED, ProcessError } from "../errors.js";
2
+ export class ProcessQuotaTracker {
3
+ ctx;
4
+ constructor(ctx) {
5
+ this.ctx = ctx;
6
+ }
7
+ checkSpawnQuotas(_scope, requestedBufferBytes) {
8
+ let hostActive = 0;
9
+ for (const p of this.ctx.processes.values()) {
10
+ if (p.info.state === "starting" ||
11
+ p.info.state === "running" ||
12
+ p.info.state === "stopping") {
13
+ hostActive += 1;
14
+ }
15
+ }
16
+ if (hostActive >= this.ctx.quotas.maxActiveProcessesPerHost) {
17
+ throw new ProcessError(QUOTA_EXCEEDED, "Host active process quota exceeded", {
18
+ limit: this.ctx.quotas.maxActiveProcessesPerHost,
19
+ });
20
+ }
21
+ const perProcBuf = requestedBufferBytes ?? this.ctx.defaultLimits.outputBufferBytes;
22
+ if (perProcBuf > this.ctx.quotas.maxOutputBufferBytesPerProcess) {
23
+ throw new ProcessError(QUOTA_EXCEEDED, "Output buffer per process quota exceeded", {
24
+ requested: perProcBuf,
25
+ limit: this.ctx.quotas.maxOutputBufferBytesPerProcess,
26
+ });
27
+ }
28
+ }
29
+ }
@@ -26,7 +26,6 @@ export class RunExecutor {
26
26
  mode: "pipe",
27
27
  },
28
28
  };
29
- const runProcessId = `run-${crypto.randomUUID()}`;
30
29
  const chunks = [];
31
30
  let totalBytes = 0;
32
31
  let truncated = false;
@@ -1,57 +1,39 @@
1
1
  import type { ProcessOutputLog } from "./output-log.js";
2
2
  /**
3
- * 已淘汰输出日志的墓碑信息。
3
+ * 终态输出日志保留缓存配置选项。
4
4
  */
5
- export interface EvictedOutputTombstone {
6
- tailCursor: string;
7
- earliestCursor: string;
8
- evictedAt: number;
5
+ export interface TerminalOutputCacheOptions {
6
+ /** 保留时长(毫秒) */
7
+ retentionMs?: number;
8
+ /** 宿主输出缓冲区字节总配额 */
9
+ maxHostBufferBytes?: number;
10
+ /** 保留日志最大条目数,默认 512 */
11
+ maxEntries?: number;
9
12
  }
10
13
  /**
11
14
  * 终态输出日志保留缓存。
12
15
  *
13
- * 独立持有已驱逐进程的输出日志缓存与墓碑登记:内存输出无法从持久层恢复,
14
- * 保留缓存支撑后续游标读取;TTL 过期转墓碑与 LRU 淘汰均保持同步原子。
16
+ * 维护已退出进程的输出日志,提供基于最大条目数与宿主缓冲总量的有界内存缓存。
15
17
  */
16
18
  export declare class TerminalOutputCache {
17
19
  /** 保留日志最大条目数上限(默认) */
18
20
  static readonly DEFAULT_MAX_ENTRIES = 512;
19
- /** 墓碑最大登记条数上限(默认) */
20
- static readonly DEFAULT_MAX_TOMBSTONES = 2048;
21
21
  private readonly entries;
22
- private readonly tombstones;
23
22
  private readonly retentionMs;
24
23
  private readonly maxHostBufferBytes;
25
24
  private readonly maxEntries;
26
- private readonly maxTombstones;
27
- constructor(options: {
28
- /** 保留时长(毫秒) */
29
- retentionMs: number;
30
- /** 宿主输出缓冲区字节总配额 */
31
- maxHostBufferBytes: number;
32
- /** 保留日志最大条目数,默认 512 */
33
- maxEntries?: number;
34
- /** 墓碑最大登记条数,默认 2048 */
35
- maxTombstones?: number;
36
- });
25
+ private totalBytes;
26
+ constructor(options?: TerminalOutputCacheOptions);
37
27
  /**
38
- * 记录已淘汰输出日志的墓碑信息(超出容量时淘汰最旧登记,防止内存无限积压)。
39
- */
40
- private recordTombstone;
41
- /**
42
- * 清理已过期的终态输出日志条目并转为墓碑。
28
+ * 清理已过期的终态输出日志条目。
43
29
  */
44
30
  private cleanExpired;
45
31
  /**
46
- * 获取指定进程的墓碑登记信息。
47
- */
48
- getTombstone(processId: string): EvictedOutputTombstone | undefined;
49
- /**
50
- * 统计终态保留日志当前在内存中实际占用的输出缓冲字节总数(附带过期清理)。
32
+ * 统计终态保留日志当前在内存中实际占用的输出缓冲字节总数。
51
33
  */
52
34
  retainedBytes(): number;
53
35
  /**
54
- * 将终态输出日志存入保留缓存,并根据宿主配额执行 LRU 与 TTL 淘汰。
36
+ * 将终态输出日志存入保留缓存,并根据容量上限淘汰旧条目。
55
37
  */
56
38
  retain(processId: string, log: ProcessOutputLog): void;
57
39
  /**
@@ -59,8 +41,12 @@ export declare class TerminalOutputCache {
59
41
  */
60
42
  get(processId: string): ProcessOutputLog | undefined;
61
43
  /**
62
- * 按 LRU 顺序逐条淘汰最旧保留日志并转为墓碑,直至剩余保留字节数不超过给定的可容纳上限。
63
- * 返回被淘汰条目释放的字节总数,供宿主输出缓冲预算扣减复用。
44
+ * 按 LRU 顺序逐条淘汰最旧保留日志,直至剩余保留字节数不超过给定的可容纳上限。
45
+ * 返回被淘汰条目释放的字节总数。
64
46
  */
65
47
  evictLRUUntil(fits: number): number;
48
+ /**
49
+ * 清空所有缓存。
50
+ */
51
+ clear(): void;
66
52
  }
@@ -1,97 +1,72 @@
1
1
  /**
2
2
  * 终态输出日志保留缓存。
3
3
  *
4
- * 独立持有已驱逐进程的输出日志缓存与墓碑登记:内存输出无法从持久层恢复,
5
- * 保留缓存支撑后续游标读取;TTL 过期转墓碑与 LRU 淘汰均保持同步原子。
4
+ * 维护已退出进程的输出日志,提供基于最大条目数与宿主缓冲总量的有界内存缓存。
6
5
  */
7
6
  export class TerminalOutputCache {
8
7
  /** 保留日志最大条目数上限(默认) */
9
8
  static DEFAULT_MAX_ENTRIES = 512;
10
- /** 墓碑最大登记条数上限(默认) */
11
- static DEFAULT_MAX_TOMBSTONES = 2048;
12
9
  entries = new Map();
13
- tombstones = new Map();
14
10
  retentionMs;
15
11
  maxHostBufferBytes;
16
12
  maxEntries;
17
- maxTombstones;
18
- constructor(options) {
19
- this.retentionMs = options.retentionMs;
20
- this.maxHostBufferBytes = options.maxHostBufferBytes;
13
+ totalBytes = 0;
14
+ constructor(options = {}) {
15
+ this.retentionMs = options.retentionMs ?? 5 * 60 * 1000;
16
+ this.maxHostBufferBytes = options.maxHostBufferBytes ?? 64 * 1024 * 1024;
21
17
  this.maxEntries = options.maxEntries ?? TerminalOutputCache.DEFAULT_MAX_ENTRIES;
22
- this.maxTombstones = options.maxTombstones ?? TerminalOutputCache.DEFAULT_MAX_TOMBSTONES;
23
18
  }
24
19
  /**
25
- * 记录已淘汰输出日志的墓碑信息(超出容量时淘汰最旧登记,防止内存无限积压)。
26
- */
27
- recordTombstone(processId, tombstone) {
28
- if (this.tombstones.size >= this.maxTombstones) {
29
- const oldestKey = this.tombstones.keys().next().value;
30
- if (oldestKey) {
31
- this.tombstones.delete(oldestKey);
32
- }
33
- }
34
- this.tombstones.set(processId, tombstone);
35
- }
36
- /**
37
- * 清理已过期的终态输出日志条目并转为墓碑。
20
+ * 清理已过期的终态输出日志条目。
38
21
  */
39
22
  cleanExpired() {
23
+ if (this.retentionMs <= 0)
24
+ return;
40
25
  const now = Date.now();
41
26
  for (const [id, entry] of this.entries.entries()) {
42
- if (now - entry.evictedAt > this.retentionMs) {
43
- this.recordTombstone(id, {
44
- tailCursor: entry.log.tailCursor,
45
- earliestCursor: entry.log.earliestCursor,
46
- evictedAt: now,
47
- });
27
+ if (now - entry.retainedAt > this.retentionMs) {
28
+ this.totalBytes -= entry.log.currentBytes;
48
29
  this.entries.delete(id);
49
30
  }
50
31
  }
32
+ if (this.totalBytes < 0) {
33
+ this.totalBytes = 0;
34
+ }
51
35
  }
52
36
  /**
53
- * 获取指定进程的墓碑登记信息。
54
- */
55
- getTombstone(processId) {
56
- return this.tombstones.get(processId);
57
- }
58
- /**
59
- * 统计终态保留日志当前在内存中实际占用的输出缓冲字节总数(附带过期清理)。
37
+ * 统计终态保留日志当前在内存中实际占用的输出缓冲字节总数。
60
38
  */
61
39
  retainedBytes() {
62
40
  this.cleanExpired();
63
- let bytes = 0;
64
- for (const entry of this.entries.values()) {
65
- bytes += entry.log.currentBytes;
66
- }
67
- return bytes;
41
+ return this.totalBytes;
68
42
  }
69
43
  /**
70
- * 将终态输出日志存入保留缓存,并根据宿主配额执行 LRU 与 TTL 淘汰。
44
+ * 将终态输出日志存入保留缓存,并根据容量上限淘汰旧条目。
71
45
  */
72
46
  retain(processId, log) {
73
47
  this.cleanExpired();
74
- // 若新加入条目会导致总输出配额超限或数量超限,按 LRU 顺序淘汰最旧条目
75
- while ((this.retainedBytes() + log.currentBytes > this.maxHostBufferBytes ||
76
- this.entries.size >= this.maxEntries) &&
77
- this.entries.size > 0) {
48
+ const existing = this.entries.get(processId);
49
+ if (existing) {
50
+ this.totalBytes -= existing.log.currentBytes;
51
+ this.entries.delete(processId);
52
+ }
53
+ while (this.entries.size > 0 &&
54
+ (this.entries.size >= this.maxEntries ||
55
+ this.totalBytes + log.currentBytes > this.maxHostBufferBytes)) {
78
56
  const oldestKey = this.entries.keys().next().value;
79
57
  if (!oldestKey)
80
58
  break;
81
- const oldestEntry = this.entries.get(oldestKey);
82
- if (oldestEntry) {
83
- this.recordTombstone(oldestKey, {
84
- tailCursor: oldestEntry.log.tailCursor,
85
- earliestCursor: oldestEntry.log.earliestCursor,
86
- evictedAt: Date.now(),
87
- });
59
+ const oldest = this.entries.get(oldestKey);
60
+ if (oldest) {
61
+ this.totalBytes -= oldest.log.currentBytes;
88
62
  }
89
63
  this.entries.delete(oldestKey);
90
64
  }
91
65
  this.entries.set(processId, {
92
66
  log,
93
- evictedAt: Date.now(),
67
+ retainedAt: Date.now(),
94
68
  });
69
+ this.totalBytes += log.currentBytes;
95
70
  }
96
71
  /**
97
72
  * 获取终态保留日志(附带过期清理与 LRU 触达更新)。
@@ -101,32 +76,36 @@ export class TerminalOutputCache {
101
76
  const entry = this.entries.get(processId);
102
77
  if (!entry)
103
78
  return undefined;
104
- // 触达刷新 LRU 顺序
105
79
  this.entries.delete(processId);
106
80
  this.entries.set(processId, entry);
107
81
  return entry.log;
108
82
  }
109
83
  /**
110
- * 按 LRU 顺序逐条淘汰最旧保留日志并转为墓碑,直至剩余保留字节数不超过给定的可容纳上限。
111
- * 返回被淘汰条目释放的字节总数,供宿主输出缓冲预算扣减复用。
84
+ * 按 LRU 顺序逐条淘汰最旧保留日志,直至剩余保留字节数不超过给定的可容纳上限。
85
+ * 返回被淘汰条目释放的字节总数。
112
86
  */
113
87
  evictLRUUntil(fits) {
88
+ this.cleanExpired();
114
89
  let evictedBytes = 0;
115
- while (this.entries.size > 0 && this.retainedBytes() > fits) {
90
+ while (this.entries.size > 0 && this.totalBytes > fits) {
116
91
  const oldestKey = this.entries.keys().next().value;
117
92
  if (!oldestKey)
118
93
  break;
119
94
  const oldest = this.entries.get(oldestKey);
120
95
  if (oldest) {
121
- this.recordTombstone(oldestKey, {
122
- tailCursor: oldest.log.tailCursor,
123
- earliestCursor: oldest.log.earliestCursor,
124
- evictedAt: Date.now(),
125
- });
126
- evictedBytes += oldest.log.currentBytes;
96
+ const bytes = oldest.log.currentBytes;
97
+ this.totalBytes -= bytes;
98
+ evictedBytes += bytes;
127
99
  }
128
100
  this.entries.delete(oldestKey);
129
101
  }
130
102
  return evictedBytes;
131
103
  }
104
+ /**
105
+ * 清空所有缓存。
106
+ */
107
+ clear() {
108
+ this.entries.clear();
109
+ this.totalBytes = 0;
110
+ }
132
111
  }
@@ -1,4 +1,3 @@
1
1
  export * from "./types.js";
2
2
  export * from "./manager.js";
3
- export * from "./client.js";
4
- export { toSnakeUpperCase, resolveEnvValue } from "../runtime/env.js";
3
+ export * from "../client/index.js";
@@ -1,4 +1,3 @@
1
1
  export * from "./types.js";
2
2
  export * from "./manager.js";
3
- export * from "./client.js";
4
- export { toSnakeUpperCase, resolveEnvValue } from "../runtime/env.js";
3
+ export * from "../client/index.js";
@@ -1,12 +1,10 @@
1
1
  import type { ProfileEntry, ProfilesConfig, ResolvedTarget, TokenResolutionSource } from "./types.js";
2
+ import { normalizeServerUrl } from "../client/index.js";
3
+ export { normalizeServerUrl };
2
4
  /**
3
5
  * 默认初始 Profiles 配置(预设 local 本地环境)。
4
6
  */
5
7
  export declare const DEFAULT_PROFILES_CONFIG: ProfilesConfig;
6
- /**
7
- * 格式化并规范化 Server URL 地址(若未指定协议,本地回环地址默认使用 http://,非本地回环地址默认使用 https://,并移除末尾斜杠)。
8
- */
9
- export declare function normalizeServerUrl(url: string): string;
10
8
  /**
11
9
  * 获取 profiles.json 配置文件的物理绝对路径(~/.actiondock/profiles.json)。
12
10
  */
@@ -1,9 +1,10 @@
1
1
  import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { toSnakeUpperCase } from "../runtime/env.js";
4
- import { isLoopbackHost } from "../utils/net.js";
5
4
  import { getActionDockHome } from "../utils/index.js";
6
- import { ActionDockError, INVALID_ARGUMENT } from "../errors.js";
5
+ import { ActionDockError, INVALID_ARGUMENT, PROFILE_NOT_FOUND } from "../errors.js";
6
+ import { normalizeServerUrl } from "../client/index.js";
7
+ export { normalizeServerUrl };
7
8
  const PROFILE_NAME_REGEX = /^[a-zA-Z0-9_\-\.]+$/;
8
9
  /**
9
10
  * 默认初始 Profiles 配置(预设 local 本地环境)。
@@ -17,37 +18,6 @@ export const DEFAULT_PROFILES_CONFIG = {
17
18
  },
18
19
  },
19
20
  };
20
- /**
21
- * 格式化并规范化 Server URL 地址(若未指定协议,本地回环地址默认使用 http://,非本地回环地址默认使用 https://,并移除末尾斜杠)。
22
- */
23
- export function normalizeServerUrl(url) {
24
- let cleaned = url.trim().replace(/\/+$/, "");
25
- if (!cleaned || cleaned === "local") {
26
- return cleaned;
27
- }
28
- if (!/^https?:\/\//i.test(cleaned)) {
29
- let hostname = cleaned;
30
- const slashIdx = hostname.indexOf("/");
31
- if (slashIdx !== -1) {
32
- hostname = hostname.slice(0, slashIdx);
33
- }
34
- if (hostname.startsWith("[")) {
35
- const endBracket = hostname.indexOf("]");
36
- if (endBracket !== -1) {
37
- hostname = hostname.slice(1, endBracket);
38
- }
39
- }
40
- else {
41
- const colonIdx = hostname.indexOf(":");
42
- if (colonIdx !== -1) {
43
- hostname = hostname.slice(0, colonIdx);
44
- }
45
- }
46
- const isLoopback = isLoopbackHost(hostname);
47
- cleaned = `${isLoopback ? "http" : "https"}://${cleaned}`;
48
- }
49
- return cleaned;
50
- }
51
21
  /**
52
22
  * 获取 profiles.json 配置文件的物理绝对路径(~/.actiondock/profiles.json)。
53
23
  */
@@ -141,10 +111,10 @@ export function saveProfiles(data, customHome) {
141
111
  export function addProfile(name, entry, customHome) {
142
112
  const trimmedName = name.trim();
143
113
  if (!trimmedName) {
144
- throw new Error("Profile name cannot be empty");
114
+ throw new ActionDockError(INVALID_ARGUMENT, "Profile name cannot be empty");
145
115
  }
146
116
  if (!PROFILE_NAME_REGEX.test(trimmedName)) {
147
- throw new Error(`Invalid profile name '${trimmedName}'. Profile names may only contain letters, numbers, hyphens, underscores, and dots.`);
117
+ throw new ActionDockError(INVALID_ARGUMENT, `Invalid profile name '${trimmedName}'. Profile names may only contain letters, numbers, hyphens, underscores, and dots.`);
148
118
  }
149
119
  const profilesConfig = loadProfiles(customHome);
150
120
  const normalizedServer = normalizeServerUrl(entry.serverUrl);
@@ -160,12 +130,12 @@ export function addProfile(name, entry, customHome) {
160
130
  export function updateProfile(name, entry, customHome) {
161
131
  const trimmedName = name.trim();
162
132
  if (!trimmedName) {
163
- throw new Error("Profile name cannot be empty");
133
+ throw new ActionDockError(INVALID_ARGUMENT, "Profile name cannot be empty");
164
134
  }
165
135
  const profilesConfig = loadProfiles(customHome);
166
136
  const existing = profilesConfig.profiles[trimmedName];
167
137
  if (!existing) {
168
- throw new Error(`Profile '${trimmedName}' not found. Configure it with 'ad profile add ${trimmedName} --server <url>'`);
138
+ throw new ActionDockError(PROFILE_NOT_FOUND, `Profile '${trimmedName}' not found. Configure it with 'ad profile add ${trimmedName} --server <url>'`);
169
139
  }
170
140
  const serverUrl = entry.serverUrl ? normalizeServerUrl(entry.serverUrl) : existing.serverUrl;
171
141
  profilesConfig.profiles[trimmedName] = {
@@ -194,7 +164,7 @@ export function useProfile(name, customHome) {
194
164
  const trimmedName = name.trim();
195
165
  const profilesConfig = loadProfiles(customHome);
196
166
  if (trimmedName !== "local" && !profilesConfig.profiles[trimmedName]) {
197
- throw new Error(`Profile '${trimmedName}' not found. Use 'ad profile list' to see available profiles or 'ad profile add' to register one.`);
167
+ throw new ActionDockError(PROFILE_NOT_FOUND, `Profile '${trimmedName}' not found. Use 'ad profile list' to see available profiles or 'ad profile add' to register one.`);
198
168
  }
199
169
  profilesConfig.currentProfile = trimmedName;
200
170
  saveProfiles(profilesConfig, customHome);
@@ -298,7 +268,7 @@ export function resolveTarget(options, customHome) {
298
268
  }
299
269
  const found = profilesConfig.profiles[pName];
300
270
  if (!found) {
301
- throw new Error(`Profile '${pName}' not found. Configure it with 'ad profile add ${pName} --server <url>'`);
271
+ throw new ActionDockError(PROFILE_NOT_FOUND, `Profile '${pName}' not found. Configure it with 'ad profile add ${pName} --server <url>'`);
302
272
  }
303
273
  const resolvedToken = resolveProfileToken(pName, found, options.token);
304
274
  const effectiveInsecure = options.insecure !== undefined
@@ -335,7 +305,7 @@ export function resolveTarget(options, customHome) {
335
305
  }
336
306
  const found = profilesConfig.profiles[pName];
337
307
  if (!found) {
338
- throw new Error(`Profile '${pName}' (from ACTIONDOCK_PROFILE) not found. Configure it with 'ad profile add ${pName} --server <url>'`);
308
+ throw new ActionDockError(PROFILE_NOT_FOUND, `Profile '${pName}' (from ACTIONDOCK_PROFILE) not found. Configure it with 'ad profile add ${pName} --server <url>'`);
339
309
  }
340
310
  const resolvedToken = resolveProfileToken(pName, found, options?.token);
341
311
  const effectiveInsecure = options?.insecure !== undefined
@@ -1,3 +1,4 @@
1
+ export type { RemoteHealthResult } from "../client/index.js";
1
2
  /**
2
3
  * 单个远端配置环境(Profile)实体定义。
3
4
  */
@@ -48,20 +49,3 @@ export interface ResolvedTarget {
48
49
  /** 是否允许向非回环地址发送明文 HTTP 请求 */
49
50
  allowInsecureHttp?: boolean;
50
51
  }
51
- /**
52
- * 远端服务器健康探测与时延检测结果。
53
- */
54
- export interface RemoteHealthResult {
55
- /** 服务端是否连通且鉴权成功 */
56
- ok: boolean;
57
- /** 服务端状态标识(如 "ok") */
58
- status?: string;
59
- /** 远端 ActionDock 版本号 */
60
- version?: string;
61
- /** 远端服务运行时间(秒) */
62
- uptime?: number;
63
- /** 网络往返延迟(毫秒) */
64
- latencyMs: number;
65
- /** 探测失败时的错误信息 */
66
- error?: string;
67
- }
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * 解析 JSON 文本并在检测到重复键时抛出错误。
3
- * 遵循 RFC 8785 清单校验规范:解析阶段必须率先拦截并拒绝重复键。
3
+ * 遵循 RFC 8785 清单校验规范:解析阶段必须率先拦截并拒绝重复键。
4
+ *
5
+ * 实现策略:JSON.parse 主解析 + 轻量键名扫描器做重复键检测。
6
+ * 扫描器以状态机遍历原始文本,仅在对象键位置(冒号前的字符串字面量)记录键名,
7
+ * 同一对象层级内出现重复键名即抛错;字符串内部与转义字符不参与键名提取。
4
8
  */
5
9
  export declare function parseJsonWithoutDuplicates<T = unknown>(jsonText: string): T;
6
10
  /**