opengstack 0.13.7 → 0.13.8
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/bin/opengstack.js +35 -90
- package/package.json +2 -3
- package/scripts/install-skills.js +29 -58
- package/skills/browse/bin/find-browse +21 -0
- package/skills/browse/bin/remote-slug +14 -0
- package/skills/browse/scripts/build-node-server.sh +48 -0
- package/skills/browse/src/activity.ts +208 -0
- package/skills/browse/src/browser-manager.ts +959 -0
- package/skills/browse/src/buffers.ts +137 -0
- package/skills/browse/src/bun-polyfill.cjs +109 -0
- package/skills/browse/src/cli.ts +678 -0
- package/skills/browse/src/commands.ts +128 -0
- package/skills/browse/src/config.ts +150 -0
- package/skills/browse/src/cookie-import-browser.ts +625 -0
- package/skills/browse/src/cookie-picker-routes.ts +230 -0
- package/skills/browse/src/cookie-picker-ui.ts +688 -0
- package/skills/browse/src/find-browse.ts +61 -0
- package/skills/browse/src/meta-commands.ts +550 -0
- package/skills/browse/src/platform.ts +17 -0
- package/skills/browse/src/read-commands.ts +358 -0
- package/skills/browse/src/server.ts +1192 -0
- package/skills/browse/src/sidebar-agent.ts +280 -0
- package/skills/browse/src/sidebar-utils.ts +21 -0
- package/skills/browse/src/snapshot.ts +407 -0
- package/skills/browse/src/url-validation.ts +95 -0
- package/skills/browse/src/write-commands.ts +364 -0
- package/skills/browse/test/activity.test.ts +120 -0
- package/skills/browse/test/adversarial-security.test.ts +32 -0
- package/skills/browse/test/browser-manager-unit.test.ts +17 -0
- package/skills/browse/test/bun-polyfill.test.ts +72 -0
- package/skills/browse/test/commands.test.ts +2075 -0
- package/skills/browse/test/compare-board.test.ts +342 -0
- package/skills/browse/test/config.test.ts +316 -0
- package/skills/browse/test/cookie-import-browser.test.ts +519 -0
- package/skills/browse/test/cookie-picker-routes.test.ts +260 -0
- package/skills/browse/test/file-drop.test.ts +271 -0
- package/skills/browse/test/find-browse.test.ts +50 -0
- package/skills/browse/test/findport.test.ts +191 -0
- package/skills/browse/test/fixtures/basic.html +33 -0
- package/skills/browse/test/fixtures/cursor-interactive.html +22 -0
- package/skills/browse/test/fixtures/dialog.html +15 -0
- package/skills/browse/test/fixtures/empty.html +2 -0
- package/skills/browse/test/fixtures/forms.html +55 -0
- package/skills/browse/test/fixtures/iframe.html +30 -0
- package/skills/browse/test/fixtures/network-idle.html +30 -0
- package/skills/browse/test/fixtures/qa-eval-checkout.html +108 -0
- package/skills/browse/test/fixtures/qa-eval-spa.html +98 -0
- package/skills/browse/test/fixtures/qa-eval.html +51 -0
- package/skills/browse/test/fixtures/responsive.html +49 -0
- package/skills/browse/test/fixtures/snapshot.html +55 -0
- package/skills/browse/test/fixtures/spa.html +24 -0
- package/skills/browse/test/fixtures/states.html +17 -0
- package/skills/browse/test/fixtures/upload.html +25 -0
- package/skills/browse/test/gstack-config.test.ts +138 -0
- package/skills/browse/test/gstack-update-check.test.ts +514 -0
- package/skills/browse/test/handoff.test.ts +235 -0
- package/skills/browse/test/path-validation.test.ts +91 -0
- package/skills/browse/test/platform.test.ts +37 -0
- package/skills/browse/test/server-auth.test.ts +65 -0
- package/skills/browse/test/sidebar-agent-roundtrip.test.ts +226 -0
- package/skills/browse/test/sidebar-agent.test.ts +199 -0
- package/skills/browse/test/sidebar-integration.test.ts +320 -0
- package/skills/browse/test/sidebar-unit.test.ts +96 -0
- package/skills/browse/test/snapshot.test.ts +467 -0
- package/skills/browse/test/state-ttl.test.ts +35 -0
- package/skills/browse/test/test-server.ts +57 -0
- package/skills/browse/test/url-validation.test.ts +72 -0
- package/skills/browse/test/watch.test.ts +129 -0
- package/skills/careful/bin/check-careful.sh +112 -0
- package/skills/cso/ACKNOWLEDGEMENTS.md +14 -0
- package/skills/freeze/bin/check-freeze.sh +79 -0
- package/skills/qa/references/issue-taxonomy.md +85 -0
- package/skills/qa/templates/qa-report-template.md +126 -0
- package/skills/review/TODOS-format.md +62 -0
- package/skills/review/checklist.md +220 -0
- package/skills/review/design-checklist.md +132 -0
- package/skills/review/greptile-triage.md +220 -0
- /package/{autoplan → skills/autoplan}/SKILL.md +0 -0
- /package/{autoplan → skills/autoplan}/SKILL.md.tmpl +0 -0
- /package/{benchmark → skills/benchmark}/SKILL.md +0 -0
- /package/{benchmark → skills/benchmark}/SKILL.md.tmpl +0 -0
- /package/{browse → skills/browse}/SKILL.md +0 -0
- /package/{browse → skills/browse}/SKILL.md.tmpl +0 -0
- /package/{canary → skills/canary}/SKILL.md +0 -0
- /package/{canary → skills/canary}/SKILL.md.tmpl +0 -0
- /package/{careful → skills/careful}/SKILL.md +0 -0
- /package/{careful → skills/careful}/SKILL.md.tmpl +0 -0
- /package/{codex → skills/codex}/SKILL.md +0 -0
- /package/{codex → skills/codex}/SKILL.md.tmpl +0 -0
- /package/{connect-chrome → skills/connect-chrome}/SKILL.md +0 -0
- /package/{connect-chrome → skills/connect-chrome}/SKILL.md.tmpl +0 -0
- /package/{cso → skills/cso}/SKILL.md +0 -0
- /package/{cso → skills/cso}/SKILL.md.tmpl +0 -0
- /package/{design-consultation → skills/design-consultation}/SKILL.md +0 -0
- /package/{design-consultation → skills/design-consultation}/SKILL.md.tmpl +0 -0
- /package/{design-review → skills/design-review}/SKILL.md +0 -0
- /package/{design-review → skills/design-review}/SKILL.md.tmpl +0 -0
- /package/{design-shotgun → skills/design-shotgun}/SKILL.md +0 -0
- /package/{design-shotgun → skills/design-shotgun}/SKILL.md.tmpl +0 -0
- /package/{document-release → skills/document-release}/SKILL.md +0 -0
- /package/{document-release → skills/document-release}/SKILL.md.tmpl +0 -0
- /package/{freeze → skills/freeze}/SKILL.md +0 -0
- /package/{freeze → skills/freeze}/SKILL.md.tmpl +0 -0
- /package/{gstack-upgrade → skills/gstack-upgrade}/SKILL.md +0 -0
- /package/{gstack-upgrade → skills/gstack-upgrade}/SKILL.md.tmpl +0 -0
- /package/{guard → skills/guard}/SKILL.md +0 -0
- /package/{guard → skills/guard}/SKILL.md.tmpl +0 -0
- /package/{investigate → skills/investigate}/SKILL.md +0 -0
- /package/{investigate → skills/investigate}/SKILL.md.tmpl +0 -0
- /package/{land-and-deploy → skills/land-and-deploy}/SKILL.md +0 -0
- /package/{land-and-deploy → skills/land-and-deploy}/SKILL.md.tmpl +0 -0
- /package/{office-hours → skills/office-hours}/SKILL.md +0 -0
- /package/{office-hours → skills/office-hours}/SKILL.md.tmpl +0 -0
- /package/{plan-ceo-review → skills/plan-ceo-review}/SKILL.md +0 -0
- /package/{plan-ceo-review → skills/plan-ceo-review}/SKILL.md.tmpl +0 -0
- /package/{plan-design-review → skills/plan-design-review}/SKILL.md +0 -0
- /package/{plan-design-review → skills/plan-design-review}/SKILL.md.tmpl +0 -0
- /package/{plan-eng-review → skills/plan-eng-review}/SKILL.md +0 -0
- /package/{plan-eng-review → skills/plan-eng-review}/SKILL.md.tmpl +0 -0
- /package/{qa → skills/qa}/SKILL.md +0 -0
- /package/{qa → skills/qa}/SKILL.md.tmpl +0 -0
- /package/{qa-only → skills/qa-only}/SKILL.md +0 -0
- /package/{qa-only → skills/qa-only}/SKILL.md.tmpl +0 -0
- /package/{retro → skills/retro}/SKILL.md +0 -0
- /package/{retro → skills/retro}/SKILL.md.tmpl +0 -0
- /package/{review → skills/review}/SKILL.md +0 -0
- /package/{review → skills/review}/SKILL.md.tmpl +0 -0
- /package/{setup-browser-cookies → skills/setup-browser-cookies}/SKILL.md +0 -0
- /package/{setup-browser-cookies → skills/setup-browser-cookies}/SKILL.md.tmpl +0 -0
- /package/{setup-deploy → skills/setup-deploy}/SKILL.md +0 -0
- /package/{setup-deploy → skills/setup-deploy}/SKILL.md.tmpl +0 -0
- /package/{ship → skills/ship}/SKILL.md +0 -0
- /package/{ship → skills/ship}/SKILL.md.tmpl +0 -0
- /package/{unfreeze → skills/unfreeze}/SKILL.md +0 -0
- /package/{unfreeze → skills/unfreeze}/SKILL.md.tmpl +0 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie picker route handler — HTTP + Playwright glue
|
|
3
|
+
*
|
|
4
|
+
* Handles all /cookie-picker/* routes. Imports from cookie-import-browser.ts
|
|
5
|
+
* (decryption) and cookie-picker-ui.ts (HTML generation).
|
|
6
|
+
*
|
|
7
|
+
* Routes (no auth — localhost-only, accepted risk):
|
|
8
|
+
* GET /cookie-picker → serves the picker HTML page
|
|
9
|
+
* GET /cookie-picker/browsers → list installed browsers
|
|
10
|
+
* GET /cookie-picker/domains → list domains + counts for a browser
|
|
11
|
+
* POST /cookie-picker/import → decrypt + import cookies to Playwright
|
|
12
|
+
* POST /cookie-picker/remove → clear cookies for domains
|
|
13
|
+
* GET /cookie-picker/imported → currently imported domains + counts
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { BrowserManager } from './browser-manager';
|
|
17
|
+
import { findInstalledBrowsers, listProfiles, listDomains, importCookies, CookieImportError, type PlaywrightCookie } from './cookie-import-browser';
|
|
18
|
+
import { getCookiePickerHTML } from './cookie-picker-ui';
|
|
19
|
+
|
|
20
|
+
// ─── State ──────────────────────────────────────────────────────
|
|
21
|
+
// Tracks which domains were imported via the picker.
|
|
22
|
+
// /imported only returns cookies for domains in this Set.
|
|
23
|
+
// /remove clears from this Set.
|
|
24
|
+
const importedDomains = new Set<string>();
|
|
25
|
+
const importedCounts = new Map<string, number>();
|
|
26
|
+
|
|
27
|
+
// ─── JSON Helpers ───────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
function corsOrigin(port: number): string {
|
|
30
|
+
return `http://127.0.0.1:${port}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function jsonResponse(data: any, opts: { port: number; status?: number }): Response {
|
|
34
|
+
return new Response(JSON.stringify(data), {
|
|
35
|
+
status: opts.status ?? 200,
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
'Access-Control-Allow-Origin': corsOrigin(opts.port),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function errorResponse(message: string, code: string, opts: { port: number; status?: number; action?: string }): Response {
|
|
44
|
+
return jsonResponse(
|
|
45
|
+
{ error: message, code, ...(opts.action ? { action: opts.action } : {}) },
|
|
46
|
+
{ port: opts.port, status: opts.status ?? 400 },
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── Route Handler ──────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
export async function handleCookiePickerRoute(
|
|
53
|
+
url: URL,
|
|
54
|
+
req: Request,
|
|
55
|
+
bm: BrowserManager,
|
|
56
|
+
authToken?: string,
|
|
57
|
+
): Promise<Response> {
|
|
58
|
+
const pathname = url.pathname;
|
|
59
|
+
const port = parseInt(url.port, 10) || 9400;
|
|
60
|
+
|
|
61
|
+
// CORS preflight
|
|
62
|
+
if (req.method === 'OPTIONS') {
|
|
63
|
+
return new Response(null, {
|
|
64
|
+
status: 204,
|
|
65
|
+
headers: {
|
|
66
|
+
'Access-Control-Allow-Origin': corsOrigin(port),
|
|
67
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
68
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// GET /cookie-picker — serve the picker UI
|
|
75
|
+
if (pathname === '/cookie-picker' && req.method === 'GET') {
|
|
76
|
+
const html = getCookiePickerHTML(port, authToken);
|
|
77
|
+
return new Response(html, {
|
|
78
|
+
status: 200,
|
|
79
|
+
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ─── Auth gate: all data/action routes below require Bearer token ───
|
|
84
|
+
if (authToken) {
|
|
85
|
+
const authHeader = req.headers.get('authorization');
|
|
86
|
+
if (!authHeader || authHeader !== `Bearer ${authToken}`) {
|
|
87
|
+
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
|
88
|
+
status: 401,
|
|
89
|
+
headers: { 'Content-Type': 'application/json' },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// GET /cookie-picker/browsers — list installed browsers
|
|
95
|
+
if (pathname === '/cookie-picker/browsers' && req.method === 'GET') {
|
|
96
|
+
const browsers = findInstalledBrowsers();
|
|
97
|
+
return jsonResponse({
|
|
98
|
+
browsers: browsers.map(b => ({
|
|
99
|
+
name: b.name,
|
|
100
|
+
aliases: b.aliases,
|
|
101
|
+
})),
|
|
102
|
+
}, { port });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// GET /cookie-picker/profiles?browser=<name> — list profiles for a browser
|
|
106
|
+
if (pathname === '/cookie-picker/profiles' && req.method === 'GET') {
|
|
107
|
+
const browserName = url.searchParams.get('browser');
|
|
108
|
+
if (!browserName) {
|
|
109
|
+
return errorResponse("Missing 'browser' parameter", 'missing_param', { port });
|
|
110
|
+
}
|
|
111
|
+
const profiles = listProfiles(browserName);
|
|
112
|
+
return jsonResponse({ profiles }, { port });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// GET /cookie-picker/domains?browser=<name>&profile=<profile> — list domains + counts
|
|
116
|
+
if (pathname === '/cookie-picker/domains' && req.method === 'GET') {
|
|
117
|
+
const browserName = url.searchParams.get('browser');
|
|
118
|
+
if (!browserName) {
|
|
119
|
+
return errorResponse("Missing 'browser' parameter", 'missing_param', { port });
|
|
120
|
+
}
|
|
121
|
+
const profile = url.searchParams.get('profile') || 'Default';
|
|
122
|
+
const result = listDomains(browserName, profile);
|
|
123
|
+
return jsonResponse({
|
|
124
|
+
browser: result.browser,
|
|
125
|
+
domains: result.domains,
|
|
126
|
+
}, { port });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// POST /cookie-picker/import — decrypt + import to Playwright session
|
|
130
|
+
if (pathname === '/cookie-picker/import' && req.method === 'POST') {
|
|
131
|
+
let body: any;
|
|
132
|
+
try {
|
|
133
|
+
body = await req.json();
|
|
134
|
+
} catch {
|
|
135
|
+
return errorResponse('Invalid JSON body', 'bad_request', { port });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { browser, domains, profile } = body;
|
|
139
|
+
if (!browser) return errorResponse("Missing 'browser' field", 'missing_param', { port });
|
|
140
|
+
if (!domains || !Array.isArray(domains) || domains.length === 0) {
|
|
141
|
+
return errorResponse("Missing or empty 'domains' array", 'missing_param', { port });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Decrypt cookies from the browser DB
|
|
145
|
+
const result = await importCookies(browser, domains, profile || 'Default');
|
|
146
|
+
|
|
147
|
+
if (result.cookies.length === 0) {
|
|
148
|
+
return jsonResponse({
|
|
149
|
+
imported: 0,
|
|
150
|
+
failed: result.failed,
|
|
151
|
+
domainCounts: {},
|
|
152
|
+
message: result.failed > 0
|
|
153
|
+
? `All ${result.failed} cookies failed to decrypt`
|
|
154
|
+
: 'No cookies found for the specified domains',
|
|
155
|
+
}, { port });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Add to Playwright context
|
|
159
|
+
const page = bm.getPage();
|
|
160
|
+
await page.context().addCookies(result.cookies);
|
|
161
|
+
|
|
162
|
+
// Track what was imported
|
|
163
|
+
for (const domain of Object.keys(result.domainCounts)) {
|
|
164
|
+
importedDomains.add(domain);
|
|
165
|
+
importedCounts.set(domain, (importedCounts.get(domain) || 0) + result.domainCounts[domain]);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.log(`[cookie-picker] Imported ${result.count} cookies for ${Object.keys(result.domainCounts).length} domains`);
|
|
169
|
+
|
|
170
|
+
return jsonResponse({
|
|
171
|
+
imported: result.count,
|
|
172
|
+
failed: result.failed,
|
|
173
|
+
domainCounts: result.domainCounts,
|
|
174
|
+
}, { port });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// POST /cookie-picker/remove — clear cookies for domains
|
|
178
|
+
if (pathname === '/cookie-picker/remove' && req.method === 'POST') {
|
|
179
|
+
let body: any;
|
|
180
|
+
try {
|
|
181
|
+
body = await req.json();
|
|
182
|
+
} catch {
|
|
183
|
+
return errorResponse('Invalid JSON body', 'bad_request', { port });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const { domains } = body;
|
|
187
|
+
if (!domains || !Array.isArray(domains) || domains.length === 0) {
|
|
188
|
+
return errorResponse("Missing or empty 'domains' array", 'missing_param', { port });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const page = bm.getPage();
|
|
192
|
+
const context = page.context();
|
|
193
|
+
for (const domain of domains) {
|
|
194
|
+
await context.clearCookies({ domain });
|
|
195
|
+
importedDomains.delete(domain);
|
|
196
|
+
importedCounts.delete(domain);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(`[cookie-picker] Removed cookies for ${domains.length} domains`);
|
|
200
|
+
|
|
201
|
+
return jsonResponse({
|
|
202
|
+
removed: domains.length,
|
|
203
|
+
domains,
|
|
204
|
+
}, { port });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// GET /cookie-picker/imported — currently imported domains + counts
|
|
208
|
+
if (pathname === '/cookie-picker/imported' && req.method === 'GET') {
|
|
209
|
+
const entries: Array<{ domain: string; count: number }> = [];
|
|
210
|
+
for (const domain of importedDomains) {
|
|
211
|
+
entries.push({ domain, count: importedCounts.get(domain) || 0 });
|
|
212
|
+
}
|
|
213
|
+
entries.sort((a, b) => b.count - a.count);
|
|
214
|
+
|
|
215
|
+
return jsonResponse({
|
|
216
|
+
domains: entries,
|
|
217
|
+
totalDomains: entries.length,
|
|
218
|
+
totalCookies: entries.reduce((sum, e) => sum + e.count, 0),
|
|
219
|
+
}, { port });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return new Response('Not found', { status: 404 });
|
|
223
|
+
} catch (err: any) {
|
|
224
|
+
if (err instanceof CookieImportError) {
|
|
225
|
+
return errorResponse(err.message, err.code, { port, status: 400, action: err.action });
|
|
226
|
+
}
|
|
227
|
+
console.error(`[cookie-picker] Error: ${err.message}`);
|
|
228
|
+
return errorResponse(err.message || 'Internal error', 'internal_error', { port, status: 500 });
|
|
229
|
+
}
|
|
230
|
+
}
|