@lovelybunch/api 1.0.75-alpha.9 → 1.0.75

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 (163) hide show
  1. package/dist/lib/jobs/job-runner.js +10 -2
  2. package/dist/lib/jobs/job-scheduler.js +21 -0
  3. package/dist/lib/mail/mail-runner.d.ts +51 -0
  4. package/dist/lib/mail/mail-runner.js +342 -0
  5. package/dist/lib/slack/slack-service.d.ts +2 -0
  6. package/dist/lib/slack/slack-service.js +3 -0
  7. package/dist/lib/storage/file-storage.d.ts +16 -16
  8. package/dist/lib/storage/file-storage.js +59 -64
  9. package/dist/lib/terminal/terminal-manager.d.ts +3 -3
  10. package/dist/lib/terminal/terminal-manager.js +10 -10
  11. package/dist/routes/api/v1/ai/route.js +39 -19
  12. package/dist/routes/api/v1/git/index.js +23 -0
  13. package/dist/routes/api/v1/mail/index.d.ts +3 -0
  14. package/dist/routes/api/v1/mail/index.js +23 -0
  15. package/dist/routes/api/v1/mail/route.d.ts +294 -0
  16. package/dist/routes/api/v1/mail/route.js +344 -0
  17. package/dist/routes/api/v1/mcp/index.js +32 -32
  18. package/dist/routes/api/v1/slack/index.d.ts +3 -0
  19. package/dist/routes/api/v1/slack/index.js +15 -0
  20. package/dist/routes/api/v1/slack/route.d.ts +124 -0
  21. package/dist/routes/api/v1/slack/route.js +192 -0
  22. package/dist/routes/api/v1/tasks/[id]/route.d.ts +117 -0
  23. package/dist/routes/api/v1/tasks/[id]/route.js +166 -0
  24. package/dist/routes/api/v1/tasks/index.d.ts +3 -0
  25. package/dist/routes/api/v1/tasks/index.js +10 -0
  26. package/dist/routes/api/v1/tasks/route.d.ts +96 -0
  27. package/dist/routes/api/v1/tasks/route.js +136 -0
  28. package/dist/routes/api/v1/terminal/[taskId]/create/index.d.ts +3 -0
  29. package/dist/routes/api/v1/terminal/[taskId]/create/index.js +5 -0
  30. package/dist/routes/api/v1/terminal/[taskId]/create/route.d.ts +10 -0
  31. package/dist/routes/api/v1/terminal/[taskId]/create/route.js +27 -0
  32. package/dist/routes/api/v1/terminal/[taskId]/destroy/index.d.ts +3 -0
  33. package/dist/routes/api/v1/terminal/[taskId]/destroy/index.js +5 -0
  34. package/dist/routes/api/v1/terminal/[taskId]/destroy/route.d.ts +10 -0
  35. package/dist/routes/api/v1/terminal/[taskId]/destroy/route.js +21 -0
  36. package/dist/routes/api/v1/terminal/[taskId]/resize/index.d.ts +3 -0
  37. package/dist/routes/api/v1/terminal/[taskId]/resize/index.js +5 -0
  38. package/dist/routes/api/v1/terminal/[taskId]/resize/route.d.ts +10 -0
  39. package/dist/routes/api/v1/terminal/[taskId]/resize/route.js +21 -0
  40. package/dist/routes/api/v1/terminal/sessions/route.js +4 -4
  41. package/dist/server-with-static.js +12 -8
  42. package/dist/server.js +12 -8
  43. package/package.json +4 -4
  44. package/static/assets/{ActivityPage-OxRci_V2.js → ActivityPage-k4I7Q53O.js} +1 -1
  45. package/static/assets/ApiKeysSettingsPage-B1YvVdmg.js +2 -0
  46. package/static/assets/{ArchitectureEditPage-D7xcH6dY.js → ArchitectureEditPage-CpowsIx2.js} +4 -4
  47. package/static/assets/{ArchitecturePage-pvnlX-NW.js → ArchitecturePage-DYxC_aMR.js} +1 -1
  48. package/static/assets/{AuthSettingsPage-Bu0CZ1rY.js → AuthSettingsPage-DtSo78Y_.js} +2 -2
  49. package/static/assets/{CallbackPage-D0lkjxCT.js → CallbackPage-bROCGapx.js} +1 -1
  50. package/static/assets/CodePage-CPCj64rX.js +2 -0
  51. package/static/assets/{CollapsibleSection-Bt_ZLnJc.js → CollapsibleSection-M5cXbl92.js} +1 -1
  52. package/static/assets/DashboardPage-B9BZZfw6.js +51 -0
  53. package/static/assets/{GitPage-TrTxZ27J.js → GitPage-BiDtdSK1.js} +2 -2
  54. package/static/assets/GitSettingsPage-THm6wDjs.js +6 -0
  55. package/static/assets/IdentityPage-BC16skg6.js +6 -0
  56. package/static/assets/{ImplementationStepsEditor-Ctx0CvbU.js → ImplementationStepsEditor-HliLQav5.js} +2 -2
  57. package/static/assets/{IntegrationsSettingsPage-C2wJVdM7.js → IntegrationsSettingsPage-CC_VKIQa.js} +1 -1
  58. package/static/assets/JobDetailPage-z1QQYvmU.js +1 -0
  59. package/static/assets/{KnowledgeDetailPage-BdTUfWqj.js → KnowledgeDetailPage-DzHXBS7Q.js} +1 -1
  60. package/static/assets/{KnowledgeEditPage-D8XK4IUf.js → KnowledgeEditPage-BwGnUH_m.js} +1 -1
  61. package/static/assets/KnowledgePage-CGIVMS02.js +3 -0
  62. package/static/assets/{LoginPage-Dqxd7cTa.js → LoginPage-VQ3lcfLV.js} +1 -1
  63. package/static/assets/MailInboxPage-DiZKqwdU.js +1 -0
  64. package/static/assets/MailProcessingModal-DIeSQBoR.js +6 -0
  65. package/static/assets/MailReadPage-C8AACmZQ.js +1 -0
  66. package/static/assets/MailSentPage-C_5yFly_.js +1 -0
  67. package/static/assets/{McpSettingsPage-10n35zXi.js → McpSettingsPage-i9YHcu1s.js} +1 -1
  68. package/static/assets/{NewKnowledgePage-BlJzzuh7.js → NewKnowledgePage-BnVY7WUD.js} +1 -1
  69. package/static/assets/{NewSkillPage-ByqN--mH.js → NewSkillPage-DwniHD6D.js} +1 -1
  70. package/static/assets/NewTaskPage-F5UX2WMc.js +90 -0
  71. package/static/assets/NotFoundPage-BbSZX_4L.js +6 -0
  72. package/static/assets/NotificationsSettingsPage-C8kjcift.js +1 -0
  73. package/static/assets/{ProjectEditPage-DKJTY2uc.js → ProjectEditPage-DUUlIEqI.js} +1 -1
  74. package/static/assets/{ProjectPage-2VblKCWz.js → ProjectPage-Unz9PQpA.js} +1 -1
  75. package/static/assets/{PromptsSettingsPage-B4mOhXuo.js → PromptsSettingsPage-DVpIuRKI.js} +1 -1
  76. package/static/assets/ResourceDetailPage-DqHZ2KYD.js +1 -0
  77. package/static/assets/{ResourcesPage-2BbjIWfF.js → ResourcesPage-BP5tuAi-.js} +1 -1
  78. package/static/assets/RoleEditPage-BgKu8S0-.js +13 -0
  79. package/static/assets/{RolePage-qXWXZ2FZ.js → RolePage-Fed52Ov5.js} +1 -1
  80. package/static/assets/{RulesSettingsPage-BtM7p8F6.js → RulesSettingsPage-BQ2O0u66.js} +3 -3
  81. package/static/assets/SchedulePage-jkxjuzBx.js +4 -0
  82. package/static/assets/SkillDetailPage-k3Q2-NFd.js +1 -0
  83. package/static/assets/{SkillEditPage-Czlo8WWT.js → SkillEditPage-urF4snjo.js} +1 -1
  84. package/static/assets/SkillsPage-DlWDhEjR.js +8 -0
  85. package/static/assets/{SkillsSettingsPage-DKtpy7qk.js → SkillsSettingsPage-BViFgckG.js} +1 -1
  86. package/static/assets/{SourceInput-BITn1Y15.js → SourceInput-CAFKTHw-.js} +1 -1
  87. package/static/assets/{TagInput-BK91_M1N.js → TagInput-C6lI-ePr.js} +1 -1
  88. package/static/assets/TaskDetailPage-DpbRHnW_.js +16 -0
  89. package/static/assets/TaskEditPage-DssRbW0h.js +1 -0
  90. package/static/assets/TasksPage-CD_eo0Bj.js +17 -0
  91. package/static/assets/TerminalPage-BG_wlccr.js +1 -0
  92. package/static/assets/TerminalSessionPage-CsK-LznK.js +8 -0
  93. package/static/assets/{UserPreferencesPage-DrgYEcxO.js → UserPreferencesPage-CWUq3efu.js} +1 -1
  94. package/static/assets/UserSettingsPage-CduI_MGS.js +1 -0
  95. package/static/assets/{UtilitiesPage-Djr4qT5L.js → UtilitiesPage-BAxokhLh.js} +1 -1
  96. package/static/assets/{alert-CsMvyYoX.js → alert-BXsc6_qu.js} +1 -1
  97. package/static/assets/{arrow-down-BZnfbld8.js → arrow-down-DmW_3gE8.js} +1 -1
  98. package/static/assets/{arrow-left-WGBYWq3h.js → arrow-left-1S-835kP.js} +1 -1
  99. package/static/assets/{arrow-up-BByVUPE7.js → arrow-up-BYism_o1.js} +1 -1
  100. package/static/assets/arrow-up-down-Dw3J0a4i.js +6 -0
  101. package/static/assets/{badge-AwLOflf5.js → badge-BUEY53dV.js} +1 -1
  102. package/static/assets/{browser-modal-BzGNFfTG.js → browser-modal-DCNdI4NT.js} +2 -2
  103. package/static/assets/{card-SN5gKnu7.js → card-BcPlIAH5.js} +1 -1
  104. package/static/assets/{chevron-left-C7uNq9l_.js → chevron-left-FMmNe7yP.js} +1 -1
  105. package/static/assets/{chevron-up-CHdIiLxL.js → chevron-up-CqM3won3.js} +1 -1
  106. package/static/assets/{chevrons-up-TXwQuoUN.js → chevrons-up-DTvCkIHc.js} +1 -1
  107. package/static/assets/{circle-alert-37E5gU9K.js → circle-alert-dseM-Ib7.js} +1 -1
  108. package/static/assets/{circle-check-big-nY4PntB5.js → circle-check-big-jKg34xC-.js} +1 -1
  109. package/static/assets/{circle-check-D02pWDME.js → circle-check-eyo6pBP1.js} +1 -1
  110. package/static/assets/{circle-play-7EXFLo4F.js → circle-play-BrY_lNiH.js} +1 -1
  111. package/static/assets/{circle-x-By4JoTHB.js → circle-x-uqmzEce1.js} +1 -1
  112. package/static/assets/{clipboard-BdymjxLO.js → clipboard-tzPFoieb.js} +1 -1
  113. package/static/assets/{clock-HDu44KTo.js → clock-Bjc06QBM.js} +1 -1
  114. package/static/assets/code-DrYqPukx.js +6 -0
  115. package/static/assets/{download-Cv2G2Eg9.js → download-Bg__QCLT.js} +1 -1
  116. package/static/assets/{external-link-DwMXcCCj.js → external-link-CNDy2UUo.js} +1 -1
  117. package/static/assets/{eye-DYnjJzdb.js → eye-DLFBnC8t.js} +1 -1
  118. package/static/assets/{folder-git-2-COeWFPHS.js → folder-git-2-DUqd0WRi.js} +1 -1
  119. package/static/assets/index-CHdBxVyk.css +2 -0
  120. package/static/assets/{index-9Tv-j_Ga.js → index-DFcWlnzl.js} +118 -103
  121. package/static/assets/{info-BmtuPMhv.js → info-D6jxZC5X.js} +1 -1
  122. package/static/assets/kiro-CX1mOsRO.js +17 -0
  123. package/static/assets/{label-TGqbNfMO.js → label-DBuh-ke5.js} +1 -1
  124. package/static/assets/{markdown-editor-ls1JPK_e.js → markdown-editor-B4YNQFT2.js} +1 -1
  125. package/static/assets/message-square-B5RWz_ff.js +6 -0
  126. package/static/assets/paperclip-4A_3MaPx.js +6 -0
  127. package/static/assets/{pause-CAWbvTiL.js → pause-BzhKXHtR.js} +1 -1
  128. package/static/assets/{play-DF_Qeu0H.js → play-CHIf-Rcz.js} +1 -1
  129. package/static/assets/{radio-group-DYTbywtK.js → radio-group-C1ct-VsJ.js} +1 -1
  130. package/static/assets/{refresh-cw-BFZxHqbC.js → refresh-cw-B3OwrDUf.js} +1 -1
  131. package/static/assets/{search-Dr90tbch.js → search-Cq1ksEdp.js} +1 -1
  132. package/static/assets/{select-Cs5qtMYV.js → select-44mcS2_G.js} +1 -1
  133. package/static/assets/{status-utils-BDOyevaX.js → status-utils-CDkPeVfP.js} +1 -1
  134. package/static/assets/{switch-4TDb6YiQ.js → switch-CIwjYvCt.js} +1 -1
  135. package/static/assets/{tabs-BrbEvF4V.js → tabs-DTV6Su-h.js} +1 -1
  136. package/static/assets/{tag-DrQkepeD.js → tag-p6yeowCW.js} +1 -1
  137. package/static/assets/{terminal-preview-uuKF9_x4.js → terminal-preview-DN38x9Jm.js} +1 -1
  138. package/static/assets/use-terminal-BXJqOeJe.js +1 -0
  139. package/static/assets/{video-DYA2WfbA.js → video-BH5ChaoS.js} +1 -1
  140. package/static/index.html +2 -2
  141. package/static/assets/ApiKeysSettingsPage-C0evI19e.js +0 -2
  142. package/static/assets/CodePage-BJ4PC5nb.js +0 -2
  143. package/static/assets/DashboardPage-BiffPdmj.js +0 -41
  144. package/static/assets/GitSettingsPage-D7q5xQd_.js +0 -6
  145. package/static/assets/IdentityPage-CY0Ak2j0.js +0 -11
  146. package/static/assets/JobDetailPage-Phx_IlKX.js +0 -1
  147. package/static/assets/KnowledgePage-Ci9G7Br-.js +0 -8
  148. package/static/assets/NewProposalPage-BP7Ttoxk.js +0 -90
  149. package/static/assets/ProposalDetailPage-m3ysyzpj.js +0 -1
  150. package/static/assets/ProposalEditPage-3XVg_paW.js +0 -1
  151. package/static/assets/ProposalsPage-B3u0aFFz.js +0 -17
  152. package/static/assets/ResourceDetailPage-somBLUpC.js +0 -1
  153. package/static/assets/RoleEditPage-CLzX7Xhi.js +0 -13
  154. package/static/assets/SchedulePage-4tFcIBSs.js +0 -4
  155. package/static/assets/SkillDetailPage-CroSdaju.js +0 -1
  156. package/static/assets/SkillsPage-CgULbcI-.js +0 -8
  157. package/static/assets/TerminalPage-8fwvnOo2.js +0 -1
  158. package/static/assets/TerminalSessionPage-BhO5U48p.js +0 -13
  159. package/static/assets/UserSettingsPage-Dj6lKLi8.js +0 -1
  160. package/static/assets/droid-CPteN3f9.js +0 -17
  161. package/static/assets/index-GFQ5RqVh.css +0 -2
  162. package/static/assets/use-terminal-BG5UXuVE.js +0 -1
  163. package/static/assets/zap-h9QOsasv.js +0 -6
