@commandgarden/cli 1.3.0 → 1.4.0
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/dist/main.js +43 -8
- package/node_modules/@commandgarden/chrome/dist/content-script.js +107 -0
- package/node_modules/@commandgarden/chrome/dist/content-script.js.map +7 -0
- package/node_modules/@commandgarden/chrome/dist/fetch-interceptor.js +44 -0
- package/node_modules/@commandgarden/chrome/dist/manifest.json +16 -0
- package/node_modules/@commandgarden/chrome/dist/popup.html +197 -0
- package/node_modules/@commandgarden/chrome/dist/popup.js +112 -0
- package/node_modules/@commandgarden/chrome/dist/popup.js.map +7 -0
- package/node_modules/@commandgarden/chrome/dist/service-worker.js +7444 -0
- package/node_modules/@commandgarden/chrome/dist/service-worker.js.map +7 -0
- package/node_modules/@commandgarden/chrome/package.json +25 -0
- package/package.json +3 -1
package/dist/main.js
CHANGED
|
@@ -12201,12 +12201,12 @@ var {
|
|
|
12201
12201
|
} = import_index.default;
|
|
12202
12202
|
|
|
12203
12203
|
// src/main.ts
|
|
12204
|
-
import { join as join4, dirname as
|
|
12204
|
+
import { join as join4, dirname as dirname3, resolve } from "path";
|
|
12205
12205
|
import { readFileSync as readFileSync8 } from "fs";
|
|
12206
12206
|
import { homedir } from "os";
|
|
12207
12207
|
import { fileURLToPath } from "url";
|
|
12208
12208
|
|
|
12209
|
-
//
|
|
12209
|
+
// node_modules/@commandgarden/shared/dist/capabilities.js
|
|
12210
12210
|
var CAPABILITIES = [
|
|
12211
12211
|
"navigate",
|
|
12212
12212
|
"cookie_read",
|
|
@@ -16268,7 +16268,7 @@ var coerce = {
|
|
|
16268
16268
|
};
|
|
16269
16269
|
var NEVER = INVALID;
|
|
16270
16270
|
|
|
16271
|
-
//
|
|
16271
|
+
// node_modules/@commandgarden/shared/dist/pipeline.js
|
|
16272
16272
|
var STEP_CAPABILITY_MAP = {
|
|
16273
16273
|
navigate: "navigate",
|
|
16274
16274
|
wait: "navigate",
|
|
@@ -16361,7 +16361,7 @@ var pipelineStepSchema = external_exports.discriminatedUnion("step", [
|
|
|
16361
16361
|
jsEvaluateStepSchema
|
|
16362
16362
|
]);
|
|
16363
16363
|
|
|
16364
|
-
//
|
|
16364
|
+
// node_modules/@commandgarden/shared/dist/connector.js
|
|
16365
16365
|
var capabilityEnum = external_exports.enum(CAPABILITIES);
|
|
16366
16366
|
var connectorArgSchema = external_exports.object({
|
|
16367
16367
|
name: external_exports.string(),
|
|
@@ -16388,7 +16388,7 @@ var connectorSchema = external_exports.object({
|
|
|
16388
16388
|
pipeline: external_exports.array(pipelineStepSchema).min(1)
|
|
16389
16389
|
});
|
|
16390
16390
|
|
|
16391
|
-
//
|
|
16391
|
+
// node_modules/@commandgarden/shared/dist/loader.js
|
|
16392
16392
|
var import_yaml = __toESM(require_dist(), 1);
|
|
16393
16393
|
function parseConnectorYaml(yamlContent) {
|
|
16394
16394
|
let parsed;
|
|
@@ -16459,10 +16459,10 @@ function validateConnectorSemantics(connector) {
|
|
|
16459
16459
|
return errors;
|
|
16460
16460
|
}
|
|
16461
16461
|
|
|
16462
|
-
//
|
|
16462
|
+
// node_modules/@commandgarden/shared/dist/events.js
|
|
16463
16463
|
import { randomUUID } from "crypto";
|
|
16464
16464
|
|
|
16465
|
-
//
|
|
16465
|
+
// node_modules/@commandgarden/shared/dist/daemon-client.js
|
|
16466
16466
|
import { readFileSync } from "fs";
|
|
16467
16467
|
function readToken(tokenPath) {
|
|
16468
16468
|
try {
|
|
@@ -17405,9 +17405,40 @@ async function executeDown(cgHome) {
|
|
|
17405
17405
|
return lines.join("\n");
|
|
17406
17406
|
}
|
|
17407
17407
|
|
|
17408
|
+
// src/commands/extension-cmd.ts
|
|
17409
|
+
import { dirname as dirname2 } from "path";
|
|
17410
|
+
import { spawn as spawn3 } from "child_process";
|
|
17411
|
+
function executeExtensionSetup(baseDir) {
|
|
17412
|
+
const manifestPath = resolveScript(
|
|
17413
|
+
baseDir,
|
|
17414
|
+
"../../chrome/dist/manifest.json",
|
|
17415
|
+
"@commandgarden/chrome",
|
|
17416
|
+
"dist/manifest.json"
|
|
17417
|
+
);
|
|
17418
|
+
const extensionDir = dirname2(manifestPath);
|
|
17419
|
+
const [command, args] = process.platform === "darwin" ? ["open", ["chrome://extensions"]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", "chrome://extensions"]] : ["xdg-open", ["chrome://extensions"]];
|
|
17420
|
+
const child = spawn3(command, args, { stdio: "ignore", detached: true });
|
|
17421
|
+
child.on("error", () => {
|
|
17422
|
+
});
|
|
17423
|
+
child.unref();
|
|
17424
|
+
return [
|
|
17425
|
+
"",
|
|
17426
|
+
"Chrome extension path:",
|
|
17427
|
+
` ${extensionDir}`,
|
|
17428
|
+
"",
|
|
17429
|
+
"To install:",
|
|
17430
|
+
' 1. Enable "Developer mode" (top-right toggle)',
|
|
17431
|
+
' 2. Click "Load unpacked"',
|
|
17432
|
+
" 3. Select the path above",
|
|
17433
|
+
"",
|
|
17434
|
+
"Opening chrome://extensions...",
|
|
17435
|
+
""
|
|
17436
|
+
].join("\n");
|
|
17437
|
+
}
|
|
17438
|
+
|
|
17408
17439
|
// src/main.ts
|
|
17409
17440
|
var __filename = fileURLToPath(import.meta.url);
|
|
17410
|
-
var __dirname =
|
|
17441
|
+
var __dirname = dirname3(__filename);
|
|
17411
17442
|
var CLI_VERSION = (() => {
|
|
17412
17443
|
try {
|
|
17413
17444
|
const pkg = JSON.parse(readFileSync8(resolve(__dirname, "..", "package.json"), "utf-8"));
|
|
@@ -17511,6 +17542,10 @@ gui.command("stop").description("Stop the GUI").action(() => {
|
|
|
17511
17542
|
gui.command("status").description("Check GUI status").action(() => {
|
|
17512
17543
|
console.log(executeGuiStatus(CG_HOME));
|
|
17513
17544
|
});
|
|
17545
|
+
var extension = program2.command("extension").description("Manage the Chrome extension");
|
|
17546
|
+
extension.command("setup").description("Show extension path and open Chrome for installation").action(() => {
|
|
17547
|
+
console.log(executeExtensionSetup(__dirname));
|
|
17548
|
+
});
|
|
17514
17549
|
program2.command("up").description("Start daemon + GUI, open browser (use --no-open to skip)").option("--no-open", "Do not open browser").action(async (opts) => {
|
|
17515
17550
|
console.log(await executeUp(BASE_URL, CG_HOME, getDaemonScript(), getAppScript(), CONFIG_PATH, {
|
|
17516
17551
|
noOpen: opts.open === false
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// src/messages.ts
|
|
4
|
+
function createDomResponse(requestId, ok, data, error) {
|
|
5
|
+
return { requestId, ok, data, error };
|
|
6
|
+
}
|
|
7
|
+
function isDomRequest(value) {
|
|
8
|
+
if (value == null || typeof value !== "object") return false;
|
|
9
|
+
const obj = value;
|
|
10
|
+
return typeof obj.id === "string" && typeof obj.action === "string" && typeof obj.params === "object";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/content/dom-executor.ts
|
|
14
|
+
function waitForSelector(selector, timeout) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
if (document.querySelector(selector)) {
|
|
17
|
+
resolve();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const interval = 100;
|
|
21
|
+
let elapsed = 0;
|
|
22
|
+
const timer = setInterval(() => {
|
|
23
|
+
if (document.querySelector(selector)) {
|
|
24
|
+
clearInterval(timer);
|
|
25
|
+
resolve();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
elapsed += interval;
|
|
29
|
+
if (elapsed >= timeout) {
|
|
30
|
+
clearInterval(timer);
|
|
31
|
+
reject(new Error(`Selector "${selector}" timed out after ${timeout}ms`));
|
|
32
|
+
}
|
|
33
|
+
}, interval);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function extractData(rowSelector, fields) {
|
|
37
|
+
const rows = document.querySelectorAll(rowSelector);
|
|
38
|
+
return Array.from(rows).map((row) => {
|
|
39
|
+
const record = {};
|
|
40
|
+
for (const [name, selector] of Object.entries(fields)) {
|
|
41
|
+
record[name] = row.querySelector(selector)?.textContent?.trim() ?? "";
|
|
42
|
+
}
|
|
43
|
+
return record;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function clickElement(selector) {
|
|
47
|
+
const el = document.querySelector(selector);
|
|
48
|
+
if (!el) throw new Error(`Element "${selector}" not found`);
|
|
49
|
+
el.click();
|
|
50
|
+
}
|
|
51
|
+
async function typeIntoElement(selector, value) {
|
|
52
|
+
const el = document.querySelector(selector);
|
|
53
|
+
if (!el) throw new Error(`Element "${selector}" not found`);
|
|
54
|
+
el.focus();
|
|
55
|
+
el.value = value;
|
|
56
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
57
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
58
|
+
}
|
|
59
|
+
async function fetchFromPage(url, method = "GET", headers, body) {
|
|
60
|
+
const resp = await fetch(url, {
|
|
61
|
+
method,
|
|
62
|
+
headers,
|
|
63
|
+
body,
|
|
64
|
+
credentials: "include"
|
|
65
|
+
});
|
|
66
|
+
const contentType = resp.headers.get("content-type") ?? "";
|
|
67
|
+
if (contentType.includes("application/json")) return resp.json();
|
|
68
|
+
return resp.text();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/content/content-script.ts
|
|
72
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
73
|
+
if (!isDomRequest(message)) return false;
|
|
74
|
+
const req = message;
|
|
75
|
+
handleRequest(req).then(
|
|
76
|
+
(data) => sendResponse(createDomResponse(req.id, true, data)),
|
|
77
|
+
(err) => sendResponse(createDomResponse(req.id, false, void 0, err.message))
|
|
78
|
+
);
|
|
79
|
+
return true;
|
|
80
|
+
});
|
|
81
|
+
async function handleRequest(req) {
|
|
82
|
+
const p = req.params;
|
|
83
|
+
switch (req.action) {
|
|
84
|
+
case "wait":
|
|
85
|
+
await waitForSelector(p.selector, p.timeout ?? 1e4);
|
|
86
|
+
return void 0;
|
|
87
|
+
case "extract":
|
|
88
|
+
return extractData(p.selector, p.fields);
|
|
89
|
+
case "click":
|
|
90
|
+
await clickElement(p.selector);
|
|
91
|
+
return void 0;
|
|
92
|
+
case "type":
|
|
93
|
+
await typeIntoElement(p.selector, p.value);
|
|
94
|
+
return void 0;
|
|
95
|
+
case "fetch":
|
|
96
|
+
return fetchFromPage(
|
|
97
|
+
p.url,
|
|
98
|
+
p.method,
|
|
99
|
+
p.headers,
|
|
100
|
+
p.body
|
|
101
|
+
);
|
|
102
|
+
default:
|
|
103
|
+
throw new Error(`Unknown action: ${req.action}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
//# sourceMappingURL=content-script.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/messages.ts", "../src/content/dom-executor.ts", "../src/content/content-script.ts"],
|
|
4
|
+
"sourcesContent": ["// src/messages.ts\nconst randomUUID = () => crypto.randomUUID();\n\nexport type DomAction = 'wait' | 'extract' | 'click' | 'type' | 'fetch';\n\nexport interface DomRequest {\n id: string;\n action: DomAction;\n params: Record<string, unknown>;\n}\n\nexport interface DomResponse {\n requestId: string;\n ok: boolean;\n data?: unknown;\n error?: string;\n}\n\nexport function createDomRequest(action: DomAction, params: Record<string, unknown>): DomRequest {\n return { id: randomUUID(), action, params };\n}\n\nexport function createDomResponse(\n requestId: string, ok: boolean, data?: unknown, error?: string,\n): DomResponse {\n return { requestId, ok, data, error };\n}\n\nexport function isDomRequest(value: unknown): value is DomRequest {\n if (value == null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n return typeof obj.id === 'string' && typeof obj.action === 'string' && typeof obj.params === 'object';\n}\n\nexport function isDomResponse(value: unknown): value is DomResponse {\n if (value == null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n return typeof obj.requestId === 'string' && typeof obj.ok === 'boolean';\n}\n", "// src/content/dom-executor.ts\n\nexport function waitForSelector(selector: string, timeout: number): Promise<void> {\n return new Promise((resolve, reject) => {\n if (document.querySelector(selector)) { resolve(); return; }\n const interval = 100;\n let elapsed = 0;\n const timer = setInterval(() => {\n if (document.querySelector(selector)) { clearInterval(timer); resolve(); return; }\n elapsed += interval;\n if (elapsed >= timeout) {\n clearInterval(timer);\n reject(new Error(`Selector \"${selector}\" timed out after ${timeout}ms`));\n }\n }, interval);\n });\n}\n\nexport function extractData(\n rowSelector: string,\n fields: Record<string, string>,\n): Record<string, string>[] {\n const rows = document.querySelectorAll(rowSelector);\n return Array.from(rows).map(row => {\n const record: Record<string, string> = {};\n for (const [name, selector] of Object.entries(fields)) {\n record[name] = row.querySelector(selector)?.textContent?.trim() ?? '';\n }\n return record;\n });\n}\n\nexport async function clickElement(selector: string): Promise<void> {\n const el = document.querySelector(selector);\n if (!el) throw new Error(`Element \"${selector}\" not found`);\n (el as HTMLElement).click();\n}\n\nexport async function typeIntoElement(selector: string, value: string): Promise<void> {\n const el = document.querySelector(selector) as HTMLInputElement | null;\n if (!el) throw new Error(`Element \"${selector}\" not found`);\n el.focus();\n el.value = value;\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\n\nexport async function fetchFromPage(\n url: string, method = 'GET', headers?: Record<string, string>, body?: string,\n): Promise<unknown> {\n const resp = await fetch(url, {\n method, headers, body, credentials: 'include',\n });\n const contentType = resp.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) return resp.json();\n return resp.text();\n}\n", "// src/content/content-script.ts\nimport { isDomRequest, createDomResponse, type DomRequest } from '../messages.js';\nimport { waitForSelector, extractData, clickElement, typeIntoElement, fetchFromPage } from './dom-executor.js';\n\nchrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {\n if (!isDomRequest(message)) return false;\n const req = message as DomRequest;\n handleRequest(req).then(\n (data) => sendResponse(createDomResponse(req.id, true, data)),\n (err) => sendResponse(createDomResponse(req.id, false, undefined, err.message)),\n );\n return true; // Keep channel open for async response\n});\n\nasync function handleRequest(req: DomRequest): Promise<unknown> {\n const p = req.params;\n switch (req.action) {\n case 'wait':\n await waitForSelector(p.selector as string, (p.timeout as number) ?? 10000);\n return undefined;\n case 'extract':\n return extractData(p.selector as string, p.fields as Record<string, string>);\n case 'click':\n await clickElement(p.selector as string);\n return undefined;\n case 'type':\n await typeIntoElement(p.selector as string, p.value as string);\n return undefined;\n case 'fetch':\n return fetchFromPage(\n p.url as string, p.method as string,\n p.headers as Record<string, string>, p.body as string,\n );\n default:\n throw new Error(`Unknown action: ${req.action}`);\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;AAsBO,WAAS,kBACd,WAAmB,IAAa,MAAgB,OACnC;AACb,WAAO,EAAE,WAAW,IAAI,MAAM,MAAM;AAAA,EACtC;AAEO,WAAS,aAAa,OAAqC;AAChE,QAAI,SAAS,QAAQ,OAAO,UAAU,SAAU,QAAO;AACvD,UAAM,MAAM;AACZ,WAAO,OAAO,IAAI,OAAO,YAAY,OAAO,IAAI,WAAW,YAAY,OAAO,IAAI,WAAW;AAAA,EAC/F;;;AC9BO,WAAS,gBAAgB,UAAkB,SAAgC;AAChF,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,SAAS,cAAc,QAAQ,GAAG;AAAE,gBAAQ;AAAG;AAAA,MAAQ;AAC3D,YAAM,WAAW;AACjB,UAAI,UAAU;AACd,YAAM,QAAQ,YAAY,MAAM;AAC9B,YAAI,SAAS,cAAc,QAAQ,GAAG;AAAE,wBAAc,KAAK;AAAG,kBAAQ;AAAG;AAAA,QAAQ;AACjF,mBAAW;AACX,YAAI,WAAW,SAAS;AACtB,wBAAc,KAAK;AACnB,iBAAO,IAAI,MAAM,aAAa,QAAQ,qBAAqB,OAAO,IAAI,CAAC;AAAA,QACzE;AAAA,MACF,GAAG,QAAQ;AAAA,IACb,CAAC;AAAA,EACH;AAEO,WAAS,YACd,aACA,QAC0B;AAC1B,UAAM,OAAO,SAAS,iBAAiB,WAAW;AAClD,WAAO,MAAM,KAAK,IAAI,EAAE,IAAI,SAAO;AACjC,YAAM,SAAiC,CAAC;AACxC,iBAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,MAAM,GAAG;AACrD,eAAO,IAAI,IAAI,IAAI,cAAc,QAAQ,GAAG,aAAa,KAAK,KAAK;AAAA,MACrE;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,iBAAsB,aAAa,UAAiC;AAClE,UAAM,KAAK,SAAS,cAAc,QAAQ;AAC1C,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,YAAY,QAAQ,aAAa;AAC1D,IAAC,GAAmB,MAAM;AAAA,EAC5B;AAEA,iBAAsB,gBAAgB,UAAkB,OAA8B;AACpF,UAAM,KAAK,SAAS,cAAc,QAAQ;AAC1C,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,YAAY,QAAQ,aAAa;AAC1D,OAAG,MAAM;AACT,OAAG,QAAQ;AACX,OAAG,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AACtD,OAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;AAAA,EACzD;AAEA,iBAAsB,cACpB,KAAa,SAAS,OAAO,SAAkC,MAC7C;AAClB,UAAM,OAAO,MAAM,MAAM,KAAK;AAAA,MAC5B;AAAA,MAAQ;AAAA,MAAS;AAAA,MAAM,aAAa;AAAA,IACtC,CAAC;AACD,UAAM,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK;AACxD,QAAI,YAAY,SAAS,kBAAkB,EAAG,QAAO,KAAK,KAAK;AAC/D,WAAO,KAAK,KAAK;AAAA,EACnB;;;ACpDA,SAAO,QAAQ,UAAU,YAAY,CAAC,SAAS,SAAS,iBAAiB;AACvE,QAAI,CAAC,aAAa,OAAO,EAAG,QAAO;AACnC,UAAM,MAAM;AACZ,kBAAc,GAAG,EAAE;AAAA,MACjB,CAAC,SAAS,aAAa,kBAAkB,IAAI,IAAI,MAAM,IAAI,CAAC;AAAA,MAC5D,CAAC,QAAQ,aAAa,kBAAkB,IAAI,IAAI,OAAO,QAAW,IAAI,OAAO,CAAC;AAAA,IAChF;AACA,WAAO;AAAA,EACT,CAAC;AAED,iBAAe,cAAc,KAAmC;AAC9D,UAAM,IAAI,IAAI;AACd,YAAQ,IAAI,QAAQ;AAAA,MAClB,KAAK;AACH,cAAM,gBAAgB,EAAE,UAAqB,EAAE,WAAsB,GAAK;AAC1E,eAAO;AAAA,MACT,KAAK;AACH,eAAO,YAAY,EAAE,UAAoB,EAAE,MAAgC;AAAA,MAC7E,KAAK;AACH,cAAM,aAAa,EAAE,QAAkB;AACvC,eAAO;AAAA,MACT,KAAK;AACH,cAAM,gBAAgB,EAAE,UAAoB,EAAE,KAAe;AAC7D,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,UACL,EAAE;AAAA,UAAe,EAAE;AAAA,UACnB,EAAE;AAAA,UAAmC,EAAE;AAAA,QACzC;AAAA,MACF;AACE,cAAM,IAAI,MAAM,mBAAmB,IAAI,MAAM,EAAE;AAAA,IACnD;AAAA,EACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// fetch-interceptor.js — injected at document_start in MAIN world.
|
|
2
|
+
// Patches window.fetch BEFORE OWA's modules capture a reference to it.
|
|
3
|
+
// Minimal — only the fetch patch, nothing else.
|
|
4
|
+
(function () {
|
|
5
|
+
if (window.__rfb) return;
|
|
6
|
+
window.__rfb = [];
|
|
7
|
+
window.__rfb_urls = [];
|
|
8
|
+
|
|
9
|
+
// Intercept Service Worker messages — OWA sends getSchedule data
|
|
10
|
+
// from the SW to the page via postMessage, bypassing window.fetch.
|
|
11
|
+
if (navigator.serviceWorker) {
|
|
12
|
+
navigator.serviceWorker.addEventListener('message', function (e) {
|
|
13
|
+
try {
|
|
14
|
+
var str = typeof e.data === 'string' ? e.data : JSON.stringify(e.data);
|
|
15
|
+
if (str.indexOf('getSchedule') > -1 && str.indexOf('availabilityView') > -1) {
|
|
16
|
+
window.__rfb.push({ req: '', body: str });
|
|
17
|
+
}
|
|
18
|
+
} catch (_e) { /* ignore */ }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var origFetch = window.fetch;
|
|
23
|
+
window.fetch = function () {
|
|
24
|
+
var a = arguments;
|
|
25
|
+
var u = (a[0] && a[0].url) || a[0] || '';
|
|
26
|
+
var rb = (a[1] && a[1].body) || '';
|
|
27
|
+
var uStr = String(u);
|
|
28
|
+
if (window.__rfb_urls.length < 100) {
|
|
29
|
+
window.__rfb_urls.push(uStr.slice(0, 120));
|
|
30
|
+
}
|
|
31
|
+
return origFetch.apply(this, a).then(function (r) {
|
|
32
|
+
try {
|
|
33
|
+
if (uStr.indexOf('graphql') > -1) {
|
|
34
|
+
r.clone().text().then(function (t) {
|
|
35
|
+
if (t.indexOf('getSchedule') > -1 && t.indexOf('availabilityView') > -1) {
|
|
36
|
+
window.__rfb.push({ req: String(rb || ''), body: t });
|
|
37
|
+
}
|
|
38
|
+
}).catch(function () {});
|
|
39
|
+
}
|
|
40
|
+
} catch (_e) { /* ignore */ }
|
|
41
|
+
return r;
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "commandGarden",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Enterprise browser automation — secure, auditable CLI commands for websites",
|
|
6
|
+
"minimum_chrome_version": "116",
|
|
7
|
+
"permissions": ["alarms", "cookies", "debugger", "idle", "tabs", "scripting", "webRequest", "storage"],
|
|
8
|
+
"host_permissions": ["<all_urls>"],
|
|
9
|
+
"background": {
|
|
10
|
+
"service_worker": "service-worker.js",
|
|
11
|
+
"type": "module"
|
|
12
|
+
},
|
|
13
|
+
"action": {
|
|
14
|
+
"default_popup": "popup.html"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<style>
|
|
6
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7
|
+
body {
|
|
8
|
+
width: 320px;
|
|
9
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
10
|
+
font-size: 13px;
|
|
11
|
+
background: #1e1e1e;
|
|
12
|
+
color: #ccc;
|
|
13
|
+
}
|
|
14
|
+
.header {
|
|
15
|
+
padding: 12px 16px;
|
|
16
|
+
font-family: 'SF Mono', SFMono-Regular, ui-monospace, Menlo, Consolas, monospace;
|
|
17
|
+
font-size: 13px;
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
color: #e0e0e0;
|
|
20
|
+
border-bottom: 1px solid #333;
|
|
21
|
+
}
|
|
22
|
+
.status-row {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
gap: 8px;
|
|
26
|
+
padding: 10px 16px;
|
|
27
|
+
border-bottom: 1px solid #333;
|
|
28
|
+
}
|
|
29
|
+
.dot {
|
|
30
|
+
width: 8px;
|
|
31
|
+
height: 8px;
|
|
32
|
+
border-radius: 50%;
|
|
33
|
+
flex-shrink: 0;
|
|
34
|
+
}
|
|
35
|
+
.dot.connected { background: #4caf50; }
|
|
36
|
+
.dot.disconnected { background: #f44336; }
|
|
37
|
+
.dot.disabled { background: #666; }
|
|
38
|
+
.status-text { flex: 1; }
|
|
39
|
+
.toggle {
|
|
40
|
+
position: relative;
|
|
41
|
+
width: 36px;
|
|
42
|
+
height: 20px;
|
|
43
|
+
flex-shrink: 0;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
}
|
|
46
|
+
.toggle input {
|
|
47
|
+
opacity: 0;
|
|
48
|
+
width: 0;
|
|
49
|
+
height: 0;
|
|
50
|
+
}
|
|
51
|
+
.toggle-track {
|
|
52
|
+
position: absolute;
|
|
53
|
+
inset: 0;
|
|
54
|
+
background: #555;
|
|
55
|
+
border-radius: 10px;
|
|
56
|
+
transition: background 0.2s;
|
|
57
|
+
}
|
|
58
|
+
.toggle input:checked + .toggle-track {
|
|
59
|
+
background: #4caf50;
|
|
60
|
+
}
|
|
61
|
+
.toggle-knob {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 2px;
|
|
64
|
+
left: 2px;
|
|
65
|
+
width: 16px;
|
|
66
|
+
height: 16px;
|
|
67
|
+
background: #fff;
|
|
68
|
+
border-radius: 50%;
|
|
69
|
+
transition: transform 0.2s;
|
|
70
|
+
pointer-events: none;
|
|
71
|
+
}
|
|
72
|
+
.toggle input:checked ~ .toggle-knob {
|
|
73
|
+
transform: translateX(16px);
|
|
74
|
+
}
|
|
75
|
+
.activity-list {
|
|
76
|
+
max-height: 240px;
|
|
77
|
+
overflow-y: auto;
|
|
78
|
+
}
|
|
79
|
+
.activity-item {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 8px;
|
|
83
|
+
padding: 8px 16px;
|
|
84
|
+
border-bottom: 1px solid #2a2a2a;
|
|
85
|
+
transition: background 0.1s;
|
|
86
|
+
}
|
|
87
|
+
.activity-item:hover {
|
|
88
|
+
background: #252525;
|
|
89
|
+
}
|
|
90
|
+
.activity-icon { font-size: 12px; flex-shrink: 0; }
|
|
91
|
+
.activity-icon.ok { color: #4caf50; }
|
|
92
|
+
.activity-icon.fail { color: #f44336; }
|
|
93
|
+
.activity-name { flex: 1; font-family: monospace; font-size: 12px; }
|
|
94
|
+
.activity-time { color: #888; font-size: 11px; flex-shrink: 0; }
|
|
95
|
+
.empty {
|
|
96
|
+
padding: 16px;
|
|
97
|
+
text-align: center;
|
|
98
|
+
color: #666;
|
|
99
|
+
font-style: italic;
|
|
100
|
+
}
|
|
101
|
+
/* Approval section */
|
|
102
|
+
.approval-section {
|
|
103
|
+
border-bottom: 1px solid #333;
|
|
104
|
+
background: #252525;
|
|
105
|
+
}
|
|
106
|
+
.approval-header {
|
|
107
|
+
padding: 10px 16px 4px;
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
color: #ccc;
|
|
111
|
+
}
|
|
112
|
+
.approval-count {
|
|
113
|
+
display: inline-block;
|
|
114
|
+
font-size: 10px;
|
|
115
|
+
min-width: 16px;
|
|
116
|
+
text-align: center;
|
|
117
|
+
padding: 0 5px;
|
|
118
|
+
line-height: 16px;
|
|
119
|
+
border-radius: 8px;
|
|
120
|
+
background: rgba(245, 158, 11, 0.18);
|
|
121
|
+
color: #F59E0B;
|
|
122
|
+
font-weight: 700;
|
|
123
|
+
margin-left: 6px;
|
|
124
|
+
vertical-align: 1px;
|
|
125
|
+
}
|
|
126
|
+
.approval-card {
|
|
127
|
+
padding: 10px 16px;
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-direction: column;
|
|
130
|
+
gap: 6px;
|
|
131
|
+
}
|
|
132
|
+
.approval-card + .approval-card {
|
|
133
|
+
border-top: 1px solid #2a2a2a;
|
|
134
|
+
}
|
|
135
|
+
.approval-connector {
|
|
136
|
+
font-family: monospace;
|
|
137
|
+
font-size: 12px;
|
|
138
|
+
color: #e0e0e0;
|
|
139
|
+
}
|
|
140
|
+
.approval-detail {
|
|
141
|
+
font-size: 11px;
|
|
142
|
+
color: #999;
|
|
143
|
+
}
|
|
144
|
+
.approval-cap {
|
|
145
|
+
display: inline-block;
|
|
146
|
+
font-size: 10px;
|
|
147
|
+
padding: 1px 6px;
|
|
148
|
+
border-radius: 3px;
|
|
149
|
+
background: rgba(245, 158, 11, 0.15);
|
|
150
|
+
color: #F59E0B;
|
|
151
|
+
font-family: monospace;
|
|
152
|
+
}
|
|
153
|
+
.approval-actions {
|
|
154
|
+
display: flex;
|
|
155
|
+
gap: 8px;
|
|
156
|
+
margin-top: 2px;
|
|
157
|
+
}
|
|
158
|
+
.btn-approve, .btn-reject {
|
|
159
|
+
font-size: 11px;
|
|
160
|
+
padding: 4px 14px;
|
|
161
|
+
border-radius: 6px;
|
|
162
|
+
border: none;
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
font-weight: 600;
|
|
165
|
+
transition: background 0.15s, color 0.15s;
|
|
166
|
+
}
|
|
167
|
+
.btn-approve {
|
|
168
|
+
background: #4caf50;
|
|
169
|
+
color: #fff;
|
|
170
|
+
}
|
|
171
|
+
.btn-approve:hover { background: #43a047; }
|
|
172
|
+
.btn-approve:active { background: #388e3c; }
|
|
173
|
+
.btn-reject {
|
|
174
|
+
background: transparent;
|
|
175
|
+
color: #999;
|
|
176
|
+
border: 1px solid #555;
|
|
177
|
+
}
|
|
178
|
+
.btn-reject:hover { background: #333; color: #ccc; }
|
|
179
|
+
.btn-reject:active { background: #2a2a2a; }
|
|
180
|
+
</style>
|
|
181
|
+
</head>
|
|
182
|
+
<body>
|
|
183
|
+
<div class="header">commandGarden</div>
|
|
184
|
+
<div class="status-row">
|
|
185
|
+
<div id="dot" class="dot disabled"></div>
|
|
186
|
+
<span id="status-text" class="status-text">Checking…</span>
|
|
187
|
+
<label class="toggle">
|
|
188
|
+
<input type="checkbox" id="toggle-input">
|
|
189
|
+
<div class="toggle-track"></div>
|
|
190
|
+
<div class="toggle-knob"></div>
|
|
191
|
+
</label>
|
|
192
|
+
</div>
|
|
193
|
+
<div id="approvals"></div>
|
|
194
|
+
<div id="activity" class="activity-list"></div>
|
|
195
|
+
<script src="popup.js"></script>
|
|
196
|
+
</body>
|
|
197
|
+
</html>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// src/popup/time-ago.ts
|
|
4
|
+
function timeAgo(timestamp, now = Date.now()) {
|
|
5
|
+
const seconds = Math.floor((now - timestamp) / 1e3);
|
|
6
|
+
if (seconds < 60) return "just now";
|
|
7
|
+
const minutes = Math.floor(seconds / 60);
|
|
8
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
9
|
+
const hours = Math.floor(minutes / 60);
|
|
10
|
+
if (hours < 24) return `${hours}h ago`;
|
|
11
|
+
const days = Math.floor(hours / 24);
|
|
12
|
+
return `${days}d ago`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/popup/popup.ts
|
|
16
|
+
var dot = document.getElementById("dot");
|
|
17
|
+
var statusText = document.getElementById("status-text");
|
|
18
|
+
var toggleInput = document.getElementById("toggle-input");
|
|
19
|
+
var activityEl = document.getElementById("activity");
|
|
20
|
+
var approvalsEl = document.getElementById("approvals");
|
|
21
|
+
var currentEnabled = true;
|
|
22
|
+
function escapeHtml(text) {
|
|
23
|
+
const el = document.createElement("span");
|
|
24
|
+
el.textContent = text;
|
|
25
|
+
return el.innerHTML;
|
|
26
|
+
}
|
|
27
|
+
function renderApprovals(approvals) {
|
|
28
|
+
if (approvals.length === 0) {
|
|
29
|
+
approvalsEl.innerHTML = "";
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const cards = approvals.map(
|
|
33
|
+
(a) => `<div class="approval-card" data-id="${escapeHtml(a.approvalId)}">
|
|
34
|
+
<div class="approval-connector">${escapeHtml(a.connectorKey)}</div>
|
|
35
|
+
<div class="approval-detail">Step ${a.stepIndex + 1} [${escapeHtml(a.stepType)}] <span class="approval-cap">${escapeHtml(a.capability)}</span></div>
|
|
36
|
+
<div class="approval-actions">
|
|
37
|
+
<button class="btn-approve" data-approval-id="${escapeHtml(a.approvalId)}" data-approved="true">Approve</button>
|
|
38
|
+
<button class="btn-reject" data-approval-id="${escapeHtml(a.approvalId)}" data-approved="false">Reject</button>
|
|
39
|
+
</div>
|
|
40
|
+
</div>`
|
|
41
|
+
).join("");
|
|
42
|
+
approvalsEl.innerHTML = `<div class="approval-section">
|
|
43
|
+
<div class="approval-header">Pending approvals <span class="approval-count">${approvals.length}</span></div>
|
|
44
|
+
${cards}
|
|
45
|
+
</div>`;
|
|
46
|
+
approvalsEl.querySelectorAll("[data-approval-id]").forEach((btn) => {
|
|
47
|
+
btn.addEventListener("click", () => {
|
|
48
|
+
const approvalId = btn.dataset.approvalId;
|
|
49
|
+
const approved = btn.dataset.approved === "true";
|
|
50
|
+
btn.disabled = true;
|
|
51
|
+
chrome.runtime.sendMessage(
|
|
52
|
+
{ type: "approvalDecision", approvalId, approved },
|
|
53
|
+
() => {
|
|
54
|
+
fetchStatus();
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function render(status) {
|
|
61
|
+
currentEnabled = status.enabled;
|
|
62
|
+
toggleInput.checked = status.enabled;
|
|
63
|
+
if (!status.enabled) {
|
|
64
|
+
dot.className = "dot disabled";
|
|
65
|
+
statusText.textContent = "Disabled";
|
|
66
|
+
} else if (status.connected) {
|
|
67
|
+
dot.className = "dot connected";
|
|
68
|
+
statusText.textContent = "Connected";
|
|
69
|
+
} else {
|
|
70
|
+
dot.className = "dot disconnected";
|
|
71
|
+
statusText.textContent = "Disconnected";
|
|
72
|
+
}
|
|
73
|
+
renderApprovals(status.pendingApprovals ?? []);
|
|
74
|
+
if (status.recentActivity.length === 0) {
|
|
75
|
+
activityEl.innerHTML = '<div class="empty">No recent activity</div>';
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
activityEl.innerHTML = status.recentActivity.map((entry) => {
|
|
80
|
+
const icon = entry.ok ? '<span class="activity-icon ok">\u2713</span>' : '<span class="activity-icon fail">\u2717</span>';
|
|
81
|
+
return `<div class="activity-item">${icon}<span class="activity-name">${escapeHtml(entry.connector)}</span><span class="activity-time">${timeAgo(entry.timestamp, now)}</span></div>`;
|
|
82
|
+
}).join("");
|
|
83
|
+
}
|
|
84
|
+
function fetchStatus() {
|
|
85
|
+
chrome.runtime.sendMessage({ type: "getStatus" }, (response) => {
|
|
86
|
+
if (chrome.runtime.lastError) {
|
|
87
|
+
statusText.textContent = "Error";
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
render(response);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
toggleInput.addEventListener("change", () => {
|
|
94
|
+
const newEnabled = toggleInput.checked;
|
|
95
|
+
toggleInput.disabled = true;
|
|
96
|
+
chrome.runtime.sendMessage({ type: "setEnabled", enabled: newEnabled }, (response) => {
|
|
97
|
+
toggleInput.disabled = false;
|
|
98
|
+
if (chrome.runtime.lastError) {
|
|
99
|
+
toggleInput.checked = currentEnabled;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
render(response);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
chrome.runtime.onMessage.addListener((msg) => {
|
|
106
|
+
if (msg.type === "approvalUpdate") {
|
|
107
|
+
renderApprovals(msg.pendingApprovals ?? []);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
fetchStatus();
|
|
111
|
+
})();
|
|
112
|
+
//# sourceMappingURL=popup.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/popup/time-ago.ts", "../src/popup/popup.ts"],
|
|
4
|
+
"sourcesContent": ["// src/popup/time-ago.ts\nexport function timeAgo(timestamp: number, now: number = Date.now()): string {\n const seconds = Math.floor((now - timestamp) / 1000);\n if (seconds < 60) return 'just now';\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n", "// src/popup/popup.ts\n/// <reference lib=\"dom\" />\nimport type { PopupStatusResponse, ApprovalInfo } from './popup-types.js';\nimport { timeAgo } from './time-ago.js';\n\nconst dot = document.getElementById('dot')!;\nconst statusText = document.getElementById('status-text')!;\nconst toggleInput = document.getElementById('toggle-input') as HTMLInputElement;\nconst activityEl = document.getElementById('activity')!;\nconst approvalsEl = document.getElementById('approvals')!;\n\nlet currentEnabled = true;\n\nfunction escapeHtml(text: string): string {\n const el = document.createElement('span');\n el.textContent = text;\n return el.innerHTML;\n}\n\nfunction renderApprovals(approvals: ApprovalInfo[]): void {\n if (approvals.length === 0) {\n approvalsEl.innerHTML = '';\n return;\n }\n\n const cards = approvals.map(a =>\n `<div class=\"approval-card\" data-id=\"${escapeHtml(a.approvalId)}\">\n <div class=\"approval-connector\">${escapeHtml(a.connectorKey)}</div>\n <div class=\"approval-detail\">Step ${a.stepIndex + 1} [${escapeHtml(a.stepType)}] <span class=\"approval-cap\">${escapeHtml(a.capability)}</span></div>\n <div class=\"approval-actions\">\n <button class=\"btn-approve\" data-approval-id=\"${escapeHtml(a.approvalId)}\" data-approved=\"true\">Approve</button>\n <button class=\"btn-reject\" data-approval-id=\"${escapeHtml(a.approvalId)}\" data-approved=\"false\">Reject</button>\n </div>\n </div>`\n ).join('');\n\n approvalsEl.innerHTML =\n `<div class=\"approval-section\">\n <div class=\"approval-header\">Pending approvals <span class=\"approval-count\">${approvals.length}</span></div>\n ${cards}\n </div>`;\n\n approvalsEl.querySelectorAll('[data-approval-id]').forEach(btn => {\n btn.addEventListener('click', () => {\n const approvalId = (btn as HTMLElement).dataset.approvalId!;\n const approved = (btn as HTMLElement).dataset.approved === 'true';\n (btn as HTMLButtonElement).disabled = true;\n chrome.runtime.sendMessage(\n { type: 'approvalDecision', approvalId, approved },\n () => { fetchStatus(); },\n );\n });\n });\n}\n\nfunction render(status: PopupStatusResponse): void {\n currentEnabled = status.enabled;\n toggleInput.checked = status.enabled;\n\n if (!status.enabled) {\n dot.className = 'dot disabled';\n statusText.textContent = 'Disabled';\n } else if (status.connected) {\n dot.className = 'dot connected';\n statusText.textContent = 'Connected';\n } else {\n dot.className = 'dot disconnected';\n statusText.textContent = 'Disconnected';\n }\n\n renderApprovals(status.pendingApprovals ?? []);\n\n if (status.recentActivity.length === 0) {\n activityEl.innerHTML = '<div class=\"empty\">No recent activity</div>';\n return;\n }\n\n const now = Date.now();\n activityEl.innerHTML = status.recentActivity\n .map(entry => {\n const icon = entry.ok\n ? '<span class=\"activity-icon ok\">\u2713</span>'\n : '<span class=\"activity-icon fail\">\u2717</span>';\n return `<div class=\"activity-item\">${icon}<span class=\"activity-name\">${escapeHtml(entry.connector)}</span><span class=\"activity-time\">${timeAgo(entry.timestamp, now)}</span></div>`;\n })\n .join('');\n}\n\nfunction fetchStatus(): void {\n chrome.runtime.sendMessage({ type: 'getStatus' }, (response: PopupStatusResponse) => {\n if (chrome.runtime.lastError) {\n statusText.textContent = 'Error';\n return;\n }\n render(response);\n });\n}\n\ntoggleInput.addEventListener('change', () => {\n const newEnabled = toggleInput.checked;\n toggleInput.disabled = true;\n chrome.runtime.sendMessage({ type: 'setEnabled', enabled: newEnabled }, (response: PopupStatusResponse) => {\n toggleInput.disabled = false;\n if (chrome.runtime.lastError) {\n toggleInput.checked = currentEnabled;\n return;\n }\n render(response);\n });\n});\n\n// Listen for push updates from the service worker (approval state changes)\nchrome.runtime.onMessage.addListener((msg) => {\n if (msg.type === 'approvalUpdate') {\n renderApprovals(msg.pendingApprovals ?? []);\n }\n});\n\nfetchStatus();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|