vibeman 0.0.0 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/index.js +114 -0
- package/dist/runtime/api/.tsbuildinfo +1 -0
- package/dist/runtime/api/agent/agent-service.d.ts +224 -0
- package/dist/runtime/api/agent/agent-service.js +895 -0
- package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +61 -0
- package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +362 -0
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +34 -0
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +315 -0
- package/dist/runtime/api/agent/ai-providers/index.d.ts +9 -0
- package/dist/runtime/api/agent/ai-providers/index.js +7 -0
- package/dist/runtime/api/agent/ai-providers/types.d.ts +182 -0
- package/dist/runtime/api/agent/ai-providers/types.js +5 -0
- package/dist/runtime/api/agent/codex-cli-provider.test.d.ts +1 -0
- package/dist/runtime/api/agent/codex-cli-provider.test.js +125 -0
- package/dist/runtime/api/agent/core-agent-service.d.ts +119 -0
- package/dist/runtime/api/agent/core-agent-service.js +267 -0
- package/dist/runtime/api/agent/parsers.d.ts +16 -0
- package/dist/runtime/api/agent/parsers.js +308 -0
- package/dist/runtime/api/agent/prompt-service.d.ts +30 -0
- package/dist/runtime/api/agent/prompt-service.js +449 -0
- package/dist/runtime/api/agent/prompt-service.test.d.ts +1 -0
- package/dist/runtime/api/agent/prompt-service.test.js +230 -0
- package/dist/runtime/api/agent/routing-policy.d.ts +188 -0
- package/dist/runtime/api/agent/routing-policy.js +246 -0
- package/dist/runtime/api/api/router-helpers.d.ts +32 -0
- package/dist/runtime/api/api/router-helpers.js +31 -0
- package/dist/runtime/api/api/routers/ai.d.ts +188 -0
- package/dist/runtime/api/api/routers/ai.js +395 -0
- package/dist/runtime/api/api/routers/executions.d.ts +98 -0
- package/dist/runtime/api/api/routers/executions.js +94 -0
- package/dist/runtime/api/api/routers/git.d.ts +45 -0
- package/dist/runtime/api/api/routers/git.js +35 -0
- package/dist/runtime/api/api/routers/provider-config.d.ts +165 -0
- package/dist/runtime/api/api/routers/provider-config.js +252 -0
- package/dist/runtime/api/api/routers/settings.d.ts +139 -0
- package/dist/runtime/api/api/routers/settings.js +113 -0
- package/dist/runtime/api/api/routers/tasks.d.ts +141 -0
- package/dist/runtime/api/api/routers/tasks.js +238 -0
- package/dist/runtime/api/api/routers/workflows.d.ts +267 -0
- package/dist/runtime/api/api/routers/workflows.js +310 -0
- package/dist/runtime/api/api/routers/worktrees.d.ts +101 -0
- package/dist/runtime/api/api/routers/worktrees.js +80 -0
- package/dist/runtime/api/api/trpc.d.ts +118 -0
- package/dist/runtime/api/api/trpc.js +34 -0
- package/dist/runtime/api/index.d.ts +9 -0
- package/dist/runtime/api/index.js +117 -0
- package/dist/runtime/api/lib/id-generator.d.ts +70 -0
- package/dist/runtime/api/lib/id-generator.js +123 -0
- package/dist/runtime/api/lib/image-paste-drop-extension.d.ts +26 -0
- package/dist/runtime/api/lib/image-paste-drop-extension.js +125 -0
- package/dist/runtime/api/lib/local-config.d.ts +245 -0
- package/dist/runtime/api/lib/local-config.js +288 -0
- package/dist/runtime/api/lib/logger.d.ts +11 -0
- package/dist/runtime/api/lib/logger.js +188 -0
- package/dist/runtime/api/lib/markdown-utils.d.ts +8 -0
- package/dist/runtime/api/lib/markdown-utils.js +282 -0
- package/dist/runtime/api/lib/markdown-utils.test.d.ts +1 -0
- package/dist/runtime/api/lib/markdown-utils.test.js +348 -0
- package/dist/runtime/api/lib/provider-detection.d.ts +59 -0
- package/dist/runtime/api/lib/provider-detection.js +244 -0
- package/dist/runtime/api/lib/server/agent-service-singleton.d.ts +6 -0
- package/dist/runtime/api/lib/server/agent-service-singleton.js +27 -0
- package/dist/runtime/api/lib/server/bootstrap.d.ts +38 -0
- package/dist/runtime/api/lib/server/bootstrap.js +197 -0
- package/dist/runtime/api/lib/server/git-service-singleton.d.ts +6 -0
- package/dist/runtime/api/lib/server/git-service-singleton.js +47 -0
- package/dist/runtime/api/lib/server/project-root.d.ts +2 -0
- package/dist/runtime/api/lib/server/project-root.js +61 -0
- package/dist/runtime/api/lib/server/task-service-singleton.d.ts +7 -0
- package/dist/runtime/api/lib/server/task-service-singleton.js +58 -0
- package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.d.ts +7 -0
- package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.js +57 -0
- package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.d.ts +1 -0
- package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.js +27 -0
- package/dist/runtime/api/lib/tiptap-utils.d.ts +130 -0
- package/dist/runtime/api/lib/tiptap-utils.js +327 -0
- package/dist/runtime/api/lib/trpc/client.d.ts +1 -0
- package/dist/runtime/api/lib/trpc/client.js +5 -0
- package/dist/runtime/api/lib/trpc/server.d.ts +915 -0
- package/dist/runtime/api/lib/trpc/server.js +11 -0
- package/dist/runtime/api/lib/trpc/ws-server.d.ts +8 -0
- package/dist/runtime/api/lib/trpc/ws-server.js +33 -0
- package/dist/runtime/api/persistence/database-service.d.ts +14 -0
- package/dist/runtime/api/persistence/database-service.js +74 -0
- package/dist/runtime/api/persistence/execution-log-persistence.d.ts +90 -0
- package/dist/runtime/api/persistence/execution-log-persistence.js +410 -0
- package/dist/runtime/api/persistence/execution-log-persistence.test.d.ts +1 -0
- package/dist/runtime/api/persistence/execution-log-persistence.test.js +170 -0
- package/dist/runtime/api/router.d.ts +918 -0
- package/dist/runtime/api/router.js +34 -0
- package/dist/runtime/api/settings-service.d.ts +110 -0
- package/dist/runtime/api/settings-service.js +613 -0
- package/dist/runtime/api/tasks/file-watcher.d.ts +23 -0
- package/dist/runtime/api/tasks/file-watcher.js +88 -0
- package/dist/runtime/api/tasks/task-file-parser.d.ts +13 -0
- package/dist/runtime/api/tasks/task-file-parser.js +161 -0
- package/dist/runtime/api/tasks/task-service.d.ts +36 -0
- package/dist/runtime/api/tasks/task-service.js +173 -0
- package/dist/runtime/api/types/index.d.ts +179 -0
- package/dist/runtime/api/types/index.js +1 -0
- package/dist/runtime/api/types/settings.d.ts +81 -0
- package/dist/runtime/api/types/settings.js +2 -0
- package/dist/runtime/api/types.d.ts +2 -0
- package/dist/runtime/api/types.js +1 -0
- package/dist/runtime/api/utils/env.d.ts +6 -0
- package/dist/runtime/api/utils/env.js +12 -0
- package/dist/runtime/api/utils/stripNextEnv.d.ts +7 -0
- package/dist/runtime/api/utils/stripNextEnv.js +22 -0
- package/dist/runtime/api/utils/title-slug.d.ts +6 -0
- package/dist/runtime/api/utils/title-slug.js +77 -0
- package/dist/runtime/api/utils/url.d.ts +2 -0
- package/dist/runtime/api/utils/url.js +19 -0
- package/dist/runtime/api/vcs/git-history-service.d.ts +57 -0
- package/dist/runtime/api/vcs/git-history-service.js +228 -0
- package/dist/runtime/api/vcs/git-service.d.ts +127 -0
- package/dist/runtime/api/vcs/git-service.js +284 -0
- package/dist/runtime/api/vcs/worktree-service.d.ts +93 -0
- package/dist/runtime/api/vcs/worktree-service.js +506 -0
- package/dist/runtime/api/vcs/worktree-service.test.d.ts +1 -0
- package/dist/runtime/api/vcs/worktree-service.test.js +20 -0
- package/dist/runtime/api/workflows/quality-pipeline.d.ts +58 -0
- package/dist/runtime/api/workflows/quality-pipeline.js +400 -0
- package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +318 -0
- package/dist/runtime/api/workflows/vibing-orchestrator.js +1860 -0
- package/dist/runtime/web/.next/BUILD_ID +1 -0
- package/dist/runtime/web/.next/app-build-manifest.json +59 -0
- package/dist/runtime/web/.next/app-path-routes-manifest.json +7 -0
- package/dist/runtime/web/.next/build-manifest.json +33 -0
- package/dist/runtime/web/.next/package.json +1 -0
- package/dist/runtime/web/.next/prerender-manifest.json +61 -0
- package/dist/runtime/web/.next/react-loadable-manifest.json +39 -0
- package/dist/runtime/web/.next/required-server-files.json +334 -0
- package/dist/runtime/web/.next/routes-manifest.json +62 -0
- package/dist/runtime/web/.next/server/app/_not-found/page.js +2 -0
- package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/app/_not-found.html +7 -0
- package/dist/runtime/web/.next/server/app/_not-found.meta +8 -0
- package/dist/runtime/web/.next/server/app/_not-found.rsc +22 -0
- package/dist/runtime/web/.next/server/app/api/health/route.js +1 -0
- package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -0
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -0
- package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/app/index.html +7 -0
- package/dist/runtime/web/.next/server/app/index.meta +7 -0
- package/dist/runtime/web/.next/server/app/index.rsc +27 -0
- package/dist/runtime/web/.next/server/app/page.js +147 -0
- package/dist/runtime/web/.next/server/app/page.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/app-paths-manifest.json +7 -0
- package/dist/runtime/web/.next/server/chunks/217.js +1 -0
- package/dist/runtime/web/.next/server/chunks/383.js +6 -0
- package/dist/runtime/web/.next/server/chunks/458.js +1 -0
- package/dist/runtime/web/.next/server/chunks/576.js +18 -0
- package/dist/runtime/web/.next/server/chunks/635.js +22 -0
- package/dist/runtime/web/.next/server/chunks/761.js +1 -0
- package/dist/runtime/web/.next/server/chunks/777.js +3 -0
- package/dist/runtime/web/.next/server/chunks/825.js +1 -0
- package/dist/runtime/web/.next/server/chunks/838.js +1 -0
- package/dist/runtime/web/.next/server/chunks/973.js +15 -0
- package/dist/runtime/web/.next/server/functions-config-manifest.json +4 -0
- package/dist/runtime/web/.next/server/middleware-build-manifest.js +1 -0
- package/dist/runtime/web/.next/server/middleware-manifest.json +6 -0
- package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/dist/runtime/web/.next/server/next-font-manifest.js +1 -0
- package/dist/runtime/web/.next/server/next-font-manifest.json +1 -0
- package/dist/runtime/web/.next/server/pages/404.html +7 -0
- package/dist/runtime/web/.next/server/pages/500.html +1 -0
- package/dist/runtime/web/.next/server/pages/_app.js +1 -0
- package/dist/runtime/web/.next/server/pages/_app.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/pages/_document.js +1 -0
- package/dist/runtime/web/.next/server/pages/_document.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/pages/_error.js +19 -0
- package/dist/runtime/web/.next/server/pages/_error.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/pages-manifest.json +6 -0
- package/dist/runtime/web/.next/server/server-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -0
- package/dist/runtime/web/.next/server/webpack-runtime.js +1 -0
- package/dist/runtime/web/.next/static/chunks/18-15c10d3288afef2e.js +1 -0
- package/dist/runtime/web/.next/static/chunks/1c0ca389.537bbe362e3ffbd9.js +3 -0
- package/dist/runtime/web/.next/static/chunks/22747d63-ad5da0c19f4cfe41.js +71 -0
- package/dist/runtime/web/.next/static/chunks/277-0142a939f08738c3.js +63 -0
- package/dist/runtime/web/.next/static/chunks/355.056c2645878a799a.js +1 -0
- package/dist/runtime/web/.next/static/chunks/420.a5ccf151c9e2b2f1.js +1 -0
- package/dist/runtime/web/.next/static/chunks/439.1be0c6242fd248d5.js +15 -0
- package/dist/runtime/web/.next/static/chunks/440.c52e7c0f797e22b2.js +1 -0
- package/dist/runtime/web/.next/static/chunks/575-e2478287c27da87b.js +1 -0
- package/dist/runtime/web/.next/static/chunks/691.920d88c115087314.js +1 -0
- package/dist/runtime/web/.next/static/chunks/765-e838910065b50c3d.js +1 -0
- package/dist/runtime/web/.next/static/chunks/87c73c54-09e1ba5c70e60a51.js +1 -0
- package/dist/runtime/web/.next/static/chunks/891cff7f.0f71fc028f87e683.js +1 -0
- package/dist/runtime/web/.next/static/chunks/8bb4d8db-3e2aa02b0a2384b9.js +1 -0
- package/dist/runtime/web/.next/static/chunks/9af238c7-271a911d4e99ab18.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/_not-found/page-1cb74d1cba27d0ab.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-105a61ae865ba536.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-105a61ae865ba536.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-105a61ae865ba536.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/layout-8435322f09fd0975.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/page-8c3ba579efc6f918.js +1 -0
- package/dist/runtime/web/.next/static/chunks/cac567b0-5b77dd12911823cd.js +1 -0
- package/dist/runtime/web/.next/static/chunks/framework-2518f1345b5b2806.js +1 -0
- package/dist/runtime/web/.next/static/chunks/main-17665e5e39de9a8a.js +1 -0
- package/dist/runtime/web/.next/static/chunks/main-app-c0b0f5ba4f7f9d75.js +1 -0
- package/dist/runtime/web/.next/static/chunks/pages/_app-d6f6b3bbc3d81ee1.js +1 -0
- package/dist/runtime/web/.next/static/chunks/pages/_error-75a96cf1997cc3b9.js +1 -0
- package/dist/runtime/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/runtime/web/.next/static/chunks/webpack-c8de37305b4635cf.js +1 -0
- package/dist/runtime/web/.next/static/css/08c950681f1a9a92.css +1 -0
- package/dist/runtime/web/.next/static/css/2728291c68f99cb1.css +3 -0
- package/dist/runtime/web/.next/static/css/521bd69cc298cd1a.css +1 -0
- package/dist/runtime/web/.next/static/css/537e22821e101b87.css +1 -0
- package/dist/runtime/web/.next/static/mRpNgPfbYR_0wrODzlg_4/_buildManifest.js +1 -0
- package/dist/runtime/web/.next/static/mRpNgPfbYR_0wrODzlg_4/_ssgManifest.js +1 -0
- package/dist/runtime/web/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
- package/dist/runtime/web/package.json +65 -0
- package/dist/runtime/web/server.js +44 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +84 -7
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createServer } from 'http';
|
|
2
|
+
import { nodeHTTPRequestHandler } from '@trpc/server/adapters/node-http';
|
|
3
|
+
import { applyWSSHandler } from '@trpc/server/adapters/ws';
|
|
4
|
+
import { WebSocketServer } from 'ws';
|
|
5
|
+
import { appRouter } from './lib/trpc/server.js';
|
|
6
|
+
import { getTaskService } from './lib/server/task-service-singleton.js';
|
|
7
|
+
import { log } from './lib/logger.js';
|
|
8
|
+
import { pathToFileURL } from 'url';
|
|
9
|
+
function getNumberEnv(name, fallback) {
|
|
10
|
+
const raw = process.env[name];
|
|
11
|
+
const n = raw ? Number(raw) : NaN;
|
|
12
|
+
return Number.isFinite(n) ? n : fallback;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Starts a standalone HTTP server that serves tRPC over HTTP and WebSocket on the same port.
|
|
16
|
+
* HTTP endpoint: /trpc
|
|
17
|
+
* WebSocket path: /trpc
|
|
18
|
+
*/
|
|
19
|
+
export async function startApiServer({ port, host }) {
|
|
20
|
+
const httpServer = createServer((req, res) => {
|
|
21
|
+
// Basic CORS for local dev
|
|
22
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
23
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
|
|
24
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
|
|
25
|
+
if (req.method === 'OPTIONS') {
|
|
26
|
+
res.writeHead(200);
|
|
27
|
+
res.end();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (req.url?.startsWith('/trpc')) {
|
|
31
|
+
const host = req.headers.host || 'localhost';
|
|
32
|
+
const url = new URL(req.url, `http://${host}`);
|
|
33
|
+
// Strip the /trpc prefix and any leading slash; omit query string
|
|
34
|
+
const rawPath = url.pathname.replace(/^\/trpc\/?/, '');
|
|
35
|
+
const trpcPath = rawPath.replace(/^\/+/, '');
|
|
36
|
+
return nodeHTTPRequestHandler({
|
|
37
|
+
router: appRouter,
|
|
38
|
+
createContext: () => ({ taskService: getTaskService() }),
|
|
39
|
+
req: req,
|
|
40
|
+
res: res,
|
|
41
|
+
path: trpcPath,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (req.url === '/api/health') {
|
|
45
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
46
|
+
res.end(JSON.stringify({ status: 'ok', timestamp: new Date().toISOString() }));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
50
|
+
res.end(JSON.stringify({ error: 'Not Found' }));
|
|
51
|
+
});
|
|
52
|
+
// Share same HTTP server for WS, listening on /trpc
|
|
53
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
54
|
+
// Upgrade handler for WebSocket path filtering
|
|
55
|
+
httpServer.on('upgrade', (request, socket, head) => {
|
|
56
|
+
const { url } = request;
|
|
57
|
+
if (!url || !url.startsWith('/trpc')) {
|
|
58
|
+
socket.destroy();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
62
|
+
wss.emit('connection', ws, request);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
// Hook tRPC WS handler
|
|
66
|
+
applyWSSHandler({
|
|
67
|
+
wss,
|
|
68
|
+
router: appRouter,
|
|
69
|
+
createContext: () => ({ taskService: getTaskService() }),
|
|
70
|
+
});
|
|
71
|
+
await new Promise((resolve) => {
|
|
72
|
+
httpServer.listen(port, host, () => resolve());
|
|
73
|
+
});
|
|
74
|
+
const addr = httpServer.address();
|
|
75
|
+
const actualPort = typeof addr === 'object' && addr && 'port' in addr ? addr.port : port;
|
|
76
|
+
log.info('API server listening', { port: actualPort, host, path: '/trpc' }, 'api-server');
|
|
77
|
+
console.log(`📡 API ready at http://${host}:${actualPort}/trpc (HTTP & WS)`);
|
|
78
|
+
// Graceful shutdown
|
|
79
|
+
const shutdown = () => {
|
|
80
|
+
try {
|
|
81
|
+
wss.close();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
log.error('Failed to close WebSocket server', error, 'api-server');
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
httpServer.close();
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
log.error('Failed to close HTTP server', error, 'api-server');
|
|
91
|
+
}
|
|
92
|
+
process.exit(0);
|
|
93
|
+
};
|
|
94
|
+
process.on('SIGINT', shutdown);
|
|
95
|
+
process.on('SIGTERM', shutdown);
|
|
96
|
+
}
|
|
97
|
+
// Self-executing entry when run directly under ESM (e.g., tsx src/index.ts or node dist/index.js)
|
|
98
|
+
try {
|
|
99
|
+
const isMain = process.argv[1] ? pathToFileURL(process.argv[1]).href === import.meta.url : false;
|
|
100
|
+
if (isMain) {
|
|
101
|
+
const host = process.env.HOST || 'localhost';
|
|
102
|
+
const resolveApiPort = async () => {
|
|
103
|
+
const port = getNumberEnv('PORT', 3000);
|
|
104
|
+
return port + 1;
|
|
105
|
+
};
|
|
106
|
+
(async () => {
|
|
107
|
+
const port = await resolveApiPort();
|
|
108
|
+
startApiServer({ port, host }).catch((err) => {
|
|
109
|
+
console.error('Failed to start API server:', err);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
});
|
|
112
|
+
})();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// ignore
|
|
117
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified ID generation utility for consistent ID creation across the application
|
|
3
|
+
*/
|
|
4
|
+
export type IdPrefix = 'workflow' | 'exec' | 'improve' | 'merge' | 'review' | 'claude' | 'task' | 'session';
|
|
5
|
+
interface IdGeneratorOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Use UUID v4 for guaranteed uniqueness (default: true)
|
|
8
|
+
*/
|
|
9
|
+
useUuid?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Include timestamp for sortability (default: false)
|
|
12
|
+
*/
|
|
13
|
+
includeTimestamp?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Custom separator between parts (default: '_')
|
|
16
|
+
*/
|
|
17
|
+
separator?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generate a unique ID with consistent format
|
|
21
|
+
*
|
|
22
|
+
* @param prefix - The prefix to identify the ID type
|
|
23
|
+
* @param options - Configuration options for ID generation
|
|
24
|
+
* @returns A unique identifier string
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* generateId('workflow') // 'workflow_550e8400-e29b-41d4-a716-446655440000'
|
|
28
|
+
* generateId('exec', { includeTimestamp: true }) // 'exec_1703123456789_550e8400-e29b-41d4'
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateId(prefix: IdPrefix | string, options?: IdGeneratorOptions): string;
|
|
31
|
+
/**
|
|
32
|
+
* Generate a short, human-readable ID (less secure, for display purposes)
|
|
33
|
+
*
|
|
34
|
+
* @param prefix - The prefix to identify the ID type
|
|
35
|
+
* @returns A shorter identifier string
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* generateShortId('task') // 'task_a1b2c3d4'
|
|
39
|
+
*/
|
|
40
|
+
export declare function generateShortId(prefix: IdPrefix | string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Parse an ID to extract its components
|
|
43
|
+
*
|
|
44
|
+
* @param id - The ID to parse
|
|
45
|
+
* @param separator - The separator used in the ID
|
|
46
|
+
* @returns Parsed components of the ID
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseId(id: string, separator?: string): {
|
|
49
|
+
prefix: string;
|
|
50
|
+
timestamp?: number;
|
|
51
|
+
uuid?: string;
|
|
52
|
+
full: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Validate if a string is a valid UUID v4
|
|
56
|
+
*/
|
|
57
|
+
export declare function isValidUuid(id: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Extract the UUID portion from a prefixed ID
|
|
60
|
+
*/
|
|
61
|
+
export declare function extractUuid(id: string): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Create a batch of IDs efficiently
|
|
64
|
+
*/
|
|
65
|
+
export declare function generateBatchIds(prefix: IdPrefix | string, count: number, options?: IdGeneratorOptions): string[];
|
|
66
|
+
/**
|
|
67
|
+
* Generate a deterministic ID based on input (useful for idempotency)
|
|
68
|
+
*/
|
|
69
|
+
export declare function generateDeterministicId(prefix: IdPrefix | string, input: string): Promise<string>;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
/**
|
|
3
|
+
* Generate a unique ID with consistent format
|
|
4
|
+
*
|
|
5
|
+
* @param prefix - The prefix to identify the ID type
|
|
6
|
+
* @param options - Configuration options for ID generation
|
|
7
|
+
* @returns A unique identifier string
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* generateId('workflow') // 'workflow_550e8400-e29b-41d4-a716-446655440000'
|
|
11
|
+
* generateId('exec', { includeTimestamp: true }) // 'exec_1703123456789_550e8400-e29b-41d4'
|
|
12
|
+
*/
|
|
13
|
+
export function generateId(prefix, options = {}) {
|
|
14
|
+
const { useUuid = true, includeTimestamp = false, separator = '_' } = options;
|
|
15
|
+
const parts = [prefix];
|
|
16
|
+
if (includeTimestamp) {
|
|
17
|
+
// Use high-resolution timestamp for better uniqueness
|
|
18
|
+
parts.push(Date.now().toString());
|
|
19
|
+
}
|
|
20
|
+
if (useUuid) {
|
|
21
|
+
parts.push(uuidv4());
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// Fallback to crypto.randomUUID if available (browser/Node 19+)
|
|
25
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
26
|
+
parts.push(crypto.randomUUID());
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Legacy fallback with better entropy
|
|
30
|
+
const timestamp = Date.now();
|
|
31
|
+
const random1 = Math.random().toString(36).substring(2, 11);
|
|
32
|
+
const random2 = Math.random().toString(36).substring(2, 7);
|
|
33
|
+
const counter = getCounter();
|
|
34
|
+
parts.push(`${timestamp.toString(36)}${random1}${random2}${counter.toString(36)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return parts.join(separator);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Generate a short, human-readable ID (less secure, for display purposes)
|
|
41
|
+
*
|
|
42
|
+
* @param prefix - The prefix to identify the ID type
|
|
43
|
+
* @returns A shorter identifier string
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* generateShortId('task') // 'task_a1b2c3d4'
|
|
47
|
+
*/
|
|
48
|
+
export function generateShortId(prefix) {
|
|
49
|
+
const timestamp = Date.now().toString(36).slice(-4);
|
|
50
|
+
const random = Math.random().toString(36).substring(2, 6);
|
|
51
|
+
return `${prefix}_${timestamp}${random}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse an ID to extract its components
|
|
55
|
+
*
|
|
56
|
+
* @param id - The ID to parse
|
|
57
|
+
* @param separator - The separator used in the ID
|
|
58
|
+
* @returns Parsed components of the ID
|
|
59
|
+
*/
|
|
60
|
+
export function parseId(id, separator = '_') {
|
|
61
|
+
const parts = id.split(separator);
|
|
62
|
+
const result = {
|
|
63
|
+
prefix: parts[0] || '',
|
|
64
|
+
full: id,
|
|
65
|
+
};
|
|
66
|
+
// Check if second part is a timestamp (13+ digit number for millisecond timestamps)
|
|
67
|
+
// This avoids treating short numbers like "123" as timestamps
|
|
68
|
+
if (parts[1] && /^\d{13,}$/.test(parts[1])) {
|
|
69
|
+
result.timestamp = parseInt(parts[1], 10);
|
|
70
|
+
// When timestamp is present, UUID is the third part onward
|
|
71
|
+
result.uuid = parts.slice(2).join(separator);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// No timestamp, so UUID is everything after the prefix
|
|
75
|
+
result.uuid = parts.slice(1).join(separator);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate if a string is a valid UUID v4
|
|
81
|
+
*/
|
|
82
|
+
export function isValidUuid(id) {
|
|
83
|
+
const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
84
|
+
return uuidV4Regex.test(id);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Extract the UUID portion from a prefixed ID
|
|
88
|
+
*/
|
|
89
|
+
export function extractUuid(id) {
|
|
90
|
+
const parts = id.split('_');
|
|
91
|
+
const lastPart = parts[parts.length - 1];
|
|
92
|
+
return isValidUuid(lastPart) ? lastPart : null;
|
|
93
|
+
}
|
|
94
|
+
// Counter for additional entropy in fallback ID generation
|
|
95
|
+
let counter = 0;
|
|
96
|
+
function getCounter() {
|
|
97
|
+
counter = (counter + 1) % 10000;
|
|
98
|
+
return counter;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create a batch of IDs efficiently
|
|
102
|
+
*/
|
|
103
|
+
export function generateBatchIds(prefix, count, options) {
|
|
104
|
+
return Array.from({ length: count }, () => generateId(prefix, options));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Generate a deterministic ID based on input (useful for idempotency)
|
|
108
|
+
*/
|
|
109
|
+
export async function generateDeterministicId(prefix, input) {
|
|
110
|
+
// Use Web Crypto API if available
|
|
111
|
+
if (typeof crypto !== 'undefined' && crypto.subtle) {
|
|
112
|
+
const encoder = new TextEncoder();
|
|
113
|
+
const data = encoder.encode(input);
|
|
114
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
115
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
116
|
+
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
117
|
+
return `${prefix}_${hashHex.substring(0, 32)}`;
|
|
118
|
+
}
|
|
119
|
+
// Fallback for Node.js
|
|
120
|
+
const cryptoModule = await import('crypto');
|
|
121
|
+
const hash = cryptoModule.createHash('sha256').update(input).digest('hex');
|
|
122
|
+
return `${prefix}_${hash.substring(0, 32)}`;
|
|
123
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
export interface ImagePasteDropOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Function that handles the image upload process
|
|
5
|
+
*/
|
|
6
|
+
upload?: (file: File, onProgress?: (event: {
|
|
7
|
+
progress: number;
|
|
8
|
+
}) => void, abortSignal?: AbortSignal) => Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Callback for upload errors
|
|
11
|
+
*/
|
|
12
|
+
onError?: (error: Error) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Callback for successful uploads
|
|
15
|
+
*/
|
|
16
|
+
onSuccess?: (url: string) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum file size in bytes
|
|
19
|
+
*/
|
|
20
|
+
maxSize?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Allowed MIME types
|
|
23
|
+
*/
|
|
24
|
+
allowedTypes?: string[];
|
|
25
|
+
}
|
|
26
|
+
export declare const ImagePasteDrop: Extension<ImagePasteDropOptions, any>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import { Plugin, PluginKey } from '@tiptap/pm/state';
|
|
3
|
+
import { handleImageUpload } from './tiptap-utils.js';
|
|
4
|
+
export const ImagePasteDrop = Extension.create({
|
|
5
|
+
name: 'imagePasteDrop',
|
|
6
|
+
addOptions() {
|
|
7
|
+
return {
|
|
8
|
+
upload: handleImageUpload,
|
|
9
|
+
onError: undefined,
|
|
10
|
+
onSuccess: undefined,
|
|
11
|
+
maxSize: 5 * 1024 * 1024, // 5MB
|
|
12
|
+
allowedTypes: [
|
|
13
|
+
'image/jpeg',
|
|
14
|
+
'image/jpg',
|
|
15
|
+
'image/png',
|
|
16
|
+
'image/gif',
|
|
17
|
+
'image/webp',
|
|
18
|
+
'image/svg+xml',
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
addProseMirrorPlugins() {
|
|
23
|
+
const uploadAndInsertImages = (files, view, position) => {
|
|
24
|
+
files.forEach(async (file) => {
|
|
25
|
+
try {
|
|
26
|
+
// Validate file size
|
|
27
|
+
if (this.options.maxSize && file.size > this.options.maxSize) {
|
|
28
|
+
const error = new Error(`File size exceeds maximum allowed (${this.options.maxSize / (1024 * 1024)}MB)`);
|
|
29
|
+
this.options.onError?.(error);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Insert placeholder first
|
|
33
|
+
const placeholderTransaction = view.state.tr.insert(position, view.state.schema.text('🔄 Uploading...'));
|
|
34
|
+
view.dispatch(placeholderTransaction);
|
|
35
|
+
// Upload the image
|
|
36
|
+
const uploadFunction = this.options.upload || handleImageUpload;
|
|
37
|
+
const imageUrl = await uploadFunction(file);
|
|
38
|
+
// Replace placeholder with actual image
|
|
39
|
+
const filename = file.name.replace(/\.[^/.]+$/, '') || 'image';
|
|
40
|
+
const imageNode = view.state.schema.nodes.image.create({
|
|
41
|
+
src: imageUrl,
|
|
42
|
+
alt: filename,
|
|
43
|
+
title: filename,
|
|
44
|
+
});
|
|
45
|
+
// Find the placeholder text and replace it
|
|
46
|
+
const currentState = view.state;
|
|
47
|
+
let found = false;
|
|
48
|
+
currentState.doc.descendants((node, pos) => {
|
|
49
|
+
if (!found && node.isText && node.text === '🔄 Uploading...') {
|
|
50
|
+
const replaceTransaction = currentState.tr.replaceRangeWith(pos, pos + node.nodeSize, imageNode);
|
|
51
|
+
view.dispatch(replaceTransaction);
|
|
52
|
+
found = true;
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
});
|
|
57
|
+
this.options.onSuccess?.(imageUrl);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// Remove placeholder on error
|
|
61
|
+
const currentState = view.state;
|
|
62
|
+
currentState.doc.descendants((node, pos) => {
|
|
63
|
+
if (node.isText && node.text === '🔄 Uploading...') {
|
|
64
|
+
const deleteTransaction = currentState.tr.delete(pos, pos + node.nodeSize);
|
|
65
|
+
view.dispatch(deleteTransaction);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
});
|
|
70
|
+
const errorMessage = error instanceof Error ? error : new Error('Upload failed');
|
|
71
|
+
this.options.onError?.(errorMessage);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
return [
|
|
76
|
+
new Plugin({
|
|
77
|
+
key: new PluginKey('imagePasteDrop'),
|
|
78
|
+
props: {
|
|
79
|
+
handleDOMEvents: {
|
|
80
|
+
// Handle drag and drop
|
|
81
|
+
drop: (view, event) => {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
const { files } = event.dataTransfer;
|
|
84
|
+
const imageFiles = Array.from(files).filter((file) => this.options.allowedTypes?.includes(file.type));
|
|
85
|
+
if (imageFiles.length === 0) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
// Get drop position
|
|
89
|
+
const pos = view.posAtCoords({
|
|
90
|
+
left: event.clientX,
|
|
91
|
+
top: event.clientY,
|
|
92
|
+
});
|
|
93
|
+
if (!pos)
|
|
94
|
+
return false;
|
|
95
|
+
uploadAndInsertImages(imageFiles, view, pos.pos);
|
|
96
|
+
return true;
|
|
97
|
+
},
|
|
98
|
+
// Handle paste
|
|
99
|
+
paste: (view, event) => {
|
|
100
|
+
const { files } = event.clipboardData;
|
|
101
|
+
const imageFiles = Array.from(files).filter((file) => this.options.allowedTypes?.includes(file.type));
|
|
102
|
+
if (imageFiles.length === 0) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
// Insert at current cursor position
|
|
107
|
+
const { selection } = view.state;
|
|
108
|
+
uploadAndInsertImages(imageFiles, view, selection.from);
|
|
109
|
+
return true;
|
|
110
|
+
},
|
|
111
|
+
// Prevent default drag behaviors
|
|
112
|
+
dragover: (view, event) => {
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
return false;
|
|
115
|
+
},
|
|
116
|
+
dragenter: (view, event) => {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
return false;
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
];
|
|
124
|
+
},
|
|
125
|
+
});
|