← Files OneGateARCHIVED FILE
skills/onegate-dapp-debug/scripts/onegate.mjs
12.5 KB · Oct 3, 2026 · 06:30 UTC
#!/usr/bin/env node
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import {
inspectDaemon,
sendDaemonCommand,
} from "./runtime/daemon-client.mjs";
import { runtimeStateDirectory } from "./runtime/daemon-protocol.mjs";
import { ONEGATE_RUNTIME_VERSION } from "./runtime/identity.mjs";
import { renderQrPng } from "./runtime/qr-png.mjs";
const BOOLEAN_OPTIONS = new Set([
"confirm",
"force",
"headless",
"ignore-cache",
"defer",
]);
function requireCompatibleNode() {
const major = Number.parseInt(process.versions.node.split(".")[0], 10);
if (major < 22 || typeof WebSocket !== "function") {
throw commandError(
"UNSUPPORTED_NODE",
`OneGate requires Node.js 22 or newer with built-in WebSocket support; received ${process.version}.`,
);
}
}
function parseOptions(tokens) {
const options = {};
for (let index = 0; index < tokens.length; index += 1) {
const token = tokens[index];
if (!token.startsWith("--")) {
throw commandError("INVALID_ARGUMENT", `Unexpected positional argument: ${token}`);
}
const equals = token.indexOf("=");
const name = token.slice(2, equals === -1 ? undefined : equals);
if (!name) throw commandError("INVALID_ARGUMENT", "Option names cannot be empty.");
if (equals !== -1) {
options[name] = token.slice(equals + 1);
continue;
}
if (BOOLEAN_OPTIONS.has(name)) {
options[name] = true;
continue;
}
const value = tokens[index + 1];
if (value === undefined || value.startsWith("--")) {
throw commandError("INVALID_ARGUMENT", `--${name} requires a value.`);
}
options[name] = value;
index += 1;
}
return options;
}
function commandError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
function requireOption(options, name) {
const value = options[name];
if (typeof value !== "string" || value.length === 0) {
throw commandError("INVALID_ARGUMENT", `--${name} is required.`);
}
return value;
}
function allowOnly(options, names) {
const allowed = new Set(names);
for (const name of Object.keys(options)) {
if (!allowed.has(name)) {
throw commandError("INVALID_ARGUMENT", `Unknown option: --${name}`);
}
}
}
function parseInteger(value, name) {
if (value === undefined) return undefined;
if (!/^\d+$/u.test(value)) {
throw commandError("INVALID_ARGUMENT", `--${name} must be a non-negative integer.`);
}
return Number.parseInt(value, 10);
}
function parseJson(value, name) {
if (value === undefined) return { provided: false };
try {
return { provided: true, value: JSON.parse(value) };
} catch {
throw commandError("INVALID_ARGUMENT", `--${name} must be valid JSON.`);
}
}
function usage() {
return {
version: ONEGATE_RUNTIME_VERSION,
commands: [
"doctor",
"targets discover",
"debug-target pair start --output <png> [--name <debugger-name>]",
"debug-target pair status --id <pairing-id>",
"debug-target pair cancel --id <pairing-id>",
"debug-target list",
"debug-target forget --id <debug-target-id> --confirm",
"identity [show]",
"identity regenerate --confirm",
"review start [--browser-executable <path>] [--headless]",
"debug start --url <url> [--target browser|onegate] [--debug-target <debug-target-id>] [--browser-executable <path>] [--profile <path>] [--headless]",
"session list",
"session status --id <session-id>",
"session logs --id <session-id> [--after-sequence <n>]",
"session trace --id <session-id>",
"session screenshot --id <session-id> --output <png-path>",
"session requests --id <session-id>",
"session approve --id <session-id> --request-id <request-id> [--result <json>]",
"session reject --id <session-id> --request-id <request-id> [--reason <text>]",
"session evaluate --id <session-id> --expression <javascript> [--defer]",
"session operation --id <session-id> --operation-id <operation-id>",
"session reload --id <session-id> [--ignore-cache]",
"session stop --id <session-id>",
"daemon status",
"daemon stop [--force]",
],
};
}
function resolveCommand(tokens) {
const [group, action, ...rest] = tokens;
if (!group || group === "help" || group === "--help" || group === "-h") {
return { display: "help", local: "help", options: {} };
}
if (group === "doctor") {
return { display: "doctor", runtime: "doctor", options: parseOptions(tokens.slice(1)) };
}
if (group === "targets" && action === "discover") {
return { display: "targets discover", runtime: "targets.discover", options: parseOptions(rest) };
}
if (group === "debug-target" && action === "pair" && ["start", "status", "cancel"].includes(rest[0])) {
const [pairAction, ...pairRest] = rest;
return {
display: `debug-target pair ${pairAction}`,
runtime: `debug-target.pair.${pairAction}`,
options: parseOptions(pairRest),
};
}
if (group === "debug-target" && ["list", "forget"].includes(action)) {
return { display: `debug-target ${action}`, runtime: `debug-target.${action}`, options: parseOptions(rest) };
}
if (group === "identity" && (action === undefined || action === "show")) {
return {
display: action ? "identity show" : "identity",
runtime: "identity.get",
options: parseOptions(action ? rest : tokens.slice(1)),
};
}
if (group === "identity" && action === "regenerate") {
return { display: "identity regenerate", runtime: "identity.regenerate", options: parseOptions(rest) };
}
if (group === "review" && action === "start") {
return { display: "review start", runtime: "review.start", options: parseOptions(rest) };
}
if (group === "debug" && action === "start") {
return { display: "debug start", runtime: "debug.start", options: parseOptions(rest) };
}
if (group === "session" && [
"list",
"status",
"logs",
"trace",
"screenshot",
"evaluate",
"requests",
"approve",
"reject",
"operation",
"reload",
"stop",
].includes(action)) {
return { display: `session ${action}`, runtime: `session.${action}`, options: parseOptions(rest) };
}
if (group === "daemon" && ["status", "stop"].includes(action)) {
return { display: `daemon ${action}`, local: `daemon.${action}`, options: parseOptions(rest) };
}
throw commandError("UNKNOWN_COMMAND", `Unknown OneGate command: ${tokens.join(" ")}`);
}
function runtimeArguments(command) {
const options = command.options;
switch (command.runtime) {
case "doctor":
case "targets.discover":
case "identity.get":
case "session.list": {
allowOnly(options, []);
return {};
}
case "debug-target.pair.start": {
allowOnly(options, ["output", "name"]);
requireOption(options, "output");
return { name: options.name };
}
case "debug-target.pair.status":
case "debug-target.pair.cancel": {
allowOnly(options, ["id"]);
return { pairingId: requireOption(options, "id") };
}
case "debug-target.list": {
allowOnly(options, []);
return {};
}
case "debug-target.forget": {
allowOnly(options, ["id", "confirm"]);
if (options.confirm !== true) {
throw commandError("CONFIRMATION_REQUIRED", "--confirm is required to forget a trusted debug target.");
}
return { debugTargetId: requireOption(options, "id") };
}
case "identity.regenerate": {
allowOnly(options, ["confirm"]);
return { confirm: options.confirm === true };
}
case "review.start": {
allowOnly(options, ["browser-executable", "headless"]);
return {
browserExecutable: options["browser-executable"],
headless: options.headless === true,
};
}
case "debug.start": {
allowOnly(options, ["target", "debug-target", "url", "browser-executable", "profile", "headless"]);
return {
target: options.target ?? "browser",
url: requireOption(options, "url"),
browserExecutable: options["browser-executable"],
profilePath: options.profile,
headless: options.headless === true,
debugTargetId: options["debug-target"],
};
}
case "session.status":
case "session.trace":
case "session.stop": {
allowOnly(options, ["id"]);
return { sessionId: requireOption(options, "id") };
}
case "session.screenshot": {
allowOnly(options, ["id", "output"]);
requireOption(options, "output");
return { sessionId: requireOption(options, "id") };
}
case "session.logs": {
allowOnly(options, ["id", "after-sequence"]);
return {
sessionId: requireOption(options, "id"),
afterSequence: parseInteger(options["after-sequence"], "after-sequence") ?? 0,
};
}
case "session.evaluate": {
allowOnly(options, ["id", "expression", "defer"]);
return {
sessionId: requireOption(options, "id"),
expression: requireOption(options, "expression"),
defer: options.defer === true,
};
}
case "session.requests": {
allowOnly(options, ["id"]);
return { sessionId: requireOption(options, "id") };
}
case "session.approve": {
allowOnly(options, ["id", "request-id", "result"]);
const result = parseJson(options.result, "result");
return {
sessionId: requireOption(options, "id"),
requestId: requireOption(options, "request-id"),
...(result.provided ? { result: result.value } : {}),
};
}
case "session.reject": {
allowOnly(options, ["id", "request-id", "reason"]);
return {
sessionId: requireOption(options, "id"),
requestId: requireOption(options, "request-id"),
reason: options.reason,
};
}
case "session.operation": {
allowOnly(options, ["id", "operation-id"]);
return {
sessionId: requireOption(options, "id"),
operationId: requireOption(options, "operation-id"),
};
}
case "session.reload": {
allowOnly(options, ["id", "ignore-cache"]);
return {
sessionId: requireOption(options, "id"),
ignoreCache: options["ignore-cache"] === true,
};
}
default:
throw commandError("UNKNOWN_COMMAND", `Unknown runtime command: ${command.runtime}`);
}
}
async function execute(command) {
if (command.local === "help") return usage();
if (command.local === "daemon.status") {
allowOnly(command.options, []);
return inspectDaemon(runtimeStateDirectory());
}
if (command.local === "daemon.stop") {
allowOnly(command.options, ["force"]);
return sendDaemonCommand("daemon.stop", { force: command.options.force === true }, {
start: false,
timeoutMs: 10_000,
});
}
const args = runtimeArguments(command);
const result = await sendDaemonCommand(command.runtime, args);
if (command.runtime === "debug-target.pair.start") {
const output = path.resolve(requireOption(command.options, "output"));
await mkdir(path.dirname(output), { recursive: true });
const data = renderQrPng(result.invitation);
await writeFile(output, data);
const { invitation, ...publicResult } = result;
return { ...publicResult, output, bytes: data.length, mimeType: "image/png" };
}
if (command.runtime !== "session.screenshot") return result;
const output = path.resolve(requireOption(command.options, "output"));
await mkdir(path.dirname(output), { recursive: true });
const data = Buffer.from(result.data, "base64");
await writeFile(output, data);
return {
sessionId: result.sessionId,
mimeType: result.mimeType,
output,
bytes: data.length,
};
}
const rawTokens = process.argv.slice(2);
let display = rawTokens.join(" ") || "help";
try {
requireCompatibleNode();
const command = resolveCommand(rawTokens);
display = command.display;
const result = await execute(command);
process.stdout.write(`${JSON.stringify({ ok: true, command: display, result })}\n`);
} catch (error) {
process.stdout.write(`${JSON.stringify({
ok: false,
command: display,
error: {
code: error?.code ?? "ONEGATE_ERROR",
message: error instanceof Error ? error.message : String(error),
},
})}\n`);
process.exitCode = 1;
}
SHA-256: 8cc0bc2cb2c7fadb74778c778f6a1b432f3e8bbba4b5b2735a1c5452618056bc