@opengsd/gsd-pi 1.0.2-dev.50223bc → 1.0.2-dev.5961fbf

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 (251) hide show
  1. package/dist/resource-loader.d.ts +5 -0
  2. package/dist/resource-loader.js +24 -8
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/gsd/auto/loop.js +19 -0
  5. package/dist/resources/extensions/gsd/auto/phases.js +1 -1
  6. package/dist/resources/extensions/gsd/auto-worktree.js +2 -54
  7. package/dist/resources/extensions/gsd/worktree-post-create-hook.js +117 -0
  8. package/dist/web/standalone/.next/BUILD_ID +1 -1
  9. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  10. package/dist/web/standalone/.next/build-manifest.json +2 -2
  11. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  12. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  29. package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
  30. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  31. package/dist/web/standalone/.next/server/app/index.html +1 -1
  32. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  39. package/dist/web/standalone/.next/server/chunks/1834.js +1 -1
  40. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  41. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  42. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  43. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  44. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  45. package/dist/web/standalone/package.json +0 -1
  46. package/dist/worktree-cli.d.ts +0 -2
  47. package/dist/worktree-cli.js +21 -9
  48. package/package.json +9 -4
  49. package/packages/cloud-mcp-gateway/bin/gsd-cloud-mcp-gateway.js +14 -0
  50. package/packages/cloud-mcp-gateway/package.json +5 -4
  51. package/packages/contracts/package.json +2 -2
  52. package/packages/daemon/bin/gsd-daemon.js +14 -0
  53. package/packages/daemon/bin/gsd-mcp-runtime.js +14 -0
  54. package/packages/daemon/bin/gsd-mcp.js +14 -0
  55. package/packages/daemon/dist/channel-manager.d.ts +53 -0
  56. package/packages/daemon/dist/channel-manager.d.ts.map +1 -0
  57. package/packages/daemon/dist/channel-manager.js +167 -0
  58. package/packages/daemon/dist/channel-manager.js.map +1 -0
  59. package/packages/daemon/dist/cli.d.ts +3 -0
  60. package/packages/daemon/dist/cli.d.ts.map +1 -0
  61. package/packages/daemon/dist/cli.js +94 -0
  62. package/packages/daemon/dist/cli.js.map +1 -0
  63. package/packages/daemon/dist/cloud-cli.d.ts +7 -0
  64. package/packages/daemon/dist/cloud-cli.d.ts.map +1 -0
  65. package/packages/daemon/dist/cloud-cli.js +96 -0
  66. package/packages/daemon/dist/cloud-cli.js.map +1 -0
  67. package/packages/daemon/dist/cloud-config.d.ts +18 -0
  68. package/packages/daemon/dist/cloud-config.d.ts.map +1 -0
  69. package/packages/daemon/dist/cloud-config.js +209 -0
  70. package/packages/daemon/dist/cloud-config.js.map +1 -0
  71. package/packages/daemon/dist/cloud-config.test.d.ts +2 -0
  72. package/packages/daemon/dist/cloud-config.test.d.ts.map +1 -0
  73. package/packages/daemon/dist/cloud-config.test.js +132 -0
  74. package/packages/daemon/dist/cloud-config.test.js.map +1 -0
  75. package/packages/daemon/dist/cloud-runtime.d.ts +26 -0
  76. package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -0
  77. package/packages/daemon/dist/cloud-runtime.js +180 -0
  78. package/packages/daemon/dist/cloud-runtime.js.map +1 -0
  79. package/packages/daemon/dist/cloud-runtime.test.d.ts +2 -0
  80. package/packages/daemon/dist/cloud-runtime.test.d.ts.map +1 -0
  81. package/packages/daemon/dist/cloud-runtime.test.js +28 -0
  82. package/packages/daemon/dist/cloud-runtime.test.js.map +1 -0
  83. package/packages/daemon/dist/cloud-token.d.ts +3 -0
  84. package/packages/daemon/dist/cloud-token.d.ts.map +1 -0
  85. package/packages/daemon/dist/cloud-token.js +37 -0
  86. package/packages/daemon/dist/cloud-token.js.map +1 -0
  87. package/packages/daemon/dist/commands.d.ts +25 -0
  88. package/packages/daemon/dist/commands.d.ts.map +1 -0
  89. package/packages/daemon/dist/commands.js +81 -0
  90. package/packages/daemon/dist/commands.js.map +1 -0
  91. package/packages/daemon/dist/config.d.ts +17 -0
  92. package/packages/daemon/dist/config.d.ts.map +1 -0
  93. package/packages/daemon/dist/config.js +146 -0
  94. package/packages/daemon/dist/config.js.map +1 -0
  95. package/packages/daemon/dist/daemon.d.ts +38 -0
  96. package/packages/daemon/dist/daemon.d.ts.map +1 -0
  97. package/packages/daemon/dist/daemon.js +194 -0
  98. package/packages/daemon/dist/daemon.js.map +1 -0
  99. package/packages/daemon/dist/daemon.test.d.ts +2 -0
  100. package/packages/daemon/dist/daemon.test.d.ts.map +1 -0
  101. package/packages/daemon/dist/daemon.test.js +692 -0
  102. package/packages/daemon/dist/daemon.test.js.map +1 -0
  103. package/packages/daemon/dist/discord-bot.d.ts +70 -0
  104. package/packages/daemon/dist/discord-bot.d.ts.map +1 -0
  105. package/packages/daemon/dist/discord-bot.js +433 -0
  106. package/packages/daemon/dist/discord-bot.js.map +1 -0
  107. package/packages/daemon/dist/discord-bot.test.d.ts +2 -0
  108. package/packages/daemon/dist/discord-bot.test.d.ts.map +1 -0
  109. package/packages/daemon/dist/discord-bot.test.js +667 -0
  110. package/packages/daemon/dist/discord-bot.test.js.map +1 -0
  111. package/packages/daemon/dist/event-bridge.d.ts +72 -0
  112. package/packages/daemon/dist/event-bridge.d.ts.map +1 -0
  113. package/packages/daemon/dist/event-bridge.js +366 -0
  114. package/packages/daemon/dist/event-bridge.js.map +1 -0
  115. package/packages/daemon/dist/event-bridge.test.d.ts +9 -0
  116. package/packages/daemon/dist/event-bridge.test.d.ts.map +1 -0
  117. package/packages/daemon/dist/event-bridge.test.js +528 -0
  118. package/packages/daemon/dist/event-bridge.test.js.map +1 -0
  119. package/packages/daemon/dist/event-formatter.d.ts +34 -0
  120. package/packages/daemon/dist/event-formatter.d.ts.map +1 -0
  121. package/packages/daemon/dist/event-formatter.js +355 -0
  122. package/packages/daemon/dist/event-formatter.js.map +1 -0
  123. package/packages/daemon/dist/event-formatter.test.d.ts +2 -0
  124. package/packages/daemon/dist/event-formatter.test.d.ts.map +1 -0
  125. package/packages/daemon/dist/event-formatter.test.js +333 -0
  126. package/packages/daemon/dist/event-formatter.test.js.map +1 -0
  127. package/packages/daemon/dist/index.d.ts +25 -0
  128. package/packages/daemon/dist/index.d.ts.map +1 -0
  129. package/packages/daemon/dist/index.js +17 -0
  130. package/packages/daemon/dist/index.js.map +1 -0
  131. package/packages/daemon/dist/launchd.d.ts +49 -0
  132. package/packages/daemon/dist/launchd.d.ts.map +1 -0
  133. package/packages/daemon/dist/launchd.js +188 -0
  134. package/packages/daemon/dist/launchd.js.map +1 -0
  135. package/packages/daemon/dist/launchd.test.d.ts +2 -0
  136. package/packages/daemon/dist/launchd.test.d.ts.map +1 -0
  137. package/packages/daemon/dist/launchd.test.js +296 -0
  138. package/packages/daemon/dist/launchd.test.js.map +1 -0
  139. package/packages/daemon/dist/local-tool-executor.d.ts +22 -0
  140. package/packages/daemon/dist/local-tool-executor.d.ts.map +1 -0
  141. package/packages/daemon/dist/local-tool-executor.js +307 -0
  142. package/packages/daemon/dist/local-tool-executor.js.map +1 -0
  143. package/packages/daemon/dist/local-tool-executor.test.d.ts +2 -0
  144. package/packages/daemon/dist/local-tool-executor.test.d.ts.map +1 -0
  145. package/packages/daemon/dist/local-tool-executor.test.js +111 -0
  146. package/packages/daemon/dist/local-tool-executor.test.js.map +1 -0
  147. package/packages/daemon/dist/logger.d.ts +25 -0
  148. package/packages/daemon/dist/logger.d.ts.map +1 -0
  149. package/packages/daemon/dist/logger.js +72 -0
  150. package/packages/daemon/dist/logger.js.map +1 -0
  151. package/packages/daemon/dist/mcp-cli.d.ts +3 -0
  152. package/packages/daemon/dist/mcp-cli.d.ts.map +1 -0
  153. package/packages/daemon/dist/mcp-cli.js +8 -0
  154. package/packages/daemon/dist/mcp-cli.js.map +1 -0
  155. package/packages/daemon/dist/mcp-cli.test.d.ts +2 -0
  156. package/packages/daemon/dist/mcp-cli.test.d.ts.map +1 -0
  157. package/packages/daemon/dist/mcp-cli.test.js +13 -0
  158. package/packages/daemon/dist/mcp-cli.test.js.map +1 -0
  159. package/packages/daemon/dist/mcp-runtime-cli.d.ts +3 -0
  160. package/packages/daemon/dist/mcp-runtime-cli.d.ts.map +1 -0
  161. package/packages/daemon/dist/mcp-runtime-cli.js +8 -0
  162. package/packages/daemon/dist/mcp-runtime-cli.js.map +1 -0
  163. package/packages/daemon/dist/message-batcher.d.ts +78 -0
  164. package/packages/daemon/dist/message-batcher.d.ts.map +1 -0
  165. package/packages/daemon/dist/message-batcher.js +173 -0
  166. package/packages/daemon/dist/message-batcher.js.map +1 -0
  167. package/packages/daemon/dist/message-batcher.test.d.ts +2 -0
  168. package/packages/daemon/dist/message-batcher.test.d.ts.map +1 -0
  169. package/packages/daemon/dist/message-batcher.test.js +242 -0
  170. package/packages/daemon/dist/message-batcher.test.js.map +1 -0
  171. package/packages/daemon/dist/orchestrator.d.ts +98 -0
  172. package/packages/daemon/dist/orchestrator.d.ts.map +1 -0
  173. package/packages/daemon/dist/orchestrator.js +359 -0
  174. package/packages/daemon/dist/orchestrator.js.map +1 -0
  175. package/packages/daemon/dist/orchestrator.test.d.ts +8 -0
  176. package/packages/daemon/dist/orchestrator.test.d.ts.map +1 -0
  177. package/packages/daemon/dist/orchestrator.test.js +425 -0
  178. package/packages/daemon/dist/orchestrator.test.js.map +1 -0
  179. package/packages/daemon/dist/project-scanner.d.ts +18 -0
  180. package/packages/daemon/dist/project-scanner.d.ts.map +1 -0
  181. package/packages/daemon/dist/project-scanner.js +90 -0
  182. package/packages/daemon/dist/project-scanner.js.map +1 -0
  183. package/packages/daemon/dist/project-scanner.test.d.ts +5 -0
  184. package/packages/daemon/dist/project-scanner.test.d.ts.map +1 -0
  185. package/packages/daemon/dist/project-scanner.test.js +183 -0
  186. package/packages/daemon/dist/project-scanner.test.js.map +1 -0
  187. package/packages/daemon/dist/session-manager.d.ts +70 -0
  188. package/packages/daemon/dist/session-manager.d.ts.map +1 -0
  189. package/packages/daemon/dist/session-manager.js +358 -0
  190. package/packages/daemon/dist/session-manager.js.map +1 -0
  191. package/packages/daemon/dist/session-manager.test.d.ts +9 -0
  192. package/packages/daemon/dist/session-manager.test.d.ts.map +1 -0
  193. package/packages/daemon/dist/session-manager.test.js +616 -0
  194. package/packages/daemon/dist/session-manager.test.js.map +1 -0
  195. package/packages/daemon/dist/types.d.ts +133 -0
  196. package/packages/daemon/dist/types.d.ts.map +1 -0
  197. package/packages/daemon/dist/types.js +8 -0
  198. package/packages/daemon/dist/types.js.map +1 -0
  199. package/packages/daemon/dist/verbosity.d.ts +27 -0
  200. package/packages/daemon/dist/verbosity.d.ts.map +1 -0
  201. package/packages/daemon/dist/verbosity.js +86 -0
  202. package/packages/daemon/dist/verbosity.js.map +1 -0
  203. package/packages/daemon/dist/verbosity.test.d.ts +2 -0
  204. package/packages/daemon/dist/verbosity.test.d.ts.map +1 -0
  205. package/packages/daemon/dist/verbosity.test.js +136 -0
  206. package/packages/daemon/dist/verbosity.test.js.map +1 -0
  207. package/packages/daemon/package.json +9 -8
  208. package/packages/gsd-agent-core/package.json +6 -6
  209. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  210. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
  211. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  212. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  213. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  214. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  215. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
  216. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
  217. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
  218. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
  219. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  220. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
  221. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  222. package/packages/gsd-agent-modes/package.json +8 -8
  223. package/packages/mcp-server/bin/gsd-mcp-server.js +14 -0
  224. package/packages/mcp-server/package.json +6 -5
  225. package/packages/native/package.json +3 -3
  226. package/packages/pi-agent-core/package.json +4 -4
  227. package/packages/pi-ai/bin/pi-ai.js +14 -0
  228. package/packages/pi-ai/dist/models.generated.d.ts +0 -17
  229. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  230. package/packages/pi-ai/dist/models.generated.js +18 -35
  231. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  232. package/packages/pi-ai/package.json +5 -4
  233. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
  234. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/core/tools/read.js +5 -3
  236. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  237. package/packages/pi-coding-agent/package.json +9 -9
  238. package/packages/pi-tui/package.json +2 -2
  239. package/packages/rpc-client/package.json +3 -3
  240. package/pkg/package.json +1 -1
  241. package/scripts/ensure-workspace-builds.cjs +4 -4
  242. package/scripts/install/deps.js +10 -0
  243. package/src/resources/extensions/gsd/auto/loop.ts +22 -0
  244. package/src/resources/extensions/gsd/auto/phases.ts +1 -1
  245. package/src/resources/extensions/gsd/auto-worktree.ts +2 -56
  246. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
  247. package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +141 -1
  248. package/src/resources/extensions/gsd/worktree-post-create-hook.ts +127 -0
  249. package/dist/tsconfig.extensions.tsbuildinfo +0 -1
  250. /package/dist/web/standalone/.next/static/{JP7xjsa5zSaO76XhE-mFJ → spUYLkQXoHJyxYOMH9VQy}/_buildManifest.js +0 -0
  251. /package/dist/web/standalone/.next/static/{JP7xjsa5zSaO76XhE-mFJ → spUYLkQXoHJyxYOMH9VQy}/_ssgManifest.js +0 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * ChannelManager — manages per-project Discord text channels under a
