genbox 1.0.234 → 1.0.236
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/dashboard/hooks/index.js +14 -0
- package/dist/commands/dashboard/hooks/useGenboxActions.js +100 -0
- package/dist/commands/dashboard/hooks/useLiveOutput.js +137 -0
- package/dist/commands/dashboard/hooks/useSessionActions.js +224 -0
- package/dist/commands/dashboard/hooks/useSessionPoller.js +290 -0
- package/dist/commands/dashboard/hooks/useSessionPollerContext.js +332 -0
- package/dist/commands/dashboard/store/context.js +244 -0
- package/dist/commands/dashboard/store/dashboard.js +110 -0
- package/dist/commands/dashboard-blessed-messages-old.d.ts +8 -0
- package/dist/commands/dashboard-blessed-messages-old.d.ts.map +1 -0
- package/dist/commands/dashboard-blessed-messages-old.js +827 -0
- package/dist/commands/dashboard-blessed-messages-old.js.map +1 -0
- package/dist/commands/dashboard-blessed-messages.d.ts.map +1 -1
- package/dist/commands/dashboard-blessed-messages.js +16 -3
- package/dist/commands/dashboard-blessed-messages.js.map +1 -1
- package/dist/commands/session/send.d.ts.map +1 -1
- package/dist/commands/session/send.js +31 -79
- package/dist/commands/session/send.js.map +1 -1
- package/dist/index.js +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dashboard Hooks Barrel Export
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.useGenboxActions = exports.useSessionActions = exports.useLiveOutput = exports.useSessionPoller = void 0;
|
|
7
|
+
var useSessionPoller_1 = require("./useSessionPoller");
|
|
8
|
+
Object.defineProperty(exports, "useSessionPoller", { enumerable: true, get: function () { return useSessionPoller_1.useSessionPoller; } });
|
|
9
|
+
var useLiveOutput_1 = require("./useLiveOutput");
|
|
10
|
+
Object.defineProperty(exports, "useLiveOutput", { enumerable: true, get: function () { return useLiveOutput_1.useLiveOutput; } });
|
|
11
|
+
var useSessionActions_1 = require("./useSessionActions");
|
|
12
|
+
Object.defineProperty(exports, "useSessionActions", { enumerable: true, get: function () { return useSessionActions_1.useSessionActions; } });
|
|
13
|
+
var useGenboxActions_1 = require("./useGenboxActions");
|
|
14
|
+
Object.defineProperty(exports, "useGenboxActions", { enumerable: true, get: function () { return useGenboxActions_1.useGenboxActions; } });
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Genbox Actions Hook
|
|
4
|
+
* Handles create, destroy, extend operations
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.useGenboxActions = useGenboxActions;
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const util_1 = require("util");
|
|
11
|
+
const dashboard_1 = require("../store/dashboard");
|
|
12
|
+
const api_1 = require("../../../api");
|
|
13
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
14
|
+
function useGenboxActions() {
|
|
15
|
+
const { setModal } = (0, dashboard_1.useDashboardStore)();
|
|
16
|
+
/**
|
|
17
|
+
* Create a new genbox
|
|
18
|
+
* Opens the creation wizard
|
|
19
|
+
*/
|
|
20
|
+
const createGenbox = (0, react_1.useCallback)(async (options) => {
|
|
21
|
+
try {
|
|
22
|
+
// This will exit the dashboard and run the wizard
|
|
23
|
+
// Return false to indicate dashboard should exit
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
/**
|
|
31
|
+
* Destroy a genbox
|
|
32
|
+
*/
|
|
33
|
+
const destroyGenbox = (0, react_1.useCallback)(async (genbox) => {
|
|
34
|
+
try {
|
|
35
|
+
await (0, api_1.fetchApi)(`/genboxes/${genbox.id}`, {
|
|
36
|
+
method: 'DELETE',
|
|
37
|
+
});
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}, []);
|
|
44
|
+
/**
|
|
45
|
+
* Extend genbox time
|
|
46
|
+
*/
|
|
47
|
+
const extendGenbox = (0, react_1.useCallback)(async (genbox, hours = 1) => {
|
|
48
|
+
try {
|
|
49
|
+
await (0, api_1.fetchApi)(`/genboxes/${genbox.id}/extend`, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
body: JSON.stringify({ hours }),
|
|
52
|
+
});
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}, []);
|
|
59
|
+
/**
|
|
60
|
+
* Connect to genbox via SSH
|
|
61
|
+
*/
|
|
62
|
+
const connectSSH = (0, react_1.useCallback)((genbox) => {
|
|
63
|
+
return {
|
|
64
|
+
connect: () => {
|
|
65
|
+
try {
|
|
66
|
+
(0, child_process_1.execSync)(`ssh genbox-${genbox.name}`, { stdio: 'inherit' });
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Normal exit
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
74
|
+
/**
|
|
75
|
+
* Show destroy confirmation modal
|
|
76
|
+
*/
|
|
77
|
+
const confirmDestroyGenbox = (0, react_1.useCallback)((genbox, onConfirm) => {
|
|
78
|
+
setModal({
|
|
79
|
+
type: 'confirm',
|
|
80
|
+
title: 'Destroy Genbox',
|
|
81
|
+
message: `Are you sure you want to destroy "${genbox.name}"?\nThis will terminate all running sessions.`,
|
|
82
|
+
confirmLabel: 'Destroy',
|
|
83
|
+
cancelLabel: 'Cancel',
|
|
84
|
+
onConfirm: () => {
|
|
85
|
+
destroyGenbox(genbox).then(() => {
|
|
86
|
+
onConfirm();
|
|
87
|
+
});
|
|
88
|
+
setModal(null);
|
|
89
|
+
},
|
|
90
|
+
onCancel: () => setModal(null),
|
|
91
|
+
});
|
|
92
|
+
}, [setModal, destroyGenbox]);
|
|
93
|
+
return {
|
|
94
|
+
createGenbox,
|
|
95
|
+
destroyGenbox,
|
|
96
|
+
extendGenbox,
|
|
97
|
+
connectSSH,
|
|
98
|
+
confirmDestroyGenbox,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live Output Hook
|
|
4
|
+
* Polls live terminal output for selected session
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.useLiveOutput = useLiveOutput;
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const util_1 = require("util");
|
|
11
|
+
const dashboard_1 = require("../store/dashboard");
|
|
12
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
13
|
+
/**
|
|
14
|
+
* Query terminal output from genbox via dtach typescript file
|
|
15
|
+
*/
|
|
16
|
+
async function queryGenboxTerminalOutput(genboxName, sessionName, lines = 100) {
|
|
17
|
+
try {
|
|
18
|
+
const { stdout } = await execAsync(`ssh -o ConnectTimeout=2 -o BatchMode=yes genbox-${genboxName} '
|
|
19
|
+
TYPESCRIPT_FILE="/home/dev/.genbox/typescript-${sessionName}"
|
|
20
|
+
if [ -f "$TYPESCRIPT_FILE" ]; then
|
|
21
|
+
tail -n ${lines} "$TYPESCRIPT_FILE" 2>/dev/null || cat "$TYPESCRIPT_FILE" 2>/dev/null
|
|
22
|
+
else
|
|
23
|
+
echo "No output captured yet"
|
|
24
|
+
fi
|
|
25
|
+
' 2>/dev/null`, { encoding: 'utf8', timeout: 5000 });
|
|
26
|
+
return cleanTerminalOutput(stdout);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Query daemon events for activity log
|
|
34
|
+
*/
|
|
35
|
+
async function queryGenboxDaemonEvents(genboxName, sessionName, count = 20) {
|
|
36
|
+
try {
|
|
37
|
+
const { stdout } = await execAsync(`ssh -o ConnectTimeout=2 -o BatchMode=yes genbox-${genboxName} '
|
|
38
|
+
curl -s "http://127.0.0.1:47191/api/sessions/${sessionName}/events?limit=${count}" 2>/dev/null ||
|
|
39
|
+
curl -s "http://127.0.0.1:47192/api/sessions/${sessionName}/events?limit=${count}" 2>/dev/null ||
|
|
40
|
+
echo "[]"
|
|
41
|
+
' 2>/dev/null`, { encoding: 'utf8', timeout: 5000 });
|
|
42
|
+
const events = JSON.parse(stdout.trim() || '[]');
|
|
43
|
+
if (!Array.isArray(events))
|
|
44
|
+
return [];
|
|
45
|
+
return events.map((e) => ({
|
|
46
|
+
timestamp: new Date(e.timestamp || e.createdAt || Date.now()),
|
|
47
|
+
type: mapEventType(e.type || e.eventType),
|
|
48
|
+
content: e.content || e.message || e.data?.toolName || 'Event',
|
|
49
|
+
session: sessionName,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function mapEventType(type) {
|
|
57
|
+
if (type?.includes('tool'))
|
|
58
|
+
return 'tool';
|
|
59
|
+
if (type?.includes('error'))
|
|
60
|
+
return 'error';
|
|
61
|
+
if (type?.includes('state'))
|
|
62
|
+
return 'state';
|
|
63
|
+
return 'message';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Clean terminal output from dtach recording
|
|
67
|
+
*/
|
|
68
|
+
function cleanTerminalOutput(output) {
|
|
69
|
+
let cleaned = output
|
|
70
|
+
.replace(/Script started on.*\n?/g, '')
|
|
71
|
+
.replace(/Script done on.*\n?/g, '');
|
|
72
|
+
// Fix broken ANSI codes
|
|
73
|
+
cleaned = cleaned.replace(/(?<!\x1B)\[(\d+(?:;\d+)*)?m/g, '\x1B[$1m');
|
|
74
|
+
// Find last complete screen frame
|
|
75
|
+
const clearPatterns = ['\x1B[H\x1B[J', '\x1B[2J'];
|
|
76
|
+
let lastClearIndex = -1;
|
|
77
|
+
for (const pattern of clearPatterns) {
|
|
78
|
+
const idx = cleaned.lastIndexOf(pattern);
|
|
79
|
+
if (idx > lastClearIndex)
|
|
80
|
+
lastClearIndex = idx + pattern.length;
|
|
81
|
+
}
|
|
82
|
+
if (lastClearIndex > 0) {
|
|
83
|
+
cleaned = cleaned.substring(lastClearIndex);
|
|
84
|
+
}
|
|
85
|
+
// Remove control sequences but preserve basic text
|
|
86
|
+
cleaned = cleaned
|
|
87
|
+
.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '')
|
|
88
|
+
.replace(/\x1B\][^\x07]*\x07/g, '')
|
|
89
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
|
|
90
|
+
return cleaned;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Hook for polling live terminal output
|
|
94
|
+
*/
|
|
95
|
+
function useLiveOutput(intervalMs = 2000) {
|
|
96
|
+
const pollerRef = (0, react_1.useRef)(null);
|
|
97
|
+
const { setLiveOutput, addActivityEvent, getSelectedSession, viewMode } = (0, dashboard_1.useDashboardStore)();
|
|
98
|
+
const pollOutput = (0, react_1.useCallback)(async () => {
|
|
99
|
+
const session = getSelectedSession();
|
|
100
|
+
if (!session || !session.genbox || session.type !== 'cloud') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Only poll if in split or live view
|
|
104
|
+
const currentViewMode = dashboard_1.useDashboardStore.getState().viewMode;
|
|
105
|
+
if (currentViewMode !== 'split' && currentViewMode !== 'live') {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const [output, events] = await Promise.all([
|
|
110
|
+
queryGenboxTerminalOutput(session.genbox, session.name, 100),
|
|
111
|
+
queryGenboxDaemonEvents(session.genbox, session.name, 10),
|
|
112
|
+
]);
|
|
113
|
+
if (output) {
|
|
114
|
+
setLiveOutput(output);
|
|
115
|
+
}
|
|
116
|
+
// Add new events to activity log (avoid duplicates)
|
|
117
|
+
for (const event of events) {
|
|
118
|
+
addActivityEvent(event);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Ignore errors
|
|
123
|
+
}
|
|
124
|
+
}, [getSelectedSession, setLiveOutput, addActivityEvent]);
|
|
125
|
+
(0, react_1.useEffect)(() => {
|
|
126
|
+
// Initial poll
|
|
127
|
+
pollOutput();
|
|
128
|
+
// Set up polling
|
|
129
|
+
pollerRef.current = setInterval(pollOutput, intervalMs);
|
|
130
|
+
return () => {
|
|
131
|
+
if (pollerRef.current) {
|
|
132
|
+
clearInterval(pollerRef.current);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}, [pollOutput, intervalMs]);
|
|
136
|
+
return { pollOutput };
|
|
137
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Actions Hook
|
|
4
|
+
* Handles attach, send, kill operations
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.useSessionActions = useSessionActions;
|
|
41
|
+
const react_1 = require("react");
|
|
42
|
+
const child_process_1 = require("child_process");
|
|
43
|
+
const util_1 = require("util");
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const os = __importStar(require("os"));
|
|
46
|
+
const dashboard_1 = require("../store/dashboard");
|
|
47
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
48
|
+
function getDtachSocketDir() {
|
|
49
|
+
return path.join(os.homedir(), '.genbox', 'sockets');
|
|
50
|
+
}
|
|
51
|
+
function getRemoteDtachSocketDir() {
|
|
52
|
+
return '/home/dev/.genbox/sockets';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get SSH key path
|
|
56
|
+
*/
|
|
57
|
+
function getPrivateSshKeyPath() {
|
|
58
|
+
const keyPath = path.join(os.homedir(), '.genbox', 'id_ed25519');
|
|
59
|
+
return keyPath;
|
|
60
|
+
}
|
|
61
|
+
function useSessionActions() {
|
|
62
|
+
const { setAttachedSession, setModal, getSelectedSession } = (0, dashboard_1.useDashboardStore)();
|
|
63
|
+
/**
|
|
64
|
+
* Attach to a session
|
|
65
|
+
* Returns a function to spawn the attach process
|
|
66
|
+
*/
|
|
67
|
+
const attachToSession = (0, react_1.useCallback)(async (session) => {
|
|
68
|
+
const socketPath = session.type === 'cloud'
|
|
69
|
+
? `${getRemoteDtachSocketDir()}/${session.name}.sock`
|
|
70
|
+
: path.join(getDtachSocketDir(), `${session.name}.sock`);
|
|
71
|
+
if (session.type === 'cloud' && session.genbox) {
|
|
72
|
+
// Return function to spawn SSH + dtach
|
|
73
|
+
return {
|
|
74
|
+
attach: () => {
|
|
75
|
+
const keyPath = getPrivateSshKeyPath();
|
|
76
|
+
const sshArgs = [
|
|
77
|
+
'-t',
|
|
78
|
+
'-i',
|
|
79
|
+
keyPath,
|
|
80
|
+
'-o',
|
|
81
|
+
'StrictHostKeyChecking=no',
|
|
82
|
+
'-o',
|
|
83
|
+
'UserKnownHostsFile=/dev/null',
|
|
84
|
+
'-o',
|
|
85
|
+
'LogLevel=ERROR',
|
|
86
|
+
`genbox-${session.genbox}`,
|
|
87
|
+
`dtach -a ${socketPath}`,
|
|
88
|
+
];
|
|
89
|
+
// Use execSync to run in foreground
|
|
90
|
+
try {
|
|
91
|
+
(0, child_process_1.execSync)(`ssh ${sshArgs.join(' ')}`, { stdio: 'inherit' });
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Detach is normal exit
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
else if (session.type === 'native') {
|
|
100
|
+
return {
|
|
101
|
+
attach: () => {
|
|
102
|
+
try {
|
|
103
|
+
(0, child_process_1.execSync)(`dtach -a ${socketPath}`, { stdio: 'inherit' });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Detach is normal exit
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
else if (session.type === 'docker') {
|
|
112
|
+
const containerName = session.genbox || session.name;
|
|
113
|
+
return {
|
|
114
|
+
attach: () => {
|
|
115
|
+
try {
|
|
116
|
+
(0, child_process_1.execSync)(`docker exec -it ${containerName} dtach -a ${socketPath}`, {
|
|
117
|
+
stdio: 'inherit',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Detach is normal exit
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
else if (session.type === 'multipass') {
|
|
127
|
+
const vmName = session.genbox || session.name;
|
|
128
|
+
return {
|
|
129
|
+
attach: () => {
|
|
130
|
+
try {
|
|
131
|
+
(0, child_process_1.execSync)(`multipass exec ${vmName} -- dtach -a ${socketPath}`, {
|
|
132
|
+
stdio: 'inherit',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Detach is normal exit
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return { attach: () => { } };
|
|
142
|
+
}, []);
|
|
143
|
+
/**
|
|
144
|
+
* Send a prompt to a session via daemon API
|
|
145
|
+
*/
|
|
146
|
+
const sendPrompt = (0, react_1.useCallback)(async (session, prompt) => {
|
|
147
|
+
if (!session.genbox || session.type !== 'cloud') {
|
|
148
|
+
// For local sessions, we'd need a different approach
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
// Try daemon API first
|
|
153
|
+
const escapedPrompt = prompt.replace(/'/g, "'\\''");
|
|
154
|
+
await execAsync(`ssh -o ConnectTimeout=5 -o BatchMode=yes genbox-${session.genbox} '
|
|
155
|
+
curl -s -X POST "http://127.0.0.1:47191/api/sessions/${session.name}/send" \
|
|
156
|
+
-H "Content-Type: application/json" \
|
|
157
|
+
-d '"'"'{"prompt": "${escapedPrompt}"}'"'"' 2>/dev/null ||
|
|
158
|
+
curl -s -X POST "http://127.0.0.1:47192/api/sessions/${session.name}/send" \
|
|
159
|
+
-H "Content-Type: application/json" \
|
|
160
|
+
-d '"'"'{"prompt": "${escapedPrompt}"}'"'"' 2>/dev/null
|
|
161
|
+
'`, { encoding: 'utf8', timeout: 10000 });
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}, []);
|
|
168
|
+
/**
|
|
169
|
+
* Kill a session
|
|
170
|
+
*/
|
|
171
|
+
const killSession = (0, react_1.useCallback)(async (session) => {
|
|
172
|
+
try {
|
|
173
|
+
if (session.type === 'cloud' && session.genbox) {
|
|
174
|
+
// Kill via daemon API or remove socket
|
|
175
|
+
await execAsync(`ssh -o ConnectTimeout=5 -o BatchMode=yes genbox-${session.genbox} '
|
|
176
|
+
curl -s -X POST "http://127.0.0.1:47191/api/sessions/${session.name}/stop" 2>/dev/null ||
|
|
177
|
+
curl -s -X POST "http://127.0.0.1:47192/api/sessions/${session.name}/stop" 2>/dev/null ||
|
|
178
|
+
rm -f /home/dev/.genbox/sockets/${session.name}.sock
|
|
179
|
+
'`, { encoding: 'utf8', timeout: 10000 });
|
|
180
|
+
}
|
|
181
|
+
else if (session.type === 'native') {
|
|
182
|
+
const socketPath = path.join(getDtachSocketDir(), `${session.name}.sock`);
|
|
183
|
+
await execAsync(`rm -f "${socketPath}"`, { encoding: 'utf8' });
|
|
184
|
+
}
|
|
185
|
+
else if (session.type === 'docker') {
|
|
186
|
+
const containerName = session.genbox || session.name;
|
|
187
|
+
await execAsync(`docker exec ${containerName} rm -f /home/dev/.genbox/sockets/${session.name}.sock`, { encoding: 'utf8' });
|
|
188
|
+
}
|
|
189
|
+
else if (session.type === 'multipass') {
|
|
190
|
+
const vmName = session.genbox || session.name;
|
|
191
|
+
await execAsync(`multipass exec ${vmName} -- rm -f /home/dev/.genbox/sockets/${session.name}.sock`, { encoding: 'utf8' });
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}, []);
|
|
199
|
+
/**
|
|
200
|
+
* Show kill confirmation modal
|
|
201
|
+
*/
|
|
202
|
+
const confirmKillSession = (0, react_1.useCallback)((session, onConfirm) => {
|
|
203
|
+
setModal({
|
|
204
|
+
type: 'confirm',
|
|
205
|
+
title: 'Kill Session',
|
|
206
|
+
message: `Are you sure you want to kill "${session.name}"?`,
|
|
207
|
+
confirmLabel: 'Kill',
|
|
208
|
+
cancelLabel: 'Cancel',
|
|
209
|
+
onConfirm: () => {
|
|
210
|
+
killSession(session).then(() => {
|
|
211
|
+
onConfirm();
|
|
212
|
+
});
|
|
213
|
+
setModal(null);
|
|
214
|
+
},
|
|
215
|
+
onCancel: () => setModal(null),
|
|
216
|
+
});
|
|
217
|
+
}, [setModal, killSession]);
|
|
218
|
+
return {
|
|
219
|
+
attachToSession,
|
|
220
|
+
sendPrompt,
|
|
221
|
+
killSession,
|
|
222
|
+
confirmKillSession,
|
|
223
|
+
};
|
|
224
|
+
}
|