← Files TokenXARCHIVED FILE
scripts/tokenx.mjs
8.71 KB · Oct 2, 2026 · 00:29 UTC
import { readFile, stat } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { classifyPrompt } from "./lib/classify.mjs";
import { buildAssignmentPrompt } from "./lib/assignment-prompts.mjs";
import { loadConfig } from "./lib/config.mjs";
import { resolveAssignmentProfile } from "./lib/delegation-plan.mjs";
import {
resetConfiguration,
setConfiguration,
showConfiguration,
} from "./configure.mjs";
import { runDoctor } from "./doctor.mjs";
import {
updatePluginFromMarketplace,
} from "./lib/plugin-update.mjs";
import { requirePluginData } from "./lib/plugin-data.mjs";
import { removeSessionState } from "./lib/state.mjs";
const pluginRoot = fileURLToPath(new URL("../", import.meta.url));
function printJson(value) {
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
}
function requireArgs(actual, expected, usage) {
if (
actual.length !== expected.length ||
actual.some((value, index) => value !== expected[index])
) {
throw new RangeError(`invalid arguments; usage: ${usage}`);
}
}
async function readMode600TextFile(path, label) {
const metadata = await stat(path);
if (!metadata.isFile()) {
throw new TypeError(`${label} must be a regular file`);
}
if (process.platform !== "win32" && (metadata.mode & 0o777) !== 0o600) {
throw new RangeError(`${label} mode must be 0600`);
}
if (metadata.size > 1024 * 1024) {
throw new RangeError(`${label} must not exceed 1 MiB`);
}
return readFile(path, "utf8");
}
async function explain(args, pluginData) {
if (args.length !== 2 || args[0] !== "--prompt-file") {
throw new RangeError(
"invalid arguments; usage: explain --prompt-file <path>",
);
}
const promptPath = args[1];
const prompt = await readMode600TextFile(promptPath, "prompt file");
const config = await loadConfig({ pluginData, env: process.env });
const decision = classifyPrompt(prompt, config, {
sessionId: "tokenx-explain",
turnId: null,
nowMs: Date.now(),
});
printJson({
route: decision.route,
model: decision.model,
reasoningEffort: decision.reasoningEffort,
method: decision.method,
reasonCodes: decision.reasonCodes,
explicit: decision.explicit,
});
}
function parseAssignmentArgs(args) {
const options = {
role: null,
parentRoute: null,
taskFile: null,
files: null,
contextFile: null,
evidence: null,
verify: [],
json: false,
};
const valueOptions = Object.freeze({
"--role": "role",
"--parent-route": "parentRoute",
"--task-file": "taskFile",
"--files": "files",
"--context-file": "contextFile",
"--evidence": "evidence",
});
for (let index = 0; index < args.length; index += 1) {
const argument = args[index];
if (argument === "--json") {
if (options.json) {
throw new RangeError("--json may be specified only once");
}
options.json = true;
continue;
}
if (argument === "--verify") {
const command = args[index + 1];
if (command === undefined) {
throw new RangeError("--verify requires a command");
}
options.verify.push(command);
if (options.verify.length > 8) {
throw new RangeError("verify must contain at most 8 commands");
}
index += 1;
continue;
}
const key = valueOptions[argument];
if (key === undefined) {
throw new RangeError(`unknown assignment argument: ${String(argument)}`);
}
if (options[key] !== null) {
throw new RangeError(`${argument} may be specified only once`);
}
const value = args[index + 1];
if (value === undefined) {
throw new RangeError(`${argument} requires a value`);
}
options[key] = value;
index += 1;
}
if (
options.role === null ||
options.parentRoute === null ||
options.taskFile === null
) {
throw new RangeError(
"invalid arguments; usage: assignment --role <role> " +
"--parent-route <route> --task-file <0600 path> " +
"[--files a,b] [--verify <cmd>]... " +
"[--context-file <0600 path>] [--evidence <line>] [--json]",
);
}
return options;
}
async function assignmentCommand(args, pluginData) {
if (args.length === 1 && args[0] === "--help") {
process.stdout.write(
"usage: tokenx assignment --role <role> --parent-route <route> --task-file <0600 path> [--files a,b] [--verify <cmd>]... [--context-file <0600 path>] [--evidence <line>] [--json]\n\n" +
"This stateless renderer does not authorize dispatch. Only the current persisted TokenX-advertised task_name authorizes a child; do not supply assignment_id.\n",
);
return;
}
const options = parseAssignmentArgs(args);
const task = await readMode600TextFile(options.taskFile, "task file");
const context = options.contextFile === null
? undefined
: await readMode600TextFile(options.contextFile, "context file");
const config = await loadConfig({ pluginData, env: process.env });
const resolved = resolveAssignmentProfile(
options.parentRoute,
options.role,
config,
);
const input = {
role: options.role,
task,
};
if (options.files !== null) {
input.files = options.files.split(",");
}
if (options.verify.length > 0) {
input.verify = options.verify;
}
if (context !== undefined) {
input.context = context;
}
if (options.evidence !== null) {
input.evidence = options.evidence;
}
const prompt = buildAssignmentPrompt(input);
if (options.json) {
printJson({
prompt,
role: options.role,
parentRoute: options.parentRoute,
profileKey: resolved.profileKey,
reasoningEffort: resolved.reasoningEffort,
});
return;
}
process.stdout.write(prompt);
process.stderr.write(
`role=${options.role} profile=${resolved.profileKey} ` +
`model=${config.routes[resolved.profileKey].model} ` +
`effort=${resolved.reasoningEffort}\n`,
);
}
async function configCommand(args, pluginData) {
const [action, ...rest] = args;
if (action === "show") {
requireArgs(rest, [], "config show");
printJson(
await showConfiguration({
pluginData,
env: process.env,
}),
);
return;
}
if (action === "set") {
if (rest.length !== 2) {
throw new RangeError(
"invalid arguments; usage: config set <path> <value>",
);
}
printJson(
await setConfiguration({
pluginData,
path: rest[0],
rawValue: rest[1],
env: process.env,
}),
);
return;
}
if (action === "reset") {
requireArgs(rest, [], "config reset");
printJson(await resetConfiguration({ pluginData }));
return;
}
throw new RangeError(`unknown config command: ${String(action)}`);
}
async function updateCommand(args, pluginData) {
if (args.length === 0) {
printJson(await updatePluginFromMarketplace({ pluginData }));
return;
}
if (
args.length === 2 &&
args[0] === "--marketplace" &&
args[1].length > 0
) {
printJson(
await updatePluginFromMarketplace({
pluginData,
marketplaceName: args[1],
}),
);
return;
}
throw new RangeError(
"invalid arguments; usage: update [--marketplace <name>]",
);
}
async function main(args) {
const pluginData = requirePluginData(process.env);
const [command, ...rest] = args;
if (command === "explain") {
await explain(rest, pluginData);
} else if (command === "assignment") {
await assignmentCommand(rest, pluginData);
} else if (command === "config") {
await configCommand(rest, pluginData);
} else if (command === "doctor") {
requireArgs(rest, ["--json"], "doctor --json");
const report = await runDoctor({
pluginRoot,
pluginData,
});
printJson(report);
if (!report.ok) {
process.exitCode = 1;
}
} else if (command === "update") {
await updateCommand(rest, pluginData);
} else if (command === "cleanup") {
if (rest.length !== 2 || rest[0] !== "--session") {
throw new RangeError(
"invalid arguments; usage: cleanup --session <opaque-id>",
);
}
const sessionId = rest[1];
if (sessionId.length === 0 || sessionId.length > 512) {
throw new RangeError("session identifier length is invalid");
}
printJson({
removed: await removeSessionState({
pluginData,
sessionId,
includeOutcomes: true,
includeModelPin: true,
}),
sessionId,
});
} else {
throw new RangeError(`unknown command: ${String(command)}`);
}
}
try {
await main(process.argv.slice(2));
} catch (error) {
const detail =
error instanceof Error && error.message
? error.message.replaceAll(/\s+/g, " ").trim()
: "unknown error";
process.stderr.write(`TokenX CLI failed: ${detail.slice(0, 450)}\n`);
process.exitCode = 1;
}
SHA-256: da112dec5e1721a13b65c70a2fab129227faf19d1fab7cedb91f429fc66430c3