@abdasis/agentdeck 2.44.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 +122 -0
- package/agent/dist/config.d.ts +31 -0
- package/agent/dist/config.d.ts.map +1 -0
- package/agent/dist/config.js +81 -0
- package/agent/dist/config.js.map +1 -0
- package/agent/dist/db/commands.d.ts +25 -0
- package/agent/dist/db/commands.d.ts.map +1 -0
- package/agent/dist/db/commands.js +93 -0
- package/agent/dist/db/commands.js.map +1 -0
- package/agent/dist/db/index.d.ts +30 -0
- package/agent/dist/db/index.d.ts.map +1 -0
- package/agent/dist/db/index.js +368 -0
- package/agent/dist/db/index.js.map +1 -0
- package/agent/dist/db/schema.d.ts +74 -0
- package/agent/dist/db/schema.d.ts.map +1 -0
- package/agent/dist/db/schema.js +100 -0
- package/agent/dist/db/schema.js.map +1 -0
- package/agent/dist/db/sessions.d.ts +46 -0
- package/agent/dist/db/sessions.d.ts.map +1 -0
- package/agent/dist/db/sessions.js +198 -0
- package/agent/dist/db/sessions.js.map +1 -0
- package/agent/dist/index.d.ts +2 -0
- package/agent/dist/index.d.ts.map +1 -0
- package/agent/dist/index.js +19 -0
- package/agent/dist/index.js.map +1 -0
- package/agent/dist/lib/command-session.d.ts +20 -0
- package/agent/dist/lib/command-session.d.ts.map +1 -0
- package/agent/dist/lib/command-session.js +64 -0
- package/agent/dist/lib/command-session.js.map +1 -0
- package/agent/dist/lib/git.d.ts +8 -0
- package/agent/dist/lib/git.d.ts.map +1 -0
- package/agent/dist/lib/git.js +194 -0
- package/agent/dist/lib/git.js.map +1 -0
- package/agent/dist/lib/init-script.d.ts +33 -0
- package/agent/dist/lib/init-script.d.ts.map +1 -0
- package/agent/dist/lib/init-script.js +523 -0
- package/agent/dist/lib/init-script.js.map +1 -0
- package/agent/dist/lib/transcript.d.ts +16 -0
- package/agent/dist/lib/transcript.d.ts.map +1 -0
- package/agent/dist/lib/transcript.js +64 -0
- package/agent/dist/lib/transcript.js.map +1 -0
- package/agent/dist/logger.d.ts +12 -0
- package/agent/dist/logger.d.ts.map +1 -0
- package/agent/dist/logger.js +39 -0
- package/agent/dist/logger.js.map +1 -0
- package/agent/dist/middleware/auth.d.ts +15 -0
- package/agent/dist/middleware/auth.d.ts.map +1 -0
- package/agent/dist/middleware/auth.js +55 -0
- package/agent/dist/middleware/auth.js.map +1 -0
- package/agent/dist/routes/commands.d.ts +8 -0
- package/agent/dist/routes/commands.d.ts.map +1 -0
- package/agent/dist/routes/commands.js +229 -0
- package/agent/dist/routes/commands.js.map +1 -0
- package/agent/dist/routes/files.d.ts +7 -0
- package/agent/dist/routes/files.d.ts.map +1 -0
- package/agent/dist/routes/files.js +206 -0
- package/agent/dist/routes/files.js.map +1 -0
- package/agent/dist/routes/hooks.d.ts +7 -0
- package/agent/dist/routes/hooks.d.ts.map +1 -0
- package/agent/dist/routes/hooks.js +147 -0
- package/agent/dist/routes/hooks.js.map +1 -0
- package/agent/dist/routes/index.d.ts +10 -0
- package/agent/dist/routes/index.d.ts.map +1 -0
- package/agent/dist/routes/index.js +10 -0
- package/agent/dist/routes/index.js.map +1 -0
- package/agent/dist/routes/pair.d.ts +26 -0
- package/agent/dist/routes/pair.d.ts.map +1 -0
- package/agent/dist/routes/pair.js +384 -0
- package/agent/dist/routes/pair.js.map +1 -0
- package/agent/dist/routes/projects.d.ts +6 -0
- package/agent/dist/routes/projects.d.ts.map +1 -0
- package/agent/dist/routes/projects.js +121 -0
- package/agent/dist/routes/projects.js.map +1 -0
- package/agent/dist/routes/sessions-git.d.ts +7 -0
- package/agent/dist/routes/sessions-git.d.ts.map +1 -0
- package/agent/dist/routes/sessions-git.js +31 -0
- package/agent/dist/routes/sessions-git.js.map +1 -0
- package/agent/dist/routes/sessions-tmux.d.ts +7 -0
- package/agent/dist/routes/sessions-tmux.d.ts.map +1 -0
- package/agent/dist/routes/sessions-tmux.js +94 -0
- package/agent/dist/routes/sessions-tmux.js.map +1 -0
- package/agent/dist/routes/sessions.d.ts +7 -0
- package/agent/dist/routes/sessions.d.ts.map +1 -0
- package/agent/dist/routes/sessions.js +172 -0
- package/agent/dist/routes/sessions.js.map +1 -0
- package/agent/dist/server.d.ts +6 -0
- package/agent/dist/server.d.ts.map +1 -0
- package/agent/dist/server.js +95 -0
- package/agent/dist/server.js.map +1 -0
- package/agent/dist/terminal.d.ts +19 -0
- package/agent/dist/terminal.d.ts.map +1 -0
- package/agent/dist/terminal.js +175 -0
- package/agent/dist/terminal.js.map +1 -0
- package/agent/dist/updater.d.ts +18 -0
- package/agent/dist/updater.d.ts.map +1 -0
- package/agent/dist/updater.js +122 -0
- package/agent/dist/updater.js.map +1 -0
- package/agent/dist/version.d.ts +20 -0
- package/agent/dist/version.d.ts.map +1 -0
- package/agent/dist/version.js +66 -0
- package/agent/dist/version.js.map +1 -0
- package/agent/dist/websocket-handlers.d.ts +38 -0
- package/agent/dist/websocket-handlers.d.ts.map +1 -0
- package/agent/dist/websocket-handlers.js +389 -0
- package/agent/dist/websocket-handlers.js.map +1 -0
- package/agent/node_modules/@agentdeck/shared/dist/index.d.ts +2 -0
- package/agent/node_modules/@agentdeck/shared/dist/index.d.ts.map +1 -0
- package/agent/node_modules/@agentdeck/shared/dist/index.js +2 -0
- package/agent/node_modules/@agentdeck/shared/dist/index.js.map +1 -0
- package/agent/node_modules/@agentdeck/shared/dist/types/index.d.ts +235 -0
- package/agent/node_modules/@agentdeck/shared/dist/types/index.d.ts.map +1 -0
- package/agent/node_modules/@agentdeck/shared/dist/types/index.js +5 -0
- package/agent/node_modules/@agentdeck/shared/dist/types/index.js.map +1 -0
- package/agent/node_modules/@agentdeck/shared/package.json +29 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +304 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/hooks.d.ts +3 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +187 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +109 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/logs.d.ts +3 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +38 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/profile.d.ts +3 -0
- package/dist/commands/profile.d.ts.map +1 -0
- package/dist/commands/profile.js +156 -0
- package/dist/commands/profile.js.map +1 -0
- package/dist/commands/service.d.ts +3 -0
- package/dist/commands/service.d.ts.map +1 -0
- package/dist/commands/service.js +235 -0
- package/dist/commands/service.js.map +1 -0
- package/dist/commands/start.d.ts +3 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +122 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +3 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +23 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +129 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/version.d.ts +3 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +39 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +68 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +156 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/hooks.d.ts +67 -0
- package/dist/lib/hooks.d.ts.map +1 -0
- package/dist/lib/hooks.js +353 -0
- package/dist/lib/hooks.js.map +1 -0
- package/dist/lib/paths.d.ts +36 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +77 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/prerequisites.d.ts +60 -0
- package/dist/lib/prerequisites.d.ts.map +1 -0
- package/dist/lib/prerequisites.js +266 -0
- package/dist/lib/prerequisites.js.map +1 -0
- package/dist/lib/process.d.ts +40 -0
- package/dist/lib/process.d.ts.map +1 -0
- package/dist/lib/process.js +192 -0
- package/dist/lib/process.js.map +1 -0
- package/dist/service/index.d.ts +44 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +18 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/launchd.d.ts +18 -0
- package/dist/service/launchd.d.ts.map +1 -0
- package/dist/service/launchd.js +208 -0
- package/dist/service/launchd.js.map +1 -0
- package/dist/service/systemd.d.ts +18 -0
- package/dist/service/systemd.d.ts.map +1 -0
- package/dist/service/systemd.js +196 -0
- package/dist/service/systemd.js.map +1 -0
- package/hooks/notify-agentdeck.sh +53 -0
- package/package.json +83 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-related API routes: listing, folder scanning, and git clone.
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from 'express';
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import { config } from '../config.js';
|
|
7
|
+
import { authMiddleware } from '../middleware/auth.js';
|
|
8
|
+
export function createProjectRoutes() {
|
|
9
|
+
const router = Router();
|
|
10
|
+
// List whitelisted projects
|
|
11
|
+
router.get('/projects', authMiddleware, (_req, res) => {
|
|
12
|
+
res.json(config.projects.whitelist);
|
|
13
|
+
});
|
|
14
|
+
// Dynamic folder listing - scans basePath for directories
|
|
15
|
+
router.get('/folders', authMiddleware, async (_req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const fs = await import('fs/promises');
|
|
18
|
+
const basePath = config.projects.basePath.replace('~', process.env.HOME);
|
|
19
|
+
const entries = await fs.readdir(basePath, { withFileTypes: true });
|
|
20
|
+
const folders = entries
|
|
21
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
|
|
22
|
+
.map((entry) => entry.name)
|
|
23
|
+
.sort();
|
|
24
|
+
res.json(folders);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
console.error('Failed to list folders:', err);
|
|
28
|
+
res.status(500).json({ error: 'Failed to list folders' });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
// Clone a git repository
|
|
32
|
+
router.post('/clone', authMiddleware, async (req, res) => {
|
|
33
|
+
const { url } = req.body;
|
|
34
|
+
if (!url) {
|
|
35
|
+
return res.status(400).json({ success: false, error: 'URL is required' });
|
|
36
|
+
}
|
|
37
|
+
// Validate URL format (https:// or git@)
|
|
38
|
+
const httpsPattern = /^https:\/\/.+\/.+/;
|
|
39
|
+
const sshPattern = /^git@.+:.+/;
|
|
40
|
+
if (!httpsPattern.test(url) && !sshPattern.test(url)) {
|
|
41
|
+
return res.status(400).json({ success: false, error: 'Invalid URL format' });
|
|
42
|
+
}
|
|
43
|
+
// Extract repo name from URL
|
|
44
|
+
let repoName;
|
|
45
|
+
try {
|
|
46
|
+
if (url.startsWith('git@')) {
|
|
47
|
+
// git@github.com:user/repo.git -> repo
|
|
48
|
+
const match = url.match(/:([^/]+\/)?(.+?)(\.git)?$/);
|
|
49
|
+
repoName = match?.[2] || '';
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// https://github.com/user/repo.git -> repo
|
|
53
|
+
const urlObj = new URL(url);
|
|
54
|
+
const pathParts = urlObj.pathname.split('/').filter(Boolean);
|
|
55
|
+
repoName = pathParts[pathParts.length - 1]?.replace(/\.git$/, '') || '';
|
|
56
|
+
}
|
|
57
|
+
if (!repoName) {
|
|
58
|
+
return res
|
|
59
|
+
.status(400)
|
|
60
|
+
.json({ success: false, error: 'Could not extract repo name from URL' });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (_err) {
|
|
64
|
+
return res.status(400).json({ success: false, error: 'Invalid URL format' });
|
|
65
|
+
}
|
|
66
|
+
const fs = await import('fs/promises');
|
|
67
|
+
const path = await import('path');
|
|
68
|
+
const basePath = config.projects.basePath.replace('~', process.env.HOME);
|
|
69
|
+
const targetPath = path.join(basePath, repoName);
|
|
70
|
+
// Check if folder already exists
|
|
71
|
+
try {
|
|
72
|
+
await fs.access(targetPath);
|
|
73
|
+
return res.status(400).json({
|
|
74
|
+
success: false,
|
|
75
|
+
error: `Folder "${repoName}" already exists`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (_err) {
|
|
79
|
+
// Folder doesn't exist, good to proceed
|
|
80
|
+
}
|
|
81
|
+
// Clone the repository
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
const gitProcess = spawn('git', ['clone', url, repoName], {
|
|
84
|
+
cwd: basePath,
|
|
85
|
+
env: process.env,
|
|
86
|
+
});
|
|
87
|
+
let stderr = '';
|
|
88
|
+
gitProcess.stderr.on('data', (data) => {
|
|
89
|
+
stderr += data.toString();
|
|
90
|
+
});
|
|
91
|
+
gitProcess.on('close', (code) => {
|
|
92
|
+
if (code === 0) {
|
|
93
|
+
console.log(`Successfully cloned ${url} to ${targetPath}`);
|
|
94
|
+
res.json({
|
|
95
|
+
success: true,
|
|
96
|
+
project: repoName,
|
|
97
|
+
path: targetPath,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.error(`Git clone failed: ${stderr}`);
|
|
102
|
+
res.status(500).json({
|
|
103
|
+
success: false,
|
|
104
|
+
error: stderr.trim() || 'Git clone failed',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
resolve();
|
|
108
|
+
});
|
|
109
|
+
gitProcess.on('error', (err) => {
|
|
110
|
+
console.error('Failed to spawn git:', err);
|
|
111
|
+
res.status(500).json({
|
|
112
|
+
success: false,
|
|
113
|
+
error: 'Failed to execute git command',
|
|
114
|
+
});
|
|
115
|
+
resolve();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
return router;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions-git.d.ts","sourceRoot":"","sources":["../../src/routes/sessions-git.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAIjC,wBAAgB,sBAAsB,IAAI,MAAM,CAsB/C"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git history route for a session's project.
|
|
3
|
+
* Dipasang sebagai sub-router dari sessions.ts di /api/sessions.
|
|
4
|
+
*/
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { Router } from 'express';
|
|
7
|
+
import { config } from '../config.js';
|
|
8
|
+
import { readGitHistory } from '../lib/git.js';
|
|
9
|
+
export function createSessionGitRoutes() {
|
|
10
|
+
const router = Router();
|
|
11
|
+
// Git history for a session's project (polled by web right-sidebar)
|
|
12
|
+
router.get('/:sessionName/git', async (req, res) => {
|
|
13
|
+
const { sessionName } = req.params;
|
|
14
|
+
if (!/^[\w-]+$/.test(sessionName)) {
|
|
15
|
+
return res.status(400).json({ error: 'Invalid session name' });
|
|
16
|
+
}
|
|
17
|
+
const project = sessionName.split('--')[0];
|
|
18
|
+
const basePath = config.projects.basePath.replace('~', process.env.HOME);
|
|
19
|
+
const cwd = project === '' ? basePath : path.join(basePath, project);
|
|
20
|
+
try {
|
|
21
|
+
const data = await readGitHistory(cwd);
|
|
22
|
+
res.json(data);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Non-repo / missing path is a normal state, not a server error
|
|
26
|
+
res.json({ repo: false, commits: [], changes: [], fetchedAt: Date.now() });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return router;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=sessions-git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions-git.js","sourceRoot":"","sources":["../../src/routes/sessions-git.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,UAAU,sBAAsB;IACpC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,oEAAoE;IACpE,MAAM,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACjD,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;QACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAClC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,IAAK,CAAC,CAAC;QAC1E,MAAM,GAAG,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;YACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;YAChE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tmux-direct session routes: terminal scrollback capture and input injection.
|
|
3
|
+
* Dipasang sebagai sub-router dari sessions.ts di /api/sessions.
|
|
4
|
+
*/
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
export declare function createSessionTmuxRoutes(): Router;
|
|
7
|
+
//# sourceMappingURL=sessions-tmux.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions-tmux.d.ts","sourceRoot":"","sources":["../../src/routes/sessions-tmux.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,wBAAgB,uBAAuB,IAAI,MAAM,CAyGhD"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tmux-direct session routes: terminal scrollback capture and input injection.
|
|
3
|
+
* Dipasang sebagai sub-router dari sessions.ts di /api/sessions.
|
|
4
|
+
*/
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
import * as sessionsDb from '../db/sessions.js';
|
|
7
|
+
export function createSessionTmuxRoutes() {
|
|
8
|
+
const router = Router();
|
|
9
|
+
// Get session output (terminal scrollback)
|
|
10
|
+
router.get('/:sessionName/output', async (req, res) => {
|
|
11
|
+
const { sessionName } = req.params;
|
|
12
|
+
const lines = parseInt(req.query.lines) || 1000;
|
|
13
|
+
const format = req.query.format || 'plain';
|
|
14
|
+
const { exec } = await import('child_process');
|
|
15
|
+
const { promisify } = await import('util');
|
|
16
|
+
const execAsync = promisify(exec);
|
|
17
|
+
if (!/^[\w-]+$/.test(sessionName)) {
|
|
18
|
+
return res.status(400).json({ error: 'Invalid session name' });
|
|
19
|
+
}
|
|
20
|
+
// Limit lines to prevent memory issues
|
|
21
|
+
const maxLines = Math.min(lines, 50000);
|
|
22
|
+
try {
|
|
23
|
+
const { stdout } = await execAsync(`tmux capture-pane -t "${sessionName}" -p -S -${maxLines} -J 2>/dev/null`);
|
|
24
|
+
let output = stdout;
|
|
25
|
+
// Strip ANSI codes if plain format requested
|
|
26
|
+
if (format === 'plain') {
|
|
27
|
+
// eslint-disable-next-line no-control-regex
|
|
28
|
+
output = output.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
|
|
29
|
+
}
|
|
30
|
+
const outputLines = output.split('\n');
|
|
31
|
+
// Check if session is still running
|
|
32
|
+
let isRunning = true;
|
|
33
|
+
try {
|
|
34
|
+
await execAsync(`tmux has-session -t "${sessionName}" 2>/dev/null`);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
isRunning = false;
|
|
38
|
+
}
|
|
39
|
+
res.json({
|
|
40
|
+
sessionName,
|
|
41
|
+
output,
|
|
42
|
+
totalLines: outputLines.length,
|
|
43
|
+
returnedLines: outputLines.length,
|
|
44
|
+
isRunning,
|
|
45
|
+
capturedAt: Date.now(),
|
|
46
|
+
source: 'live',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
res.status(404).json({ error: 'Session not found' });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
// Send input to a session
|
|
54
|
+
router.post('/:sessionName/input', async (req, res) => {
|
|
55
|
+
const { sessionName } = req.params;
|
|
56
|
+
const { text, sendEnter = true } = req.body;
|
|
57
|
+
const { exec } = await import('child_process');
|
|
58
|
+
const { promisify } = await import('util');
|
|
59
|
+
const execAsync = promisify(exec);
|
|
60
|
+
if (!/^[\w-]+$/.test(sessionName)) {
|
|
61
|
+
return res.status(400).json({ success: false, error: 'Invalid session name' });
|
|
62
|
+
}
|
|
63
|
+
if (!text || typeof text !== 'string') {
|
|
64
|
+
return res.status(400).json({ success: false, error: 'Text is required' });
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
// Check if session exists
|
|
68
|
+
await execAsync(`tmux has-session -t "${sessionName}" 2>/dev/null`);
|
|
69
|
+
// Escape special characters for tmux send-keys
|
|
70
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/;/g, '\\;');
|
|
71
|
+
// Send the text
|
|
72
|
+
if (sendEnter) {
|
|
73
|
+
await execAsync(`tmux send-keys -t "${sessionName}" "${escapedText}" Enter`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
await execAsync(`tmux send-keys -t "${sessionName}" "${escapedText}"`);
|
|
77
|
+
}
|
|
78
|
+
// Update last activity
|
|
79
|
+
sessionsDb.upsertSession(sessionName, {
|
|
80
|
+
lastEvent: 'Input sent',
|
|
81
|
+
});
|
|
82
|
+
res.json({
|
|
83
|
+
success: true,
|
|
84
|
+
sessionName,
|
|
85
|
+
bytesSent: text.length,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
res.status(404).json({ success: false, error: 'Session not found' });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return router;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=sessions-tmux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions-tmux.js","sourceRoot":"","sources":["../../src/routes/sessions-tmux.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session API routes: list, preview, kill, archive tmux sessions.
|
|
3
|
+
* Simplified version without spawn, worktree, push, or PR features.
|
|
4
|
+
*/
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
export declare function createSessionRoutes(): Router;
|
|
7
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAYjC,wBAAgB,mBAAmB,IAAI,MAAM,CAgN5C"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session API routes: list, preview, kill, archive tmux sessions.
|
|
3
|
+
* Simplified version without spawn, worktree, push, or PR features.
|
|
4
|
+
*/
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
import * as sessionsDb from '../db/sessions.js';
|
|
7
|
+
import { broadcastSessionRemoved, broadcastSessionArchived, broadcastStatusUpdate, } from '../websocket-handlers.js';
|
|
8
|
+
import { createSessionTmuxRoutes } from './sessions-tmux.js';
|
|
9
|
+
import { createSessionGitRoutes } from './sessions-git.js';
|
|
10
|
+
import { authMiddleware } from '../middleware/auth.js';
|
|
11
|
+
export function createSessionRoutes() {
|
|
12
|
+
const router = Router();
|
|
13
|
+
router.use(authMiddleware);
|
|
14
|
+
// Sub-routers: /:sessionName/output, /:sessionName/input, /:sessionName/git.
|
|
15
|
+
// Path relatif tetap sama karena router ini sendiri dipasang di /api/sessions.
|
|
16
|
+
router.use('/', createSessionTmuxRoutes());
|
|
17
|
+
router.use('/', createSessionGitRoutes());
|
|
18
|
+
// Enhanced sessions endpoint with detailed info
|
|
19
|
+
router.get('/', async (_req, res) => {
|
|
20
|
+
const { exec } = await import('child_process');
|
|
21
|
+
const { promisify } = await import('util');
|
|
22
|
+
const execAsync = promisify(exec);
|
|
23
|
+
try {
|
|
24
|
+
const { stdout } = await execAsync('tmux list-sessions -F "#{session_name}|#{session_created}" 2>/dev/null');
|
|
25
|
+
const sessions = [];
|
|
26
|
+
for (const line of stdout.trim().split('\n').filter(Boolean)) {
|
|
27
|
+
const [name, created] = line.split('|');
|
|
28
|
+
const [project] = name.split('--');
|
|
29
|
+
// Get DB data if available
|
|
30
|
+
const dbSession = sessionsDb.getSession(name);
|
|
31
|
+
sessions.push(sessionsDb.dbSessionToInfo(name, project, parseInt(created) * 1000, dbSession));
|
|
32
|
+
}
|
|
33
|
+
res.json(sessions);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
res.json([]);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
// Get archived sessions
|
|
40
|
+
router.get('/archived', (_req, res) => {
|
|
41
|
+
const archivedSessions = sessionsDb.getArchivedSessions();
|
|
42
|
+
const sessions = archivedSessions.map((session) => ({
|
|
43
|
+
name: session.name,
|
|
44
|
+
project: session.project,
|
|
45
|
+
createdAt: session.created_at,
|
|
46
|
+
lastEvent: session.last_event ?? undefined,
|
|
47
|
+
archivedAt: session.archived_at ?? undefined,
|
|
48
|
+
}));
|
|
49
|
+
res.json(sessions);
|
|
50
|
+
});
|
|
51
|
+
// Get single session info by name
|
|
52
|
+
router.get('/:sessionName/status', (req, res) => {
|
|
53
|
+
const { sessionName } = req.params;
|
|
54
|
+
if (!/^[\w-]+$/.test(sessionName)) {
|
|
55
|
+
return res.status(400).json({ error: 'Invalid session name' });
|
|
56
|
+
}
|
|
57
|
+
const dbSession = sessionsDb.getSession(sessionName);
|
|
58
|
+
if (!dbSession) {
|
|
59
|
+
return res.status(404).json({ error: 'Session not found' });
|
|
60
|
+
}
|
|
61
|
+
res.json(sessionsDb.dbSessionToInfo(sessionName, dbSession.project, dbSession.created_at, dbSession));
|
|
62
|
+
});
|
|
63
|
+
// Acknowledge session - reset needs_attention status
|
|
64
|
+
router.post('/:sessionName/acknowledge', (req, res) => {
|
|
65
|
+
const { sessionName } = req.params;
|
|
66
|
+
if (!/^[\w-]+$/.test(sessionName)) {
|
|
67
|
+
return res.status(400).json({ error: 'Invalid session name' });
|
|
68
|
+
}
|
|
69
|
+
const session = sessionsDb.getSession(sessionName);
|
|
70
|
+
if (!session) {
|
|
71
|
+
return res.status(404).json({ error: 'Session not found' });
|
|
72
|
+
}
|
|
73
|
+
// Only reset if currently needs_attention
|
|
74
|
+
if (session.status === 'needs_attention') {
|
|
75
|
+
const updatedSession = sessionsDb.upsertSession(sessionName, {
|
|
76
|
+
status: 'working',
|
|
77
|
+
attentionReason: null,
|
|
78
|
+
});
|
|
79
|
+
// Broadcast status change to all WebSocket clients
|
|
80
|
+
broadcastStatusUpdate({
|
|
81
|
+
name: sessionName,
|
|
82
|
+
project: updatedSession.project,
|
|
83
|
+
status: 'working',
|
|
84
|
+
attentionReason: undefined,
|
|
85
|
+
statusSource: 'hook',
|
|
86
|
+
createdAt: updatedSession.created_at,
|
|
87
|
+
lastActivity: updatedSession.last_activity,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
res.json({ success: true });
|
|
91
|
+
});
|
|
92
|
+
// Get terminal preview (last N lines from tmux pane)
|
|
93
|
+
router.get('/:sessionName/preview', async (req, res) => {
|
|
94
|
+
const { sessionName } = req.params;
|
|
95
|
+
const { exec } = await import('child_process');
|
|
96
|
+
const { promisify } = await import('util');
|
|
97
|
+
const execAsync = promisify(exec);
|
|
98
|
+
if (!/^[\w-]+$/.test(sessionName)) {
|
|
99
|
+
return res.status(400).json({ error: 'Invalid session name' });
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const { stdout } = await execAsync(`tmux capture-pane -t "${sessionName}" -p -S -20 2>/dev/null`);
|
|
103
|
+
const allLines = stdout.split('\n');
|
|
104
|
+
const lines = allLines
|
|
105
|
+
.slice(-16, -1)
|
|
106
|
+
.filter((line) => line.trim() !== '' || allLines.indexOf(line) > allLines.length - 5);
|
|
107
|
+
res.json({
|
|
108
|
+
lines: lines.length > 0 ? lines : ['(empty terminal)'],
|
|
109
|
+
timestamp: Date.now(),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
res.status(404).json({ error: 'Session not found' });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// Kill a tmux session
|
|
117
|
+
router.delete('/:sessionName', async (req, res) => {
|
|
118
|
+
const { sessionName } = req.params;
|
|
119
|
+
const { exec } = await import('child_process');
|
|
120
|
+
const { promisify } = await import('util');
|
|
121
|
+
const execAsync = promisify(exec);
|
|
122
|
+
if (!/^[\w-]+$/.test(sessionName)) {
|
|
123
|
+
return res.status(400).json({ error: 'Invalid session name' });
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
await execAsync(`tmux kill-session -t "${sessionName}" 2>/dev/null`);
|
|
127
|
+
console.log(`Killed tmux session: ${sessionName}`);
|
|
128
|
+
sessionsDb.deleteSession(sessionName);
|
|
129
|
+
broadcastSessionRemoved(sessionName);
|
|
130
|
+
res.json({ success: true, message: `Session ${sessionName} killed` });
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
res.status(404).json({ error: 'Session not found or already killed' });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// Archive a session (mark as done and keep in history)
|
|
137
|
+
router.post('/:sessionName/archive', async (req, res) => {
|
|
138
|
+
const { sessionName } = req.params;
|
|
139
|
+
const { exec } = await import('child_process');
|
|
140
|
+
const { promisify } = await import('util');
|
|
141
|
+
const execAsync = promisify(exec);
|
|
142
|
+
if (!/^[\w-]+$/.test(sessionName)) {
|
|
143
|
+
return res.status(400).json({ error: 'Invalid session name' });
|
|
144
|
+
}
|
|
145
|
+
const archivedSession = sessionsDb.archiveSession(sessionName);
|
|
146
|
+
if (!archivedSession) {
|
|
147
|
+
return res.status(404).json({ error: 'Session not found' });
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
await execAsync(`tmux kill-session -t "${sessionName}" 2>/dev/null`);
|
|
151
|
+
console.log(`[Archive] Killed tmux session: ${sessionName}`);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
console.log(`[Archive] Tmux session ${sessionName} was already gone`);
|
|
155
|
+
}
|
|
156
|
+
const archivedInfo = {
|
|
157
|
+
name: sessionName,
|
|
158
|
+
project: archivedSession.project,
|
|
159
|
+
createdAt: archivedSession.created_at,
|
|
160
|
+
lastEvent: archivedSession.last_event ?? undefined,
|
|
161
|
+
archivedAt: archivedSession.archived_at ?? undefined,
|
|
162
|
+
};
|
|
163
|
+
broadcastSessionArchived(sessionName, archivedInfo);
|
|
164
|
+
res.json({
|
|
165
|
+
success: true,
|
|
166
|
+
message: `Session ${sessionName} archived`,
|
|
167
|
+
session: archivedInfo,
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
return router;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main server entry point - Express HTTP server with WebSocket support.
|
|
3
|
+
* Routes and handlers are split into separate modules for maintainability.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createServer(): Promise<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>>;
|
|
6
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyCH,wBAAsB,YAAY,gHA8EjC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main server entry point - Express HTTP server with WebSocket support.
|
|
3
|
+
* Routes and handlers are split into separate modules for maintainability.
|
|
4
|
+
*/
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import cors from 'cors';
|
|
7
|
+
import { WebSocketServer } from 'ws';
|
|
8
|
+
import { createServer as createHttpServer } from 'http';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import { initDatabase, closeDatabase } from './db/index.js';
|
|
11
|
+
import * as sessionsDb from './db/sessions.js';
|
|
12
|
+
// Routes
|
|
13
|
+
import { createProjectRoutes, createSessionRoutes, createPairRoutes, createHooksRoutes, createCommandRoutes, createFileRoutes, } from './routes/index.js';
|
|
14
|
+
// WebSocket
|
|
15
|
+
import { handleTerminalConnection, handleSessionsConnection } from './websocket-handlers.js';
|
|
16
|
+
import { authenticateToken } from './middleware/auth.js';
|
|
17
|
+
// Utility to get active tmux sessions
|
|
18
|
+
function getActiveTmuxSessions() {
|
|
19
|
+
try {
|
|
20
|
+
const output = execSync('tmux list-sessions -F "#{session_name}" 2>/dev/null', {
|
|
21
|
+
encoding: 'utf-8',
|
|
22
|
+
});
|
|
23
|
+
return new Set(output
|
|
24
|
+
.trim()
|
|
25
|
+
.split('\n')
|
|
26
|
+
.filter((s) => s));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return new Set();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function createServer() {
|
|
33
|
+
const app = express();
|
|
34
|
+
app.use(cors());
|
|
35
|
+
app.use(express.json());
|
|
36
|
+
const server = createHttpServer(app);
|
|
37
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
38
|
+
// Initialize SQLite database
|
|
39
|
+
initDatabase();
|
|
40
|
+
// Reconcile sessions with active tmux sessions
|
|
41
|
+
const activeTmuxSessions = getActiveTmuxSessions();
|
|
42
|
+
sessionsDb.reconcileWithTmux(activeTmuxSessions);
|
|
43
|
+
// Load existing sessions
|
|
44
|
+
const dbSessions = sessionsDb.getAllSessions();
|
|
45
|
+
console.log(`[DB] Loaded ${dbSessions.length} sessions from database`);
|
|
46
|
+
// Health check endpoint for container orchestration
|
|
47
|
+
app.get('/health', (_req, res) => {
|
|
48
|
+
res.json({ status: 'ok', timestamp: Date.now() });
|
|
49
|
+
});
|
|
50
|
+
// Mount API routes
|
|
51
|
+
app.use('/api', createProjectRoutes());
|
|
52
|
+
app.use('/api/sessions', createSessionRoutes());
|
|
53
|
+
app.use('/api', createCommandRoutes());
|
|
54
|
+
app.use('/api', createFileRoutes());
|
|
55
|
+
// Mount pairing routes (both at /pair and /api/pair for flexibility)
|
|
56
|
+
app.use('/pair', createPairRoutes());
|
|
57
|
+
app.use('/api/pair', createPairRoutes());
|
|
58
|
+
// Mount hooks routes for Claude Code hook notifications
|
|
59
|
+
app.use('/api/hooks', createHooksRoutes());
|
|
60
|
+
// Handle WebSocket upgrades
|
|
61
|
+
server.on('upgrade', async (req, socket, head) => {
|
|
62
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
63
|
+
// Auth per-machine: bila agent punya accessToken, wajib ?token= yang cocok.
|
|
64
|
+
// Install lama tanpa accessToken → authenticateToken true (opt-in).
|
|
65
|
+
if (!authenticateToken(url.searchParams.get('token') ?? undefined)) {
|
|
66
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
67
|
+
socket.destroy();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (url.pathname === '/terminal') {
|
|
71
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
72
|
+
handleTerminalConnection(ws, url);
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (url.pathname === '/sessions') {
|
|
77
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
78
|
+
handleSessionsConnection(ws, url);
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
socket.destroy();
|
|
83
|
+
});
|
|
84
|
+
// Graceful shutdown
|
|
85
|
+
const shutdown = () => {
|
|
86
|
+
console.log('[Server] Shutting down...');
|
|
87
|
+
closeDatabase();
|
|
88
|
+
server.close();
|
|
89
|
+
process.exit(0);
|
|
90
|
+
};
|
|
91
|
+
process.on('SIGTERM', shutdown);
|
|
92
|
+
process.on('SIGINT', shutdown);
|
|
93
|
+
return server;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface Terminal {
|
|
2
|
+
write(data: string): void;
|
|
3
|
+
resize(cols: number, rows: number): void;
|
|
4
|
+
onData(callback: (data: string) => void): void;
|
|
5
|
+
onExit(callback: (info: {
|
|
6
|
+
exitCode: number;
|
|
7
|
+
}) => void): void;
|
|
8
|
+
kill(): void;
|
|
9
|
+
detach(): void;
|
|
10
|
+
captureHistory(lines?: number): Promise<string>;
|
|
11
|
+
isExistingSession(): boolean;
|
|
12
|
+
onReady(callback: () => void): void;
|
|
13
|
+
}
|
|
14
|
+
export interface CreateTerminalOptions {
|
|
15
|
+
/** Custom environment variables to inject into the session */
|
|
16
|
+
customEnvVars?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function createTerminal(cwd: string, sessionName: string, options?: CreateTerminalOptions | Record<string, string>): Terminal;
|
|
19
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC/C,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC7D,IAAI,IAAI,IAAI,CAAC;IACb,MAAM,IAAI,IAAI,CAAC;IACf,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAC3D,QAAQ,CAiMV"}
|