opengstack 0.13.10 → 0.14.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/AGENTS.md +4 -4
- package/CLAUDE.md +127 -110
- package/README.md +10 -5
- package/SKILL.md +500 -70
- package/bin/opengstack.js +69 -69
- package/{skills/land-and-deploy/SKILL.md → commands/autoplan.md} +7 -25
- package/{skills/benchmark/SKILL.md → commands/benchmark.md} +84 -108
- package/{skills/browse/SKILL.md → commands/browse.md} +60 -81
- package/{skills/ship/SKILL.md → commands/canary.md} +7 -27
- package/{skills/careful/SKILL.md → commands/careful.md} +2 -22
- package/{skills/canary/SKILL.md → commands/codex.md} +7 -26
- package/{skills/connect-chrome/SKILL.md → commands/connect-chrome.md} +7 -24
- package/commands/cso.md +70 -0
- package/commands/design-consultation.md +70 -0
- package/commands/design-review.md +70 -0
- package/commands/design-shotgun.md +70 -0
- package/commands/document-release.md +70 -0
- package/{skills/freeze/SKILL.md → commands/freeze.md} +3 -29
- package/{skills/guard/SKILL.md → commands/guard.md} +4 -35
- package/commands/investigate.md +70 -0
- package/commands/land-and-deploy.md +70 -0
- package/commands/office-hours.md +70 -0
- package/{skills/gstack-upgrade/SKILL.md → commands/opengstack-upgrade.md} +64 -79
- package/commands/plan-ceo-review.md +70 -0
- package/commands/plan-design-review.md +70 -0
- package/commands/plan-eng-review.md +70 -0
- package/commands/qa-only.md +70 -0
- package/commands/qa.md +70 -0
- package/commands/retro.md +70 -0
- package/commands/review.md +70 -0
- package/{skills/setup-browser-cookies/SKILL.md → commands/setup-browser-cookies.md} +22 -40
- package/commands/setup-deploy.md +70 -0
- package/commands/ship.md +70 -0
- package/commands/unfreeze.md +25 -0
- package/docs/designs/CHROME_VS_CHROMIUM_EXPLORATION.md +9 -9
- package/docs/designs/CONDUCTOR_CHROME_SIDEBAR_INTEGRATION.md +2 -2
- package/docs/designs/CONDUCTOR_SESSION_API.md +16 -16
- package/docs/designs/DESIGN_SHOTGUN.md +74 -74
- package/docs/designs/DESIGN_TOOLS_V1.md +111 -111
- package/docs/skills.md +483 -202
- package/package.json +42 -43
- package/scripts/analytics.ts +188 -0
- package/scripts/dev-skill.ts +83 -0
- package/scripts/discover-skills.ts +39 -0
- package/scripts/eval-compare.ts +97 -0
- package/scripts/eval-list.ts +117 -0
- package/scripts/eval-select.ts +86 -0
- package/scripts/eval-summary.ts +188 -0
- package/scripts/eval-watch.ts +172 -0
- package/scripts/gen-skill-docs.ts +473 -0
- package/scripts/resolvers/browse.ts +129 -0
- package/scripts/resolvers/codex-helpers.ts +133 -0
- package/scripts/resolvers/composition.ts +48 -0
- package/scripts/resolvers/confidence.ts +37 -0
- package/scripts/resolvers/constants.ts +50 -0
- package/scripts/resolvers/design.ts +950 -0
- package/scripts/resolvers/index.ts +59 -0
- package/scripts/resolvers/learnings.ts +96 -0
- package/scripts/resolvers/preamble.ts +505 -0
- package/scripts/resolvers/review.ts +884 -0
- package/scripts/resolvers/testing.ts +573 -0
- package/scripts/resolvers/types.ts +45 -0
- package/scripts/resolvers/utility.ts +421 -0
- package/scripts/skill-check.ts +190 -0
- package/scripts/cleanup.py +0 -100
- package/scripts/filter-skills.sh +0 -114
- package/scripts/filter_skills.py +0 -164
- package/scripts/install-skills.js +0 -60
- package/skills/autoplan/SKILL.md +0 -96
- package/skills/autoplan/SKILL.md.tmpl +0 -694
- package/skills/benchmark/SKILL.md.tmpl +0 -222
- package/skills/browse/SKILL.md.tmpl +0 -131
- package/skills/browse/bin/find-browse +0 -21
- package/skills/browse/bin/remote-slug +0 -14
- package/skills/browse/scripts/build-node-server.sh +0 -48
- package/skills/browse/src/activity.ts +0 -208
- package/skills/browse/src/browser-manager.ts +0 -959
- package/skills/browse/src/buffers.ts +0 -137
- package/skills/browse/src/bun-polyfill.cjs +0 -109
- package/skills/browse/src/cli.ts +0 -678
- package/skills/browse/src/commands.ts +0 -128
- package/skills/browse/src/config.ts +0 -150
- package/skills/browse/src/cookie-import-browser.ts +0 -625
- package/skills/browse/src/cookie-picker-routes.ts +0 -230
- package/skills/browse/src/cookie-picker-ui.ts +0 -688
- package/skills/browse/src/find-browse.ts +0 -61
- package/skills/browse/src/meta-commands.ts +0 -550
- package/skills/browse/src/platform.ts +0 -17
- package/skills/browse/src/read-commands.ts +0 -358
- package/skills/browse/src/server.ts +0 -1192
- package/skills/browse/src/sidebar-agent.ts +0 -280
- package/skills/browse/src/sidebar-utils.ts +0 -21
- package/skills/browse/src/snapshot.ts +0 -407
- package/skills/browse/src/url-validation.ts +0 -95
- package/skills/browse/src/write-commands.ts +0 -364
- package/skills/browse/test/activity.test.ts +0 -120
- package/skills/browse/test/adversarial-security.test.ts +0 -32
- package/skills/browse/test/browser-manager-unit.test.ts +0 -17
- package/skills/browse/test/bun-polyfill.test.ts +0 -72
- package/skills/browse/test/commands.test.ts +0 -2075
- package/skills/browse/test/compare-board.test.ts +0 -342
- package/skills/browse/test/config.test.ts +0 -316
- package/skills/browse/test/cookie-import-browser.test.ts +0 -519
- package/skills/browse/test/cookie-picker-routes.test.ts +0 -260
- package/skills/browse/test/file-drop.test.ts +0 -271
- package/skills/browse/test/find-browse.test.ts +0 -50
- package/skills/browse/test/findport.test.ts +0 -191
- package/skills/browse/test/fixtures/basic.html +0 -33
- package/skills/browse/test/fixtures/cursor-interactive.html +0 -22
- package/skills/browse/test/fixtures/dialog.html +0 -15
- package/skills/browse/test/fixtures/empty.html +0 -2
- package/skills/browse/test/fixtures/forms.html +0 -55
- package/skills/browse/test/fixtures/iframe.html +0 -30
- package/skills/browse/test/fixtures/network-idle.html +0 -30
- package/skills/browse/test/fixtures/qa-eval-checkout.html +0 -108
- package/skills/browse/test/fixtures/qa-eval-spa.html +0 -98
- package/skills/browse/test/fixtures/qa-eval.html +0 -51
- package/skills/browse/test/fixtures/responsive.html +0 -49
- package/skills/browse/test/fixtures/snapshot.html +0 -55
- package/skills/browse/test/fixtures/spa.html +0 -24
- package/skills/browse/test/fixtures/states.html +0 -17
- package/skills/browse/test/fixtures/upload.html +0 -25
- package/skills/browse/test/gstack-config.test.ts +0 -138
- package/skills/browse/test/gstack-update-check.test.ts +0 -514
- package/skills/browse/test/handoff.test.ts +0 -235
- package/skills/browse/test/path-validation.test.ts +0 -91
- package/skills/browse/test/platform.test.ts +0 -37
- package/skills/browse/test/server-auth.test.ts +0 -65
- package/skills/browse/test/sidebar-agent-roundtrip.test.ts +0 -226
- package/skills/browse/test/sidebar-agent.test.ts +0 -199
- package/skills/browse/test/sidebar-integration.test.ts +0 -320
- package/skills/browse/test/sidebar-unit.test.ts +0 -96
- package/skills/browse/test/snapshot.test.ts +0 -467
- package/skills/browse/test/state-ttl.test.ts +0 -35
- package/skills/browse/test/test-server.ts +0 -57
- package/skills/browse/test/url-validation.test.ts +0 -72
- package/skills/browse/test/watch.test.ts +0 -129
- package/skills/canary/SKILL.md.tmpl +0 -212
- package/skills/careful/SKILL.md.tmpl +0 -56
- package/skills/careful/bin/check-careful.sh +0 -112
- package/skills/codex/SKILL.md +0 -90
- package/skills/codex/SKILL.md.tmpl +0 -417
- package/skills/connect-chrome/SKILL.md.tmpl +0 -195
- package/skills/cso/ACKNOWLEDGEMENTS.md +0 -14
- package/skills/cso/SKILL.md +0 -93
- package/skills/cso/SKILL.md.tmpl +0 -606
- package/skills/design-consultation/SKILL.md +0 -94
- package/skills/design-consultation/SKILL.md.tmpl +0 -415
- package/skills/design-review/SKILL.md +0 -94
- package/skills/design-review/SKILL.md.tmpl +0 -290
- package/skills/design-shotgun/SKILL.md +0 -91
- package/skills/design-shotgun/SKILL.md.tmpl +0 -285
- package/skills/document-release/SKILL.md +0 -91
- package/skills/document-release/SKILL.md.tmpl +0 -359
- package/skills/freeze/SKILL.md.tmpl +0 -77
- package/skills/freeze/bin/check-freeze.sh +0 -79
- package/skills/gstack-upgrade/SKILL.md.tmpl +0 -222
- package/skills/guard/SKILL.md.tmpl +0 -77
- package/skills/investigate/SKILL.md +0 -105
- package/skills/investigate/SKILL.md.tmpl +0 -194
- package/skills/land-and-deploy/SKILL.md.tmpl +0 -881
- package/skills/office-hours/SKILL.md +0 -96
- package/skills/office-hours/SKILL.md.tmpl +0 -645
- package/skills/plan-ceo-review/SKILL.md +0 -94
- package/skills/plan-ceo-review/SKILL.md.tmpl +0 -811
- package/skills/plan-design-review/SKILL.md +0 -92
- package/skills/plan-design-review/SKILL.md.tmpl +0 -446
- package/skills/plan-eng-review/SKILL.md +0 -93
- package/skills/plan-eng-review/SKILL.md.tmpl +0 -303
- package/skills/qa/SKILL.md +0 -95
- package/skills/qa/SKILL.md.tmpl +0 -316
- package/skills/qa/references/issue-taxonomy.md +0 -85
- package/skills/qa/templates/qa-report-template.md +0 -126
- package/skills/qa-only/SKILL.md +0 -89
- package/skills/qa-only/SKILL.md.tmpl +0 -101
- package/skills/retro/SKILL.md +0 -89
- package/skills/retro/SKILL.md.tmpl +0 -820
- package/skills/review/SKILL.md +0 -92
- package/skills/review/SKILL.md.tmpl +0 -281
- package/skills/review/TODOS-format.md +0 -62
- package/skills/review/checklist.md +0 -220
- package/skills/review/design-checklist.md +0 -132
- package/skills/review/greptile-triage.md +0 -220
- package/skills/setup-browser-cookies/SKILL.md.tmpl +0 -81
- package/skills/setup-deploy/SKILL.md +0 -92
- package/skills/setup-deploy/SKILL.md.tmpl +0 -215
- package/skills/ship/SKILL.md.tmpl +0 -636
- package/skills/unfreeze/SKILL.md +0 -37
- package/skills/unfreeze/SKILL.md.tmpl +0 -36
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sidebar Agent — polls agent-queue from server, spawns claude -p for each
|
|
3
|
-
* message, streams live events back to the server via /sidebar-agent/event.
|
|
4
|
-
*
|
|
5
|
-
* This runs as a NON-COMPILED bun process because compiled bun binaries
|
|
6
|
-
* cannot posix_spawn external executables. The server writes to the queue
|
|
7
|
-
* file, this process reads it and spawns claude.
|
|
8
|
-
*
|
|
9
|
-
* Usage: BROWSE_BIN=/path/to/browse bun run browse/src/sidebar-agent.ts
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { spawn } from 'child_process';
|
|
13
|
-
import * as fs from 'fs';
|
|
14
|
-
import * as path from 'path';
|
|
15
|
-
|
|
16
|
-
const QUEUE = process.env.SIDEBAR_QUEUE_PATH || path.join(process.env.HOME || '/tmp', '.gstack', 'sidebar-agent-queue.jsonl');
|
|
17
|
-
const SERVER_PORT = parseInt(process.env.BROWSE_SERVER_PORT || '34567', 10);
|
|
18
|
-
const SERVER_URL = `http://127.0.0.1:${SERVER_PORT}`;
|
|
19
|
-
const POLL_MS = 500; // Fast polling — server already did the user-facing response
|
|
20
|
-
const B = process.env.BROWSE_BIN || path.resolve(__dirname, '../../.claude/skills/gstack/browse/dist/browse');
|
|
21
|
-
|
|
22
|
-
let lastLine = 0;
|
|
23
|
-
let authToken: string | null = null;
|
|
24
|
-
let isProcessing = false;
|
|
25
|
-
|
|
26
|
-
// ─── File drop relay ──────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
function getGitRoot(): string | null {
|
|
29
|
-
try {
|
|
30
|
-
const { execSync } = require('child_process');
|
|
31
|
-
return execSync('git rev-parse --show-toplevel', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
32
|
-
} catch {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function writeToInbox(message: string, pageUrl?: string, sessionId?: string): void {
|
|
38
|
-
const gitRoot = getGitRoot();
|
|
39
|
-
if (!gitRoot) {
|
|
40
|
-
console.error('[sidebar-agent] Cannot write to inbox — not in a git repo');
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const inboxDir = path.join(gitRoot, '.context', 'sidebar-inbox');
|
|
45
|
-
fs.mkdirSync(inboxDir, { recursive: true });
|
|
46
|
-
|
|
47
|
-
const now = new Date();
|
|
48
|
-
const timestamp = now.toISOString().replace(/:/g, '-');
|
|
49
|
-
const filename = `${timestamp}-observation.json`;
|
|
50
|
-
const tmpFile = path.join(inboxDir, `.${filename}.tmp`);
|
|
51
|
-
const finalFile = path.join(inboxDir, filename);
|
|
52
|
-
|
|
53
|
-
const inboxMessage = {
|
|
54
|
-
type: 'observation',
|
|
55
|
-
timestamp: now.toISOString(),
|
|
56
|
-
page: { url: pageUrl || 'unknown', title: '' },
|
|
57
|
-
userMessage: message,
|
|
58
|
-
sidebarSessionId: sessionId || 'unknown',
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
fs.writeFileSync(tmpFile, JSON.stringify(inboxMessage, null, 2));
|
|
62
|
-
fs.renameSync(tmpFile, finalFile);
|
|
63
|
-
console.log(`[sidebar-agent] Wrote inbox message: ${filename}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ─── Auth ────────────────────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
async function refreshToken(): Promise<string | null> {
|
|
69
|
-
// Read token from state file (same-user, mode 0o600) instead of /health
|
|
70
|
-
try {
|
|
71
|
-
const stateFile = process.env.BROWSE_STATE_FILE ||
|
|
72
|
-
path.join(process.env.HOME || '/tmp', '.gstack', 'browse.json');
|
|
73
|
-
const data = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
74
|
-
authToken = data.token || null;
|
|
75
|
-
return authToken;
|
|
76
|
-
} catch {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ─── Event relay to server ──────────────────────────────────────
|
|
82
|
-
|
|
83
|
-
async function sendEvent(event: Record<string, any>): Promise<void> {
|
|
84
|
-
if (!authToken) await refreshToken();
|
|
85
|
-
if (!authToken) return;
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
await fetch(`${SERVER_URL}/sidebar-agent/event`, {
|
|
89
|
-
method: 'POST',
|
|
90
|
-
headers: {
|
|
91
|
-
'Content-Type': 'application/json',
|
|
92
|
-
'Authorization': `Bearer ${authToken}`,
|
|
93
|
-
},
|
|
94
|
-
body: JSON.stringify(event),
|
|
95
|
-
});
|
|
96
|
-
} catch (err) {
|
|
97
|
-
console.error('[sidebar-agent] Failed to send event:', err);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ─── Claude subprocess ──────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
function shorten(str: string): string {
|
|
104
|
-
return str
|
|
105
|
-
.replace(new RegExp(B.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '$B')
|
|
106
|
-
.replace(/\/Users\/[^/]+/g, '~')
|
|
107
|
-
.replace(/\/conductor\/workspaces\/[^/]+\/[^/]+/g, '')
|
|
108
|
-
.replace(/\.claude\/skills\/gstack\//g, '')
|
|
109
|
-
.replace(/browse\/dist\/browse/g, '$B');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function summarizeToolInput(tool: string, input: any): string {
|
|
113
|
-
if (!input) return '';
|
|
114
|
-
if (tool === 'Bash' && input.command) {
|
|
115
|
-
let cmd = shorten(input.command);
|
|
116
|
-
return cmd.length > 80 ? cmd.slice(0, 80) + '…' : cmd;
|
|
117
|
-
}
|
|
118
|
-
if (tool === 'Read' && input.file_path) return shorten(input.file_path);
|
|
119
|
-
if (tool === 'Edit' && input.file_path) return shorten(input.file_path);
|
|
120
|
-
if (tool === 'Write' && input.file_path) return shorten(input.file_path);
|
|
121
|
-
if (tool === 'Grep' && input.pattern) return `/${input.pattern}/`;
|
|
122
|
-
if (tool === 'Glob' && input.pattern) return input.pattern;
|
|
123
|
-
try { return shorten(JSON.stringify(input)).slice(0, 60); } catch { return ''; }
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async function handleStreamEvent(event: any): Promise<void> {
|
|
127
|
-
if (event.type === 'system' && event.session_id) {
|
|
128
|
-
// Relay claude session ID for --resume support
|
|
129
|
-
await sendEvent({ type: 'system', claudeSessionId: event.session_id });
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (event.type === 'assistant' && event.message?.content) {
|
|
133
|
-
for (const block of event.message.content) {
|
|
134
|
-
if (block.type === 'tool_use') {
|
|
135
|
-
await sendEvent({ type: 'tool_use', tool: block.name, input: summarizeToolInput(block.name, block.input) });
|
|
136
|
-
} else if (block.type === 'text' && block.text) {
|
|
137
|
-
await sendEvent({ type: 'text', text: block.text });
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (event.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
|
|
143
|
-
await sendEvent({ type: 'tool_use', tool: event.content_block.name, input: summarizeToolInput(event.content_block.name, event.content_block.input) });
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (event.type === 'content_block_delta' && event.delta?.type === 'text_delta' && event.delta.text) {
|
|
147
|
-
await sendEvent({ type: 'text_delta', text: event.delta.text });
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (event.type === 'result') {
|
|
151
|
-
await sendEvent({ type: 'result', text: event.result || '' });
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async function askClaude(queueEntry: any): Promise<void> {
|
|
156
|
-
const { prompt, args, stateFile, cwd } = queueEntry;
|
|
157
|
-
|
|
158
|
-
isProcessing = true;
|
|
159
|
-
await sendEvent({ type: 'agent_start' });
|
|
160
|
-
|
|
161
|
-
return new Promise((resolve) => {
|
|
162
|
-
// Build args fresh — don't trust --resume from queue (session may be stale)
|
|
163
|
-
let claudeArgs = ['-p', prompt, '--output-format', 'stream-json', '--verbose',
|
|
164
|
-
'--allowedTools', 'Bash,Read,Glob,Grep'];
|
|
165
|
-
|
|
166
|
-
// Validate cwd exists — queue may reference a stale worktree
|
|
167
|
-
let effectiveCwd = cwd || process.cwd();
|
|
168
|
-
try { fs.accessSync(effectiveCwd); } catch { effectiveCwd = process.cwd(); }
|
|
169
|
-
|
|
170
|
-
const proc = spawn('claude', claudeArgs, {
|
|
171
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
172
|
-
cwd: effectiveCwd,
|
|
173
|
-
env: { ...process.env, BROWSE_STATE_FILE: stateFile || '' },
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
proc.stdin.end();
|
|
177
|
-
|
|
178
|
-
let buffer = '';
|
|
179
|
-
|
|
180
|
-
proc.stdout.on('data', (data: Buffer) => {
|
|
181
|
-
buffer += data.toString();
|
|
182
|
-
const lines = buffer.split('\n');
|
|
183
|
-
buffer = lines.pop() || '';
|
|
184
|
-
for (const line of lines) {
|
|
185
|
-
if (!line.trim()) continue;
|
|
186
|
-
try { handleStreamEvent(JSON.parse(line)); } catch {}
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
proc.stderr.on('data', () => {}); // Claude logs to stderr, ignore
|
|
191
|
-
|
|
192
|
-
proc.on('close', (code) => {
|
|
193
|
-
if (buffer.trim()) {
|
|
194
|
-
try { handleStreamEvent(JSON.parse(buffer)); } catch {}
|
|
195
|
-
}
|
|
196
|
-
sendEvent({ type: 'agent_done' }).then(() => {
|
|
197
|
-
isProcessing = false;
|
|
198
|
-
resolve();
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
proc.on('error', (err) => {
|
|
203
|
-
sendEvent({ type: 'agent_error', error: err.message }).then(() => {
|
|
204
|
-
isProcessing = false;
|
|
205
|
-
resolve();
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
// Timeout (default 300s / 5 min — multi-page tasks need time)
|
|
210
|
-
const timeoutMs = parseInt(process.env.SIDEBAR_AGENT_TIMEOUT || '300000', 10);
|
|
211
|
-
setTimeout(() => {
|
|
212
|
-
try { proc.kill(); } catch {}
|
|
213
|
-
sendEvent({ type: 'agent_error', error: `Timed out after ${timeoutMs / 1000}s` }).then(() => {
|
|
214
|
-
isProcessing = false;
|
|
215
|
-
resolve();
|
|
216
|
-
});
|
|
217
|
-
}, timeoutMs);
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// ─── Poll loop ───────────────────────────────────────────────────
|
|
222
|
-
|
|
223
|
-
function countLines(): number {
|
|
224
|
-
try {
|
|
225
|
-
return fs.readFileSync(QUEUE, 'utf-8').split('\n').filter(Boolean).length;
|
|
226
|
-
} catch { return 0; }
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function readLine(n: number): string | null {
|
|
230
|
-
try {
|
|
231
|
-
const lines = fs.readFileSync(QUEUE, 'utf-8').split('\n').filter(Boolean);
|
|
232
|
-
return lines[n - 1] || null;
|
|
233
|
-
} catch { return null; }
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
async function poll() {
|
|
237
|
-
if (isProcessing) return; // One at a time — server handles queuing
|
|
238
|
-
|
|
239
|
-
const current = countLines();
|
|
240
|
-
if (current <= lastLine) return;
|
|
241
|
-
|
|
242
|
-
while (lastLine < current && !isProcessing) {
|
|
243
|
-
lastLine++;
|
|
244
|
-
const line = readLine(lastLine);
|
|
245
|
-
if (!line) continue;
|
|
246
|
-
|
|
247
|
-
let entry: any;
|
|
248
|
-
try { entry = JSON.parse(line); } catch { continue; }
|
|
249
|
-
if (!entry.message && !entry.prompt) continue;
|
|
250
|
-
|
|
251
|
-
console.log(`[sidebar-agent] Processing: "${entry.message}"`);
|
|
252
|
-
// Write to inbox so workspace agent can pick it up
|
|
253
|
-
writeToInbox(entry.message || entry.prompt, entry.pageUrl, entry.sessionId);
|
|
254
|
-
try {
|
|
255
|
-
await askClaude(entry);
|
|
256
|
-
} catch (err) {
|
|
257
|
-
console.error(`[sidebar-agent] Error:`, err);
|
|
258
|
-
await sendEvent({ type: 'agent_error', error: String(err) });
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// ─── Main ────────────────────────────────────────────────────────
|
|
264
|
-
|
|
265
|
-
async function main() {
|
|
266
|
-
const dir = path.dirname(QUEUE);
|
|
267
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
268
|
-
if (!fs.existsSync(QUEUE)) fs.writeFileSync(QUEUE, '');
|
|
269
|
-
|
|
270
|
-
lastLine = countLines();
|
|
271
|
-
await refreshToken();
|
|
272
|
-
|
|
273
|
-
console.log(`[sidebar-agent] Started. Watching ${QUEUE} from line ${lastLine}`);
|
|
274
|
-
console.log(`[sidebar-agent] Server: ${SERVER_URL}`);
|
|
275
|
-
console.log(`[sidebar-agent] Browse binary: ${B}`);
|
|
276
|
-
|
|
277
|
-
setInterval(poll, POLL_MS);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
main().catch(console.error);
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared sidebar utilities — extracted for testability.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Sanitize a URL from the Chrome extension before embedding in a prompt.
|
|
7
|
-
* Only accepts http/https, strips control characters, truncates to 2048 chars.
|
|
8
|
-
* Returns null if the URL is invalid or uses a non-http scheme.
|
|
9
|
-
*/
|
|
10
|
-
export function sanitizeExtensionUrl(url: string | null | undefined): string | null {
|
|
11
|
-
if (!url) return null;
|
|
12
|
-
try {
|
|
13
|
-
const u = new URL(url);
|
|
14
|
-
if (u.protocol === 'http:' || u.protocol === 'https:') {
|
|
15
|
-
return u.href.replace(/[\x00-\x1f\x7f]/g, '').slice(0, 2048);
|
|
16
|
-
}
|
|
17
|
-
return null;
|
|
18
|
-
} catch {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
}
|