profclaw 0.0.1-beta.1 → 0.0.1-beta.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 (151) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/openclaw.js +1 -1
  3. package/dist/agents/executor.js +38 -6
  4. package/dist/auth/auth-service.js +8 -6
  5. package/dist/auth/device-identity.js +7 -5
  6. package/dist/auth/github-oauth.js +3 -1
  7. package/dist/auth/pairing-codes.js +6 -4
  8. package/dist/browser/service.js +5 -3
  9. package/dist/chat/execution/guardrails.js +3 -1
  10. package/dist/chat/execution/workflows/executor.js +4 -2
  11. package/dist/chat/memory.js +3 -1
  12. package/dist/chat/proactive/index.js +13 -11
  13. package/dist/chat/tools.js +8 -7
  14. package/dist/cli/commands/auth.js +22 -13
  15. package/dist/cli/commands/onboard.js +248 -59
  16. package/dist/cli/commands/setup.js +180 -104
  17. package/dist/cron/issue-poller.js +1 -1
  18. package/dist/hooks/agent-webhook.js +10 -7
  19. package/dist/hooks/prompt-submit.js +5 -6
  20. package/dist/hooks/session-end.js +10 -6
  21. package/dist/hooks/tool-use.js +9 -6
  22. package/dist/integrations/github-ticket-sync.js +8 -6
  23. package/dist/integrations/github.js +8 -6
  24. package/dist/integrations/jira.js +5 -3
  25. package/dist/mcp/server.js +12 -9
  26. package/dist/memory/experience-store.js +15 -14
  27. package/dist/notifications/slack.js +4 -2
  28. package/dist/providers/types.d.ts +2 -2
  29. package/dist/queue/failure-handler.js +15 -13
  30. package/dist/queue/index.js +6 -4
  31. package/dist/queue/memory-queue.js +23 -27
  32. package/dist/queue/task-queue.js +18 -22
  33. package/dist/routes/agents.js +3 -1
  34. package/dist/routes/auth.js +11 -9
  35. package/dist/routes/devices.js +17 -15
  36. package/dist/routes/gateway.js +3 -1
  37. package/dist/routes/hooks.js +7 -5
  38. package/dist/routes/import.js +10 -8
  39. package/dist/routes/labels.js +11 -9
  40. package/dist/routes/oobe.js +8 -6
  41. package/dist/routes/openapi.js +1 -1
  42. package/dist/routes/projects.js +27 -25
  43. package/dist/routes/settings.js +7 -5
  44. package/dist/routes/setup.js +12 -11
  45. package/dist/routes/slack.js +28 -27
  46. package/dist/routes/stats.js +6 -4
  47. package/dist/routes/summaries.js +4 -3
  48. package/dist/routes/sync.js +7 -5
  49. package/dist/routes/tasks.js +10 -8
  50. package/dist/routes/tickets.js +26 -26
  51. package/dist/routes/tokens.js +5 -3
  52. package/dist/routes/users.js +14 -12
  53. package/dist/routes/webhooks.js +5 -3
  54. package/dist/settings/index.js +3 -1
  55. package/dist/storage/index.d.ts +2 -1
  56. package/dist/storage/index.js +9 -4
  57. package/dist/storage/libsql.js +29 -21
  58. package/dist/storage/schema.d.ts +107 -46
  59. package/dist/summaries/storage.js +3 -1
  60. package/dist/tickets/index.js +10 -8
  61. package/dist/utils/circuit-breaker.js +10 -5
  62. package/dist/utils/logger.d.ts +5 -1
  63. package/dist/utils/logger.js +23 -7
  64. package/package.json +4 -3
  65. package/ui/dist/assets/{ActivityView-CkPgKhwm.js → ActivityView-DIPQSJ_d.js} +1 -1
  66. package/ui/dist/assets/{AgentList-C3SVdeEz.js → AgentList-CqyaYrcq.js} +1 -1
  67. package/ui/dist/assets/{AnalyticsDashboard-DVU--vlP.js → AnalyticsDashboard-C-mjuGHT.js} +1 -1
  68. package/ui/dist/assets/{AreaChart-B5qMEGWj.js → AreaChart-CfYWmbtr.js} +1 -1
  69. package/ui/dist/assets/{CartesianChart-B50bcUJi.js → CartesianChart-BaQ3It0c.js} +1 -1
  70. package/ui/dist/assets/{ChatView-BZFJs7w_.js → ChatView-DxxQy6pl.js} +1 -1
  71. package/ui/dist/assets/CostsDashboard-CDBBcGkg.js +1 -0
  72. package/ui/dist/assets/{EstimateSelect-CLpB4NQO.js → EstimateSelect-CDjf5qKl.js} +1 -1
  73. package/ui/dist/assets/{FloatingChatbot-CBzBY9a0.js → FloatingChatbot-CZHjvJIe.js} +1 -1
  74. package/ui/dist/assets/{InviteCodeManagement-DsbdPa6W.js → InviteCodeManagement-DTm-IPTm.js} +1 -1
  75. package/ui/dist/assets/{ProjectDetail-BEM3tFXg.js → ProjectDetail-DlYUcOss.js} +1 -1
  76. package/ui/dist/assets/{ProjectIcon-CN1FmFAk.js → ProjectIcon-BMRpnM04.js} +1 -1
  77. package/ui/dist/assets/{ProjectList-DSDaNhxQ.js → ProjectList-D85TN0i0.js} +1 -1
  78. package/ui/dist/assets/{Settings-dPVcqC_d.js → Settings-CgPqzUvj.js} +3 -3
  79. package/ui/dist/assets/{StatusIndicator-DA3NAo_b.js → StatusIndicator-yig0uZEA.js} +1 -1
  80. package/ui/dist/assets/{SummaryDetail-Bjnf9lT8.js → SummaryDetail-CwLAfHgJ.js} +1 -1
  81. package/ui/dist/assets/{SummaryList-ENwKDBKW.js → SummaryList-BvS5Idnu.js} +1 -1
  82. package/ui/dist/assets/{TaskDetail-DnwG6MzB.js → TaskDetail-zarC1fOt.js} +1 -1
  83. package/ui/dist/assets/{TaskList-D9Ug_HiV.js → TaskList-CyJq3ANE.js} +1 -1
  84. package/ui/dist/assets/{TicketBoard-CviH1571.js → TicketBoard-BlWr3-iH.js} +1 -1
  85. package/ui/dist/assets/{TicketDetail-8Jk8O-33.js → TicketDetail-DmqWKj2W.js} +1 -1
  86. package/ui/dist/assets/{TicketList-B-iV4rRA.js → TicketList-Scj4pMWa.js} +1 -1
  87. package/ui/dist/assets/{UserManagement-CPZ8s7s2.js → UserManagement-BH7uG7dw.js} +1 -1
  88. package/ui/dist/assets/{ViewSwitcher-BUyOOWDI.js → ViewSwitcher-3HzZUdWn.js} +1 -1
  89. package/ui/dist/assets/{alert-dialog-BOmnTkN-.js → alert-dialog-DWPb2IfQ.js} +1 -1
  90. package/ui/dist/assets/{archive-D5my9zOG.js → archive-CGck7gxR.js} +1 -1
  91. package/ui/dist/assets/{badge-B4ov81_m.js → badge-DEmc3Z_l.js} +1 -1
  92. package/ui/dist/assets/{brain-C53ElFLD.js → brain-CfFdLVqb.js} +1 -1
  93. package/ui/dist/assets/{bug-D0TJoCYn.js → bug-DqSGPUUE.js} +1 -1
  94. package/ui/dist/assets/{calendar-BkWIhKa1.js → calendar-BuZ4zbPW.js} +1 -1
  95. package/ui/dist/assets/{chevron-up-BRTtndUZ.js → chevron-up-DcJ4LL_N.js} +1 -1
  96. package/ui/dist/assets/{circle-x-BN89YhbS.js → circle-x-C78PNZjF.js} +1 -1
  97. package/ui/dist/assets/{constants-fc7TFI7u.js → constants-DhDjZe4P.js} +1 -1
  98. package/ui/dist/assets/{cpu-B41x1-vV.js → cpu-Ck6AoG8c.js} +1 -1
  99. package/ui/dist/assets/{database-D9Cnfmzj.js → database-CA2w8Lvt.js} +1 -1
  100. package/ui/dist/assets/{dialog-DrWB4EYD.js → dialog-BOdGWhgE.js} +1 -1
  101. package/ui/dist/assets/{ellipsis-NuUWekvu.js → ellipsis-FhHKRbfl.js} +1 -1
  102. package/ui/dist/assets/{ellipsis-vertical-BueMfuCw.js → ellipsis-vertical-CO0UqE4-.js} +1 -1
  103. package/ui/dist/assets/{file-code-HR9829Jc.js → file-code-ChnhYYj1.js} +1 -1
  104. package/ui/dist/assets/{folder-open-C_7oYEqe.js → folder-open-CHwA1-ve.js} +1 -1
  105. package/ui/dist/assets/{funnel-B5gmNXGf.js → funnel-ByEjRFNB.js} +1 -1
  106. package/ui/dist/assets/{git-branch-BRMiJmV3.js → git-branch-GTOPQgU4.js} +1 -1
  107. package/ui/dist/assets/{git-commit-horizontal-CXOUQeJI.js → git-commit-horizontal-F5M84Kut.js} +1 -1
  108. package/ui/dist/assets/{globe-CCspjvus.js → globe-CQWfr7hT.js} +1 -1
  109. package/ui/dist/assets/{hash-g3102kPC.js → hash-nKC9gtLg.js} +1 -1
  110. package/ui/dist/assets/{history-CNqcBBrV.js → history-DfFSA9xJ.js} +1 -1
  111. package/ui/dist/assets/{index-kb4AJXUB.js → index--JkIxVXb.js} +3 -3
  112. package/ui/dist/assets/index-Ca3vk7Gq.js +1 -0
  113. package/ui/dist/assets/{index-C9jNQXzL.js → index-DHGvP9mp.js} +1 -1
  114. package/ui/dist/assets/index-DeI8lKRR.js +1 -0
  115. package/ui/dist/assets/{index-DURTouJI.js → index-Z4-RtZcs.js} +1 -1
  116. package/ui/dist/assets/{index-CEZ6R2Uw.js → index-a4Fxq0Hb.js} +1 -1
  117. package/ui/dist/assets/{layers-CLXiHN6o.js → layers-DFeLExSa.js} +1 -1
  118. package/ui/dist/assets/{layout-dashboard-B07MFhBs.js → layout-dashboard-WmgjMhWF.js} +1 -1
  119. package/ui/dist/assets/{lightbulb-B2dgG5ks.js → lightbulb-Bo8hEr7B.js} +1 -1
  120. package/ui/dist/assets/{link-2-BxNmr9hL.js → link-2-YqrpQXBH.js} +1 -1
  121. package/ui/dist/assets/{markdown-renderer-CjU6hBcW.js → markdown-renderer-Croje6Xe.js} +1 -1
  122. package/ui/dist/assets/{message-square-Bf2-CVbt.js → message-square-CD0H5hot.js} +1 -1
  123. package/ui/dist/assets/{pencil-D096Ey62.js → pencil-DpFnj5dn.js} +1 -1
  124. package/ui/dist/assets/{play-DfdcSI3x.js → play-DVgea_jT.js} +1 -1
  125. package/ui/dist/assets/{popover-BV0QJDQq.js → popover-JXecfWG5.js} +1 -1
  126. package/ui/dist/assets/{progress-DjPEDJfI.js → progress-C1ghf1Fu.js} +1 -1
  127. package/ui/dist/assets/{rocket-CPsTc-5G.js → rocket-BFxPYGx9.js} +1 -1
  128. package/ui/dist/assets/{select-LsO16slr.js → select-MADP8m6Q.js} +1 -1
  129. package/ui/dist/assets/{send-BDYBZDUZ.js → send-eYnW_7n-.js} +1 -1
  130. package/ui/dist/assets/{server-B3k6noce.js → server-CPMaysH8.js} +1 -1
  131. package/ui/dist/assets/{settings-2-Cu4xTjhG.js → settings-2-scXUOuE_.js} +1 -1
  132. package/ui/dist/assets/{sheet-BScLnPKX.js → sheet-r6NSOxi-.js} +1 -1
  133. package/ui/dist/assets/{shield-check-CQqkwufI.js → shield-check-PEr7nmVw.js} +1 -1
  134. package/ui/dist/assets/{shield-off-ZFruH9KO.js → shield-off-CLtXio62.js} +1 -1
  135. package/ui/dist/assets/{skeleton-CtkIA_MU.js → skeleton-C9oEzNlU.js} +1 -1
  136. package/ui/dist/assets/{smartphone-DnoksVkc.js → smartphone-DusFfY0U.js} +1 -1
  137. package/ui/dist/assets/{square-uD9ZHMfW.js → square-e1yxB2eR.js} +1 -1
  138. package/ui/dist/assets/{switch-MQPnwGJK.js → switch-tNs0n-kz.js} +1 -1
  139. package/ui/dist/assets/{table-B8cqlywA.js → table-CXxtLicg.js} +1 -1
  140. package/ui/dist/assets/{tag-wEJQRWlV.js → tag-tAwMmQ9G.js} +1 -1
  141. package/ui/dist/assets/{textarea-D5LJCOGl.js → textarea-BjiEeIWh.js} +1 -1
  142. package/ui/dist/assets/{timer-mbGHegmr.js → timer-Vel1N3RP.js} +1 -1
  143. package/ui/dist/assets/{tooltip-Df1CunSp.js → tooltip-ZMwtfr1J.js} +1 -1
  144. package/ui/dist/assets/{trash-2-B4oAXsX-.js → trash-2-eM-CNwsZ.js} +1 -1
  145. package/ui/dist/assets/{trending-up-BGWxvlmZ.js → trending-up-BGafmjRo.js} +1 -1
  146. package/ui/dist/assets/{wand-sparkles-CZy4xvL3.js → wand-sparkles-DPvcd9Pi.js} +1 -1
  147. package/ui/dist/assets/{wrench-ByZkjO6m.js → wrench-B8s3zYVV.js} +1 -1
  148. package/ui/dist/index.html +1 -1
  149. package/ui/dist/assets/CostsDashboard-ChfH8BlG.js +0 -1
  150. package/ui/dist/assets/index-27mTPKuh.js +0 -1
  151. package/ui/dist/assets/index-BMx3fH-e.js +0 -1
