repoburg 1.3.185 → 1.3.188
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/agent-pane/backend/package-lock.json +2359 -0
- package/agent-pane/backend/package.json +29 -0
- package/agent-pane/backend/src/codex/codex-provider.ts +363 -0
- package/agent-pane/backend/src/codex/item-mapper.ts +59 -0
- package/agent-pane/backend/src/db/index.ts +91 -0
- package/agent-pane/backend/src/db/schema.ts +76 -0
- package/agent-pane/backend/src/index.ts +57 -0
- package/agent-pane/backend/src/routes/sessions.ts +134 -0
- package/agent-pane/backend/src/routes/turns.ts +60 -0
- package/agent-pane/backend/src/routes/workspaces.ts +110 -0
- package/agent-pane/backend/src/ws/index.ts +43 -0
- package/agent-pane/backend/tsconfig.json +23 -0
- package/agent-pane/frontend/index.html +18 -0
- package/agent-pane/frontend/package-lock.json +1948 -0
- package/agent-pane/frontend/package.json +28 -0
- package/agent-pane/frontend/src/App.tsx +44 -0
- package/agent-pane/frontend/src/AppInitializer.tsx +46 -0
- package/agent-pane/frontend/src/api/client.ts +41 -0
- package/agent-pane/frontend/src/api/sessions.ts +47 -0
- package/agent-pane/frontend/src/api/websocket.ts +149 -0
- package/agent-pane/frontend/src/api/workspaces.ts +24 -0
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.tsx +50 -0
- package/agent-pane/frontend/src/components/CommandInputBar.tsx +42 -0
- package/agent-pane/frontend/src/components/CommandPalette.tsx +207 -0
- package/agent-pane/frontend/src/components/DiffViewer.tsx +27 -0
- package/agent-pane/frontend/src/components/IdeWorkspace.tsx +25 -0
- package/agent-pane/frontend/src/components/NewSessionDialog.tsx +84 -0
- package/agent-pane/frontend/src/components/RenderArea.tsx +170 -0
- package/agent-pane/frontend/src/components/RightPanel.tsx +40 -0
- package/agent-pane/frontend/src/components/RuntimeIcon.tsx +16 -0
- package/agent-pane/frontend/src/components/SessionTabs.tsx +44 -0
- package/agent-pane/frontend/src/components/StatusBar.tsx +68 -0
- package/agent-pane/frontend/src/components/StatusDot.tsx +13 -0
- package/agent-pane/frontend/src/components/Toast.tsx +80 -0
- package/agent-pane/frontend/src/components/TurnContent.tsx +294 -0
- package/agent-pane/frontend/src/components/WorkspaceSidebar.tsx +350 -0
- package/agent-pane/frontend/src/components/panels/ChangesPanel.tsx +81 -0
- package/agent-pane/frontend/src/components/panels/LogsPanel.tsx +25 -0
- package/agent-pane/frontend/src/components/panels/MetricsPanel.tsx +82 -0
- package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +45 -0
- package/agent-pane/frontend/src/main.tsx +10 -0
- package/agent-pane/frontend/src/store/store.ts +635 -0
- package/agent-pane/frontend/src/styles/app.css +365 -0
- package/agent-pane/frontend/src/styles/tokens.css +62 -0
- package/agent-pane/frontend/src/types/index.ts +153 -0
- package/agent-pane/frontend/tsconfig.json +22 -0
- package/agent-pane/frontend/vite.config.ts +16 -0
- package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { getDb } from '../db/index.ts';
|
|
4
|
+
import type { Session, SessionDetail, Turn, Item } from '../db/schema.ts';
|
|
5
|
+
|
|
6
|
+
/** Raw DB row for turns — token_usage is a JSON string, not parsed. */
|
|
7
|
+
interface TurnRow extends Omit<Turn, 'token_usage'> {
|
|
8
|
+
token_usage: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Raw DB row for items — data is a JSON string, not parsed. */
|
|
12
|
+
interface ItemRow extends Omit<Item, 'data'> {
|
|
13
|
+
data: string | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function registerSessionRoutes(fastify: FastifyInstance): Promise<void> {
|
|
17
|
+
// GET /api/sessions — list all sessions
|
|
18
|
+
fastify.get('/api/sessions', async () => {
|
|
19
|
+
const rows = getDb().prepare('SELECT * FROM sessions ORDER BY created_at DESC').all() as Session[];
|
|
20
|
+
return rows;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// GET /api/sessions/:id — session with turns + items
|
|
24
|
+
fastify.get<{ Params: { id: string } }>('/api/sessions/:id', async (request, reply) => {
|
|
25
|
+
const { id } = request.params;
|
|
26
|
+
const db = getDb();
|
|
27
|
+
|
|
28
|
+
const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(id) as Session | undefined;
|
|
29
|
+
if (!session) {
|
|
30
|
+
return reply.code(404).send({ error: 'Session not found' });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const turnRows = db.prepare('SELECT * FROM turns WHERE session_id = ? ORDER BY created_at ASC').all(id) as TurnRow[];
|
|
34
|
+
const itemRows = db.prepare(
|
|
35
|
+
'SELECT * FROM items WHERE turn_id IN (SELECT id FROM turns WHERE session_id = ?) ORDER BY created_at ASC',
|
|
36
|
+
).all(id) as ItemRow[];
|
|
37
|
+
|
|
38
|
+
// Parse item data from JSON string and group by turn_id
|
|
39
|
+
const itemsByTurn = new Map<string, Item[]>();
|
|
40
|
+
for (const row of itemRows) {
|
|
41
|
+
const item: Item = {
|
|
42
|
+
...row,
|
|
43
|
+
data: row.data ? JSON.parse(row.data) : null,
|
|
44
|
+
};
|
|
45
|
+
const list = itemsByTurn.get(item.turn_id) ?? [];
|
|
46
|
+
list.push(item);
|
|
47
|
+
itemsByTurn.set(item.turn_id, list);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const detail: SessionDetail = {
|
|
51
|
+
...session,
|
|
52
|
+
turns: turnRows.map((row) => ({
|
|
53
|
+
...row,
|
|
54
|
+
token_usage: row.token_usage ? JSON.parse(row.token_usage) : null,
|
|
55
|
+
items: itemsByTurn.get(row.id) ?? [],
|
|
56
|
+
})),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return detail;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// POST /api/sessions — create session
|
|
63
|
+
fastify.post<{ Body: { title?: string; runtime_type?: string; model?: string; cwd?: string } }>(
|
|
64
|
+
'/api/sessions',
|
|
65
|
+
async (request) => {
|
|
66
|
+
const { title = null, runtime_type = 'codex', model = null, cwd = null } = request.body || {};
|
|
67
|
+
const id = uuidv4();
|
|
68
|
+
const now = new Date().toISOString();
|
|
69
|
+
|
|
70
|
+
getDb().prepare(
|
|
71
|
+
`INSERT INTO sessions (id, title, runtime_type, model, cwd, external_thread_id, status, is_running, created_at, updated_at)
|
|
72
|
+
VALUES (?, ?, ?, ?, ?, NULL, 'active', 0, ?, ?)`,
|
|
73
|
+
).run(id, title, runtime_type, model, cwd, now, now);
|
|
74
|
+
|
|
75
|
+
const session = getDb().prepare('SELECT * FROM sessions WHERE id = ?').get(id) as Session;
|
|
76
|
+
return session;
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// DELETE /api/sessions/:id — delete session (cascade)
|
|
81
|
+
fastify.delete<{ Params: { id: string } }>('/api/sessions/:id', async (request, reply) => {
|
|
82
|
+
const { id } = request.params;
|
|
83
|
+
const db = getDb();
|
|
84
|
+
|
|
85
|
+
const existing = db.prepare('SELECT id FROM sessions WHERE id = ?').get(id);
|
|
86
|
+
if (!existing) {
|
|
87
|
+
return reply.code(404).send({ error: 'Session not found' });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Delete items first (turns cascade via FK, but better-sqlite3 needs explicit cascade if pragma is set)
|
|
91
|
+
db.prepare('DELETE FROM items WHERE turn_id IN (SELECT id FROM turns WHERE session_id = ?)').run(id);
|
|
92
|
+
db.prepare('DELETE FROM turns WHERE session_id = ?').run(id);
|
|
93
|
+
db.prepare('DELETE FROM sessions WHERE id = ?').run(id);
|
|
94
|
+
|
|
95
|
+
return { success: true };
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// PATCH /api/sessions/:id — update session (title, lifecycle, has_unreviewed)
|
|
99
|
+
fastify.patch<{ Params: { id: string }; Body: { title?: string; lifecycle?: string; has_unreviewed?: number } }>(
|
|
100
|
+
'/api/sessions/:id',
|
|
101
|
+
async (request, reply) => {
|
|
102
|
+
const { id } = request.params;
|
|
103
|
+
const { title, lifecycle, has_unreviewed } = request.body || {};
|
|
104
|
+
const db = getDb();
|
|
105
|
+
|
|
106
|
+
const existing = db.prepare('SELECT id FROM sessions WHERE id = ?').get(id);
|
|
107
|
+
if (!existing) {
|
|
108
|
+
return reply.code(404).send({ error: 'Session not found' });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const now = new Date().toISOString();
|
|
112
|
+
const updates: string[] = ['updated_at = ?'];
|
|
113
|
+
const params: (string | number | null)[] = [now];
|
|
114
|
+
|
|
115
|
+
if (title !== undefined) {
|
|
116
|
+
updates.push('title = ?');
|
|
117
|
+
params.push(title);
|
|
118
|
+
}
|
|
119
|
+
if (lifecycle !== undefined) {
|
|
120
|
+
updates.push('lifecycle = ?');
|
|
121
|
+
params.push(lifecycle);
|
|
122
|
+
}
|
|
123
|
+
if (has_unreviewed !== undefined) {
|
|
124
|
+
updates.push('has_unreviewed = ?');
|
|
125
|
+
params.push(has_unreviewed);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
db.prepare(`UPDATE sessions SET ${updates.join(', ')} WHERE id = ?`).run(...params, id);
|
|
129
|
+
|
|
130
|
+
const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(id) as Session;
|
|
131
|
+
return session;
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { getDb } from '../db/index.ts';
|
|
4
|
+
import { startTurn, interruptTurn } from '../codex/codex-provider.ts';
|
|
5
|
+
import type { Session, Turn } from '../db/schema.ts';
|
|
6
|
+
|
|
7
|
+
export async function registerTurnRoutes(fastify: FastifyInstance): Promise<void> {
|
|
8
|
+
// POST /api/sessions/:id/turns — submit prompt, starts Codex turn
|
|
9
|
+
fastify.post<{ Params: { id: string }; Body: { user_prompt: string } }>(
|
|
10
|
+
'/api/sessions/:id/turns',
|
|
11
|
+
async (request, reply) => {
|
|
12
|
+
const { id } = request.params;
|
|
13
|
+
const { user_prompt } = request.body || {};
|
|
14
|
+
|
|
15
|
+
if (!user_prompt || !user_prompt.trim()) {
|
|
16
|
+
return reply.code(400).send({ error: 'user_prompt is required' });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const db = getDb();
|
|
20
|
+
const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(id) as Session | undefined;
|
|
21
|
+
if (!session) {
|
|
22
|
+
return reply.code(404).send({ error: 'Session not found' });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (session.is_running) {
|
|
26
|
+
return reply.code(409).send({ error: 'A turn is already running for this session' });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Create turn in DB
|
|
30
|
+
const turnId = uuidv4();
|
|
31
|
+
const now = new Date().toISOString();
|
|
32
|
+
db.prepare(
|
|
33
|
+
`INSERT INTO turns (id, session_id, user_prompt, agent_response, thoughts, token_usage, external_turn_id, status, error, created_at, updated_at)
|
|
34
|
+
VALUES (?, ?, ?, NULL, NULL, NULL, NULL, 'pending', NULL, ?, ?)`,
|
|
35
|
+
).run(turnId, id, user_prompt, now, now);
|
|
36
|
+
|
|
37
|
+
const turn = db.prepare('SELECT * FROM turns WHERE id = ?').get(turnId) as Turn;
|
|
38
|
+
|
|
39
|
+
// Fire and forget — events stream via WebSocket
|
|
40
|
+
startTurn(session, turn).catch((err) => {
|
|
41
|
+
console.error(`[codex-provider] Unhandled error for session ${id}:`, err);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return turn;
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// POST /api/sessions/:id/cancel — cancel running turn
|
|
49
|
+
fastify.post<{ Params: { id: string } }>(
|
|
50
|
+
'/api/sessions/:id/cancel',
|
|
51
|
+
async (request, reply) => {
|
|
52
|
+
const { id } = request.params;
|
|
53
|
+
const cancelled = interruptTurn(id);
|
|
54
|
+
if (!cancelled) {
|
|
55
|
+
return reply.code(409).send({ error: 'No running turn to cancel' });
|
|
56
|
+
}
|
|
57
|
+
return { success: true };
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import { getDb } from '../db/index.ts';
|
|
6
|
+
import type { Workspace } from '../db/schema.ts';
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
|
|
10
|
+
export async function registerWorkspaceRoutes(fastify: FastifyInstance): Promise<void> {
|
|
11
|
+
// GET /api/workspaces — list ALL workspaces (active + soft-deleted)
|
|
12
|
+
fastify.get('/api/workspaces', async () => {
|
|
13
|
+
const rows = getDb()
|
|
14
|
+
.prepare('SELECT * FROM workspaces ORDER BY created_at ASC')
|
|
15
|
+
.all() as Workspace[];
|
|
16
|
+
return rows;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// POST /api/workspaces — create workspace (or restore if soft-deleted)
|
|
20
|
+
fastify.post<{ Body: { path: string; label?: string } }>(
|
|
21
|
+
'/api/workspaces',
|
|
22
|
+
async (request, reply) => {
|
|
23
|
+
const { path, label = null } = request.body || {};
|
|
24
|
+
if (!path || !path.trim()) {
|
|
25
|
+
return reply.code(400).send({ error: 'Path is required' });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const trimmedPath = path.trim();
|
|
29
|
+
const db = getDb();
|
|
30
|
+
|
|
31
|
+
// Check if workspace with this path already exists
|
|
32
|
+
const existing = db.prepare('SELECT * FROM workspaces WHERE path = ?').get(trimmedPath) as Workspace | undefined;
|
|
33
|
+
|
|
34
|
+
if (existing) {
|
|
35
|
+
if (existing.deleted === 0) {
|
|
36
|
+
return reply.code(409).send({ error: 'Workspace with this path already exists' });
|
|
37
|
+
}
|
|
38
|
+
// Restore soft-deleted workspace
|
|
39
|
+
db.prepare('UPDATE workspaces SET deleted = 0, label = ? WHERE id = ?').run(label, existing.id);
|
|
40
|
+
const restored = db.prepare('SELECT * FROM workspaces WHERE id = ?').get(existing.id) as Workspace;
|
|
41
|
+
return restored;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Create new workspace
|
|
45
|
+
const id = uuidv4();
|
|
46
|
+
const now = new Date().toISOString();
|
|
47
|
+
db.prepare(
|
|
48
|
+
'INSERT INTO workspaces (id, path, label, deleted, created_at) VALUES (?, ?, ?, 0, ?)',
|
|
49
|
+
).run(id, trimmedPath, label, now);
|
|
50
|
+
|
|
51
|
+
const workspace = db.prepare('SELECT * FROM workspaces WHERE id = ?').get(id) as Workspace;
|
|
52
|
+
return workspace;
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// GET /api/pick-directory — open native OS folder picker (macOS only)
|
|
57
|
+
fastify.get('/api/pick-directory', async (_request, reply) => {
|
|
58
|
+
try {
|
|
59
|
+
const { stdout } = await execFileAsync('osascript', [
|
|
60
|
+
'-e',
|
|
61
|
+
'POSIX path of (choose folder with prompt "Select a workspace directory")',
|
|
62
|
+
]);
|
|
63
|
+
const path = stdout.trim().replace(/\/$/, '');
|
|
64
|
+
return { path };
|
|
65
|
+
} catch {
|
|
66
|
+
// User cancelled the dialog or osascript not available
|
|
67
|
+
return reply.code(204).send({ cancelled: true });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// POST /api/workspaces/hide — soft-delete workspace by path (creates record if not exists)
|
|
72
|
+
fastify.post<{ Body: { path: string } }>(
|
|
73
|
+
'/api/workspaces/hide',
|
|
74
|
+
async (request, reply) => {
|
|
75
|
+
const { path } = request.body || {};
|
|
76
|
+
if (!path || !path.trim()) {
|
|
77
|
+
return reply.code(400).send({ error: 'Path is required' });
|
|
78
|
+
}
|
|
79
|
+
const trimmedPath = path.trim();
|
|
80
|
+
const db = getDb();
|
|
81
|
+
|
|
82
|
+
const existing = db.prepare('SELECT * FROM workspaces WHERE path = ?').get(trimmedPath) as Workspace | undefined;
|
|
83
|
+
if (existing) {
|
|
84
|
+
db.prepare('UPDATE workspaces SET deleted = 1 WHERE id = ?').run(existing.id);
|
|
85
|
+
} else {
|
|
86
|
+
const id = uuidv4();
|
|
87
|
+
const now = new Date().toISOString();
|
|
88
|
+
db.prepare(
|
|
89
|
+
'INSERT INTO workspaces (id, path, label, deleted, created_at) VALUES (?, ?, NULL, 1, ?)',
|
|
90
|
+
).run(id, trimmedPath, now);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { success: true };
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// DELETE /api/workspaces/:id — soft-delete workspace by ID (does NOT delete sessions)
|
|
98
|
+
fastify.delete<{ Params: { id: string } }>('/api/workspaces/:id', async (request, reply) => {
|
|
99
|
+
const { id } = request.params;
|
|
100
|
+
const db = getDb();
|
|
101
|
+
|
|
102
|
+
const existing = db.prepare('SELECT id FROM workspaces WHERE id = ? AND deleted = 0').get(id);
|
|
103
|
+
if (!existing) {
|
|
104
|
+
return reply.code(404).send({ error: 'Workspace not found' });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
db.prepare('UPDATE workspaces SET deleted = 1 WHERE id = ?').run(id);
|
|
108
|
+
return { success: true };
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { WebSocket } from 'ws';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
|
|
4
|
+
let wss: WebSocketServer | null = null;
|
|
5
|
+
|
|
6
|
+
/** Attach WebSocket server to an existing HTTP server (noServer mode). */
|
|
7
|
+
export function initWs(): WebSocketServer {
|
|
8
|
+
wss = new WebSocketServer({ noServer: true });
|
|
9
|
+
return wss;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Handle HTTP upgrade for the /ws path. Call from server.on('upgrade'). */
|
|
13
|
+
export function handleUpgrade(
|
|
14
|
+
wss: WebSocketServer,
|
|
15
|
+
request: any,
|
|
16
|
+
socket: any,
|
|
17
|
+
head: any,
|
|
18
|
+
): void {
|
|
19
|
+
const url = request.url || '';
|
|
20
|
+
if (url.startsWith('/ws')) {
|
|
21
|
+
wss.handleUpgrade(request, socket, head, (ws: WebSocket) => {
|
|
22
|
+
wss.emit('connection', ws, request);
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
socket.destroy();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Broadcast a JSON event to all connected WebSocket clients. */
|
|
30
|
+
export function broadcast(event: string, data: unknown): void {
|
|
31
|
+
if (!wss) return;
|
|
32
|
+
const message = JSON.stringify({ event, data });
|
|
33
|
+
for (const client of wss.clients) {
|
|
34
|
+
if (client.readyState === client.OPEN) {
|
|
35
|
+
client.send(message);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Get the WebSocket server instance (or null if not initialized). */
|
|
41
|
+
export function getWss(): WebSocketServer | null {
|
|
42
|
+
return wss;
|
|
43
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"declaration": false,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"noImplicitReturns": true,
|
|
18
|
+
"noFallthroughCasesInSwitch": true,
|
|
19
|
+
"allowImportingTsExtensions": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["src/**/*"],
|
|
22
|
+
"exclude": ["node_modules", "dist"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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>AgentPane</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;450;510;590;600&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|