@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.
- package/dist/lib/jobs/job-runner.js +10 -2
- package/dist/lib/jobs/job-scheduler.js +21 -0
- package/dist/lib/mail/mail-runner.d.ts +51 -0
- package/dist/lib/mail/mail-runner.js +342 -0
- package/dist/lib/slack/slack-service.d.ts +2 -0
- package/dist/lib/slack/slack-service.js +3 -0
- package/dist/lib/storage/file-storage.d.ts +16 -16
- package/dist/lib/storage/file-storage.js +59 -64
- package/dist/lib/terminal/terminal-manager.d.ts +3 -3
- package/dist/lib/terminal/terminal-manager.js +10 -10
- package/dist/routes/api/v1/ai/route.js +39 -19
- package/dist/routes/api/v1/git/index.js +23 -0
- package/dist/routes/api/v1/mail/index.d.ts +3 -0
- package/dist/routes/api/v1/mail/index.js +23 -0
- package/dist/routes/api/v1/mail/route.d.ts +294 -0
- package/dist/routes/api/v1/mail/route.js +344 -0
- package/dist/routes/api/v1/mcp/index.js +32 -32
- package/dist/routes/api/v1/slack/index.d.ts +3 -0
- package/dist/routes/api/v1/slack/index.js +15 -0
- package/dist/routes/api/v1/slack/route.d.ts +124 -0
- package/dist/routes/api/v1/slack/route.js +192 -0
- package/dist/routes/api/v1/tasks/[id]/route.d.ts +117 -0
- package/dist/routes/api/v1/tasks/[id]/route.js +166 -0
- package/dist/routes/api/v1/tasks/index.d.ts +3 -0
- package/dist/routes/api/v1/tasks/index.js +10 -0
- package/dist/routes/api/v1/tasks/route.d.ts +96 -0
- package/dist/routes/api/v1/tasks/route.js +136 -0
- package/dist/routes/api/v1/terminal/[taskId]/create/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[taskId]/create/index.js +5 -0
- package/dist/routes/api/v1/terminal/[taskId]/create/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[taskId]/create/route.js +27 -0
- package/dist/routes/api/v1/terminal/[taskId]/destroy/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[taskId]/destroy/index.js +5 -0
- package/dist/routes/api/v1/terminal/[taskId]/destroy/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[taskId]/destroy/route.js +21 -0
- package/dist/routes/api/v1/terminal/[taskId]/resize/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[taskId]/resize/index.js +5 -0
- package/dist/routes/api/v1/terminal/[taskId]/resize/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[taskId]/resize/route.js +21 -0
- package/dist/routes/api/v1/terminal/sessions/route.js +4 -4
- package/dist/server-with-static.js +12 -8
- package/dist/server.js +12 -8
- package/package.json +4 -4
- package/static/assets/{ActivityPage-OxRci_V2.js → ActivityPage-k4I7Q53O.js} +1 -1
- package/static/assets/ApiKeysSettingsPage-B1YvVdmg.js +2 -0
- package/static/assets/{ArchitectureEditPage-D7xcH6dY.js → ArchitectureEditPage-CpowsIx2.js} +4 -4
- package/static/assets/{ArchitecturePage-pvnlX-NW.js → ArchitecturePage-DYxC_aMR.js} +1 -1
- package/static/assets/{AuthSettingsPage-Bu0CZ1rY.js → AuthSettingsPage-DtSo78Y_.js} +2 -2
- package/static/assets/{CallbackPage-D0lkjxCT.js → CallbackPage-bROCGapx.js} +1 -1
- package/static/assets/CodePage-CPCj64rX.js +2 -0
- package/static/assets/{CollapsibleSection-Bt_ZLnJc.js → CollapsibleSection-M5cXbl92.js} +1 -1
- package/static/assets/DashboardPage-B9BZZfw6.js +51 -0
- package/static/assets/{GitPage-TrTxZ27J.js → GitPage-BiDtdSK1.js} +2 -2
- package/static/assets/GitSettingsPage-THm6wDjs.js +6 -0
- package/static/assets/IdentityPage-BC16skg6.js +6 -0
- package/static/assets/{ImplementationStepsEditor-Ctx0CvbU.js → ImplementationStepsEditor-HliLQav5.js} +2 -2
- package/static/assets/{IntegrationsSettingsPage-C2wJVdM7.js → IntegrationsSettingsPage-CC_VKIQa.js} +1 -1
- package/static/assets/JobDetailPage-z1QQYvmU.js +1 -0
- package/static/assets/{KnowledgeDetailPage-BdTUfWqj.js → KnowledgeDetailPage-DzHXBS7Q.js} +1 -1
- package/static/assets/{KnowledgeEditPage-D8XK4IUf.js → KnowledgeEditPage-BwGnUH_m.js} +1 -1
- package/static/assets/KnowledgePage-CGIVMS02.js +3 -0
- package/static/assets/{LoginPage-Dqxd7cTa.js → LoginPage-VQ3lcfLV.js} +1 -1
- package/static/assets/MailInboxPage-DiZKqwdU.js +1 -0
- package/static/assets/MailProcessingModal-DIeSQBoR.js +6 -0
- package/static/assets/MailReadPage-C8AACmZQ.js +1 -0
- package/static/assets/MailSentPage-C_5yFly_.js +1 -0
- package/static/assets/{McpSettingsPage-10n35zXi.js → McpSettingsPage-i9YHcu1s.js} +1 -1
- package/static/assets/{NewKnowledgePage-BlJzzuh7.js → NewKnowledgePage-BnVY7WUD.js} +1 -1
- package/static/assets/{NewSkillPage-ByqN--mH.js → NewSkillPage-DwniHD6D.js} +1 -1
- package/static/assets/NewTaskPage-F5UX2WMc.js +90 -0
- package/static/assets/NotFoundPage-BbSZX_4L.js +6 -0
- package/static/assets/NotificationsSettingsPage-C8kjcift.js +1 -0
- package/static/assets/{ProjectEditPage-DKJTY2uc.js → ProjectEditPage-DUUlIEqI.js} +1 -1
- package/static/assets/{ProjectPage-2VblKCWz.js → ProjectPage-Unz9PQpA.js} +1 -1
- package/static/assets/{PromptsSettingsPage-B4mOhXuo.js → PromptsSettingsPage-DVpIuRKI.js} +1 -1
- package/static/assets/ResourceDetailPage-DqHZ2KYD.js +1 -0
- package/static/assets/{ResourcesPage-2BbjIWfF.js → ResourcesPage-BP5tuAi-.js} +1 -1
- package/static/assets/RoleEditPage-BgKu8S0-.js +13 -0
- package/static/assets/{RolePage-qXWXZ2FZ.js → RolePage-Fed52Ov5.js} +1 -1
- package/static/assets/{RulesSettingsPage-BtM7p8F6.js → RulesSettingsPage-BQ2O0u66.js} +3 -3
- package/static/assets/SchedulePage-jkxjuzBx.js +4 -0
- package/static/assets/SkillDetailPage-k3Q2-NFd.js +1 -0
- package/static/assets/{SkillEditPage-Czlo8WWT.js → SkillEditPage-urF4snjo.js} +1 -1
- package/static/assets/SkillsPage-DlWDhEjR.js +8 -0
- package/static/assets/{SkillsSettingsPage-DKtpy7qk.js → SkillsSettingsPage-BViFgckG.js} +1 -1
- package/static/assets/{SourceInput-BITn1Y15.js → SourceInput-CAFKTHw-.js} +1 -1
- package/static/assets/{TagInput-BK91_M1N.js → TagInput-C6lI-ePr.js} +1 -1
- package/static/assets/TaskDetailPage-DpbRHnW_.js +16 -0
- package/static/assets/TaskEditPage-DssRbW0h.js +1 -0
- package/static/assets/TasksPage-CD_eo0Bj.js +17 -0
- package/static/assets/TerminalPage-BG_wlccr.js +1 -0
- package/static/assets/TerminalSessionPage-CsK-LznK.js +8 -0
- package/static/assets/{UserPreferencesPage-DrgYEcxO.js → UserPreferencesPage-CWUq3efu.js} +1 -1
- package/static/assets/UserSettingsPage-CduI_MGS.js +1 -0
- package/static/assets/{UtilitiesPage-Djr4qT5L.js → UtilitiesPage-BAxokhLh.js} +1 -1
- package/static/assets/{alert-CsMvyYoX.js → alert-BXsc6_qu.js} +1 -1
- package/static/assets/{arrow-down-BZnfbld8.js → arrow-down-DmW_3gE8.js} +1 -1
- package/static/assets/{arrow-left-WGBYWq3h.js → arrow-left-1S-835kP.js} +1 -1
- package/static/assets/{arrow-up-BByVUPE7.js → arrow-up-BYism_o1.js} +1 -1
- package/static/assets/arrow-up-down-Dw3J0a4i.js +6 -0
- package/static/assets/{badge-AwLOflf5.js → badge-BUEY53dV.js} +1 -1
- package/static/assets/{browser-modal-BzGNFfTG.js → browser-modal-DCNdI4NT.js} +2 -2
- package/static/assets/{card-SN5gKnu7.js → card-BcPlIAH5.js} +1 -1
- package/static/assets/{chevron-left-C7uNq9l_.js → chevron-left-FMmNe7yP.js} +1 -1
- package/static/assets/{chevron-up-CHdIiLxL.js → chevron-up-CqM3won3.js} +1 -1
- package/static/assets/{chevrons-up-TXwQuoUN.js → chevrons-up-DTvCkIHc.js} +1 -1
- package/static/assets/{circle-alert-37E5gU9K.js → circle-alert-dseM-Ib7.js} +1 -1
- package/static/assets/{circle-check-big-nY4PntB5.js → circle-check-big-jKg34xC-.js} +1 -1
- package/static/assets/{circle-check-D02pWDME.js → circle-check-eyo6pBP1.js} +1 -1
- package/static/assets/{circle-play-7EXFLo4F.js → circle-play-BrY_lNiH.js} +1 -1
- package/static/assets/{circle-x-By4JoTHB.js → circle-x-uqmzEce1.js} +1 -1
- package/static/assets/{clipboard-BdymjxLO.js → clipboard-tzPFoieb.js} +1 -1
- package/static/assets/{clock-HDu44KTo.js → clock-Bjc06QBM.js} +1 -1
- package/static/assets/code-DrYqPukx.js +6 -0
- package/static/assets/{download-Cv2G2Eg9.js → download-Bg__QCLT.js} +1 -1
- package/static/assets/{external-link-DwMXcCCj.js → external-link-CNDy2UUo.js} +1 -1
- package/static/assets/{eye-DYnjJzdb.js → eye-DLFBnC8t.js} +1 -1
- package/static/assets/{folder-git-2-COeWFPHS.js → folder-git-2-DUqd0WRi.js} +1 -1
- package/static/assets/index-CHdBxVyk.css +2 -0
- package/static/assets/{index-9Tv-j_Ga.js → index-DFcWlnzl.js} +118 -103
- package/static/assets/{info-BmtuPMhv.js → info-D6jxZC5X.js} +1 -1
- package/static/assets/kiro-CX1mOsRO.js +17 -0
- package/static/assets/{label-TGqbNfMO.js → label-DBuh-ke5.js} +1 -1
- package/static/assets/{markdown-editor-ls1JPK_e.js → markdown-editor-B4YNQFT2.js} +1 -1
- package/static/assets/message-square-B5RWz_ff.js +6 -0
- package/static/assets/paperclip-4A_3MaPx.js +6 -0
- package/static/assets/{pause-CAWbvTiL.js → pause-BzhKXHtR.js} +1 -1
- package/static/assets/{play-DF_Qeu0H.js → play-CHIf-Rcz.js} +1 -1
- package/static/assets/{radio-group-DYTbywtK.js → radio-group-C1ct-VsJ.js} +1 -1
- package/static/assets/{refresh-cw-BFZxHqbC.js → refresh-cw-B3OwrDUf.js} +1 -1
- package/static/assets/{search-Dr90tbch.js → search-Cq1ksEdp.js} +1 -1
- package/static/assets/{select-Cs5qtMYV.js → select-44mcS2_G.js} +1 -1
- package/static/assets/{status-utils-BDOyevaX.js → status-utils-CDkPeVfP.js} +1 -1
- package/static/assets/{switch-4TDb6YiQ.js → switch-CIwjYvCt.js} +1 -1
- package/static/assets/{tabs-BrbEvF4V.js → tabs-DTV6Su-h.js} +1 -1
- package/static/assets/{tag-DrQkepeD.js → tag-p6yeowCW.js} +1 -1
- package/static/assets/{terminal-preview-uuKF9_x4.js → terminal-preview-DN38x9Jm.js} +1 -1
- package/static/assets/use-terminal-BXJqOeJe.js +1 -0
- package/static/assets/{video-DYA2WfbA.js → video-BH5ChaoS.js} +1 -1
- package/static/index.html +2 -2
- package/static/assets/ApiKeysSettingsPage-C0evI19e.js +0 -2
- package/static/assets/CodePage-BJ4PC5nb.js +0 -2
- package/static/assets/DashboardPage-BiffPdmj.js +0 -41
- package/static/assets/GitSettingsPage-D7q5xQd_.js +0 -6
- package/static/assets/IdentityPage-CY0Ak2j0.js +0 -11
- package/static/assets/JobDetailPage-Phx_IlKX.js +0 -1
- package/static/assets/KnowledgePage-Ci9G7Br-.js +0 -8
- package/static/assets/NewProposalPage-BP7Ttoxk.js +0 -90
- package/static/assets/ProposalDetailPage-m3ysyzpj.js +0 -1
- package/static/assets/ProposalEditPage-3XVg_paW.js +0 -1
- package/static/assets/ProposalsPage-B3u0aFFz.js +0 -17
- package/static/assets/ResourceDetailPage-somBLUpC.js +0 -1
- package/static/assets/RoleEditPage-CLzX7Xhi.js +0 -13
- package/static/assets/SchedulePage-4tFcIBSs.js +0 -4
- package/static/assets/SkillDetailPage-CroSdaju.js +0 -1
- package/static/assets/SkillsPage-CgULbcI-.js +0 -8
- package/static/assets/TerminalPage-8fwvnOo2.js +0 -1
- package/static/assets/TerminalSessionPage-BhO5U48p.js +0 -13
- package/static/assets/UserSettingsPage-Dj6lKLi8.js +0 -1
- package/static/assets/droid-CPteN3f9.js +0 -17
- package/static/assets/index-GFQ5RqVh.css +0 -2
- package/static/assets/use-terminal-BG5UXuVE.js +0 -1
- 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,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,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,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,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
|
|
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 (
|
|
49
|
-
sessions = terminalManager.
|
|
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
|
-
|
|
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
|
|
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/[
|
|
163
|
-
import terminalDestroy from './routes/api/v1/terminal/[
|
|
164
|
-
import terminalResize from './routes/api/v1/terminal/[
|
|
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/
|
|
191
|
+
app.route('/api/v1/tasks', tasks);
|
|
190
192
|
app.route('/api/v1/terminal/sessions', terminalSessions);
|
|
191
|
-
app.route('/api/v1/terminal/:
|
|
192
|
-
app.route('/api/v1/terminal/:
|
|
193
|
-
app.route('/api/v1/terminal/:
|
|
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 {
|