@xbrowser/cli 0.14.0 → 0.14.1

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.
@@ -0,0 +1,28 @@
1
+ import "./chunk-3RG5ZIWI.js";
2
+
3
+ // src/hooks/screenshot.ts
4
+ var screenshotHook = {
5
+ name: "screenshot",
6
+ async onAfterCommand(ctx) {
7
+ try {
8
+ const quality = parseInt(process.env.XBROWSER_SCREENSHOT_QUALITY || "40");
9
+ const buffer = await ctx.page.screenshot({
10
+ type: "jpeg",
11
+ quality: Math.max(10, Math.min(100, quality))
12
+ });
13
+ const entry = {
14
+ step: ctx.command,
15
+ command: ctx.command,
16
+ base64: buffer.toString("base64"),
17
+ url: ctx.page.url(),
18
+ timestamp: Date.now()
19
+ };
20
+ return { screenshot: entry };
21
+ } catch {
22
+ return void 0;
23
+ }
24
+ }
25
+ };
26
+ export {
27
+ screenshotHook
28
+ };
@@ -0,0 +1,26 @@
1
+ // src/hooks/screenshot.ts
2
+ var screenshotHook = {
3
+ name: "screenshot",
4
+ async onAfterCommand(ctx) {
5
+ try {
6
+ const quality = parseInt(process.env.XBROWSER_SCREENSHOT_QUALITY || "40");
7
+ const buffer = await ctx.page.screenshot({
8
+ type: "jpeg",
9
+ quality: Math.max(10, Math.min(100, quality))
10
+ });
11
+ const entry = {
12
+ step: ctx.command,
13
+ command: ctx.command,
14
+ base64: buffer.toString("base64"),
15
+ url: ctx.page.url(),
16
+ timestamp: Date.now()
17
+ };
18
+ return { screenshot: entry };
19
+ } catch {
20
+ return void 0;
21
+ }
22
+ }
23
+ };
24
+ export {
25
+ screenshotHook
26
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "A self-contained browser automation CLI tool — navigate, click, fill forms, extract data, record & replay sessions via YAML scripts",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,281 +0,0 @@
1
- import {
2
- ensureProxyFetch,
3
- getRegistryUrl,
4
- loadAuth
5
- } from "./chunk-43VX3TYN.js";
6
- import "./chunk-YEN2ODUI.js";
7
-
8
- // src/plugin/builtins/admin.ts
9
- import { z } from "zod";
10
- var adminResult = z.object({
11
- success: z.boolean(),
12
- data: z.record(z.unknown()).optional(),
13
- message: z.string().optional()
14
- });
15
- function requireAuth(options) {
16
- const auth = loadAuth();
17
- if (!auth?.token) {
18
- throw new Error("Not logged in. Run: xbrowser marketplace login");
19
- }
20
- const registryUrl = getRegistryUrl(options, auth.registry);
21
- return { token: auth.token, registryUrl };
22
- }
23
- async function adminFetch(url, token, init) {
24
- await ensureProxyFetch();
25
- const res = await fetch(url, {
26
- ...init,
27
- headers: {
28
- Authorization: `Bearer ${token}`,
29
- ...init?.headers
30
- }
31
- });
32
- if (res.status === 403) {
33
- throw new Error("Forbidden: admin access required");
34
- }
35
- if (!res.ok) {
36
- const errBody = await res.json().catch(() => ({}));
37
- throw new Error(
38
- `Request failed (${res.status}): ${errBody.error || errBody.message || res.statusText}`
39
- );
40
- }
41
- return await res.json();
42
- }
43
- function formatPluginRow(p) {
44
- const name = p.name || "?";
45
- const slug = p.slug || "?";
46
- const status = p.status || "?";
47
- const version = p.version || "?";
48
- const featured = p.featured ? " [featured]" : "";
49
- const author = p.author || p.developer || "";
50
- return ` ${slug.padEnd(25)} ${name.padEnd(30)} v${version.padEnd(10)} ${status}${featured}${author ? ` by ${author}` : ""}`;
51
- }
52
- function setupAdminPlugin(xcli) {
53
- const site = xcli.createSite({
54
- name: "admin",
55
- url: "https://xbrowser.dev",
56
- description: "xbrowser marketplace admin tools"
57
- });
58
- const cmd = (name, config) => site.command(name, config);
59
- cmd("pending", {
60
- description: "List pending plugins",
61
- scope: "global",
62
- parameters: z.object({
63
- registry: z.string().optional().describe("Custom registry URL")
64
- }),
65
- result: adminResult,
66
- handler: async (params) => {
67
- const { token, registryUrl } = requireAuth(params);
68
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/pending`, token);
69
- const plugins = body.data || [];
70
- const lines = [];
71
- if (plugins.length === 0) {
72
- lines.push("No pending plugins");
73
- } else {
74
- lines.push(`
75
- Pending plugins (${plugins.length}):
76
- `);
77
- for (const p of plugins) {
78
- lines.push(formatPluginRow(p));
79
- if (p.description) lines.push(` ${p.description}`);
80
- }
81
- lines.push("");
82
- }
83
- return { success: true, data: { plugins, total: plugins.length, text: lines.join("\n") } };
84
- }
85
- });
86
- cmd("approve", {
87
- description: "Approve a plugin",
88
- scope: "global",
89
- parameters: z.object({
90
- slug: z.string().describe("Plugin slug to approve"),
91
- registry: z.string().optional().describe("Custom registry URL")
92
- }),
93
- result: adminResult,
94
- handler: async (params) => {
95
- const slug = params.slug;
96
- const { token, registryUrl } = requireAuth(params);
97
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/${slug}/approve`, token, { method: "PUT" });
98
- const data = body.data;
99
- const text = `Approved: ${slug}${data?.name ? `
100
- Name: ${data.name}` : ""}`;
101
- return { success: true, data: { ok: true, slug, ...data, text } };
102
- }
103
- });
104
- cmd("reject", {
105
- description: "Reject a plugin",
106
- scope: "global",
107
- parameters: z.object({
108
- slug: z.string().describe("Plugin slug to reject"),
109
- reason: z.string().optional().describe("Rejection reason"),
110
- registry: z.string().optional().describe("Custom registry URL")
111
- }),
112
- result: adminResult,
113
- handler: async (params) => {
114
- const slug = params.slug;
115
- const reason = params.reason;
116
- const { token, registryUrl } = requireAuth(params);
117
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/${slug}/reject`, token, {
118
- method: "PUT",
119
- headers: { "Content-Type": "application/json" },
120
- body: JSON.stringify({ reason })
121
- });
122
- const data = body.data;
123
- const text = `Rejected: ${slug}${reason ? `
124
- Reason: ${reason}` : ""}`;
125
- return { success: true, data: { ok: true, slug, ...data, text } };
126
- }
127
- });
128
- cmd("feature", {
129
- description: "Toggle featured status",
130
- scope: "global",
131
- parameters: z.object({
132
- slug: z.string().describe("Plugin slug"),
133
- registry: z.string().optional().describe("Custom registry URL")
134
- }),
135
- result: adminResult,
136
- handler: async (params) => {
137
- const slug = params.slug;
138
- const { token, registryUrl } = requireAuth(params);
139
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/${slug}/feature`, token, { method: "PUT" });
140
- const data = body.data;
141
- const featured = data?.featured ? "featured" : "unfeatured";
142
- const text = `Toggled featured: ${slug} -> ${featured}`;
143
- return { success: true, data: { ok: true, slug, ...data, text } };
144
- }
145
- });
146
- cmd("remove", {
147
- description: "Remove a plugin",
148
- scope: "global",
149
- parameters: z.object({
150
- slug: z.string().describe("Plugin slug to remove"),
151
- registry: z.string().optional().describe("Custom registry URL")
152
- }),
153
- result: adminResult,
154
- handler: async (params) => {
155
- const slug = params.slug;
156
- const { token, registryUrl } = requireAuth(params);
157
- await adminFetch(`${registryUrl}/api/admin/plugins/${slug}`, token, { method: "DELETE" });
158
- return { success: true, data: { ok: true, slug, text: `Removed: ${slug}` } };
159
- }
160
- });
161
- cmd("stats", {
162
- description: "Dashboard stats",
163
- scope: "global",
164
- parameters: z.object({
165
- registry: z.string().optional().describe("Custom registry URL")
166
- }),
167
- result: adminResult,
168
- handler: async (params) => {
169
- const { token, registryUrl } = requireAuth(params);
170
- const body = await adminFetch(`${registryUrl}/api/admin/stats/dashboard`, token);
171
- const data = body.data;
172
- const lines = ["\nDashboard Stats:\n"];
173
- if (data) {
174
- for (const [k, v] of Object.entries(data)) {
175
- lines.push(` ${k}: ${typeof v === "object" ? JSON.stringify(v) : v}`);
176
- }
177
- }
178
- lines.push("");
179
- return { success: true, data: { ...data, text: lines.join("\n") } };
180
- }
181
- });
182
- cmd("inventory", {
183
- description: "Full plugin inventory",
184
- scope: "global",
185
- parameters: z.object({
186
- registry: z.string().optional().describe("Custom registry URL")
187
- }),
188
- result: adminResult,
189
- handler: async (params) => {
190
- const { token, registryUrl } = requireAuth(params);
191
- const body = await adminFetch(`${registryUrl}/api/admin/stats/inventory`, token);
192
- const plugins = body.data || [];
193
- const lines = [];
194
- if (plugins.length === 0) {
195
- lines.push("No plugins in inventory");
196
- } else {
197
- lines.push(`
198
- Plugin Inventory (${plugins.length}):
199
- `);
200
- for (const p of plugins) lines.push(formatPluginRow(p));
201
- lines.push("");
202
- }
203
- return { success: true, data: { plugins, total: plugins.length, text: lines.join("\n") } };
204
- }
205
- });
206
- cmd("list", {
207
- description: "List all plugins",
208
- scope: "global",
209
- parameters: z.object({
210
- status: z.string().optional().describe("Filter by status"),
211
- registry: z.string().optional().describe("Custom registry URL")
212
- }),
213
- result: adminResult,
214
- handler: async (params) => {
215
- const status = params.status;
216
- const { token, registryUrl } = requireAuth(params);
217
- const url = new URL(`${registryUrl}/api/admin/plugins`);
218
- if (status) url.searchParams.set("status", status);
219
- const body = await adminFetch(url.toString(), token);
220
- const plugins = body.data || [];
221
- const lines = [];
222
- if (plugins.length === 0) {
223
- lines.push(status ? `No plugins with status "${status}"` : "No plugins");
224
- } else {
225
- lines.push(`
226
- Plugins${status ? ` [${status}]` : ""} (${plugins.length}):
227
- `);
228
- for (const p of plugins) lines.push(formatPluginRow(p));
229
- lines.push("");
230
- }
231
- return { success: true, data: { plugins, total: plugins.length, status, text: lines.join("\n") } };
232
- }
233
- });
234
- cmd("bulk-approve", {
235
- description: "Bulk approve plugins",
236
- scope: "global",
237
- parameters: z.object({
238
- slugs: z.union([z.string(), z.array(z.string())]).describe("Plugin slugs to approve"),
239
- registry: z.string().optional().describe("Custom registry URL")
240
- }),
241
- result: adminResult,
242
- handler: async (params) => {
243
- const slugs = Array.isArray(params.slugs) ? params.slugs : [params.slugs];
244
- if (slugs.length === 0) {
245
- return { success: false, message: "Usage: xbrowser admin bulk-approve <slug1> <slug2> ..." };
246
- }
247
- const { token, registryUrl } = requireAuth(params);
248
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/bulk-approve`, token, {
249
- method: "POST",
250
- headers: { "Content-Type": "application/json" },
251
- body: JSON.stringify({ slugs })
252
- });
253
- const data = body.data;
254
- const approved = data?.approved || slugs;
255
- const lines = [`Bulk approved ${approved.length} plugins:`];
256
- for (const s of approved) lines.push(` - ${s}`);
257
- return { success: true, data: { ok: true, slugs, ...data, text: lines.join("\n") } };
258
- }
259
- });
260
- cmd("cleanup", {
261
- description: "Reset fake data / cleanup database",
262
- scope: "global",
263
- parameters: z.object({
264
- registry: z.string().optional().describe("Custom registry URL")
265
- }),
266
- result: adminResult,
267
- handler: async (params) => {
268
- const { token, registryUrl } = requireAuth(params);
269
- const body = await adminFetch(`${registryUrl}/api/admin/db/cleanup`, token, { method: "POST" });
270
- const data = body.data;
271
- const lines = ["Cleanup completed"];
272
- if (data) {
273
- for (const [k, v] of Object.entries(data)) lines.push(` ${k}: ${v}`);
274
- }
275
- return { success: true, data: { ok: true, ...data, text: lines.join("\n") } };
276
- }
277
- });
278
- }
279
- export {
280
- setupAdminPlugin as default
281
- };
@@ -1,285 +0,0 @@
1
- import {
2
- loadAuth
3
- } from "./chunk-KTSQU4QT.js";
4
- import {
5
- ensureProxyFetch
6
- } from "./chunk-VEKPHQBR.js";
7
- import {
8
- getRegistryUrl
9
- } from "./chunk-M7CMBPCA.js";
10
- import "./chunk-3RG5ZIWI.js";
11
-
12
- // src/plugin/builtins/admin.ts
13
- import { z } from "zod";
14
- var adminResult = z.object({
15
- success: z.boolean(),
16
- data: z.record(z.unknown()).optional(),
17
- message: z.string().optional()
18
- });
19
- function requireAuth(options) {
20
- const auth = loadAuth();
21
- if (!auth?.token) {
22
- throw new Error("Not logged in. Run: xbrowser marketplace login");
23
- }
24
- const registryUrl = getRegistryUrl(options, auth.registry);
25
- return { token: auth.token, registryUrl };
26
- }
27
- async function adminFetch(url, token, init) {
28
- await ensureProxyFetch();
29
- const res = await fetch(url, {
30
- ...init,
31
- headers: {
32
- Authorization: `Bearer ${token}`,
33
- ...init?.headers
34
- }
35
- });
36
- if (res.status === 403) {
37
- throw new Error("Forbidden: admin access required");
38
- }
39
- if (!res.ok) {
40
- const errBody = await res.json().catch(() => ({}));
41
- throw new Error(
42
- `Request failed (${res.status}): ${errBody.error || errBody.message || res.statusText}`
43
- );
44
- }
45
- return await res.json();
46
- }
47
- function formatPluginRow(p) {
48
- const name = p.name || "?";
49
- const slug = p.slug || "?";
50
- const status = p.status || "?";
51
- const version = p.version || "?";
52
- const featured = p.featured ? " [featured]" : "";
53
- const author = p.author || p.developer || "";
54
- return ` ${slug.padEnd(25)} ${name.padEnd(30)} v${version.padEnd(10)} ${status}${featured}${author ? ` by ${author}` : ""}`;
55
- }
56
- function setupAdminPlugin(xcli) {
57
- const site = xcli.createSite({
58
- name: "admin",
59
- url: "https://xbrowser.dev",
60
- description: "xbrowser marketplace admin tools"
61
- });
62
- const cmd = (name, config) => site.command(name, config);
63
- cmd("pending", {
64
- description: "List pending plugins",
65
- scope: "global",
66
- parameters: z.object({
67
- registry: z.string().optional().describe("Custom registry URL")
68
- }),
69
- result: adminResult,
70
- handler: async (params) => {
71
- const { token, registryUrl } = requireAuth(params);
72
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/pending`, token);
73
- const plugins = body.data || [];
74
- const lines = [];
75
- if (plugins.length === 0) {
76
- lines.push("No pending plugins");
77
- } else {
78
- lines.push(`
79
- Pending plugins (${plugins.length}):
80
- `);
81
- for (const p of plugins) {
82
- lines.push(formatPluginRow(p));
83
- if (p.description) lines.push(` ${p.description}`);
84
- }
85
- lines.push("");
86
- }
87
- return { success: true, data: { plugins, total: plugins.length, text: lines.join("\n") } };
88
- }
89
- });
90
- cmd("approve", {
91
- description: "Approve a plugin",
92
- scope: "global",
93
- parameters: z.object({
94
- slug: z.string().describe("Plugin slug to approve"),
95
- registry: z.string().optional().describe("Custom registry URL")
96
- }),
97
- result: adminResult,
98
- handler: async (params) => {
99
- const slug = params.slug;
100
- const { token, registryUrl } = requireAuth(params);
101
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/${slug}/approve`, token, { method: "PUT" });
102
- const data = body.data;
103
- const text = `Approved: ${slug}${data?.name ? `
104
- Name: ${data.name}` : ""}`;
105
- return { success: true, data: { ok: true, slug, ...data, text } };
106
- }
107
- });
108
- cmd("reject", {
109
- description: "Reject a plugin",
110
- scope: "global",
111
- parameters: z.object({
112
- slug: z.string().describe("Plugin slug to reject"),
113
- reason: z.string().optional().describe("Rejection reason"),
114
- registry: z.string().optional().describe("Custom registry URL")
115
- }),
116
- result: adminResult,
117
- handler: async (params) => {
118
- const slug = params.slug;
119
- const reason = params.reason;
120
- const { token, registryUrl } = requireAuth(params);
121
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/${slug}/reject`, token, {
122
- method: "PUT",
123
- headers: { "Content-Type": "application/json" },
124
- body: JSON.stringify({ reason })
125
- });
126
- const data = body.data;
127
- const text = `Rejected: ${slug}${reason ? `
128
- Reason: ${reason}` : ""}`;
129
- return { success: true, data: { ok: true, slug, ...data, text } };
130
- }
131
- });
132
- cmd("feature", {
133
- description: "Toggle featured status",
134
- scope: "global",
135
- parameters: z.object({
136
- slug: z.string().describe("Plugin slug"),
137
- registry: z.string().optional().describe("Custom registry URL")
138
- }),
139
- result: adminResult,
140
- handler: async (params) => {
141
- const slug = params.slug;
142
- const { token, registryUrl } = requireAuth(params);
143
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/${slug}/feature`, token, { method: "PUT" });
144
- const data = body.data;
145
- const featured = data?.featured ? "featured" : "unfeatured";
146
- const text = `Toggled featured: ${slug} -> ${featured}`;
147
- return { success: true, data: { ok: true, slug, ...data, text } };
148
- }
149
- });
150
- cmd("remove", {
151
- description: "Remove a plugin",
152
- scope: "global",
153
- parameters: z.object({
154
- slug: z.string().describe("Plugin slug to remove"),
155
- registry: z.string().optional().describe("Custom registry URL")
156
- }),
157
- result: adminResult,
158
- handler: async (params) => {
159
- const slug = params.slug;
160
- const { token, registryUrl } = requireAuth(params);
161
- await adminFetch(`${registryUrl}/api/admin/plugins/${slug}`, token, { method: "DELETE" });
162
- return { success: true, data: { ok: true, slug, text: `Removed: ${slug}` } };
163
- }
164
- });
165
- cmd("stats", {
166
- description: "Dashboard stats",
167
- scope: "global",
168
- parameters: z.object({
169
- registry: z.string().optional().describe("Custom registry URL")
170
- }),
171
- result: adminResult,
172
- handler: async (params) => {
173
- const { token, registryUrl } = requireAuth(params);
174
- const body = await adminFetch(`${registryUrl}/api/admin/stats/dashboard`, token);
175
- const data = body.data;
176
- const lines = ["\nDashboard Stats:\n"];
177
- if (data) {
178
- for (const [k, v] of Object.entries(data)) {
179
- lines.push(` ${k}: ${typeof v === "object" ? JSON.stringify(v) : v}`);
180
- }
181
- }
182
- lines.push("");
183
- return { success: true, data: { ...data, text: lines.join("\n") } };
184
- }
185
- });
186
- cmd("inventory", {
187
- description: "Full plugin inventory",
188
- scope: "global",
189
- parameters: z.object({
190
- registry: z.string().optional().describe("Custom registry URL")
191
- }),
192
- result: adminResult,
193
- handler: async (params) => {
194
- const { token, registryUrl } = requireAuth(params);
195
- const body = await adminFetch(`${registryUrl}/api/admin/stats/inventory`, token);
196
- const plugins = body.data || [];
197
- const lines = [];
198
- if (plugins.length === 0) {
199
- lines.push("No plugins in inventory");
200
- } else {
201
- lines.push(`
202
- Plugin Inventory (${plugins.length}):
203
- `);
204
- for (const p of plugins) lines.push(formatPluginRow(p));
205
- lines.push("");
206
- }
207
- return { success: true, data: { plugins, total: plugins.length, text: lines.join("\n") } };
208
- }
209
- });
210
- cmd("list", {
211
- description: "List all plugins",
212
- scope: "global",
213
- parameters: z.object({
214
- status: z.string().optional().describe("Filter by status"),
215
- registry: z.string().optional().describe("Custom registry URL")
216
- }),
217
- result: adminResult,
218
- handler: async (params) => {
219
- const status = params.status;
220
- const { token, registryUrl } = requireAuth(params);
221
- const url = new URL(`${registryUrl}/api/admin/plugins`);
222
- if (status) url.searchParams.set("status", status);
223
- const body = await adminFetch(url.toString(), token);
224
- const plugins = body.data || [];
225
- const lines = [];
226
- if (plugins.length === 0) {
227
- lines.push(status ? `No plugins with status "${status}"` : "No plugins");
228
- } else {
229
- lines.push(`
230
- Plugins${status ? ` [${status}]` : ""} (${plugins.length}):
231
- `);
232
- for (const p of plugins) lines.push(formatPluginRow(p));
233
- lines.push("");
234
- }
235
- return { success: true, data: { plugins, total: plugins.length, status, text: lines.join("\n") } };
236
- }
237
- });
238
- cmd("bulk-approve", {
239
- description: "Bulk approve plugins",
240
- scope: "global",
241
- parameters: z.object({
242
- slugs: z.union([z.string(), z.array(z.string())]).describe("Plugin slugs to approve"),
243
- registry: z.string().optional().describe("Custom registry URL")
244
- }),
245
- result: adminResult,
246
- handler: async (params) => {
247
- const slugs = Array.isArray(params.slugs) ? params.slugs : [params.slugs];
248
- if (slugs.length === 0) {
249
- return { success: false, message: "Usage: xbrowser admin bulk-approve <slug1> <slug2> ..." };
250
- }
251
- const { token, registryUrl } = requireAuth(params);
252
- const body = await adminFetch(`${registryUrl}/api/admin/plugins/bulk-approve`, token, {
253
- method: "POST",
254
- headers: { "Content-Type": "application/json" },
255
- body: JSON.stringify({ slugs })
256
- });
257
- const data = body.data;
258
- const approved = data?.approved || slugs;
259
- const lines = [`Bulk approved ${approved.length} plugins:`];
260
- for (const s of approved) lines.push(` - ${s}`);
261
- return { success: true, data: { ok: true, slugs, ...data, text: lines.join("\n") } };
262
- }
263
- });
264
- cmd("cleanup", {
265
- description: "Reset fake data / cleanup database",
266
- scope: "global",
267
- parameters: z.object({
268
- registry: z.string().optional().describe("Custom registry URL")
269
- }),
270
- result: adminResult,
271
- handler: async (params) => {
272
- const { token, registryUrl } = requireAuth(params);
273
- const body = await adminFetch(`${registryUrl}/api/admin/db/cleanup`, token, { method: "POST" });
274
- const data = body.data;
275
- const lines = ["Cleanup completed"];
276
- if (data) {
277
- for (const [k, v] of Object.entries(data)) lines.push(` ${k}: ${v}`);
278
- }
279
- return { success: true, data: { ok: true, ...data, text: lines.join("\n") } };
280
- }
281
- });
282
- }
283
- export {
284
- setupAdminPlugin as default
285
- };