commandmate 0.2.13 → 0.3.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 (154) hide show
  1. package/.env.example +21 -2
  2. package/.next/BUILD_ID +1 -1
  3. package/.next/app-build-manifest.json +32 -24
  4. package/.next/app-path-routes-manifest.json +1 -1
  5. package/.next/build-manifest.json +7 -7
  6. package/.next/cache/.tsbuildinfo +1 -1
  7. package/.next/cache/config.json +3 -3
  8. package/.next/cache/webpack/client-production/0.pack +0 -0
  9. package/.next/cache/webpack/client-production/1.pack +0 -0
  10. package/.next/cache/webpack/client-production/2.pack +0 -0
  11. package/.next/cache/webpack/client-production/index.pack +0 -0
  12. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  13. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  14. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  15. package/.next/cache/webpack/server-production/0.pack +0 -0
  16. package/.next/cache/webpack/server-production/index.pack +0 -0
  17. package/.next/next-server.js.nft.json +1 -1
  18. package/.next/prerender-manifest.json +1 -1
  19. package/.next/react-loadable-manifest.json +2 -2
  20. package/.next/required-server-files.json +1 -1
  21. package/.next/routes-manifest.json +1 -1
  22. package/.next/server/app/_not-found/page.js +1 -1
  23. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  24. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/app/update-check/route.js +1 -1
  26. package/.next/server/app/api/auth/login/route.js +1 -0
  27. package/.next/server/app/api/auth/login/route.js.nft.json +1 -0
  28. package/.next/server/app/api/auth/logout/route.js +1 -0
  29. package/.next/server/app/api/auth/logout/route.js.nft.json +1 -0
  30. package/.next/server/app/api/auth/status/route.js +1 -0
  31. package/.next/server/app/api/auth/status/route.js.nft.json +1 -0
  32. package/.next/server/app/api/hooks/claude-done/route.js +1 -1
  33. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  34. package/.next/server/app/api/repositories/route.js +2 -2
  35. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  36. package/.next/server/app/api/slash-commands/route.js +1 -1
  37. package/.next/server/app/api/slash-commands/route.js.nft.json +1 -1
  38. package/.next/server/app/api/slash-commands.body +1 -1
  39. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  40. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  41. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  42. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  43. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  44. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  45. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  46. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  47. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
  48. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  49. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  50. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  51. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  52. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  53. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  54. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  55. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  56. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
  57. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
  58. package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
  59. package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
  60. package/.next/server/app/api/worktrees/route.js +1 -1
  61. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  62. package/.next/server/app/login/page.js +1 -0
  63. package/.next/server/app/login/page.js.nft.json +1 -0
  64. package/.next/server/app/login/page_client-reference-manifest.js +1 -0
  65. package/.next/server/app/page.js +2 -2
  66. package/.next/server/app/page.js.nft.json +1 -1
  67. package/.next/server/app/page_client-reference-manifest.js +1 -1
  68. package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
  69. package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
  70. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  71. package/.next/server/app/worktrees/[id]/page.js +2 -2
  72. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  73. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  74. package/.next/server/app/worktrees/[id]/terminal/page.js +1 -1
  75. package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
  76. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  77. package/.next/server/app-paths-manifest.json +12 -8
  78. package/.next/server/chunks/3013.js +1 -0
  79. package/.next/server/chunks/3074.js +1 -0
  80. package/.next/server/chunks/{1287.js → 3294.js} +2 -2
  81. package/.next/server/chunks/3860.js +1 -1
  82. package/.next/server/chunks/4893.js +2 -2
  83. package/.next/server/chunks/539.js +35 -0
  84. package/.next/server/chunks/5795.js +1 -0
  85. package/.next/server/chunks/7536.js +1 -1
  86. package/.next/server/chunks/7566.js +19 -0
  87. package/.next/server/chunks/8693.js +1 -1
  88. package/.next/server/edge-runtime-webpack.js +2 -0
  89. package/.next/server/edge-runtime-webpack.js.map +1 -0
  90. package/.next/server/functions-config-manifest.json +1 -1
  91. package/.next/server/middleware-build-manifest.js +1 -1
  92. package/.next/server/middleware-manifest.json +28 -2
  93. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  94. package/.next/server/pages/500.html +1 -1
  95. package/.next/server/server-reference-manifest.json +1 -1
  96. package/.next/server/src/middleware.js +14 -0
  97. package/.next/server/src/middleware.js.map +1 -0
  98. package/.next/static/chunks/{2626.2125083a1ff3b80a.js → 6163.f672451d4575decf.js} +1 -1
  99. package/.next/static/chunks/{656.d72f25ce819bd77e.js → 656.5e2de0173f5a06bd.js} +1 -1
  100. package/.next/static/chunks/8091-925542bdfc843dce.js +1 -0
  101. package/.next/static/chunks/8528-4d554d3b94d4cf9b.js +1 -0
  102. package/.next/static/chunks/app/{layout-07755491d5d57242.js → layout-9110f9a5e41c6bf4.js} +1 -1
  103. package/.next/static/chunks/app/login/page-2d42204ba87cd136.js +1 -0
  104. package/.next/static/chunks/app/page-238b5a70d8c101e9.js +1 -0
  105. package/.next/static/chunks/app/worktrees/[id]/page-9418e49bdc1de02c.js +1 -0
  106. package/.next/static/chunks/main-db79434ee4a6c931.js +1 -0
  107. package/.next/static/chunks/webpack-3c0ee3ce5b546818.js +1 -0
  108. package/.next/static/css/b9ea6a4fad17dc32.css +3 -0
  109. package/.next/trace +5 -5
  110. package/.next/types/app/api/auth/login/route.ts +343 -0
  111. package/.next/types/app/api/auth/logout/route.ts +343 -0
  112. package/.next/types/app/api/auth/status/route.ts +343 -0
  113. package/.next/types/app/login/page.ts +79 -0
  114. package/README.md +6 -1
  115. package/dist/cli/commands/init.d.ts.map +1 -1
  116. package/dist/cli/commands/init.js +2 -0
  117. package/dist/cli/commands/start.d.ts +2 -0
  118. package/dist/cli/commands/start.d.ts.map +1 -1
  119. package/dist/cli/commands/start.js +159 -14
  120. package/dist/cli/commands/status.d.ts.map +1 -1
  121. package/dist/cli/commands/status.js +4 -0
  122. package/dist/cli/config/security-messages.d.ts +3 -1
  123. package/dist/cli/config/security-messages.d.ts.map +1 -1
  124. package/dist/cli/config/security-messages.js +6 -2
  125. package/dist/cli/index.js +17 -0
  126. package/dist/cli/types/index.d.ts +17 -0
  127. package/dist/cli/types/index.d.ts.map +1 -1
  128. package/dist/cli/utils/daemon.d.ts.map +1 -1
  129. package/dist/cli/utils/daemon.js +16 -3
  130. package/dist/config/auth-config.d.ts +43 -0
  131. package/dist/config/auth-config.d.ts.map +1 -0
  132. package/dist/config/auth-config.js +112 -0
  133. package/dist/lib/auth.d.ts +104 -0
  134. package/dist/lib/auth.d.ts.map +1 -0
  135. package/dist/lib/auth.js +250 -0
  136. package/dist/server/server.js +123 -12
  137. package/dist/server/src/config/auth-config.js +112 -0
  138. package/dist/server/src/lib/auth.js +250 -0
  139. package/dist/server/src/lib/auto-yes-manager.js +180 -96
  140. package/dist/server/src/lib/ip-restriction.js +241 -0
  141. package/dist/server/src/lib/ws-server.js +63 -33
  142. package/dist/server/src/types/slash-commands.js +1 -0
  143. package/package.json +2 -2
  144. package/.next/server/chunks/9238.js +0 -35
  145. package/.next/server/chunks/9367.js +0 -19
  146. package/.next/static/chunks/5970-2e18108d0cabd8af.js +0 -1
  147. package/.next/static/chunks/816-af44cb865b0c980e.js +0 -1
  148. package/.next/static/chunks/app/page-a6593b9640df66a6.js +0 -1
  149. package/.next/static/chunks/app/worktrees/[id]/page-d9a7913679eccfd9.js +0 -1
  150. package/.next/static/chunks/main-f00f82f1cf18dd99.js +0 -1
  151. package/.next/static/chunks/webpack-e6531fcf859d9451.js +0 -1
  152. package/.next/static/css/897ffb669f47c97b.css +0 -3
  153. /package/.next/static/{oUEq-Bd47xtkJcFDOI6rr → clTo9tuAoPMLcGRuVENfO}/_buildManifest.js +0 -0
  154. /package/.next/static/{oUEq-Bd47xtkJcFDOI6rr → clTo9tuAoPMLcGRuVENfO}/_ssgManifest.js +0 -0
