replicas-cli 0.2.745 → 0.2.746

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.
Files changed (2) hide show
  1. package/dist/index.cjs +337 -55
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -23146,6 +23146,313 @@ function normalizeBackgroundTaskStatus(status) {
23146
23146
  // ../shared/src/display-message/constants.ts
23147
23147
  var USER_MESSAGE_MATCH_GRACE_PERIOD_MS = 3e4;
23148
23148
 
23149
+ // ../shared/src/shell-commands.ts
23150
+ var SHELL_OPAQUE = "\0";
23151
+ var MAX_DEPTH = 8;
23152
+ var OPERATOR = /&&|\|\||;;|\|&|<<<|<<-|<<|&>>|&>|>>|>&|>\||<&|<>|[;&|()<>]/y;
23153
+ var NAME = /[A-Za-z_]\w*/y;
23154
+ var SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ksh"]);
23155
+ var PREFIX_WORDS = /* @__PURE__ */ new Set(["!", "{", "}", "if", "then", "else", "elif", "fi", "do", "done", "while", "until", "time", "exec", "command", "builtin", "nohup"]);
23156
+ var WRAPPER_VALUE_OPTIONS = {
23157
+ env: /^(?:-[uCS]|--(?:unset|chdir|split-string))$/,
23158
+ sudo: /^(?:-[ugCpUrhDRT]|--(?:user|group|close-from|prompt|other-user|role|type|host|chdir|command-timeout))$/,
23159
+ nice: /^(?:-n|--adjustment)$/,
23160
+ timeout: /^(?:-[sk]|--(?:signal|kill-after))$/
23161
+ };
23162
+ var ANSI_C_ESCAPES = { n: "\n", t: " ", r: "\r", a: "\x07", b: "\b", e: "\x1B", E: "\x1B", f: "\f", v: "\v" };
23163
+ function parseShellScript(script, cwd) {
23164
+ const scope = { src: script, vars: /* @__PURE__ */ new Map(), dir: cwd, commands: [], depth: 0 };
23165
+ parseUntil(scope, 0, false);
23166
+ return scope.commands;
23167
+ }
23168
+ function resolveShellPath(dir, path6) {
23169
+ if (path6.includes(SHELL_OPAQUE) || path6.startsWith("~")) return null;
23170
+ if (!path6.startsWith("/") && dir === null) return null;
23171
+ const parts = [];
23172
+ for (const part of (path6.startsWith("/") ? path6 : `${dir}/${path6}`).split("/")) {
23173
+ if (part === "..") parts.pop();
23174
+ else if (part && part !== ".") parts.push(part);
23175
+ }
23176
+ return `/${parts.join("/")}`;
23177
+ }
23178
+ function stripCommandPrefixes(words) {
23179
+ const assignments = [];
23180
+ let k = 0;
23181
+ while (k < words.length) {
23182
+ const word = words[k];
23183
+ if (/^[A-Za-z_]\w*=/.test(word)) assignments.push(word);
23184
+ else if (WRAPPER_VALUE_OPTIONS[word]) {
23185
+ while (words[k + 1]?.startsWith("-")) k += WRAPPER_VALUE_OPTIONS[word].test(words[k + 1]) ? 2 : 1;
23186
+ if (word === "timeout") k++;
23187
+ } else if (!PREFIX_WORDS.has(word)) break;
23188
+ k++;
23189
+ }
23190
+ return { argv: words.slice(k), assignments };
23191
+ }
23192
+ function indexOrEnd(src, search, from) {
23193
+ const index = src.indexOf(search, from);
23194
+ return index < 0 ? src.length : index;
23195
+ }
23196
+ function decodeAnsiC(text2) {
23197
+ return text2.replace(/\\(x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{1,4}|[0-7]{1,3}|[\s\S])/g, (_match, escape) => {
23198
+ if (escape[0] === "x" || escape[0] === "u") return String.fromCharCode(parseInt(escape.slice(1), 16));
23199
+ if (/^[0-7]/.test(escape)) return String.fromCharCode(parseInt(escape, 8));
23200
+ return ANSI_C_ESCAPES[escape] ?? escape;
23201
+ });
23202
+ }
23203
+ function staticOutput(argv, stdin) {
23204
+ const [name, ...args] = argv;
23205
+ if (name === "cat") return args.length === 0 || args.length === 1 && args[0] === "-" ? stdin : void 0;
23206
+ if (name === "echo") {
23207
+ let newline = true;
23208
+ let escapes = false;
23209
+ while (/^-[neE]+$/.test(args[0] ?? "")) {
23210
+ const flags = args.shift() ?? "";
23211
+ if (flags.includes("n")) newline = false;
23212
+ if (flags.includes("e")) escapes = true;
23213
+ }
23214
+ const text2 = args.join(" ");
23215
+ return (escapes ? decodeAnsiC(text2) : text2) + (newline ? "\n" : "");
23216
+ }
23217
+ if (name === "printf" && args.length > 0) {
23218
+ const format = decodeAnsiC(args[0]);
23219
+ if (/%(?![s%])/.test(format)) return void 0;
23220
+ const values = args.slice(1);
23221
+ let output = "";
23222
+ let next = 0;
23223
+ do {
23224
+ output += format.replace(/%([s%])/g, (_match, directive) => directive === "%" ? "%" : values[next++] ?? "");
23225
+ } while (format.includes("%s") && next < values.length);
23226
+ return output;
23227
+ }
23228
+ return void 0;
23229
+ }
23230
+ function commandStdin(raw) {
23231
+ const heredoc = raw.heredocs.at(-1);
23232
+ if (heredoc) return heredoc.body;
23233
+ const input = raw.redirects.findLast(({ op }) => op === "<<<" || op === "<" || op === "0<");
23234
+ if (input) return input.op === "<<<" ? `${input.target}
23235
+ ` : SHELL_OPAQUE;
23236
+ if (raw.pipeFrom) return staticOutput(stripCommandPrefixes(raw.pipeFrom.words).argv, commandStdin(raw.pipeFrom)) ?? SHELL_OPAQUE;
23237
+ return void 0;
23238
+ }
23239
+ function assign(scope, assignments) {
23240
+ for (const assignment of assignments) {
23241
+ const separator = assignment.indexOf("=");
23242
+ scope.vars.set(assignment.slice(0, separator), assignment.slice(separator + 1));
23243
+ }
23244
+ }
23245
+ function emit(scope, raw) {
23246
+ const { argv, assignments } = stripCommandPrefixes(raw.words);
23247
+ if (argv.length === 0) {
23248
+ assign(scope, assignments);
23249
+ return;
23250
+ }
23251
+ const stdin = commandStdin(raw);
23252
+ const output = staticOutput(argv, stdin) ?? SHELL_OPAQUE;
23253
+ const stdinFile = raw.redirects.findLast(({ op }) => op === "<" || op === "0<");
23254
+ const name = argv[0].split("/").pop() ?? "";
23255
+ const flag = SHELLS.has(name) ? argv.findIndex((arg, index) => index > 0 && /^-[A-Za-z]*c[A-Za-z]*$/.test(arg)) : -1;
23256
+ const script = flag > 0 ? argv[flag + 1] : SHELLS.has(name) && argv.length === 1 && stdin !== SHELL_OPAQUE ? stdin : name === "eval" ? argv.slice(1).join(" ") : void 0;
23257
+ const truncated = script !== void 0 && scope.depth >= MAX_DEPTH;
23258
+ scope.commands.push({
23259
+ argv,
23260
+ dir: scope.dir,
23261
+ stdin,
23262
+ afterAnd: raw.afterAnd,
23263
+ stdinFile: stdinFile ? resolveShellPath(scope.dir, stdinFile.target) : null,
23264
+ writes: raw.redirects.flatMap(({ op, target }) => /^(?:1?>|1?>\||1?>>|&>|&>>|>&)$/.test(op) && !/^(?:\d+|-)$/.test(target) ? [{ path: resolveShellPath(scope.dir, target), content: output, append: op.endsWith(">>") }] : []),
23265
+ wrapper: script !== void 0,
23266
+ truncated
23267
+ });
23268
+ if (name === "cd" || name === "pushd" || name === "popd") {
23269
+ scope.dir = name !== "popd" && argv[1] && argv[1] !== "-" ? resolveShellPath(scope.dir, argv[1]) : null;
23270
+ } else if (["export", "declare", "local", "readonly"].includes(name)) {
23271
+ assign(scope, argv.slice(1).filter((arg) => /^[A-Za-z_]\w*=/.test(arg)));
23272
+ }
23273
+ if (script && !truncated) parseUntil({ src: script, vars: name === "eval" ? scope.vars : /* @__PURE__ */ new Map(), dir: scope.dir, commands: scope.commands, depth: scope.depth + 1 }, 0, false);
23274
+ }
23275
+ function readHeredocBodies(scope, heredocs, start) {
23276
+ const { src } = scope;
23277
+ let position = start;
23278
+ for (const heredoc of heredocs) {
23279
+ const lines = [];
23280
+ while (position < src.length) {
23281
+ const end = indexOrEnd(src, "\n", position);
23282
+ const line = heredoc.strip ? src.slice(position, end).replace(/^\t+/, "") : src.slice(position, end);
23283
+ position = end + 1;
23284
+ if (line === heredoc.delimiter) break;
23285
+ lines.push(line);
23286
+ }
23287
+ heredoc.body = lines.length > 0 ? `${lines.join("\n")}
23288
+ ` : "";
23289
+ if (heredoc.expand) heredoc.body = readDoubleQuoted({ ...scope, src: heredoc.body }, 0, null).value;
23290
+ }
23291
+ return Math.min(position, src.length);
23292
+ }
23293
+ function readDoubleQuoted(scope, start, terminator) {
23294
+ const { src } = scope;
23295
+ let value = "";
23296
+ let i = start;
23297
+ while (i < src.length && src[i] !== terminator) {
23298
+ const c = src[i];
23299
+ if (c === "\\" && i + 1 < src.length) {
23300
+ const next = src[i + 1];
23301
+ if (next === "\n") i += 2;
23302
+ else if ("$`\\".includes(next) || next === terminator) {
23303
+ value += next;
23304
+ i += 2;
23305
+ } else {
23306
+ value += c;
23307
+ i++;
23308
+ }
23309
+ } else if (c === "$" || c === "`") {
23310
+ const expansion = readExpansion(scope, i, true);
23311
+ value += expansion.value;
23312
+ i = expansion.end;
23313
+ } else {
23314
+ value += c;
23315
+ i++;
23316
+ }
23317
+ }
23318
+ return { value, end: i };
23319
+ }
23320
+ function substitute(scope, src, start, closeParen) {
23321
+ const child = { src, vars: new Map(scope.vars), dir: scope.dir, commands: [], depth: scope.depth + 1 };
23322
+ const end = parseUntil(child, start, closeParen);
23323
+ scope.commands.push(...child.commands);
23324
+ const [only] = child.commands;
23325
+ const output = child.commands.length === 1 ? staticOutput(only.argv, only.stdin) : void 0;
23326
+ return { value: output?.replace(/\n+$/, "") ?? SHELL_OPAQUE, end };
23327
+ }
23328
+ function readExpansion(scope, i, inDouble) {
23329
+ const { src } = scope;
23330
+ if (src[i] === "`") {
23331
+ let inner = "";
23332
+ let j = i + 1;
23333
+ while (j < src.length && src[j] !== "`") {
23334
+ if (src[j] === "\\" && "$`\\".includes(src[j + 1] ?? "")) {
23335
+ inner += src[j + 1];
23336
+ j += 2;
23337
+ } else inner += src[j++];
23338
+ }
23339
+ return { value: substitute(scope, inner, 0, false).value, end: j + 1 };
23340
+ }
23341
+ const next = src[i + 1];
23342
+ if (next === "(") return substitute(scope, src, i + 2, true);
23343
+ if (next === "{") {
23344
+ const end = indexOrEnd(src, "}", i + 2);
23345
+ return { value: scope.vars.get(src.slice(i + 2, end)) ?? SHELL_OPAQUE, end: end + 1 };
23346
+ }
23347
+ if (next === "'" && !inDouble) {
23348
+ let j = i + 2;
23349
+ while (j < src.length && src[j] !== "'") j += src[j] === "\\" ? 2 : 1;
23350
+ return { value: decodeAnsiC(src.slice(i + 2, j)), end: j + 1, quoted: true };
23351
+ }
23352
+ if (next === '"' && !inDouble) {
23353
+ const { value, end } = readDoubleQuoted(scope, i + 2, '"');
23354
+ return { value, end: end + 1, quoted: true };
23355
+ }
23356
+ NAME.lastIndex = i + 1;
23357
+ const name = NAME.exec(src)?.[0];
23358
+ if (name) return { value: scope.vars.get(name) ?? SHELL_OPAQUE, end: i + 1 + name.length };
23359
+ if (next && /[0-9@*#?$!-]/.test(next)) return { value: SHELL_OPAQUE, end: i + 2 };
23360
+ return { value: "$", end: i + 1 };
23361
+ }
23362
+ function parseUntil(scope, start, closeParen) {
23363
+ const { src } = scope;
23364
+ let i = start;
23365
+ let line = [];
23366
+ let current = { words: [], redirects: [], heredocs: [], afterAnd: false };
23367
+ let pendingHeredocs = [];
23368
+ let word = null;
23369
+ let quoted = false;
23370
+ let redirect = null;
23371
+ let parens = 0;
23372
+ const append = (text2, isQuoted = false) => {
23373
+ word = (word ?? "") + text2;
23374
+ quoted || (quoted = isQuoted);
23375
+ };
23376
+ const endWord = () => {
23377
+ if (word === null) return;
23378
+ if (redirect === "<<" || redirect === "<<-") {
23379
+ const heredoc = { strip: redirect === "<<-", delimiter: word, expand: !quoted, body: "" };
23380
+ current.heredocs.push(heredoc);
23381
+ pendingHeredocs.push(heredoc);
23382
+ } else if (redirect) {
23383
+ current.redirects.push({ op: redirect, target: word });
23384
+ } else {
23385
+ current.words.push(word);
23386
+ }
23387
+ word = null;
23388
+ quoted = false;
23389
+ redirect = null;
23390
+ };
23391
+ const endCommand = (op) => {
23392
+ endWord();
23393
+ const done = current;
23394
+ current = { words: [], redirects: [], heredocs: [], pipeFrom: op === "|" || op === "|&" ? done : void 0, afterAnd: op === "&&" };
23395
+ if (done.words.length > 0 || done.redirects.length > 0 || done.heredocs.length > 0) line.push(done);
23396
+ };
23397
+ const flush = () => {
23398
+ endCommand();
23399
+ for (const raw of line) emit(scope, raw);
23400
+ line = [];
23401
+ };
23402
+ while (i < src.length) {
23403
+ const c = src[i];
23404
+ if (c === "\\") {
23405
+ if (src[i + 1] !== "\n") append(src[i + 1] ?? "", true);
23406
+ i += 2;
23407
+ } else if (c === "'") {
23408
+ const end = indexOrEnd(src, "'", i + 1);
23409
+ append(src.slice(i + 1, end), true);
23410
+ i = end + 1;
23411
+ } else if (c === '"') {
23412
+ const { value, end } = readDoubleQuoted(scope, i + 1, '"');
23413
+ append(value, true);
23414
+ i = end + 1;
23415
+ } else if (c === "$" || c === "`") {
23416
+ const expansion = readExpansion(scope, i, false);
23417
+ append(expansion.value, expansion.quoted);
23418
+ i = expansion.end;
23419
+ } else if (c === "#" && word === null) {
23420
+ i = indexOrEnd(src, "\n", i);
23421
+ } else if (c === "\n") {
23422
+ endWord();
23423
+ i = readHeredocBodies(scope, pendingHeredocs, i + 1);
23424
+ pendingHeredocs = [];
23425
+ flush();
23426
+ } else if (c === " " || c === " " || c === "\r") {
23427
+ endWord();
23428
+ i++;
23429
+ } else if (c === ")" && parens === 0 && closeParen) {
23430
+ flush();
23431
+ return i + 1;
23432
+ } else {
23433
+ OPERATOR.lastIndex = i;
23434
+ const op = OPERATOR.exec(src)?.[0];
23435
+ if (!op) {
23436
+ append(c);
23437
+ i++;
23438
+ continue;
23439
+ }
23440
+ const fd = /[<>]/.test(op) && !quoted && /^\d+$/.test(word ?? "") ? word : null;
23441
+ if (fd !== null) word = null;
23442
+ endWord();
23443
+ i += op.length;
23444
+ if (/[<>]/.test(op)) redirect = `${fd ?? ""}${op}`;
23445
+ else {
23446
+ if (op === "(") parens++;
23447
+ if (op === ")") parens = Math.max(0, parens - 1);
23448
+ endCommand(op);
23449
+ }
23450
+ }
23451
+ }
23452
+ flush();
23453
+ return i;
23454
+ }
23455
+
23149
23456
  // ../shared/src/display-message/parsers/utils.ts
23150
23457
  var INTERRUPTED_MESSAGE_REGEX = /^\[Request interrupted by user.*\]$/;
23151
23458
  function userMessageImages(value) {
@@ -23261,61 +23568,11 @@ function insertDisplayMessageByTimestamp(messages, message) {
23261
23568
  }
23262
23569
 
23263
23570
  // ../shared/src/display-message/format.ts
23264
- function unquoteShellArg(value) {
23265
- const trimmed = value.trim();
23266
- if (trimmed.length < 2) return trimmed;
23267
- if (trimmed[0] === "'" && trimmed[trimmed.length - 1] === "'") {
23268
- return trimmed.slice(1, -1).replace(/'\\''/g, "'");
23269
- }
23270
- if (trimmed[0] === '"' && trimmed[trimmed.length - 1] === '"') {
23271
- return trimmed.slice(1, -1).replace(/\\(["\\$`])/g, "$1");
23272
- }
23273
- return trimmed;
23274
- }
23275
- function shellPayload(command) {
23276
- const match = command.trim().match(/^(?:\S*\/)?(?:ba|z|c|fi)?sh\s+-(?:[a-zA-Z]*c[a-zA-Z]*|c)\s+([\s\S]+)$/);
23277
- return match ? unquoteShellArg(match[1]) : command.trim();
23278
- }
23279
- function splitShellWords(command) {
23280
- const words = [];
23281
- let word = "";
23282
- let quote = null;
23283
- let escaped = false;
23284
- for (const char of command) {
23285
- if (escaped) {
23286
- word += char;
23287
- escaped = false;
23288
- continue;
23289
- }
23290
- if (char === "\\" && quote !== "'") {
23291
- escaped = true;
23292
- continue;
23293
- }
23294
- if (quote) {
23295
- if (char === quote) quote = null;
23296
- else word += char;
23297
- continue;
23298
- }
23299
- if (char === '"' || char === "'") {
23300
- quote = char;
23301
- continue;
23302
- }
23303
- if (/\s/.test(char)) {
23304
- if (word) {
23305
- words.push(word);
23306
- word = "";
23307
- }
23308
- continue;
23309
- }
23310
- word += char;
23311
- }
23312
- if (word) words.push(word);
23313
- return words;
23314
- }
23315
23571
  function commandWords(command) {
23316
- const payload = shellPayload(command);
23317
- if (!payload || /[;|<>]|&&/.test(payload)) return null;
23318
- return splitShellWords(payload);
23572
+ const commands = parseShellScript(command, null).filter(({ wrapper }) => !wrapper);
23573
+ if (commands.length !== 1) return null;
23574
+ const [{ argv, stdin, stdinFile, writes }] = commands;
23575
+ return stdin === void 0 && !stdinFile && writes.length === 0 ? argv.map((word) => word.replaceAll(SHELL_OPAQUE, "\u2026")) : null;
23319
23576
  }
23320
23577
  function skillNamesFromCommand(command) {
23321
23578
  const words = commandWords(command);
@@ -27172,7 +27429,7 @@ var headlessFilesystemAgentResultSchema = external_exports.object({
27172
27429
  });
27173
27430
 
27174
27431
  // ../shared/src/cli-version.ts
27175
- var CLI_VERSION = "0.2.745";
27432
+ var CLI_VERSION = "0.2.746";
27176
27433
 
27177
27434
  // ../shared/src/version.ts
27178
27435
  function compareVersions(v1, v2) {
@@ -27187,6 +27444,31 @@ function compareVersions(v1, v2) {
27187
27444
  return 0;
27188
27445
  }
27189
27446
 
27447
+ // ../shared/src/routes/command-protection.ts
27448
+ var commandProtectionRepoStateSchema = external_exports.object({
27449
+ branch: external_exports.string().nullable(),
27450
+ defaultBranch: external_exports.string().nullable(),
27451
+ pushBranch: external_exports.string().nullable(),
27452
+ pushConfig: external_exports.string().nullable(),
27453
+ headMessage: external_exports.string().nullable(),
27454
+ mergeInProgress: external_exports.boolean().optional()
27455
+ });
27456
+ var commandProtectionEvaluateRequestSchema = external_exports.object({
27457
+ provider: external_exports.enum(["claude", "codex", "cursor", "opencode", "pi"]),
27458
+ source: external_exports.enum(["claude_pre_tool_use", "codex_approval_request", "cursor_pre_tool_use", "opencode_pre_tool_use", "pi_tool_call"]),
27459
+ toolName: external_exports.string(),
27460
+ toolInput: external_exports.unknown(),
27461
+ command: external_exports.string().nullish(),
27462
+ cwd: external_exports.string().nullish(),
27463
+ toolUseId: external_exports.string().nullish(),
27464
+ // Read by the engine before the command runs, keyed by absolute path, so the
27465
+ // policy can see branches and commit messages the command text only references.
27466
+ gitContext: external_exports.object({
27467
+ repos: external_exports.record(external_exports.string(), commandProtectionRepoStateSchema),
27468
+ messageFiles: external_exports.record(external_exports.string(), external_exports.string())
27469
+ }).optional()
27470
+ });
27471
+
27190
27472
  // ../shared/src/routes/linear.ts
27191
27473
  var linearSessionMetadataSchema = external_exports.object({
27192
27474
  via_mothership: external_exports.boolean().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.745",
3
+ "version": "0.2.746",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {