@abdasis/agentdeck 2.44.2

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 (196) hide show
  1. package/README.md +122 -0
  2. package/agent/dist/config.d.ts +31 -0
  3. package/agent/dist/config.d.ts.map +1 -0
  4. package/agent/dist/config.js +81 -0
  5. package/agent/dist/config.js.map +1 -0
  6. package/agent/dist/db/commands.d.ts +25 -0
  7. package/agent/dist/db/commands.d.ts.map +1 -0
  8. package/agent/dist/db/commands.js +93 -0
  9. package/agent/dist/db/commands.js.map +1 -0
  10. package/agent/dist/db/index.d.ts +30 -0
  11. package/agent/dist/db/index.d.ts.map +1 -0
  12. package/agent/dist/db/index.js +368 -0
  13. package/agent/dist/db/index.js.map +1 -0
  14. package/agent/dist/db/schema.d.ts +74 -0
  15. package/agent/dist/db/schema.d.ts.map +1 -0
  16. package/agent/dist/db/schema.js +100 -0
  17. package/agent/dist/db/schema.js.map +1 -0
  18. package/agent/dist/db/sessions.d.ts +46 -0
  19. package/agent/dist/db/sessions.d.ts.map +1 -0
  20. package/agent/dist/db/sessions.js +198 -0
  21. package/agent/dist/db/sessions.js.map +1 -0
  22. package/agent/dist/index.d.ts +2 -0
  23. package/agent/dist/index.d.ts.map +1 -0
  24. package/agent/dist/index.js +19 -0
  25. package/agent/dist/index.js.map +1 -0
  26. package/agent/dist/lib/command-session.d.ts +20 -0
  27. package/agent/dist/lib/command-session.d.ts.map +1 -0
  28. package/agent/dist/lib/command-session.js +64 -0
  29. package/agent/dist/lib/command-session.js.map +1 -0
  30. package/agent/dist/lib/git.d.ts +8 -0
  31. package/agent/dist/lib/git.d.ts.map +1 -0
  32. package/agent/dist/lib/git.js +194 -0
  33. package/agent/dist/lib/git.js.map +1 -0
  34. package/agent/dist/lib/init-script.d.ts +33 -0
  35. package/agent/dist/lib/init-script.d.ts.map +1 -0
  36. package/agent/dist/lib/init-script.js +523 -0
  37. package/agent/dist/lib/init-script.js.map +1 -0
  38. package/agent/dist/lib/transcript.d.ts +16 -0
  39. package/agent/dist/lib/transcript.d.ts.map +1 -0
  40. package/agent/dist/lib/transcript.js +64 -0
  41. package/agent/dist/lib/transcript.js.map +1 -0
  42. package/agent/dist/logger.d.ts +12 -0
  43. package/agent/dist/logger.d.ts.map +1 -0
  44. package/agent/dist/logger.js +39 -0
  45. package/agent/dist/logger.js.map +1 -0
  46. package/agent/dist/middleware/auth.d.ts +15 -0
  47. package/agent/dist/middleware/auth.d.ts.map +1 -0
  48. package/agent/dist/middleware/auth.js +55 -0
  49. package/agent/dist/middleware/auth.js.map +1 -0
  50. package/agent/dist/routes/commands.d.ts +8 -0
  51. package/agent/dist/routes/commands.d.ts.map +1 -0
  52. package/agent/dist/routes/commands.js +229 -0
  53. package/agent/dist/routes/commands.js.map +1 -0
  54. package/agent/dist/routes/files.d.ts +7 -0
  55. package/agent/dist/routes/files.d.ts.map +1 -0
  56. package/agent/dist/routes/files.js +206 -0
  57. package/agent/dist/routes/files.js.map +1 -0
  58. package/agent/dist/routes/hooks.d.ts +7 -0
  59. package/agent/dist/routes/hooks.d.ts.map +1 -0
  60. package/agent/dist/routes/hooks.js +147 -0
  61. package/agent/dist/routes/hooks.js.map +1 -0
  62. package/agent/dist/routes/index.d.ts +10 -0
  63. package/agent/dist/routes/index.d.ts.map +1 -0
  64. package/agent/dist/routes/index.js +10 -0
  65. package/agent/dist/routes/index.js.map +1 -0
  66. package/agent/dist/routes/pair.d.ts +26 -0
  67. package/agent/dist/routes/pair.d.ts.map +1 -0
  68. package/agent/dist/routes/pair.js +384 -0
  69. package/agent/dist/routes/pair.js.map +1 -0
  70. package/agent/dist/routes/projects.d.ts +6 -0
  71. package/agent/dist/routes/projects.d.ts.map +1 -0
  72. package/agent/dist/routes/projects.js +121 -0
  73. package/agent/dist/routes/projects.js.map +1 -0
  74. package/agent/dist/routes/sessions-git.d.ts +7 -0
  75. package/agent/dist/routes/sessions-git.d.ts.map +1 -0
  76. package/agent/dist/routes/sessions-git.js +31 -0
  77. package/agent/dist/routes/sessions-git.js.map +1 -0
  78. package/agent/dist/routes/sessions-tmux.d.ts +7 -0
  79. package/agent/dist/routes/sessions-tmux.d.ts.map +1 -0
  80. package/agent/dist/routes/sessions-tmux.js +94 -0
  81. package/agent/dist/routes/sessions-tmux.js.map +1 -0
  82. package/agent/dist/routes/sessions.d.ts +7 -0
  83. package/agent/dist/routes/sessions.d.ts.map +1 -0
  84. package/agent/dist/routes/sessions.js +172 -0
  85. package/agent/dist/routes/sessions.js.map +1 -0
  86. package/agent/dist/server.d.ts +6 -0
  87. package/agent/dist/server.d.ts.map +1 -0
  88. package/agent/dist/server.js +95 -0
  89. package/agent/dist/server.js.map +1 -0
  90. package/agent/dist/terminal.d.ts +19 -0
  91. package/agent/dist/terminal.d.ts.map +1 -0
  92. package/agent/dist/terminal.js +175 -0
  93. package/agent/dist/terminal.js.map +1 -0
  94. package/agent/dist/updater.d.ts +18 -0
  95. package/agent/dist/updater.d.ts.map +1 -0
  96. package/agent/dist/updater.js +122 -0
  97. package/agent/dist/updater.js.map +1 -0
  98. package/agent/dist/version.d.ts +20 -0
  99. package/agent/dist/version.d.ts.map +1 -0
  100. package/agent/dist/version.js +66 -0
  101. package/agent/dist/version.js.map +1 -0
  102. package/agent/dist/websocket-handlers.d.ts +38 -0
  103. package/agent/dist/websocket-handlers.d.ts.map +1 -0
  104. package/agent/dist/websocket-handlers.js +389 -0
  105. package/agent/dist/websocket-handlers.js.map +1 -0
  106. package/agent/node_modules/@agentdeck/shared/dist/index.d.ts +2 -0
  107. package/agent/node_modules/@agentdeck/shared/dist/index.d.ts.map +1 -0
  108. package/agent/node_modules/@agentdeck/shared/dist/index.js +2 -0
  109. package/agent/node_modules/@agentdeck/shared/dist/index.js.map +1 -0
  110. package/agent/node_modules/@agentdeck/shared/dist/types/index.d.ts +235 -0
  111. package/agent/node_modules/@agentdeck/shared/dist/types/index.d.ts.map +1 -0
  112. package/agent/node_modules/@agentdeck/shared/dist/types/index.js +5 -0
  113. package/agent/node_modules/@agentdeck/shared/dist/types/index.js.map +1 -0
  114. package/agent/node_modules/@agentdeck/shared/package.json +29 -0
  115. package/dist/commands/doctor.d.ts +3 -0
  116. package/dist/commands/doctor.d.ts.map +1 -0
  117. package/dist/commands/doctor.js +304 -0
  118. package/dist/commands/doctor.js.map +1 -0
  119. package/dist/commands/hooks.d.ts +3 -0
  120. package/dist/commands/hooks.d.ts.map +1 -0
  121. package/dist/commands/hooks.js +187 -0
  122. package/dist/commands/hooks.js.map +1 -0
  123. package/dist/commands/init.d.ts +3 -0
  124. package/dist/commands/init.d.ts.map +1 -0
  125. package/dist/commands/init.js +109 -0
  126. package/dist/commands/init.js.map +1 -0
  127. package/dist/commands/logs.d.ts +3 -0
  128. package/dist/commands/logs.d.ts.map +1 -0
  129. package/dist/commands/logs.js +38 -0
  130. package/dist/commands/logs.js.map +1 -0
  131. package/dist/commands/profile.d.ts +3 -0
  132. package/dist/commands/profile.d.ts.map +1 -0
  133. package/dist/commands/profile.js +156 -0
  134. package/dist/commands/profile.js.map +1 -0
  135. package/dist/commands/service.d.ts +3 -0
  136. package/dist/commands/service.d.ts.map +1 -0
  137. package/dist/commands/service.js +235 -0
  138. package/dist/commands/service.js.map +1 -0
  139. package/dist/commands/start.d.ts +3 -0
  140. package/dist/commands/start.d.ts.map +1 -0
  141. package/dist/commands/start.js +122 -0
  142. package/dist/commands/start.js.map +1 -0
  143. package/dist/commands/status.d.ts +3 -0
  144. package/dist/commands/status.d.ts.map +1 -0
  145. package/dist/commands/status.js +48 -0
  146. package/dist/commands/status.js.map +1 -0
  147. package/dist/commands/stop.d.ts +3 -0
  148. package/dist/commands/stop.d.ts.map +1 -0
  149. package/dist/commands/stop.js +23 -0
  150. package/dist/commands/stop.js.map +1 -0
  151. package/dist/commands/update.d.ts +3 -0
  152. package/dist/commands/update.d.ts.map +1 -0
  153. package/dist/commands/update.js +129 -0
  154. package/dist/commands/update.js.map +1 -0
  155. package/dist/commands/version.d.ts +3 -0
  156. package/dist/commands/version.d.ts.map +1 -0
  157. package/dist/commands/version.js +39 -0
  158. package/dist/commands/version.js.map +1 -0
  159. package/dist/index.d.ts +5 -0
  160. package/dist/index.d.ts.map +1 -0
  161. package/dist/index.js +45 -0
  162. package/dist/index.js.map +1 -0
  163. package/dist/lib/config.d.ts +68 -0
  164. package/dist/lib/config.d.ts.map +1 -0
  165. package/dist/lib/config.js +156 -0
  166. package/dist/lib/config.js.map +1 -0
  167. package/dist/lib/hooks.d.ts +67 -0
  168. package/dist/lib/hooks.d.ts.map +1 -0
  169. package/dist/lib/hooks.js +353 -0
  170. package/dist/lib/hooks.js.map +1 -0
  171. package/dist/lib/paths.d.ts +36 -0
  172. package/dist/lib/paths.d.ts.map +1 -0
  173. package/dist/lib/paths.js +77 -0
  174. package/dist/lib/paths.js.map +1 -0
  175. package/dist/lib/prerequisites.d.ts +60 -0
  176. package/dist/lib/prerequisites.d.ts.map +1 -0
  177. package/dist/lib/prerequisites.js +266 -0
  178. package/dist/lib/prerequisites.js.map +1 -0
  179. package/dist/lib/process.d.ts +40 -0
  180. package/dist/lib/process.d.ts.map +1 -0
  181. package/dist/lib/process.js +192 -0
  182. package/dist/lib/process.js.map +1 -0
  183. package/dist/service/index.d.ts +44 -0
  184. package/dist/service/index.d.ts.map +1 -0
  185. package/dist/service/index.js +18 -0
  186. package/dist/service/index.js.map +1 -0
  187. package/dist/service/launchd.d.ts +18 -0
  188. package/dist/service/launchd.d.ts.map +1 -0
  189. package/dist/service/launchd.js +208 -0
  190. package/dist/service/launchd.js.map +1 -0
  191. package/dist/service/systemd.d.ts +18 -0
  192. package/dist/service/systemd.d.ts.map +1 -0
  193. package/dist/service/systemd.js +196 -0
  194. package/dist/service/systemd.js.map +1 -0
  195. package/hooks/notify-agentdeck.sh +53 -0
  196. package/package.json +83 -0