3
+ * 'GSD Projects' category, with archive support.
4
+ *
5
+ * Pure helper `sanitizeChannelName` exported separately for testability.
6
+ */
7
+ import { ChannelType, PermissionFlagsBits, } from 'discord.js';
8
+ // ---------------------------------------------------------------------------
9
+ // Constants
10
+ // ---------------------------------------------------------------------------
11
+ const DEFAULT_CATEGORY_NAME = 'GSD Projects';
12
+ const ARCHIVE_CATEGORY_NAME = 'GSD Archive';
13
+ const CHANNEL_PREFIX = 'gsd-';
14
+ const MAX_CHANNEL_NAME_LENGTH = 100; // Discord's limit
15
+ // ---------------------------------------------------------------------------
16
+ // Pure helpers — exported for testability
17
+ // ---------------------------------------------------------------------------
18
+ /**
19
+ * Sanitize a project directory path into a valid Discord channel name.
20
+ *
21
+ * - Takes the basename of the path
22
+ * - Lowercases
23
+ * - Replaces non-alphanumeric (except hyphens) with hyphens
24
+ * - Collapses consecutive hyphens
25
+ * - Trims leading/trailing hyphens
26
+ * - Prefixes with 'gsd-'
27
+ * - Caps total length at 100 chars (Discord limit)
28
+ *
29
+ * Returns 'gsd-unnamed' for empty/whitespace-only inputs.
30
+ */
31
+ export function sanitizeChannelName(projectDir) {
32
+ // Extract basename — handle both forward and back slashes
33
+ const parts = projectDir.replace(/\\/g, '/').split('/');
34
+ let basename = parts[parts.length - 1] ?? '';
35
+ // Trim whitespace
36
+ basename = basename.trim();
37
+ // Fallback for empty basename
38
+ if (!basename) {
39
+ return 'gsd-unnamed';
40
+ }
41
+ // Lowercase
42
+ let name = basename.toLowerCase();
43
+ // Replace non-alphanumeric (except hyphens) with hyphens
44
+ name = name.replace(/[^a-z0-9-]/g, '-');
45
+ // Collapse consecutive hyphens
46
+ name = name.replace(/-{2,}/g, '-');
47
+ // Trim leading/trailing hyphens
48
+ name = name.replace(/^-+|-+$/g, '');
49
+ // Fallback if nothing remains after sanitization
50
+ if (!name) {
51
+ return 'gsd-unnamed';
52
+ }
53
+ // Prefix
54
+ const prefixed = `${CHANNEL_PREFIX}${name}`;
55
+ // Cap at max length
56
+ if (prefixed.length > MAX_CHANNEL_NAME_LENGTH) {
57
+ // Truncate and remove any trailing hyphen from the cut
58
+ return prefixed.slice(0, MAX_CHANNEL_NAME_LENGTH).replace(/-+$/, '');
59
+ }
60
+ return prefixed;
61
+ }
62
+ export class ChannelManager {
63
+ guild;
64
+ logger;
65
+ categoryName;
66
+ categoryCache = null;
67
+ archiveCategoryCache = null;
68
+ constructor(opts) {
69
+ this.guild = opts.guild;
70
+ this.logger = opts.logger;
71
+ this.categoryName = opts.categoryName ?? DEFAULT_CATEGORY_NAME;
72
+ }
73
+ /**
74
+ * Find or create the project category channel.
75
+ * Caches the result — subsequent calls return the cached category.
76
+ */
77
+ async resolveCategory() {
78
+ if (this.categoryCache) {
79
+ return this.categoryCache;
80
+ }
81
+ const existing = this.findCategoryByName(this.categoryName);
82
+ if (existing) {
83
+ this.categoryCache = existing;
84
+ this.logger.debug('category resolved from cache', { name: this.categoryName, id: existing.id });
85
+ return existing;
86
+ }
87
+ // Create the category
88
+ const created = await this.guild.channels.create({
89
+ name: this.categoryName,
90
+ type: ChannelType.GuildCategory,
91
+ });
92
+ this.categoryCache = created;
93
+ this.logger.info('category created', { name: this.categoryName, id: created.id });
94
+ return this.categoryCache;
95
+ }
96
+ /**
97
+ * Create a text channel for a project under the GSD Projects category.
98
+ * Channel name is derived from the project directory path.
99
+ */
100
+ async createProjectChannel(projectDir) {
101
+ const name = sanitizeChannelName(projectDir);
102
+ const category = await this.resolveCategory();
103
+ const channel = await this.guild.channels.create({
104
+ name,
105
+ type: ChannelType.GuildText,
106
+ parent: category.id,
107
+ });
108
+ this.logger.info('project channel created', {
109
+ name,
110
+ channelId: channel.id,
111
+ categoryId: category.id,
112
+ projectDir,
113
+ });
114
+ return channel;
115
+ }
116
+ /**
117
+ * Archive a channel by moving it to the 'GSD Archive' category and
118
+ * setting permission overwrite to deny ViewChannel for @everyone.
119
+ */
120
+ async archiveChannel(channelId) {
121
+ const archive = await this.resolveArchiveCategory();
122
+ const channel = this.guild.channels.cache.get(channelId);
123
+ if (!channel) {
124
+ this.logger.warn('archive target not found', { channelId });
125
+ return;
126
+ }
127
+ if (!('edit' in channel) || typeof channel.edit !== 'function') {
128
+ this.logger.warn('archive target is not editable', { channelId, type: channel.type });
129
+ return;
130
+ }
131
+ await channel.edit({
132
+ parent: archive.id,
133
+ permissionOverwrites: [
134
+ {
135
+ id: this.guild.id, // @everyone role ID matches guild ID
136
+ deny: [PermissionFlagsBits.ViewChannel],
137
+ },
138
+ ],
139
+ });
140
+ this.logger.info('channel archived', { channelId, archiveCategoryId: archive.id });
141
+ }
142
+ // ---------------------------------------------------------------------------
143
+ // Private helpers
144
+ // ---------------------------------------------------------------------------
145
+ findCategoryByName(name) {
146
+ const match = this.guild.channels.cache.find((ch) => ch.type === ChannelType.GuildCategory && ch.name === name);
147
+ return match ?? null;
148
+ }
149
+ async resolveArchiveCategory() {
150
+ if (this.archiveCategoryCache) {
151
+ return this.archiveCategoryCache;
152
+ }
153
+ const existing = this.findCategoryByName(ARCHIVE_CATEGORY_NAME);
154
+ if (existing) {
155
+ this.archiveCategoryCache = existing;
156
+ return existing;
157
+ }
158
+ const created = await this.guild.channels.create({
159
+ name: ARCHIVE_CATEGORY_NAME,
160
+ type: ChannelType.GuildCategory,
161
+ });
162
+ this.archiveCategoryCache = created;
163
+ this.logger.info('archive category created', { name: ARCHIVE_CATEGORY_NAME, id: created.id });
164
+ return this.archiveCategoryCache;
165
+ }
166
+ }
167
+ //# sourceMappingURL=channel-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-manager.js","sourceRoot":"","sources":["../src/channel-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,WAAW,EACX,mBAAmB,GAKpB,MAAM,YAAY,CAAC;AAGpB,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,qBAAqB,GAAG,cAAc,CAAC;AAC7C,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAC5C,MAAM,cAAc,GAAG,MAAM,CAAC;AAC9B,MAAM,uBAAuB,GAAG,GAAG,CAAC,CAAC,kBAAkB;AAEvD,8EAA8E;AAC9E,0CAA0C;AAC1C,8EAA8E;AAE9E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,0DAA0D;IAC1D,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxD,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAE7C,kBAAkB;IAClB,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAE3B,8BAA8B;IAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,YAAY;IACZ,IAAI,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAElC,yDAAyD;IACzD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAExC,+BAA+B;IAC/B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAEnC,gCAAgC;IAChC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEpC,iDAAiD;IACjD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,SAAS;IACT,MAAM,QAAQ,GAAG,GAAG,cAAc,GAAG,IAAI,EAAE,CAAC;IAE5C,oBAAoB;IACpB,IAAI,QAAQ,CAAC,MAAM,GAAG,uBAAuB,EAAE,CAAC;QAC9C,uDAAuD;QACvD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,uBAAuB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAYD,MAAM,OAAO,cAAc;IACR,KAAK,CAAQ;IACb,MAAM,CAAS;IACf,YAAY,CAAS;IAE9B,aAAa,GAA2B,IAAI,CAAC;IAC7C,oBAAoB,GAA2B,IAAI,CAAC;IAE5D,YAAY,IAA2B;QACrC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,qBAAqB,CAAC;IACjE,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe;QACnB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,aAAa,CAAC;QAC5B,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC5D,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;YAChG,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,sBAAsB;QACtB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC/C,IAAI,EAAE,IAAI,CAAC,YAAY;YACvB,IAAI,EAAE,WAAW,CAAC,aAAa;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,aAAa,GAAG,OAA0B,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAClF,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,oBAAoB,CAAC,UAAkB;QAC3C,MAAM,IAAI,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAE9C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC/C,IAAI;YACJ,IAAI,EAAE,WAAW,CAAC,SAAS;YAC3B,MAAM,EAAE,QAAQ,CAAC,EAAE;SACpB,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE;YAC1C,IAAI;YACJ,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,UAAU,EAAE,QAAQ,CAAC,EAAE;YACvB,UAAU;SACX,CAAC,CAAC;QAEH,OAAO,OAAsB,CAAC;IAChC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc,CAAC,SAAiB;QACpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAEpD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;YAC5D,OAAO;QACT,CAAC;QAED,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gCAAgC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;QAED,MAAM,OAAO,CAAC,IAAI,CAAC;YACjB,MAAM,EAAE,OAAO,CAAC,EAAE;YAClB,oBAAoB,EAAE;gBACpB;oBACE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,qCAAqC;oBACxD,IAAI,EAAE,CAAC,mBAAmB,CAAC,WAAW,CAAC;iBACxC;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,8EAA8E;IAC9E,kBAAkB;IAClB,8EAA8E;IAEtE,kBAAkB,CAAC,IAAY;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAC1C,CAAC,EAAqB,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,WAAW,CAAC,aAAa,IAAI,EAAE,CAAC,IAAI,KAAK,IAAI,CACrF,CAAC;QACF,OAAQ,KAAyB,IAAI,IAAI,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,sBAAsB;QAClC,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,oBAAoB,CAAC;QACnC,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC,qBAAqB,CAAC,CAAC;QAChE,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,oBAAoB,GAAG,QAAQ,CAAC;YACrC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC/C,IAAI,EAAE,qBAAqB;YAC3B,IAAI,EAAE,WAAW,CAAC,aAAa;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,oBAAoB,GAAG,OAA0B,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9F,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACnC,CAAC;CACF","sourcesContent":["/**\n * ChannelManager — manages per-project Discord text channels under a\n * 'GSD Projects' category, with archive support.\n *\n * Pure helper `sanitizeChannelName` exported separately for testability.\n */\n\nimport {\n ChannelType,\n PermissionFlagsBits,\n type Guild,\n type CategoryChannel,\n type TextChannel,\n type GuildBasedChannel,\n} from 'discord.js';\nimport type { Logger } from './logger.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_CATEGORY_NAME = 'GSD Projects';\nconst ARCHIVE_CATEGORY_NAME = 'GSD Archive';\nconst CHANNEL_PREFIX = 'gsd-';\nconst MAX_CHANNEL_NAME_LENGTH = 100; // Discord's limit\n\n// ---------------------------------------------------------------------------\n// Pure helpers — exported for testability\n// ---------------------------------------------------------------------------\n\n/**\n * Sanitize a project directory path into a valid Discord channel name.\n *\n * - Takes the basename of the path\n * - Lowercases\n * - Replaces non-alphanumeric (except hyphens) with hyphens\n * - Collapses consecutive hyphens\n * - Trims leading/trailing hyphens\n * - Prefixes with 'gsd-'\n * - Caps total length at 100 chars (Discord limit)\n *\n * Returns 'gsd-unnamed' for empty/whitespace-only inputs.\n */\nexport function sanitizeChannelName(projectDir: string): string {\n // Extract basename — handle both forward and back slashes\n const parts = projectDir.replace(/\\\\/g, '/').split('/');\n let basename = parts[parts.length - 1] ?? '';\n\n // Trim whitespace\n basename = basename.trim();\n\n // Fallback for empty basename\n if (!basename) {\n return 'gsd-unnamed';\n }\n\n // Lowercase\n let name = basename.toLowerCase();\n\n // Replace non-alphanumeric (except hyphens) with hyphens\n name = name.replace(/[^a-z0-9-]/g, '-');\n\n // Collapse consecutive hyphens\n name = name.replace(/-{2,}/g, '-');\n\n // Trim leading/trailing hyphens\n name = name.replace(/^-+|-+$/g, '');\n\n // Fallback if nothing remains after sanitization\n if (!name) {\n return 'gsd-unnamed';\n }\n\n // Prefix\n const prefixed = `${CHANNEL_PREFIX}${name}`;\n\n // Cap at max length\n if (prefixed.length > MAX_CHANNEL_NAME_LENGTH) {\n // Truncate and remove any trailing hyphen from the cut\n return prefixed.slice(0, MAX_CHANNEL_NAME_LENGTH).replace(/-+$/, '');\n }\n\n return prefixed;\n}\n\n// ---------------------------------------------------------------------------\n// ChannelManager class\n// ---------------------------------------------------------------------------\n\nexport interface ChannelManagerOptions {\n guild: Guild;\n logger: Logger;\n categoryName?: string;\n}\n\nexport class ChannelManager {\n private readonly guild: Guild;\n private readonly logger: Logger;\n private readonly categoryName: string;\n\n private categoryCache: CategoryChannel | null = null;\n private archiveCategoryCache: CategoryChannel | null = null;\n\n constructor(opts: ChannelManagerOptions) {\n this.guild = opts.guild;\n this.logger = opts.logger;\n this.categoryName = opts.categoryName ?? DEFAULT_CATEGORY_NAME;\n }\n\n /**\n * Find or create the project category channel.\n * Caches the result — subsequent calls return the cached category.\n */\n async resolveCategory(): Promise<CategoryChannel> {\n if (this.categoryCache) {\n return this.categoryCache;\n }\n\n const existing = this.findCategoryByName(this.categoryName);\n if (existing) {\n this.categoryCache = existing;\n this.logger.debug('category resolved from cache', { name: this.categoryName, id: existing.id });\n return existing;\n }\n\n // Create the category\n const created = await this.guild.channels.create({\n name: this.categoryName,\n type: ChannelType.GuildCategory,\n });\n\n this.categoryCache = created as CategoryChannel;\n this.logger.info('category created', { name: this.categoryName, id: created.id });\n return this.categoryCache;\n }\n\n /**\n * Create a text channel for a project under the GSD Projects category.\n * Channel name is derived from the project directory path.\n */\n async createProjectChannel(projectDir: string): Promise<TextChannel> {\n const name = sanitizeChannelName(projectDir);\n const category = await this.resolveCategory();\n\n const channel = await this.guild.channels.create({\n name,\n type: ChannelType.GuildText,\n parent: category.id,\n });\n\n this.logger.info('project channel created', {\n name,\n channelId: channel.id,\n categoryId: category.id,\n projectDir,\n });\n\n return channel as TextChannel;\n }\n\n /**\n * Archive a channel by moving it to the 'GSD Archive' category and\n * setting permission overwrite to deny ViewChannel for @everyone.\n */\n async archiveChannel(channelId: string): Promise<void> {\n const archive = await this.resolveArchiveCategory();\n\n const channel = this.guild.channels.cache.get(channelId);\n if (!channel) {\n this.logger.warn('archive target not found', { channelId });\n return;\n }\n\n if (!('edit' in channel) || typeof channel.edit !== 'function') {\n this.logger.warn('archive target is not editable', { channelId, type: channel.type });\n return;\n }\n\n await channel.edit({\n parent: archive.id,\n permissionOverwrites: [\n {\n id: this.guild.id, // @everyone role ID matches guild ID\n deny: [PermissionFlagsBits.ViewChannel],\n },\n ],\n });\n\n this.logger.info('channel archived', { channelId, archiveCategoryId: archive.id });\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private findCategoryByName(name: string): CategoryChannel | null {\n const match = this.guild.channels.cache.find(\n (ch: GuildBasedChannel) => ch.type === ChannelType.GuildCategory && ch.name === name,\n );\n return (match as CategoryChannel) ?? null;\n }\n\n private async resolveArchiveCategory(): Promise<CategoryChannel> {\n if (this.archiveCategoryCache) {\n return this.archiveCategoryCache;\n }\n\n const existing = this.findCategoryByName(ARCHIVE_CATEGORY_NAME);\n if (existing) {\n this.archiveCategoryCache = existing;\n return existing;\n }\n\n const created = await this.guild.channels.create({\n name: ARCHIVE_CATEGORY_NAME,\n type: ChannelType.GuildCategory,\n });\n\n this.archiveCategoryCache = created as CategoryChannel;\n this.logger.info('archive category created', { name: ARCHIVE_CATEGORY_NAME, id: created.id });\n return this.archiveCategoryCache;\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { resolve, dirname } from 'node:path';
5
+ import { resolveConfigPath, loadConfig } from './config.js';
6
+ import { Logger } from './logger.js';
7
+ import { Daemon } from './daemon.js';
8
+ import { install, uninstall, status } from './launchd.js';
9
+ import { handleCloudRuntimeCommand } from './cloud-cli.js';
10
+ const USAGE = `Usage: gsd-daemon [options]
11
+ gsd-daemon cloud status [--config <path>]
12
+ gsd-daemon cloud pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]
13
+ gsd-daemon cloud connect [--config <path>] [--verbose]
14
+ gsd-daemon cloud disconnect [--config <path>]
15
+
16
+ Options:
17
+ --config <path> Path to YAML config file (default: ~/.gsd/daemon.yaml)
18
+ --verbose Print log entries to stderr in addition to the log file
19
+ --install Install the launchd LaunchAgent (auto-starts on login)
20
+ --uninstall Uninstall the launchd LaunchAgent
21
+ --status Show launchd agent status (registered, PID, exit code)
22
+ --help Show this help message and exit
23
+ `;
24
+ async function main() {
25
+ if (process.argv[2] === 'cloud') {
26
+ await handleCloudRuntimeCommand(process.argv.slice(3), {
27
+ binaryName: 'gsd-daemon',
28
+ nestedCommandName: 'cloud',
29
+ });
30
+ return;
31
+ }
32
+ const { values } = parseArgs({
33
+ options: {
34
+ config: { type: 'string', short: 'c' },
35
+ verbose: { type: 'boolean', short: 'v', default: false },
36
+ install: { type: 'boolean', default: false },
37
+ uninstall: { type: 'boolean', default: false },
38
+ status: { type: 'boolean', default: false },
39
+ help: { type: 'boolean', short: 'h', default: false },
40
+ },
41
+ strict: true,
42
+ });
43
+ if (values.help) {
44
+ process.stdout.write(USAGE);
45
+ process.exit(0);
46
+ }
47
+ // --- launchd commands (dispatch before Daemon creation) ---
48
+ if (values.install) {
49
+ const configPath = resolveConfigPath(values.config);
50
+ const thisFile = fileURLToPath(import.meta.url);
51
+ const scriptPath = resolve(dirname(thisFile), 'cli.js');
52
+ install({
53
+ nodePath: process.execPath,
54
+ scriptPath,
55
+ configPath,
56
+ });
57
+ process.stdout.write('gsd-daemon: launchd agent installed and loaded.\n');
58
+ process.exit(0);
59
+ }
60
+ if (values.uninstall) {
61
+ uninstall();
62
+ process.stdout.write('gsd-daemon: launchd agent uninstalled.\n');
63
+ process.exit(0);
64
+ }
65
+ if (values.status) {
66
+ const result = status();
67
+ if (!result.registered) {
68
+ process.stdout.write('gsd-daemon: not registered with launchd.\n');
69
+ }
70
+ else if (result.pid != null) {
71
+ process.stdout.write(`gsd-daemon: running (PID ${result.pid}, last exit status: ${result.lastExitStatus ?? 'n/a'})\n`);
72
+ }
73
+ else {
74
+ process.stdout.write(`gsd-daemon: registered but not running (last exit status: ${result.lastExitStatus ?? 'n/a'})\n`);
75
+ }
76
+ process.exit(0);
77
+ }
78
+ // --- normal daemon start ---
79
+ const configPath = resolveConfigPath(values.config);
80
+ const config = loadConfig(configPath);
81
+ const logger = new Logger({
82
+ filePath: config.log.file,
83
+ level: config.log.level,
84
+ verbose: values.verbose,
85
+ });
86
+ const daemon = new Daemon(config, logger);
87
+ await daemon.start();
88
+ }
89
+ main().catch((err) => {
90
+ const msg = err instanceof Error ? err.message : String(err);
91
+ process.stderr.write(`gsd-daemon: fatal: ${msg}\n`);
92
+ process.exit(1);
93
+ });
94
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from 'node:util';\nimport { fileURLToPath } from 'node:url';\nimport { resolve, dirname } from 'node:path';\nimport { resolveConfigPath, loadConfig } from './config.js';\nimport { Logger } from './logger.js';\nimport { Daemon } from './daemon.js';\nimport { install, uninstall, status } from './launchd.js';\nimport { handleCloudRuntimeCommand } from './cloud-cli.js';\n\nconst USAGE = `Usage: gsd-daemon [options]\n gsd-daemon cloud status [--config <path>]\n gsd-daemon cloud pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]\n gsd-daemon cloud connect [--config <path>] [--verbose]\n gsd-daemon cloud disconnect [--config <path>]\n\nOptions:\n --config <path> Path to YAML config file (default: ~/.gsd/daemon.yaml)\n --verbose Print log entries to stderr in addition to the log file\n --install Install the launchd LaunchAgent (auto-starts on login)\n --uninstall Uninstall the launchd LaunchAgent\n --status Show launchd agent status (registered, PID, exit code)\n --help Show this help message and exit\n`;\n\nasync function main(): Promise<void> {\n if (process.argv[2] === 'cloud') {\n await handleCloudRuntimeCommand(process.argv.slice(3), {\n binaryName: 'gsd-daemon',\n nestedCommandName: 'cloud',\n });\n return;\n }\n\n const { values } = parseArgs({\n options: {\n config: { type: 'string', short: 'c' },\n verbose: { type: 'boolean', short: 'v', default: false },\n install: { type: 'boolean', default: false },\n uninstall: { type: 'boolean', default: false },\n status: { type: 'boolean', default: false },\n help: { type: 'boolean', short: 'h', default: false },\n },\n strict: true,\n });\n\n if (values.help) {\n process.stdout.write(USAGE);\n process.exit(0);\n }\n\n // --- launchd commands (dispatch before Daemon creation) ---\n\n if (values.install) {\n const configPath = resolveConfigPath(values.config);\n const thisFile = fileURLToPath(import.meta.url);\n const scriptPath = resolve(dirname(thisFile), 'cli.js');\n\n install({\n nodePath: process.execPath,\n scriptPath,\n configPath,\n });\n process.stdout.write('gsd-daemon: launchd agent installed and loaded.\\n');\n process.exit(0);\n }\n\n if (values.uninstall) {\n uninstall();\n process.stdout.write('gsd-daemon: launchd agent uninstalled.\\n');\n process.exit(0);\n }\n\n if (values.status) {\n const result = status();\n if (!result.registered) {\n process.stdout.write('gsd-daemon: not registered with launchd.\\n');\n } else if (result.pid != null) {\n process.stdout.write(\n `gsd-daemon: running (PID ${result.pid}, last exit status: ${result.lastExitStatus ?? 'n/a'})\\n`,\n );\n } else {\n process.stdout.write(\n `gsd-daemon: registered but not running (last exit status: ${result.lastExitStatus ?? 'n/a'})\\n`,\n );\n }\n process.exit(0);\n }\n\n // --- normal daemon start ---\n\n const configPath = resolveConfigPath(values.config);\n const config = loadConfig(configPath);\n\n const logger = new Logger({\n filePath: config.log.file,\n level: config.log.level,\n verbose: values.verbose,\n });\n\n const daemon = new Daemon(config, logger);\n await daemon.start();\n}\n\nmain().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`gsd-daemon: fatal: ${msg}\\n`);\n process.exit(1);\n});\n"]}
@@ -0,0 +1,7 @@
1
+ export declare const CLOUD_COMMAND_USAGE = "Commands:\n status [--config <path>]\n pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]\n connect [--config <path>] [--verbose]\n disconnect [--config <path>]\n";
2
+ export declare function handleCloudRuntimeCommand(argv: string[], opts: {
3
+ binaryName: string;
4
+ nestedCommandName?: string;
5
+ }): Promise<void>;
6
+ export declare function formatCloudRuntimeUsage(binaryName: string, nestedCommandName?: string): string;
7
+ //# sourceMappingURL=cloud-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-cli.d.ts","sourceRoot":"","sources":["../src/cloud-cli.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,mBAAmB,qMAK/B,CAAC;AAEF,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE;IACpE,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,CA0EhB;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAe9F"}
@@ -0,0 +1,96 @@
1
+ import { parseArgs } from "node:util";
2
+ import { resolveConfigPath, loadConfig } from "./config.js";
3
+ import { Logger } from "./logger.js";
4
+ import { Daemon } from "./daemon.js";
5
+ import { clearCloudConfig, exchangePairingCode, redactedCloudStatus, saveCloudConfig } from "./cloud-config.js";
6
+ export const CLOUD_COMMAND_USAGE = `Commands:
7
+ status [--config <path>]
8
+ pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]
9
+ connect [--config <path>] [--verbose]
10
+ disconnect [--config <path>]
11
+ `;
12
+ export async function handleCloudRuntimeCommand(argv, opts) {
13
+ if (argv[0] === "--help" || argv[0] === "-h") {
14
+ process.stdout.write(formatCloudRuntimeUsage(opts.binaryName, opts.nestedCommandName));
15
+ process.exit(0);
16
+ }
17
+ const command = argv[0];
18
+ const { values } = parseArgs({
19
+ args: argv.slice(1),
20
+ options: {
21
+ config: { type: "string", short: "c" },
22
+ gateway: { type: "string" },
23
+ code: { type: "string" },
24
+ "runtime-name": { type: "string" },
25
+ verbose: { type: "boolean", short: "v", default: false },
26
+ help: { type: "boolean", short: "h", default: false },
27
+ },
28
+ strict: true,
29
+ });
30
+ if (values.help || !command) {
31
+ process.stdout.write(formatCloudRuntimeUsage(opts.binaryName, opts.nestedCommandName));
32
+ process.exit(0);
33
+ }
34
+ const configPath = resolveConfigPath(values.config);
35
+ if (command === "status") {
36
+ process.stdout.write(`${JSON.stringify(redactedCloudStatus(loadConfig(configPath)), null, 2)}\n`);
37
+ return;
38
+ }
39
+ if (command === "disconnect") {
40
+ clearCloudConfig(configPath);
41
+ process.stdout.write(`${opts.binaryName}: cloud runtime disconnected locally.\n`);
42
+ return;
43
+ }
44
+ if (command === "pair") {
45
+ if (!values.gateway || !values.code) {
46
+ throw new Error("pair requires --gateway and --code");
47
+ }
48
+ const runtimeName = values["runtime-name"];
49
+ const result = await exchangePairingCode({
50
+ gatewayUrl: values.gateway,
51
+ code: values.code,
52
+ runtimeName,
53
+ });
54
+ saveCloudConfig(configPath, {
55
+ gateway_url: values.gateway,
56
+ device_token: result.deviceToken,
57
+ runtime_id: result.runtimeId,
58
+ ...(runtimeName ? { runtime_name: runtimeName } : {}),
59
+ enabled: true,
60
+ });
61
+ process.stdout.write(`${opts.binaryName}: paired cloud runtime ${result.runtimeId}.\n`);
62
+ return;
63
+ }
64
+ if (command === "connect") {
65
+ const config = loadConfig(configPath);
66
+ if (!config.cloud?.device_token || !config.cloud.runtime_id) {
67
+ throw new Error("cloud runtime is not paired; run `pair` first");
68
+ }
69
+ const logger = new Logger({
70
+ filePath: config.log.file,
71
+ level: config.log.level,
72
+ verbose: values.verbose,
73
+ });
74
+ const daemon = new Daemon(config, logger);
75
+ await daemon.start();
76
+ return;
77
+ }
78
+ throw new Error(`Unknown cloud runtime command: ${command}`);
79
+ }
80
+ export function formatCloudRuntimeUsage(binaryName, nestedCommandName) {
81
+ const prefix = nestedCommandName ? `${binaryName} ${nestedCommandName}` : binaryName;
82
+ return `Usage: ${prefix} status [--config <path>]
83
+ ${prefix} pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]
84
+ ${prefix} connect [--config <path>] [--verbose]
85
+ ${prefix} disconnect [--config <path>]
86
+
87
+ Options:
88
+ --config <path> Path to YAML config file (default: ~/.gsd/daemon.yaml)
89
+ --gateway <url> Cloud MCP Gateway URL
90
+ --code <code> Pairing code from the Cloud MCP Gateway
91
+ --runtime-name <name> Friendly name for this Local GSD Runtime
92
+ --verbose Print log entries to stderr in addition to the log file
93
+ --help Show this help message and exit
94
+ `;
95
+ }
96
+ //# sourceMappingURL=cloud-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-cli.js","sourceRoot":"","sources":["../src/cloud-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseArgs } from \"node:util\";\nimport { resolveConfigPath, loadConfig } from \"./config.js\";\nimport { Logger } from \"./logger.js\";\nimport { Daemon } from \"./daemon.js\";\nimport { clearCloudConfig, exchangePairingCode, redactedCloudStatus, saveCloudConfig } from \"./cloud-config.js\";\n\nexport const CLOUD_COMMAND_USAGE = `Commands:\n status [--config <path>]\n pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]\n connect [--config <path>] [--verbose]\n disconnect [--config <path>]\n`;\n\nexport async function handleCloudRuntimeCommand(argv: string[], opts: {\n binaryName: string;\n nestedCommandName?: string;\n}): Promise<void> {\n if (argv[0] === \"--help\" || argv[0] === \"-h\") {\n process.stdout.write(formatCloudRuntimeUsage(opts.binaryName, opts.nestedCommandName));\n process.exit(0);\n }\n\n const command = argv[0];\n const { values } = parseArgs({\n args: argv.slice(1),\n options: {\n config: { type: \"string\", short: \"c\" },\n gateway: { type: \"string\" },\n code: { type: \"string\" },\n \"runtime-name\": { type: \"string\" },\n verbose: { type: \"boolean\", short: \"v\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n strict: true,\n });\n\n if (values.help || !command) {\n process.stdout.write(formatCloudRuntimeUsage(opts.binaryName, opts.nestedCommandName));\n process.exit(0);\n }\n\n const configPath = resolveConfigPath(values.config);\n if (command === \"status\") {\n process.stdout.write(`${JSON.stringify(redactedCloudStatus(loadConfig(configPath)), null, 2)}\\n`);\n return;\n }\n\n if (command === \"disconnect\") {\n clearCloudConfig(configPath);\n process.stdout.write(`${opts.binaryName}: cloud runtime disconnected locally.\\n`);\n return;\n }\n\n if (command === \"pair\") {\n if (!values.gateway || !values.code) {\n throw new Error(\"pair requires --gateway and --code\");\n }\n const runtimeName = values[\"runtime-name\"];\n const result = await exchangePairingCode({\n gatewayUrl: values.gateway,\n code: values.code,\n runtimeName,\n });\n saveCloudConfig(configPath, {\n gateway_url: values.gateway,\n device_token: result.deviceToken,\n runtime_id: result.runtimeId,\n ...(runtimeName ? { runtime_name: runtimeName } : {}),\n enabled: true,\n });\n process.stdout.write(`${opts.binaryName}: paired cloud runtime ${result.runtimeId}.\\n`);\n return;\n }\n\n if (command === \"connect\") {\n const config = loadConfig(configPath);\n if (!config.cloud?.device_token || !config.cloud.runtime_id) {\n throw new Error(\"cloud runtime is not paired; run `pair` first\");\n }\n const logger = new Logger({\n filePath: config.log.file,\n level: config.log.level,\n verbose: values.verbose,\n });\n const daemon = new Daemon(config, logger);\n await daemon.start();\n return;\n }\n\n throw new Error(`Unknown cloud runtime command: ${command}`);\n}\n\nexport function formatCloudRuntimeUsage(binaryName: string, nestedCommandName?: string): string {\n const prefix = nestedCommandName ? `${binaryName} ${nestedCommandName}` : binaryName;\n return `Usage: ${prefix} status [--config <path>]\n ${prefix} pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]\n ${prefix} connect [--config <path>] [--verbose]\n ${prefix} disconnect [--config <path>]\n\nOptions:\n --config <path> Path to YAML config file (default: ~/.gsd/daemon.yaml)\n --gateway <url> Cloud MCP Gateway URL\n --code <code> Pairing code from the Cloud MCP Gateway\n --runtime-name <name> Friendly name for this Local GSD Runtime\n --verbose Print log entries to stderr in addition to the log file\n --help Show this help message and exit\n`;\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { LookupFunction } from "node:net";
2
+ import type { DaemonConfig } from "./types.js";
3
+ export interface PairingExchangeResult {
4
+ runtimeId: string;
5
+ deviceToken: string;
6
+ }
7
+ export declare function exchangePairingCode(params: {
8
+ gatewayUrl: string;
9
+ code: string;
10
+ runtimeName?: string;
11
+ }): Promise<PairingExchangeResult>;
12
+ export declare function parseCloudGatewayUrl(value: string): URL;
13
+ export declare function saveCloudConfig(configPath: string, nextCloud: NonNullable<DaemonConfig["cloud"]>): DaemonConfig;
14
+ export declare function clearCloudConfig(configPath: string): DaemonConfig;
15
+ export declare function redactedCloudStatus(config: DaemonConfig): Record<string, unknown>;
16
+ export declare function validateGatewayNetworkTarget(url: URL): void;
17
+ export declare function createGatewayLookup(url: URL): LookupFunction;
18
+ //# sourceMappingURL=cloud-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-config.d.ts","sourceRoot":"","sources":["../src/cloud-config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAK/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAUjC;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CA2BvD;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,YAAY,CAgB/G;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAWjE;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWjF;AAeD,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAK3D;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,cAAc,CAc5D"}