MCPcopy
hub / github.com/garrytan/gstack / handleCommandInternalImpl

Function handleCommandInternalImpl

browse/src/server.ts:942–1301  ·  view source on GitHub ↗

* Core command execution logic. Returns a structured result instead of HTTP Response. * Used by both the HTTP handler (handleCommand) and chain subcommand routing. * * Options: * skipRateCheck: true when called from chain (chain counts as 1 request) * skipActivity: true when called from cha

(
  body: { command: string; args?: string[]; tabId?: number },
  tokenInfo?: TokenInfo | null,
  opts?: { skipRateCheck?: boolean; skipActivity?: boolean; chainDepth?: number },
)

Source from the content-addressed store, hash-verified

940 * chainDepth: recursion guard — reject nested chains (depth > 0 means inside a chain)
941 */
942async function handleCommandInternalImpl(
943 body: { command: string; args?: string[]; tabId?: number },
944 tokenInfo?: TokenInfo | null,
945 opts?: { skipRateCheck?: boolean; skipActivity?: boolean; chainDepth?: number },
946): Promise<CommandResult> {
947 const { args = [], tabId } = body;
948 const rawCommand = body.command;
949
950 if (!rawCommand) {
951 return { status: 400, result: JSON.stringify({ error: 'Missing "command" field' }), json: true };
952 }
953
954 // ─── Alias canonicalization (before scope, watch, tab-ownership, dispatch) ─
955 // Agent-friendly names like 'setcontent' route to canonical 'load-html'. Must
956 // happen BEFORE scope check so a read-scoped token calling 'setcontent' is still
957 // rejected (load-html lives in SCOPE_WRITE). Audit logging preserves rawCommand
958 // so the trail records what the agent actually typed.
959 const command = canonicalizeCommand(rawCommand);
960 const isAliased = command !== rawCommand;
961
962 // ─── Recursion guard: reject nested chains ──────────────────
963 if (command === 'chain' && (opts?.chainDepth ?? 0) > 0) {
964 return { status: 400, result: JSON.stringify({ error: 'Nested chain commands are not allowed' }), json: true };
965 }
966
967 // ─── Scope check (for scoped tokens) ──────────────────────────
968 if (tokenInfo && tokenInfo.clientId !== 'root') {
969 if (!checkScope(tokenInfo, command)) {
970 return {
971 status: 403, json: true,
972 result: JSON.stringify({
973 error: `Command "${command}" not allowed by your token scope`,
974 hint: `Your scopes: ${tokenInfo.scopes.join(', ')}. Ask the user to re-pair with --admin for eval/cookies/storage access.`,
975 }),
976 };
977 }
978
979 // `--out` writes the evaluate result to local disk, which is a WRITE
980 // capability distinct from the JS-exec (admin) capability js/eval need.
981 // Require write scope so an admin-but-not-write token can't write files.
982 if (hasOutArg(args) && !tokenInfo.scopes.includes('write')) {
983 return {
984 status: 403, json: true,
985 result: JSON.stringify({
986 error: `"--out" writes to disk and requires the "write" scope`,
987 hint: `Your scopes: ${tokenInfo.scopes.join(', ')}. Re-pair with write access to use --out.`,
988 }),
989 };
990 }
991
992 // Domain check for navigation commands
993 if ((command === 'goto' || command === 'newtab') && args[0]) {
994 if (!checkDomain(tokenInfo, args[0])) {
995 return {
996 status: 403, json: true,
997 result: JSON.stringify({
998 error: `Domain not allowed by your token scope`,
999 hint: `Allowed domains: ${tokenInfo.domains?.join(', ') || 'none configured'}`,

Callers 1

handleCommandInternalFunction · 0.85

Calls 15

canonicalizeCommandFunction · 0.90
checkScopeFunction · 0.90
hasOutArgFunction · 0.90
checkDomainFunction · 0.90
checkRateFunction · 0.90
recordCommandFunction · 0.90
emitActivityFunction · 0.90
markHiddenElementsFunction · 0.90
handleReadCommandFunction · 0.90
cleanupHiddenMarkersFunction · 0.90
handleWriteCommandFunction · 0.90

Tested by

no test coverage detected