agentgui 1.0.130 → 1.0.138
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/CLAUDE.md +46 -0
- package/bin/gmgui.cjs +1 -1
- package/database.js +11 -2
- package/package.json +2 -1
- package/server.js +77 -10
- package/setup-npm-token.sh +68 -0
- package/static/app.js +5 -3
- package/static/index.html +245 -3
- package/static/js/client.js +66 -8
- package/static/js/conversations.js +34 -6
- package/static/js/features.js +17 -17
- package/static/js/streaming-renderer.js +48 -24
- package/static/js/syntax-highlighter.js +1 -1
- package/static/js/voice.js +430 -0
- package/static/styles.css +86 -0
package/CLAUDE.md
CHANGED
|
@@ -128,6 +128,51 @@ Production ready - no additional configuration needed beyond:
|
|
|
128
128
|
2. Set PORT environment variable if needed
|
|
129
129
|
3. Run: `npm start` or `npm run dev` for development
|
|
130
130
|
|
|
131
|
+
## npm Publishing Setup
|
|
132
|
+
|
|
133
|
+
Automated npm publishing is configured via GitHub Actions with OIDC authentication. To complete setup:
|
|
134
|
+
|
|
135
|
+
### Step 1: Configure OIDC Trusted Publisher on npm.org
|
|
136
|
+
|
|
137
|
+
Visit: https://www.npmjs.com/package/agentgui/access
|
|
138
|
+
|
|
139
|
+
Click "Add Trusted Publisher" and fill in:
|
|
140
|
+
- **Publishing provider**: GitHub
|
|
141
|
+
- **Owner**: AnEntrypoint
|
|
142
|
+
- **Repository**: agentgui
|
|
143
|
+
- **Workflow file**: `.github/workflows/publish-npm.yml`
|
|
144
|
+
|
|
145
|
+
This requires npm account access with 2FA completion.
|
|
146
|
+
|
|
147
|
+
### Step 2: Trigger Publishing Workflow
|
|
148
|
+
|
|
149
|
+
Once OIDC is configured, push to main branch to trigger automatic publishing:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
git commit --allow-empty -m "test: verify npm publish with OIDC"
|
|
153
|
+
git push
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Monitor at: https://github.com/AnEntrypoint/agentgui/actions
|
|
157
|
+
|
|
158
|
+
### Optional: Add Granular Token Backup
|
|
159
|
+
|
|
160
|
+
Generate a 3-month granular access token for fallback authentication:
|
|
161
|
+
|
|
162
|
+
Visit: https://www.npmjs.com/settings/lanmower/tokens
|
|
163
|
+
|
|
164
|
+
Click "Generate New Token" → "Granular Access Token" and configure:
|
|
165
|
+
- **Name**: github-actions-3month
|
|
166
|
+
- **Permissions**: Read and write
|
|
167
|
+
- **Package**: agentgui
|
|
168
|
+
- **Expiration**: 90 days
|
|
169
|
+
- **Bypass 2FA**: enabled
|
|
170
|
+
|
|
171
|
+
Then add to GitHub Actions secrets:
|
|
172
|
+
```bash
|
|
173
|
+
gh secret set NPM_TOKEN --body "YOUR_TOKEN" --repo AnEntrypoint/agentgui
|
|
174
|
+
```
|
|
175
|
+
|
|
131
176
|
## Support
|
|
132
177
|
|
|
133
178
|
For issues, check:
|
|
@@ -135,3 +180,4 @@ For issues, check:
|
|
|
135
180
|
- Server logs for backend issues
|
|
136
181
|
- Database at `./data/agentgui.db` for data persistence
|
|
137
182
|
- WebSocket connection in Network tab (should show `/sync` as connected)
|
|
183
|
+
- GitHub Actions: https://github.com/AnEntrypoint/agentgui/actions for publishing errors
|
package/bin/gmgui.cjs
CHANGED
|
@@ -44,7 +44,7 @@ async function gmgui(args = []) {
|
|
|
44
44
|
return new Promise((resolve, reject) => {
|
|
45
45
|
const ps = spawn(runtime, [path.join(projectRoot, 'server.js')], {
|
|
46
46
|
cwd: projectRoot,
|
|
47
|
-
env: { ...process.env, PORT: port, BASE_URL: baseUrl },
|
|
47
|
+
env: { ...process.env, PORT: port, BASE_URL: baseUrl, STARTUP_CWD: process.cwd() },
|
|
48
48
|
stdio: 'inherit'
|
|
49
49
|
});
|
|
50
50
|
|
package/database.js
CHANGED
|
@@ -280,7 +280,7 @@ export const queries = {
|
|
|
280
280
|
|
|
281
281
|
getConversationsList() {
|
|
282
282
|
const stmt = db.prepare(
|
|
283
|
-
'SELECT id, title, agentType, created_at, updated_at, messageCount, workingDirectory FROM conversations WHERE status != ? ORDER BY updated_at DESC'
|
|
283
|
+
'SELECT id, title, agentType, created_at, updated_at, messageCount, workingDirectory, isStreaming FROM conversations WHERE status != ? ORDER BY updated_at DESC'
|
|
284
284
|
);
|
|
285
285
|
return stmt.all('deleted');
|
|
286
286
|
},
|
|
@@ -573,11 +573,19 @@ export const queries = {
|
|
|
573
573
|
}
|
|
574
574
|
|
|
575
575
|
const deleteStmt = db.transaction(() => {
|
|
576
|
+
const sessionIds = db.prepare('SELECT id FROM sessions WHERE conversationId = ?').all(id).map(r => r.id);
|
|
577
|
+
db.prepare('DELETE FROM stream_updates WHERE conversationId = ?').run(id);
|
|
576
578
|
db.prepare('DELETE FROM chunks WHERE conversationId = ?').run(id);
|
|
577
579
|
db.prepare('DELETE FROM events WHERE conversationId = ?').run(id);
|
|
580
|
+
if (sessionIds.length > 0) {
|
|
581
|
+
const placeholders = sessionIds.map(() => '?').join(',');
|
|
582
|
+
db.prepare(`DELETE FROM stream_updates WHERE sessionId IN (${placeholders})`).run(...sessionIds);
|
|
583
|
+
db.prepare(`DELETE FROM chunks WHERE sessionId IN (${placeholders})`).run(...sessionIds);
|
|
584
|
+
db.prepare(`DELETE FROM events WHERE sessionId IN (${placeholders})`).run(...sessionIds);
|
|
585
|
+
}
|
|
578
586
|
db.prepare('DELETE FROM sessions WHERE conversationId = ?').run(id);
|
|
579
587
|
db.prepare('DELETE FROM messages WHERE conversationId = ?').run(id);
|
|
580
|
-
db.prepare('
|
|
588
|
+
db.prepare('DELETE FROM conversations WHERE id = ?').run(id);
|
|
581
589
|
});
|
|
582
590
|
|
|
583
591
|
deleteStmt();
|
|
@@ -1064,6 +1072,7 @@ export const queries = {
|
|
|
1064
1072
|
}
|
|
1065
1073
|
|
|
1066
1074
|
const deleteStmt = db.transaction(() => {
|
|
1075
|
+
db.prepare('DELETE FROM stream_updates WHERE conversationId = ?').run(id);
|
|
1067
1076
|
db.prepare('DELETE FROM chunks WHERE conversationId = ?').run(id);
|
|
1068
1077
|
db.prepare('DELETE FROM events WHERE conversationId = ?').run(id);
|
|
1069
1078
|
db.prepare('DELETE FROM sessions WHERE conversationId = ?').run(id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentgui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.138",
|
|
4
4
|
"description": "Multi-agent ACP client with real-time communication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"busboy": "^1.6.0",
|
|
27
27
|
"express": "^5.2.1",
|
|
28
28
|
"fsbrowse": "^0.2.13",
|
|
29
|
+
"webtalk": "github:anEntrypoint/realtime-whisper-webgpu",
|
|
29
30
|
"ws": "^8.14.2"
|
|
30
31
|
}
|
|
31
32
|
}
|
package/server.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
4
5
|
import { fileURLToPath } from 'url';
|
|
5
6
|
import { WebSocketServer } from 'ws';
|
|
6
7
|
import { execSync } from 'child_process';
|
|
@@ -11,7 +12,8 @@ import { runClaudeWithStreaming } from './lib/claude-runner.js';
|
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
const express = require('express');
|
|
13
14
|
const Busboy = require('busboy');
|
|
14
|
-
const fsbrowse = require('
|
|
15
|
+
const fsbrowse = require('fsbrowse');
|
|
16
|
+
const { webtalk } = require('webtalk');
|
|
15
17
|
|
|
16
18
|
const SYSTEM_PROMPT = `Always write your responses in ripple-ui enhanced HTML. Avoid overriding light/dark mode CSS variables. Use all the benefits of HTML to express technical details with proper semantic markup, tables, code blocks, headings, and lists. Write clean, well-structured HTML that respects the existing design system.`;
|
|
17
19
|
|
|
@@ -28,12 +30,18 @@ const PORT = process.env.PORT || 3000;
|
|
|
28
30
|
const BASE_URL = (process.env.BASE_URL || '/gm').replace(/\/+$/, '');
|
|
29
31
|
const watch = process.argv.includes('--no-watch') ? false : (process.argv.includes('--watch') || process.env.HOT_RELOAD !== 'false');
|
|
30
32
|
|
|
33
|
+
const STARTUP_CWD = process.env.STARTUP_CWD || process.cwd();
|
|
31
34
|
const staticDir = path.join(__dirname, 'static');
|
|
32
35
|
if (!fs.existsSync(staticDir)) fs.mkdirSync(staticDir, { recursive: true });
|
|
33
36
|
|
|
34
37
|
// Express sub-app for fsbrowse file browser and file upload
|
|
35
38
|
const expressApp = express();
|
|
36
39
|
|
|
40
|
+
// Separate Express app for webtalk (STT/TTS) - isolated to contain COEP/COOP headers
|
|
41
|
+
const webtalkApp = express();
|
|
42
|
+
const webtalkInstance = webtalk(webtalkApp, { path: '/webtalk' });
|
|
43
|
+
webtalkInstance.init().catch(err => debugLog('Webtalk init: ' + err.message));
|
|
44
|
+
|
|
37
45
|
// File upload endpoint - copies dropped files to conversation workingDirectory
|
|
38
46
|
expressApp.post(BASE_URL + '/api/upload/:conversationId', (req, res) => {
|
|
39
47
|
try {
|
|
@@ -139,14 +147,72 @@ const server = http.createServer(async (req, res) => {
|
|
|
139
147
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
140
148
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
141
149
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
150
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'credentialless');
|
|
151
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
|
152
|
+
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
|
|
142
153
|
if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; }
|
|
143
154
|
|
|
144
|
-
// Route file upload and fsbrowse requests through Express sub-app
|
|
145
155
|
const pathOnly = req.url.split('?')[0];
|
|
156
|
+
const webtalkPrefix = BASE_URL + '/webtalk';
|
|
157
|
+
const isWebtalkRoute = pathOnly.startsWith(webtalkPrefix) ||
|
|
158
|
+
pathOnly.startsWith(BASE_URL + '/api/tts-status') ||
|
|
159
|
+
pathOnly.startsWith(BASE_URL + '/assets/') ||
|
|
160
|
+
pathOnly.startsWith(BASE_URL + '/tts/') ||
|
|
161
|
+
pathOnly.startsWith(BASE_URL + '/models/') ||
|
|
162
|
+
pathOnly.startsWith('/webtalk') ||
|
|
163
|
+
pathOnly.startsWith('/assets/') ||
|
|
164
|
+
pathOnly.startsWith('/tts/') ||
|
|
165
|
+
pathOnly.startsWith('/models/');
|
|
166
|
+
if (isWebtalkRoute) {
|
|
167
|
+
const webtalkSdkDir = path.dirname(require.resolve('webtalk/package.json'));
|
|
168
|
+
const sdkFiles = { '/demo': 'app.html', '/sdk.js': 'sdk.js', '/stt.js': 'stt.js', '/tts.js': 'tts.js', '/tts-utils.js': 'tts-utils.js' };
|
|
169
|
+
let stripped = pathOnly.startsWith(webtalkPrefix) ? pathOnly.slice(webtalkPrefix.length) : (pathOnly.startsWith('/webtalk') ? pathOnly.slice('/webtalk'.length) : null);
|
|
170
|
+
if (stripped !== null && !sdkFiles[stripped] && !stripped.endsWith('.js') && sdkFiles[stripped + '.js']) stripped += '.js';
|
|
171
|
+
if (stripped !== null && sdkFiles[stripped]) {
|
|
172
|
+
const filePath = path.join(webtalkSdkDir, sdkFiles[stripped]);
|
|
173
|
+
return fs.readFile(filePath, 'utf-8', (err, content) => {
|
|
174
|
+
if (err) { res.writeHead(404); res.end('Not found'); return; }
|
|
175
|
+
if (stripped === '/demo') {
|
|
176
|
+
let patched = content
|
|
177
|
+
.replace(/from\s+['"](\/webtalk\/[^'"]+)['"]/g, (_, p) => `from '${BASE_URL}${p}'`)
|
|
178
|
+
.replace(/from\s+['"]\.\/([^'"]+)['"]/g, (_, p) => `from '${BASE_URL}/webtalk/${p}'`)
|
|
179
|
+
.replace('<head>', `<head>\n <script>window.__WEBTALK_BASE='${BASE_URL}';</script>`);
|
|
180
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cross-Origin-Embedder-Policy': 'credentialless', 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Resource-Policy': 'cross-origin' });
|
|
181
|
+
return res.end(patched);
|
|
182
|
+
}
|
|
183
|
+
let js = content;
|
|
184
|
+
const ensureExt = (mod) => mod.endsWith('.js') ? mod : mod + '.js';
|
|
185
|
+
if (js.includes('require(') || js.includes('module.exports')) {
|
|
186
|
+
js = js.replace(/const\s*\{([^}]+)\}\s*=\s*require\(['"]\.\/([^'"]+)['"]\);?/g, (_, names, mod) => `import {${names}} from '${BASE_URL}/webtalk/${ensureExt(mod)}';`);
|
|
187
|
+
js = js.replace(/const\s+(\w+)\s*=\s*require\(['"]\.\/([^'"]+)['"]\);?/g, (_, name, mod) => `import ${name} from '${BASE_URL}/webtalk/${ensureExt(mod)}';`);
|
|
188
|
+
js = js.replace(/module\.exports\s*=\s*\{([^}]+)\};?/, (_, names) => `export {${names.trim().replace(/\s+/g, ' ')} };`);
|
|
189
|
+
}
|
|
190
|
+
js = js.replace(/from\s+['"]\.\/([^'"]+)['"]/g, (_, p) => `from '${BASE_URL}/webtalk/${ensureExt(p)}'`);
|
|
191
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8', 'Cross-Origin-Resource-Policy': 'cross-origin' });
|
|
192
|
+
res.end(js);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (req.url.startsWith(BASE_URL)) req.url = req.url.slice(BASE_URL.length) || '/';
|
|
196
|
+
const origSetHeader = res.setHeader.bind(res);
|
|
197
|
+
res.setHeader = (name, value) => {
|
|
198
|
+
if (name.toLowerCase() === 'cross-origin-embedder-policy') return;
|
|
199
|
+
origSetHeader(name, value);
|
|
200
|
+
};
|
|
201
|
+
return webtalkApp(req, res);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Route file upload and fsbrowse requests through Express sub-app
|
|
146
205
|
if (pathOnly.startsWith(BASE_URL + '/api/upload/') || pathOnly.startsWith(BASE_URL + '/files/')) {
|
|
147
206
|
return expressApp(req, res);
|
|
148
207
|
}
|
|
149
208
|
|
|
209
|
+
if (req.url === '/favicon.ico' || req.url === BASE_URL + '/favicon.ico') {
|
|
210
|
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect width="100" height="100" rx="20" fill="#3b82f6"/><text x="50" y="68" font-size="50" font-family="sans-serif" font-weight="bold" fill="white" text-anchor="middle">G</text></svg>';
|
|
211
|
+
res.writeHead(200, { 'Content-Type': 'image/svg+xml', 'Cache-Control': 'public, max-age=86400' });
|
|
212
|
+
res.end(svg);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
150
216
|
if (req.url === '/') { res.writeHead(302, { Location: BASE_URL + '/' }); res.end(); return; }
|
|
151
217
|
|
|
152
218
|
if (!req.url.startsWith(BASE_URL + '/') && req.url !== BASE_URL) {
|
|
@@ -424,16 +490,16 @@ const server = http.createServer(async (req, res) => {
|
|
|
424
490
|
|
|
425
491
|
if (routePath === '/api/home' && req.method === 'GET') {
|
|
426
492
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
427
|
-
res.end(JSON.stringify({ home:
|
|
493
|
+
res.end(JSON.stringify({ home: os.homedir(), cwd: STARTUP_CWD }));
|
|
428
494
|
return;
|
|
429
495
|
}
|
|
430
496
|
|
|
431
497
|
if (routePath === '/api/folders' && req.method === 'POST') {
|
|
432
498
|
const body = await parseBody(req);
|
|
433
|
-
const folderPath = body.path ||
|
|
499
|
+
const folderPath = body.path || STARTUP_CWD;
|
|
434
500
|
try {
|
|
435
501
|
const expandedPath = folderPath.startsWith('~') ?
|
|
436
|
-
folderPath.replace('~',
|
|
502
|
+
folderPath.replace('~', os.homedir()) : folderPath;
|
|
437
503
|
const entries = fs.readdirSync(expandedPath, { withFileTypes: true });
|
|
438
504
|
const folders = entries
|
|
439
505
|
.filter(e => e.isDirectory() && !e.name.startsWith('.'))
|
|
@@ -452,7 +518,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
452
518
|
const imagePath = routePath.slice('/api/image/'.length);
|
|
453
519
|
const decodedPath = decodeURIComponent(imagePath);
|
|
454
520
|
const expandedPath = decodedPath.startsWith('~') ?
|
|
455
|
-
decodedPath.replace('~',
|
|
521
|
+
decodedPath.replace('~', os.homedir()) : decodedPath;
|
|
456
522
|
const normalizedPath = path.normalize(expandedPath);
|
|
457
523
|
if (!normalizedPath.startsWith('/') || normalizedPath.includes('..')) {
|
|
458
524
|
res.writeHead(403); res.end('Forbidden'); return;
|
|
@@ -510,7 +576,7 @@ function serveFile(filePath, res) {
|
|
|
510
576
|
if (err) { res.writeHead(500); res.end('Server error'); return; }
|
|
511
577
|
let content = data.toString();
|
|
512
578
|
if (ext === '.html') {
|
|
513
|
-
const baseTag = `<script>window.__BASE_URL='${BASE_URL}';</script>`;
|
|
579
|
+
const baseTag = `<script>window.__BASE_URL='${BASE_URL}';</script>\n <script type="importmap">{"imports":{"webtalk-sdk":"${BASE_URL}/webtalk/sdk.js"}}</script>`;
|
|
514
580
|
content = content.replace('<head>', '<head>\n ' + baseTag);
|
|
515
581
|
if (watch) {
|
|
516
582
|
content += `\n<script>(function(){const ws=new WebSocket('ws://'+location.host+'${BASE_URL}/hot-reload');ws.onmessage=e=>{if(JSON.parse(e.data).type==='reload')location.reload()};})();</script>`;
|
|
@@ -550,12 +616,13 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
|
|
|
550
616
|
const startTime = Date.now();
|
|
551
617
|
activeExecutions.set(conversationId, true);
|
|
552
618
|
queries.setIsStreaming(conversationId, true);
|
|
619
|
+
queries.updateSession(sessionId, { status: 'active' });
|
|
553
620
|
|
|
554
621
|
try {
|
|
555
622
|
debugLog(`[stream] Starting: conversationId=${conversationId}, sessionId=${sessionId}`);
|
|
556
623
|
|
|
557
624
|
const conv = queries.getConversation(conversationId);
|
|
558
|
-
const cwd = conv?.workingDirectory ||
|
|
625
|
+
const cwd = conv?.workingDirectory || STARTUP_CWD;
|
|
559
626
|
const resumeSessionId = conv?.claudeSessionId || null;
|
|
560
627
|
|
|
561
628
|
let allBlocks = [];
|
|
@@ -761,7 +828,7 @@ async function processMessage(conversationId, messageId, content, agentId) {
|
|
|
761
828
|
debugLog(`[processMessage] Starting: conversationId=${conversationId}, agentId=${agentId}`);
|
|
762
829
|
|
|
763
830
|
const conv = queries.getConversation(conversationId);
|
|
764
|
-
const cwd = conv?.workingDirectory ||
|
|
831
|
+
const cwd = conv?.workingDirectory || STARTUP_CWD;
|
|
765
832
|
const resumeSessionId = conv?.claudeSessionId || null;
|
|
766
833
|
|
|
767
834
|
let contentStr = typeof content === 'object' ? JSON.stringify(content) : content;
|
|
@@ -886,7 +953,7 @@ function broadcastSync(event) {
|
|
|
886
953
|
shouldSend = true;
|
|
887
954
|
} else if (event.conversationId && ws.subscriptions?.has(`conv-${event.conversationId}`)) {
|
|
888
955
|
shouldSend = true;
|
|
889
|
-
} else if (event.type === 'message_created' || event.type === 'conversation_created' || event.type === 'conversations_updated' || event.type === 'conversation_deleted' || event.type === 'queue_status') {
|
|
956
|
+
} else if (event.type === 'message_created' || event.type === 'conversation_created' || event.type === 'conversations_updated' || event.type === 'conversation_deleted' || event.type === 'queue_status' || event.type === 'streaming_start' || event.type === 'streaming_complete' || event.type === 'streaming_error') {
|
|
890
957
|
shouldSend = true;
|
|
891
958
|
}
|
|
892
959
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "=========================================="
|
|
5
|
+
echo "npm Granular Access Token Setup (3-Month)"
|
|
6
|
+
echo "=========================================="
|
|
7
|
+
echo ""
|
|
8
|
+
echo "STEP 1: Manual token generation on npm.org"
|
|
9
|
+
echo "=========================================="
|
|
10
|
+
echo ""
|
|
11
|
+
echo "Please follow these steps:"
|
|
12
|
+
echo ""
|
|
13
|
+
echo "1. Open your browser and go to:"
|
|
14
|
+
echo " https://www.npmjs.com/settings/lanmower/tokens"
|
|
15
|
+
echo ""
|
|
16
|
+
echo "2. Click 'Generate New Token'"
|
|
17
|
+
echo ""
|
|
18
|
+
echo "3. Select 'Granular Access Token'"
|
|
19
|
+
echo ""
|
|
20
|
+
echo "4. Fill in the form:"
|
|
21
|
+
echo " - Token name: github-actions-3month"
|
|
22
|
+
echo " - Description: GitHub Actions npm publishing (3 month validity)"
|
|
23
|
+
echo " - Permissions: Read and write"
|
|
24
|
+
echo " - Packages: agentgui"
|
|
25
|
+
echo " - Expiration: 90 days"
|
|
26
|
+
echo " - Bypass 2FA: CHECKED"
|
|
27
|
+
echo ""
|
|
28
|
+
echo "5. Click 'Generate'"
|
|
29
|
+
echo ""
|
|
30
|
+
echo "6. COPY the token value (shown only once!)"
|
|
31
|
+
echo ""
|
|
32
|
+
echo "=========================================="
|
|
33
|
+
echo "STEP 2: Add token to GitHub Actions secret"
|
|
34
|
+
echo "=========================================="
|
|
35
|
+
echo ""
|
|
36
|
+
read -p "Paste your npm token here: " NPM_TOKEN
|
|
37
|
+
|
|
38
|
+
if [ -z "$NPM_TOKEN" ]; then
|
|
39
|
+
echo "Error: Token cannot be empty"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo ""
|
|
44
|
+
echo "Setting GitHub Actions secret..."
|
|
45
|
+
|
|
46
|
+
gh secret set NPM_TOKEN --body "$NPM_TOKEN" --repo AnEntrypoint/agentgui
|
|
47
|
+
|
|
48
|
+
echo ""
|
|
49
|
+
echo "Verifying secret was set..."
|
|
50
|
+
gh secret list --repo AnEntrypoint/agentgui | grep NPM_TOKEN
|
|
51
|
+
|
|
52
|
+
echo ""
|
|
53
|
+
echo "=========================================="
|
|
54
|
+
echo "STEP 3: Test the workflow"
|
|
55
|
+
echo "=========================================="
|
|
56
|
+
echo ""
|
|
57
|
+
echo "Creating test commit to trigger workflow..."
|
|
58
|
+
|
|
59
|
+
git -C /home/user/agentgui commit --allow-empty -m "test: verify npm publishing with 3-month token"
|
|
60
|
+
git -C /home/user/agentgui push
|
|
61
|
+
|
|
62
|
+
echo ""
|
|
63
|
+
echo "Monitor the workflow at:"
|
|
64
|
+
echo "https://github.com/AnEntrypoint/agentgui/actions"
|
|
65
|
+
echo ""
|
|
66
|
+
echo "=========================================="
|
|
67
|
+
echo "Setup Complete!"
|
|
68
|
+
echo "=========================================="
|
package/static/app.js
CHANGED
|
@@ -494,7 +494,8 @@ class GMGUIApp {
|
|
|
494
494
|
codeLines.push(lines[i]);
|
|
495
495
|
i++;
|
|
496
496
|
}
|
|
497
|
-
|
|
497
|
+
const clCount = codeLines.length;
|
|
498
|
+
html += `<details class="collapsible-code"><summary class="collapsible-code-summary">${this.escapeHtml(lang)} - ${clCount} line${clCount !== 1 ? 's' : ''}</summary><div class="code-block" data-language="${this.escapeHtml(lang)}"><pre><code>${this.escapeHtml(codeLines.join('\n'))}</code></pre></div></details>`;
|
|
498
499
|
i++;
|
|
499
500
|
continue;
|
|
500
501
|
}
|
|
@@ -592,9 +593,10 @@ class GMGUIApp {
|
|
|
592
593
|
<div class="html-content">${code}</div>
|
|
593
594
|
</div>`;
|
|
594
595
|
} else {
|
|
595
|
-
|
|
596
|
+
const lcCount = code.split('\n').length;
|
|
597
|
+
return `<details class="collapsible-code"><summary class="collapsible-code-summary">${this.escapeHtml(language)} - ${lcCount} line${lcCount !== 1 ? 's' : ''}</summary><div class="code-block" data-language="${this.escapeHtml(language)}">
|
|
596
598
|
<pre><code>${this.escapeHtml(code)}</code></pre>
|
|
597
|
-
</div>`;
|
|
599
|
+
</div></details>`;
|
|
598
600
|
}
|
|
599
601
|
}
|
|
600
602
|
|