feique 1.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 (227) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +220 -0
  3. package/README.md +265 -0
  4. package/dist/backend/claude.d.ts +36 -0
  5. package/dist/backend/claude.js +358 -0
  6. package/dist/backend/claude.js.map +1 -0
  7. package/dist/backend/codex.d.ts +31 -0
  8. package/dist/backend/codex.js +100 -0
  9. package/dist/backend/codex.js.map +1 -0
  10. package/dist/backend/factory.d.ts +9 -0
  11. package/dist/backend/factory.js +56 -0
  12. package/dist/backend/factory.js.map +1 -0
  13. package/dist/backend/types.d.ts +54 -0
  14. package/dist/backend/types.js +2 -0
  15. package/dist/backend/types.js.map +1 -0
  16. package/dist/bridge/commands.d.ts +135 -0
  17. package/dist/bridge/commands.js +860 -0
  18. package/dist/bridge/commands.js.map +1 -0
  19. package/dist/bridge/service.d.ts +160 -0
  20. package/dist/bridge/service.js +3785 -0
  21. package/dist/bridge/service.js.map +1 -0
  22. package/dist/bridge/task-queue.d.ts +14 -0
  23. package/dist/bridge/task-queue.js +81 -0
  24. package/dist/bridge/task-queue.js.map +1 -0
  25. package/dist/bridge/types.d.ts +39 -0
  26. package/dist/bridge/types.js +2 -0
  27. package/dist/bridge/types.js.map +1 -0
  28. package/dist/cli.d.ts +2 -0
  29. package/dist/cli.js +1199 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/codex/capabilities.d.ts +20 -0
  32. package/dist/codex/capabilities.js +41 -0
  33. package/dist/codex/capabilities.js.map +1 -0
  34. package/dist/codex/runner.d.ts +47 -0
  35. package/dist/codex/runner.js +294 -0
  36. package/dist/codex/runner.js.map +1 -0
  37. package/dist/codex/session-index.d.ts +22 -0
  38. package/dist/codex/session-index.js +205 -0
  39. package/dist/codex/session-index.js.map +1 -0
  40. package/dist/collaboration/awareness.d.ts +36 -0
  41. package/dist/collaboration/awareness.js +107 -0
  42. package/dist/collaboration/awareness.js.map +1 -0
  43. package/dist/collaboration/digest.d.ts +65 -0
  44. package/dist/collaboration/digest.js +178 -0
  45. package/dist/collaboration/digest.js.map +1 -0
  46. package/dist/collaboration/handoff.d.ts +66 -0
  47. package/dist/collaboration/handoff.js +94 -0
  48. package/dist/collaboration/handoff.js.map +1 -0
  49. package/dist/collaboration/insights.d.ts +24 -0
  50. package/dist/collaboration/insights.js +243 -0
  51. package/dist/collaboration/insights.js.map +1 -0
  52. package/dist/collaboration/knowledge.d.ts +26 -0
  53. package/dist/collaboration/knowledge.js +105 -0
  54. package/dist/collaboration/knowledge.js.map +1 -0
  55. package/dist/collaboration/timeline.d.ts +31 -0
  56. package/dist/collaboration/timeline.js +150 -0
  57. package/dist/collaboration/timeline.js.map +1 -0
  58. package/dist/collaboration/trust.d.ts +49 -0
  59. package/dist/collaboration/trust.js +176 -0
  60. package/dist/collaboration/trust.js.map +1 -0
  61. package/dist/config/codex-skill.d.ts +7 -0
  62. package/dist/config/codex-skill.js +44 -0
  63. package/dist/config/codex-skill.js.map +1 -0
  64. package/dist/config/doctor.d.ts +12 -0
  65. package/dist/config/doctor.js +314 -0
  66. package/dist/config/doctor.js.map +1 -0
  67. package/dist/config/init.d.ts +3 -0
  68. package/dist/config/init.js +123 -0
  69. package/dist/config/init.js.map +1 -0
  70. package/dist/config/load.d.ts +33 -0
  71. package/dist/config/load.js +252 -0
  72. package/dist/config/load.js.map +1 -0
  73. package/dist/config/mutate.d.ts +21 -0
  74. package/dist/config/mutate.js +86 -0
  75. package/dist/config/mutate.js.map +1 -0
  76. package/dist/config/paths.d.ts +3 -0
  77. package/dist/config/paths.js +33 -0
  78. package/dist/config/paths.js.map +1 -0
  79. package/dist/config/schema.d.ts +308 -0
  80. package/dist/config/schema.js +250 -0
  81. package/dist/config/schema.js.map +1 -0
  82. package/dist/control-plane/project-session.d.ts +67 -0
  83. package/dist/control-plane/project-session.js +234 -0
  84. package/dist/control-plane/project-session.js.map +1 -0
  85. package/dist/feishu/base.d.ts +19 -0
  86. package/dist/feishu/base.js +93 -0
  87. package/dist/feishu/base.js.map +1 -0
  88. package/dist/feishu/cards.d.ts +22 -0
  89. package/dist/feishu/cards.js +144 -0
  90. package/dist/feishu/cards.js.map +1 -0
  91. package/dist/feishu/client.d.ts +61 -0
  92. package/dist/feishu/client.js +315 -0
  93. package/dist/feishu/client.js.map +1 -0
  94. package/dist/feishu/diagnostics.d.ts +42 -0
  95. package/dist/feishu/diagnostics.js +194 -0
  96. package/dist/feishu/diagnostics.js.map +1 -0
  97. package/dist/feishu/doc.d.ts +13 -0
  98. package/dist/feishu/doc.js +59 -0
  99. package/dist/feishu/doc.js.map +1 -0
  100. package/dist/feishu/extractors.d.ts +7 -0
  101. package/dist/feishu/extractors.js +215 -0
  102. package/dist/feishu/extractors.js.map +1 -0
  103. package/dist/feishu/long-connection.d.ts +12 -0
  104. package/dist/feishu/long-connection.js +41 -0
  105. package/dist/feishu/long-connection.js.map +1 -0
  106. package/dist/feishu/message-resource.d.ts +14 -0
  107. package/dist/feishu/message-resource.js +309 -0
  108. package/dist/feishu/message-resource.js.map +1 -0
  109. package/dist/feishu/replay.d.ts +37 -0
  110. package/dist/feishu/replay.js +114 -0
  111. package/dist/feishu/replay.js.map +1 -0
  112. package/dist/feishu/task.d.ts +18 -0
  113. package/dist/feishu/task.js +86 -0
  114. package/dist/feishu/task.js.map +1 -0
  115. package/dist/feishu/text.d.ts +23 -0
  116. package/dist/feishu/text.js +155 -0
  117. package/dist/feishu/text.js.map +1 -0
  118. package/dist/feishu/webhook.d.ts +23 -0
  119. package/dist/feishu/webhook.js +130 -0
  120. package/dist/feishu/webhook.js.map +1 -0
  121. package/dist/feishu/wiki.d.ts +52 -0
  122. package/dist/feishu/wiki.js +300 -0
  123. package/dist/feishu/wiki.js.map +1 -0
  124. package/dist/index.d.ts +9 -0
  125. package/dist/index.js +9 -0
  126. package/dist/index.js.map +1 -0
  127. package/dist/knowledge/search.d.ts +11 -0
  128. package/dist/knowledge/search.js +83 -0
  129. package/dist/knowledge/search.js.map +1 -0
  130. package/dist/logging.d.ts +3 -0
  131. package/dist/logging.js +40 -0
  132. package/dist/logging.js.map +1 -0
  133. package/dist/mcp/server.d.ts +34 -0
  134. package/dist/mcp/server.js +1196 -0
  135. package/dist/mcp/server.js.map +1 -0
  136. package/dist/memory/embedding-factory.d.ts +6 -0
  137. package/dist/memory/embedding-factory.js +20 -0
  138. package/dist/memory/embedding-factory.js.map +1 -0
  139. package/dist/memory/embeddings.d.ts +40 -0
  140. package/dist/memory/embeddings.js +150 -0
  141. package/dist/memory/embeddings.js.map +1 -0
  142. package/dist/memory/ollama-embeddings.d.ts +63 -0
  143. package/dist/memory/ollama-embeddings.js +215 -0
  144. package/dist/memory/ollama-embeddings.js.map +1 -0
  145. package/dist/memory/retrieve.d.ts +17 -0
  146. package/dist/memory/retrieve.js +29 -0
  147. package/dist/memory/retrieve.js.map +1 -0
  148. package/dist/memory/summarize.d.ts +13 -0
  149. package/dist/memory/summarize.js +58 -0
  150. package/dist/memory/summarize.js.map +1 -0
  151. package/dist/observability/cost.d.ts +12 -0
  152. package/dist/observability/cost.js +22 -0
  153. package/dist/observability/cost.js.map +1 -0
  154. package/dist/observability/dashboard-html.d.ts +5 -0
  155. package/dist/observability/dashboard-html.js +304 -0
  156. package/dist/observability/dashboard-html.js.map +1 -0
  157. package/dist/observability/metrics.d.ts +36 -0
  158. package/dist/observability/metrics.js +230 -0
  159. package/dist/observability/metrics.js.map +1 -0
  160. package/dist/observability/readiness.d.ts +31 -0
  161. package/dist/observability/readiness.js +57 -0
  162. package/dist/observability/readiness.js.map +1 -0
  163. package/dist/observability/server.d.ts +84 -0
  164. package/dist/observability/server.js +181 -0
  165. package/dist/observability/server.js.map +1 -0
  166. package/dist/projects/paths.d.ts +9 -0
  167. package/dist/projects/paths.js +30 -0
  168. package/dist/projects/paths.js.map +1 -0
  169. package/dist/runtime/instance-lock.d.ts +12 -0
  170. package/dist/runtime/instance-lock.js +99 -0
  171. package/dist/runtime/instance-lock.js.map +1 -0
  172. package/dist/runtime/process.d.ts +2 -0
  173. package/dist/runtime/process.js +43 -0
  174. package/dist/runtime/process.js.map +1 -0
  175. package/dist/runtime/shutdown.d.ts +11 -0
  176. package/dist/runtime/shutdown.js +38 -0
  177. package/dist/runtime/shutdown.js.map +1 -0
  178. package/dist/security/access.d.ts +13 -0
  179. package/dist/security/access.js +160 -0
  180. package/dist/security/access.js.map +1 -0
  181. package/dist/service/install.d.ts +19 -0
  182. package/dist/service/install.js +35 -0
  183. package/dist/service/install.js.map +1 -0
  184. package/dist/service/templates.d.ts +22 -0
  185. package/dist/service/templates.js +118 -0
  186. package/dist/service/templates.js.map +1 -0
  187. package/dist/state/audit-log.d.ts +33 -0
  188. package/dist/state/audit-log.js +116 -0
  189. package/dist/state/audit-log.js.map +1 -0
  190. package/dist/state/config-history-store.d.ts +27 -0
  191. package/dist/state/config-history-store.js +65 -0
  192. package/dist/state/config-history-store.js.map +1 -0
  193. package/dist/state/handoff-store.d.ts +20 -0
  194. package/dist/state/handoff-store.js +97 -0
  195. package/dist/state/handoff-store.js.map +1 -0
  196. package/dist/state/idempotency-store.d.ts +19 -0
  197. package/dist/state/idempotency-store.js +84 -0
  198. package/dist/state/idempotency-store.js.map +1 -0
  199. package/dist/state/memory-store.d.ts +137 -0
  200. package/dist/state/memory-store.js +713 -0
  201. package/dist/state/memory-store.js.map +1 -0
  202. package/dist/state/pending-command-store.d.ts +30 -0
  203. package/dist/state/pending-command-store.js +108 -0
  204. package/dist/state/pending-command-store.js.map +1 -0
  205. package/dist/state/run-state-store.d.ts +58 -0
  206. package/dist/state/run-state-store.js +269 -0
  207. package/dist/state/run-state-store.js.map +1 -0
  208. package/dist/state/session-store.d.ts +56 -0
  209. package/dist/state/session-store.js +275 -0
  210. package/dist/state/session-store.js.map +1 -0
  211. package/dist/state/trust-store.d.ts +15 -0
  212. package/dist/state/trust-store.js +53 -0
  213. package/dist/state/trust-store.js.map +1 -0
  214. package/dist/utils/fs.d.ts +4 -0
  215. package/dist/utils/fs.js +26 -0
  216. package/dist/utils/fs.js.map +1 -0
  217. package/dist/utils/json.d.ts +1 -0
  218. package/dist/utils/json.js +9 -0
  219. package/dist/utils/json.js.map +1 -0
  220. package/dist/utils/path.d.ts +3 -0
  221. package/dist/utils/path.js +22 -0
  222. package/dist/utils/path.js.map +1 -0
  223. package/dist/utils/serial-executor.d.ts +5 -0
  224. package/dist/utils/serial-executor.js +12 -0
  225. package/dist/utils/serial-executor.js.map +1 -0
  226. package/package.json +71 -0
  227. package/skills/feique-session/SKILL.md +27 -0
