@gendigital/sage-openclaw 0.10.0 → 0.12.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 +5 -5
- package/dist/index.cjs +1881 -977
- package/dist/index.cjs.map +4 -4
- package/dist/mcp-server.cjs +31882 -0
- package/dist/mcp-server.cjs.map +7 -0
- package/dist/model-download-worker.cjs +140 -111
- package/dist/model-download-worker.cjs.map +3 -3
- package/dist/skill-upload-worker.cjs +5632 -0
- package/dist/skill-upload-worker.cjs.map +7 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +9 -3
- package/resources/skills/security-awareness/SKILL.md +5 -1
- package/resources/threats/agent-layer.yaml +186 -0
- package/resources/threats/commands.yaml +24 -53
- package/resources/threats/credentials.yaml +12 -20
- package/resources/threats/dummy.yaml +44 -50
- package/resources/threats/files.yaml +34 -53
- package/resources/threats/mac-commands.yaml +20 -47
- package/resources/threats/mac-credentials.yaml +4 -12
- package/resources/threats/mac-files.yaml +5 -15
- package/resources/threats/mac-obfuscation.yaml +6 -13
- package/resources/threats/mac-persistence.yaml +11 -20
- package/resources/threats/mac-supply-chain.yaml +5 -8
- package/resources/threats/mitre.yaml +105 -167
- package/resources/threats/obfuscation.yaml +7 -15
- package/resources/threats/persistence.yaml +10 -17
- package/resources/threats/prompt-injection.yaml +92 -43
- package/resources/threats/self-defense.yaml +8 -12
- package/resources/threats/supply_chain.yaml +1 -2
- package/resources/threats/urls.yaml +9 -14
- package/resources/threats/win-commands.yaml +46 -106
- package/resources/threats/win-credentials.yaml +14 -24
- package/resources/threats/win-files.yaml +10 -20
- package/resources/threats/win-obfuscation.yaml +21 -37
- package/resources/threats/win-persistence.yaml +8 -16
- package/resources/threats/win-supply-chain.yaml +2 -3
- /package/resources/{allowlists → trusted-domains}/trusted-installer-domains.yaml +0 -0
|
@@ -67,7 +67,7 @@ function Pn(s3, t) {
|
|
|
67
67
|
function zn(s3, t) {
|
|
68
68
|
s3.head = new ue(t, void 0, s3.head, s3), s3.tail || (s3.tail = s3.head), s3.length++;
|
|
69
69
|
}
|
|
70
|
-
var import_events, import_fs, import_node_events, import_node_stream, import_node_string_decoder, import_node_path4, import_node_fs2, import_path, import_events2, import_assert, import_buffer, ks, import_zlib, import_node_path5, import_node_path6, import_fs2, import_fs3, import_path2, import_node_path7, import_path3, import_node_fs3, import_node_assert, import_node_crypto2, import_node_fs4, import_node_path8, import_fs4, import_node_fs5, import_node_path9, import_node_fs6,
|
|
70
|
+
var import_events, import_fs, import_node_events, import_node_stream, import_node_string_decoder, import_node_path4, import_node_fs2, import_path, import_events2, import_assert, import_buffer, ks, import_zlib, import_node_path5, import_node_path6, import_fs2, import_fs3, import_path2, import_node_path7, import_path3, import_node_fs3, import_node_assert, import_node_crypto2, import_node_fs4, import_node_path8, import_fs4, import_node_fs5, import_node_path9, import_node_fs6, import_promises3, import_node_path10, import_node_path11, import_node_fs7, import_node_path12, kr, vr, Os, Br, Pr, zr, q, j, rt, Le, jt, Ne, Ts, Ae, xs, z, Mt, b, Qt, Bt, _, A, g, yi, De, L, w, Ri, bi, Ls, _i, Z, gi, Ie, Jt, yt, C, te, Ur, Hr, Wr, Gr, Ce, Oi, Zr, Yr, D, Vr, ot, H, ee, m, xi, J, Li, Ii, Ci, se, Fe, Ut, Ht, Ni, Pt, ht, U, nt, Y, zt, Ai, Q, ie, Di, ke, Rt, ve, bt, _t, Me, tt, Wt, $r, As, Ds, Is, Cs, Fs, Xr, re, K, jr, M, Qr, vs, Jr, ki, Ot, Gt, vi, ne, Be, Pe, ze, Ue, He, We, Ge, Ze, Ye, Ms, en, sn, Bs, rn, nn, Ps, zs, Bi, oe, hn, he, Ke, F, an, Tt, Pi, ln, at, cn, fn, dn, lt, un, mn, pn, zi, En, xt, ct, Sn, yn, Rn, bn, f, Yt, Lt, gn, Zi, Yi, On, B, Nt, et, Ui, Us, V, ae, ft, Hs, p, it, dt, Hi, At, y, Ve, $e, Wi, Ws, Gs, le, Gi, Xe, Kt, ut, qe, Dt, je, Qe, Zs, Tn, st, mt, Nn, Ki, An, Dn, It, Vi, Cn, Ys, ce, Je, $i, Fn, kn, Xi, Ks, Js, vn, Xs, qs, js, ji, Qs, fe, ti, Qi, ei, Ji, ts, es, is, pt, ii, ss, qi, X, de, si, ri, Mn, ni, ue, di, tr, oi, me, W, Ct, Ft, pe, rs, G, ns, hi, er, as, ls, ai, li, ir, os, ci, rr, hs, Et, kt, Un, Hn, or, hr, Wn, Gn, Zn, Yn, fr, dr, ar, ur, mr, pr, Kn, Vn, $n, lr, cs, fs2, ui, Xn, ds, qn, us, we, wt, Qn, Er, ms, wr, Jn, Sr, ps, yr, $t, Rr, to, eo, io, Ei, _r, gr, ys, Or, Rs, P, bs, _s, Si, Tr, xr, ye, Lr, Nr, Es, St, O, wi, Ar, Xt, ws, Ss, gs, Re, be, _e, ge, ro, Oe, no, oo, ho, Dr, qt, Se, Te, ao, lo, co, fo, uo, mo, po, Eo, vt, wo, So;
|
|
71
71
|
var init_index_min = __esm({
|
|
72
72
|
"../../node_modules/.pnpm/tar@7.5.13/node_modules/tar/dist/esm/index.min.js"() {
|
|
73
73
|
import_events = __toESM(require("events"), 1);
|
|
@@ -99,7 +99,7 @@ var init_index_min = __esm({
|
|
|
99
99
|
import_node_fs5 = __toESM(require("node:fs"), 1);
|
|
100
100
|
import_node_path9 = __toESM(require("node:path"), 1);
|
|
101
101
|
import_node_fs6 = __toESM(require("node:fs"), 1);
|
|
102
|
-
|
|
102
|
+
import_promises3 = __toESM(require("node:fs/promises"), 1);
|
|
103
103
|
import_node_path10 = __toESM(require("node:path"), 1);
|
|
104
104
|
import_node_path11 = require("node:path");
|
|
105
105
|
import_node_fs7 = __toESM(require("node:fs"), 1);
|
|
@@ -2258,7 +2258,7 @@ var init_index_min = __esm({
|
|
|
2258
2258
|
E ? e(E) : x && a ? ds(x, o, h, (xe) => S(xe)) : n ? import_node_fs6.default.chmod(s3, r, e) : e();
|
|
2259
2259
|
};
|
|
2260
2260
|
if (s3 === d) return Qn(s3, S);
|
|
2261
|
-
if (l) return
|
|
2261
|
+
if (l) return import_promises3.default.mkdir(s3, { mode: r, recursive: true }).then((E) => S(null, E ?? void 0), S);
|
|
2262
2262
|
let N = f(import_node_path10.default.relative(d, s3)).split("/");
|
|
2263
2263
|
ms(d, N, r, c, d, void 0, S);
|
|
2264
2264
|
};
|
|
@@ -2970,6 +2970,7 @@ var import_node_path = require("node:path");
|
|
|
2970
2970
|
var fs = __toESM(require("node:fs"), 1);
|
|
2971
2971
|
var fsPromises = __toESM(require("node:fs/promises"), 1);
|
|
2972
2972
|
var import_node_os = require("node:os");
|
|
2973
|
+
var import_promises = require("node:timers/promises");
|
|
2973
2974
|
var name1 = "read";
|
|
2974
2975
|
var name2 = "File";
|
|
2975
2976
|
function getFileContent(path, encoding = "utf-8") {
|
|
@@ -2984,6 +2985,43 @@ function getProcEnv() {
|
|
|
2984
2985
|
function getHomeDir() {
|
|
2985
2986
|
return getProcEnv().HOME || (0, import_node_os.homedir)();
|
|
2986
2987
|
}
|
|
2988
|
+
async function removeStaleFileLock(lockPath, staleAgeMs) {
|
|
2989
|
+
try {
|
|
2990
|
+
const s3 = await fsPromises.stat(lockPath);
|
|
2991
|
+
if (Date.now() - s3.mtimeMs < staleAgeMs) return;
|
|
2992
|
+
await fsPromises.rmdir(lockPath);
|
|
2993
|
+
} catch {
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
async function acquireFileLock(filePath, timeoutMs = 250, staleAgeMs = 3e4) {
|
|
2997
|
+
const lockPath = `${filePath}.lock`;
|
|
2998
|
+
const deadline = Date.now() + timeoutMs;
|
|
2999
|
+
while (true) {
|
|
3000
|
+
try {
|
|
3001
|
+
await fsPromises.mkdir(lockPath);
|
|
3002
|
+
return async () => {
|
|
3003
|
+
try {
|
|
3004
|
+
await fsPromises.rmdir(lockPath);
|
|
3005
|
+
} catch {
|
|
3006
|
+
}
|
|
3007
|
+
};
|
|
3008
|
+
} catch {
|
|
3009
|
+
await removeStaleFileLock(lockPath, staleAgeMs);
|
|
3010
|
+
const remainingMs = deadline - Date.now();
|
|
3011
|
+
if (remainingMs <= 0) return void 0;
|
|
3012
|
+
await (0, import_promises.setTimeout)(Math.min(50, remainingMs));
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
async function withFileLock(filePath, callback) {
|
|
3017
|
+
const release = await acquireFileLock(filePath);
|
|
3018
|
+
if (!release) return;
|
|
3019
|
+
try {
|
|
3020
|
+
return await callback();
|
|
3021
|
+
} finally {
|
|
3022
|
+
await release();
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
2987
3025
|
|
|
2988
3026
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
2989
3027
|
var external_exports = {};
|
|
@@ -7043,23 +7081,21 @@ var ArtifactSchema = external_exports.object({
|
|
|
7043
7081
|
value: external_exports.string(),
|
|
7044
7082
|
context: external_exports.string().optional()
|
|
7045
7083
|
});
|
|
7046
|
-
var
|
|
7047
|
-
var ActionSchema = external_exports.enum(["block", "require_approval", "log"]);
|
|
7084
|
+
var VerdictSeveritySchema = external_exports.enum(["info", "warning", "critical"]);
|
|
7048
7085
|
var ThreatSchema = external_exports.object({
|
|
7049
7086
|
id: external_exports.string(),
|
|
7050
7087
|
version: external_exports.number().int().optional(),
|
|
7051
7088
|
category: external_exports.string(),
|
|
7052
|
-
severity:
|
|
7089
|
+
severity: VerdictSeveritySchema,
|
|
7053
7090
|
confidence: external_exports.number(),
|
|
7054
|
-
action: ActionSchema,
|
|
7055
7091
|
pattern: external_exports.string(),
|
|
7056
7092
|
match_on: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]),
|
|
7057
7093
|
title: external_exports.string(),
|
|
7058
7094
|
expires_at: external_exports.string().nullable().optional(),
|
|
7059
|
-
revoked: external_exports.boolean().optional().default(false)
|
|
7095
|
+
revoked: external_exports.boolean().optional().default(false),
|
|
7096
|
+
flags: external_exports.array(external_exports.string()).optional().default([])
|
|
7060
7097
|
});
|
|
7061
7098
|
var DecisionSchema = external_exports.enum(["allow", "deny", "ask"]);
|
|
7062
|
-
var VerdictSeveritySchema = external_exports.enum(["info", "warning", "critical"]);
|
|
7063
7099
|
var SensitivitySchema = external_exports.enum(["paranoid", "balanced", "relaxed"]);
|
|
7064
7100
|
var UrlCheckConfigSchema = external_exports.object({
|
|
7065
7101
|
endpoint: external_exports.string().optional(),
|
|
@@ -7072,9 +7108,6 @@ var CacheConfigSchema = external_exports.object({
|
|
|
7072
7108
|
ttl_clean_seconds: external_exports.number().default(86400),
|
|
7073
7109
|
path: external_exports.string().default("~/.sage/cache.json")
|
|
7074
7110
|
});
|
|
7075
|
-
var AllowlistConfigSchema = external_exports.object({
|
|
7076
|
-
path: external_exports.string().default("~/.sage/allowlist.json")
|
|
7077
|
-
});
|
|
7078
7111
|
var LoggingConfigSchema = external_exports.object({
|
|
7079
7112
|
enabled: external_exports.boolean().default(true),
|
|
7080
7113
|
log_clean: external_exports.boolean().default(false),
|
|
@@ -7104,14 +7137,21 @@ var PackageCheckConfigSchema = external_exports.object({
|
|
|
7104
7137
|
var AmsiCheckConfigSchema = external_exports.object({
|
|
7105
7138
|
enabled: external_exports.boolean().default(true)
|
|
7106
7139
|
});
|
|
7107
|
-
var
|
|
7108
|
-
|
|
7140
|
+
var SkillCheckConfigSchema = external_exports.object({
|
|
7141
|
+
enabled: external_exports.boolean().default(true),
|
|
7142
|
+
cache_ttl_days: external_exports.number().min(0).default(1),
|
|
7143
|
+
/**
|
|
7144
|
+
* Upload unknown skills (never seen by the analyzer) for deep content
|
|
7145
|
+
* analysis. When false, the scan still looks skills up by content hash and
|
|
7146
|
+
* still flags known-risky ones — but no skill content ever leaves the
|
|
7147
|
+
* machine and the upload worker never runs (lookup-only mode).
|
|
7148
|
+
*/
|
|
7149
|
+
upload_enabled: external_exports.boolean().default(true)
|
|
7150
|
+
});
|
|
7109
7151
|
var PiCheckConfigSchema = external_exports.object({
|
|
7110
7152
|
enabled: external_exports.boolean().default(false),
|
|
7111
7153
|
max_content_length: external_exports.number().default(16384),
|
|
7112
|
-
model_path: external_exports.string().optional()
|
|
7113
|
-
high_risk_threshold: external_exports.number().default(DEFAULT_PI_HIGH_RISK_THRESHOLD),
|
|
7114
|
-
medium_risk_threshold: external_exports.number().default(DEFAULT_PI_MEDIUM_RISK_THRESHOLD)
|
|
7154
|
+
model_path: external_exports.string().optional()
|
|
7115
7155
|
});
|
|
7116
7156
|
var ExceptionDecisionSchema = external_exports.enum(["allow", "deny"]);
|
|
7117
7157
|
var ExceptionMatchSchema = external_exports.enum(["executable", "domain", "path", "plugin", "regex"]);
|
|
@@ -7133,15 +7173,16 @@ var ConfigSchema = external_exports.object({
|
|
|
7133
7173
|
file_check: FileCheckConfigSchema.default({}),
|
|
7134
7174
|
package_check: PackageCheckConfigSchema.default({}),
|
|
7135
7175
|
amsi_check: AmsiCheckConfigSchema.default({}),
|
|
7176
|
+
skill_check: SkillCheckConfigSchema.default({}),
|
|
7136
7177
|
pi_check: PiCheckConfigSchema.default({}),
|
|
7137
7178
|
heuristics_enabled: external_exports.boolean().default(true),
|
|
7138
7179
|
cache: CacheConfigSchema.default({}),
|
|
7139
|
-
allowlist: AllowlistConfigSchema.default({}),
|
|
7140
7180
|
exceptions: ExceptionsConfigSchema.default({}),
|
|
7141
7181
|
logging: LoggingConfigSchema.default({}),
|
|
7142
7182
|
operational_logging: OperationalLoggingConfigSchema.default({}),
|
|
7143
7183
|
sensitivity: SensitivitySchema.default("balanced"),
|
|
7144
7184
|
disabled_threats: external_exports.array(external_exports.string()).default([]),
|
|
7185
|
+
announce_clean_scans: external_exports.boolean().default(true),
|
|
7145
7186
|
brand_key: external_exports.string().min(1).max(32).regex(/^[a-z0-9_-]+$/u).optional(),
|
|
7146
7187
|
community_iq: external_exports.boolean().default(true)
|
|
7147
7188
|
});
|
|
@@ -7156,6 +7197,8 @@ var HookTypeSchema = external_exports.enum([
|
|
|
7156
7197
|
|
|
7157
7198
|
// ../core/src/config.ts
|
|
7158
7199
|
var SAGE_DIR = "~/.sage";
|
|
7200
|
+
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
7201
|
+
var CLOCK_SKEW_TOLERANCE_MS = 5 * 60 * 1e3;
|
|
7159
7202
|
function resolvedSageDir() {
|
|
7160
7203
|
return resolvePath(SAGE_DIR);
|
|
7161
7204
|
}
|
|
@@ -7165,9 +7208,6 @@ function defaultConfigPath() {
|
|
|
7165
7208
|
function defaultCachePath() {
|
|
7166
7209
|
return (0, import_node_path.join)(resolvedSageDir(), "cache.json");
|
|
7167
7210
|
}
|
|
7168
|
-
function defaultAllowlistPath() {
|
|
7169
|
-
return (0, import_node_path.join)(resolvedSageDir(), "allowlist.json");
|
|
7170
|
-
}
|
|
7171
7211
|
function defaultExceptionsPath() {
|
|
7172
7212
|
return (0, import_node_path.join)(resolvedSageDir(), "exceptions.json");
|
|
7173
7213
|
}
|
|
@@ -7225,13 +7265,12 @@ function sanitizeBrandKey(data, logger) {
|
|
|
7225
7265
|
if (typeof brandKey === "string" && brandKey.length >= 1 && brandKey.length <= 32 && BRAND_KEY_RE.test(brandKey)) {
|
|
7226
7266
|
return data;
|
|
7227
7267
|
}
|
|
7228
|
-
logger.warn(`Invalid brand_key in config
|
|
7268
|
+
logger.warn(`Invalid brand_key in config - ignoring`, { brand_key: brandKey });
|
|
7229
7269
|
const { brand_key: _2, ...rest } = data;
|
|
7230
7270
|
return rest;
|
|
7231
7271
|
}
|
|
7232
7272
|
function sanitizeConfigPaths(config, logger) {
|
|
7233
7273
|
const cachePath = defaultCachePath();
|
|
7234
|
-
const allowlistPath = defaultAllowlistPath();
|
|
7235
7274
|
const exceptionsPath = defaultExceptionsPath();
|
|
7236
7275
|
const auditPath = defaultAuditPath();
|
|
7237
7276
|
const operationalLogPath = defaultOperationalLogPath();
|
|
@@ -7241,10 +7280,6 @@ function sanitizeConfigPaths(config, logger) {
|
|
|
7241
7280
|
...config.cache,
|
|
7242
7281
|
path: normalizeStateFilePath(config.cache.path, cachePath, "cache", logger)
|
|
7243
7282
|
},
|
|
7244
|
-
allowlist: {
|
|
7245
|
-
...config.allowlist,
|
|
7246
|
-
path: normalizeStateFilePath(config.allowlist.path, allowlistPath, "allowlist", logger)
|
|
7247
|
-
},
|
|
7248
7283
|
exceptions: {
|
|
7249
7284
|
...config.exceptions,
|
|
7250
7285
|
path: normalizeStateFilePath(config.exceptions.path, exceptionsPath, "exceptions", logger)
|
|
@@ -7298,7 +7333,7 @@ async function loadConfig(configPath, logger = nullLogger) {
|
|
|
7298
7333
|
|
|
7299
7334
|
// ../core/src/installation-id.ts
|
|
7300
7335
|
var import_node_crypto = require("node:crypto");
|
|
7301
|
-
var
|
|
7336
|
+
var import_promises2 = require("node:fs/promises");
|
|
7302
7337
|
var import_node_path2 = require("node:path");
|
|
7303
7338
|
async function getInstallationId(sageDirPath) {
|
|
7304
7339
|
const sageDir = sageDirPath ?? resolvePath("~/.sage");
|
|
@@ -7313,8 +7348,8 @@ async function getInstallationId(sageDirPath) {
|
|
|
7313
7348
|
}
|
|
7314
7349
|
try {
|
|
7315
7350
|
const id = (0, import_node_crypto.randomUUID)();
|
|
7316
|
-
await (0,
|
|
7317
|
-
await (0,
|
|
7351
|
+
await (0, import_promises2.mkdir)(sageDir, { recursive: true, mode: 448 });
|
|
7352
|
+
await (0, import_promises2.writeFile)(idPath, id, { encoding: "utf-8", mode: 384, flag: fileExists ? "w" : "wx" });
|
|
7318
7353
|
return id;
|
|
7319
7354
|
} catch (err) {
|
|
7320
7355
|
if (err.code === "EEXIST") {
|
|
@@ -7332,17 +7367,18 @@ async function getInstallationId(sageDirPath) {
|
|
|
7332
7367
|
// ../core/src/clients/model-downloader.ts
|
|
7333
7368
|
var import_node_crypto3 = require("node:crypto");
|
|
7334
7369
|
var import_node_fs8 = require("node:fs");
|
|
7335
|
-
var
|
|
7370
|
+
var import_promises4 = require("node:fs/promises");
|
|
7336
7371
|
var import_node_path13 = require("node:path");
|
|
7337
7372
|
var import_node_stream2 = require("node:stream");
|
|
7338
|
-
var
|
|
7373
|
+
var import_promises5 = require("node:stream/promises");
|
|
7339
7374
|
|
|
7340
7375
|
// ../core/src/model-storage.ts
|
|
7341
7376
|
var import_node_fs = require("node:fs");
|
|
7342
7377
|
var import_node_path3 = require("node:path");
|
|
7343
|
-
var MODEL_SCHEMA_VERSION = "
|
|
7378
|
+
var MODEL_SCHEMA_VERSION = "v2";
|
|
7344
7379
|
var REQUIRED_MODELS_BY_SCHEMA = {
|
|
7345
|
-
v1: ["pi-model"]
|
|
7380
|
+
v1: ["pi-model"],
|
|
7381
|
+
v2: ["pi-model"]
|
|
7346
7382
|
};
|
|
7347
7383
|
var REQUIRED_MODEL_FILES = {
|
|
7348
7384
|
"pi-model": [
|
|
@@ -7391,8 +7427,8 @@ async function downloadModel(opts) {
|
|
|
7391
7427
|
const stagingDir = getDownloadStagingDir(sageDir);
|
|
7392
7428
|
const lockPath = `${modelDir}.lock`;
|
|
7393
7429
|
try {
|
|
7394
|
-
await (0,
|
|
7395
|
-
await (0,
|
|
7430
|
+
await (0, import_promises4.mkdir)(stagingDir, { recursive: true });
|
|
7431
|
+
await (0, import_promises4.mkdir)((0, import_node_path13.resolve)(modelDir, ".."), { recursive: true });
|
|
7396
7432
|
} catch (err) {
|
|
7397
7433
|
logger.warn(`failed to prepare model storage for ${modelName}: ${err}`);
|
|
7398
7434
|
return false;
|
|
@@ -7418,7 +7454,7 @@ async function downloadModel(opts) {
|
|
|
7418
7454
|
});
|
|
7419
7455
|
return false;
|
|
7420
7456
|
}
|
|
7421
|
-
await (0,
|
|
7457
|
+
await (0, import_promises4.mkdir)(extractDir, { recursive: true });
|
|
7422
7458
|
await extractTarGz(archivePath, extractDir);
|
|
7423
7459
|
const extractDirResolved = extractDir;
|
|
7424
7460
|
const missing = requiredModelFiles(modelName).filter(
|
|
@@ -7431,26 +7467,26 @@ async function downloadModel(opts) {
|
|
|
7431
7467
|
const aside = `${modelDir}.${suffix}.old`;
|
|
7432
7468
|
let asideUsed = false;
|
|
7433
7469
|
if (await pathExists(modelDir)) {
|
|
7434
|
-
await (0,
|
|
7470
|
+
await (0, import_promises4.rename)(modelDir, aside);
|
|
7435
7471
|
asideUsed = true;
|
|
7436
7472
|
}
|
|
7437
7473
|
try {
|
|
7438
|
-
await (0,
|
|
7474
|
+
await (0, import_promises4.rename)(extractDir, modelDir);
|
|
7439
7475
|
extractDir = null;
|
|
7440
7476
|
} catch (err) {
|
|
7441
7477
|
if (asideUsed) {
|
|
7442
7478
|
try {
|
|
7443
|
-
await (0,
|
|
7479
|
+
await (0, import_promises4.rename)(aside, modelDir);
|
|
7444
7480
|
} catch {
|
|
7445
7481
|
}
|
|
7446
7482
|
}
|
|
7447
7483
|
throw err;
|
|
7448
7484
|
}
|
|
7449
7485
|
if (asideUsed) {
|
|
7450
|
-
await (0,
|
|
7486
|
+
await (0, import_promises4.rm)(aside, { recursive: true, force: true }).catch(() => {
|
|
7451
7487
|
});
|
|
7452
7488
|
}
|
|
7453
|
-
await (0,
|
|
7489
|
+
await (0, import_promises4.unlink)(archivePath).catch(() => {
|
|
7454
7490
|
});
|
|
7455
7491
|
archivePath = null;
|
|
7456
7492
|
logger.info(`model ${modelName} installed (schema ${schema})`);
|
|
@@ -7462,14 +7498,14 @@ async function downloadModel(opts) {
|
|
|
7462
7498
|
return false;
|
|
7463
7499
|
} finally {
|
|
7464
7500
|
if (extractDir) {
|
|
7465
|
-
await (0,
|
|
7501
|
+
await (0, import_promises4.rm)(extractDir, { recursive: true, force: true }).catch(() => {
|
|
7466
7502
|
});
|
|
7467
7503
|
}
|
|
7468
7504
|
if (archivePath) {
|
|
7469
|
-
await (0,
|
|
7505
|
+
await (0, import_promises4.unlink)(archivePath).catch(() => {
|
|
7470
7506
|
});
|
|
7471
7507
|
}
|
|
7472
|
-
await (0,
|
|
7508
|
+
await (0, import_promises4.unlink)(lockPath).catch(() => {
|
|
7473
7509
|
});
|
|
7474
7510
|
}
|
|
7475
7511
|
}
|
|
@@ -7482,11 +7518,11 @@ async function streamFetchToFile(url, destPath, fetchImpl = fetch) {
|
|
|
7482
7518
|
throw new Error(`empty response body from ${url}`);
|
|
7483
7519
|
}
|
|
7484
7520
|
const nodeStream = import_node_stream2.Readable.fromWeb(response.body);
|
|
7485
|
-
await (0,
|
|
7521
|
+
await (0, import_promises5.pipeline)(nodeStream, (0, import_node_fs8.createWriteStream)(destPath));
|
|
7486
7522
|
}
|
|
7487
7523
|
async function sha256OfFile(path) {
|
|
7488
7524
|
const hash = (0, import_node_crypto3.createHash)("sha256");
|
|
7489
|
-
const buf = await (0,
|
|
7525
|
+
const buf = await (0, import_promises4.readFile)(path);
|
|
7490
7526
|
hash.update(buf);
|
|
7491
7527
|
return hash.digest("hex");
|
|
7492
7528
|
}
|
|
@@ -7500,7 +7536,7 @@ async function extractTarGz(archivePath, destDir) {
|
|
|
7500
7536
|
}
|
|
7501
7537
|
async function pathExists(path) {
|
|
7502
7538
|
try {
|
|
7503
|
-
await (0,
|
|
7539
|
+
await (0, import_promises4.stat)(path);
|
|
7504
7540
|
return true;
|
|
7505
7541
|
} catch {
|
|
7506
7542
|
return false;
|
|
@@ -7517,7 +7553,7 @@ function fileExistsSync(path) {
|
|
|
7517
7553
|
async function tryAcquireLock(lockPath, logger) {
|
|
7518
7554
|
try {
|
|
7519
7555
|
const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
|
|
7520
|
-
await (0,
|
|
7556
|
+
await (0, import_promises4.writeFile)(lockPath, payload, { flag: "wx" });
|
|
7521
7557
|
return true;
|
|
7522
7558
|
} catch (err) {
|
|
7523
7559
|
const code = err.code;
|
|
@@ -7527,15 +7563,15 @@ async function tryAcquireLock(lockPath, logger) {
|
|
|
7527
7563
|
}
|
|
7528
7564
|
}
|
|
7529
7565
|
try {
|
|
7530
|
-
const raw = await (0,
|
|
7566
|
+
const raw = await (0, import_promises4.readFile)(lockPath, "utf-8");
|
|
7531
7567
|
const parsed = JSON.parse(raw);
|
|
7532
7568
|
const age = Date.now() - (parsed.ts ?? 0);
|
|
7533
7569
|
if (age > STALE_LOCK_MS) {
|
|
7534
|
-
await (0,
|
|
7570
|
+
await (0, import_promises4.unlink)(lockPath).catch(() => {
|
|
7535
7571
|
});
|
|
7536
7572
|
try {
|
|
7537
7573
|
const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
|
|
7538
|
-
await (0,
|
|
7574
|
+
await (0, import_promises4.writeFile)(lockPath, payload, { flag: "wx" });
|
|
7539
7575
|
return true;
|
|
7540
7576
|
} catch {
|
|
7541
7577
|
return false;
|
|
@@ -7547,7 +7583,7 @@ async function tryAcquireLock(lockPath, logger) {
|
|
|
7547
7583
|
}
|
|
7548
7584
|
|
|
7549
7585
|
// ../core/src/sage-proxy.ts
|
|
7550
|
-
function
|
|
7586
|
+
function mapSageHostOs(platform) {
|
|
7551
7587
|
switch (platform) {
|
|
7552
7588
|
case "win32":
|
|
7553
7589
|
return "WINDOWS";
|
|
@@ -7559,21 +7595,37 @@ function mapSageProxyOs(platform) {
|
|
|
7559
7595
|
return platform;
|
|
7560
7596
|
}
|
|
7561
7597
|
}
|
|
7562
|
-
function
|
|
7598
|
+
function mapSageHostArchitecture(arch) {
|
|
7563
7599
|
return arch.toUpperCase();
|
|
7564
7600
|
}
|
|
7601
|
+
function buildSageUserConfig(config) {
|
|
7602
|
+
return {
|
|
7603
|
+
sensitivity: config.sensitivity,
|
|
7604
|
+
url_check_enabled: config.url_check.enabled,
|
|
7605
|
+
file_check_enabled: config.file_check.enabled,
|
|
7606
|
+
package_check_enabled: config.package_check.enabled,
|
|
7607
|
+
heuristics_enabled: config.heuristics_enabled,
|
|
7608
|
+
pi_check_enabled: config.pi_check.enabled,
|
|
7609
|
+
community_iq_enabled: config.community_iq,
|
|
7610
|
+
// Optional-chained + defaulted: real callers pass a loadConfig result (skill_check
|
|
7611
|
+
// always present via the schema default), but this feeds fail-open telemetry, so a
|
|
7612
|
+
// partial config must never crash the envelope build.
|
|
7613
|
+
skill_check_upload_enabled: config.skill_check?.upload_enabled ?? true
|
|
7614
|
+
};
|
|
7615
|
+
}
|
|
7565
7616
|
function buildSageProxyEnvelope(args) {
|
|
7566
7617
|
return {
|
|
7567
7618
|
identity: { uuid: args.iid },
|
|
7568
7619
|
product: { version_app: args.versionApp },
|
|
7569
7620
|
platform: {
|
|
7570
|
-
os: args.platformOs ??
|
|
7571
|
-
architecture: args.platformArchitecture ??
|
|
7621
|
+
os: args.platformOs ?? mapSageHostOs(process.platform),
|
|
7622
|
+
architecture: args.platformArchitecture ?? mapSageHostArchitecture(process.arch)
|
|
7572
7623
|
},
|
|
7573
7624
|
agent: {
|
|
7574
7625
|
agent_runtime: args.agentRuntime,
|
|
7575
7626
|
agent_runtime_version: args.agentRuntimeVersion
|
|
7576
|
-
}
|
|
7627
|
+
},
|
|
7628
|
+
...args.config ? { config: buildSageUserConfig(args.config) } : {}
|
|
7577
7629
|
};
|
|
7578
7630
|
}
|
|
7579
7631
|
|
|
@@ -7582,7 +7634,7 @@ var import_node_path14 = require("node:path");
|
|
|
7582
7634
|
var import_node_url = require("node:url");
|
|
7583
7635
|
var import_meta = {};
|
|
7584
7636
|
function resolveVersion() {
|
|
7585
|
-
if (true) return "0.
|
|
7637
|
+
if (true) return "0.12.0";
|
|
7586
7638
|
try {
|
|
7587
7639
|
const pkgPath = (0, import_node_path14.join)((0, import_node_path14.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
|
|
7588
7640
|
const pkg = JSON.parse(getFileContentSync(pkgPath));
|
|
@@ -7621,14 +7673,23 @@ async function fetchModelManifest(args) {
|
|
|
7621
7673
|
logger.debug("Skipping model manifest fetch: missing installation id");
|
|
7622
7674
|
return null;
|
|
7623
7675
|
}
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7676
|
+
let config = args.config;
|
|
7677
|
+
if (!config) {
|
|
7678
|
+
try {
|
|
7679
|
+
config = await loadConfig(args.configPath, logger);
|
|
7680
|
+
} catch (err) {
|
|
7681
|
+
logger.debug(`Model manifest config load failed: ${err}`);
|
|
7682
|
+
}
|
|
7683
|
+
}
|
|
7631
7684
|
try {
|
|
7685
|
+
const envelope = buildSageProxyEnvelope({
|
|
7686
|
+
iid: args.iid,
|
|
7687
|
+
versionApp: args.versionApp ?? VERSION,
|
|
7688
|
+
agentRuntime: args.agentRuntime,
|
|
7689
|
+
agentRuntimeVersion: args.agentRuntimeVersion ?? "unknown",
|
|
7690
|
+
config
|
|
7691
|
+
});
|
|
7692
|
+
const body = { ...envelope, models: { schema: args.schema } };
|
|
7632
7693
|
const response = await fetch(resolveEndpoint("/v2/model-manifest"), {
|
|
7633
7694
|
method: "POST",
|
|
7634
7695
|
signal: AbortSignal.timeout(timeoutMs),
|
|
@@ -7705,6 +7766,8 @@ async function ensureModelsAvailable(args) {
|
|
|
7705
7766
|
schema,
|
|
7706
7767
|
agentRuntime: args.agentRuntime ?? "unknown",
|
|
7707
7768
|
agentRuntimeVersion: args.agentRuntimeVersion,
|
|
7769
|
+
config: args.config,
|
|
7770
|
+
configPath: args.configPath,
|
|
7708
7771
|
versionApp: args.versionApp,
|
|
7709
7772
|
logger
|
|
7710
7773
|
});
|
|
@@ -7750,78 +7813,42 @@ async function ensureModelsAvailable(args) {
|
|
|
7750
7813
|
}
|
|
7751
7814
|
|
|
7752
7815
|
// ../core/src/jsonl-log-writer.ts
|
|
7753
|
-
var
|
|
7816
|
+
var import_promises6 = require("node:fs/promises");
|
|
7754
7817
|
var import_node_path15 = require("node:path");
|
|
7755
|
-
var import_promises6 = require("node:timers/promises");
|
|
7756
7818
|
var writeQueues = /* @__PURE__ */ new Map();
|
|
7757
|
-
var ROTATE_LOCK_TIMEOUT_MS = 250;
|
|
7758
|
-
var ROTATE_LOCK_STALE_MS = 3e4;
|
|
7759
|
-
var ROTATE_LOCK_POLL_MS = 50;
|
|
7760
7819
|
async function shouldRotate(filePath, maxBytes, maxFiles) {
|
|
7761
7820
|
if (maxBytes <= 0 || maxFiles <= 0) return false;
|
|
7762
7821
|
try {
|
|
7763
|
-
const s3 = await (0,
|
|
7822
|
+
const s3 = await (0, import_promises6.stat)(filePath);
|
|
7764
7823
|
return s3.size >= maxBytes;
|
|
7765
7824
|
} catch {
|
|
7766
7825
|
return false;
|
|
7767
7826
|
}
|
|
7768
7827
|
}
|
|
7769
|
-
async function removeStaleRotateLock(lockPath) {
|
|
7770
|
-
try {
|
|
7771
|
-
const s3 = await (0, import_promises5.stat)(lockPath);
|
|
7772
|
-
if (Date.now() - s3.mtimeMs < ROTATE_LOCK_STALE_MS) return;
|
|
7773
|
-
await (0, import_promises5.rmdir)(lockPath);
|
|
7774
|
-
} catch {
|
|
7775
|
-
}
|
|
7776
|
-
}
|
|
7777
|
-
async function acquireRotateLock(filePath) {
|
|
7778
|
-
const lockPath = `${filePath}.lock`;
|
|
7779
|
-
const deadline = Date.now() + ROTATE_LOCK_TIMEOUT_MS;
|
|
7780
|
-
while (true) {
|
|
7781
|
-
try {
|
|
7782
|
-
await (0, import_promises5.mkdir)(lockPath);
|
|
7783
|
-
return async () => {
|
|
7784
|
-
try {
|
|
7785
|
-
await (0, import_promises5.rmdir)(lockPath);
|
|
7786
|
-
} catch {
|
|
7787
|
-
}
|
|
7788
|
-
};
|
|
7789
|
-
} catch {
|
|
7790
|
-
await removeStaleRotateLock(lockPath);
|
|
7791
|
-
const remainingMs = deadline - Date.now();
|
|
7792
|
-
if (remainingMs <= 0) return void 0;
|
|
7793
|
-
await (0, import_promises6.setTimeout)(Math.min(ROTATE_LOCK_POLL_MS, remainingMs));
|
|
7794
|
-
}
|
|
7795
|
-
}
|
|
7796
|
-
}
|
|
7797
7828
|
async function rotateIfNeeded(filePath, maxBytes, maxFiles) {
|
|
7798
7829
|
if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
|
|
7799
|
-
|
|
7800
|
-
if (!releaseLock) return;
|
|
7801
|
-
try {
|
|
7830
|
+
await withFileLock(filePath, async () => {
|
|
7802
7831
|
if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
|
|
7803
7832
|
try {
|
|
7804
|
-
await (0,
|
|
7833
|
+
await (0, import_promises6.unlink)(`${filePath}.${maxFiles}`);
|
|
7805
7834
|
} catch {
|
|
7806
7835
|
}
|
|
7807
7836
|
for (let i = maxFiles - 1; i >= 1; i--) {
|
|
7808
7837
|
try {
|
|
7809
|
-
await (0,
|
|
7838
|
+
await (0, import_promises6.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
|
|
7810
7839
|
} catch {
|
|
7811
7840
|
}
|
|
7812
7841
|
}
|
|
7813
7842
|
try {
|
|
7814
|
-
await (0,
|
|
7843
|
+
await (0, import_promises6.rename)(filePath, `${filePath}.1`);
|
|
7815
7844
|
} catch {
|
|
7816
7845
|
}
|
|
7817
|
-
}
|
|
7818
|
-
await releaseLock();
|
|
7819
|
-
}
|
|
7846
|
+
});
|
|
7820
7847
|
}
|
|
7821
7848
|
async function appendJsonlEntryNow(path, config, entry) {
|
|
7822
|
-
await (0,
|
|
7849
|
+
await (0, import_promises6.mkdir)((0, import_node_path15.dirname)(path), { recursive: true });
|
|
7823
7850
|
await rotateIfNeeded(path, config.max_bytes, config.max_files);
|
|
7824
|
-
await (0,
|
|
7851
|
+
await (0, import_promises6.appendFile)(path, `${JSON.stringify(entry)}
|
|
7825
7852
|
`);
|
|
7826
7853
|
}
|
|
7827
7854
|
async function appendJsonlEntry(config, entry) {
|
|
@@ -7951,11 +7978,12 @@ function createOperationalLogger(config, runtime) {
|
|
|
7951
7978
|
async function readWorkerArgs() {
|
|
7952
7979
|
const env = process.env;
|
|
7953
7980
|
const sageDir = env.SAGE_DIR ? resolvePath(env.SAGE_DIR) : resolvePath("~/.sage");
|
|
7981
|
+
const configPath = env.SAGE_CONFIG_PATH ? resolvePath(env.SAGE_CONFIG_PATH) : void 0;
|
|
7954
7982
|
const agentRuntime = env.SAGE_AGENT_RUNTIME ?? "unknown";
|
|
7955
7983
|
const agentRuntimeVersion = env.SAGE_AGENT_RUNTIME_VERSION || void 0;
|
|
7956
7984
|
const versionApp = env.SAGE_VERSION_APP || void 0;
|
|
7957
7985
|
const schema = env.SAGE_MODEL_SCHEMA || MODEL_SCHEMA_VERSION;
|
|
7958
|
-
return { sageDir, agentRuntime, agentRuntimeVersion, versionApp, schema };
|
|
7986
|
+
return { sageDir, configPath, agentRuntime, agentRuntimeVersion, versionApp, schema };
|
|
7959
7987
|
}
|
|
7960
7988
|
async function createWorkerLogger(agentRuntime) {
|
|
7961
7989
|
try {
|
|
@@ -7995,6 +8023,7 @@ async function workerMain() {
|
|
|
7995
8023
|
iid,
|
|
7996
8024
|
agentRuntime: args.agentRuntime,
|
|
7997
8025
|
agentRuntimeVersion: args.agentRuntimeVersion,
|
|
8026
|
+
configPath: args.configPath,
|
|
7998
8027
|
versionApp: args.versionApp,
|
|
7999
8028
|
schema: args.schema,
|
|
8000
8029
|
logger
|