@@ -11,7 +11,7 @@
11
11
  import { Command } from 'commander';
12
12
  import chalk from 'chalk';
13
13
  import figlet from 'figlet';
14
- import { createInterface } from 'readline/promises';
14
+ import { select, input, password as passwordPrompt, confirm } from '@inquirer/prompts';
15
15
  import { randomUUID } from 'crypto';
16
16
  import { eq } from 'drizzle-orm';
17
17
  import { initStorage, getDb, saveProviderConfig } from '../../storage/index.js';
@@ -28,36 +28,8 @@ function showBanner() {
28
28
  console.log(chalk.cyan(banner));
29
29
  console.log(chalk.dim(' Setup Wizard\n'));
30
30
  }
31
- async function prompt(rl, question) {
32
- const answer = await rl.question(chalk.bold(question));
33
- return answer.trim();
34
- }
35
- async function promptPassword(rl, question) {
36
- // readline/promises doesn't natively hide input, but for a CLI wizard this is acceptable
37
- const answer = await rl.question(chalk.bold(question));
38
- return answer.trim();
39
- }
40
- async function promptChoice(rl, question, options) {
41
- console.log('');
42
- console.log(chalk.bold(question));
43
- for (const opt of options) {
44
- console.log(` ${chalk.cyan(opt.key)}) ${opt.label}`);
45
- }
46
- const validKeys = options.map((o) => o.key);
47
- while (true) {
48
- const answer = await prompt(rl, `Choice [${validKeys.join('/')}]: `);
49
- if (validKeys.includes(answer))
50
- return answer;
51
- console.log(chalk.red(` Invalid choice. Options: ${validKeys.join(', ')}`));
52
- }
53
- }
54
- async function promptYesNo(rl, question, defaultYes = false) {
55
- const hint = defaultYes ? 'Y/n' : 'y/N';
56
- const answer = await prompt(rl, `${question} [${hint}]: `);
57
- if (!answer)
58
- return defaultYes;
59
- return answer.toLowerCase().startsWith('y');
60
- }
31
+ // Inquirer theme for consistent prefix
32
+ const theme = { prefix: ' ', style: { highlight: (text) => chalk.cyan.bold(text) } };
61
33
  async function checkRedis() {
62
34
  try {
63
35
  const IORedis = (await import('ioredis')).default;
@@ -172,43 +144,72 @@ async function stepSystemCheck(db) {
172
144
  return { redisOk, hasAdmin, hasAnthropicKey, hasOpenAIKey };
173
145
  }
174
146
  // Step 2: AI Provider
175
- async function stepAiProvider(rl, status) {
147
+ async function stepAiProvider(status) {
176
148
  if (status.hasAnthropicKey || status.hasOpenAIKey) {
177
149
  const provider = status.hasAnthropicKey ? 'Anthropic' : 'OpenAI';
178
150
  info(`AI provider already configured (${provider} from env).`);
179
- const reconfigure = await promptYesNo(rl, ' Reconfigure AI provider?', false);
180
- if (!reconfigure)
151
+ try {
152
+ const reconfigure = await confirm({
153
+ message: 'Reconfigure AI provider?',
154
+ default: false,
155
+ theme,
156
+ });
157
+ if (!reconfigure)
158
+ return 'skip';
159
+ }
160
+ catch {
181
161
  return 'skip';
162
+ }
163
+ }
164
+ let choice;
165
+ try {
166
+ choice = await select({
167
+ message: 'Select AI Provider',
168
+ choices: [
169
+ { name: `Anthropic ${chalk.green('(recommended)')}`, value: 'anthropic', description: 'Claude 4.x, 3.5 Sonnet' },
170
+ { name: 'OpenAI', value: 'openai', description: 'GPT-4o, o1, o3' },
171
+ { name: `Ollama ${chalk.cyan('(free, local)')}`, value: 'ollama', description: 'Llama, Mistral, Qwen - runs on your machine' },
172
+ { name: chalk.dim('Skip for now'), value: 'skip', description: 'Configure later in Settings' },
173
+ ],
174
+ theme,
175
+ });
176
+ }
177
+ catch {
178
+ return 'skip';
182
179
  }
183
- const choice = await promptChoice(rl, ' Select AI Provider:', [
184
- { key: '1', label: 'Anthropic API Key (recommended)' },
185
- { key: '2', label: 'OpenAI API Key' },
186
- { key: '3', label: 'Ollama (free, local AI)' },
187
- { key: '4', label: 'Skip for now' },
188
- ]);
189
- if (choice === '4') {
180
+ if (choice === 'skip') {
190
181
  info('AI provider skipped. You can configure it later in Settings.');
191
182
  return 'skip';
192
183
  }
193
- if (choice === '1') {
194
- const apiKey = await prompt(rl, ' Anthropic API Key: ');
195
- if (!apiKey) {
196
- warn('No key provided, skipping.');
184
+ if (choice === 'anthropic') {
185
+ try {
186
+ const apiKey = await input({ message: 'Anthropic API Key', theme });
187
+ if (!apiKey) {
188
+ warn('No key provided, skipping.');
189
+ return 'skip';
190
+ }
191
+ await saveProviderConfig({ type: 'anthropic', apiKey, enabled: true });
192
+ success('Anthropic provider configured.');
193
+ return 'anthropic';
194
+ }
195
+ catch {
197
196
  return 'skip';
198
197
  }
199
- await saveProviderConfig({ type: 'anthropic', apiKey, enabled: true });
200
- success('Anthropic provider configured.');
201
- return 'anthropic';
202
- }
203
- if (choice === '2') {
204
- const apiKey = await prompt(rl, ' OpenAI API Key: ');
205
- if (!apiKey) {
206
- warn('No key provided, skipping.');
198
+ }
199
+ if (choice === 'openai') {
200
+ try {
201
+ const apiKey = await input({ message: 'OpenAI API Key', theme });
202
+ if (!apiKey) {
203
+ warn('No key provided, skipping.');
204
+ return 'skip';
205
+ }
206
+ await saveProviderConfig({ type: 'openai', apiKey, enabled: true });
207
+ success('OpenAI provider configured.');
208
+ return 'openai';
209
+ }
210
+ catch {
207
211
  return 'skip';
208
212
  }
209
- await saveProviderConfig({ type: 'openai', apiKey, enabled: true });
210
- success('OpenAI provider configured.');
211
- return 'openai';
212
213
  }
213
214
  // Ollama
214
215
  const ollamaUrl = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';
@@ -261,7 +262,7 @@ function showRecoveryCodes(codes) {
261
262
  console.log('');
262
263
  warn('These codes cannot be shown again!');
263
264
  }
264
- async function stepAdminAccount(rl, db) {
265
+ async function stepAdminAccount(db) {
265
266
  if (!db) {
266
267
  error('Database not available. Cannot create admin account.');
267
268
  return null;
@@ -271,37 +272,58 @@ async function stepAdminAccount(rl, db) {
271
272
  if (existingAdmins.length > 0) {
272
273
  console.log(chalk.dim(' Existing admin accounts:'));
273
274
  for (const admin of existingAdmins) {
274
- console.log(` ${chalk.green('•')} ${admin.name} ${chalk.dim(`<${admin.email}>`)}`);
275
+ console.log(` ${chalk.green('\u2022')} ${admin.name} ${chalk.dim(`<${admin.email}>`)}`);
275
276
  }
276
277
  console.log('');
277
- console.log(' What would you like to do?');
278
- console.log(` ${chalk.bold('1)')} Keep existing — no changes`);
279
- console.log(` ${chalk.bold('2)')} Reset password for an existing admin`);
280
- console.log(` ${chalk.bold('3)')} Create an additional admin`);
281
- console.log('');
282
- const choice = await prompt(rl, ' Choice [1]: ');
283
- const picked = choice || '1';
284
- if (picked === '1') {
278
+ let picked;
279
+ try {
280
+ picked = await select({
281
+ message: 'What would you like to do?',
282
+ choices: [
283
+ { name: 'Keep existing - no changes', value: 'keep' },
284
+ { name: 'Reset password for an existing admin', value: 'reset' },
285
+ { name: 'Create an additional admin', value: 'create' },
286
+ ],
287
+ theme,
288
+ });
289
+ }
290
+ catch {
291
+ return existingAdmins[0].email;
292
+ }
293
+ if (picked === 'keep') {
285
294
  success(`Keeping ${existingAdmins.length} existing admin(s).`);
286
295
  return existingAdmins[0].email;
287
296
  }
288
- if (picked === '2') {
289
- // Reset password for existing admin
297
+ if (picked === 'reset') {
290
298
  let targetAdmin = existingAdmins[0];
291
299
  if (existingAdmins.length > 1) {
292
- console.log('');
293
- for (let i = 0; i < existingAdmins.length; i++) {
294
- console.log(` ${chalk.bold(`${i + 1})`)} ${existingAdmins[i].email}`);
300
+ try {
301
+ const adminId = await select({
302
+ message: 'Which admin?',
303
+ choices: existingAdmins.map(a => ({
304
+ name: `${a.name} ${chalk.dim(`<${a.email}>`)}`,
305
+ value: a.id,
306
+ })),
307
+ theme,
308
+ });
309
+ targetAdmin = existingAdmins.find(a => a.id === adminId) || existingAdmins[0];
295
310
  }
296
- const idx = await prompt(rl, ` Which admin? [1]: `);
297
- const n = parseInt(idx || '1', 10) - 1;
298
- if (n >= 0 && n < existingAdmins.length) {
299
- targetAdmin = existingAdmins[n];
311
+ catch {
312
+ return existingAdmins[0].email;
300
313
  }
301
314
  }
302
315
  let newPassword = '';
303
316
  while (true) {
304
- newPassword = await promptPassword(rl, ' New password (min 8 chars, 1 letter, 1 number): ');
317
+ try {
318
+ newPassword = await passwordPrompt({
319
+ message: 'New password (min 8 chars, 1 letter, 1 number)',
320
+ mask: '*',
321
+ theme,
322
+ });
323
+ }
324
+ catch {
325
+ return existingAdmins[0].email;
326
+ }
305
327
  const strengthError = validatePasswordStrength(newPassword);
306
328
  if (!strengthError)
307
329
  break;
@@ -321,18 +343,30 @@ async function stepAdminAccount(rl, db) {
321
343
  showRecoveryCodes(recoveryCodes);
322
344
  return targetAdmin.email;
323
345
  }
324
- // picked === '3' — fall through to create new admin
346
+ // picked === 'create' - fall through to create new admin
325
347
  console.log('');
326
348
  }
327
349
  // Create new admin
328
- const name = await prompt(rl, ' Name: ');
350
+ let name;
351
+ try {
352
+ name = await input({ message: 'Name', theme });
353
+ }
354
+ catch {
355
+ return existingAdmins.length > 0 ? existingAdmins[0].email : null;
356
+ }
329
357
  if (!name) {
330
358
  warn('Admin account creation skipped.');
331
359
  return existingAdmins.length > 0 ? existingAdmins[0].email : null;
332
360
  }
333
- const email = await prompt(rl, ' Email: ');
334
- if (!email || !email.includes('@')) {
335
- error('Invalid email address.');
361
+ let email;
362
+ try {
363
+ email = await input({
364
+ message: 'Email',
365
+ validate: (val) => val.includes('@') || 'Must be a valid email address',
366
+ theme,
367
+ });
368
+ }
369
+ catch {
336
370
  return existingAdmins.length > 0 ? existingAdmins[0].email : null;
337
371
  }
338
372
  // Check email uniqueness
@@ -345,10 +379,19 @@ async function stepAdminAccount(rl, db) {
345
379
  error(`User with email ${email} already exists.`);
346
380
  return existingAdmins.length > 0 ? existingAdmins[0].email : null;
347
381
  }
348
- let password = '';
382
+ let pw = '';
349
383
  while (true) {
350
- password = await promptPassword(rl, ' Password (min 8 chars, 1 letter, 1 number): ');
351
- const strengthError = validatePasswordStrength(password);
384
+ try {
385
+ pw = await passwordPrompt({
386
+ message: 'Password (min 8 chars, 1 letter, 1 number)',
387
+ mask: '*',
388
+ theme,
389
+ });
390
+ }
391
+ catch {
392
+ return existingAdmins.length > 0 ? existingAdmins[0].email : null;
393
+ }
394
+ const strengthError = validatePasswordStrength(pw);
352
395
  if (!strengthError)
353
396
  break;
354
397
  console.log(chalk.red(` ${strengthError}`));
@@ -356,7 +399,7 @@ async function stepAdminAccount(rl, db) {
356
399
  // Prefer server API (avoids cross-process LibSQL issues in Docker)
357
400
  const serverUrl = await getServerBaseUrl();
358
401
  if (serverUrl) {
359
- const apiResult = await createAdminViaApi(serverUrl, email, password, name);
402
+ const apiResult = await createAdminViaApi(serverUrl, email, pw, name);
360
403
  if (apiResult) {
361
404
  success(`Admin account created: ${apiResult.email}`);
362
405
  showRecoveryCodes(apiResult.recoveryCodes);
@@ -364,26 +407,45 @@ async function stepAdminAccount(rl, db) {
364
407
  }
365
408
  }
366
409
  // Fall back to direct DB
367
- const result = await createAdminUser(db, name, email, password);
410
+ const result = await createAdminUser(db, name, email, pw);
368
411
  success(`Admin account created: ${result.email}`);
369
412
  showRecoveryCodes(result.recoveryCodes);
370
413
  return result.email;
371
414
  }
372
415
  // Step 4: Registration Mode
373
- async function stepRegistrationMode(rl, db) {
416
+ async function stepRegistrationMode(db) {
374
417
  console.log(chalk.bold.white('\n Step 4: Registration Mode\n'));
375
- const choice = await promptChoice(rl, ' How should new users register?', [
376
- { key: '1', label: 'Invite only (default, more secure)' },
377
- { key: '2', label: 'Open registration' },
378
- ]);
379
- const mode = choice === '1' ? 'invite' : 'open';
418
+ let mode;
419
+ try {
420
+ mode = await select({
421
+ message: 'How should new users register?',
422
+ choices: [
423
+ { name: 'Invite only (more secure)', value: 'invite', description: 'Users need an invite code to sign up' },
424
+ { name: 'Open registration', value: 'open', description: 'Anyone can create an account' },
425
+ ],
426
+ theme,
427
+ });
428
+ }
429
+ catch {
430
+ mode = 'invite';
431
+ }
380
432
  await updateSettings({
381
433
  system: { registrationMode: mode },
382
434
  });
383
435
  success(`Registration mode set to ${chalk.bold(mode)}.`);
384
436
  const codes = [];
385
437
  if (mode === 'invite' && db) {
386
- const generate = await promptYesNo(rl, ' Generate 3 invite codes now?', true);
438
+ let generate;
439
+ try {
440
+ generate = await confirm({
441
+ message: 'Generate 3 invite codes now?',
442
+ default: true,
443
+ theme,
444
+ });
445
+ }
446
+ catch {
447
+ generate = false;
448
+ }
387
449
  if (generate) {
388
450
  for (let i = 0; i < 3; i++) {
389
451
  const code = generateInviteCode();
@@ -409,9 +471,19 @@ async function stepRegistrationMode(rl, db) {
409
471
  return { mode, codes };
410
472
  }
411
473
  // Step 5: GitHub OAuth (optional)
412
- async function stepGitHubOAuth(rl) {
474
+ async function stepGitHubOAuth() {
413
475
  console.log(chalk.bold.white('\n Step 5: GitHub OAuth (Optional)\n'));
414
- const configure = await promptYesNo(rl, ' Configure GitHub OAuth login?', false);
476
+ let configure;
477
+ try {
478
+ configure = await confirm({
479
+ message: 'Configure GitHub OAuth login?',
480
+ default: false,
481
+ theme,
482
+ });
483
+ }
484
+ catch {
485
+ return false;
486
+ }
415
487
  if (!configure) {
416
488
  info('GitHub OAuth skipped. Configure later in Settings.');
417
489
  return false;
@@ -421,8 +493,15 @@ async function stepGitHubOAuth(rl) {
421
493
  info(`Callback URL: ${chalk.cyan(`http://localhost:${port}/api/auth/github/callback`)}`);
422
494
  info('Create a GitHub OAuth App at: https://github.com/settings/developers');
423
495
  console.log('');
424
- const clientId = await prompt(rl, ' GitHub Client ID: ');
425
- const clientSecret = await prompt(rl, ' GitHub Client Secret: ');
496
+ let clientId;
497
+ let clientSecret;
498
+ try {
499
+ clientId = await input({ message: 'GitHub Client ID', theme });
500
+ clientSecret = await input({ message: 'GitHub Client Secret', theme });
501
+ }
502
+ catch {
503
+ return false;
504
+ }
426
505
  if (!clientId || !clientSecret) {
427
506
  warn('Incomplete credentials, skipping GitHub OAuth.');
428
507
  return false;
@@ -602,20 +681,17 @@ export function setupCommand() {
602
681
  error('Failed to initialize database.');
603
682
  process.exit(1);
604
683
  }
605
- // Create a single readline interface for all steps
606
- const rl = createInterface({ input: process.stdin, output: process.stdout });
607
684
  // Step 1: System Check
608
685
  const status = await stepSystemCheck(db);
609
686
  // Step 2: AI Provider
610
687
  console.log(chalk.bold.white('\n Step 2: AI Provider\n'));
611
- const aiProvider = await stepAiProvider(rl, status);
688
+ const aiProvider = await stepAiProvider(status);
612
689
  // Step 3: Admin Account
613
- const adminEmail = await stepAdminAccount(rl, db);
690
+ const adminEmail = await stepAdminAccount(db);
614
691
  // Step 4: Registration Mode
615
- const regResult = await stepRegistrationMode(rl, db);
692
+ const regResult = await stepRegistrationMode(db);
616
693
  // Step 5: GitHub OAuth
617
- const githubOAuth = await stepGitHubOAuth(rl);
618
- rl.close();
694
+ const githubOAuth = await stepGitHubOAuth();
619
695
  // Step 6: Summary
620
696
  showSummary({
621
697
  redisOk: status.redisOk,
@@ -84,7 +84,7 @@ ${issue.body || 'No description provided.'}
84
84
 
85
85
  Closes #${issue.number}
86
86
 
87
- Co-Authored-By: profClaw <bot@profclaw.dev>"
87
+ Co-Authored-By: profClaw <bot@profclaw.ai>"
88
88
  8. Push: git push -u origin HEAD
89
89
  9. Create PR: gh pr create --title "<title>" --body "Closes #${issue.number}"
90
90
 
@@ -7,6 +7,8 @@
7
7
  import { randomUUID } from 'crypto';
8
8
  import { createHmac, timingSafeEqual } from 'crypto';
9
9
  import { z } from 'zod';
10
+ import { createContextualLogger } from '../utils/logger.js';
11
+ const log = createContextualLogger('AgentWebhook');
10
12
  // Schemas
11
13
  /**
12
14
  * Generic agent completion payload
@@ -62,7 +64,7 @@ function verifySignature(agent, rawBody, signature) {
62
64
  const secret = WEBHOOK_SECRETS[agent] || WEBHOOK_SECRETS.generic;
63
65
  // Skip verification if no secret configured
64
66
  if (!secret) {
65
- console.warn(`[Webhook] No secret configured for ${agent}, skipping verification`);
67
+ log.warn('No secret configured for agent, skipping verification', { agent });
66
68
  return true;
67
69
  }
68
70
  if (!signature) {
@@ -103,7 +105,7 @@ export async function handleOpenClawWebhook(c) {
103
105
  }
104
106
  const parsed = OpenClawCompletionSchema.safeParse(payload);
105
107
  if (!parsed.success) {
106
- console.error('[Webhook] OpenClaw validation failed:', parsed.error.flatten());
108
+ log.error('OpenClaw validation failed', new Error(parsed.error.message));
107
109
  throw new Error(`Validation failed: ${parsed.error.message}`);
108
110
  }
109
111
  return processAgentCompletion(parsed.data);
@@ -129,7 +131,7 @@ export async function handleGenericAgentWebhook(c) {
129
131
  }
130
132
  const parsed = AgentCompletionSchema.safeParse(payload);
131
133
  if (!parsed.success) {
132
- console.error('[Webhook] Agent validation failed:', parsed.error.flatten());
134
+ log.error('Agent validation failed', new Error(parsed.error.message));
133
135
  throw new Error(`Validation failed: ${parsed.error.message}`);
134
136
  }
135
137
  return processAgentCompletion(parsed.data);
@@ -158,10 +160,11 @@ function processAgentCompletion(completion) {
158
160
  taskReports.set(report.taskId, reports);
159
161
  }
160
162
  // Log
161
- console.log(`[Webhook] Agent ${completion.agent} reported: ${completion.status}`);
162
- if (completion.artifacts?.length) {
163
- console.log(`[Webhook] Artifacts: ${completion.artifacts.length} items`);
164
- }
163
+ log.info('Agent reported completion', {
164
+ agent: completion.agent,
165
+ status: completion.status,
166
+ artifacts: completion.artifacts?.length,
167
+ });
165
168
  return report;
166
169
  }
167
170
  // Query Functions
@@ -13,6 +13,8 @@
13
13
  */
14
14
  import { randomUUID } from 'crypto';
15
15
  import { UserPromptSubmitPayloadSchema } from './schemas.js';
16
+ import { createContextualLogger } from '../utils/logger.js';
17
+ const log = createContextualLogger('PromptSubmit');
16
18
  // In-memory storage for prompt events
17
19
  const promptEvents = new Map();
18
20
  /**
@@ -35,7 +37,7 @@ export async function handlePromptSubmit(c) {
35
37
  }
36
38
  const parsed = UserPromptSubmitPayloadSchema.safeParse(payload);
37
39
  if (!parsed.success) {
38
- console.error('[Hook] Prompt submit validation failed:', parsed.error.flatten());
40
+ log.error('Prompt submit validation failed', new Error(parsed.error.message));
39
41
  return {
40
42
  success: false,
41
43
  eventId: '',
@@ -56,17 +58,14 @@ export async function handlePromptSubmit(c) {
56
58
  storePromptEvent(event);
57
59
  // Log for debugging
58
60
  const promptPreview = event.prompt.slice(0, 50) + (event.prompt.length > 50 ? '...' : '');
59
- console.log(`[Hook] UserPromptSubmit: "${promptPreview}"`);
60
- if (event.context?.file) {
61
- console.log(`[Hook] Context file: ${event.context.file}`);
62
- }
61
+ log.info('UserPromptSubmit', { promptPreview, contextFile: event.context?.file });
63
62
  return {
64
63
  success: true,
65
64
  eventId: event.id,
66
65
  };
67
66
  }
68
67
  catch (error) {
69
- console.error('[Hook] Error processing prompt submit:', error);
68
+ log.error('Error processing prompt submit', error instanceof Error ? error : new Error(String(error)));
70
69
  return {
71
70
  success: false,
72
71
  eventId: '',
@@ -8,6 +8,8 @@ import { randomUUID } from 'crypto';
8
8
  import { SessionEndPayloadSchema } from './schemas.js';
9
9
  import { getSessionEvents, clearSessionEvents, getSessionSummary } from './tool-use.js';
10
10
  import { aggregateInferences } from '../intelligence/rules.js';
11
+ import { createContextualLogger } from '../utils/logger.js';
12
+ const log = createContextualLogger('SessionEnd');
11
13
  // Session aggregates storage
12
14
  const sessionAggregates = new Map();
13
15
  /**
@@ -30,7 +32,7 @@ export async function handleSessionEnd(c) {
30
32
  }
31
33
  const parsed = SessionEndPayloadSchema.safeParse(payload);
32
34
  if (!parsed.success) {
33
- console.error('[Hook] Session end validation failed:', parsed.error.flatten());
35
+ log.error('Session end validation failed', new Error(parsed.error.message));
34
36
  return {
35
37
  success: false,
36
38
  eventId: '',
@@ -67,10 +69,12 @@ export async function handleSessionEnd(c) {
67
69
  // Store aggregate
68
70
  sessionAggregates.set(sessionId, aggregate);
69
71
  // Log session summary
70
- console.log(`[Hook] Session ended: ${sessionId}`);
71
- console.log(`[Hook] Files modified: ${aggregate.filesModified.length}`);
72
- console.log(`[Hook] Files created: ${aggregate.filesCreated.length}`);
73
- console.log(`[Hook] Total tool uses: ${Object.values(summary.toolUseCounts).reduce((a, b) => a + b, 0)}`);
72
+ log.info('Session ended', {
73
+ sessionId,
74
+ filesModified: aggregate.filesModified.length,
75
+ filesCreated: aggregate.filesCreated.length,
76
+ totalToolUses: Object.values(summary.toolUseCounts).reduce((a, b) => a + b, 0),
77
+ });
74
78
  // Clear in-memory events (we have the aggregate now)
75
79
  clearSessionEvents(sessionId);
76
80
  return {
@@ -80,7 +84,7 @@ export async function handleSessionEnd(c) {
80
84
  };
81
85
  }
82
86
  catch (error) {
83
- console.error('[Hook] Error processing session end:', error);
87
+ log.error('Error processing session end', error instanceof Error ? error : new Error(String(error)));
84
88
  return {
85
89
  success: false,
86
90
  eventId: '',
@@ -14,6 +14,8 @@
14
14
  import { randomUUID } from 'crypto';
15
15
  import { PostToolUsePayloadSchema } from './schemas.js';
16
16
  import { inferFromToolUse } from '../intelligence/rules.js';
17
+ import { createContextualLogger } from '../utils/logger.js';
18
+ const log = createContextualLogger('ToolUse');
17
19
  // In-memory storage for hook events (will be replaced with DB later)
18
20
  const hookEvents = new Map();
19
21
  const sessionEvents = new Map();
@@ -42,7 +44,7 @@ export async function handlePostToolUse(c) {
42
44
  // Validate payload
43
45
  const parsed = PostToolUsePayloadSchema.safeParse(payload);
44
46
  if (!parsed.success) {
45
- console.error('[Hook] Validation failed:', parsed.error.flatten());
47
+ log.error('Validation failed', new Error(parsed.error.message));
46
48
  return {
47
49
  success: false,
48
50
  eventId: '',
@@ -67,10 +69,11 @@ export async function handlePostToolUse(c) {
67
69
  // Store event
68
70
  storeEvent(event);
69
71
  // Log for debugging
70
- console.log(`[Hook] PostToolUse: ${hookPayload.tool} on ${eventData.filePath || 'N/A'}`);
71
- if (inference.confidence > 0) {
72
- console.log(`[Hook] Inference: ${JSON.stringify(inference)}`);
73
- }
72
+ log.info('PostToolUse', {
73
+ tool: hookPayload.tool,
74
+ filePath: eventData.filePath || 'N/A',
75
+ inference: inference.confidence > 0 ? inference : undefined,
76
+ });
74
77
  return {
75
78
  success: true,
76
79
  eventId: event.id,
@@ -78,7 +81,7 @@ export async function handlePostToolUse(c) {
78
81
  };
79
82
  }
80
83
  catch (error) {
81
- console.error('[Hook] Error processing PostToolUse:', error);
84
+ log.error('Error processing PostToolUse', error instanceof Error ? error : new Error(String(error)));
82
85
  return {
83
86
  success: false,
84
87
  eventId: '',