@@ -0,0 +1,166 @@
1
+ import { FileStorageAdapter } from '../../../../../lib/storage/file-storage.js';
2
+ import { getLogger } from '@lovelybunch/core/logging';
3
+ const storage = new FileStorageAdapter();
4
+ // Logger is lazily initialized inside handlers to use server config
5
+ export async function GET(c) {
6
+ try {
7
+ const id = c.req.param('id');
8
+ const task = await storage.getTask(id);
9
+ if (!task) {
10
+ return c.json({
11
+ success: false,
12
+ error: {
13
+ code: 'TASK_NOT_FOUND',
14
+ message: `Task with id ${id} not found`
15
+ }
16
+ }, 404);
17
+ }
18
+ return c.json({
19
+ success: true,
20
+ data: task
21
+ });
22
+ }
23
+ catch (error) {
24
+ console.error('Error getting task:', error);
25
+ return c.json({
26
+ success: false,
27
+ error: {
28
+ code: 'GET_TASK_ERROR',
29
+ message: error.message
30
+ }
31
+ }, 500);
32
+ }
33
+ }
34
+ export async function PATCH(c) {
35
+ try {
36
+ const id = c.req.param('id');
37
+ const updates = await c.req.json();
38
+ // Get existing task to merge updates properly
39
+ const existing = await storage.getTask(id);
40
+ if (!existing) {
41
+ return c.json({
42
+ success: false,
43
+ error: {
44
+ code: 'TASK_NOT_FOUND',
45
+ message: `Task with id ${id} not found`
46
+ }
47
+ }, 404);
48
+ }
49
+ // Handle special fields like comments
50
+ let finalUpdates = { ...updates };
51
+ if (updates.comments) {
52
+ finalUpdates = { ...finalUpdates, comments: updates.comments };
53
+ }
54
+ await storage.updateTask(id, finalUpdates);
55
+ // Fetch the updated task
56
+ const updatedTask = await storage.getTask(id);
57
+ // Determine what changed
58
+ const changedFields = Object.keys(updates);
59
+ const oldStatus = existing.status;
60
+ const newStatus = updatedTask?.status || existing.status;
61
+ // Log task.update event
62
+ const logger = getLogger();
63
+ logger.log({
64
+ kind: 'task.update',
65
+ actor: updatedTask?.author.type === 'agent'
66
+ ? `agent:${updatedTask.author.name}`
67
+ : `human:${updatedTask?.author.email || 'unknown'}`,
68
+ subject: `task:${id}`,
69
+ tags: ['task'],
70
+ payload: {
71
+ id,
72
+ changes: changedFields,
73
+ oldStatus,
74
+ newStatus
75
+ }
76
+ });
77
+ // If status changed, also log a status.change event
78
+ if (oldStatus !== newStatus) {
79
+ logger.log({
80
+ kind: 'task.status.change',
81
+ actor: updatedTask?.author.type === 'agent'
82
+ ? `agent:${updatedTask.author.name}`
83
+ : `human:${updatedTask?.author.email || 'unknown'}`,
84
+ subject: `task:${id}`,
85
+ tags: ['task', 'status'],
86
+ payload: {
87
+ id,
88
+ from: oldStatus,
89
+ to: newStatus,
90
+ reason: null
91
+ }
92
+ });
93
+ // Send Slack notification for status change (non-blocking)
94
+ import('../../../../../lib/slack/slack-service.js').then(({ getSlackService }) => {
95
+ getSlackService().sendNotification({
96
+ type: 'proposal.statusChange',
97
+ proposalId: id,
98
+ intent: updatedTask?.title || existing.title || '',
99
+ status: newStatus,
100
+ previousStatus: oldStatus,
101
+ }).catch(err => console.warn('[tasks] Slack notification failed:', err));
102
+ }).catch(() => { });
103
+ }
104
+ return c.json({
105
+ success: true,
106
+ data: updatedTask
107
+ });
108
+ }
109
+ catch (error) {
110
+ console.error('Error updating task:', error);
111
+ if (error.message.includes('not found')) {
112
+ return c.json({
113
+ success: false,
114
+ error: {
115
+ code: 'TASK_NOT_FOUND',
116
+ message: error.message
117
+ }
118
+ }, 404);
119
+ }
120
+ return c.json({
121
+ success: false,
122
+ error: {
123
+ code: 'UPDATE_TASK_ERROR',
124
+ message: error.message
125
+ }
126
+ }, 500);
127
+ }
128
+ }
129
+ export async function DELETE(c) {
130
+ try {
131
+ const id = c.req.param('id');
132
+ // Get task info before deleting
133
+ const task = await storage.getTask(id);
134
+ await storage.deleteTask(id);
135
+ // Log the deletion event
136
+ if (task) {
137
+ const logger = getLogger();
138
+ logger.log({
139
+ kind: 'task.delete',
140
+ actor: task.author.type === 'agent'
141
+ ? `agent:${task.author.name}`
142
+ : `human:${task.author.email || 'unknown'}`,
143
+ subject: `task:${id}`,
144
+ tags: ['task'],
145
+ payload: {
146
+ id,
147
+ title: task.title
148
+ }
149
+ });
150
+ }
151
+ return c.json({
152
+ success: true,
153
+ message: `Task ${id} deleted successfully`
154
+ });
155
+ }
156
+ catch (error) {
157
+ console.error('Error deleting task:', error);
158
+ return c.json({
159
+ success: false,
160
+ error: {
161
+ code: 'DELETE_TASK_ERROR',
162
+ message: error.message
163
+ }
164
+ }, 500);
165
+ }
166
+ }
@@ -0,0 +1,3 @@
1
+ import { Hono } from 'hono';
2
+ declare const tasks: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default tasks;
@@ -0,0 +1,10 @@
1
+ import { Hono } from 'hono';
2
+ import { GET, POST } from './route.js';
3
+ import { GET as getTask, PATCH as patchTask, DELETE as deleteTask } from './[id]/route.js';
4
+ const tasks = new Hono();
5
+ tasks.get('/', GET);
6
+ tasks.post('/', POST);
7
+ tasks.get('/:id', getTask);
8
+ tasks.patch('/:id', patchTask);
9
+ tasks.delete('/:id', deleteTask);
10
+ export default tasks;
@@ -0,0 +1,96 @@
1
+ import { Context } from 'hono';
2
+ export declare function GET(c: Context): Promise<(Response & import("hono").TypedResponse<{
3
+ totalCount: number;
4
+ success: true;
5
+ data: {
6
+ id: string;
7
+ title: string;
8
+ intent?: string;
9
+ content?: string;
10
+ author: {
11
+ type: import("@lovelybunch/types").AuthorType;
12
+ id: string;
13
+ name: string;
14
+ email?: string;
15
+ };
16
+ productSpecRef?: string;
17
+ planSteps: {
18
+ id: string;
19
+ description: string;
20
+ command?: string;
21
+ expectedOutcome?: string;
22
+ status: "pending" | "in-progress" | "completed" | "failed";
23
+ output?: string;
24
+ error?: string;
25
+ executedAt?: string;
26
+ }[];
27
+ status: import("@lovelybunch/types").TaskStatus;
28
+ comments?: {
29
+ id: string;
30
+ author: string;
31
+ content: string;
32
+ createdAt: string;
33
+ }[];
34
+ metadata: {
35
+ createdAt: string;
36
+ updatedAt: string;
37
+ reviewers: string[];
38
+ tags?: string[];
39
+ priority?: "low" | "medium" | "high" | "critical";
40
+ readiness?: number;
41
+ };
42
+ }[];
43
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
44
+ success: false;
45
+ error: {
46
+ code: string;
47
+ message: any;
48
+ };
49
+ }, 500, "json">)>;
50
+ export declare function POST(c: Context): Promise<(Response & import("hono").TypedResponse<{
51
+ success: true;
52
+ data: {
53
+ id: string;
54
+ title: string;
55
+ intent?: string;
56
+ content?: string;
57
+ author: {
58
+ type: import("@lovelybunch/types").AuthorType;
59
+ id: string;
60
+ name: string;
61
+ email?: string;
62
+ };
63
+ productSpecRef?: string;
64
+ planSteps: {
65
+ id: string;
66
+ description: string;
67
+ command?: string;
68
+ expectedOutcome?: string;
69
+ status: "pending" | "in-progress" | "completed" | "failed";
70
+ output?: string;
71
+ error?: string;
72
+ executedAt?: string;
73
+ }[];
74
+ status: import("@lovelybunch/types").TaskStatus;
75
+ comments?: {
76
+ id: string;
77
+ author: string;
78
+ content: string;
79
+ createdAt: string;
80
+ }[];
81
+ metadata: {
82
+ createdAt: string;
83
+ updatedAt: string;
84
+ reviewers: string[];
85
+ tags?: string[];
86
+ priority?: "low" | "medium" | "high" | "critical";
87
+ readiness?: number;
88
+ };
89
+ };
90
+ }, 201, "json">) | (Response & import("hono").TypedResponse<{
91
+ success: false;
92
+ error: {
93
+ code: string;
94
+ message: any;
95
+ };
96
+ }, 500, "json">)>;
@@ -0,0 +1,136 @@
1
+ import { FileStorageAdapter } from '../../../../lib/storage/file-storage.js';
2
+ import { getAuthorInfo } from '../../../../lib/user-preferences.js';
3
+ import Fuse from 'fuse.js';
4
+ import { getLogger } from '@lovelybunch/core/logging';
5
+ const storage = new FileStorageAdapter();
6
+ // Logger is lazily initialized inside handlers to use server config
7
+ export async function GET(c) {
8
+ try {
9
+ const url = new URL(c.req.url);
10
+ const searchParams = url.searchParams;
11
+ const status = searchParams.get('status');
12
+ const author = searchParams.get('author');
13
+ const priority = searchParams.get('priority');
14
+ const tags = searchParams.get('tags')?.split(',');
15
+ const searchQuery = searchParams.get('q');
16
+ const limitParam = searchParams.get('limit');
17
+ const limit = limitParam ? Math.min(Math.max(1, parseInt(limitParam, 10) || 20), 100) : undefined;
18
+ let tasks = await storage.listTasks({
19
+ status,
20
+ author: author || undefined,
21
+ priority: priority || undefined,
22
+ tags
23
+ });
24
+ // Apply search filter if provided
25
+ if (searchQuery?.trim()) {
26
+ const fuse = new Fuse(tasks, {
27
+ keys: [
28
+ { name: 'title', weight: 0.4 },
29
+ { name: 'intent', weight: 0.4 }, // Deprecated fallback
30
+ { name: 'id', weight: 0.3 },
31
+ { name: 'author.name', weight: 0.2 },
32
+ { name: 'metadata.tags', weight: 0.1 }
33
+ ],
34
+ threshold: 0.3,
35
+ includeScore: true
36
+ });
37
+ const searchResults = fuse.search(searchQuery);
38
+ tasks = searchResults.map(result => result.item);
39
+ }
40
+ const totalCount = tasks.length;
41
+ if (limit !== undefined) {
42
+ tasks = tasks.slice(0, limit);
43
+ }
44
+ return c.json({
45
+ success: true,
46
+ data: tasks,
47
+ ...(limit !== undefined && { totalCount })
48
+ });
49
+ }
50
+ catch (error) {
51
+ console.error('Error listing tasks:', error);
52
+ return c.json({
53
+ success: false,
54
+ error: {
55
+ code: 'LIST_TASKS_ERROR',
56
+ message: error.message
57
+ }
58
+ }, 500);
59
+ }
60
+ }
61
+ export async function POST(c) {
62
+ try {
63
+ const body = await c.req.json();
64
+ // Get author info from saved preferences if not provided
65
+ const authorInfo = await getAuthorInfo();
66
+ // Create a new task with required fields
67
+ const now = new Date();
68
+ // Use title as primary, fall back to deprecated intent for backward compatibility
69
+ const titleValue = body.title || body.intent || '';
70
+ const task = {
71
+ id: body.id || `cp-${Date.now()}`,
72
+ title: titleValue,
73
+ // Keep intent for backward compatibility (deprecated)
74
+ ...(body.intent && { intent: body.intent }),
75
+ content: body.content,
76
+ author: body.author || {
77
+ id: 'current-user',
78
+ name: authorInfo.name,
79
+ email: authorInfo.email,
80
+ role: 'engineer',
81
+ type: 'human'
82
+ },
83
+ planSteps: body.planSteps || [],
84
+ status: body.status || 'draft',
85
+ metadata: {
86
+ createdAt: now,
87
+ updatedAt: now,
88
+ reviewers: body.metadata?.reviewers || [],
89
+ tags: body.metadata?.tags || [],
90
+ priority: body.metadata?.priority || 'medium',
91
+ ...(body.metadata?.readiness != null && { readiness: body.metadata.readiness }),
92
+ },
93
+ productSpecRef: body.productSpecRef
94
+ };
95
+ await storage.createTask(task);
96
+ // Log the task creation event
97
+ const logger = getLogger();
98
+ logger.log({
99
+ kind: 'task.create',
100
+ actor: task.author.type === 'agent'
101
+ ? `agent:${task.author.name}`
102
+ : `human:${task.author.email}`,
103
+ subject: `task:${task.id}`,
104
+ tags: ['task'],
105
+ payload: {
106
+ id: task.id,
107
+ title: task.title,
108
+ priority: task.metadata.priority,
109
+ author: task.author
110
+ }
111
+ });
112
+ // Send Slack notification (non-blocking)
113
+ import('../../../../lib/slack/slack-service.js').then(({ getSlackService }) => {
114
+ getSlackService().sendNotification({
115
+ type: 'proposal.created',
116
+ proposalId: task.id,
117
+ intent: task.title,
118
+ author: task.author.name,
119
+ }).catch(err => console.warn('[tasks] Slack notification failed:', err));
120
+ }).catch(() => { });
121
+ return c.json({
122
+ success: true,
123
+ data: task
124
+ }, 201);
125
+ }
126
+ catch (error) {
127
+ console.error('Error creating task:', error);
128
+ return c.json({
129
+ success: false,
130
+ error: {
131
+ code: 'CREATE_TASK_ERROR',
132
+ message: error.message
133
+ }
134
+ }, 500);
135
+ }
136
+ }
@@ -0,0 +1,3 @@
1
+ import { Hono } from 'hono';
2
+ declare const create: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default create;
@@ -0,0 +1,5 @@
1
+ import { Hono } from 'hono';
2
+ import { POST } from './route.js';
3
+ const create = new Hono();
4
+ create.post('/', POST);
5
+ export default create;
@@ -0,0 +1,10 @@
1
+ import { Context } from 'hono';
2
+ export declare function POST(c: Context): Promise<(Response & import("hono").TypedResponse<{
3
+ error: string;
4
+ }, 400, "json">) | (Response & import("hono").TypedResponse<{
5
+ sessionId: string;
6
+ taskId: string;
7
+ createdAt: string;
8
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
9
+ error: string;
10
+ }, 500, "json">)>;
@@ -0,0 +1,27 @@
1
+ import { getGlobalTerminalManager } from '../../../../../../lib/terminal/global-manager.js';
2
+ import { loadUserSettings } from '../../../../../../lib/user-preferences.js';
3
+ export async function POST(c) {
4
+ try {
5
+ const taskId = c.req.param('taskId');
6
+ const body = await c.req.json().catch(() => ({}));
7
+ const { startupCommand } = body;
8
+ if (!taskId) {
9
+ return c.json({ error: 'Task ID is required' }, 400);
10
+ }
11
+ // Load user settings to get shell preference
12
+ const userSettings = await loadUserSettings();
13
+ const shellPreference = (userSettings.preferences.terminalShell || 'bash');
14
+ // Create a new terminal session for this task
15
+ const terminalManager = getGlobalTerminalManager();
16
+ const session = await terminalManager.createSession(taskId, shellPreference, startupCommand);
17
+ return c.json({
18
+ sessionId: session.id,
19
+ taskId: session.taskId,
20
+ createdAt: session.createdAt,
21
+ });
22
+ }
23
+ catch (error) {
24
+ console.error('Error creating terminal session:', error);
25
+ return c.json({ error: 'Failed to create terminal session' }, 500);
26
+ }
27
+ }
@@ -0,0 +1,3 @@
1
+ import { Hono } from 'hono';
2
+ declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default app;
@@ -0,0 +1,5 @@
1
+ import { Hono } from 'hono';
2
+ import { POST } from './route.js';
3
+ const app = new Hono();
4
+ app.post('/', POST);
5
+ export default app;
@@ -0,0 +1,10 @@
1
+ import { Context } from 'hono';
2
+ export declare function POST(c: Context): Promise<(Response & import("hono").TypedResponse<{
3
+ error: string;
4
+ }, 400, "json">) | (Response & import("hono").TypedResponse<{
5
+ error: string;
6
+ }, 404, "json">) | (Response & import("hono").TypedResponse<{
7
+ success: true;
8
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
9
+ error: string;
10
+ }, 500, "json">)>;
@@ -0,0 +1,21 @@
1
+ import { getGlobalTerminalManager } from '../../../../../../lib/terminal/global-manager.js';
2
+ export async function POST(c) {
3
+ try {
4
+ const taskId = c.req.param('taskId');
5
+ const body = await c.req.json();
6
+ const { sessionId } = body;
7
+ if (!sessionId) {
8
+ return c.json({ error: 'Session ID is required' }, 400);
9
+ }
10
+ const terminalManager = getGlobalTerminalManager();
11
+ const success = terminalManager.destroySession(sessionId);
12
+ if (!success) {
13
+ return c.json({ error: 'Session not found' }, 404);
14
+ }
15
+ return c.json({ success: true });
16
+ }
17
+ catch (error) {
18
+ console.error('Error destroying terminal session:', error);
19
+ return c.json({ error: 'Failed to destroy terminal session' }, 500);
20
+ }
21
+ }
@@ -0,0 +1,3 @@
1
+ import { Hono } from 'hono';
2
+ declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default app;
@@ -0,0 +1,5 @@
1
+ import { Hono } from 'hono';
2
+ import { POST } from './route.js';
3
+ const app = new Hono();
4
+ app.post('/', POST);
5
+ export default app;
@@ -0,0 +1,10 @@
1
+ import { Context } from 'hono';
2
+ export declare function POST(c: Context): Promise<(Response & import("hono").TypedResponse<{
3
+ error: string;
4
+ }, 400, "json">) | (Response & import("hono").TypedResponse<{
5
+ error: string;
6
+ }, 404, "json">) | (Response & import("hono").TypedResponse<{
7
+ success: true;
8
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
9
+ error: string;
10
+ }, 500, "json">)>;
@@ -0,0 +1,21 @@
1
+ import { getGlobalTerminalManager } from '../../../../../../lib/terminal/global-manager.js';
2
+ export async function POST(c) {
3
+ try {
4
+ const taskId = c.req.param('taskId');
5
+ const body = await c.req.json();
6
+ const { sessionId, cols, rows } = body;
7
+ if (!sessionId || !cols || !rows) {
8
+ return c.json({ error: 'Session ID, cols, and rows are required' }, 400);
9
+ }
10
+ const terminalManager = getGlobalTerminalManager();
11
+ const success = terminalManager.resizeSession(sessionId, cols, rows);
12
+ if (!success) {
13
+ return c.json({ error: 'Session not found or resize failed' }, 404);
14
+ }
15
+ return c.json({ success: true });
16
+ }
17
+ catch (error) {
18
+ console.error('Error resizing terminal session:', error);
19
+ return c.json({ error: 'Failed to resize terminal session' }, 500);
20
+ }
21
+ }
@@ -31,7 +31,7 @@ export async function GET(c) {
31
31
  try {
32
32
  const url = new URL(c.req.url);
33
33
  const searchParams = url.searchParams;
34
- const proposalId = searchParams.get('proposalId');
34
+ const taskId = searchParams.get('taskId');
35
35
  const sessionId = searchParams.get('sessionId');
36
36
  const withPreview = ['1', 'true', 'yes'].includes((searchParams.get('withPreview') || '').toLowerCase());
37
37
  const linesParam = parseInt(searchParams.get('lines') || '0', 10);
@@ -45,8 +45,8 @@ export async function GET(c) {
45
45
  const s = terminalManager.getSession(sessionId);
46
46
  sessions = s ? [s] : [];
47
47
  }
48
- else if (proposalId) {
49
- sessions = terminalManager.getSessionsByProposal(proposalId);
48
+ else if (taskId) {
49
+ sessions = terminalManager.getSessionsByTask(taskId);
50
50
  }
51
51
  else {
52
52
  sessions = terminalManager.getAllSessions();
@@ -54,7 +54,7 @@ export async function GET(c) {
54
54
  // Return session info without the PTY instance
55
55
  const sessionInfo = sessions.map(session => ({
56
56
  id: session.id,
57
- proposalId: session.proposalId,
57
+ taskId: session.taskId,
58
58
  createdAt: session.createdAt,
59
59
  lastActivity: session.lastActivity,
60
60
  connected: !!session.websocket,
@@ -157,11 +157,11 @@ app.get('/ws/terminal-preview/:sessionId', upgradeWebSocket((c) => ({
157
157
  import auth from './routes/api/v1/auth/index.js';
158
158
  import authSettings from './routes/api/v1/auth-settings/index.js';
159
159
  import apiKeys from './routes/api/v1/api-keys/index.js';
160
- import proposals from './routes/api/v1/proposals/index.js';
160
+ import tasks from './routes/api/v1/tasks/index.js';
161
161
  import terminalSessions from './routes/api/v1/terminal/sessions/index.js';
162
- import terminalCreate from './routes/api/v1/terminal/[proposalId]/create/index.js';
163
- import terminalDestroy from './routes/api/v1/terminal/[proposalId]/destroy/index.js';
164
- import terminalResize from './routes/api/v1/terminal/[proposalId]/resize/index.js';
162
+ import terminalCreate from './routes/api/v1/terminal/[taskId]/create/index.js';
163
+ import terminalDestroy from './routes/api/v1/terminal/[taskId]/destroy/index.js';
164
+ import terminalResize from './routes/api/v1/terminal/[taskId]/resize/index.js';
165
165
  import ai from './routes/api/v1/ai/index.js';
166
166
  import chats from './routes/api/v1/chats/index.js';
167
167
  import chatsById from './routes/api/v1/chats/[id]/index.js';
@@ -181,16 +181,18 @@ import symlinks from './routes/api/v1/symlinks/index.js';
181
181
  import jobs from './routes/api/v1/jobs/index.js';
182
182
  import events from './routes/api/v1/events/index.js';
183
183
  import version from './routes/api/v1/version/index.js';
184
+ import { slackRoutes } from './routes/api/v1/slack/index.js';
185
+ import { mailRoutes } from './routes/api/v1/mail/index.js';
184
186
  // Register API routes FIRST
185
187
  console.log('🔗 Registering API routes...');
186
188
  app.route('/api/v1/auth', auth);
187
189
  app.route('/api/v1/auth-settings', authSettings);
188
190
  app.route('/api/v1/api-keys', apiKeys);
189
- app.route('/api/v1/proposals', proposals);
191
+ app.route('/api/v1/tasks', tasks);
190
192
  app.route('/api/v1/terminal/sessions', terminalSessions);
191
- app.route('/api/v1/terminal/:proposalId/create', terminalCreate);
192
- app.route('/api/v1/terminal/:proposalId/destroy', terminalDestroy);
193
- app.route('/api/v1/terminal/:proposalId/resize', terminalResize);
193
+ app.route('/api/v1/terminal/:taskId/create', terminalCreate);
194
+ app.route('/api/v1/terminal/:taskId/destroy', terminalDestroy);
195
+ app.route('/api/v1/terminal/:taskId/resize', terminalResize);
194
196
  app.route('/api/v1/ai', ai);
195
197
  app.route('/api/v1/chats', chats);
196
198
  app.route('/api/v1/chats/:id', chatsById);
@@ -210,6 +212,8 @@ app.route('/api/v1/symlinks', symlinks);
210
212
  app.route('/api/v1/jobs', jobs);
211
213
  app.route('/api/v1/events', events);
212
214
  app.route('/api/v1/version', version);
215
+ app.route('/api/v1/slack', slackRoutes);
216
+ app.route('/api/v1/mail', mailRoutes);
213
217
  console.log('✅ API routes registered');
214
218
  app.get(PUBLIC_AGENT_CARD_PATH, authMiddleware, async (c) => {
215
219
  try {