@@ -0,0 +1,39 @@
1
+ import pino from 'pino';
2
+ // Get log level from environment or default to 'info'
3
+ const level = process.env.LOG_LEVEL || 'info';
4
+ // Check if we're in development mode (for pretty printing)
5
+ const isDev = process.env.NODE_ENV !== 'production';
6
+ // Create base logger
7
+ const baseLogger = pino({
8
+ level,
9
+ ...(isDev && {
10
+ transport: {
11
+ target: 'pino-pretty',
12
+ options: {
13
+ colorize: true,
14
+ translateTime: 'HH:MM:ss',
15
+ ignore: 'pid,hostname',
16
+ },
17
+ },
18
+ }),
19
+ });
20
+ // Create child loggers for each module
21
+ export const logger = {
22
+ // Main logger for generic messages
23
+ main: baseLogger,
24
+ // Server/API related logs
25
+ server: baseLogger.child({ module: 'Server' }),
26
+ // Database related logs
27
+ db: baseLogger.child({ module: 'DB' }),
28
+ // Session management logs
29
+ session: baseLogger.child({ module: 'Session' }),
30
+ // Terminal/PTY related logs
31
+ terminal: baseLogger.child({ module: 'Terminal' }),
32
+ // Environment management logs
33
+ env: baseLogger.child({ module: 'Environments' }),
34
+ // Connection management logs
35
+ connections: baseLogger.child({ module: 'Connections' }),
36
+ // Hook status logs
37
+ hooks: baseLogger.child({ module: 'Hooks' }),
38
+ };
39
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,sDAAsD;AACtD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC;AAE9C,2DAA2D;AAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AAEpD,qBAAqB;AACrB,MAAM,UAAU,GAAG,IAAI,CAAC;IACtB,KAAK;IACL,GAAG,CAAC,KAAK,IAAI;QACX,SAAS,EAAE;YACT,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE;gBACP,QAAQ,EAAE,IAAI;gBACd,aAAa,EAAE,UAAU;gBACzB,MAAM,EAAE,cAAc;aACvB;SACF;KACF,CAAC;CACH,CAAC,CAAC;AAEH,uCAAuC;AACvC,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,mCAAmC;IACnC,IAAI,EAAE,UAAU;IAEhB,0BAA0B;IAC1B,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAE9C,wBAAwB;IACxB,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAEtC,0BAA0B;IAC1B,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAEhD,4BAA4B;IAC5B,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAElD,8BAA8B;IAC9B,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAEjD,6BAA6B;IAC7B,WAAW,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAExD,mBAAmB;IACnB,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;CAC7C,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Request, Response, NextFunction } from 'express';
2
+ /**
3
+ * Validasi token terhadap accessToken agent. True bila agent belum set
4
+ * accessToken (opt-in, install lama) ATAU token cocok. Dipakai middleware HTTP
5
+ * dan handshake WS upgrade.
6
+ */
7
+ export declare function authenticateToken(provided: string | undefined): boolean;
8
+ /**
9
+ * Auth middleware per-machine. Bila agent belum punya accessToken (install
10
+ * lama pre-auth) → allow semua (opt-in). Bila accessToken ada → wajib token
11
+ * yang cocok. Dipasang di router projects + sessions. Pairing/health/hooks
12
+ * tidak pakai middleware ini.
13
+ */
14
+ export declare function authMiddleware(req: Request, res: Response, next: NextFunction): void;
15
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAiC/D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIvE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAMpF"}
@@ -0,0 +1,55 @@
1
+ import { timingSafeEqual } from 'crypto';
2
+ import { getAccessToken } from '../config.js';
3
+ /**
4
+ * Bandingkan dua string constant-time. Panjang beda → tetap bandingkan ke
5
+ * buffer sepanjang expected lalu return false (hindari length leak via early
6
+ * return).
7
+ */
8
+ function safeEqual(a, b) {
9
+ const aBuf = Buffer.from(a);
10
+ const bBuf = Buffer.from(b);
11
+ if (aBuf.length !== bBuf.length) {
12
+ // Bandingkan aBuf dengan dirinya sendiri agar waktu tetap konsisten.
13
+ timingSafeEqual(aBuf, aBuf);
14
+ return false;
15
+ }
16
+ return timingSafeEqual(aBuf, bBuf);
17
+ }
18
+ /**
19
+ * Ambil token dari query `?token=` atau header `Authorization: Bearer ...`.
20
+ */
21
+ function extractToken(req) {
22
+ const q = req.query?.token;
23
+ if (typeof q === 'string' && q)
24
+ return q;
25
+ const auth = req.headers.authorization;
26
+ if (typeof auth === 'string' && auth.toLowerCase().startsWith('bearer ')) {
27
+ return auth.slice(7).trim();
28
+ }
29
+ return undefined;
30
+ }
31
+ /**
32
+ * Validasi token terhadap accessToken agent. True bila agent belum set
33
+ * accessToken (opt-in, install lama) ATAU token cocok. Dipakai middleware HTTP
34
+ * dan handshake WS upgrade.
35
+ */
36
+ export function authenticateToken(provided) {
37
+ const expected = getAccessToken();
38
+ if (!expected)
39
+ return true;
40
+ return !!provided && safeEqual(provided, expected);
41
+ }
42
+ /**
43
+ * Auth middleware per-machine. Bila agent belum punya accessToken (install
44
+ * lama pre-auth) → allow semua (opt-in). Bila accessToken ada → wajib token
45
+ * yang cocok. Dipasang di router projects + sessions. Pairing/health/hooks
46
+ * tidak pakai middleware ini.
47
+ */
48
+ export function authMiddleware(req, res, next) {
49
+ if (authenticateToken(extractToken(req))) {
50
+ next();
51
+ return;
52
+ }
53
+ res.status(401).json({ error: 'Unauthorized: invalid or missing access token' });
54
+ }
55
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;GAIG;AACH,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS;IACrC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,qEAAqE;QACrE,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,GAAY;IAChC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC;IAC3B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACvC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACzE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAA4B;IAC5D,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO,CAAC,CAAC,QAAQ,IAAI,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC5E,IAAI,iBAAiB,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACzC,IAAI,EAAE,CAAC;QACP,OAAO;IACT,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Project commands API: CRUD definitions + start/stop/restart running commands.
3
+ * Definition disimpan di SQLite project_commands; running instance = tmux session
4
+ * + sessions row ber-command_id.
5
+ */
6
+ import { Router } from 'express';
7
+ export declare function createCommandRoutes(): Router;
8
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/routes/commands.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAiEjC,wBAAgB,mBAAmB,IAAI,MAAM,CA+K5C"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Project commands API: CRUD definitions + start/stop/restart running commands.
3
+ * Definition disimpan di SQLite project_commands; running instance = tmux session
4
+ * + sessions row ber-command_id.
5
+ */
6
+ import { Router } from 'express';
7
+ import * as path from 'path';
8
+ import { config } from '../config.js';
9
+ import * as commandsDb from '../db/commands.js';
10
+ import { insertCommandSession, deleteSession } from '../db/sessions.js';
11
+ import { generateCommandSessionName, spawnCommandSession, killCommandSession, hasCommandSession, } from '../lib/command-session.js';
12
+ import { broadcastSessionsList, broadcastSessionRemoved } from '../websocket-handlers.js';
13
+ import { authMiddleware } from '../middleware/auth.js';
14
+ function resolveBasePath() {
15
+ return config.projects.basePath.replace('~', process.env.HOME || '~');
16
+ }
17
+ /** Path absolut folder project; null jika di luar basePath (path traversal). */
18
+ function projectPath(project) {
19
+ const basePath = resolveBasePath();
20
+ const full = path.join(basePath, project);
21
+ const rel = path.relative(basePath, full);
22
+ if (rel.startsWith('..') || path.isAbsolute(rel))
23
+ return null;
24
+ return full;
25
+ }
26
+ /** Resolve cwd: default = folder project; null jika di luar project path. */
27
+ function resolveCwd(project, cwdOverride) {
28
+ const base = projectPath(project);
29
+ if (!base)
30
+ return null;
31
+ if (!cwdOverride)
32
+ return base;
33
+ const full = path.isAbsolute(cwdOverride) ? cwdOverride : path.join(base, cwdOverride);
34
+ const rel = path.relative(base, full);
35
+ if (rel.startsWith('..'))
36
+ return null;
37
+ return full;
38
+ }
39
+ function toWire(cmd) {
40
+ if (!cmd)
41
+ return null;
42
+ const runState = {
43
+ sessionName: cmd.runState.sessionName,
44
+ status: cmd.runState.status ?? null,
45
+ lastActivity: cmd.runState.lastActivity,
46
+ };
47
+ return {
48
+ id: cmd.id,
49
+ project: cmd.project,
50
+ name: cmd.name,
51
+ command: cmd.command,
52
+ cwd: cmd.cwd,
53
+ position: cmd.position,
54
+ createdAt: cmd.created_at,
55
+ updatedAt: cmd.updated_at,
56
+ runState,
57
+ };
58
+ }
59
+ export function createCommandRoutes() {
60
+ const router = Router();
61
+ router.use(authMiddleware);
62
+ // ponytail: in-memory lock per command-id, cegah race dua POST /start bersamaan
63
+ // (check-then-spawn). Cek+add sync tanpa await = atomic di event loop. Cukup
64
+ // untuk single-user; jika multi-instance, ganti dengan mutex per-process / DB unique.
65
+ const startingCommands = new Set();
66
+ // List definitions + run state for a project
67
+ router.get('/projects/:project/commands', (req, res) => {
68
+ const { project } = req.params;
69
+ const list = commandsDb
70
+ .listCommandsWithRunState(project)
71
+ .map(toWire)
72
+ .filter((c) => c !== null);
73
+ res.json(list);
74
+ });
75
+ // Create definition
76
+ router.post('/projects/:project/commands', (req, res) => {
77
+ const { project } = req.params;
78
+ if (!projectPath(project)) {
79
+ return res.status(400).json({ error: 'Project folder not found' });
80
+ }
81
+ const { name, command, cwd } = req.body ?? {};
82
+ if (!name || !command) {
83
+ return res.status(400).json({ error: 'name and command are required' });
84
+ }
85
+ if (cwd !== undefined && cwd !== null && cwd !== '' && !resolveCwd(project, cwd)) {
86
+ return res.status(400).json({ error: 'cwd must be inside project base path' });
87
+ }
88
+ try {
89
+ const created = commandsDb.createCommand(project, { name, command, cwd: cwd || null });
90
+ res.status(201).json(toWire(commandsDb.getCommandWithRunState(created.id)));
91
+ }
92
+ catch (err) {
93
+ // SQLITE_CONSTRAINT_UNIQUE = nama duplikat; error lain (mis. tabel hilang)
94
+ // jangan disamarkan sebagai "already exists" — kasih pesan asli.
95
+ if (err instanceof Error && /UNIQUE constraint/i.test(err.message)) {
96
+ res.status(409).json({ error: 'Command name already exists for this project' });
97
+ }
98
+ else {
99
+ console.error('[commands] create failed:', err);
100
+ res.status(500).json({ error: 'Failed to save command' });
101
+ }
102
+ }
103
+ });
104
+ // Update definition
105
+ router.put('/commands/:id', (req, res) => {
106
+ const id = Number(req.params.id);
107
+ const cmd = commandsDb.getCommand(id);
108
+ if (!cmd)
109
+ return res.status(404).json({ error: 'Command not found' });
110
+ const { name, command, cwd, position } = req.body ?? {};
111
+ if (cwd !== undefined && cwd !== null && cwd !== '' && !resolveCwd(cmd.project, cwd)) {
112
+ return res.status(400).json({ error: 'cwd must be inside project base path' });
113
+ }
114
+ try {
115
+ commandsDb.updateCommand(id, {
116
+ name,
117
+ command,
118
+ cwd: cwd === undefined ? undefined : cwd || null,
119
+ position,
120
+ });
121
+ res.json(toWire(commandsDb.getCommandWithRunState(id)));
122
+ }
123
+ catch (err) {
124
+ if (err instanceof Error && /UNIQUE constraint/i.test(err.message)) {
125
+ res.status(409).json({ error: 'Command name already exists for this project' });
126
+ }
127
+ else {
128
+ console.error('[commands] update failed:', err);
129
+ res.status(500).json({ error: 'Failed to save command' });
130
+ }
131
+ }
132
+ });
133
+ // Delete definition (stop running instance first)
134
+ router.delete('/commands/:id', async (req, res) => {
135
+ const id = Number(req.params.id);
136
+ const cmd = commandsDb.getCommand(id);
137
+ if (!cmd)
138
+ return res.status(404).json({ error: 'Command not found' });
139
+ const session = commandsDb.getSessionByCommandId(id);
140
+ if (session) {
141
+ await killCommandSession(session.name);
142
+ deleteSession(session.name);
143
+ broadcastSessionRemoved(session.name);
144
+ }
145
+ commandsDb.deleteCommand(id);
146
+ commandsDb.clearCommandId(id);
147
+ res.json({ success: true });
148
+ });
149
+ // Start
150
+ router.post('/commands/:id/start', async (req, res) => {
151
+ const id = Number(req.params.id);
152
+ const cmd = commandsDb.getCommand(id);
153
+ if (!cmd)
154
+ return res.status(404).json({ error: 'Command not found' });
155
+ if (commandsDb.getSessionByCommandId(id)) {
156
+ return res.status(409).json({ error: 'Already running' });
157
+ }
158
+ if (startingCommands.has(id)) {
159
+ return res.status(409).json({ error: 'Already starting' });
160
+ }
161
+ startingCommands.add(id);
162
+ const cwd = resolveCwd(cmd.project, cmd.cwd);
163
+ if (!cwd) {
164
+ startingCommands.delete(id);
165
+ return res.status(400).json({ error: 'Project folder not found' });
166
+ }
167
+ const sessionName = generateCommandSessionName(cmd.project, cmd.name);
168
+ try {
169
+ await spawnCommandSession(sessionName, cwd, cmd.command);
170
+ }
171
+ catch (err) {
172
+ console.error(`[commands] spawn failed for ${cmd.project}/${cmd.name}:`, err);
173
+ startingCommands.delete(id);
174
+ return res.status(500).json({ error: 'Failed to start command' });
175
+ }
176
+ if (!hasCommandSession(sessionName)) {
177
+ // Command keluar langsung (mis. typo / binary hilang)
178
+ startingCommands.delete(id);
179
+ return res.status(500).json({ error: 'Command exited immediately' });
180
+ }
181
+ insertCommandSession(sessionName, cmd.project, id);
182
+ startingCommands.delete(id);
183
+ await broadcastSessionsList();
184
+ res.json(toWire(commandsDb.getCommandWithRunState(id)));
185
+ });
186
+ // Stop
187
+ router.post('/commands/:id/stop', async (req, res) => {
188
+ const id = Number(req.params.id);
189
+ const session = commandsDb.getSessionByCommandId(id);
190
+ if (!session)
191
+ return res.status(404).json({ error: 'Not running' });
192
+ await killCommandSession(session.name);
193
+ deleteSession(session.name);
194
+ broadcastSessionRemoved(session.name);
195
+ res.json(toWire(commandsDb.getCommandWithRunState(id)));
196
+ });
197
+ // Restart = stop (tolerant) + start
198
+ router.post('/commands/:id/restart', async (req, res) => {
199
+ const id = Number(req.params.id);
200
+ const cmd = commandsDb.getCommand(id);
201
+ if (!cmd)
202
+ return res.status(404).json({ error: 'Command not found' });
203
+ const existing = commandsDb.getSessionByCommandId(id);
204
+ if (existing) {
205
+ await killCommandSession(existing.name);
206
+ deleteSession(existing.name);
207
+ broadcastSessionRemoved(existing.name);
208
+ }
209
+ const cwd = resolveCwd(cmd.project, cmd.cwd);
210
+ if (!cwd)
211
+ return res.status(400).json({ error: 'Project folder not found' });
212
+ const sessionName = generateCommandSessionName(cmd.project, cmd.name);
213
+ try {
214
+ await spawnCommandSession(sessionName, cwd, cmd.command);
215
+ }
216
+ catch (err) {
217
+ console.error('[commands] restart spawn failed:', err);
218
+ return res.status(500).json({ error: 'Failed to restart command' });
219
+ }
220
+ if (!hasCommandSession(sessionName)) {
221
+ return res.status(500).json({ error: 'Command exited immediately' });
222
+ }
223
+ insertCommandSession(sessionName, cmd.project, id);
224
+ await broadcastSessionsList();
225
+ res.json(toWire(commandsDb.getCommandWithRunState(id)));
226
+ });
227
+ return router;
228
+ }
229
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/routes/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * File explorer API: read-only listing direktori + baca isi file per project.
3
+ * Path-traversal guard reuse pola routes/commands.ts. Tidak ada write/delete.
4
+ */
5
+ import { Router } from 'express';
6
+ export declare function createFileRoutes(): Router;
7
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/routes/files.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AA2FjC,wBAAgB,gBAAgB,IAAI,MAAM,CA4GzC"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * File explorer API: read-only listing direktori + baca isi file per project.
3
+ * Path-traversal guard reuse pola routes/commands.ts. Tidak ada write/delete.
4
+ */
5
+ import { Router } from 'express';
6
+ import * as path from 'path';
7
+ import * as fs from 'fs/promises';
8
+ import { config } from '../config.js';
9
+ import { authMiddleware } from '../middleware/auth.js';
10
+ const MAX_TEXT_BYTES = 1024 * 1024; // 1 MB
11
+ const MAX_IMAGE_BYTES = 1024 * 1024; // 1 MB
12
+ // ponytail: skip daftar folder berat/tidak relevan di listing MVP. Toggle nanti.
13
+ const SKIP_DIRS = new Set(['.git', 'node_modules']);
14
+ const MIME_MAP = {
15
+ '.ts': 'text/typescript',
16
+ '.tsx': 'text/typescript',
17
+ '.js': 'text/javascript',
18
+ '.jsx': 'text/javascript',
19
+ '.mjs': 'text/javascript',
20
+ '.cjs': 'text/javascript',
21
+ '.json': 'application/json',
22
+ '.md': 'text/markdown',
23
+ '.markdown': 'text/markdown',
24
+ '.css': 'text/css',
25
+ '.scss': 'text/css',
26
+ '.html': 'text/html',
27
+ '.htm': 'text/html',
28
+ '.xml': 'text/xml',
29
+ '.yml': 'text/yaml',
30
+ '.yaml': 'text/yaml',
31
+ '.toml': 'text/toml',
32
+ '.ini': 'text/plain',
33
+ '.env': 'text/plain',
34
+ '.sh': 'text/x-shellscript',
35
+ '.bash': 'text/x-shellscript',
36
+ '.py': 'text/x-python',
37
+ '.go': 'text/x-go',
38
+ '.rs': 'text/x-rust',
39
+ '.java': 'text/x-java',
40
+ '.php': 'text/x-php',
41
+ '.rb': 'text/x-ruby',
42
+ '.sql': 'text/x-sql',
43
+ '.txt': 'text/plain',
44
+ '.log': 'text/plain',
45
+ '.csv': 'text/csv',
46
+ '.png': 'image/png',
47
+ '.jpg': 'image/jpeg',
48
+ '.jpeg': 'image/jpeg',
49
+ '.gif': 'image/gif',
50
+ '.webp': 'image/webp',
51
+ '.svg': 'image/svg+xml',
52
+ };
53
+ const IMAGE_MIMES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/svg+xml']);
54
+ function resolveBasePath() {
55
+ return config.projects.basePath.replace('~', process.env.HOME || '~');
56
+ }
57
+ /** Path absolut folder project; null jika di luar basePath (path traversal). */
58
+ function projectPath(project) {
59
+ const basePath = resolveBasePath();
60
+ const full = path.join(basePath, project);
61
+ const rel = path.relative(basePath, full);
62
+ if (rel.startsWith('..') || path.isAbsolute(rel))
63
+ return null;
64
+ return full;
65
+ }
66
+ /** Resolve relPath di dalam project root; null jika keluar (path traversal). */
67
+ function resolveInsideProject(project, relPath) {
68
+ const root = projectPath(project);
69
+ if (!root)
70
+ return null;
71
+ const clean = relPath.replace(/^\/+/, ''); // trim leading slash
72
+ if (path.isAbsolute(clean))
73
+ return null;
74
+ const full = path.join(root, clean);
75
+ const rel = path.relative(root, full);
76
+ if (rel.startsWith('..') || path.isAbsolute(rel))
77
+ return null;
78
+ return full;
79
+ }
80
+ function mimeTypeFor(name) {
81
+ const ext = path.extname(name).toLowerCase();
82
+ return MIME_MAP[ext] ?? 'application/octet-stream';
83
+ }
84
+ /** Deteksi apakah file biner adalah text dengan cek utf8 round-trip (no dep). */
85
+ function looksLikeText(buf) {
86
+ if (buf.length === 0)
87
+ return true;
88
+ const str = buf.toString('utf8');
89
+ return Buffer.byteLength(str, 'utf8') === buf.length && !buf.includes(0x00);
90
+ }
91
+ export function createFileRoutes() {
92
+ const router = Router();
93
+ router.use(authMiddleware);
94
+ // List direktori (file + dir + symlink), skip SKIP_DIRS.
95
+ router.get('/projects/:project/files', async (req, res) => {
96
+ const project = req.params.project;
97
+ const rawPath = typeof req.query.path === 'string' ? req.query.path : '';
98
+ const dirPath = resolveInsideProject(project, path.normalize(rawPath));
99
+ if (!dirPath)
100
+ return res.status(400).json({ error: 'Invalid path' });
101
+ try {
102
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
103
+ const mapped = [];
104
+ for (const entry of entries) {
105
+ if (entry.isDirectory() && SKIP_DIRS.has(entry.name))
106
+ continue;
107
+ if (entry.isSymbolicLink())
108
+ mapped.push({ name: entry.name, type: 'symlink' });
109
+ else if (entry.isDirectory())
110
+ mapped.push({ name: entry.name, type: 'dir' });
111
+ else if (entry.isFile())
112
+ mapped.push({ name: entry.name, type: 'file' });
113
+ }
114
+ mapped.sort((a, b) => {
115
+ if (a.type === 'dir' && b.type !== 'dir')
116
+ return -1;
117
+ if (a.type !== 'dir' && b.type === 'dir')
118
+ return 1;
119
+ return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
120
+ });
121
+ const response = { path: rawPath, entries: mapped };
122
+ res.json(response);
123
+ }
124
+ catch (err) {
125
+ if (err.code === 'ENOENT') {
126
+ return res.status(404).json({ error: 'Path not found' });
127
+ }
128
+ console.error('files list error:', err);
129
+ res.status(500).json({ error: 'Failed to list directory' });
130
+ }
131
+ });
132
+ // Baca isi file: text (utf8, truncate 1MB) atau image (base64).
133
+ router.get('/projects/:project/file', async (req, res) => {
134
+ const project = req.params.project;
135
+ const rawPath = typeof req.query.path === 'string' ? req.query.path : '';
136
+ const fullPath = resolveInsideProject(project, path.normalize(rawPath));
137
+ if (!fullPath)
138
+ return res.status(400).json({ error: 'Invalid path' });
139
+ try {
140
+ // ponytail: lstat no-follow; symlink tidak di-resolve (cegah escape basePath).
141
+ const lstat = await fs.lstat(fullPath);
142
+ if (lstat.isSymbolicLink())
143
+ return res.status(400).json({ error: 'Symlink not followed' });
144
+ if (!lstat.isFile())
145
+ return res.status(400).json({ error: 'Not a file' });
146
+ const stat = await fs.stat(fullPath);
147
+ const name = path.basename(fullPath);
148
+ const mimeType = mimeTypeFor(name);
149
+ const size = stat.size;
150
+ if (IMAGE_MIMES.has(mimeType)) {
151
+ if (size > MAX_IMAGE_BYTES)
152
+ return res.status(413).json({ error: 'File too large' });
153
+ const buf = await fs.readFile(fullPath);
154
+ const response = {
155
+ path: rawPath,
156
+ name,
157
+ mimeType,
158
+ size,
159
+ encoding: 'base64',
160
+ content: buf.toString('base64'),
161
+ };
162
+ return res.json(response);
163
+ }
164
+ // Text: baca sampai MAX_TEXT_BYTES, flag truncated jika lebih.
165
+ const handle = await fs.open(fullPath, 'r');
166
+ try {
167
+ const buf = Buffer.alloc(Math.min(size, MAX_TEXT_BYTES));
168
+ const { bytesRead } = await handle.read(buf, 0, buf.length, 0);
169
+ const data = buf.subarray(0, bytesRead);
170
+ if (!looksLikeText(data)) {
171
+ return res.json({
172
+ path: rawPath,
173
+ name,
174
+ mimeType: 'application/octet-stream',
175
+ size,
176
+ encoding: 'base64',
177
+ content: '',
178
+ });
179
+ }
180
+ const truncated = size > MAX_TEXT_BYTES;
181
+ const response = {
182
+ path: rawPath,
183
+ name,
184
+ mimeType,
185
+ size,
186
+ encoding: 'text',
187
+ content: data.toString('utf8'),
188
+ ...(truncated ? { truncated: true } : {}),
189
+ };
190
+ res.json(response);
191
+ }
192
+ finally {
193
+ await handle.close();
194
+ }
195
+ }
196
+ catch (err) {
197
+ if (err.code === 'ENOENT') {
198
+ return res.status(404).json({ error: 'File not found' });
199
+ }
200
+ console.error('file read error:', err);
201
+ res.status(500).json({ error: 'Failed to read file' });
202
+ }
203
+ });
204
+ return router;
205
+ }
206
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/routes/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Hook API routes for Claude Code / Codex hook notifications.
3
+ * Receives status updates from hook scripts and broadcasts to WebSocket subscribers.
4
+ */
5
+ import { Router } from 'express';
6
+ export declare function createHooksRoutes(): Router;
7
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/routes/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAsEjC,wBAAgB,iBAAiB,IAAI,MAAM,CAyG1C"}