@@ -4,6 +4,7 @@
4
4
  * Issue #96: npm install CLI support
5
5
  * Issue #125: Use getEnvPath and getPidFilePath for correct path resolution
6
6
  * Issue #136: Add --issue and --auto-port flags for worktree support
7
+ * Issue #331: Add --auth, --auth-expire, --https, --cert, --key, --allow-http flags
7
8
  * Start CommandMate server
8
9
  */
9
10
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -21,11 +22,41 @@ const security_messages_1 = require("../config/security-messages");
21
22
  const input_validators_1 = require("../utils/input-validators");
22
23
  const port_allocator_1 = require("../utils/port-allocator");
23
24
  const resource_resolvers_1 = require("../utils/resource-resolvers");
25
+ const auth_1 = require("../../lib/auth");
24
26
  const logger = new logger_1.CLILogger();
27
+ /**
28
+ * Display the generated authentication token to the user.
29
+ * Called once after server start; the token is not persisted and cannot be retrieved later.
30
+ *
31
+ * @param token - The plaintext authentication token to display
32
+ */
33
+ function displayAuthToken(token) {
34
+ logger.info('');
35
+ logger.info('Authentication token (save this - it will not be shown again):');
36
+ logger.info(` ${token}`);
37
+ logger.info('');
38
+ }
39
+ /** HTTPS certificate warning when --auth is used without --cert/--key */
40
+ const HTTPS_WARNING = `
41
+ \x1b[1m\x1b[33mWARNING: Authentication enabled without HTTPS\x1b[0m
42
+
43
+ Token will be transmitted in plain text over HTTP.
44
+ For secure authentication, use HTTPS with a TLS certificate.
45
+
46
+ \x1b[1mQuick setup with mkcert:\x1b[0m
47
+ brew install mkcert # macOS
48
+ mkcert -install
49
+ mkcert localhost
50
+
51
+ commandmate start --auth --cert ./localhost.pem --key ./localhost-key.pem
52
+
53
+ Use --allow-http to suppress this warning.
54
+ `;
25
55
  /**
26
56
  * Execute start command
27
57
  * Issue #125: Use getEnvPath and getPidFilePath for correct path resolution
28
58
  * Issue #136: Support --issue and --auto-port flags for worktree servers
59
+ * Issue #331: Support --auth, --auth-expire, --https, --cert, --key, --allow-http
29
60
  */
