@gendigital/sage-openclaw 0.5.4 → 0.6.0

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/README.md CHANGED
@@ -33,9 +33,9 @@ Sage works out of the box with no configuration. Optional config file at `~/.sag
33
33
 
34
34
  ## Links
35
35
 
36
- - [Source & documentation](https://github.com/avast/sage)
37
- - [OpenClaw platform guide](https://github.com/avast/sage/blob/main/docs/platform-guides/openclaw.md)
38
- - [Privacy policy](https://github.com/avast/sage/blob/main/docs/privacy.md)
36
+ - [Source & documentation](https://github.com/gendigitalinc/sage)
37
+ - [OpenClaw platform guide](https://github.com/gendigitalinc/sage/blob/main/docs/platform-guides/openclaw.md)
38
+ - [Privacy policy](https://github.com/gendigitalinc/sage/blob/main/docs/privacy.md)
39
39
 
40
40
  ## License
41
41
 
package/dist/index.cjs CHANGED
@@ -6868,14 +6868,14 @@ var require_parser = __commonJS({
6868
6868
  case "scalar":
6869
6869
  case "single-quoted-scalar":
6870
6870
  case "double-quoted-scalar": {
6871
- const fs = this.flowScalar(this.type);
6871
+ const fs2 = this.flowScalar(this.type);
6872
6872
  if (atNextItem || it.value) {
6873
- map.items.push({ start, key: fs, sep: [] });
6873
+ map.items.push({ start, key: fs2, sep: [] });
6874
6874
  this.onKeyLine = true;
6875
6875
  } else if (it.sep) {
6876
- this.stack.push(fs);
6876
+ this.stack.push(fs2);
6877
6877
  } else {
6878
- Object.assign(it, { key: fs, sep: [] });
6878
+ Object.assign(it, { key: fs2, sep: [] });
6879
6879
  this.onKeyLine = true;
6880
6880
  }
6881
6881
  return;
@@ -7003,13 +7003,13 @@ var require_parser = __commonJS({
7003
7003
  case "scalar":
7004
7004
  case "single-quoted-scalar":
7005
7005
  case "double-quoted-scalar": {
7006
- const fs = this.flowScalar(this.type);
7006
+ const fs2 = this.flowScalar(this.type);
7007
7007
  if (!it || it.value)
7008
- fc.items.push({ start: [], key: fs, sep: [] });
7008
+ fc.items.push({ start: [], key: fs2, sep: [] });
7009
7009
  else if (it.sep)
7010
- this.stack.push(fs);
7010
+ this.stack.push(fs2);
7011
7011
  else
7012
- Object.assign(it, { key: fs, sep: [] });
7012
+ Object.assign(it, { key: fs2, sep: [] });
7013
7013
  return;
7014
7014
  }
7015
7015
  case "flow-map-end":
@@ -7330,6 +7330,7 @@ var import_node_path2 = require("node:path");
7330
7330
 
7331
7331
  // ../core/dist/file-utils.js
7332
7332
  var import_node_crypto = require("node:crypto");
7333
+ var fs = __toESM(require("node:fs"), 1);
7333
7334
  var fsPromises = __toESM(require("node:fs/promises"), 1);
7334
7335
  var import_node_path = require("node:path");
7335
7336
  var name1 = "read";
@@ -7337,6 +7338,9 @@ var name2 = "File";
7337
7338
  function getFileContent(path, encoding = "utf-8") {
7338
7339
  return fsPromises[name1 + name2](path, encoding);
7339
7340
  }
7341
+ function getFileContentSync(path, encoding = "utf-8") {
7342
+ return fs[`${name1 + name2}Sync`](path, encoding);
7343
+ }
7340
7344
  function getFileContentRaw(path) {
7341
7345
  return fsPromises[name1 + name2](path);
7342
7346
  }
@@ -11500,10 +11504,6 @@ var ConfigSchema = external_exports.object({
11500
11504
 
11501
11505
  // ../core/dist/config.js
11502
11506
  var SAGE_DIR = "~/.sage";
11503
- function currentHomeDir() {
11504
- const envHome = process.env.HOME?.trim();
11505
- return envHome && envHome.length > 0 ? envHome : (0, import_node_os.homedir)();
11506
- }
11507
11507
  function resolvedSageDir() {
11508
11508
  return resolvePath(SAGE_DIR);
11509
11509
  }
@@ -11521,7 +11521,7 @@ function defaultAuditPath() {
11521
11521
  }
11522
11522
  function resolvePath(pathStr) {
11523
11523
  if (pathStr.startsWith("~/") || pathStr === "~") {
11524
- return (0, import_node_path2.join)(currentHomeDir(), pathStr.slice(1));
11524
+ return (0, import_node_path2.join)((0, import_node_os.homedir)(), pathStr.slice(1));
11525
11525
  }
11526
11526
  return pathStr;
11527
11527
  }
@@ -11951,10 +11951,12 @@ var VerdictCache = class {
11951
11951
  path;
11952
11952
  config;
11953
11953
  logger;
11954
- constructor(config, logger = nullLogger) {
11954
+ version;
11955
+ constructor(config, logger = nullLogger, version) {
11955
11956
  this.config = config;
11956
11957
  this.logger = logger;
11957
11958
  this.path = resolvePath(config.path);
11959
+ this.version = version;
11958
11960
  }
11959
11961
  async load() {
11960
11962
  if (!this.config.enabled)
@@ -11976,12 +11978,8 @@ var VerdictCache = class {
11976
11978
  return null;
11977
11979
  const key = normalizeUrl(url);
11978
11980
  const entry = this.store.urls[key];
11979
- if (!entry)
11981
+ if (!entry || this.isStale(entry))
11980
11982
  return null;
11981
- if (new Date(entry.expiresAt).getTime() <= Date.now()) {
11982
- delete this.store.urls[key];
11983
- return null;
11984
- }
11985
11983
  return {
11986
11984
  verdict: entry.verdict,
11987
11985
  severity: entry.severity,
@@ -11999,19 +11997,16 @@ var VerdictCache = class {
11999
11997
  this.store.urls[key] = {
12000
11998
  ...verdict,
12001
11999
  checkedAt: now.toISOString(),
12002
- expiresAt: expiresAt.toISOString()
12000
+ expiresAt: expiresAt.toISOString(),
12001
+ sageVersion: this.version
12003
12002
  };
12004
12003
  }
12005
12004
  getCommand(commandHash) {
12006
12005
  if (!this.config.enabled)
12007
12006
  return null;
12008
12007
  const entry = this.store.commands[commandHash];
12009
- if (!entry)
12008
+ if (!entry || this.isStale(entry))
12010
12009
  return null;
12011
- if (new Date(entry.expiresAt).getTime() <= Date.now()) {
12012
- delete this.store.commands[commandHash];
12013
- return null;
12014
- }
12015
12010
  return {
12016
12011
  verdict: entry.verdict,
12017
12012
  severity: entry.severity,
@@ -12026,19 +12021,16 @@ var VerdictCache = class {
12026
12021
  this.store.commands[commandHash] = {
12027
12022
  ...verdict,
12028
12023
  checkedAt: now.toISOString(),
12029
- expiresAt: FAR_FUTURE
12024
+ expiresAt: FAR_FUTURE,
12025
+ sageVersion: this.version
12030
12026
  };
12031
12027
  }
12032
12028
  getPackage(key) {
12033
12029
  if (!this.config.enabled)
12034
12030
  return null;
12035
12031
  const entry = this.store.packages[key];
12036
- if (!entry)
12032
+ if (!entry || this.isStale(entry))
12037
12033
  return null;
12038
- if (new Date(entry.expiresAt).getTime() <= Date.now()) {
12039
- delete this.store.packages[key];
12040
- return null;
12041
- }
12042
12034
  return {
12043
12035
  verdict: entry.verdict,
12044
12036
  severity: entry.severity,
@@ -12055,9 +12047,17 @@ var VerdictCache = class {
12055
12047
  this.store.packages[key] = {
12056
12048
  ...verdict,
12057
12049
  checkedAt: now.toISOString(),
12058
- expiresAt: expiresAt.toISOString()
12050
+ expiresAt: expiresAt.toISOString(),
12051
+ sageVersion: this.version
12059
12052
  };
12060
12053
  }
12054
+ isStale(entry) {
12055
+ if (new Date(entry.expiresAt).getTime() <= Date.now())
12056
+ return true;
12057
+ if (this.version && this.version !== "dev" && entry.sageVersion !== this.version)
12058
+ return true;
12059
+ return false;
12060
+ }
12061
12061
  computePackageTtl(verdict, packageAgeDays) {
12062
12062
  const isFresh = packageAgeDays !== null && packageAgeDays < 7;
12063
12063
  switch (verdict) {
@@ -12073,15 +12073,37 @@ var VerdictCache = class {
12073
12073
  if (!this.config.enabled)
12074
12074
  return;
12075
12075
  try {
12076
+ this.pruneStaleEntries();
12076
12077
  await atomicWriteJson(this.path, this.store);
12077
12078
  } catch (e) {
12078
12079
  this.logger.warn(`Failed to save cache to ${this.path}`, { error: String(e) });
12079
12080
  }
12080
12081
  }
12082
+ pruneStaleEntries() {
12083
+ for (const [key, entry] of Object.entries(this.store.urls)) {
12084
+ if (this.isStale(entry))
12085
+ delete this.store.urls[key];
12086
+ }
12087
+ for (const [key, entry] of Object.entries(this.store.commands)) {
12088
+ if (this.isStale(entry))
12089
+ delete this.store.commands[key];
12090
+ }
12091
+ for (const [key, entry] of Object.entries(this.store.packages)) {
12092
+ if (this.isStale(entry))
12093
+ delete this.store.packages[key];
12094
+ }
12095
+ }
12081
12096
  };
12082
12097
 
12098
+ // ../core/dist/clients/amsi-spawn.js
12099
+ var chldproc1 = "node:child_";
12100
+ var chldproc2 = "process";
12101
+ var chldproc = require(chldproc1 + chldproc2);
12102
+ function spawn(command, args, options) {
12103
+ return chldproc.spawn(command, args, options);
12104
+ }
12105
+
12083
12106
  // ../core/dist/clients/amsi.js
12084
- var import_node_child_process = require("node:child_process");
12085
12107
  var AMSI_RESULT_DETECTED = 32768;
12086
12108
  var AMSI_RESULT_BLOCKED_BY_ADMIN_START = 16384;
12087
12109
  var MAX_SCAN_LENGTH = 1048576;
@@ -12089,7 +12111,7 @@ var PS_TIMEOUT = 15e3;
12089
12111
  function isWSL() {
12090
12112
  if (process.platform !== "linux")
12091
12113
  return false;
12092
- return !!process.env.WSL_DISTRO_NAME;
12114
+ return !!process["env"]["WSL_DISTRO_NAME"];
12093
12115
  }
12094
12116
  function isAmsiSupported() {
12095
12117
  return process.platform === "win32" || isWSL();
@@ -12340,7 +12362,7 @@ var PersistentPowershellAmsiBackend = class {
12340
12362
  }
12341
12363
  async init() {
12342
12364
  try {
12343
- this.process = (0, import_node_child_process.spawn)("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", PS_PERSISTENT_SCRIPT], {
12365
+ this.process = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", PS_PERSISTENT_SCRIPT], {
12344
12366
  stdio: ["pipe", "pipe", "pipe"],
12345
12367
  windowsHide: true
12346
12368
  });
@@ -12464,7 +12486,7 @@ var WslPowershellAmsiBackend = class {
12464
12486
  const truncated = content.length > MAX_SCAN_LENGTH ? content.slice(0, MAX_SCAN_LENGTH) : content;
12465
12487
  return new Promise((resolve3) => {
12466
12488
  try {
12467
- const ps = (0, import_node_child_process.spawn)("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", PS_ONESHOT_SCRIPT], { stdio: ["pipe", "pipe", "pipe"] });
12489
+ const ps = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", PS_ONESHOT_SCRIPT], { stdio: ["pipe", "pipe", "pipe"] });
12468
12490
  let stdout = "";
12469
12491
  const timer = setTimeout(() => {
12470
12492
  this.logger.warn("AMSI: PowerShell one-shot timeout", { contentName });
@@ -12556,7 +12578,23 @@ var AmsiClient = class {
12556
12578
  };
12557
12579
 
12558
12580
  // ../core/dist/version.js
12559
- var VERSION = true ? "0.5.2" : "dev";
12581
+ var import_node_fs = require("node:fs");
12582
+ var import_node_path5 = require("node:path");
12583
+ var import_node_url = require("node:url");
12584
+ var import_meta = {};
12585
+ function resolveVersion() {
12586
+ if (true)
12587
+ return "0.6.0";
12588
+ try {
12589
+ const pkgPath = (0, import_node_path5.join)((0, import_node_path5.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
12590
+ const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf-8"));
12591
+ if (typeof pkg.version === "string")
12592
+ return pkg.version;
12593
+ } catch {
12594
+ }
12595
+ return "dev";
12596
+ }
12597
+ var VERSION = resolveVersion();
12560
12598
 
12561
12599
  // ../core/dist/clients/file-check.js
12562
12600
  var DEFAULT_TIMEOUT = 5;
@@ -13234,7 +13272,7 @@ function extractFromEdit(toolInput) {
13234
13272
 
13235
13273
  // ../core/dist/trusted-domains.js
13236
13274
  var import_promises2 = require("node:fs/promises");
13237
- var import_node_path5 = require("node:path");
13275
+ var import_node_path6 = require("node:path");
13238
13276
  var import_yaml = __toESM(require_dist(), 1);
13239
13277
  async function loadTrustedDomains(allowlistsDir, logger = nullLogger) {
13240
13278
  let files;
@@ -13246,7 +13284,7 @@ async function loadTrustedDomains(allowlistsDir, logger = nullLogger) {
13246
13284
  }
13247
13285
  const domains = [];
13248
13286
  for (const filename of files) {
13249
- const filePath = (0, import_node_path5.join)(allowlistsDir, filename);
13287
+ const filePath = (0, import_node_path6.join)(allowlistsDir, filename);
13250
13288
  let content;
13251
13289
  try {
13252
13290
  content = await getFileContent(filePath);
@@ -13824,7 +13862,7 @@ function extractFromRequirementsTxt(content) {
13824
13862
 
13825
13863
  // ../core/dist/threat-loader.js
13826
13864
  var import_promises3 = require("node:fs/promises");
13827
- var import_node_path6 = require("node:path");
13865
+ var import_node_path7 = require("node:path");
13828
13866
  var import_yaml2 = __toESM(require_dist(), 1);
13829
13867
  var REQUIRED_FIELDS = /* @__PURE__ */ new Set([
13830
13868
  "id",
@@ -13862,7 +13900,7 @@ async function loadThreats(threatDir, logger = nullLogger) {
13862
13900
  return threats;
13863
13901
  }
13864
13902
  for (const filename of files) {
13865
- const filePath = (0, import_node_path6.join)(threatDir, filename);
13903
+ const filePath = (0, import_node_path7.join)(threatDir, filename);
13866
13904
  let content;
13867
13905
  try {
13868
13906
  content = await getFileContent(filePath);
@@ -13958,7 +13996,7 @@ async function evaluateToolCall(request, context) {
13958
13996
  }
13959
13997
  let cache = null;
13960
13998
  try {
13961
- cache = new VerdictCache(config.cache, logger);
13999
+ cache = new VerdictCache(config.cache, logger, VERSION);
13962
14000
  await cache.load();
13963
14001
  } catch {
13964
14002
  cache = null;
@@ -14189,7 +14227,7 @@ function separatorLine(headerLength) {
14189
14227
  return "\u2501".repeat(headerLength);
14190
14228
  }
14191
14229
  function formatUpdateNotice(result) {
14192
- return `\u2B06\uFE0F Update available: v${result.currentVersion} \u2192 v${result.latestVersion} (https://github.com/avast/sage)`;
14230
+ return `\u2B06\uFE0F Update available: v${result.currentVersion} \u2192 v${result.latestVersion} (https://github.com/gendigitalinc/sage)`;
14193
14231
  }
