fleet-commander-ai 0.0.1
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/LICENSE +202 -0
- package/README.md +159 -0
- package/bin/fleet-commander-mcp.js +27 -0
- package/bin/fleet-commander.js +22 -0
- package/dist/client/assets/index-CHukC8Hq.js +188 -0
- package/dist/client/assets/index-CHukC8Hq.js.map +1 -0
- package/dist/client/assets/index-DvMjcYbg.css +1 -0
- package/dist/client/index.html +13 -0
- package/dist/server/config.d.ts +51 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +104 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/db.d.ts +388 -0
- package/dist/server/db.d.ts.map +1 -0
- package/dist/server/db.js +1524 -0
- package/dist/server/db.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +162 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/mcp/index.d.ts +2 -0
- package/dist/server/mcp/index.d.ts.map +1 -0
- package/dist/server/mcp/index.js +112 -0
- package/dist/server/mcp/index.js.map +1 -0
- package/dist/server/mcp/tools/add-project.d.ts +9 -0
- package/dist/server/mcp/tools/add-project.d.ts.map +1 -0
- package/dist/server/mcp/tools/add-project.js +58 -0
- package/dist/server/mcp/tools/add-project.js.map +1 -0
- package/dist/server/mcp/tools/get-team-timeline.d.ts +9 -0
- package/dist/server/mcp/tools/get-team-timeline.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-team-timeline.js +48 -0
- package/dist/server/mcp/tools/get-team-timeline.js.map +1 -0
- package/dist/server/mcp/tools/get-team.d.ts +9 -0
- package/dist/server/mcp/tools/get-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-team.js +48 -0
- package/dist/server/mcp/tools/get-team.js.map +1 -0
- package/dist/server/mcp/tools/get-usage.d.ts +9 -0
- package/dist/server/mcp/tools/get-usage.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-usage.js +33 -0
- package/dist/server/mcp/tools/get-usage.js.map +1 -0
- package/dist/server/mcp/tools/launch-team.d.ts +8 -0
- package/dist/server/mcp/tools/launch-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/launch-team.js +49 -0
- package/dist/server/mcp/tools/launch-team.js.map +1 -0
- package/dist/server/mcp/tools/list-issues.d.ts +9 -0
- package/dist/server/mcp/tools/list-issues.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-issues.js +47 -0
- package/dist/server/mcp/tools/list-issues.js.map +1 -0
- package/dist/server/mcp/tools/list-projects.d.ts +9 -0
- package/dist/server/mcp/tools/list-projects.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-projects.js +32 -0
- package/dist/server/mcp/tools/list-projects.js.map +1 -0
- package/dist/server/mcp/tools/list-teams.d.ts +9 -0
- package/dist/server/mcp/tools/list-teams.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-teams.js +43 -0
- package/dist/server/mcp/tools/list-teams.js.map +1 -0
- package/dist/server/mcp/tools/restart-team.d.ts +8 -0
- package/dist/server/mcp/tools/restart-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/restart-team.js +46 -0
- package/dist/server/mcp/tools/restart-team.js.map +1 -0
- package/dist/server/mcp/tools/send-message.d.ts +9 -0
- package/dist/server/mcp/tools/send-message.d.ts.map +1 -0
- package/dist/server/mcp/tools/send-message.js +48 -0
- package/dist/server/mcp/tools/send-message.js.map +1 -0
- package/dist/server/mcp/tools/stop-team.d.ts +8 -0
- package/dist/server/mcp/tools/stop-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/stop-team.js +46 -0
- package/dist/server/mcp/tools/stop-team.js.map +1 -0
- package/dist/server/mcp/tools/system-health.d.ts +9 -0
- package/dist/server/mcp/tools/system-health.d.ts.map +1 -0
- package/dist/server/mcp/tools/system-health.js +32 -0
- package/dist/server/mcp/tools/system-health.js.map +1 -0
- package/dist/server/middleware/error-handler.d.ts +32 -0
- package/dist/server/middleware/error-handler.d.ts.map +1 -0
- package/dist/server/middleware/error-handler.js +65 -0
- package/dist/server/middleware/error-handler.js.map +1 -0
- package/dist/server/routes/events.d.ts +16 -0
- package/dist/server/routes/events.d.ts.map +1 -0
- package/dist/server/routes/events.js +164 -0
- package/dist/server/routes/events.js.map +1 -0
- package/dist/server/routes/issues.d.ts +4 -0
- package/dist/server/routes/issues.d.ts.map +1 -0
- package/dist/server/routes/issues.js +198 -0
- package/dist/server/routes/issues.js.map +1 -0
- package/dist/server/routes/project-groups.d.ts +4 -0
- package/dist/server/routes/project-groups.d.ts.map +1 -0
- package/dist/server/routes/project-groups.js +124 -0
- package/dist/server/routes/project-groups.js.map +1 -0
- package/dist/server/routes/projects.d.ts +4 -0
- package/dist/server/routes/projects.d.ts.map +1 -0
- package/dist/server/routes/projects.js +319 -0
- package/dist/server/routes/projects.js.map +1 -0
- package/dist/server/routes/prs.d.ts +4 -0
- package/dist/server/routes/prs.d.ts.map +1 -0
- package/dist/server/routes/prs.js +186 -0
- package/dist/server/routes/prs.js.map +1 -0
- package/dist/server/routes/query.d.ts +4 -0
- package/dist/server/routes/query.d.ts.map +1 -0
- package/dist/server/routes/query.js +82 -0
- package/dist/server/routes/query.js.map +1 -0
- package/dist/server/routes/state-machine.d.ts +4 -0
- package/dist/server/routes/state-machine.d.ts.map +1 -0
- package/dist/server/routes/state-machine.js +86 -0
- package/dist/server/routes/state-machine.js.map +1 -0
- package/dist/server/routes/stream.d.ts +18 -0
- package/dist/server/routes/stream.d.ts.map +1 -0
- package/dist/server/routes/stream.js +62 -0
- package/dist/server/routes/stream.js.map +1 -0
- package/dist/server/routes/system.d.ts +4 -0
- package/dist/server/routes/system.d.ts.map +1 -0
- package/dist/server/routes/system.js +225 -0
- package/dist/server/routes/system.js.map +1 -0
- package/dist/server/routes/teams.d.ts +4 -0
- package/dist/server/routes/teams.d.ts.map +1 -0
- package/dist/server/routes/teams.js +570 -0
- package/dist/server/routes/teams.js.map +1 -0
- package/dist/server/routes/usage.d.ts +4 -0
- package/dist/server/routes/usage.d.ts.map +1 -0
- package/dist/server/routes/usage.js +80 -0
- package/dist/server/routes/usage.js.map +1 -0
- package/dist/server/schema.sql +267 -0
- package/dist/server/services/cc-query.d.ts +20 -0
- package/dist/server/services/cc-query.d.ts.map +1 -0
- package/dist/server/services/cc-query.js +352 -0
- package/dist/server/services/cc-query.js.map +1 -0
- package/dist/server/services/cleanup.d.ts +15 -0
- package/dist/server/services/cleanup.d.ts.map +1 -0
- package/dist/server/services/cleanup.js +232 -0
- package/dist/server/services/cleanup.js.map +1 -0
- package/dist/server/services/diagnostics-service.d.ts +85 -0
- package/dist/server/services/diagnostics-service.d.ts.map +1 -0
- package/dist/server/services/diagnostics-service.js +242 -0
- package/dist/server/services/diagnostics-service.js.map +1 -0
- package/dist/server/services/event-collector.d.ts +125 -0
- package/dist/server/services/event-collector.d.ts.map +1 -0
- package/dist/server/services/event-collector.js +299 -0
- package/dist/server/services/event-collector.js.map +1 -0
- package/dist/server/services/event-service.d.ts +22 -0
- package/dist/server/services/event-service.d.ts.map +1 -0
- package/dist/server/services/event-service.js +53 -0
- package/dist/server/services/event-service.js.map +1 -0
- package/dist/server/services/github-poller.d.ts +68 -0
- package/dist/server/services/github-poller.d.ts.map +1 -0
- package/dist/server/services/github-poller.js +563 -0
- package/dist/server/services/github-poller.js.map +1 -0
- package/dist/server/services/issue-fetcher.d.ts +231 -0
- package/dist/server/services/issue-fetcher.d.ts.map +1 -0
- package/dist/server/services/issue-fetcher.js +1053 -0
- package/dist/server/services/issue-fetcher.js.map +1 -0
- package/dist/server/services/issue-service.d.ts +102 -0
- package/dist/server/services/issue-service.d.ts.map +1 -0
- package/dist/server/services/issue-service.js +279 -0
- package/dist/server/services/issue-service.js.map +1 -0
- package/dist/server/services/message-template-service.d.ts +39 -0
- package/dist/server/services/message-template-service.d.ts.map +1 -0
- package/dist/server/services/message-template-service.js +87 -0
- package/dist/server/services/message-template-service.js.map +1 -0
- package/dist/server/services/pr-service.d.ts +73 -0
- package/dist/server/services/pr-service.d.ts.map +1 -0
- package/dist/server/services/pr-service.js +231 -0
- package/dist/server/services/pr-service.js.map +1 -0
- package/dist/server/services/project-group-service.d.ts +64 -0
- package/dist/server/services/project-group-service.d.ts.map +1 -0
- package/dist/server/services/project-group-service.js +149 -0
- package/dist/server/services/project-group-service.js.map +1 -0
- package/dist/server/services/project-service.d.ts +161 -0
- package/dist/server/services/project-service.d.ts.map +1 -0
- package/dist/server/services/project-service.js +623 -0
- package/dist/server/services/project-service.js.map +1 -0
- package/dist/server/services/service-error.d.ts +25 -0
- package/dist/server/services/service-error.d.ts.map +1 -0
- package/dist/server/services/service-error.js +49 -0
- package/dist/server/services/service-error.js.map +1 -0
- package/dist/server/services/sse-broker.d.ts +144 -0
- package/dist/server/services/sse-broker.d.ts.map +1 -0
- package/dist/server/services/sse-broker.js +111 -0
- package/dist/server/services/sse-broker.js.map +1 -0
- package/dist/server/services/startup-recovery.d.ts +10 -0
- package/dist/server/services/startup-recovery.d.ts.map +1 -0
- package/dist/server/services/startup-recovery.js +122 -0
- package/dist/server/services/startup-recovery.js.map +1 -0
- package/dist/server/services/stuck-detector.d.ts +20 -0
- package/dist/server/services/stuck-detector.d.ts.map +1 -0
- package/dist/server/services/stuck-detector.js +167 -0
- package/dist/server/services/stuck-detector.js.map +1 -0
- package/dist/server/services/stuck-detector.test.d.ts +2 -0
- package/dist/server/services/stuck-detector.test.d.ts.map +1 -0
- package/dist/server/services/stuck-detector.test.js +363 -0
- package/dist/server/services/stuck-detector.test.js.map +1 -0
- package/dist/server/services/team-manager.d.ts +188 -0
- package/dist/server/services/team-manager.d.ts.map +1 -0
- package/dist/server/services/team-manager.js +1869 -0
- package/dist/server/services/team-manager.js.map +1 -0
- package/dist/server/services/team-service.d.ts +251 -0
- package/dist/server/services/team-service.d.ts.map +1 -0
- package/dist/server/services/team-service.js +707 -0
- package/dist/server/services/team-service.js.map +1 -0
- package/dist/server/services/usage-service.d.ts +42 -0
- package/dist/server/services/usage-service.d.ts.map +1 -0
- package/dist/server/services/usage-service.js +101 -0
- package/dist/server/services/usage-service.js.map +1 -0
- package/dist/server/services/usage-tracker.d.ts +68 -0
- package/dist/server/services/usage-tracker.d.ts.map +1 -0
- package/dist/server/services/usage-tracker.js +220 -0
- package/dist/server/services/usage-tracker.js.map +1 -0
- package/dist/server/utils/build-timeline.d.ts +32 -0
- package/dist/server/utils/build-timeline.d.ts.map +1 -0
- package/dist/server/utils/build-timeline.js +142 -0
- package/dist/server/utils/build-timeline.js.map +1 -0
- package/dist/server/utils/find-git-bash.d.ts +10 -0
- package/dist/server/utils/find-git-bash.d.ts.map +1 -0
- package/dist/server/utils/find-git-bash.js +46 -0
- package/dist/server/utils/find-git-bash.js.map +1 -0
- package/dist/server/utils/hook-installer.d.ts +20 -0
- package/dist/server/utils/hook-installer.d.ts.map +1 -0
- package/dist/server/utils/hook-installer.js +90 -0
- package/dist/server/utils/hook-installer.js.map +1 -0
- package/dist/server/utils/process-utils.d.ts +10 -0
- package/dist/server/utils/process-utils.d.ts.map +1 -0
- package/dist/server/utils/process-utils.js +33 -0
- package/dist/server/utils/process-utils.js.map +1 -0
- package/dist/server/utils/resolve-claude-path.d.ts +4 -0
- package/dist/server/utils/resolve-claude-path.d.ts.map +1 -0
- package/dist/server/utils/resolve-claude-path.js +66 -0
- package/dist/server/utils/resolve-claude-path.js.map +1 -0
- package/dist/server/utils/resolve-message.d.ts +10 -0
- package/dist/server/utils/resolve-message.d.ts.map +1 -0
- package/dist/server/utils/resolve-message.js +27 -0
- package/dist/server/utils/resolve-message.js.map +1 -0
- package/dist/shared/message-templates.d.ts +9 -0
- package/dist/shared/message-templates.d.ts.map +1 -0
- package/dist/shared/message-templates.js +88 -0
- package/dist/shared/message-templates.js.map +1 -0
- package/dist/shared/state-machine.d.ts +28 -0
- package/dist/shared/state-machine.d.ts.map +1 -0
- package/dist/shared/state-machine.js +282 -0
- package/dist/shared/state-machine.js.map +1 -0
- package/dist/shared/types.d.ts +404 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +5 -0
- package/dist/shared/types.js.map +1 -0
- package/hooks/DESIGN.md +562 -0
- package/hooks/on_notification.sh +9 -0
- package/hooks/on_post_tool_use.sh +9 -0
- package/hooks/on_pre_compact.sh +10 -0
- package/hooks/on_session_end.sh +8 -0
- package/hooks/on_session_start.sh +8 -0
- package/hooks/on_stop.sh +8 -0
- package/hooks/on_stop_failure.sh +8 -0
- package/hooks/on_subagent_start.sh +8 -0
- package/hooks/on_subagent_stop.sh +8 -0
- package/hooks/on_teammate_idle.sh +8 -0
- package/hooks/on_tool_error.sh +8 -0
- package/hooks/send_event.sh +101 -0
- package/hooks/settings.json.example +120 -0
- package/package.json +93 -0
- package/prompts/default-prompt.md +16 -0
- package/scripts/install.ps1 +22 -0
- package/scripts/install.sh +229 -0
- package/scripts/launch.js +64 -0
- package/scripts/uninstall.ps1 +22 -0
- package/scripts/uninstall.sh +123 -0
- package/templates/agents/fleet-dev.md +162 -0
- package/templates/agents/fleet-planner.md +263 -0
- package/templates/agents/fleet-reviewer.md +309 -0
- package/templates/archive/fleet-coordinator.md +128 -0
- package/templates/archive/fleet-dev-csharp.md +74 -0
- package/templates/archive/fleet-dev-devops.md +76 -0
- package/templates/archive/fleet-dev-fsharp.md +83 -0
- package/templates/archive/fleet-dev-generic.md +64 -0
- package/templates/archive/fleet-dev-python.md +75 -0
- package/templates/archive/fleet-dev-typescript.md +76 -0
- package/templates/guides/api-design.md +159 -0
- package/templates/guides/csharp-conventions.md +182 -0
- package/templates/guides/devops-conventions.md +192 -0
- package/templates/guides/fsharp-conventions.md +201 -0
- package/templates/guides/python-conventions.md +146 -0
- package/templates/guides/sql-database.md +125 -0
- package/templates/guides/testing-strategies.md +123 -0
- package/templates/guides/typescript-conventions.md +130 -0
- package/templates/workflow.md +513 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Fleet Commander — Centralized Error Handler
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// All API errors are returned in a consistent {error, code, details?} format.
|
|
5
|
+
// Stack traces are logged server-side only and never leaked to clients.
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Application error class
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Throw an AppError from route handlers or services to control the HTTP
|
|
12
|
+
* status code, machine-readable code, and optional details sent to the client.
|
|
13
|
+
*
|
|
14
|
+
* Example:
|
|
15
|
+
* throw new AppError(404, 'TEAM_NOT_FOUND', `Team ${id} not found`);
|
|
16
|
+
* throw new AppError(400, 'INVALID_PAYLOAD', 'Missing team_id', { field: 'team_id' });
|
|
17
|
+
*/
|
|
18
|
+
export class AppError extends Error {
|
|
19
|
+
statusCode;
|
|
20
|
+
code;
|
|
21
|
+
details;
|
|
22
|
+
constructor(statusCode, code, message, details) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.statusCode = statusCode;
|
|
25
|
+
this.code = code;
|
|
26
|
+
this.details = details;
|
|
27
|
+
this.name = 'AppError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Fastify error handler
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* Centralized error handler registered via `server.setErrorHandler(errorHandler)`.
|
|
35
|
+
*
|
|
36
|
+
* Behaviour:
|
|
37
|
+
* - AppError instances use their own statusCode and code.
|
|
38
|
+
* - Fastify validation errors (e.g. schema failures) use 400 + the error code.
|
|
39
|
+
* - All other errors default to 500 / INTERNAL_ERROR.
|
|
40
|
+
* - 5xx errors log the full stack trace; 4xx errors log a warning.
|
|
41
|
+
* - Client responses never include stack traces.
|
|
42
|
+
*/
|
|
43
|
+
export function errorHandler(error, request, reply) {
|
|
44
|
+
const statusCode = error instanceof AppError
|
|
45
|
+
? error.statusCode
|
|
46
|
+
: error.statusCode ?? 500;
|
|
47
|
+
const code = error instanceof AppError
|
|
48
|
+
? error.code
|
|
49
|
+
: error.code ?? 'INTERNAL_ERROR';
|
|
50
|
+
// Server-side logging: full stack for 500s, concise warning for 4xx
|
|
51
|
+
if (statusCode >= 500) {
|
|
52
|
+
request.log.error({ err: error, url: request.url }, 'Internal server error');
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
request.log.warn({ code, url: request.url, statusCode }, error.message);
|
|
56
|
+
}
|
|
57
|
+
// Build client-safe response — never leak stack traces
|
|
58
|
+
const response = {
|
|
59
|
+
error: statusCode >= 500 ? 'Internal server error' : error.message,
|
|
60
|
+
code,
|
|
61
|
+
details: error instanceof AppError ? error.details : undefined,
|
|
62
|
+
};
|
|
63
|
+
reply.status(statusCode).send(response);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=error-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../../src/server/middleware/error-handler.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,8CAA8C;AAC9C,gFAAgF;AAChF,8EAA8E;AAC9E,wEAAwE;AACxE,gFAAgF;AAchF,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IAExB;IACA;IAEA;IAJT,YACS,UAAkB,EAClB,IAAY,EACnB,OAAe,EACR,OAAiB;QAExB,KAAK,CAAC,OAAO,CAAC,CAAC;QALR,eAAU,GAAV,UAAU,CAAQ;QAClB,SAAI,GAAJ,IAAI,CAAQ;QAEZ,YAAO,GAAP,OAAO,CAAU;QAGxB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAmB,EACnB,OAAuB,EACvB,KAAmB;IAEnB,MAAM,UAAU,GAAG,KAAK,YAAY,QAAQ;QAC1C,CAAC,CAAC,KAAK,CAAC,UAAU;QAClB,CAAC,CAAC,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC;IAE5B,MAAM,IAAI,GAAG,KAAK,YAAY,QAAQ;QACpC,CAAC,CAAC,KAAK,CAAC,IAAI;QACZ,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,gBAAgB,CAAC;IAEnC,oEAAoE;IACpE,IAAI,UAAU,IAAI,GAAG,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,uBAAuB,CAAC,CAAC;IAC/E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1E,CAAC;IAED,uDAAuD;IACvD,MAAM,QAAQ,GAAa;QACzB,KAAK,EAAE,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO;QAClE,IAAI;QACJ,OAAO,EAAE,KAAK,YAAY,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KAC/D,CAAC;IAEF,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FastifyPluginCallback } from 'fastify';
|
|
2
|
+
import type { EventPayload } from '../services/event-collector.js';
|
|
3
|
+
/**
|
|
4
|
+
* New format: shell sends event, team, timestamp, and raw cc_stdin.
|
|
5
|
+
* Server parses cc_stdin with JSON.parse() and extracts all CC fields.
|
|
6
|
+
*/
|
|
7
|
+
declare function buildPayloadFromCcStdin(body: Record<string, unknown>): EventPayload;
|
|
8
|
+
/**
|
|
9
|
+
* Legacy format: shell extracts fields individually and sends them as top-level
|
|
10
|
+
* body fields. Maintains backward compatibility with old hook installations.
|
|
11
|
+
*/
|
|
12
|
+
declare function buildPayloadFromLegacy(body: Record<string, unknown>): EventPayload;
|
|
13
|
+
declare const eventsRoutes: FastifyPluginCallback;
|
|
14
|
+
export default eventsRoutes;
|
|
15
|
+
export { buildPayloadFromCcStdin, buildPayloadFromLegacy };
|
|
16
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/server/routes/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,qBAAqB,EAAgC,MAAM,SAAS,CAAC;AAEpG,OAAO,KAAK,EAAE,YAAY,EAAkD,MAAM,gCAAgC,CAAC;AAwBnH;;;GAGG;AACH,iBAAS,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,YAAY,CAqD5E;AAED;;;GAGG;AACH,iBAAS,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,YAAY,CAmB3E;AAED,QAAA,MAAM,YAAY,EAAE,qBAoFnB,CAAC;AAEF,eAAe,YAAY,CAAC;AAG5B,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { processEvent, EventCollectorError } from '../services/event-collector.js';
|
|
2
|
+
import { getDatabase } from '../db.js';
|
|
3
|
+
import { sseBroker } from '../services/sse-broker.js';
|
|
4
|
+
import { getTeamManager } from '../services/team-manager.js';
|
|
5
|
+
import { getEventService } from '../services/event-service.js';
|
|
6
|
+
import { ServiceError } from '../services/service-error.js';
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Payload builders — new cc_stdin format vs legacy field-by-field
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/** Helper to safely extract a string from an unknown value */
|
|
11
|
+
function str(val) {
|
|
12
|
+
if (val === undefined || val === null || val === '')
|
|
13
|
+
return undefined;
|
|
14
|
+
return String(val);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* New format: shell sends event, team, timestamp, and raw cc_stdin.
|
|
18
|
+
* Server parses cc_stdin with JSON.parse() and extracts all CC fields.
|
|
19
|
+
*/
|
|
20
|
+
function buildPayloadFromCcStdin(body) {
|
|
21
|
+
const payload = {
|
|
22
|
+
event: String(body.event),
|
|
23
|
+
team: String(body.team),
|
|
24
|
+
timestamp: str(body.timestamp),
|
|
25
|
+
cc_stdin: String(body.cc_stdin),
|
|
26
|
+
};
|
|
27
|
+
// Parse raw CC stdin JSON
|
|
28
|
+
let cc = {};
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(String(body.cc_stdin));
|
|
31
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
32
|
+
cc = parsed;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// If cc_stdin is not valid JSON, store it as-is but extract nothing
|
|
37
|
+
return payload;
|
|
38
|
+
}
|
|
39
|
+
// Extract known CC fields
|
|
40
|
+
payload.session_id = str(cc.session_id);
|
|
41
|
+
payload.tool_name = str(cc.tool_name);
|
|
42
|
+
payload.agent_type = str(cc.agent_type);
|
|
43
|
+
payload.teammate_name = str(cc.teammate_name);
|
|
44
|
+
payload.message = str(cc.message);
|
|
45
|
+
payload.error = str(cc.error);
|
|
46
|
+
payload.tool_use_id = str(cc.tool_use_id);
|
|
47
|
+
payload.error_details = str(cc.error_details);
|
|
48
|
+
payload.last_assistant_message = str(cc.last_assistant_message);
|
|
49
|
+
// tool_input: CC sends this as an object; stringify it for storage
|
|
50
|
+
if (cc.tool_input !== undefined && cc.tool_input !== null) {
|
|
51
|
+
payload.tool_input = typeof cc.tool_input === 'string'
|
|
52
|
+
? cc.tool_input
|
|
53
|
+
: JSON.stringify(cc.tool_input);
|
|
54
|
+
}
|
|
55
|
+
// Extract SendMessage routing fields from parsed tool_input
|
|
56
|
+
if (payload.tool_name === 'SendMessage' && cc.tool_input && typeof cc.tool_input === 'object') {
|
|
57
|
+
const toolInput = cc.tool_input;
|
|
58
|
+
payload.msg_to = str(toolInput.to);
|
|
59
|
+
payload.msg_summary = str(toolInput.summary);
|
|
60
|
+
}
|
|
61
|
+
// New fields that CC provides but were previously dropped by shell regex
|
|
62
|
+
payload.model = str(cc.model);
|
|
63
|
+
payload.source = str(cc.source);
|
|
64
|
+
payload.notification_type = str(cc.notification_type);
|
|
65
|
+
payload.agent_id = str(cc.agent_id);
|
|
66
|
+
payload.cwd = str(cc.cwd);
|
|
67
|
+
return payload;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Legacy format: shell extracts fields individually and sends them as top-level
|
|
71
|
+
* body fields. Maintains backward compatibility with old hook installations.
|
|
72
|
+
*/
|
|
73
|
+
function buildPayloadFromLegacy(body) {
|
|
74
|
+
return {
|
|
75
|
+
event: String(body.event),
|
|
76
|
+
team: String(body.team),
|
|
77
|
+
timestamp: str(body.timestamp),
|
|
78
|
+
session_id: str(body.session_id),
|
|
79
|
+
tool_name: str(body.tool_name),
|
|
80
|
+
agent_type: str(body.agent_type),
|
|
81
|
+
teammate_name: str(body.teammate_name),
|
|
82
|
+
message: str(body.message),
|
|
83
|
+
error: str(body.error),
|
|
84
|
+
tool_use_id: str(body.tool_use_id),
|
|
85
|
+
tool_input: str(body.tool_input),
|
|
86
|
+
error_details: str(body.error_details),
|
|
87
|
+
last_assistant_message: str(body.last_assistant_message),
|
|
88
|
+
worktree_root: str(body.worktree_root),
|
|
89
|
+
msg_to: str(body.msg_to),
|
|
90
|
+
msg_summary: str(body.msg_summary),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const eventsRoutes = (fastify, _opts, done) => {
|
|
94
|
+
// POST /api/events — receive a hook event
|
|
95
|
+
fastify.post('/api/events', async (request, reply) => {
|
|
96
|
+
try {
|
|
97
|
+
const body = request.body;
|
|
98
|
+
if (!body || typeof body !== 'object' || !body.event || !body.team) {
|
|
99
|
+
return reply.code(400).send({
|
|
100
|
+
error: 'Bad Request',
|
|
101
|
+
message: 'Missing required fields: event, team',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Build EventPayload — detect new format (cc_stdin) vs legacy field-by-field
|
|
105
|
+
const payload = body.cc_stdin
|
|
106
|
+
? buildPayloadFromCcStdin(body)
|
|
107
|
+
: buildPayloadFromLegacy(body);
|
|
108
|
+
const db = getDatabase();
|
|
109
|
+
const manager = getTeamManager();
|
|
110
|
+
const result = processEvent(payload, db, sseBroker, manager);
|
|
111
|
+
// When a stop event is received, a team may be finishing —
|
|
112
|
+
// trigger queue processing so queued teams can launch.
|
|
113
|
+
if (payload.event === 'stop' || payload.event === 'stop_failure' || payload.event === 'session_end') {
|
|
114
|
+
const team = db.getTeamByWorktree(payload.team);
|
|
115
|
+
if (team?.projectId) {
|
|
116
|
+
getTeamManager().processQueue(team.projectId).catch((err) => {
|
|
117
|
+
request.log.error(err, 'processQueue error after stop/session_end/stop_failure event');
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return reply.code(200).send(result);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
if (err instanceof EventCollectorError) {
|
|
125
|
+
const status = err.code === 'TEAM_NOT_FOUND' ? 404 : 400;
|
|
126
|
+
return reply.code(status).send({
|
|
127
|
+
error: status === 404 ? 'Not Found' : 'Bad Request',
|
|
128
|
+
message: err.message,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
request.log.error(err, 'Unexpected error processing event');
|
|
132
|
+
return reply.code(500).send({
|
|
133
|
+
error: 'Internal Server Error',
|
|
134
|
+
message: 'Failed to process event',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
// GET /api/events — query events with filters
|
|
139
|
+
fastify.get('/api/events', async (request, reply) => {
|
|
140
|
+
try {
|
|
141
|
+
const query = request.query;
|
|
142
|
+
const service = getEventService();
|
|
143
|
+
const events = service.queryEvents({
|
|
144
|
+
teamId: query.team_id ? parseInt(query.team_id, 10) : undefined,
|
|
145
|
+
eventType: query.type || undefined,
|
|
146
|
+
since: query.since || undefined,
|
|
147
|
+
limit: query.limit ? parseInt(query.limit, 10) : undefined,
|
|
148
|
+
});
|
|
149
|
+
return reply.code(200).send(events);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
if (err instanceof ServiceError) {
|
|
153
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
154
|
+
}
|
|
155
|
+
request.log.error(err, 'Unexpected error querying events');
|
|
156
|
+
return reply.code(500).send({ error: 'Internal Server Error', message: 'Failed to query events' });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
done();
|
|
160
|
+
};
|
|
161
|
+
export default eventsRoutes;
|
|
162
|
+
// Exported for testing
|
|
163
|
+
export { buildPayloadFromCcStdin, buildPayloadFromLegacy };
|
|
164
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/server/routes/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../../../src/server/routes/issues.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAQ7E,iBAAe,WAAW,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAkMjE;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Fleet Commander -- Issue Routes (REST endpoints for issue hierarchy)
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Registered as a Fastify plugin. Provides endpoints for:
|
|
5
|
+
// GET /api/issues — full hierarchy tree (cached, all projects)
|
|
6
|
+
// GET /api/issues/next — suggest next issue to work on
|
|
7
|
+
// GET /api/issues/available — issues with no active team
|
|
8
|
+
// GET /api/issues/:number — single issue detail
|
|
9
|
+
// POST /api/issues/refresh — force re-fetch from GitHub
|
|
10
|
+
// GET /api/projects/:projectId/issues — per-project issue tree
|
|
11
|
+
// Business logic is delegated to IssueService.
|
|
12
|
+
// =============================================================================
|
|
13
|
+
import { getIssueService } from '../services/issue-service.js';
|
|
14
|
+
import { ServiceError } from '../services/service-error.js';
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Fastify plugin
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
async function issueRoutes(server) {
|
|
19
|
+
/**
|
|
20
|
+
* GET /api/issues — Full hierarchy tree (cached, all projects)
|
|
21
|
+
* Returns the complete issue tree enriched with active team info.
|
|
22
|
+
* Also returns `groups` — issues grouped by project — so the client
|
|
23
|
+
* can render collapsible project sections when "All Projects" is selected.
|
|
24
|
+
*/
|
|
25
|
+
server.get('/api/issues', async (_request, reply) => {
|
|
26
|
+
try {
|
|
27
|
+
const service = getIssueService();
|
|
28
|
+
const result = await service.getAllIssues();
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (err instanceof ServiceError) {
|
|
33
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
34
|
+
}
|
|
35
|
+
_request.log.error(err, 'Failed to get issues');
|
|
36
|
+
return reply.code(500).send({
|
|
37
|
+
error: 'Internal Server Error',
|
|
38
|
+
message: err instanceof Error ? err.message : String(err),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* GET /api/projects/:projectId/issues — Per-project issue tree
|
|
44
|
+
* Returns the issue hierarchy for a specific project.
|
|
45
|
+
*/
|
|
46
|
+
server.get('/api/projects/:projectId/issues', async (request, reply) => {
|
|
47
|
+
try {
|
|
48
|
+
const projectId = parseInt(request.params.projectId, 10);
|
|
49
|
+
const service = getIssueService();
|
|
50
|
+
const result = await service.getProjectIssues(projectId);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err instanceof ServiceError) {
|
|
55
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
56
|
+
}
|
|
57
|
+
request.log.error(err, 'Failed to get project issues');
|
|
58
|
+
return reply.code(500).send({
|
|
59
|
+
error: 'Internal Server Error',
|
|
60
|
+
message: err instanceof Error ? err.message : String(err),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* GET /api/issues/next — Suggest next issue to work on
|
|
66
|
+
* Returns the highest-priority Ready issue with no active team.
|
|
67
|
+
*/
|
|
68
|
+
server.get('/api/issues/next', async (_request, reply) => {
|
|
69
|
+
try {
|
|
70
|
+
const service = getIssueService();
|
|
71
|
+
return service.getNextIssue();
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err instanceof ServiceError) {
|
|
75
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
76
|
+
}
|
|
77
|
+
_request.log.error(err, 'Failed to get next issue');
|
|
78
|
+
return reply.code(500).send({
|
|
79
|
+
error: 'Internal Server Error',
|
|
80
|
+
message: err instanceof Error ? err.message : String(err),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* GET /api/issues/available — Issues with no active team
|
|
86
|
+
* Returns all open leaf issues that have no team currently working on them.
|
|
87
|
+
*/
|
|
88
|
+
server.get('/api/issues/available', async (_request, reply) => {
|
|
89
|
+
try {
|
|
90
|
+
const service = getIssueService();
|
|
91
|
+
return service.getAvailableIssues();
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
if (err instanceof ServiceError) {
|
|
95
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
96
|
+
}
|
|
97
|
+
_request.log.error(err, 'Failed to get available issues');
|
|
98
|
+
return reply.code(500).send({
|
|
99
|
+
error: 'Internal Server Error',
|
|
100
|
+
message: err instanceof Error ? err.message : String(err),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
/**
|
|
105
|
+
* GET /api/issues/:number — Single issue detail
|
|
106
|
+
* Returns a single issue from the cache, enriched with team info.
|
|
107
|
+
*/
|
|
108
|
+
server.get('/api/issues/:number', async (request, reply) => {
|
|
109
|
+
try {
|
|
110
|
+
const issueNumber = parseInt(request.params.number, 10);
|
|
111
|
+
const service = getIssueService();
|
|
112
|
+
return service.getIssue(issueNumber);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (err instanceof ServiceError) {
|
|
116
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
117
|
+
}
|
|
118
|
+
request.log.error(err, 'Failed to get issue');
|
|
119
|
+
return reply.code(500).send({
|
|
120
|
+
error: 'Internal Server Error',
|
|
121
|
+
message: err instanceof Error ? err.message : String(err),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* GET /api/projects/:projectId/issues/dependencies — Dependencies for all issues in a project
|
|
127
|
+
* Returns dependency info for all cached issues in the specified project.
|
|
128
|
+
*/
|
|
129
|
+
server.get('/api/projects/:projectId/issues/dependencies', async (request, reply) => {
|
|
130
|
+
try {
|
|
131
|
+
const projectId = parseInt(request.params.projectId, 10);
|
|
132
|
+
const service = getIssueService();
|
|
133
|
+
return await service.getProjectDependencies(projectId);
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
if (err instanceof ServiceError) {
|
|
137
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
138
|
+
}
|
|
139
|
+
request.log.error(err, 'Failed to get project dependencies');
|
|
140
|
+
return reply.code(500).send({
|
|
141
|
+
error: 'Internal Server Error',
|
|
142
|
+
message: err instanceof Error ? err.message : String(err),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
/**
|
|
147
|
+
* GET /api/issues/:number/dependencies — Dependencies for a single issue
|
|
148
|
+
* Returns dependency info for the specified issue number.
|
|
149
|
+
* Requires a projectId query parameter.
|
|
150
|
+
*/
|
|
151
|
+
server.get('/api/issues/:number/dependencies', async (request, reply) => {
|
|
152
|
+
try {
|
|
153
|
+
const issueNumber = parseInt(request.params.number, 10);
|
|
154
|
+
const projectIdStr = request.query.projectId;
|
|
155
|
+
if (!projectIdStr) {
|
|
156
|
+
return reply.code(400).send({
|
|
157
|
+
error: 'Bad Request',
|
|
158
|
+
message: 'projectId query parameter is required',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const projectId = parseInt(projectIdStr, 10);
|
|
162
|
+
const service = getIssueService();
|
|
163
|
+
return await service.getIssueDependencies(issueNumber, projectId);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
if (err instanceof ServiceError) {
|
|
167
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
168
|
+
}
|
|
169
|
+
request.log.error(err, 'Failed to get issue dependencies');
|
|
170
|
+
return reply.code(500).send({
|
|
171
|
+
error: 'Internal Server Error',
|
|
172
|
+
message: err instanceof Error ? err.message : String(err),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
/**
|
|
177
|
+
* POST /api/issues/refresh — Force re-fetch from GitHub
|
|
178
|
+
* Clears the cache and re-fetches the full hierarchy for all projects.
|
|
179
|
+
*/
|
|
180
|
+
server.post('/api/issues/refresh', async (_request, reply) => {
|
|
181
|
+
try {
|
|
182
|
+
const service = getIssueService();
|
|
183
|
+
return await service.refresh();
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
if (err instanceof ServiceError) {
|
|
187
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
188
|
+
}
|
|
189
|
+
_request.log.error(err, 'Failed to refresh issues');
|
|
190
|
+
return reply.code(500).send({
|
|
191
|
+
error: 'Internal Server Error',
|
|
192
|
+
message: err instanceof Error ? err.message : String(err),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
export default issueRoutes;
|
|
198
|
+
//# sourceMappingURL=issues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.js","sourceRoot":"","sources":["../../../src/server/routes/issues.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-groups.d.ts","sourceRoot":"","sources":["../../../src/server/routes/project-groups.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,qBAAqB,EAGtB,MAAM,SAAS,CAAC;AA0BjB,QAAA,MAAM,mBAAmB,EAAE,qBAoJ1B,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Fleet Commander — Project Group Routes (CRUD)
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Business logic is delegated to ProjectGroupService.
|
|
5
|
+
// =============================================================================
|
|
6
|
+
import { getProjectGroupService } from '../services/project-group-service.js';
|
|
7
|
+
import { ServiceError } from '../services/service-error.js';
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Plugin
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
const projectGroupsRoutes = (fastify, _opts, done) => {
|
|
12
|
+
// -------------------------------------------------------------------------
|
|
13
|
+
// GET /api/project-groups — list all groups with project counts
|
|
14
|
+
// -------------------------------------------------------------------------
|
|
15
|
+
fastify.get('/api/project-groups', async (_request, reply) => {
|
|
16
|
+
try {
|
|
17
|
+
const service = getProjectGroupService();
|
|
18
|
+
const enriched = service.listWithCounts();
|
|
19
|
+
return reply.code(200).send(enriched);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
if (err instanceof ServiceError) {
|
|
23
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
24
|
+
}
|
|
25
|
+
_request.log.error(err, 'Failed to list project groups');
|
|
26
|
+
return reply.code(500).send({
|
|
27
|
+
error: 'Internal Server Error',
|
|
28
|
+
message: err instanceof Error ? err.message : String(err),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// -------------------------------------------------------------------------
|
|
33
|
+
// POST /api/project-groups — create a new group
|
|
34
|
+
// -------------------------------------------------------------------------
|
|
35
|
+
fastify.post('/api/project-groups', async (request, reply) => {
|
|
36
|
+
try {
|
|
37
|
+
const service = getProjectGroupService();
|
|
38
|
+
const group = service.createGroup(request.body || {});
|
|
39
|
+
return reply.code(201).send(group);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (err instanceof ServiceError) {
|
|
43
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
44
|
+
}
|
|
45
|
+
request.log.error(err, 'Failed to create project group');
|
|
46
|
+
return reply.code(500).send({
|
|
47
|
+
error: 'Internal Server Error',
|
|
48
|
+
message: err instanceof Error ? err.message : String(err),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// -------------------------------------------------------------------------
|
|
53
|
+
// GET /api/project-groups/:id — get group detail with its projects
|
|
54
|
+
// -------------------------------------------------------------------------
|
|
55
|
+
fastify.get('/api/project-groups/:id', async (request, reply) => {
|
|
56
|
+
try {
|
|
57
|
+
const groupId = parseInt(request.params.id, 10);
|
|
58
|
+
if (isNaN(groupId) || groupId < 1) {
|
|
59
|
+
return reply.code(400).send({
|
|
60
|
+
error: 'Bad Request',
|
|
61
|
+
message: 'Invalid group ID',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const service = getProjectGroupService();
|
|
65
|
+
const result = service.getWithProjects(groupId);
|
|
66
|
+
return reply.code(200).send(result);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
if (err instanceof ServiceError) {
|
|
70
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
71
|
+
}
|
|
72
|
+
request.log.error(err, 'Failed to get project group');
|
|
73
|
+
return reply.code(500).send({
|
|
74
|
+
error: 'Internal Server Error',
|
|
75
|
+
message: err instanceof Error ? err.message : String(err),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// -------------------------------------------------------------------------
|
|
80
|
+
// PUT /api/project-groups/:id — update group
|
|
81
|
+
// -------------------------------------------------------------------------
|
|
82
|
+
fastify.put('/api/project-groups/:id', async (request, reply) => {
|
|
83
|
+
try {
|
|
84
|
+
const groupId = parseInt(request.params.id, 10);
|
|
85
|
+
const service = getProjectGroupService();
|
|
86
|
+
const updated = service.updateGroup(groupId, request.body || {});
|
|
87
|
+
return reply.code(200).send(updated);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
if (err instanceof ServiceError) {
|
|
91
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
92
|
+
}
|
|
93
|
+
request.log.error(err, 'Failed to update project group');
|
|
94
|
+
return reply.code(500).send({
|
|
95
|
+
error: 'Internal Server Error',
|
|
96
|
+
message: err instanceof Error ? err.message : String(err),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// -------------------------------------------------------------------------
|
|
101
|
+
// DELETE /api/project-groups/:id — delete group (unlinks projects)
|
|
102
|
+
// -------------------------------------------------------------------------
|
|
103
|
+
fastify.delete('/api/project-groups/:id', async (request, reply) => {
|
|
104
|
+
try {
|
|
105
|
+
const groupId = parseInt(request.params.id, 10);
|
|
106
|
+
const service = getProjectGroupService();
|
|
107
|
+
service.deleteGroup(groupId);
|
|
108
|
+
return reply.code(200).send({ success: true });
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (err instanceof ServiceError) {
|
|
112
|
+
return reply.code(err.statusCode).send({ error: err.code, message: err.message });
|
|
113
|
+
}
|
|
114
|
+
request.log.error(err, 'Failed to delete project group');
|
|
115
|
+
return reply.code(500).send({
|
|
116
|
+
error: 'Internal Server Error',
|
|
117
|
+
message: err instanceof Error ? err.message : String(err),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
done();
|
|
122
|
+
};
|
|
123
|
+
export default projectGroupsRoutes;
|
|
124
|
+
//# sourceMappingURL=project-groups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-groups.js","sourceRoot":"","sources":["../../../src/server/routes/project-groups.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../../src/server/routes/projects.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAEV,qBAAqB,EAGtB,MAAM,SAAS,CAAC;AAwCjB,QAAA,MAAM,cAAc,EAAE,qBAmYrB,CAAC;AAEF,eAAe,cAAc,CAAC"}
|