30
61
  async function startCommand(options) {
31
62
  try {
@@ -38,6 +69,17 @@ async function startCommand(options) {
38
69
  return;
39
70
  }
40
71
  }
72
+ // Issue #331: Validate auth-expire format before proceeding
73
+ if (options.authExpire) {
74
+ try {
75
+ (0, auth_1.parseDuration)(options.authExpire);
76
+ }
77
+ catch (error) {
78
+ logger.error(`Invalid --auth-expire value: ${(0, types_1.getErrorMessage)(error)}`);
79
+ process.exit(types_1.ExitCode.CONFIG_ERROR);
80
+ return;
81
+ }
82
+ }
41
83
  // Issue #125: Check for .env file at correct location
42
84
  // Issue #136: Use issue number for worktree-specific paths
43
85
  const envPath = (0, env_setup_1.getEnvPath)(options.issue);
@@ -69,6 +111,58 @@ async function startCommand(options) {
69
111
  dbPath = dbResolver.resolve(options.issue);
70
112
  logger.info(`Using database: ${dbPath}`);
71
113
  }
114
+ // Issue #331: Generate token and hash if --auth is enabled
115
+ let authTokenHash;
116
+ let authToken;
117
+ if (options.auth) {
118
+ authToken = (0, auth_1.generateToken)();
119
+ authTokenHash = (0, auth_1.hashToken)(authToken);
120
+ }
121
+ // Issue #331: Validate --cert/--key must be specified together
122
+ if ((options.cert && !options.key) || (!options.cert && options.key)) {
123
+ logger.error('--cert and --key must be specified together');
124
+ process.exit(types_1.ExitCode.CONFIG_ERROR);
125
+ return;
126
+ }
127
+ // Issue #331: Determine protocol (matches server.ts: cert+key → always HTTPS)
128
+ const hasCert = !!(options.cert && options.key);
129
+ const protocol = hasCert ? 'https' : 'http';
130
+ // Issue #331: HTTPS certificate validation
131
+ if (hasCert) {
132
+ if (!(0, fs_1.existsSync)(options.cert)) {
133
+ logger.error(`Certificate file not found: ${options.cert}`);
134
+ process.exit(types_1.ExitCode.CONFIG_ERROR);
135
+ return;
136
+ }
137
+ if (!(0, fs_1.existsSync)(options.key)) {
138
+ logger.error(`Key file not found: ${options.key}`);
139
+ process.exit(types_1.ExitCode.CONFIG_ERROR);
140
+ return;
141
+ }
142
+ }
143
+ // Issue #331: Warn about --auth without HTTPS
144
+ if (options.auth && !hasCert && !options.allowHttp) {
145
+ console.log(HTTPS_WARNING);
146
+ }
147
+ // Load .env to check for legacy CM_AUTH_TOKEN
148
+ const mainEnvResult = (0, dotenv_1.config)({ path: mainEnvPath });
149
+ let envResult = mainEnvResult;
150
+ if (options.issue !== undefined && (0, fs_1.existsSync)(envPath) && envPath !== mainEnvPath) {
151
+ envResult = (0, dotenv_1.config)({ path: envPath, override: true });
152
+ }
153
+ // Issue #331: Warn about legacy CM_AUTH_TOKEN
154
+ const allEnvParsed = {
155
+ ...(mainEnvResult.parsed || {}),
156
+ ...(envResult.parsed || {}),
157
+ };
158
+ if (allEnvParsed.CM_AUTH_TOKEN) {
159
+ logger.warn('CM_AUTH_TOKEN found in .env file. This is no longer used.');
160
+ logger.warn('Use "commandmate start --auth" instead. The token is now generated automatically.');
161
+ }
162
+ if (mainEnvResult.error) {
163
+ logger.warn(`Failed to load .env file at ${mainEnvPath}: ${mainEnvResult.error.message}`);
164
+ logger.info('Continuing with existing environment variables');
165
+ }
72
166
  // Daemon mode
73
167
  if (options.daemon) {
74
168
  // Check if already running
@@ -79,23 +173,57 @@ async function startCommand(options) {
79
173
  return;
80
174
  }
81
175
  logger.info(`Starting ${serverLabel} in background...`);
176
+ // Issue #331: Set auth environment variables in process.env so daemon.ts can forward them
177
+ // to the child process. daemon.ts reads process.env[key] to build the child env object.
178
+ if (authTokenHash) {
179
+ process.env.CM_AUTH_TOKEN_HASH = authTokenHash;
180
+ }
181
+ if (options.authExpire) {
182
+ process.env.CM_AUTH_EXPIRE = options.authExpire;
183
+ }
184
+ if (options.cert) {
185
+ process.env.CM_HTTPS_CERT = options.cert;
186
+ }
187
+ if (options.key) {
188
+ process.env.CM_HTTPS_KEY = options.key;
189
+ }
190
+ if (options.allowHttp) {
191
+ process.env.CM_ALLOW_HTTP = '1';
192
+ }
193
+ // Issue #332: Set IP restriction env vars for daemon mode
194
+ if (options.allowedIps) {
195
+ process.env.CM_ALLOWED_IPS = options.allowedIps;
196
+ }
197
+ if (options.trustProxy) {
198
+ process.env.CM_TRUST_PROXY = 'true';
199
+ }
82
200
  try {
83
201
  const pid = await daemonManager.start({
84
202
  dev: options.dev,
85
203
  port: port,
86
204
  // Issue #136: Pass DB path for worktree servers
87
205
  dbPath: dbPath,
206
+ // Issue #331: Pass auth and HTTPS options
207
+ auth: options.auth,
208
+ authExpire: options.authExpire,
209
+ cert: options.cert,
210
+ key: options.key,
211
+ allowHttp: options.allowHttp,
88
212
  });
89
213
  logger.success(`${serverLabel} started in background (PID: ${pid})`);
90
214
  const actualPort = port || parseInt(process.env.CM_PORT || '3000', 10);
91
215
  const bind = process.env.CM_BIND || '127.0.0.1';
92
- const url = `http://${bind === '0.0.0.0' ? '127.0.0.1' : bind}:${actualPort}`;
216
+ const url = `${protocol}://${bind === '0.0.0.0' ? '127.0.0.1' : bind}:${actualPort}`;
93
217
  logger.info(`URL: ${url}`);
218
+ // Issue #331: Show token to user (shown once, not persisted)
219
+ if (authToken) {
220
+ displayAuthToken(authToken);
221
+ }
94
222
  (0, security_logger_1.logSecurityEvent)({
95
223
  timestamp: new Date().toISOString(),
96
224
  command: 'start',
97
225
  action: 'success',
98
- details: `Daemon started (PID: ${pid})${options.issue !== undefined ? ` (Issue #${options.issue})` : ''}`,
226
+ details: `Daemon started (PID: ${pid})${options.issue !== undefined ? ` (Issue #${options.issue})` : ''}${options.auth ? ' [auth enabled]' : ''}`,
99
227
  });
100
228
  process.exit(types_1.ExitCode.SUCCESS);
101
229
  }
@@ -115,17 +243,6 @@ async function startCommand(options) {
115
243
  // Foreground mode (default)
116
244
  const npmScript = options.dev ? 'dev' : 'start';
117
245
  logger.info(`Starting ${serverLabel} in foreground (${options.dev ? 'development' : 'production'} mode)...`);
118
- // Issue #125: Load .env file from correct location (same as daemon mode)
119
- // Issue #136: Load main .env first, then overlay worktree-specific .env if exists
120
- const mainEnvResult = (0, dotenv_1.config)({ path: mainEnvPath });
121
- let envResult = mainEnvResult;
122
- if (options.issue !== undefined && (0, fs_1.existsSync)(envPath) && envPath !== mainEnvPath) {
123
- envResult = (0, dotenv_1.config)({ path: envPath, override: true });
124
- }
125
- if (mainEnvResult.error) {
126
- logger.warn(`Failed to load .env file at ${mainEnvPath}: ${mainEnvResult.error.message}`);
127
- logger.info('Continuing with existing environment variables');
128
- }
129
246
  // Build environment by merging process.env with .env values
130
247
  const env = {
131
248
  ...process.env,
@@ -140,11 +257,39 @@ async function startCommand(options) {
140
257
  if (dbPath) {
141
258
  env.CM_DB_PATH = dbPath;
142
259
  }
260
+ // Issue #331: Set auth environment variables
261
+ if (authTokenHash) {
262
+ env.CM_AUTH_TOKEN_HASH = authTokenHash;
263
+ }
264
+ if (options.authExpire) {
265
+ env.CM_AUTH_EXPIRE = options.authExpire;
266
+ }
267
+ if (options.cert) {
268
+ env.CM_HTTPS_CERT = options.cert;
269
+ }
270
+ if (options.key) {
271
+ env.CM_HTTPS_KEY = options.key;
272
+ }
273
+ if (options.allowHttp) {
274
+ env.CM_ALLOW_HTTP = '1';
275
+ }
276
+ // Issue #332: Set IP restriction env vars for foreground mode
277
+ if (options.allowedIps) {
278
+ env.CM_ALLOWED_IPS = options.allowedIps;
279
+ }
280
+ if (options.trustProxy) {
281
+ env.CM_TRUST_PROXY = 'true';
282
+ }
143
283
  // Issue #179: Security warning for external access - recommend reverse proxy
144
284
  const bindAddress = env.CM_BIND || '127.0.0.1';
145
- if (bindAddress === '0.0.0.0') {
285
+ // Issue #332: Suppress warning when --allowed-ips is set (IP restriction provides access control)
286
+ if (bindAddress === '0.0.0.0' && !options.auth && !options.allowedIps) {
146
287
  console.log(security_messages_1.REVERSE_PROXY_WARNING);
147
288
  }
289
+ // Issue #331: Show token to user (foreground mode, shown once, not persisted)
290
+ if (authToken) {
291
+ displayAuthToken(authToken);
292
+ }
148
293
  // Use package installation directory, not current working directory
149
294
  const packageRoot = (0, paths_1.getPackageRoot)();
150
295
  const child = (0, child_process_1.spawn)('npm', ['run', npmScript], {
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAA6B,aAAa,EAAE,MAAM,UAAU,CAAC;AAqFpE;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6E9E"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAA6B,aAAa,EAAE,MAAM,UAAU,CAAC;AAqFpE;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkF9E"}
@@ -140,6 +140,10 @@ async function statusCommand(options = {}) {
140
140
  if (status.url) {
141
141
  console.log(`URL: ${status.url}`);
142
142
  }
143
+ // Issue #332: Show IP restriction status
144
+ if (process.env.CM_ALLOWED_IPS) {
145
+ console.log(`IP ACL: ${process.env.CM_ALLOWED_IPS}`);
146
+ }
143
147
  console.log('');
144
148
  process.exit(types_1.ExitCode.SUCCESS);
145
149
  }
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Security Messages
3
3
  * Issue #179: Reverse proxy authentication recommendation
4
+ * Issue #331: Updated to include --auth option
4
5
  * Shared warning constants for CLI commands (DRY principle)
5
6
  *
6
7
  * [SF-002] GitHub URLs imported from github-links.ts (DRY)
7
8
  */
8
9
  /**
9
- * Warning message displayed when CM_BIND=0.0.0.0
10
+ * Warning message displayed when CM_BIND=0.0.0.0 and --auth is not enabled
10
11
  * Used by: init.ts, start.ts, daemon.ts
12
+ * Issue #331: Added --auth as a recommended option
11
13
  */
12
14
  export declare const REVERSE_PROXY_WARNING: string;
13
15
  //# sourceMappingURL=security-messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"security-messages.d.ts","sourceRoot":"","sources":["../../../src/cli/config/security-messages.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAgBjC,CAAC"}
1
+ {"version":3,"file":"security-messages.d.ts","sourceRoot":"","sources":["../../../src/cli/config/security-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAkBjC,CAAC"}
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * Security Messages
4
4
  * Issue #179: Reverse proxy authentication recommendation
5
+ * Issue #331: Updated to include --auth option
5
6
  * Shared warning constants for CLI commands (DRY principle)
6
7
  *
7
8
  * [SF-002] GitHub URLs imported from github-links.ts (DRY)
@@ -10,20 +11,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
11
  exports.REVERSE_PROXY_WARNING = void 0;
11
12
  const github_links_1 = require("../../config/github-links");
12
13
  /**
13
- * Warning message displayed when CM_BIND=0.0.0.0
14
+ * Warning message displayed when CM_BIND=0.0.0.0 and --auth is not enabled
14
15
  * Used by: init.ts, start.ts, daemon.ts
16
+ * Issue #331: Added --auth as a recommended option
15
17
  */
16
18
  exports.REVERSE_PROXY_WARNING = `
17
19
  \x1b[1m\x1b[31mWARNING: Server is exposed to external networks without authentication\x1b[0m
18
20
 
19
21
  Exposing the server without reverse proxy authentication
20
- is a serious security risk.
22
+ or built-in token auth is a serious security risk.
21
23
 
22
24
  \x1b[1mRisks:\x1b[0m
23
25
  File read/write/delete and command execution
24
26
  become accessible to third parties.
25
27
 
26
28
  \x1b[1mRecommended authentication methods:\x1b[0m
29
+ - commandmate start --auth (built-in token auth)
30
+ - commandmate start --allowed-ips 192.168.1.0/24 (IP restriction)
27
31
  - Nginx + Basic Auth
28
32
  - Cloudflare Access
29
33
  - Tailscale
package/dist/cli/index.js CHANGED
@@ -33,6 +33,7 @@ program
33
33
  });
34
34
  // Start command
35
35
  // Issue #136: Add --issue and --auto-port flags for worktree support
36
+ // Issue #331: Add --auth, --auth-expire, --https, --cert, --key, --allow-http flags
36
37
  program
37
38
  .command('start')
38
39
  .description('Start the CommandMate server')
@@ -41,6 +42,14 @@ program
41
42
  .option('-p, --port <number>', 'Override port number', parseInt)
42
43
  .option('-i, --issue <number>', 'Start server for specific issue worktree', parseInt)
43
44
  .option('--auto-port', 'Automatically allocate port for worktree server')
45
+ .option('--auth', 'Enable token authentication')
46
+ .option('--auth-expire <duration>', 'Token expiration (e.g., 24h, 7d, 90m)')
47
+ .option('--https', 'Enable HTTPS')
48
+ .option('--cert <path>', 'Path to TLS certificate file')
49
+ .option('--key <path>', 'Path to TLS private key file')
50
+ .option('--allow-http', 'Suppress HTTPS warning when using --auth without certificates')
51
+ .option('--allowed-ips <cidrs>', 'Allowed IP addresses/CIDR ranges (comma-separated)')
52
+ .option('--trust-proxy', 'Trust X-Forwarded-For header from reverse proxy')
44
53
  .action(async (options) => {
45
54
  await (0, start_1.startCommand)({
46
55
  dev: options.dev,
@@ -48,6 +57,14 @@ program
48
57
  port: options.port,
49
58
  issue: options.issue,
50
59
  autoPort: options.autoPort,
60
+ auth: options.auth,
61
+ authExpire: options.authExpire,
62
+ https: options.https,
63
+ cert: options.cert,
64
+ key: options.key,
65
+ allowHttp: options.allowHttp,
66
+ allowedIps: options.allowedIps,
67
+ trustProxy: options.trustProxy,
51
68
  });
52
69
  });
53
70
  // Stop command
@@ -26,6 +26,7 @@ export interface InitOptions {
26
26
  /**
27
27
  * Options for start command
28
28
  * Issue #136: Added issue and autoPort for worktree support
29
+ * Issue #331: Added auth, authExpire, https, cert, key, allowHttp for token auth and HTTPS
29
30
  */
30
31
  export interface StartOptions {
31
32
  /** Start in development mode */
@@ -40,6 +41,22 @@ export interface StartOptions {
40
41
  autoPort?: boolean;
41
42
  /** Override database path for worktree server (Issue #136) */
42
43
  dbPath?: string;
44
+ /** Enable token authentication (Issue #331) */
45
+ auth?: boolean;
46
+ /** Token expiration duration (e.g., "24h", "7d") (Issue #331) */
47
+ authExpire?: string;
48
+ /** Enable HTTPS (Issue #331) */
49
+ https?: boolean;
50
+ /** Path to TLS certificate file (Issue #331) */
51
+ cert?: string;
52
+ /** Path to TLS private key file (Issue #331) */
53
+ key?: string;
54
+ /** Suppress HTTPS warning when using --auth without certificates (Issue #331) */
55
+ allowHttp?: boolean;
56
+ /** Issue #332: Allowed IP addresses/CIDR ranges (comma-separated) */
57
+ allowedIps?: string;
58
+ /** Issue #332: Trust X-Forwarded-For header from reverse proxy */
59
+ trustProxy?: boolean;
43
60
  }
44
61
  /**
45
62
  * Options for stop command
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/types/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/daemon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOtD;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAAY;gBAEd,WAAW,EAAE,MAAM;IAK/B;;;;;OAKG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAiFnD;;;;OAIG;IACG,IAAI,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IA8BpD;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IA6B/C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC;;OAEG;YACW,WAAW;CAiB1B"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/daemon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOtD;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAAY;gBAEd,WAAW,EAAE,MAAM;IAK/B;;;;;OAKG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA6FnD;;;;OAIG;IACG,IAAI,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IA8BpD;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IA+B/C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC;;OAEG;YACW,WAAW;CAiB1B"}
@@ -64,14 +64,25 @@ class DaemonManager {
64
64
  if (options.dbPath) {
65
65
  env.CM_DB_PATH = options.dbPath;
66
66
  }
67
+ // Issue #331: Forward auth and HTTPS environment variables from parent process to daemon.
68
+ // These are set by startCommand before calling daemon.start().
69
+ // Issue #332: Added CM_ALLOWED_IPS, CM_TRUST_PROXY for IP restriction in daemon mode
70
+ const authEnvKeys = ['CM_AUTH_TOKEN_HASH', 'CM_AUTH_EXPIRE', 'CM_HTTPS_CERT', 'CM_HTTPS_KEY', 'CM_ALLOW_HTTP', 'CM_ALLOWED_IPS', 'CM_TRUST_PROXY'];
71
+ for (const key of authEnvKeys) {
72
+ if (process.env[key]) {
73
+ env[key] = process.env[key];
74
+ }
75
+ }
67
76
  // Issue #179: Security warning for external access - recommend reverse proxy
68
77
  const bindAddress = env.CM_BIND || '127.0.0.1';
69
78
  const port = env.CM_PORT || '3000';
70
- if (bindAddress === '0.0.0.0') {
79
+ const protocol = env.CM_HTTPS_CERT ? 'https' : 'http';
80
+ // Issue #332: Suppress warning when CM_ALLOWED_IPS is set (IP restriction provides access control)
81
+ if (bindAddress === '0.0.0.0' && !env.CM_AUTH_TOKEN_HASH && !env.CM_ALLOWED_IPS) {
71
82
  console.log(security_messages_1.REVERSE_PROXY_WARNING);
72
83
  }
73
84
  // Log startup with accurate settings (Stage 4 review: MF-2)
74
- this.logger.info(`Starting server at http://${bindAddress}:${port}`);
85
+ this.logger.info(`Starting server at ${protocol}://${bindAddress}:${port}`);
75
86
  // Spawn detached process
76
87
  const child = (0, child_process_1.spawn)('npm', ['run', npmScript], {
77
88
  cwd: packageRoot,
@@ -139,7 +150,9 @@ class DaemonManager {
139
150
  // Get port from environment or default
140
151
  const port = parseInt(process.env.CM_PORT || '3000', 10);
141
152
  const bind = process.env.CM_BIND || '127.0.0.1';
142
- const url = `http://${bind === '0.0.0.0' ? '127.0.0.1' : bind}:${port}`;
153
+ // Issue #331: Use HTTPS protocol if certificate is configured
154
+ const protocol = process.env.CM_HTTPS_CERT ? 'https' : 'http';
155
+ const url = `${protocol}://${bind === '0.0.0.0' ? '127.0.0.1' : bind}:${port}`;
143
156
  // Note: Getting accurate uptime would require storing start time
144
157
  // For now, we don't track uptime
145
158
  return {
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Authentication Configuration Constants
3
+ * Issue #331: Shared constants for auth.ts and middleware.ts
4
+ *
5
+ * CONSTRAINT: This module must be Edge Runtime compatible.
6
+ * No Node.js-specific imports (crypto, fs, etc.) are allowed.
7
+ */
8
+ /** Cookie name for authentication token */
9
+ export declare const AUTH_COOKIE_NAME: "cm_auth_token";
10
+ /**
11
+ * Validate that a CM_AUTH_TOKEN_HASH value is a well-formed SHA-256 hex string.
12
+ * Used by both auth.ts and middleware.ts to ensure consistent auth-enabled detection.
13
+ * Returns a type predicate so callers can narrow the type after checking.
14
+ *
15
+ * @param hash - The hash string to validate
16
+ * @returns true if the hash is a valid 64-character hex string
17
+ */
18
+ export declare function isValidTokenHash(hash: string | undefined): hash is string;
19
+ /**
20
+ * Paths excluded from authentication check.
21
+ * S002: Must use === for matching (no startsWith - bypass attack prevention)
22
+ */
23
+ export declare const AUTH_EXCLUDED_PATHS: readonly ["/login", "/api/auth/login", "/api/auth/logout", "/api/auth/status"];
24
+ /** Default token expiration duration (24 hours) */
25
+ export declare const DEFAULT_EXPIRE_DURATION_MS: number;
26
+ /**
27
+ * Parse a duration string into milliseconds.
28
+ * Supported formats: Nh (hours), Nd (days), Nm (minutes)
29
+ * Minimum: 1h, Maximum: 30d
30
+ *
31
+ * @param s - Duration string (e.g., "24h", "7d", "90m")
32
+ * @returns Duration in milliseconds
33
+ * @throws Error if format is invalid or out of range
34
+ */
35
+ export declare function parseDuration(s: string): number;
36
+ /**
37
+ * Compute token expiration timestamp from environment variables.
38
+ * Used by both auth.ts (Node.js) and middleware.ts (Edge Runtime).
39
+ *
40
+ * @returns Expiration timestamp (ms since epoch), or null if auth is not enabled
41
+ */
42
+ export declare function computeExpireAt(): number | null;
43
+ //# sourceMappingURL=auth-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-config.d.ts","sourceRoot":"","sources":["../../src/config/auth-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2CAA2C;AAC3C,eAAO,MAAM,gBAAgB,EAAG,eAAwB,CAAC;AAKzD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,IAAI,MAAM,CAEzE;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,gFAKtB,CAAC;AAeX,mDAAmD;AACnD,eAAO,MAAM,0BAA0B,QAAmB,CAAC;AAQ3D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAgC/C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAgB/C"}
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /**
3
+ * Authentication Configuration Constants
4
+ * Issue #331: Shared constants for auth.ts and middleware.ts
5
+ *
6
+ * CONSTRAINT: This module must be Edge Runtime compatible.
7
+ * No Node.js-specific imports (crypto, fs, etc.) are allowed.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.DEFAULT_EXPIRE_DURATION_MS = exports.AUTH_EXCLUDED_PATHS = exports.AUTH_COOKIE_NAME = void 0;
11
+ exports.isValidTokenHash = isValidTokenHash;
12
+ exports.parseDuration = parseDuration;
13
+ exports.computeExpireAt = computeExpireAt;
14
+ /** Cookie name for authentication token */
15
+ exports.AUTH_COOKIE_NAME = 'cm_auth_token';
16
+ /** Valid SHA-256 hex string pattern: exactly 64 hex characters */
17
+ const VALID_TOKEN_HASH_PATTERN = /^[0-9a-f]{64}$/;
18
+ /**
19
+ * Validate that a CM_AUTH_TOKEN_HASH value is a well-formed SHA-256 hex string.
20
+ * Used by both auth.ts and middleware.ts to ensure consistent auth-enabled detection.
21
+ * Returns a type predicate so callers can narrow the type after checking.
22
+ *
23
+ * @param hash - The hash string to validate
24
+ * @returns true if the hash is a valid 64-character hex string
25
+ */
26
+ function isValidTokenHash(hash) {
27
+ return !!hash && VALID_TOKEN_HASH_PATTERN.test(hash);
28
+ }
29
+ /**
30
+ * Paths excluded from authentication check.
31
+ * S002: Must use === for matching (no startsWith - bypass attack prevention)
32
+ */
33
+ exports.AUTH_EXCLUDED_PATHS = [
34
+ '/login',
35
+ '/api/auth/login',
36
+ '/api/auth/logout',
37
+ '/api/auth/status',
38
+ ];
39
+ // ============================================================
40
+ // Duration Parsing (Edge Runtime compatible)
41
+ // ============================================================
42
+ /** Milliseconds in one minute */
43
+ const MS_PER_MINUTE = 60 * 1000;
44
+ /** Milliseconds in one hour */
45
+ const MS_PER_HOUR = 60 * MS_PER_MINUTE;
46
+ /** Milliseconds in one day */
47
+ const MS_PER_DAY = 24 * MS_PER_HOUR;
48
+ /** Default token expiration duration (24 hours) */
49
+ exports.DEFAULT_EXPIRE_DURATION_MS = 24 * MS_PER_HOUR;
50
+ /** Minimum duration: 1 hour */
51
+ const MIN_DURATION_MS = MS_PER_HOUR;
52
+ /** Maximum duration: 30 days */
53
+ const MAX_DURATION_MS = 30 * MS_PER_DAY;
54
+ /**
55
+ * Parse a duration string into milliseconds.
56
+ * Supported formats: Nh (hours), Nd (days), Nm (minutes)
57
+ * Minimum: 1h, Maximum: 30d
58
+ *
59
+ * @param s - Duration string (e.g., "24h", "7d", "90m")
60
+ * @returns Duration in milliseconds
61
+ * @throws Error if format is invalid or out of range
62
+ */
63
+ function parseDuration(s) {
64
+ const match = s.match(/^(\d+)([hdm])$/);
65
+ if (!match) {
66
+ throw new Error(`Invalid duration format: "${s}". Use Nh, Nd, or Nm (e.g., "24h", "7d", "90m")`);
67
+ }
68
+ const value = parseInt(match[1], 10);
69
+ const unit = match[2];
70
+ /** Map of duration unit characters to their millisecond multipliers */
71
+ const unitMultipliers = {
72
+ h: MS_PER_HOUR,
73
+ d: MS_PER_DAY,
74
+ m: MS_PER_MINUTE,
75
+ };
76
+ const multiplier = unitMultipliers[unit];
77
+ if (multiplier === undefined) {
78
+ throw new Error(`Invalid duration unit: "${unit}"`);
79
+ }
80
+ const ms = value * multiplier;
81
+ if (ms < MIN_DURATION_MS) {
82
+ throw new Error(`Duration too short: minimum is 1h (60m). Got: "${s}"`);
83
+ }
84
+ if (ms > MAX_DURATION_MS) {
85
+ throw new Error(`Duration too long: maximum is 30d (720h). Got: "${s}"`);
86
+ }
87
+ return ms;
88
+ }
89
+ /**
90
+ * Compute token expiration timestamp from environment variables.
91
+ * Used by both auth.ts (Node.js) and middleware.ts (Edge Runtime).
92
+ *
93
+ * @returns Expiration timestamp (ms since epoch), or null if auth is not enabled
94
+ */
95
+ function computeExpireAt() {
96
+ const expireStr = process.env.CM_AUTH_EXPIRE;
97
+ const now = Date.now();
98
+ if (expireStr) {
99
+ try {
100
+ return now + parseDuration(expireStr);
101
+ }
102
+ catch {
103
+ // Invalid duration format - use default
104
+ return now + exports.DEFAULT_EXPIRE_DURATION_MS;
105
+ }
106
+ }
107
+ // Default 24h if auth is enabled
108
+ if (process.env.CM_AUTH_TOKEN_HASH) {
109
+ return now + exports.DEFAULT_EXPIRE_DURATION_MS;
110
+ }
111
+ return null;
112
+ }