14194
14232
  function formatStartupClean(version, versionCheck) {
14195
14233
  const base = `\u{1F6E1}\uFE0F Sage v${version} by Gen Digital \u2705 No threats found`;
@@ -14377,13 +14415,13 @@ async function removeFromAllowlist(type, value, configPath, logger = nullLogger)
14377
14415
  // ../core/dist/installation-id.js
14378
14416
  var import_node_crypto4 = require("node:crypto");
14379
14417
  var import_promises4 = require("node:fs/promises");
14380
- var import_node_path7 = require("node:path");
14418
+ var import_node_path8 = require("node:path");
14381
14419
  async function getInstallationId(sageDirPath) {
14382
14420
  const sageDir = sageDirPath ?? resolvePath("~/.sage");
14383
- const idPath = (0, import_node_path7.join)(sageDir, "installation-id");
14421
+ const idPath = (0, import_node_path8.join)(sageDir, "installation-id");
14384
14422
  let fileExists = false;
14385
14423
  try {
14386
- const existing = await (0, import_promises4.readFile)(idPath, "utf-8");
14424
+ const existing = await getFileContent(idPath, "utf-8");
14387
14425
  const trimmed = existing.trim();
14388
14426
  if (trimmed.length > 0)
14389
14427
  return trimmed;
@@ -14398,7 +14436,7 @@ async function getInstallationId(sageDirPath) {
14398
14436
  } catch (err) {
14399
14437
  if (err.code === "EEXIST") {
14400
14438
  try {
14401
- const existing = await (0, import_promises4.readFile)(idPath, "utf-8");
14439
+ const existing = await getFileContent(idPath, "utf-8");
14402
14440
  return existing.trim() || void 0;
14403
14441
  } catch {
14404
14442
  return void 0;
@@ -14412,8 +14450,8 @@ async function getInstallationId(sageDirPath) {
14412
14450
  var import_node_crypto5 = require("node:crypto");
14413
14451
  var import_promises5 = require("node:fs/promises");
14414
14452
  var import_node_os3 = require("node:os");
14415
- var import_node_path8 = require("node:path");
14416
- var DEFAULT_CACHE_PATH = (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".sage", "plugin_scan_cache.json");
14453
+ var import_node_path9 = require("node:path");
14454
+ var DEFAULT_CACHE_PATH = (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".sage", "plugin_scan_cache.json");
14417
14455
  var CACHE_TTL_DAYS = 7;
14418
14456
  function cacheKey(pluginKey, version, lastUpdated) {
14419
14457
  return `${pluginKey}:${version}:${lastUpdated}`;
@@ -14431,7 +14469,7 @@ async function computeConfigHash(sageVersion, ...dirs) {
14431
14469
  }
14432
14470
  for (const file of files) {
14433
14471
  try {
14434
- const content = await getFileContent((0, import_node_path8.join)(dir, file));
14472
+ const content = await getFileContent((0, import_node_path9.join)(dir, file));
14435
14473
  h.update(content);
14436
14474
  } catch {
14437
14475
  }
@@ -14524,12 +14562,14 @@ function storeResult(cache, pluginKey, version, lastUpdated, findings) {
14524
14562
  var import_node_crypto6 = require("node:crypto");
14525
14563
  var import_promises6 = require("node:fs/promises");
14526
14564
  var import_node_os4 = require("node:os");
14527
- var import_node_path9 = require("node:path");
14528
- var DEFAULT_PLUGINS_REGISTRY = (0, import_node_path9.join)((0, import_node_os4.homedir)(), ".claude", "plugins", "installed_plugins.json");
14565
+ var import_node_path10 = require("node:path");
14566
+ var DEFAULT_PLUGINS_REGISTRY = (0, import_node_path10.join)((0, import_node_os4.homedir)(), ".claude", "plugins", "installed_plugins.json");
14529
14567
  var SCANNABLE_EXTENSIONS = /* @__PURE__ */ new Set([
14530
14568
  ".py",
14531
14569
  ".js",
14532
14570
  ".ts",
14571
+ ".mjs",
14572
+ ".mts",
14533
14573
  ".sh",
14534
14574
  ".bash",
14535
14575
  ".zsh",
@@ -14555,7 +14595,7 @@ async function walkPluginFiles(installPath, logger) {
14555
14595
  return;
14556
14596
  }
14557
14597
  if (stats.isFile()) {
14558
- if (SCANNABLE_EXTENSIONS.has((0, import_node_path9.extname)(dirOrFile).toLowerCase()) && stats.size <= MAX_FILE_SIZE) {
14598
+ if (SCANNABLE_EXTENSIONS.has((0, import_node_path10.extname)(dirOrFile).toLowerCase()) && stats.size <= MAX_FILE_SIZE) {
14559
14599
  files.push(dirOrFile);
14560
14600
  }
14561
14601
  return;
@@ -14570,7 +14610,7 @@ async function walkPluginFiles(installPath, logger) {
14570
14610
  for (const entry of entries) {
14571
14611
  if (SKIP_DIRS.has(entry))
14572
14612
  continue;
14573
- const fullPath = (0, import_node_path9.join)(dirOrFile, entry);
14613
+ const fullPath = (0, import_node_path10.join)(dirOrFile, entry);
14574
14614
  await walk(fullPath);
14575
14615
  }
14576
14616
  }
@@ -14588,6 +14628,82 @@ function isHarmlessEcho(line) {
14588
14628
  const withoutQuotes = line.replace(/"(?:[^"\\]|\\.)*"|'[^']*'/g, "");
14589
14629
  return !withoutQuotes.includes("|");
14590
14630
  }
14631
+ var JS_TS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".ts", ".mjs", ".mts"]);
14632
+ var STRING_OR_COMMENT_RE = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`|\/\/.*$|\/\*[\s\S]*?\*\//gm;
14633
+ function stripJsComments(src) {
14634
+ return src.replace(STRING_OR_COMMENT_RE, (match) => {
14635
+ if (match.startsWith("//") || match.startsWith("/*"))
14636
+ return "";
14637
+ return match;
14638
+ });
14639
+ }
14640
+ var STR_ARG = `(?:"((?:[^"\\\\]|\\\\.)*)"|'((?:[^'\\\\]|\\\\.)*)'|\`([^\`]*)\`)`;
14641
+ var JS_EXEC_RE = new RegExp(`\\bexec(?:File)?(?:Sync)?\\s*\\(\\s*${STR_ARG}`, "g");
14642
+ var JS_SPAWN_RE = new RegExp(`\\bspawn(?:Sync)?\\s*\\(\\s*${STR_ARG}`, "g");
14643
+ var JS_SPAWN_ARGS_RE = /\bspawn(?:Sync)?\s*\(\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|`([^`]*)`)\s*,\s*\[([^\]]*)\]/g;
14644
+ var SHELL_EXECUTABLES = /* @__PURE__ */ new Set([
14645
+ "bash",
14646
+ "sh",
14647
+ "zsh",
14648
+ "dash",
14649
+ "ksh",
14650
+ "csh",
14651
+ "fish",
14652
+ "cmd",
14653
+ "cmd.exe",
14654
+ "powershell",
14655
+ "powershell.exe",
14656
+ "pwsh"
14657
+ ]);
14658
+ function isShellExecutable(exe) {
14659
+ const basename = exe.split("/").pop()?.split("\\").pop() ?? exe;
14660
+ return SHELL_EXECUTABLES.has(basename);
14661
+ }
14662
+ var JS_EXECA_RE = new RegExp(`\\bexeca\\s*\\(\\s*${STR_ARG}`, "g");
14663
+ var JS_EXECA_ARGS_RE = /\bexeca\s*\(\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)'|`([^`]*)`)\s*,\s*\[([^\]]*)\]/g;
14664
+ var JS_BUN_SHELL_RE = new RegExp(`\\bBun\\.shell\\s*\\(\\s*${STR_ARG}`, "g");
14665
+ var JS_BUN_DOLLAR_RE = /\bBun\.\$`([^`]+)`/g;
14666
+ var JS_ZX_RE = /(?<!\w)\$`([^`]+)`/g;
14667
+ function extractCommandsFromJsTs(content, fileName) {
14668
+ const commands = /* @__PURE__ */ new Set();
14669
+ const stripped = stripJsComments(content);
14670
+ const collapsed = stripped.replace(/\n/g, " ");
14671
+ function addMatch(m) {
14672
+ const val = m[1] ?? m[2] ?? m[3];
14673
+ if (val)
14674
+ commands.add(val.trim());
14675
+ }
14676
+ for (const re of [JS_EXEC_RE, JS_SPAWN_RE, JS_EXECA_RE, JS_BUN_SHELL_RE]) {
14677
+ for (const m of collapsed.matchAll(re)) {
14678
+ addMatch(m);
14679
+ }
14680
+ }
14681
+ for (const re of [JS_BUN_DOLLAR_RE, JS_ZX_RE]) {
14682
+ for (const m of collapsed.matchAll(re)) {
14683
+ if (m[1])
14684
+ commands.add(m[1].trim());
14685
+ }
14686
+ }
14687
+ for (const re of [JS_SPAWN_ARGS_RE, JS_EXECA_ARGS_RE]) {
14688
+ for (const arrMatch of collapsed.matchAll(re)) {
14689
+ const exe = (arrMatch[1] ?? arrMatch[2] ?? arrMatch[3] ?? "").trim();
14690
+ if (!isShellExecutable(exe))
14691
+ continue;
14692
+ const arrayContent = arrMatch[4] ?? "";
14693
+ const strLitRe = new RegExp(STR_ARG, "g");
14694
+ for (const strMatch of arrayContent.matchAll(strLitRe)) {
14695
+ const val = strMatch[1] ?? strMatch[2] ?? strMatch[3];
14696
+ if (val)
14697
+ commands.add(val.trim());
14698
+ }
14699
+ }
14700
+ }
14701
+ return [...commands].map((value) => ({
14702
+ type: "command",
14703
+ value,
14704
+ context: `plugin_file:${fileName}`
14705
+ }));
14706
+ }
14591
14707
  function extractArtifactsFromFile(filePath, content) {
14592
14708
  const artifacts = [];
14593
14709
  const fileName = filePath.split("/").pop() ?? filePath;
@@ -14596,7 +14712,7 @@ function extractArtifactsFromFile(filePath, content) {
14596
14712
  continue;
14597
14713
  artifacts.push({ type: "url", value: url, context: `plugin_file:${fileName}` });
14598
14714
  }
14599
- const ext = (0, import_node_path9.extname)(filePath).toLowerCase();
14715
+ const ext = (0, import_node_path10.extname)(filePath).toLowerCase();
14600
14716
  if ([".sh", ".bash", ".zsh", ".py"].includes(ext)) {
14601
14717
  for (const line of content.split("\n")) {
14602
14718
  const trimmed = line.trim();
@@ -14609,6 +14725,9 @@ function extractArtifactsFromFile(filePath, content) {
14609
14725
  }
14610
14726
  }
14611
14727
  }
14728
+ if (JS_TS_EXTENSIONS.has(ext)) {
14729
+ artifacts.push(...extractCommandsFromJsTs(content, fileName));
14730
+ }
14612
14731
  return artifacts;
14613
14732
  }
14614
14733
  async function scanPlugin(plugin, threats, options = {}) {
@@ -14641,7 +14760,7 @@ async function scanPlugin(plugin, threats, options = {}) {
14641
14760
  confidence: match.threat.confidence,
14642
14761
  action: match.threat.action,
14643
14762
  artifact: match.artifact.slice(0, 200),
14644
- sourceFile: (0, import_node_path9.relative)(plugin.installPath, filePath)
14763
+ sourceFile: (0, import_node_path10.relative)(plugin.installPath, filePath)
14645
14764
  });
14646
14765
  }
14647
14766
  }
@@ -14696,7 +14815,7 @@ async function scanPlugin(plugin, threats, options = {}) {
14696
14815
  confidence: 1,
14697
14816
  action: "block",
14698
14817
  artifact: fr.sha256,
14699
- sourceFile: (0, import_node_path9.relative)(plugin.installPath, filePath)
14818
+ sourceFile: (0, import_node_path10.relative)(plugin.installPath, filePath)
14700
14819
  });
14701
14820
  }
14702
14821
  }
@@ -14969,20 +15088,19 @@ function createAllowlistRemoveTool(logger) {
14969
15088
  }
14970
15089
 
14971
15090
  // src/bundled-dirs.ts
14972
- var import_node_fs = require("node:fs");
14973
- var import_node_path10 = require("node:path");
14974
- var packageRoot = (0, import_node_path10.resolve)(__dirname, "..");
15091
+ var import_node_path11 = require("node:path");
15092
+ var packageRoot = (0, import_node_path11.resolve)(__dirname, "..");
14975
15093
  function getBundledDataDirs() {
14976
15094
  return {
14977
- threatsDir: (0, import_node_path10.join)(packageRoot, "resources", "threats"),
14978
- allowlistsDir: (0, import_node_path10.join)(packageRoot, "resources", "allowlists")
15095
+ threatsDir: (0, import_node_path11.join)(packageRoot, "resources", "threats"),
15096
+ allowlistsDir: (0, import_node_path11.join)(packageRoot, "resources", "allowlists")
14979
15097
  };
14980
15098
  }
14981
15099
  var cachedVersion;
14982
15100
  function getSageVersion() {
14983
15101
  if (cachedVersion) return cachedVersion;
14984
15102
  try {
14985
- const pkg = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path10.join)(packageRoot, "package.json"), "utf-8"));
15103
+ const pkg = JSON.parse(getFileContentSync((0, import_node_path11.join)(packageRoot, "package.json"), "utf-8"));
14986
15104
  cachedVersion = pkg.version ?? "0.0.0";
14987
15105
  } catch {
14988
15106
  cachedVersion = "0.0.0";
@@ -15048,8 +15166,8 @@ function createLogger(pluginLogger) {
15048
15166
  // src/plugin-discovery.ts
15049
15167
  var import_promises7 = require("node:fs/promises");
15050
15168
  var import_node_os6 = require("node:os");
15051
- var import_node_path11 = require("node:path");
15052
- var DEFAULT_EXTENSIONS_DIR = (0, import_node_path11.join)((0, import_node_os6.homedir)(), ".openclaw", "extensions");
15169
+ var import_node_path12 = require("node:path");
15170
+ var DEFAULT_EXTENSIONS_DIR = (0, import_node_path12.join)((0, import_node_os6.homedir)(), ".openclaw", "extensions");
15053
15171
  async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSIONS_DIR) {
15054
15172
  logger.info("Sage plugin discovery: scanning extensions directory", { path: extensionsDir });
15055
15173
  let entries;
@@ -15061,7 +15179,7 @@ async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSION
15061
15179
  }
15062
15180
  const plugins = [];
15063
15181
  for (const entry of entries) {
15064
- const extDir = (0, import_node_path11.join)(extensionsDir, entry);
15182
+ const extDir = (0, import_node_path12.join)(extensionsDir, entry);
15065
15183
  let stats;
15066
15184
  try {
15067
15185
  stats = await (0, import_promises7.stat)(extDir);
@@ -15071,7 +15189,7 @@ async function discoverOpenClawPlugins(logger, extensionsDir = DEFAULT_EXTENSION
15071
15189
  }
15072
15190
  let pkg;
15073
15191
  try {
15074
- const pkgPath = (0, import_node_path11.join)(extDir, "package.json");
15192
+ const pkgPath = (0, import_node_path12.join)(extDir, "package.json");
15075
15193
  const raw = await getFileContent(pkgPath);
15076
15194
  pkg = JSON.parse(raw);
15077
15195
  } catch {
@@ -15101,7 +15219,7 @@ function createOpenClawScanHandler(logger, context, onResult) {
15101
15219
  logger,
15102
15220
  context,
15103
15221
  discoverPlugins: () => discoverOpenClawPlugins(logger),
15104
- selfPrefix: "sage-openclaw@",
15222
+ selfPrefix: "@gendigital/sage-openclaw@",
15105
15223
  threatsDir,
15106
15224
  allowlistsDir,
15107
15225
  version,