@bytespell/shella 0.2.2 → 0.2.3
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/bin/shella-init.js +28 -93
- package/dist/bin/shella-init.js.map +1 -1
- package/dist/src/api.d.ts.map +1 -1
- package/dist/src/api.js +36 -2
- package/dist/src/api.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +60 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/logger.js +46 -1
- package/dist/src/logger.js.map +1 -1
- package/dist/src/plugin-manager.d.ts +22 -1
- package/dist/src/plugin-manager.d.ts.map +1 -1
- package/dist/src/plugin-manager.js +84 -32
- package/dist/src/plugin-manager.js.map +1 -1
- package/dist/src/port-allocator.js +1 -1
- package/dist/src/port-allocator.js.map +1 -1
- package/dist/src/registry.d.ts +15 -1
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +29 -0
- package/dist/src/registry.js.map +1 -1
- package/dist/src/types.d.ts +20 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/watcher.d.ts +4 -3
- package/dist/src/watcher.d.ts.map +1 -1
- package/dist/src/watcher.js +91 -74
- package/dist/src/watcher.js.map +1 -1
- package/package.json +6 -2
- package/bundled-plugins/agent/README.md +0 -3
- package/bundled-plugins/agent/components.json +0 -24
- package/bundled-plugins/agent/dist/assets/index-BGeDYr6P.css +0 -1
- package/bundled-plugins/agent/dist/assets/index-J14lI3Er.js +0 -49
- package/bundled-plugins/agent/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/bundled-plugins/agent/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/bundled-plugins/agent/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/bundled-plugins/agent/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/bundled-plugins/agent/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/bundled-plugins/agent/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/bundled-plugins/agent/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/bundled-plugins/agent/dist/index.html +0 -14
- package/bundled-plugins/agent/dist/vite.svg +0 -1
- package/bundled-plugins/agent/eslint.config.js +0 -23
- package/bundled-plugins/agent/index.html +0 -13
- package/bundled-plugins/agent/package-lock.json +0 -11034
- package/bundled-plugins/agent/package.json +0 -52
- package/bundled-plugins/agent/public/vite.svg +0 -1
- package/bundled-plugins/agent/server.js +0 -240
- package/bundled-plugins/agent/src/App.tsx +0 -586
- package/bundled-plugins/agent/src/assets/react.svg +0 -1
- package/bundled-plugins/agent/src/components/ui/alert-dialog.tsx +0 -182
- package/bundled-plugins/agent/src/components/ui/badge.tsx +0 -45
- package/bundled-plugins/agent/src/components/ui/button.tsx +0 -60
- package/bundled-plugins/agent/src/components/ui/card.tsx +0 -94
- package/bundled-plugins/agent/src/components/ui/combobox.tsx +0 -294
- package/bundled-plugins/agent/src/components/ui/dropdown-menu.tsx +0 -253
- package/bundled-plugins/agent/src/components/ui/field.tsx +0 -225
- package/bundled-plugins/agent/src/components/ui/input-group.tsx +0 -147
- package/bundled-plugins/agent/src/components/ui/input.tsx +0 -19
- package/bundled-plugins/agent/src/components/ui/label.tsx +0 -24
- package/bundled-plugins/agent/src/components/ui/select.tsx +0 -185
- package/bundled-plugins/agent/src/components/ui/separator.tsx +0 -26
- package/bundled-plugins/agent/src/components/ui/switch.tsx +0 -31
- package/bundled-plugins/agent/src/components/ui/textarea.tsx +0 -18
- package/bundled-plugins/agent/src/index.css +0 -131
- package/bundled-plugins/agent/src/lib/utils.ts +0 -6
- package/bundled-plugins/agent/src/main.tsx +0 -11
- package/bundled-plugins/agent/tsconfig.app.json +0 -32
- package/bundled-plugins/agent/tsconfig.json +0 -13
- package/bundled-plugins/agent/tsconfig.node.json +0 -26
- package/bundled-plugins/agent/vite.config.ts +0 -14
- package/bundled-plugins/terminal/index.html +0 -24
- package/bundled-plugins/terminal/package-lock.json +0 -3316
- package/bundled-plugins/terminal/package.json +0 -37
- package/bundled-plugins/terminal/server.ts +0 -169
- package/bundled-plugins/terminal/src/App.tsx +0 -168
- package/bundled-plugins/terminal/src/main.tsx +0 -9
- package/bundled-plugins/terminal/tsconfig.json +0 -22
- package/bundled-plugins/terminal/vite.config.ts +0 -10
- package/templates/express/package.json +0 -11
- package/templates/express/public/index.html +0 -64
- package/templates/express/server.js +0 -37
- package/templates/vanilla/package.json +0 -8
- package/templates/vanilla/public/index.html +0 -64
- package/templates/vanilla/server.js +0 -54
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@shella/terminal",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.2.2",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "server.ts",
|
|
7
|
-
"shella": {
|
|
8
|
-
"displayName": "Terminal",
|
|
9
|
-
"devCommand": "tsx server.ts"
|
|
10
|
-
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"dev": "tsx server.ts",
|
|
13
|
-
"build": "vite build",
|
|
14
|
-
"preview": "vite preview"
|
|
15
|
-
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@xterm/addon-fit": "^0.10.0",
|
|
18
|
-
"@xterm/addon-web-links": "^0.11.0",
|
|
19
|
-
"@xterm/xterm": "^5.5.0",
|
|
20
|
-
"express": "^5.0.0",
|
|
21
|
-
"node-pty": "^1.2.0-beta.7",
|
|
22
|
-
"react": "^19.2.0",
|
|
23
|
-
"react-dom": "^19.2.0",
|
|
24
|
-
"ws": "^8.18.0"
|
|
25
|
-
},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"@types/express": "^5.0.0",
|
|
28
|
-
"@types/node": "^22.0.0",
|
|
29
|
-
"@types/react": "^19.2.0",
|
|
30
|
-
"@types/react-dom": "^19.2.0",
|
|
31
|
-
"@types/ws": "^8.5.0",
|
|
32
|
-
"@vitejs/plugin-react": "^4.3.0",
|
|
33
|
-
"tsx": "^4.19.0",
|
|
34
|
-
"typescript": "^5.6.0",
|
|
35
|
-
"vite": "^6.0.0"
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import express from 'express'
|
|
2
|
-
import { createServer } from 'http'
|
|
3
|
-
import { WebSocketServer, WebSocket } from 'ws'
|
|
4
|
-
import * as pty from 'node-pty'
|
|
5
|
-
import path from 'path'
|
|
6
|
-
import fs from 'fs'
|
|
7
|
-
import { fileURLToPath } from 'url'
|
|
8
|
-
|
|
9
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
10
|
-
const app = express()
|
|
11
|
-
const PORT = process.env.PORT
|
|
12
|
-
|
|
13
|
-
// Check if we're in dev mode (no dist folder)
|
|
14
|
-
const isDev = !fs.existsSync(path.join(__dirname, 'dist', 'index.html'))
|
|
15
|
-
|
|
16
|
-
// Allow embedding in iframes (for shella-web)
|
|
17
|
-
app.use((_req, res, next) => {
|
|
18
|
-
res.removeHeader('X-Frame-Options')
|
|
19
|
-
res.setHeader('Content-Security-Policy', 'frame-ancestors *')
|
|
20
|
-
next()
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
app.use(express.json())
|
|
24
|
-
|
|
25
|
-
// Health check
|
|
26
|
-
app.get('/api/health', (_req, res) => {
|
|
27
|
-
res.json({ status: 'ok', shell: process.env.SHELL || '/bin/bash' })
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
async function startServer() {
|
|
31
|
-
if (isDev) {
|
|
32
|
-
// Dev mode: use Vite middleware for HMR
|
|
33
|
-
const { createServer: createViteServer } = await import('vite')
|
|
34
|
-
const vite = await createViteServer({
|
|
35
|
-
server: { middlewareMode: true, hmr: { port: Number(PORT) + 1000 } },
|
|
36
|
-
appType: 'spa',
|
|
37
|
-
})
|
|
38
|
-
app.use(vite.middlewares)
|
|
39
|
-
} else {
|
|
40
|
-
// Production: serve built files
|
|
41
|
-
app.use(express.static(path.join(__dirname, 'dist')))
|
|
42
|
-
app.get('*', (_req, res) => {
|
|
43
|
-
res.sendFile(path.join(__dirname, 'dist', 'index.html'))
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const server = createServer(app)
|
|
48
|
-
|
|
49
|
-
// PTY is spawned lazily on first client connection with correct size
|
|
50
|
-
const shell = process.env.SHELL || '/bin/bash'
|
|
51
|
-
const home = process.env.HOME || '/tmp'
|
|
52
|
-
|
|
53
|
-
let ptyProcess: pty.IPty | null = null
|
|
54
|
-
|
|
55
|
-
// Track connected clients
|
|
56
|
-
const clients = new Set<WebSocket>()
|
|
57
|
-
|
|
58
|
-
// Buffer recent output for new clients (scrollback)
|
|
59
|
-
const outputBuffer: string[] = []
|
|
60
|
-
const MAX_BUFFER_LINES = 1000
|
|
61
|
-
|
|
62
|
-
function spawnPty(cols: number, rows: number) {
|
|
63
|
-
if (ptyProcess) return // Already spawned
|
|
64
|
-
|
|
65
|
-
ptyProcess = pty.spawn(shell, [], {
|
|
66
|
-
name: 'xterm-256color',
|
|
67
|
-
cols,
|
|
68
|
-
rows,
|
|
69
|
-
cwd: home,
|
|
70
|
-
env: {
|
|
71
|
-
...process.env as Record<string, string>,
|
|
72
|
-
// Suppress zsh's partial line marker (%) on fresh terminal
|
|
73
|
-
PROMPT_EOL_MARK: '',
|
|
74
|
-
},
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
console.log(`[terminal] PTY spawned: pid=${ptyProcess.pid}, shell=${shell}, size=${cols}x${rows}`)
|
|
78
|
-
|
|
79
|
-
// PTY -> all connected clients
|
|
80
|
-
ptyProcess.onData((data: string) => {
|
|
81
|
-
// Buffer output for new clients
|
|
82
|
-
outputBuffer.push(data)
|
|
83
|
-
if (outputBuffer.length > MAX_BUFFER_LINES) {
|
|
84
|
-
outputBuffer.shift()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Broadcast to all connected clients
|
|
88
|
-
const message = JSON.stringify({ type: 'output', data })
|
|
89
|
-
for (const client of clients) {
|
|
90
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
91
|
-
client.send(message)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
ptyProcess.onExit(({ exitCode, signal }) => {
|
|
97
|
-
console.log(`[terminal] PTY exited: code=${exitCode}, signal=${signal}`)
|
|
98
|
-
const message = JSON.stringify({ type: 'exit', exitCode, signal })
|
|
99
|
-
for (const client of clients) {
|
|
100
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
101
|
-
client.send(message)
|
|
102
|
-
client.close()
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// PTY died, shut down the instance
|
|
106
|
-
process.exit(exitCode ?? 0)
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// WebSocket server for client connections
|
|
111
|
-
const wss = new WebSocketServer({ server, path: '/ws' })
|
|
112
|
-
|
|
113
|
-
wss.on('connection', (ws: WebSocket) => {
|
|
114
|
-
clients.add(ws)
|
|
115
|
-
|
|
116
|
-
// Send buffered output to new client so they see history (if PTY already running)
|
|
117
|
-
if (ptyProcess && outputBuffer.length > 0) {
|
|
118
|
-
ws.send(JSON.stringify({ type: 'output', data: outputBuffer.join('') }))
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Client -> PTY
|
|
122
|
-
ws.on('message', (message: Buffer) => {
|
|
123
|
-
try {
|
|
124
|
-
const msg = JSON.parse(message.toString())
|
|
125
|
-
|
|
126
|
-
switch (msg.type) {
|
|
127
|
-
case 'input':
|
|
128
|
-
ptyProcess?.write(msg.data)
|
|
129
|
-
break
|
|
130
|
-
case 'resize':
|
|
131
|
-
if (msg.cols && msg.rows) {
|
|
132
|
-
if (!ptyProcess) {
|
|
133
|
-
// First resize spawns the PTY with correct size
|
|
134
|
-
spawnPty(msg.cols, msg.rows)
|
|
135
|
-
} else {
|
|
136
|
-
ptyProcess.resize(msg.cols, msg.rows)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
break
|
|
140
|
-
}
|
|
141
|
-
} catch (err) {
|
|
142
|
-
console.error('[terminal] Invalid message:', err)
|
|
143
|
-
}
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
ws.on('close', () => {
|
|
147
|
-
clients.delete(ws)
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
ws.on('error', (err) => {
|
|
151
|
-
console.error('[terminal] WebSocket error:', err)
|
|
152
|
-
clients.delete(ws)
|
|
153
|
-
})
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
server.listen(PORT, () => {
|
|
157
|
-
console.log(`[terminal] Running on port ${PORT}${isDev ? ' (dev mode with HMR)' : ''}`)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
// Graceful shutdown - kill PTY when instance stops
|
|
161
|
-
process.on('SIGTERM', () => {
|
|
162
|
-
console.log('[terminal] SIGTERM received, shutting down')
|
|
163
|
-
ptyProcess?.kill()
|
|
164
|
-
wss.clients.forEach((client) => client.close())
|
|
165
|
-
server.close(() => process.exit(0))
|
|
166
|
-
})
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
startServer()
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react'
|
|
2
|
-
import { Terminal } from '@xterm/xterm'
|
|
3
|
-
import { FitAddon } from '@xterm/addon-fit'
|
|
4
|
-
import { WebLinksAddon } from '@xterm/addon-web-links'
|
|
5
|
-
import '@xterm/xterm/css/xterm.css'
|
|
6
|
-
|
|
7
|
-
type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error'
|
|
8
|
-
|
|
9
|
-
export default function App() {
|
|
10
|
-
const terminalRef = useRef<HTMLDivElement>(null)
|
|
11
|
-
const termRef = useRef<Terminal | null>(null)
|
|
12
|
-
const wsRef = useRef<WebSocket | null>(null)
|
|
13
|
-
const fitAddonRef = useRef<FitAddon | null>(null)
|
|
14
|
-
const [status, setStatus] = useState<ConnectionStatus>('connecting')
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
if (!terminalRef.current) return
|
|
18
|
-
|
|
19
|
-
// Create terminal
|
|
20
|
-
const term = new Terminal({
|
|
21
|
-
cursorBlink: true,
|
|
22
|
-
fontSize: 14,
|
|
23
|
-
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
|
24
|
-
theme: {
|
|
25
|
-
background: '#1a1a1a',
|
|
26
|
-
foreground: '#e0e0e0',
|
|
27
|
-
cursor: '#e0e0e0',
|
|
28
|
-
cursorAccent: '#1a1a1a',
|
|
29
|
-
selectionBackground: '#4a4a4a',
|
|
30
|
-
black: '#1a1a1a',
|
|
31
|
-
red: '#ff5555',
|
|
32
|
-
green: '#50fa7b',
|
|
33
|
-
yellow: '#f1fa8c',
|
|
34
|
-
blue: '#6272a4',
|
|
35
|
-
magenta: '#ff79c6',
|
|
36
|
-
cyan: '#8be9fd',
|
|
37
|
-
white: '#e0e0e0',
|
|
38
|
-
brightBlack: '#4a4a4a',
|
|
39
|
-
brightRed: '#ff6e6e',
|
|
40
|
-
brightGreen: '#69ff94',
|
|
41
|
-
brightYellow: '#ffffa5',
|
|
42
|
-
brightBlue: '#d6acff',
|
|
43
|
-
brightMagenta: '#ff92df',
|
|
44
|
-
brightCyan: '#a4ffff',
|
|
45
|
-
brightWhite: '#ffffff',
|
|
46
|
-
},
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
// Add addons
|
|
50
|
-
const fitAddon = new FitAddon()
|
|
51
|
-
const webLinksAddon = new WebLinksAddon()
|
|
52
|
-
term.loadAddon(fitAddon)
|
|
53
|
-
term.loadAddon(webLinksAddon)
|
|
54
|
-
|
|
55
|
-
// Open terminal in container
|
|
56
|
-
term.open(terminalRef.current)
|
|
57
|
-
fitAddon.fit()
|
|
58
|
-
|
|
59
|
-
termRef.current = term
|
|
60
|
-
fitAddonRef.current = fitAddon
|
|
61
|
-
|
|
62
|
-
// Connect WebSocket
|
|
63
|
-
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
64
|
-
const ws = new WebSocket(`${wsProtocol}//${window.location.host}/ws`)
|
|
65
|
-
wsRef.current = ws
|
|
66
|
-
|
|
67
|
-
ws.onopen = () => {
|
|
68
|
-
setStatus('connected')
|
|
69
|
-
// Send initial size
|
|
70
|
-
const dims = fitAddon.proposeDimensions()
|
|
71
|
-
if (dims) {
|
|
72
|
-
ws.send(JSON.stringify({ type: 'resize', cols: dims.cols, rows: dims.rows }))
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
ws.onmessage = (event) => {
|
|
77
|
-
try {
|
|
78
|
-
const msg = JSON.parse(event.data)
|
|
79
|
-
switch (msg.type) {
|
|
80
|
-
case 'output':
|
|
81
|
-
term.write(msg.data)
|
|
82
|
-
break
|
|
83
|
-
case 'exit':
|
|
84
|
-
term.write(`\r\n\x1b[31m[Process exited with code ${msg.exitCode}]\x1b[0m\r\n`)
|
|
85
|
-
setStatus('disconnected')
|
|
86
|
-
break
|
|
87
|
-
}
|
|
88
|
-
} catch {
|
|
89
|
-
// Raw data fallback
|
|
90
|
-
term.write(event.data)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
ws.onerror = () => {
|
|
95
|
-
setStatus('error')
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
ws.onclose = () => {
|
|
99
|
-
if (status !== 'error') {
|
|
100
|
-
setStatus('disconnected')
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Terminal input -> WebSocket
|
|
105
|
-
term.onData((data) => {
|
|
106
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
107
|
-
ws.send(JSON.stringify({ type: 'input', data }))
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
// Handle resize
|
|
112
|
-
const handleResize = () => {
|
|
113
|
-
fitAddon.fit()
|
|
114
|
-
const dims = fitAddon.proposeDimensions()
|
|
115
|
-
if (dims && ws.readyState === WebSocket.OPEN) {
|
|
116
|
-
ws.send(JSON.stringify({ type: 'resize', cols: dims.cols, rows: dims.rows }))
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
window.addEventListener('resize', handleResize)
|
|
121
|
-
|
|
122
|
-
// Also observe container size changes (for panel resizes)
|
|
123
|
-
const resizeObserver = new ResizeObserver(handleResize)
|
|
124
|
-
resizeObserver.observe(terminalRef.current)
|
|
125
|
-
|
|
126
|
-
// Cleanup
|
|
127
|
-
return () => {
|
|
128
|
-
window.removeEventListener('resize', handleResize)
|
|
129
|
-
resizeObserver.disconnect()
|
|
130
|
-
ws.close()
|
|
131
|
-
term.dispose()
|
|
132
|
-
}
|
|
133
|
-
}, [])
|
|
134
|
-
|
|
135
|
-
return (
|
|
136
|
-
<div
|
|
137
|
-
style={{
|
|
138
|
-
width: '100%',
|
|
139
|
-
height: '100%',
|
|
140
|
-
backgroundColor: '#1a1a1a',
|
|
141
|
-
display: 'flex',
|
|
142
|
-
flexDirection: 'column',
|
|
143
|
-
}}
|
|
144
|
-
>
|
|
145
|
-
{status !== 'connected' && (
|
|
146
|
-
<div
|
|
147
|
-
style={{
|
|
148
|
-
padding: '8px 12px',
|
|
149
|
-
backgroundColor: status === 'error' ? '#ff5555' : '#4a4a4a',
|
|
150
|
-
color: '#fff',
|
|
151
|
-
fontSize: '12px',
|
|
152
|
-
}}
|
|
153
|
-
>
|
|
154
|
-
{status === 'connecting' && 'Connecting...'}
|
|
155
|
-
{status === 'disconnected' && 'Disconnected'}
|
|
156
|
-
{status === 'error' && 'Connection error'}
|
|
157
|
-
</div>
|
|
158
|
-
)}
|
|
159
|
-
<div
|
|
160
|
-
ref={terminalRef}
|
|
161
|
-
style={{
|
|
162
|
-
flex: 1,
|
|
163
|
-
padding: '4px',
|
|
164
|
-
}}
|
|
165
|
-
/>
|
|
166
|
-
</div>
|
|
167
|
-
)
|
|
168
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"skipLibCheck": true,
|
|
8
|
-
"moduleResolution": "bundler",
|
|
9
|
-
"allowImportingTsExtensions": true,
|
|
10
|
-
"isolatedModules": true,
|
|
11
|
-
"moduleDetection": "force",
|
|
12
|
-
"noEmit": true,
|
|
13
|
-
"jsx": "react-jsx",
|
|
14
|
-
"strict": true,
|
|
15
|
-
"noUnusedLocals": true,
|
|
16
|
-
"noUnusedParameters": true,
|
|
17
|
-
"noFallthroughCasesInSwitch": true,
|
|
18
|
-
"noUncheckedIndexedAccess": true,
|
|
19
|
-
"esModuleInterop": true
|
|
20
|
-
},
|
|
21
|
-
"include": ["src", "server.ts"]
|
|
22
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Shella Plugin</title>
|
|
7
|
-
<style>
|
|
8
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
9
|
-
body {
|
|
10
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
11
|
-
background: #0a0a0a;
|
|
12
|
-
color: #fafafa;
|
|
13
|
-
min-height: 100vh;
|
|
14
|
-
padding: 2rem;
|
|
15
|
-
}
|
|
16
|
-
.container { max-width: 600px; margin: 0 auto; }
|
|
17
|
-
h1 { margin-bottom: 1rem; }
|
|
18
|
-
.card {
|
|
19
|
-
background: #1a1a1a;
|
|
20
|
-
border: 1px solid #333;
|
|
21
|
-
border-radius: 8px;
|
|
22
|
-
padding: 1.5rem;
|
|
23
|
-
margin-bottom: 1rem;
|
|
24
|
-
}
|
|
25
|
-
button {
|
|
26
|
-
background: #fff;
|
|
27
|
-
color: #000;
|
|
28
|
-
border: none;
|
|
29
|
-
padding: 0.5rem 1rem;
|
|
30
|
-
border-radius: 4px;
|
|
31
|
-
cursor: pointer;
|
|
32
|
-
font-size: 1rem;
|
|
33
|
-
}
|
|
34
|
-
button:hover { background: #ddd; }
|
|
35
|
-
#result {
|
|
36
|
-
margin-top: 1rem;
|
|
37
|
-
padding: 1rem;
|
|
38
|
-
background: #262626;
|
|
39
|
-
border-radius: 4px;
|
|
40
|
-
display: none;
|
|
41
|
-
}
|
|
42
|
-
</style>
|
|
43
|
-
</head>
|
|
44
|
-
<body>
|
|
45
|
-
<div class="container">
|
|
46
|
-
<h1>Shella Plugin</h1>
|
|
47
|
-
<div class="card">
|
|
48
|
-
<p>This is your plugin scaffold. Edit this file and server.js to build your UI.</p>
|
|
49
|
-
<br>
|
|
50
|
-
<button onclick="testApi()">Test API</button>
|
|
51
|
-
<div id="result"></div>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
<script>
|
|
55
|
-
async function testApi() {
|
|
56
|
-
const res = await fetch('/api/hello');
|
|
57
|
-
const data = await res.json();
|
|
58
|
-
const result = document.getElementById('result');
|
|
59
|
-
result.style.display = 'block';
|
|
60
|
-
result.textContent = JSON.stringify(data, null, 2);
|
|
61
|
-
}
|
|
62
|
-
</script>
|
|
63
|
-
</body>
|
|
64
|
-
</html>
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const app = express();
|
|
5
|
-
const PORT = process.env.PORT;
|
|
6
|
-
|
|
7
|
-
// Allow embedding in iframes (for shella-web)
|
|
8
|
-
app.use((req, res, next) => {
|
|
9
|
-
res.setHeader('Content-Security-Policy', "frame-ancestors *");
|
|
10
|
-
next();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// Middleware
|
|
14
|
-
app.use(express.json());
|
|
15
|
-
app.use(express.static(path.join(__dirname, 'public')));
|
|
16
|
-
|
|
17
|
-
// API routes
|
|
18
|
-
app.get('/api/hello', (req, res) => {
|
|
19
|
-
res.json({ message: 'Hello from the plugin!' });
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// Add your API endpoints here
|
|
23
|
-
// Example: Import from parent project
|
|
24
|
-
// const { someFunction } = require('../src/mymodule.js');
|
|
25
|
-
// app.post('/api/analyze', async (req, res) => {
|
|
26
|
-
// const result = await someFunction(req.body);
|
|
27
|
-
// res.json(result);
|
|
28
|
-
// });
|
|
29
|
-
|
|
30
|
-
const server = app.listen(PORT, () => {
|
|
31
|
-
console.log(`Plugin running on port ${PORT}`);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// Graceful shutdown
|
|
35
|
-
process.on('SIGTERM', () => {
|
|
36
|
-
server.close(() => process.exit(0));
|
|
37
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Shella Plugin</title>
|
|
7
|
-
<style>
|
|
8
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
9
|
-
body {
|
|
10
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
11
|
-
background: #0a0a0a;
|
|
12
|
-
color: #fafafa;
|
|
13
|
-
min-height: 100vh;
|
|
14
|
-
padding: 2rem;
|
|
15
|
-
}
|
|
16
|
-
.container { max-width: 600px; margin: 0 auto; }
|
|
17
|
-
h1 { margin-bottom: 1rem; }
|
|
18
|
-
.card {
|
|
19
|
-
background: #1a1a1a;
|
|
20
|
-
border: 1px solid #333;
|
|
21
|
-
border-radius: 8px;
|
|
22
|
-
padding: 1.5rem;
|
|
23
|
-
margin-bottom: 1rem;
|
|
24
|
-
}
|
|
25
|
-
button {
|
|
26
|
-
background: #fff;
|
|
27
|
-
color: #000;
|
|
28
|
-
border: none;
|
|
29
|
-
padding: 0.5rem 1rem;
|
|
30
|
-
border-radius: 4px;
|
|
31
|
-
cursor: pointer;
|
|
32
|
-
font-size: 1rem;
|
|
33
|
-
}
|
|
34
|
-
button:hover { background: #ddd; }
|
|
35
|
-
#result {
|
|
36
|
-
margin-top: 1rem;
|
|
37
|
-
padding: 1rem;
|
|
38
|
-
background: #262626;
|
|
39
|
-
border-radius: 4px;
|
|
40
|
-
display: none;
|
|
41
|
-
}
|
|
42
|
-
</style>
|
|
43
|
-
</head>
|
|
44
|
-
<body>
|
|
45
|
-
<div class="container">
|
|
46
|
-
<h1>Shella Plugin</h1>
|
|
47
|
-
<div class="card">
|
|
48
|
-
<p>This is your plugin scaffold. Edit this file and server.js to build your UI.</p>
|
|
49
|
-
<br>
|
|
50
|
-
<button onclick="testApi()">Test API</button>
|
|
51
|
-
<div id="result"></div>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
<script>
|
|
55
|
-
async function testApi() {
|
|
56
|
-
const res = await fetch('/api/hello');
|
|
57
|
-
const data = await res.json();
|
|
58
|
-
const result = document.getElementById('result');
|
|
59
|
-
result.style.display = 'block';
|
|
60
|
-
result.textContent = JSON.stringify(data, null, 2);
|
|
61
|
-
}
|
|
62
|
-
</script>
|
|
63
|
-
</body>
|
|
64
|
-
</html>
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
const http = require('http');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const PORT = process.env.PORT;
|
|
6
|
-
|
|
7
|
-
// Simple static file server + API
|
|
8
|
-
const server = http.createServer((req, res) => {
|
|
9
|
-
// Allow embedding in iframes (for shella-web)
|
|
10
|
-
res.setHeader('Content-Security-Policy', "frame-ancestors *");
|
|
11
|
-
// API routes
|
|
12
|
-
if (req.url === '/api/hello' && req.method === 'GET') {
|
|
13
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
14
|
-
res.end(JSON.stringify({ message: 'Hello from the plugin!' }));
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Serve static files from public/
|
|
19
|
-
let filePath = path.join(__dirname, 'public', req.url === '/' ? 'index.html' : req.url);
|
|
20
|
-
const ext = path.extname(filePath);
|
|
21
|
-
|
|
22
|
-
const mimeTypes = {
|
|
23
|
-
'.html': 'text/html',
|
|
24
|
-
'.js': 'text/javascript',
|
|
25
|
-
'.css': 'text/css',
|
|
26
|
-
'.json': 'application/json',
|
|
27
|
-
'.png': 'image/png',
|
|
28
|
-
'.svg': 'image/svg+xml',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
fs.readFile(filePath, (err, content) => {
|
|
32
|
-
if (err) {
|
|
33
|
-
if (err.code === 'ENOENT') {
|
|
34
|
-
res.writeHead(404);
|
|
35
|
-
res.end('Not found');
|
|
36
|
-
} else {
|
|
37
|
-
res.writeHead(500);
|
|
38
|
-
res.end('Server error');
|
|
39
|
-
}
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
res.writeHead(200, { 'Content-Type': mimeTypes[ext] || 'text/plain' });
|
|
43
|
-
res.end(content);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
server.listen(PORT, () => {
|
|
48
|
-
console.log(`Plugin running on port ${PORT}`);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Graceful shutdown
|
|
52
|
-
process.on('SIGTERM', () => {
|
|
53
|
-
server.close(() => process.exit(0));
|
|
54
|
-
});
|