@gendigital/sage-openclaw 0.9.0 → 0.11.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 +63 -20
- package/dist/index.cjs +1073 -653
- package/dist/index.cjs.map +4 -4
- package/dist/mcp-server.cjs +31776 -0
- package/dist/mcp-server.cjs.map +7 -0
- package/dist/model-download-worker.cjs +422 -29
- package/dist/model-download-worker.cjs.map +4 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +13 -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 +22 -33
- 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 +94 -44
- 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 +15 -25
- 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
|
@@ -2961,7 +2961,7 @@ var init_index_min = __esm({
|
|
|
2961
2961
|
});
|
|
2962
2962
|
|
|
2963
2963
|
// ../core/src/model-download-worker.ts
|
|
2964
|
-
var
|
|
2964
|
+
var import_promises7 = require("node:fs/promises");
|
|
2965
2965
|
|
|
2966
2966
|
// ../core/src/config.ts
|
|
2967
2967
|
var import_node_path = require("node:path");
|
|
@@ -7043,23 +7043,21 @@ var ArtifactSchema = external_exports.object({
|
|
|
7043
7043
|
value: external_exports.string(),
|
|
7044
7044
|
context: external_exports.string().optional()
|
|
7045
7045
|
});
|
|
7046
|
-
var
|
|
7047
|
-
var ActionSchema = external_exports.enum(["block", "require_approval", "log"]);
|
|
7046
|
+
var VerdictSeveritySchema = external_exports.enum(["info", "warning", "critical"]);
|
|
7048
7047
|
var ThreatSchema = external_exports.object({
|
|
7049
7048
|
id: external_exports.string(),
|
|
7050
7049
|
version: external_exports.number().int().optional(),
|
|
7051
7050
|
category: external_exports.string(),
|
|
7052
|
-
severity:
|
|
7051
|
+
severity: VerdictSeveritySchema,
|
|
7053
7052
|
confidence: external_exports.number(),
|
|
7054
|
-
action: ActionSchema,
|
|
7055
7053
|
pattern: external_exports.string(),
|
|
7056
7054
|
match_on: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]),
|
|
7057
7055
|
title: external_exports.string(),
|
|
7058
7056
|
expires_at: external_exports.string().nullable().optional(),
|
|
7059
|
-
revoked: external_exports.boolean().optional().default(false)
|
|
7057
|
+
revoked: external_exports.boolean().optional().default(false),
|
|
7058
|
+
flags: external_exports.array(external_exports.string()).optional().default([])
|
|
7060
7059
|
});
|
|
7061
7060
|
var DecisionSchema = external_exports.enum(["allow", "deny", "ask"]);
|
|
7062
|
-
var VerdictSeveritySchema = external_exports.enum(["info", "warning", "critical"]);
|
|
7063
7061
|
var SensitivitySchema = external_exports.enum(["paranoid", "balanced", "relaxed"]);
|
|
7064
7062
|
var UrlCheckConfigSchema = external_exports.object({
|
|
7065
7063
|
endpoint: external_exports.string().optional(),
|
|
@@ -7072,9 +7070,6 @@ var CacheConfigSchema = external_exports.object({
|
|
|
7072
7070
|
ttl_clean_seconds: external_exports.number().default(86400),
|
|
7073
7071
|
path: external_exports.string().default("~/.sage/cache.json")
|
|
7074
7072
|
});
|
|
7075
|
-
var AllowlistConfigSchema = external_exports.object({
|
|
7076
|
-
path: external_exports.string().default("~/.sage/allowlist.json")
|
|
7077
|
-
});
|
|
7078
7073
|
var LoggingConfigSchema = external_exports.object({
|
|
7079
7074
|
enabled: external_exports.boolean().default(true),
|
|
7080
7075
|
log_clean: external_exports.boolean().default(false),
|
|
@@ -7082,6 +7077,14 @@ var LoggingConfigSchema = external_exports.object({
|
|
|
7082
7077
|
max_bytes: external_exports.number().int().min(0).default(5 * 1024 * 1024),
|
|
7083
7078
|
max_files: external_exports.number().int().min(0).default(3)
|
|
7084
7079
|
});
|
|
7080
|
+
var OperationalLogLevelSchema = external_exports.enum(["debug", "info", "warn", "error"]);
|
|
7081
|
+
var OperationalLoggingConfigSchema = external_exports.object({
|
|
7082
|
+
enabled: external_exports.boolean().default(true),
|
|
7083
|
+
level: OperationalLogLevelSchema.default("info"),
|
|
7084
|
+
path: external_exports.string().default("~/.sage/operational.jsonl"),
|
|
7085
|
+
max_bytes: external_exports.number().int().min(0).default(5 * 1024 * 1024),
|
|
7086
|
+
max_files: external_exports.number().int().min(0).default(3)
|
|
7087
|
+
});
|
|
7085
7088
|
var FileCheckConfigSchema = external_exports.object({
|
|
7086
7089
|
endpoint: external_exports.string().optional(),
|
|
7087
7090
|
timeout_seconds: external_exports.number().default(5),
|
|
@@ -7128,9 +7131,9 @@ var ConfigSchema = external_exports.object({
|
|
|
7128
7131
|
pi_check: PiCheckConfigSchema.default({}),
|
|
7129
7132
|
heuristics_enabled: external_exports.boolean().default(true),
|
|
7130
7133
|
cache: CacheConfigSchema.default({}),
|
|
7131
|
-
allowlist: AllowlistConfigSchema.default({}),
|
|
7132
7134
|
exceptions: ExceptionsConfigSchema.default({}),
|
|
7133
7135
|
logging: LoggingConfigSchema.default({}),
|
|
7136
|
+
operational_logging: OperationalLoggingConfigSchema.default({}),
|
|
7134
7137
|
sensitivity: SensitivitySchema.default("balanced"),
|
|
7135
7138
|
disabled_threats: external_exports.array(external_exports.string()).default([]),
|
|
7136
7139
|
brand_key: external_exports.string().min(1).max(32).regex(/^[a-z0-9_-]+$/u).optional(),
|
|
@@ -7146,6 +7149,25 @@ var HookTypeSchema = external_exports.enum([
|
|
|
7146
7149
|
]);
|
|
7147
7150
|
|
|
7148
7151
|
// ../core/src/config.ts
|
|
7152
|
+
var SAGE_DIR = "~/.sage";
|
|
7153
|
+
function resolvedSageDir() {
|
|
7154
|
+
return resolvePath(SAGE_DIR);
|
|
7155
|
+
}
|
|
7156
|
+
function defaultConfigPath() {
|
|
7157
|
+
return (0, import_node_path.join)(resolvedSageDir(), "config.json");
|
|
7158
|
+
}
|
|
7159
|
+
function defaultCachePath() {
|
|
7160
|
+
return (0, import_node_path.join)(resolvedSageDir(), "cache.json");
|
|
7161
|
+
}
|
|
7162
|
+
function defaultExceptionsPath() {
|
|
7163
|
+
return (0, import_node_path.join)(resolvedSageDir(), "exceptions.json");
|
|
7164
|
+
}
|
|
7165
|
+
function defaultAuditPath() {
|
|
7166
|
+
return (0, import_node_path.join)(resolvedSageDir(), "audit.jsonl");
|
|
7167
|
+
}
|
|
7168
|
+
function defaultOperationalLogPath() {
|
|
7169
|
+
return (0, import_node_path.join)(resolvedSageDir(), "operational.jsonl");
|
|
7170
|
+
}
|
|
7149
7171
|
function resolvePath(pathStr) {
|
|
7150
7172
|
if (pathStr.startsWith("~/") || pathStr === "~") {
|
|
7151
7173
|
const home = getHomeDir();
|
|
@@ -7153,6 +7175,112 @@ function resolvePath(pathStr) {
|
|
|
7153
7175
|
}
|
|
7154
7176
|
return pathStr;
|
|
7155
7177
|
}
|
|
7178
|
+
function isWithinDirectory(baseDir, targetPath) {
|
|
7179
|
+
const rel = (0, import_node_path.relative)(baseDir, targetPath);
|
|
7180
|
+
if (rel === "") return true;
|
|
7181
|
+
if ((0, import_node_path.isAbsolute)(rel)) return false;
|
|
7182
|
+
return rel !== ".." && !rel.startsWith(`..${import_node_path.sep}`);
|
|
7183
|
+
}
|
|
7184
|
+
function normalizeStateFilePath(configuredPath, fallbackPath, field, logger) {
|
|
7185
|
+
const sageDir = resolvedSageDir();
|
|
7186
|
+
const trimmed = configuredPath.trim();
|
|
7187
|
+
if (trimmed === "") {
|
|
7188
|
+
logger.warn(`Config ${field}.path is empty; using default`, {
|
|
7189
|
+
configuredPath,
|
|
7190
|
+
defaultPath: fallbackPath
|
|
7191
|
+
});
|
|
7192
|
+
return fallbackPath;
|
|
7193
|
+
}
|
|
7194
|
+
const expanded = resolvePath(trimmed);
|
|
7195
|
+
const resolved = (0, import_node_path.isAbsolute)(expanded) ? (0, import_node_path.resolve)(expanded) : (0, import_node_path.resolve)(sageDir, expanded);
|
|
7196
|
+
if (isWithinDirectory(sageDir, resolved)) {
|
|
7197
|
+
if (resolved === sageDir) {
|
|
7198
|
+
logger.warn(`Config ${field}.path must point to a file; using default`, {
|
|
7199
|
+
configuredPath,
|
|
7200
|
+
defaultPath: fallbackPath
|
|
7201
|
+
});
|
|
7202
|
+
return fallbackPath;
|
|
7203
|
+
}
|
|
7204
|
+
return resolved;
|
|
7205
|
+
}
|
|
7206
|
+
logger.warn(`Config ${field}.path escapes ${sageDir}; using default`, {
|
|
7207
|
+
configuredPath,
|
|
7208
|
+
defaultPath: fallbackPath
|
|
7209
|
+
});
|
|
7210
|
+
return fallbackPath;
|
|
7211
|
+
}
|
|
7212
|
+
var BRAND_KEY_RE = /^[a-z0-9_-]+$/u;
|
|
7213
|
+
function sanitizeBrandKey(data, logger) {
|
|
7214
|
+
const brandKey = data.brand_key;
|
|
7215
|
+
if (brandKey === void 0) return data;
|
|
7216
|
+
if (typeof brandKey === "string" && brandKey.length >= 1 && brandKey.length <= 32 && BRAND_KEY_RE.test(brandKey)) {
|
|
7217
|
+
return data;
|
|
7218
|
+
}
|
|
7219
|
+
logger.warn(`Invalid brand_key in config \u2014 ignoring`, { brand_key: brandKey });
|
|
7220
|
+
const { brand_key: _2, ...rest } = data;
|
|
7221
|
+
return rest;
|
|
7222
|
+
}
|
|
7223
|
+
function sanitizeConfigPaths(config, logger) {
|
|
7224
|
+
const cachePath = defaultCachePath();
|
|
7225
|
+
const exceptionsPath = defaultExceptionsPath();
|
|
7226
|
+
const auditPath = defaultAuditPath();
|
|
7227
|
+
const operationalLogPath = defaultOperationalLogPath();
|
|
7228
|
+
return {
|
|
7229
|
+
...config,
|
|
7230
|
+
cache: {
|
|
7231
|
+
...config.cache,
|
|
7232
|
+
path: normalizeStateFilePath(config.cache.path, cachePath, "cache", logger)
|
|
7233
|
+
},
|
|
7234
|
+
exceptions: {
|
|
7235
|
+
...config.exceptions,
|
|
7236
|
+
path: normalizeStateFilePath(config.exceptions.path, exceptionsPath, "exceptions", logger)
|
|
7237
|
+
},
|
|
7238
|
+
logging: {
|
|
7239
|
+
...config.logging,
|
|
7240
|
+
path: normalizeStateFilePath(config.logging.path, auditPath, "logging", logger)
|
|
7241
|
+
},
|
|
7242
|
+
operational_logging: {
|
|
7243
|
+
...config.operational_logging,
|
|
7244
|
+
path: normalizeStateFilePath(
|
|
7245
|
+
config.operational_logging.path,
|
|
7246
|
+
operationalLogPath,
|
|
7247
|
+
"operational_logging",
|
|
7248
|
+
logger
|
|
7249
|
+
)
|
|
7250
|
+
}
|
|
7251
|
+
};
|
|
7252
|
+
}
|
|
7253
|
+
function defaultConfig(logger) {
|
|
7254
|
+
return sanitizeConfigPaths(ConfigSchema.parse({}), logger);
|
|
7255
|
+
}
|
|
7256
|
+
function parseConfig(raw, path, logger) {
|
|
7257
|
+
let data;
|
|
7258
|
+
try {
|
|
7259
|
+
data = JSON.parse(raw);
|
|
7260
|
+
} catch (e) {
|
|
7261
|
+
logger.warn(`Failed to parse config from ${path}`, { error: String(e) });
|
|
7262
|
+
return defaultConfig(logger);
|
|
7263
|
+
}
|
|
7264
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
7265
|
+
logger.warn(`Config file ${path} does not contain a JSON object`);
|
|
7266
|
+
return defaultConfig(logger);
|
|
7267
|
+
}
|
|
7268
|
+
const sanitized = sanitizeBrandKey(data, logger);
|
|
7269
|
+
try {
|
|
7270
|
+
return sanitizeConfigPaths(ConfigSchema.parse(sanitized), logger);
|
|
7271
|
+
} catch (e) {
|
|
7272
|
+
logger.warn(`Config validation failed, using defaults`, { error: String(e) });
|
|
7273
|
+
return defaultConfig(logger);
|
|
7274
|
+
}
|
|
7275
|
+
}
|
|
7276
|
+
async function loadConfig(configPath, logger = nullLogger) {
|
|
7277
|
+
const path = configPath ? resolvePath(configPath) : defaultConfigPath();
|
|
7278
|
+
try {
|
|
7279
|
+
return parseConfig(await getFileContent(path), path, logger);
|
|
7280
|
+
} catch {
|
|
7281
|
+
return defaultConfig(logger);
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7156
7284
|
|
|
7157
7285
|
// ../core/src/installation-id.ts
|
|
7158
7286
|
var import_node_crypto = require("node:crypto");
|
|
@@ -7440,7 +7568,7 @@ var import_node_path14 = require("node:path");
|
|
|
7440
7568
|
var import_node_url = require("node:url");
|
|
7441
7569
|
var import_meta = {};
|
|
7442
7570
|
function resolveVersion() {
|
|
7443
|
-
if (true) return "0.
|
|
7571
|
+
if (true) return "0.11.0";
|
|
7444
7572
|
try {
|
|
7445
7573
|
const pkgPath = (0, import_node_path14.join)((0, import_node_path14.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
|
|
7446
7574
|
const pkg = JSON.parse(getFileContentSync(pkgPath));
|
|
@@ -7545,7 +7673,19 @@ async function ensureModelsAvailable(args) {
|
|
|
7545
7673
|
const required = REQUIRED_MODELS_BY_SCHEMA[schema] ?? [];
|
|
7546
7674
|
if (required.length === 0) return [];
|
|
7547
7675
|
const missing = missingRequiredModels(schema, sageDir);
|
|
7548
|
-
|
|
7676
|
+
logger.debug("Model availability check started", {
|
|
7677
|
+
schema,
|
|
7678
|
+
requiredModels: required,
|
|
7679
|
+
missingModels: missing
|
|
7680
|
+
});
|
|
7681
|
+
if (missing.length === 0) {
|
|
7682
|
+
logger.debug("Model availability check completed", {
|
|
7683
|
+
schema,
|
|
7684
|
+
result: "already_available",
|
|
7685
|
+
installedModels: required
|
|
7686
|
+
});
|
|
7687
|
+
return [...required];
|
|
7688
|
+
}
|
|
7549
7689
|
const manifest = await fetchModelManifest({
|
|
7550
7690
|
iid: args.iid,
|
|
7551
7691
|
schema,
|
|
@@ -7555,8 +7695,15 @@ async function ensureModelsAvailable(args) {
|
|
|
7555
7695
|
logger
|
|
7556
7696
|
});
|
|
7557
7697
|
if (!manifest) {
|
|
7558
|
-
|
|
7559
|
-
|
|
7698
|
+
const installed2 = required.filter((name) => !missing.includes(name));
|
|
7699
|
+
logger.debug("Model availability check completed", {
|
|
7700
|
+
schema,
|
|
7701
|
+
result: "skipped",
|
|
7702
|
+
skippedReason: "manifest_unavailable",
|
|
7703
|
+
installedModels: installed2,
|
|
7704
|
+
missingModels: missing
|
|
7705
|
+
});
|
|
7706
|
+
return installed2;
|
|
7560
7707
|
}
|
|
7561
7708
|
const installed = new Set(required.filter((name) => !missing.includes(name)));
|
|
7562
7709
|
for (const name of missing) {
|
|
@@ -7578,7 +7725,212 @@ async function ensureModelsAvailable(args) {
|
|
|
7578
7725
|
logger.warn(`download of model '${name}' threw: ${err}`);
|
|
7579
7726
|
}
|
|
7580
7727
|
}
|
|
7581
|
-
|
|
7728
|
+
const installedModels = [...installed];
|
|
7729
|
+
logger.debug("Model availability check completed", {
|
|
7730
|
+
schema,
|
|
7731
|
+
result: "checked",
|
|
7732
|
+
installedModels,
|
|
7733
|
+
missingModels: required.filter((name) => !installed.has(name))
|
|
7734
|
+
});
|
|
7735
|
+
return installedModels;
|
|
7736
|
+
}
|
|
7737
|
+
|
|
7738
|
+
// ../core/src/jsonl-log-writer.ts
|
|
7739
|
+
var import_promises5 = require("node:fs/promises");
|
|
7740
|
+
var import_node_path15 = require("node:path");
|
|
7741
|
+
var import_promises6 = require("node:timers/promises");
|
|
7742
|
+
var writeQueues = /* @__PURE__ */ new Map();
|
|
7743
|
+
var ROTATE_LOCK_TIMEOUT_MS = 250;
|
|
7744
|
+
var ROTATE_LOCK_STALE_MS = 3e4;
|
|
7745
|
+
var ROTATE_LOCK_POLL_MS = 50;
|
|
7746
|
+
async function shouldRotate(filePath, maxBytes, maxFiles) {
|
|
7747
|
+
if (maxBytes <= 0 || maxFiles <= 0) return false;
|
|
7748
|
+
try {
|
|
7749
|
+
const s3 = await (0, import_promises5.stat)(filePath);
|
|
7750
|
+
return s3.size >= maxBytes;
|
|
7751
|
+
} catch {
|
|
7752
|
+
return false;
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
async function removeStaleRotateLock(lockPath) {
|
|
7756
|
+
try {
|
|
7757
|
+
const s3 = await (0, import_promises5.stat)(lockPath);
|
|
7758
|
+
if (Date.now() - s3.mtimeMs < ROTATE_LOCK_STALE_MS) return;
|
|
7759
|
+
await (0, import_promises5.rmdir)(lockPath);
|
|
7760
|
+
} catch {
|
|
7761
|
+
}
|
|
7762
|
+
}
|
|
7763
|
+
async function acquireRotateLock(filePath) {
|
|
7764
|
+
const lockPath = `${filePath}.lock`;
|
|
7765
|
+
const deadline = Date.now() + ROTATE_LOCK_TIMEOUT_MS;
|
|
7766
|
+
while (true) {
|
|
7767
|
+
try {
|
|
7768
|
+
await (0, import_promises5.mkdir)(lockPath);
|
|
7769
|
+
return async () => {
|
|
7770
|
+
try {
|
|
7771
|
+
await (0, import_promises5.rmdir)(lockPath);
|
|
7772
|
+
} catch {
|
|
7773
|
+
}
|
|
7774
|
+
};
|
|
7775
|
+
} catch {
|
|
7776
|
+
await removeStaleRotateLock(lockPath);
|
|
7777
|
+
const remainingMs = deadline - Date.now();
|
|
7778
|
+
if (remainingMs <= 0) return void 0;
|
|
7779
|
+
await (0, import_promises6.setTimeout)(Math.min(ROTATE_LOCK_POLL_MS, remainingMs));
|
|
7780
|
+
}
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7783
|
+
async function rotateIfNeeded(filePath, maxBytes, maxFiles) {
|
|
7784
|
+
if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
|
|
7785
|
+
const releaseLock = await acquireRotateLock(filePath);
|
|
7786
|
+
if (!releaseLock) return;
|
|
7787
|
+
try {
|
|
7788
|
+
if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
|
|
7789
|
+
try {
|
|
7790
|
+
await (0, import_promises5.unlink)(`${filePath}.${maxFiles}`);
|
|
7791
|
+
} catch {
|
|
7792
|
+
}
|
|
7793
|
+
for (let i = maxFiles - 1; i >= 1; i--) {
|
|
7794
|
+
try {
|
|
7795
|
+
await (0, import_promises5.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
|
|
7796
|
+
} catch {
|
|
7797
|
+
}
|
|
7798
|
+
}
|
|
7799
|
+
try {
|
|
7800
|
+
await (0, import_promises5.rename)(filePath, `${filePath}.1`);
|
|
7801
|
+
} catch {
|
|
7802
|
+
}
|
|
7803
|
+
} finally {
|
|
7804
|
+
await releaseLock();
|
|
7805
|
+
}
|
|
7806
|
+
}
|
|
7807
|
+
async function appendJsonlEntryNow(path, config, entry) {
|
|
7808
|
+
await (0, import_promises5.mkdir)((0, import_node_path15.dirname)(path), { recursive: true });
|
|
7809
|
+
await rotateIfNeeded(path, config.max_bytes, config.max_files);
|
|
7810
|
+
await (0, import_promises5.appendFile)(path, `${JSON.stringify(entry)}
|
|
7811
|
+
`);
|
|
7812
|
+
}
|
|
7813
|
+
async function appendJsonlEntry(config, entry) {
|
|
7814
|
+
const path = resolvePath(config.path);
|
|
7815
|
+
const previousWrite = writeQueues.get(path) ?? Promise.resolve();
|
|
7816
|
+
const nextWrite = previousWrite.catch(() => {
|
|
7817
|
+
}).then(() => appendJsonlEntryNow(path, config, entry));
|
|
7818
|
+
writeQueues.set(path, nextWrite);
|
|
7819
|
+
try {
|
|
7820
|
+
await nextWrite;
|
|
7821
|
+
} finally {
|
|
7822
|
+
if (writeQueues.get(path) === nextWrite) writeQueues.delete(path);
|
|
7823
|
+
}
|
|
7824
|
+
}
|
|
7825
|
+
|
|
7826
|
+
// ../core/src/operational-log.ts
|
|
7827
|
+
var LEVEL_PRIORITY = {
|
|
7828
|
+
debug: 10,
|
|
7829
|
+
info: 20,
|
|
7830
|
+
warn: 30,
|
|
7831
|
+
error: 40
|
|
7832
|
+
};
|
|
7833
|
+
var FLUSH_TIMEOUT_MS = 250;
|
|
7834
|
+
function shouldLog(config, level) {
|
|
7835
|
+
return config.enabled && LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[config.level];
|
|
7836
|
+
}
|
|
7837
|
+
function normalizeData(data) {
|
|
7838
|
+
if (!data) return void 0;
|
|
7839
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
7840
|
+
try {
|
|
7841
|
+
return JSON.parse(
|
|
7842
|
+
JSON.stringify(data, (_key, value) => {
|
|
7843
|
+
if (typeof value === "bigint") return value.toString();
|
|
7844
|
+
if (value instanceof Error) {
|
|
7845
|
+
return { name: value.name, message: value.message, stack: value.stack };
|
|
7846
|
+
}
|
|
7847
|
+
if (value && typeof value === "object") {
|
|
7848
|
+
if (seen.has(value)) return "[Circular]";
|
|
7849
|
+
seen.add(value);
|
|
7850
|
+
}
|
|
7851
|
+
return value;
|
|
7852
|
+
})
|
|
7853
|
+
);
|
|
7854
|
+
} catch {
|
|
7855
|
+
return { serialization_error: "Failed to serialize log data" };
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
async function writeOperationalLogEntry(config, entry) {
|
|
7859
|
+
if (!shouldLog(config, entry.level)) return;
|
|
7860
|
+
try {
|
|
7861
|
+
await appendJsonlEntry(config, entry);
|
|
7862
|
+
} catch {
|
|
7863
|
+
}
|
|
7864
|
+
}
|
|
7865
|
+
function createOperationalLogger(config, runtime) {
|
|
7866
|
+
const pendingWrites = /* @__PURE__ */ new Set();
|
|
7867
|
+
function log(component, level, message, data) {
|
|
7868
|
+
if (!shouldLog(config, level)) return;
|
|
7869
|
+
const entry = {
|
|
7870
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7871
|
+
level,
|
|
7872
|
+
runtime,
|
|
7873
|
+
component,
|
|
7874
|
+
message,
|
|
7875
|
+
...data ? { data: normalizeData(data) } : {}
|
|
7876
|
+
};
|
|
7877
|
+
const write = writeOperationalLogEntry(config, entry);
|
|
7878
|
+
pendingWrites.add(write);
|
|
7879
|
+
void write.finally(() => pendingWrites.delete(write));
|
|
7880
|
+
}
|
|
7881
|
+
async function flush() {
|
|
7882
|
+
const deadline = Date.now() + FLUSH_TIMEOUT_MS;
|
|
7883
|
+
while (pendingWrites.size > 0) {
|
|
7884
|
+
const remainingMs = deadline - Date.now();
|
|
7885
|
+
if (remainingMs <= 0) return;
|
|
7886
|
+
let timeout;
|
|
7887
|
+
try {
|
|
7888
|
+
const result = await Promise.race([
|
|
7889
|
+
Promise.allSettled([...pendingWrites]).then(() => "drained"),
|
|
7890
|
+
new Promise((resolve4) => {
|
|
7891
|
+
timeout = setTimeout(() => resolve4("timeout"), remainingMs);
|
|
7892
|
+
timeout.unref?.();
|
|
7893
|
+
})
|
|
7894
|
+
]);
|
|
7895
|
+
if (result === "timeout") return;
|
|
7896
|
+
} finally {
|
|
7897
|
+
if (timeout) clearTimeout(timeout);
|
|
7898
|
+
}
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
function forComponent(component) {
|
|
7902
|
+
return {
|
|
7903
|
+
debug(msg, data) {
|
|
7904
|
+
log(component, "debug", msg, data);
|
|
7905
|
+
},
|
|
7906
|
+
info(msg, data) {
|
|
7907
|
+
log(component, "info", msg, data);
|
|
7908
|
+
},
|
|
7909
|
+
warn(msg, data) {
|
|
7910
|
+
log(component, "warn", msg, data);
|
|
7911
|
+
},
|
|
7912
|
+
error(msg, data) {
|
|
7913
|
+
log(component, "error", msg, data);
|
|
7914
|
+
},
|
|
7915
|
+
flush
|
|
7916
|
+
};
|
|
7917
|
+
}
|
|
7918
|
+
return {
|
|
7919
|
+
debug(component, msg, data) {
|
|
7920
|
+
log(component, "debug", msg, data);
|
|
7921
|
+
},
|
|
7922
|
+
info(component, msg, data) {
|
|
7923
|
+
log(component, "info", msg, data);
|
|
7924
|
+
},
|
|
7925
|
+
warn(component, msg, data) {
|
|
7926
|
+
log(component, "warn", msg, data);
|
|
7927
|
+
},
|
|
7928
|
+
error(component, msg, data) {
|
|
7929
|
+
log(component, "error", msg, data);
|
|
7930
|
+
},
|
|
7931
|
+
forComponent,
|
|
7932
|
+
flush
|
|
7933
|
+
};
|
|
7582
7934
|
}
|
|
7583
7935
|
|
|
7584
7936
|
// ../core/src/model-download-worker.ts
|
|
@@ -7591,22 +7943,63 @@ async function readWorkerArgs() {
|
|
|
7591
7943
|
const schema = env.SAGE_MODEL_SCHEMA || MODEL_SCHEMA_VERSION;
|
|
7592
7944
|
return { sageDir, agentRuntime, agentRuntimeVersion, versionApp, schema };
|
|
7593
7945
|
}
|
|
7946
|
+
async function createWorkerLogger(agentRuntime) {
|
|
7947
|
+
try {
|
|
7948
|
+
const config = await loadConfig();
|
|
7949
|
+
return createOperationalLogger(
|
|
7950
|
+
config.operational_logging,
|
|
7951
|
+
agentRuntime
|
|
7952
|
+
).forComponent("model-download-worker");
|
|
7953
|
+
} catch {
|
|
7954
|
+
return nullLogger;
|
|
7955
|
+
}
|
|
7956
|
+
}
|
|
7594
7957
|
async function workerMain() {
|
|
7595
7958
|
const args = await readWorkerArgs();
|
|
7596
7959
|
if (!args) return;
|
|
7597
|
-
await (
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7960
|
+
const logger = await createWorkerLogger(args.agentRuntime);
|
|
7961
|
+
try {
|
|
7962
|
+
logger.debug("Model download worker started", {
|
|
7963
|
+
schema: args.schema,
|
|
7964
|
+
agentRuntime: args.agentRuntime,
|
|
7965
|
+
agentRuntimeVersion: args.agentRuntimeVersion,
|
|
7966
|
+
versionApp: args.versionApp
|
|
7967
|
+
});
|
|
7968
|
+
await (0, import_promises7.mkdir)(getModelStorageRoot(args.sageDir), { recursive: true }).catch(() => {
|
|
7969
|
+
});
|
|
7970
|
+
const iid = await getInstallationId(args.sageDir).catch(() => void 0);
|
|
7971
|
+
if (!iid) {
|
|
7972
|
+
logger.debug("Model download worker completed", {
|
|
7973
|
+
result: "skipped",
|
|
7974
|
+
skippedReason: "missing_installation_id"
|
|
7975
|
+
});
|
|
7976
|
+
await logger.flush?.();
|
|
7977
|
+
return;
|
|
7978
|
+
}
|
|
7979
|
+
const installedModels = await ensureModelsAvailable({
|
|
7980
|
+
sageDir: args.sageDir,
|
|
7981
|
+
iid,
|
|
7982
|
+
agentRuntime: args.agentRuntime,
|
|
7983
|
+
agentRuntimeVersion: args.agentRuntimeVersion,
|
|
7984
|
+
versionApp: args.versionApp,
|
|
7985
|
+
schema: args.schema,
|
|
7986
|
+
logger
|
|
7987
|
+
});
|
|
7988
|
+
logger.debug("Model download worker completed", {
|
|
7989
|
+
result: "completed",
|
|
7990
|
+
installedModels
|
|
7991
|
+
});
|
|
7992
|
+
await logger.flush?.();
|
|
7993
|
+
} catch (error) {
|
|
7994
|
+
logger.error("Model download worker failed open", {
|
|
7995
|
+
error: String(error),
|
|
7996
|
+
schema: args.schema,
|
|
7997
|
+
agentRuntime: args.agentRuntime,
|
|
7998
|
+
agentRuntimeVersion: args.agentRuntimeVersion,
|
|
7999
|
+
versionApp: args.versionApp
|
|
8000
|
+
});
|
|
8001
|
+
await logger.flush?.();
|
|
8002
|
+
}
|
|
7610
8003
|
}
|
|
7611
8004
|
var isWorkerEntry = (() => {
|
|
7612
8005
|
try {
|