@@ -0,0 +1,30 @@
1
+ import path from 'node:path';
2
+ export function getProjectStateDir(storageDir, projectAlias) {
3
+ return path.join(storageDir, 'projects', sanitizeProjectAlias(projectAlias));
4
+ }
5
+ export function getProjectDownloadsDir(storageDir, projectAlias, project) {
6
+ return project.download_dir ? path.resolve(project.download_dir) : path.join(getProjectStateDir(storageDir, projectAlias), 'downloads');
7
+ }
8
+ export function getProjectTempDir(storageDir, projectAlias, project) {
9
+ return project.temp_dir ? path.resolve(project.temp_dir) : path.join(getProjectStateDir(storageDir, projectAlias), 'tmp');
10
+ }
11
+ export function getProjectCacheDir(storageDir, projectAlias, project) {
12
+ return project.cache_dir ? path.resolve(project.cache_dir) : path.join(getProjectStateDir(storageDir, projectAlias), 'cache');
13
+ }
14
+ export function getProjectLogDir(storageDir, projectAlias, project) {
15
+ return project.log_dir ? path.resolve(project.log_dir) : path.join(getProjectStateDir(storageDir, projectAlias), 'logs');
16
+ }
17
+ export function getProjectAuditDir(storageDir, projectAlias, project) {
18
+ return getProjectLogDir(storageDir, projectAlias, project);
19
+ }
20
+ export function getProjectAuditFile(storageDir, projectAlias, project) {
21
+ return path.join(getProjectAuditDir(storageDir, projectAlias, project), 'project-audit.jsonl');
22
+ }
23
+ export function getProjectArchiveDir(storageDir, projectAlias) {
24
+ return path.join(getProjectStateDir(storageDir, projectAlias), 'archive');
25
+ }
26
+ function sanitizeProjectAlias(value) {
27
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, '-');
28
+ return normalized.length > 0 ? normalized : 'default';
29
+ }
30
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/projects/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,UAAU,kBAAkB,CAAC,UAAkB,EAAE,YAAoB;IACzE,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAkB,EAAE,YAAoB,EAAE,OAAsB;IACrG,OAAO,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,WAAW,CAAC,CAAC;AAC1I,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,YAAoB,EAAE,OAAsB;IAChG,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5H,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,UAAkB,EAAE,YAAoB,EAAE,OAAsB;IACjG,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;AAChI,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,YAAoB,EAAE,OAAsB;IAC/F,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;AAC3H,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,UAAkB,EAAE,YAAoB,EAAE,OAAsB;IACjG,OAAO,gBAAgB,CAAC,UAAU,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,UAAkB,EAAE,YAAoB,EAAE,OAAsB;IAClG,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,UAAkB,EAAE,YAAoB;IAC3E,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,SAAS,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa;IACzC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;IAC7E,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AACxD,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface InstanceLock {
2
+ lockPath: string;
3
+ release(): Promise<void>;
4
+ }
5
+ export declare function acquireInstanceLock(options: {
6
+ storageDir: string;
7
+ serviceName: string;
8
+ cwd?: string;
9
+ ownerPid?: number;
10
+ transport?: string;
11
+ isProcessAlive?: (pid: number) => boolean;
12
+ }): Promise<InstanceLock>;
@@ -0,0 +1,99 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { ensureDir } from '../utils/fs.js';
5
+ export async function acquireInstanceLock(options) {
6
+ await ensureDir(options.storageDir);
7
+ const lockPath = path.join(options.storageDir, `${options.serviceName}.lock`);
8
+ const payload = {
9
+ instance_id: randomUUID(),
10
+ pid: options.ownerPid ?? process.pid,
11
+ started_at: new Date().toISOString(),
12
+ cwd: options.cwd ?? process.cwd(),
13
+ ...(options.transport ? { transport: options.transport } : {}),
14
+ };
15
+ for (let attempt = 0; attempt < 2; attempt += 1) {
16
+ try {
17
+ await fs.writeFile(lockPath, JSON.stringify(payload, null, 2), { encoding: 'utf8', flag: 'wx' });
18
+ return {
19
+ lockPath,
20
+ release: async () => {
21
+ try {
22
+ const current = await readLockPayload(lockPath);
23
+ if (!current || current.instance_id !== payload.instance_id) {
24
+ return;
25
+ }
26
+ await fs.unlink(lockPath);
27
+ }
28
+ catch (error) {
29
+ if (isMissingFileError(error)) {
30
+ return;
31
+ }
32
+ throw error;
33
+ }
34
+ },
35
+ };
36
+ }
37
+ catch (error) {
38
+ if (!isFileExistsError(error)) {
39
+ throw error;
40
+ }
41
+ const existing = await readLockPayload(lockPath);
42
+ if (existing && isKnownLiveProcess(existing.pid, options.isProcessAlive)) {
43
+ throw new Error(`Another ${options.serviceName} instance is already running (pid ${existing.pid}). Lock: ${lockPath}`);
44
+ }
45
+ await fs.rm(lockPath, { force: true });
46
+ }
47
+ }
48
+ throw new Error(`Unable to acquire instance lock: ${lockPath}`);
49
+ }
50
+ async function readLockPayload(lockPath) {
51
+ try {
52
+ const content = await fs.readFile(lockPath, 'utf8');
53
+ const parsed = JSON.parse(content);
54
+ if (typeof parsed.instance_id !== 'string' || typeof parsed.pid !== 'number' || typeof parsed.started_at !== 'string') {
55
+ return null;
56
+ }
57
+ return {
58
+ instance_id: parsed.instance_id,
59
+ pid: parsed.pid,
60
+ started_at: parsed.started_at,
61
+ cwd: typeof parsed.cwd === 'string' ? parsed.cwd : '',
62
+ transport: typeof parsed.transport === 'string' ? parsed.transport : undefined,
63
+ };
64
+ }
65
+ catch (error) {
66
+ if (isMissingFileError(error)) {
67
+ return null;
68
+ }
69
+ return null;
70
+ }
71
+ }
72
+ function isKnownLiveProcess(pid, checker) {
73
+ if (!Number.isInteger(pid) || pid <= 0) {
74
+ return false;
75
+ }
76
+ if (checker) {
77
+ return checker(pid);
78
+ }
79
+ try {
80
+ process.kill(pid, 0);
81
+ return true;
82
+ }
83
+ catch (error) {
84
+ return !isMissingProcessError(error);
85
+ }
86
+ }
87
+ function isFileExistsError(error) {
88
+ return isErrorWithCode(error, 'EEXIST');
89
+ }
90
+ function isMissingFileError(error) {
91
+ return isErrorWithCode(error, 'ENOENT');
92
+ }
93
+ function isMissingProcessError(error) {
94
+ return isErrorWithCode(error, 'ESRCH');
95
+ }
96
+ function isErrorWithCode(error, code) {
97
+ return error instanceof Error && 'code' in error && error.code === code;
98
+ }
99
+ //# sourceMappingURL=instance-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-lock.js","sourceRoot":"","sources":["../../src/runtime/instance-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function isProcessAlive(pid: number): boolean;
2
+ export declare function terminateProcess(pid: number, signal?: NodeJS.Signals): boolean;
@@ -0,0 +1,43 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ export function isProcessAlive(pid) {
3
+ if (!Number.isInteger(pid) || pid <= 0) {
4
+ return false;
5
+ }
6
+ try {
7
+ process.kill(pid, 0);
8
+ }
9
+ catch (error) {
10
+ return !(error instanceof Error && 'code' in error && error.code === 'ESRCH');
11
+ }
12
+ const unixState = readUnixProcessState(pid);
13
+ if (unixState?.startsWith('Z')) {
14
+ return false;
15
+ }
16
+ return true;
17
+ }
18
+ export function terminateProcess(pid, signal = 'SIGTERM') {
19
+ if (!Number.isInteger(pid) || pid <= 0) {
20
+ return false;
21
+ }
22
+ try {
23
+ process.kill(pid, signal);
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ function readUnixProcessState(pid) {
31
+ if (process.platform === 'win32') {
32
+ return null;
33
+ }
34
+ const result = spawnSync('ps', ['-o', 'stat=', '-p', String(pid)], {
35
+ encoding: 'utf8',
36
+ });
37
+ if (result.status !== 0) {
38
+ return null;
39
+ }
40
+ const output = result.stdout.trim();
41
+ return output.length > 0 ? output : null;
42
+ }
43
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/runtime/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,SAAyB,SAAS;IAC9E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,GAAW;IACvC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE;QACjE,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACpC,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Logger } from '../logging.js';
2
+ export interface ShutdownSignalTarget {
3
+ on(event: NodeJS.Signals, listener: () => void): unknown;
4
+ off(event: NodeJS.Signals, listener: () => void): unknown;
5
+ }
6
+ export declare function waitForShutdownSignal(options: {
7
+ logger: Logger;
8
+ onShutdown: (signal: NodeJS.Signals) => Promise<void> | void;
9
+ target?: ShutdownSignalTarget;
10
+ signals?: NodeJS.Signals[];
11
+ }): Promise<NodeJS.Signals>;
@@ -0,0 +1,38 @@
1
+ const DEFAULT_SIGNALS = ['SIGINT', 'SIGTERM'];
2
+ export async function waitForShutdownSignal(options) {
3
+ const target = options.target ?? process;
4
+ const signals = options.signals ?? DEFAULT_SIGNALS;
5
+ return new Promise((resolve, reject) => {
6
+ let settled = false;
7
+ const listeners = new Map();
8
+ const cleanup = () => {
9
+ for (const [signal, listener] of listeners) {
10
+ target.off(signal, listener);
11
+ }
12
+ listeners.clear();
13
+ };
14
+ const finish = async (signal) => {
15
+ if (settled) {
16
+ return;
17
+ }
18
+ settled = true;
19
+ cleanup();
20
+ options.logger.info({ signal }, 'Received shutdown signal');
21
+ try {
22
+ await options.onShutdown(signal);
23
+ resolve(signal);
24
+ }
25
+ catch (error) {
26
+ reject(error);
27
+ }
28
+ };
29
+ for (const signal of signals) {
30
+ const listener = () => {
31
+ void finish(signal);
32
+ };
33
+ listeners.set(signal, listener);
34
+ target.on(signal, listener);
35
+ }
36
+ });
37
+ }
38
+ //# sourceMappingURL=shutdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/runtime/shutdown.ts"],"names":[],"mappings":"AAOA,MAAM,eAAe,GAAqB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAEhE,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAK3C;IACC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;IACzC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC;IAEnD,OAAO,IAAI,OAAO,CAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrD,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,SAAS,GAAG,IAAI,GAAG,EAA8B,CAAC;QAExD,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,SAAS,EAAE,CAAC;gBAC3C,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC/B,CAAC;YACD,SAAS,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,KAAK,EAAE,MAAsB,EAAE,EAAE;YAC9C,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,0BAA0B,CAAC,CAAC;YAE5D,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBACjC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;QACH,CAAC,CAAC;QAEF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,GAAG,EAAE;gBACpB,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC,CAAC;YACF,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAChC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { BridgeConfig } from '../config/schema.js';
2
+ export type AccessRole = 'viewer' | 'operator' | 'admin';
3
+ export type ProjectCapability = 'project:view' | 'project:switch' | 'session:list' | 'session:control' | 'run:execute' | 'run:cancel' | 'project:mutate';
4
+ export type GlobalCapability = 'service:status' | 'service:runs' | 'service:restart' | 'config:history' | 'config:rollback' | 'config:mutate';
5
+ export declare function resolveProjectAccessRole(config: BridgeConfig, projectAlias: string, chatId: string): AccessRole | null;
6
+ export declare function canAccessProject(config: BridgeConfig, projectAlias: string, chatId: string, minimumRole?: AccessRole): boolean;
7
+ export declare function filterAccessibleProjects(config: BridgeConfig, chatId: string, minimumRole?: AccessRole): string[];
8
+ export declare function canAccessProjectCapability(config: BridgeConfig, projectAlias: string, chatId: string, capability: ProjectCapability): boolean;
9
+ export declare function canAccessGlobalCapability(config: BridgeConfig, chatId: string, capability: GlobalCapability): boolean;
10
+ export declare function describeMinimumRole(role: AccessRole): string;
11
+ export declare function hasAccessGuard(config: BridgeConfig, projectAlias: string): boolean;
12
+ export declare function hasProjectCapabilityOverride(config: BridgeConfig, projectAlias: string, capability: ProjectCapability): boolean;
13
+ export declare function hasGlobalCapabilityOverride(config: BridgeConfig, capability: GlobalCapability): boolean;
@@ -0,0 +1,160 @@
1
+ const ROLE_ORDER = ['viewer', 'operator', 'admin'];
2
+ export function resolveProjectAccessRole(config, projectAlias, chatId) {
3
+ return maxRole([
4
+ resolveGlobalRole(config, chatId),
5
+ resolveScopedRole(config.projects[projectAlias]?.viewer_chat_ids, config.projects[projectAlias]?.operator_chat_ids, config.projects[projectAlias]?.admin_chat_ids, chatId),
6
+ ]);
7
+ }
8
+ export function canAccessProject(config, projectAlias, chatId, minimumRole = 'viewer') {
9
+ if (!hasAccessGuard(config, projectAlias)) {
10
+ return true;
11
+ }
12
+ const actual = resolveProjectAccessRole(config, projectAlias, chatId);
13
+ return actual !== null && roleRank(actual) >= roleRank(minimumRole);
14
+ }
15
+ export function filterAccessibleProjects(config, chatId, minimumRole = 'viewer') {
16
+ return Object.keys(config.projects).filter((alias) => canAccessProject(config, alias, chatId, minimumRole));
17
+ }
18
+ export function canAccessProjectCapability(config, projectAlias, chatId, capability) {
19
+ const project = config.projects[projectAlias];
20
+ if (!project) {
21
+ return false;
22
+ }
23
+ const overrideList = resolveProjectCapabilityList(config, projectAlias, capability);
24
+ if (hasEntries(overrideList)) {
25
+ const scopedList = overrideList;
26
+ if (scopedList.includes(chatId)) {
27
+ return true;
28
+ }
29
+ if (capability === 'project:mutate') {
30
+ return canAccessProject(config, projectAlias, chatId, 'admin');
31
+ }
32
+ return false;
33
+ }
34
+ switch (capability) {
35
+ case 'project:view':
36
+ case 'project:switch':
37
+ case 'session:list':
38
+ return canAccessProject(config, projectAlias, chatId, 'viewer');
39
+ case 'session:control':
40
+ case 'run:execute':
41
+ case 'run:cancel':
42
+ return canAccessProject(config, projectAlias, chatId, 'operator');
43
+ case 'project:mutate':
44
+ return canAccessProject(config, projectAlias, chatId, 'admin');
45
+ }
46
+ }
47
+ export function canAccessGlobalCapability(config, chatId, capability) {
48
+ const overrideList = resolveGlobalCapabilityList(config, capability);
49
+ if (hasEntries(overrideList)) {
50
+ const scopedList = overrideList;
51
+ if (scopedList.includes(chatId)) {
52
+ return true;
53
+ }
54
+ if (capability === 'service:restart' || capability === 'config:history' || capability === 'config:rollback' || capability === 'config:mutate') {
55
+ const globalRole = resolveGlobalRole(config, chatId);
56
+ return globalRole !== null && roleRank(globalRole) >= roleRank('admin');
57
+ }
58
+ return false;
59
+ }
60
+ const globalRole = resolveGlobalRole(config, chatId);
61
+ switch (capability) {
62
+ case 'service:status':
63
+ case 'service:runs':
64
+ return globalRole !== null && roleRank(globalRole) >= roleRank('operator');
65
+ case 'service:restart':
66
+ case 'config:history':
67
+ case 'config:rollback':
68
+ case 'config:mutate':
69
+ return globalRole !== null && roleRank(globalRole) >= roleRank('admin');
70
+ }
71
+ }
72
+ export function describeMinimumRole(role) {
73
+ switch (role) {
74
+ case 'viewer':
75
+ return 'viewer';
76
+ case 'operator':
77
+ return 'operator';
78
+ case 'admin':
79
+ return 'admin';
80
+ }
81
+ }
82
+ export function hasAccessGuard(config, projectAlias) {
83
+ return (hasEntries(config.security.viewer_chat_ids) ||
84
+ hasEntries(config.security.operator_chat_ids) ||
85
+ hasEntries(config.security.admin_chat_ids) ||
86
+ hasEntries(config.projects[projectAlias]?.viewer_chat_ids) ||
87
+ hasEntries(config.projects[projectAlias]?.operator_chat_ids) ||
88
+ hasEntries(config.projects[projectAlias]?.admin_chat_ids));
89
+ }
90
+ export function hasProjectCapabilityOverride(config, projectAlias, capability) {
91
+ const list = resolveProjectCapabilityList(config, projectAlias, capability);
92
+ return Array.isArray(list) && list.length > 0;
93
+ }
94
+ export function hasGlobalCapabilityOverride(config, capability) {
95
+ const list = resolveGlobalCapabilityList(config, capability);
96
+ return Array.isArray(list) && list.length > 0;
97
+ }
98
+ function resolveGlobalRole(config, chatId) {
99
+ return resolveScopedRole(config.security.viewer_chat_ids, config.security.operator_chat_ids, config.security.admin_chat_ids, chatId);
100
+ }
101
+ function resolveProjectCapabilityList(config, projectAlias, capability) {
102
+ const project = config.projects[projectAlias];
103
+ if (!project) {
104
+ return undefined;
105
+ }
106
+ switch (capability) {
107
+ case 'session:control':
108
+ return project.session_operator_chat_ids;
109
+ case 'run:execute':
110
+ case 'run:cancel':
111
+ return project.run_operator_chat_ids;
112
+ case 'project:mutate':
113
+ return project.config_admin_chat_ids;
114
+ default:
115
+ return undefined;
116
+ }
117
+ }
118
+ function resolveGlobalCapabilityList(config, capability) {
119
+ switch (capability) {
120
+ case 'service:status':
121
+ case 'service:runs':
122
+ return config.security.service_observer_chat_ids;
123
+ case 'service:restart':
124
+ return config.security.service_restart_chat_ids;
125
+ case 'config:history':
126
+ case 'config:rollback':
127
+ case 'config:mutate':
128
+ return config.security.config_admin_chat_ids;
129
+ }
130
+ }
131
+ function resolveScopedRole(viewerChatIds, operatorChatIds, adminChatIds, chatId) {
132
+ if (adminChatIds?.includes(chatId)) {
133
+ return 'admin';
134
+ }
135
+ if (operatorChatIds?.includes(chatId)) {
136
+ return 'operator';
137
+ }
138
+ if (viewerChatIds?.includes(chatId)) {
139
+ return 'viewer';
140
+ }
141
+ return null;
142
+ }
143
+ function maxRole(roles) {
144
+ return roles.reduce((best, current) => {
145
+ if (!current) {
146
+ return best;
147
+ }
148
+ if (!best) {
149
+ return current;
150
+ }
151
+ return roleRank(current) > roleRank(best) ? current : best;
152
+ }, null);
153
+ }
154
+ function roleRank(role) {
155
+ return ROLE_ORDER.indexOf(role);
156
+ }
157
+ function hasEntries(value) {
158
+ return Array.isArray(value) && value.length > 0;
159
+ }
160
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/security/access.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { type ServiceDescriptor } from './templates.js';
2
+ export interface InstallServiceOptions {
3
+ serviceName: string;
4
+ cliScriptPath: string;
5
+ nodeBinaryPath: string;
6
+ workingDirectory: string;
7
+ configPath?: string;
8
+ logDirectory?: string;
9
+ platform?: NodeJS.Platform;
10
+ }
11
+ export declare function resolveDefaultLogDirectory(): string;
12
+ export declare function installServiceFile(options: InstallServiceOptions): Promise<ServiceDescriptor>;
13
+ export declare function uninstallServiceFile(options: {
14
+ serviceName: string;
15
+ platform?: NodeJS.Platform;
16
+ }): Promise<{
17
+ removed: boolean;
18
+ targetPath: string;
19
+ }>;
@@ -0,0 +1,35 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import { ensureDir, fileExists, writeUtf8Atomic } from '../utils/fs.js';
5
+ import { buildServiceDescriptor } from './templates.js';
6
+ export function resolveDefaultLogDirectory() {
7
+ return path.join(os.homedir(), '.feique', 'logs');
8
+ }
9
+ export async function installServiceFile(options) {
10
+ const logDirectory = options.logDirectory ?? resolveDefaultLogDirectory();
11
+ const descriptor = buildServiceDescriptor({
12
+ ...options,
13
+ logDirectory,
14
+ });
15
+ await ensureDir(path.dirname(descriptor.targetPath));
16
+ await ensureDir(logDirectory);
17
+ await writeUtf8Atomic(descriptor.targetPath, descriptor.content);
18
+ return descriptor;
19
+ }
20
+ export async function uninstallServiceFile(options) {
21
+ const descriptor = buildServiceDescriptor({
22
+ serviceName: options.serviceName,
23
+ cliScriptPath: process.argv[1] ?? 'dist/cli.js',
24
+ nodeBinaryPath: process.execPath,
25
+ workingDirectory: process.cwd(),
26
+ logDirectory: resolveDefaultLogDirectory(),
27
+ platform: options.platform,
28
+ });
29
+ const exists = await fileExists(descriptor.targetPath);
30
+ if (exists) {
31
+ await fs.rm(descriptor.targetPath, { force: true });
32
+ }
33
+ return { removed: exists, targetPath: descriptor.targetPath };
34
+ }
35
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/service/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAA0B,MAAM,gBAAgB,CAAC;AAYhF,MAAM,UAAU,0BAA0B;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAA8B;IACrE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,0BAA0B,EAAE,CAAC;IAC1E,MAAM,UAAU,GAAG,sBAAsB,CAAC;QACxC,GAAG,OAAO;QACV,YAAY;KACb,CAAC,CAAC;IAEH,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACrD,MAAM,SAAS,CAAC,YAAY,CAAC,CAAC;IAC9B,MAAM,eAAe,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACjE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAA4D;IACrG,MAAM,UAAU,GAAG,sBAAsB,CAAC;QACxC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,aAAa;QAC/C,cAAc,EAAE,OAAO,CAAC,QAAQ;QAChC,gBAAgB,EAAE,OAAO,CAAC,GAAG,EAAE;QAC/B,YAAY,EAAE,0BAA0B,EAAE;QAC1C,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACvD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,CAAC;AAChE,CAAC"}
@@ -0,0 +1,22 @@
1
+ export type ServicePlatform = 'darwin' | 'linux';
2
+ export interface ServiceDescriptor {
3
+ platform: ServicePlatform;
4
+ serviceName: string;
5
+ targetPath: string;
6
+ content: string;
7
+ installHint: string;
8
+ uninstallHint: string;
9
+ startHint: string;
10
+ stopHint: string;
11
+ statusHint: string;
12
+ }
13
+ export interface ServiceTemplateInput {
14
+ serviceName: string;
15
+ cliScriptPath: string;
16
+ nodeBinaryPath: string;
17
+ workingDirectory: string;
18
+ configPath?: string;
19
+ logDirectory: string;
20
+ platform?: NodeJS.Platform;
21
+ }
22
+ export declare function buildServiceDescriptor(input: ServiceTemplateInput): ServiceDescriptor;
@@ -0,0 +1,118 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ export function buildServiceDescriptor(input) {
4
+ const platform = normalizePlatform(input.platform ?? process.platform);
5
+ if (platform === 'darwin') {
6
+ return buildLaunchdDescriptor(input);
7
+ }
8
+ if (platform === 'linux') {
9
+ return buildSystemdDescriptor(input);
10
+ }
11
+ throw new Error(`Unsupported platform: ${input.platform ?? process.platform}`);
12
+ }
13
+ function buildLaunchdDescriptor(input) {
14
+ const serviceName = input.serviceName;
15
+ const launchAgentPath = path.join(os.homedir(), 'Library', 'LaunchAgents', `${serviceName}.plist`);
16
+ const args = buildServeArgs(input);
17
+ const stdoutPath = path.join(input.logDirectory, `${serviceName}.out.log`);
18
+ const stderrPath = path.join(input.logDirectory, `${serviceName}.err.log`);
19
+ const content = `<?xml version="1.0" encoding="UTF-8"?>
20
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
21
+ <plist version="1.0">
22
+ <dict>
23
+ <key>Label</key>
24
+ <string>${escapeXml(serviceName)}</string>
25
+ <key>ProgramArguments</key>
26
+ <array>
27
+ ${args.map((arg) => ` <string>${escapeXml(arg)}</string>`).join('\n')}
28
+ </array>
29
+ <key>WorkingDirectory</key>
30
+ <string>${escapeXml(input.workingDirectory)}</string>
31
+ <key>RunAtLoad</key>
32
+ <true/>
33
+ <key>KeepAlive</key>
34
+ <true/>
35
+ <key>StandardOutPath</key>
36
+ <string>${escapeXml(stdoutPath)}</string>
37
+ <key>StandardErrorPath</key>
38
+ <string>${escapeXml(stderrPath)}</string>
39
+ <key>ProcessType</key>
40
+ <string>Interactive</string>
41
+ </dict>
42
+ </plist>
43
+ `;
44
+ return {
45
+ platform: 'darwin',
46
+ serviceName,
47
+ targetPath: launchAgentPath,
48
+ content,
49
+ installHint: `launchctl bootstrap gui/$(id -u) ${shellQuote(launchAgentPath)}`,
50
+ uninstallHint: `launchctl bootout gui/$(id -u) ${shellQuote(launchAgentPath)} && rm -f ${shellQuote(launchAgentPath)}`,
51
+ startHint: `launchctl kickstart -k gui/$(id -u)/${serviceName}`,
52
+ stopHint: `launchctl bootout gui/$(id -u)/${serviceName}`,
53
+ statusHint: `launchctl print gui/$(id -u)/${serviceName}`,
54
+ };
55
+ }
56
+ function buildSystemdDescriptor(input) {
57
+ const serviceName = input.serviceName;
58
+ const unitPath = path.join(os.homedir(), '.config', 'systemd', 'user', `${serviceName}.service`);
59
+ const args = buildServeArgs(input).map(shellQuote).join(' ');
60
+ const stdoutPath = path.join(input.logDirectory, `${serviceName}.out.log`);
61
+ const stderrPath = path.join(input.logDirectory, `${serviceName}.err.log`);
62
+ const content = `[Unit]
63
+ Description=飞鹊 (Feique)
64
+ After=network-online.target
65
+ Wants=network-online.target
66
+
67
+ [Service]
68
+ Type=simple
69
+ WorkingDirectory=${input.workingDirectory}
70
+ ExecStart=${args}
71
+ Restart=always
72
+ RestartSec=3
73
+ StandardOutput=append:${stdoutPath}
74
+ StandardError=append:${stderrPath}
75
+
76
+ [Install]
77
+ WantedBy=default.target
78
+ `;
79
+ return {
80
+ platform: 'linux',
81
+ serviceName,
82
+ targetPath: unitPath,
83
+ content,
84
+ installHint: `systemctl --user daemon-reload && systemctl --user enable --now ${serviceName}.service`,
85
+ uninstallHint: `systemctl --user disable --now ${serviceName}.service && rm -f ${shellQuote(unitPath)} && systemctl --user daemon-reload`,
86
+ startHint: `systemctl --user start ${serviceName}.service`,
87
+ stopHint: `systemctl --user stop ${serviceName}.service`,
88
+ statusHint: `systemctl --user status ${serviceName}.service`,
89
+ };
90
+ }
91
+ function buildServeArgs(input) {
92
+ const args = [input.nodeBinaryPath, input.cliScriptPath, 'serve'];
93
+ if (input.configPath) {
94
+ args.push('--config', input.configPath);
95
+ }
96
+ return args;
97
+ }
98
+ function normalizePlatform(platform) {
99
+ if (platform === 'darwin' || platform === 'linux') {
100
+ return platform;
101
+ }
102
+ throw new Error(`Unsupported platform: ${platform}`);
103
+ }
104
+ function escapeXml(input) {
105
+ return input
106
+ .replaceAll('&', '&amp;')
107
+ .replaceAll('<', '&lt;')
108
+ .replaceAll('>', '&gt;')
109
+ .replaceAll('"', '&quot;')
110
+ .replaceAll("'", '&apos;');
111
+ }
112
+ function shellQuote(input) {
113
+ if (/^[A-Za-z0-9_./:-]+$/.test(input)) {
114
+ return input;
115
+ }
116
+ return `'${input.replaceAll("'", `'"'"'`)}'`;
117
+ }
118
+ //# sourceMappingURL=templates.js.map