dsclaw 0.1.7 → 0.1.9

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
@@ -1,56 +1,3 @@
1
- var __getOwnPropNames = Object.getOwnPropertyNames;
2
- var __esm = (fn, res) => function __init() {
3
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
- };
5
-
6
- // src/shared/tracer.ts
7
- import { AsyncLocalStorage } from "async_hooks";
8
- import { nanoid } from "nanoid";
9
- function runWithTrace(ctx, fn) {
10
- const traceId = ctx.traceId ?? nanoid(12);
11
- return storage.run({ traceId, ...ctx }, fn);
12
- }
13
- function getTraceContext() {
14
- return storage.getStore() ?? { traceId: nanoid(12) };
15
- }
16
- function getTraceId() {
17
- return getTraceContext().traceId;
18
- }
19
- var storage;
20
- var init_tracer = __esm({
21
- "src/shared/tracer.ts"() {
22
- "use strict";
23
- storage = new AsyncLocalStorage();
24
- }
25
- });
26
-
27
- // src/shared/logger.ts
28
- import pino from "pino";
29
- function createLogger(module) {
30
- return rootLogger.child({ module });
31
- }
32
- var isPkg, rootLogger;
33
- var init_logger = __esm({
34
- "src/shared/logger.ts"() {
35
- "use strict";
36
- init_tracer();
37
- isPkg = typeof process.pkg !== "undefined";
38
- rootLogger = pino({
39
- level: process.env["LOG_LEVEL"] ?? "info",
40
- transport: !isPkg && process.env["NODE_ENV"] !== "production" ? { target: "pino-pretty", options: { colorize: true } } : void 0,
41
- mixin() {
42
- const ctx = getTraceContext();
43
- return {
44
- traceId: ctx.traceId,
45
- ...ctx.userId ? { userId: ctx.userId } : {},
46
- ...ctx.channelId ? { channelId: ctx.channelId } : {},
47
- ...ctx.agentId ? { agentId: ctx.agentId } : {}
48
- };
49
- }
50
- });
51
- }
52
- });
53
-
54
1
  // src/shared/errors.ts
55
2
  var ErrorCategory = {
56
3
  RETRYABLE: "RETRYABLE",
@@ -92,9 +39,42 @@ function classifyHttpError(status, body) {
92
39
  return ErrorCategory.FATAL;
93
40
  }
94
41
 
95
- // src/index.ts
96
- init_logger();
97
- init_tracer();
42
+ // src/shared/logger.ts
43
+ import pino from "pino";
44
+
45
+ // src/shared/tracer.ts
46
+ import { AsyncLocalStorage } from "async_hooks";
47
+ import { nanoid } from "nanoid";
48
+ var storage = new AsyncLocalStorage();
49
+ function runWithTrace(ctx, fn) {
50
+ const traceId = ctx.traceId ?? nanoid(12);
51
+ return storage.run({ traceId, ...ctx }, fn);
52
+ }
53
+ function getTraceContext() {
54
+ return storage.getStore() ?? { traceId: nanoid(12) };
55
+ }
56
+ function getTraceId() {
57
+ return getTraceContext().traceId;
58
+ }
59
+
60
+ // src/shared/logger.ts
61
+ var isPkg = typeof process.pkg !== "undefined";
62
+ var rootLogger = pino({
63
+ level: process.env["LOG_LEVEL"] ?? "info",
64
+ transport: !isPkg && process.env["NODE_ENV"] !== "production" ? { target: "pino-pretty", options: { colorize: true } } : void 0,
65
+ mixin() {
66
+ const ctx = getTraceContext();
67
+ return {
68
+ traceId: ctx.traceId,
69
+ ...ctx.userId ? { userId: ctx.userId } : {},
70
+ ...ctx.channelId ? { channelId: ctx.channelId } : {},
71
+ ...ctx.agentId ? { agentId: ctx.agentId } : {}
72
+ };
73
+ }
74
+ });
75
+ function createLogger(module) {
76
+ return rootLogger.child({ module });
77
+ }
98
78
 
99
79
  // src/shared/utils.ts
100
80
  import { createHash } from "crypto";
@@ -120,7 +100,6 @@ import { appendFileSync, mkdirSync as mkdirSync2, existsSync as existsSync2 } fr
120
100
  import { join as join2 } from "path";
121
101
 
122
102
  // src/gateway/config.ts
123
- init_logger();
124
103
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
125
104
  import { join } from "path";
126
105
  import { homedir } from "os";
@@ -161,7 +140,6 @@ function saveConfig(config) {
161
140
  }
162
141
 
163
142
  // src/shared/audit.ts
164
- init_tracer();
165
143
  var AUDIT_DIR = join2(CONFIG_DIR, "audit");
166
144
  function ensureAuditDir() {
167
145
  if (!existsSync2(AUDIT_DIR)) {
@@ -202,7 +180,6 @@ import {
202
180
  } from "fs";
203
181
  import { join as join3 } from "path";
204
182
  import { hostname, userInfo } from "os";
205
- init_logger();
206
183
  var log2 = createLogger("user-session");
207
184
  var ALG = "aes-256-gcm";
208
185
  var IV_LEN = 12;
@@ -325,7 +302,6 @@ function isOnboarding(state) {
325
302
  }
326
303
 
327
304
  // src/dsers/auth.ts
328
- init_logger();
329
305
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync4, chmodSync } from "fs";
330
306
  import { dirname } from "path";
331
307
  var log3 = createLogger("dsers:auth");
@@ -494,12 +470,7 @@ var DSersAuth = class {
494
470
  }
495
471
  };
496
472
 
497
- // src/dsers/client.ts
498
- init_logger();
499
- init_tracer();
500
-
501
473
  // src/resilience/rate-limiter.ts
502
- init_logger();
503
474
  import pLimit from "p-limit";
504
475
  var log4 = createLogger("rate-limiter");
505
476
  var DEFAULT_OUTBOUND_LIMITS = {
@@ -781,7 +752,6 @@ function findAllChromium() {
781
752
  }
782
753
 
783
754
  // src/dsers/browser-auth.ts
784
- init_logger();
785
755
  var log6 = createLogger("dsers:browser-auth");
786
756
  var DSERS_LOGIN_URL = "https://accounts.dsers.com/accounts/login";
787
757
  var LOGIN_API_PATTERN = "/account-user-bff/v1/users/login";
@@ -1009,68 +979,61 @@ async function loginViaCDP() {
1009
979
  }
1010
980
 
1011
981
  // src/agents/core-agent.ts
1012
- init_logger();
1013
982
  import { generateText, streamText, tool as aiTool, stepCountIs } from "ai";
1014
983
  import { createOpenAI } from "@ai-sdk/openai";
1015
984
  import { createAnthropic } from "@ai-sdk/anthropic";
1016
985
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
1017
986
  import { z as z2 } from "zod";
987
+ import { createMCPClient } from "@ai-sdk/mcp";
988
+ import { Experimental_StdioMCPTransport as StdioTransport } from "@ai-sdk/mcp/mcp-stdio";
1018
989
 
1019
- // src/dsers/product.ts
1020
- async function getImportList(client, params) {
1021
- const filtered = params ? Object.fromEntries(Object.entries(params).filter(([, v]) => v != null)) : {};
1022
- return client.get("/dsers-product-bff/import-list", filtered);
1023
- }
1024
- async function getImportListItem(client, id) {
1025
- return client.get(`/dsers-product-bff/import-list/${id}`);
1026
- }
1027
- async function getMyProducts(client, params) {
1028
- return client.get("/dsers-product-bff/my-product", params);
1029
- }
1030
-
1031
- // src/agents/core-agent.ts
1032
- import { configFromToken } from "@lofder/dsers-mcp-product/dist/dsers/config.js";
1033
- import { buildProvider } from "@lofder/dsers-mcp-product/dist/provider.js";
1034
- import { ImportFlowService } from "@lofder/dsers-mcp-product/dist/service.js";
1035
- import { MemoryJobStore } from "@lofder/dsers-mcp-product/dist/job-store-memory.js";
1036
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, renameSync, existsSync as existsSync6 } from "fs";
990
+ // src/dsers/mcp-token-bridge.ts
991
+ import { randomBytes as randomBytes2, createCipheriv as createCipheriv2, createHash as createHash3 } from "crypto";
992
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, chmodSync as chmodSync2 } from "fs";
1037
993
  import { join as join6 } from "path";
1038
- import { homedir as homedir2 } from "os";
1039
- var log7 = createLogger("agent:core");
1040
- var JOB_ID_MAP_DIR = join6(homedir2(), ".dsclaw");
1041
- var JOB_ID_MAP_FILE = join6(JOB_ID_MAP_DIR, "job-id-map.json");
1042
- var JOB_ID_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
1043
- function loadJobIdMap() {
1044
- const map = /* @__PURE__ */ new Map();
994
+ import { homedir as homedir2, hostname as hostname2, userInfo as userInfo2 } from "os";
995
+ var ALG2 = "aes-256-gcm";
996
+ var IV_LEN2 = 12;
997
+ var TAG_LEN2 = 16;
998
+ var PACKAGE_SEED = "dsers-mcp-product-v1";
999
+ var DSERS_BASE_URL = "https://bff-api-gw.dsers.com";
1000
+ function deriveKey2() {
1001
+ const host = hostname2();
1002
+ let user = "";
1045
1003
  try {
1046
- if (!existsSync6(JOB_ID_MAP_FILE)) return map;
1047
- const raw = JSON.parse(readFileSync4(JOB_ID_MAP_FILE, "utf-8"));
1048
- const now = Date.now();
1049
- for (const [short, entry] of Object.entries(raw)) {
1050
- if (now - entry.ts < JOB_ID_TTL_MS) {
1051
- map.set(short, entry.full);
1052
- }
1053
- }
1054
- } catch (err) {
1055
- log7.warn({ err }, "Failed to load job ID map from disk");
1004
+ user = userInfo2().username;
1005
+ } catch {
1006
+ user = process.env["USER"] ?? process.env["USERNAME"] ?? "default";
1056
1007
  }
1057
- return map;
1008
+ return createHash3("sha256").update(`${PACKAGE_SEED}:${host}:${user}`).digest();
1009
+ }
1010
+ function encrypt2(data) {
1011
+ const key = deriveKey2();
1012
+ const iv = randomBytes2(IV_LEN2);
1013
+ const cipher = createCipheriv2(ALG2, key, iv, { authTagLength: TAG_LEN2 });
1014
+ const ct = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
1015
+ const tag = cipher.getAuthTag();
1016
+ return Buffer.concat([iv, tag, ct]).toString("base64");
1058
1017
  }
1059
- function persistJobIdMap(map) {
1018
+ function writeMCPToken(sessionId, state) {
1019
+ const dir = join6(homedir2(), ".dsers-mcp");
1020
+ mkdirSync5(dir, { recursive: true, mode: 448 });
1021
+ const payload = JSON.stringify({
1022
+ session_id: sessionId,
1023
+ state,
1024
+ base_url: DSERS_BASE_URL,
1025
+ ts: Date.now()
1026
+ });
1027
+ const file = join6(dir, "credentials");
1028
+ writeFileSync4(file, encrypt2(payload), "utf-8");
1060
1029
  try {
1061
- mkdirSync5(JOB_ID_MAP_DIR, { recursive: true });
1062
- const obj = {};
1063
- const now = Date.now();
1064
- for (const [short, full] of map) {
1065
- obj[short] = { full, ts: now };
1066
- }
1067
- const tmp = JOB_ID_MAP_FILE + ".tmp";
1068
- writeFileSync4(tmp, JSON.stringify(obj));
1069
- renameSync(tmp, JOB_ID_MAP_FILE);
1070
- } catch (err) {
1071
- log7.warn({ err }, "Failed to persist job ID map");
1030
+ chmodSync2(file, 384);
1031
+ } catch {
1072
1032
  }
1073
1033
  }
1034
+
1035
+ // src/agents/core-agent.ts
1036
+ var log7 = createLogger("agent:core");
1074
1037
  function normalizeBaseUrl(url) {
1075
1038
  if (!url) return void 0;
1076
1039
  let u = url.trim();
@@ -1112,87 +1075,6 @@ function withToolAwareTimeout(promise, baseMs, isToolActive, label) {
1112
1075
  });
1113
1076
  return Promise.race([promise.finally(cleanup2), timeoutPromise]);
1114
1077
  }
1115
- var MCP_DROP_KEYS = /* @__PURE__ */ new Set([
1116
- "image_urls",
1117
- "description_html_snippet",
1118
- "effective_rules_snapshot",
1119
- "requested_rules",
1120
- "original_draft",
1121
- "resolved_source_url",
1122
- "resolver_mode",
1123
- "tags_before",
1124
- "tags_after"
1125
- ]);
1126
- function compactToolResult(result, jobIdMap) {
1127
- if (!result || typeof result !== "object") return result;
1128
- const isMcpPreview = "job_id" in result && ("title_after" in result || "status" in result);
1129
- if (isMcpPreview) {
1130
- const out2 = {};
1131
- for (const [k, v] of Object.entries(result)) {
1132
- if (MCP_DROP_KEYS.has(k)) continue;
1133
- out2[k] = v;
1134
- }
1135
- if (out2.job_id && typeof out2.job_id === "string") {
1136
- const dotIdx = out2.job_id.indexOf(".");
1137
- if (dotIdx > 0) {
1138
- const short = out2.job_id.slice(0, dotIdx);
1139
- if (jobIdMap) jobIdMap.set(short, out2.job_id);
1140
- out2.job_id = short;
1141
- }
1142
- }
1143
- if (out2.skus && Array.isArray(out2.skus) && out2.skus.length > 1) {
1144
- const [header, ...rows] = out2.skus;
1145
- out2.variant_summary = rows.slice(0, 5).map((row) => {
1146
- const obj = {};
1147
- header.forEach((key, i) => {
1148
- obj[key] = row[i];
1149
- });
1150
- return obj;
1151
- });
1152
- if (rows.length > 5) {
1153
- out2.variant_summary_note = `Showing 5 of ${rows.length} variants`;
1154
- }
1155
- delete out2.skus;
1156
- }
1157
- if (out2.title_after) {
1158
- out2._title_modified = true;
1159
- out2._display_title = out2.title_after;
1160
- }
1161
- if (out2.desc_changed) {
1162
- out2._description_modified = true;
1163
- }
1164
- if (out2.warnings?.length > 5) out2.warnings = out2.warnings.slice(0, 5);
1165
- if (out2.stores) {
1166
- out2.stores = out2.stores.map((s) => ({
1167
- store_ref: s.store_ref,
1168
- display_name: s.display_name,
1169
- platform: s.platform
1170
- }));
1171
- }
1172
- if (out2.account_info) {
1173
- const ai = out2.account_info;
1174
- out2.account_info = { plan: ai.plan, limits: ai.limits, aliexpress_auth: ai.aliexpress_auth };
1175
- }
1176
- return out2;
1177
- }
1178
- const json = JSON.stringify(result);
1179
- if (json.length <= 4e3) return result;
1180
- const out = {};
1181
- for (const [k, v] of Object.entries(result)) {
1182
- if (MCP_DROP_KEYS.has(k)) continue;
1183
- if (Array.isArray(v) && v.length > 20) {
1184
- out[k] = v.slice(0, 20);
1185
- out[`_${k}_truncated`] = `${v.length} total, showing first 20`;
1186
- } else {
1187
- out[k] = v;
1188
- }
1189
- }
1190
- const outJson = JSON.stringify(out);
1191
- if (outJson.length > 8e3) {
1192
- return { _truncated: true, _original_size: json.length, summary: outJson.slice(0, 6e3) + "..." };
1193
- }
1194
- return out;
1195
- }
1196
1078
  function buildModel(llm) {
1197
1079
  const baseURL = normalizeBaseUrl(llm.baseUrl);
1198
1080
  if (baseURL || llm.provider === "other") {
@@ -1307,42 +1189,41 @@ var DSClawCoreAgent = class {
1307
1189
  memory;
1308
1190
  llm;
1309
1191
  customTools = /* @__PURE__ */ new Map();
1310
- importService = null;
1311
- jobIdMap = loadJobIdMap();
1192
+ mcpClient = null;
1312
1193
  mcpAvailable = false;
1313
1194
  authCallback;
1314
- constructor(dsers, memory, llm, dsersSession, authCallback, jobStore) {
1195
+ constructor(dsers, memory, llm, authCallback) {
1315
1196
  this.dsers = dsers;
1316
1197
  this.memory = memory;
1317
1198
  this.llm = llm;
1318
1199
  this.authCallback = authCallback;
1319
- if (dsersSession?.sessionId) {
1320
- try {
1321
- const mcpConfig = configFromToken(dsersSession.sessionId, dsersSession.state);
1322
- const provider = buildProvider(mcpConfig);
1323
- this.importService = new ImportFlowService(provider, jobStore ?? new MemoryJobStore());
1324
- this.mcpAvailable = true;
1325
- log7.info("MCP ImportFlowService initialized");
1326
- } catch (err) {
1327
- log7.warn({ err }, "Failed to init MCP ImportFlowService \u2014 product tools unavailable");
1328
- }
1200
+ }
1201
+ async initMCP(dsersSession) {
1202
+ try {
1203
+ writeMCPToken(dsersSession.sessionId, dsersSession.state);
1204
+ this.mcpClient = await createMCPClient({
1205
+ transport: new StdioTransport({
1206
+ command: "npx",
1207
+ args: ["-y", "@lofder/dsers-mcp-product"]
1208
+ })
1209
+ });
1210
+ this.mcpAvailable = true;
1211
+ log7.info("MCP client connected via stdio");
1212
+ } catch (err) {
1213
+ log7.warn({ err }, "Failed to connect MCP \u2014 product tools unavailable");
1214
+ }
1215
+ }
1216
+ async destroy() {
1217
+ if (this.mcpClient) {
1218
+ await this.mcpClient.close();
1219
+ this.mcpClient = null;
1220
+ this.mcpAvailable = false;
1329
1221
  }
1330
1222
  }
1331
1223
  getSystemPrompt() {
1332
1224
  const rule = "ALWAYS respond in the same language as the user's most recent message. If they write Chinese, respond in Chinese. If English, respond in English. Never switch languages unless the user does.";
1333
1225
  return SYSTEM_PROMPT.replace("{{LANGUAGE_RULE}}", `Language rule: ${rule}`);
1334
1226
  }
1335
- shortenJobId(fullId) {
1336
- const dotIdx = fullId.indexOf(".");
1337
- if (dotIdx <= 0) return fullId;
1338
- const short = fullId.slice(0, dotIdx);
1339
- this.jobIdMap.set(short, fullId);
1340
- persistJobIdMap(this.jobIdMap);
1341
- return short;
1342
- }
1343
- resolveJobId(maybeShort) {
1344
- return this.jobIdMap.get(maybeShort) ?? maybeShort;
1345
- }
1346
1227
  registerTool(t2) {
1347
1228
  this.customTools.set(t2.name, t2);
1348
1229
  }
@@ -1369,7 +1250,7 @@ ${memories.map((m2) => `- ${m2.content}`).join("\n")}` : "";
1369
1250
  ),
1370
1251
  { role: "user", content: message }
1371
1252
  ];
1372
- const tools = this.buildAITools(context);
1253
+ const tools = await this.buildAITools(context);
1373
1254
  log7.info(
1374
1255
  { userId: context.userId, messageLen: message.length, toolCount: Object.keys(tools).length },
1375
1256
  "Processing message"
@@ -1421,9 +1302,9 @@ ${memories.map((m2) => `- ${m2.content}`).join("\n")}` : "";
1421
1302
  processStream(message, context, onStatus, callbacks, attachments) {
1422
1303
  const model = buildModel(this.llm);
1423
1304
  const memories = this.memory.search(message, { userId: context.userId, limit: 5 }).catch(() => []);
1424
- const tools = this.buildAITools(context);
1305
+ const toolsPromise = this.buildAITools(context);
1425
1306
  const self = this;
1426
- const streamPromise = memories.then((mems) => {
1307
+ const streamPromise = Promise.all([memories, toolsPromise]).then(([mems, tools]) => {
1427
1308
  const memoryContext = mems.length > 0 ? `
1428
1309
 
1429
1310
  Relevant memories:
@@ -1559,207 +1440,13 @@ ${mems.map((m2) => `- ${m2.content}`).join("\n")}` : "";
1559
1440
  });
1560
1441
  return { textStream, response };
1561
1442
  }
1562
- buildAITools(context) {
1563
- const dsers = this.dsers;
1564
- const svc = this.importService;
1565
- const jmap = this.jobIdMap;
1566
- const resolveJid = (id) => this.resolveJobId(id);
1567
- const audit = (action, target, params, result = "pending") => writeAuditLog({ userId: context.userId, agentId: this.id, action, target, params, result });
1443
+ async buildAITools(_context) {
1568
1444
  const tools = {};
1569
- if (svc) {
1570
- tools.dsers_store_discover = aiTool({
1571
- description: "Get connected stores, account info (plan/limits), and rule capabilities. Call first before import/push.",
1572
- inputSchema: z2.object({
1573
- target_store: z2.string().optional().describe("Filter by store ID or name")
1574
- }),
1575
- execute: async (input) => {
1576
- audit("dsers_store_discover", "mcp");
1577
- const result = await withTimeout(svc.getRuleCapabilities(input), 6e4, "Store discovery");
1578
- audit("dsers_store_discover", "mcp", void 0, "success");
1579
- return compactToolResult(result, jmap);
1580
- }
1581
- });
1582
- tools.dsers_product_import = aiTool({
1583
- description: "Import product by URL or re-apply rules to existing job. Modes: (1) source_url for new import, (2) job_id+rules_json to update rules, (3) job_id alone to refresh. Expired job_id \u2192 re-import with source_url. Returns: job_id, title (or title_before+title_after), sell_price, cost, variants, blocked, warnings.",
1584
- inputSchema: z2.object({
1585
- source_url: z2.string().optional().describe("Supplier product URL"),
1586
- source_urls_json: z2.string().optional().describe("Batch: JSON array of URLs"),
1587
- job_id: z2.string().optional().describe("Existing job ID for rule updates"),
1588
- source_hint: z2.string().optional().describe("auto|aliexpress|alibaba|accio"),
1589
- country: z2.string().default("US").describe("Country code"),
1590
- target_store: z2.string().optional().describe("Store ID or name"),
1591
- visibility_mode: z2.string().optional().describe("backend_only|sell_immediately"),
1592
- rules_json: z2.string().optional().describe(
1593
- "JSON rules. Pricing: fixed_price({fixed_price:9.99}), multiplier({multiplier:2}), fixed_markup({fixed_markup:5}). Content: title_override, description_override_html. Images: keep_first_n, drop_indexes, add_urls. variant_overrides: [{match,sell_price,compare_at_price,stock}]. option_edits: [{action,option_name,value_name?,new_name?}]."
1594
- )
1595
- }),
1596
- execute: async (input) => {
1597
- audit("dsers_product_import", "mcp", input);
1598
- const payload = {};
1599
- if (input.job_id && !input.source_url && !input.source_urls_json) {
1600
- payload.job_id = resolveJid(input.job_id);
1601
- if (input.rules_json) {
1602
- try {
1603
- payload.rules = JSON.parse(input.rules_json);
1604
- } catch {
1605
- throw new Error(
1606
- 'Invalid JSON in rules_json. Expected: {"pricing":{"mode":"fixed_price","fixed_price":9.99}} or {"content":{"title_override":"New Title"}}'
1607
- );
1608
- }
1609
- } else {
1610
- payload._keep_existing_rules = true;
1611
- }
1612
- if (input.target_store) payload.target_store = input.target_store;
1613
- if (input.visibility_mode) payload.visibility_mode = input.visibility_mode;
1614
- const result2 = await withTimeout(svc.reapplyRules(payload), 12e4, "Rule reapply");
1615
- audit("dsers_product_import", "mcp", void 0, "success");
1616
- return compactToolResult(result2, jmap);
1617
- }
1618
- if (input.source_url) payload.source_url = input.source_url;
1619
- if (input.source_urls_json) {
1620
- try {
1621
- payload.source_urls = JSON.parse(input.source_urls_json);
1622
- } catch {
1623
- throw new Error("Invalid JSON in source_urls_json.");
1624
- }
1625
- }
1626
- if (input.source_hint) payload.source_hint = input.source_hint;
1627
- if (input.country) payload.country = input.country;
1628
- if (input.target_store) payload.target_store = input.target_store;
1629
- payload.visibility_mode = input.visibility_mode || "backend_only";
1630
- if (input.rules_json) {
1631
- try {
1632
- payload.rules = JSON.parse(input.rules_json);
1633
- } catch {
1634
- throw new Error("Invalid JSON in rules_json.");
1635
- }
1636
- }
1637
- const result = await withTimeout(svc.prepareImportCandidate(payload), 12e4, "Product import");
1638
- audit("dsers_product_import", "mcp", void 0, "success");
1639
- return compactToolResult(result, jmap);
1640
- }
1641
- });
1642
- tools.dsers_product_preview = aiTool({
1643
- description: "Reload preview for an imported job. Returns prices, variants, images.",
1644
- inputSchema: z2.object({
1645
- job_id: z2.string().describe("Job ID from dsers_product_import"),
1646
- variant_offset: z2.number().optional().describe("Start index for variant pagination"),
1647
- variant_limit: z2.number().optional().describe("Max variants to return")
1648
- }),
1649
- execute: async (input) => {
1650
- audit("dsers_product_preview", "mcp", input);
1651
- const result = await withTimeout(svc.getImportPreview({ ...input, job_id: resolveJid(input.job_id) }), 3e4, "Preview");
1652
- audit("dsers_product_preview", "mcp", void 0, "success");
1653
- return compactToolResult(result, jmap);
1654
- }
1655
- });
1656
- tools.dsers_store_push = aiTool({
1657
- description: "Push product(s) to store(s). Show confirmation checklist and get user approval first. May return blocked (shipping_profile, pricing_rule) or warnings. Use push_options_json with shipping_profile_name when store has multiple shipping profiles.",
1658
- inputSchema: z2.object({
1659
- job_id: z2.string().optional().describe("Job ID"),
1660
- job_ids_json: z2.string().optional().describe("Batch: JSON array of job IDs"),
1661
- target_store: z2.string().optional().describe("Store ID or name from dsers_store_discover"),
1662
- target_stores_json: z2.string().optional().describe("Multi-store: JSON array of store names"),
1663
- visibility_mode: z2.string().optional().describe("backend_only|sell_immediately"),
1664
- force_push: z2.boolean().optional().describe("Override safety checks"),
1665
- push_options_json: z2.string().optional().describe(
1666
- 'Push config JSON. Include shipping_profile_name when multiple profiles exist. E.g. {"shipping_profile_name":"General Profile","pricing_rule_behavior":"apply_store_pricing_rule"}'
1667
- )
1668
- }),
1669
- execute: async (input) => {
1670
- audit("dsers_store_push", "mcp", input);
1671
- const payload = {};
1672
- if (input.job_ids_json) {
1673
- try {
1674
- const ids = JSON.parse(input.job_ids_json);
1675
- payload.job_ids = ids.map(resolveJid);
1676
- } catch {
1677
- throw new Error("Invalid JSON in job_ids_json");
1678
- }
1679
- } else if (input.job_id) {
1680
- payload.job_id = resolveJid(input.job_id);
1681
- }
1682
- if (input.target_store) payload.target_store = input.target_store;
1683
- if (input.target_stores_json) {
1684
- try {
1685
- payload.target_stores = JSON.parse(input.target_stores_json);
1686
- } catch {
1687
- throw new Error("Invalid JSON in target_stores_json");
1688
- }
1689
- }
1690
- if (input.visibility_mode) payload.visibility_mode = input.visibility_mode;
1691
- if (input.force_push) payload.force_push = true;
1692
- if (input.push_options_json) {
1693
- try {
1694
- payload.push_options = JSON.parse(input.push_options_json);
1695
- } catch {
1696
- throw new Error("Invalid JSON in push_options_json");
1697
- }
1698
- }
1699
- const result = await withTimeout(svc.confirmPushToStore(payload), 18e4, "Push to store");
1700
- audit("dsers_store_push", "mcp", void 0, "success");
1701
- return compactToolResult(result, jmap);
1702
- }
1703
- });
1704
- tools.dsers_rules_validate = aiTool({
1705
- description: "Validate pricing/content/image rules before importing.",
1706
- inputSchema: z2.object({
1707
- rules_json: z2.string().describe("Rules as JSON string"),
1708
- target_store: z2.string().optional().describe("Store ID or name")
1709
- }),
1710
- execute: async (input) => {
1711
- audit("dsers_rules_validate", "mcp");
1712
- let rules;
1713
- try {
1714
- rules = JSON.parse(input.rules_json);
1715
- } catch {
1716
- throw new Error("Invalid JSON in rules_json");
1717
- }
1718
- const result = await withTimeout(svc.validateRules({ rules, target_store: input.target_store ?? null }), 3e4, "Rule validation");
1719
- audit("dsers_rules_validate", "mcp", void 0, "success");
1720
- return compactToolResult(result, jmap);
1721
- }
1722
- });
1723
- tools.dsers_job_status = aiTool({
1724
- description: "Check job status: preview_ready \u2192 push_requested \u2192 completed/failed.",
1725
- inputSchema: z2.object({
1726
- job_id: z2.string().describe("Job ID")
1727
- }),
1728
- execute: async (input) => {
1729
- audit("dsers_job_status", "mcp", input);
1730
- const result = await withTimeout(svc.getJobStatus({ ...input, job_id: resolveJid(input.job_id) }), 3e4, "Job status");
1731
- audit("dsers_job_status", "mcp", void 0, "success");
1732
- return compactToolResult(result, jmap);
1733
- }
1734
- });
1735
- tools.dsers_product_visibility = aiTool({
1736
- description: "Set job visibility: backend_only (draft) or sell_immediately (live).",
1737
- inputSchema: z2.object({
1738
- job_id: z2.string().describe("Job ID"),
1739
- visibility_mode: z2.string().describe("backend_only|sell_immediately")
1740
- }),
1741
- execute: async (input) => {
1742
- audit("dsers_product_visibility", "mcp", input);
1743
- const result = await withTimeout(svc.setProductVisibility({ ...input, job_id: resolveJid(input.job_id) }), 3e4, "Set visibility");
1744
- audit("dsers_product_visibility", "mcp", void 0, "success");
1745
- return compactToolResult(result, jmap);
1746
- }
1747
- });
1748
- tools.dsers_product_delete = aiTool({
1749
- description: "Delete product from import list. Call without confirm first, then with confirm=true after user approves.",
1750
- inputSchema: z2.object({
1751
- import_item_id: z2.string().describe("Item ID from dsers_import_list_search"),
1752
- confirm: z2.boolean().optional().describe("true only after user approves")
1753
- }),
1754
- execute: async (input) => {
1755
- audit("dsers_product_delete", "mcp", input);
1756
- const result = await withTimeout(svc.deleteImportItem(input), 3e4, "Delete import item");
1757
- audit("dsers_product_delete", "mcp", void 0, "success");
1758
- return compactToolResult(result, jmap);
1759
- }
1760
- });
1445
+ if (this.mcpClient) {
1446
+ const mcpTools = await this.mcpClient.tools();
1447
+ Object.assign(tools, mcpTools);
1761
1448
  } else {
1762
- log7.warn("MCP ImportFlowService not available \u2014 no MCP tools registered");
1449
+ log7.warn("MCP client not connected \u2014 no product tools available");
1763
1450
  }
1764
1451
  if (this.authCallback) {
1765
1452
  tools.dsers_authorize = aiTool({
@@ -1771,139 +1458,13 @@ ${mems.map((m2) => `- ${m2.content}`).join("\n")}` : "";
1771
1458
  }
1772
1459
  });
1773
1460
  }
1774
- tools.dsers_import_list_search = aiTool({
1775
- description: "Search import list. Returns items with id, source_url, cost_range, sell_price_range. Use source_url with dsers_product_import for modifications.",
1776
- inputSchema: z2.object({
1777
- page: z2.number().optional().describe("Page number (default 1)"),
1778
- pageSize: z2.number().optional().describe("Items per page (default 20)"),
1779
- keyword: z2.string().optional().describe("Search keyword")
1780
- }),
1781
- execute: async (input) => {
1782
- audit("dsers_import_list_search", "dsers:product", input);
1783
- const result = await withTimeout(getImportList(dsers, input), 3e4, "Search import list");
1784
- audit("dsers_import_list_search", "dsers:product", void 0, "success");
1785
- const items = result?.data?.list ?? result?.data ?? [];
1786
- if (Array.isArray(items) && items.length > 0) {
1787
- const enriched = await Promise.allSettled(
1788
- items.slice(0, 10).map(async (item) => {
1789
- const itemId = item.id ?? item.importListId;
1790
- if (!itemId) return item;
1791
- try {
1792
- const detail = await withTimeout(
1793
- getImportListItem(dsers, String(itemId)),
1794
- 15e3,
1795
- "Item detail"
1796
- );
1797
- const detailData = detail?.data ?? detail;
1798
- const variants = detailData?.variants ?? detailData?.skuList ?? detailData?.variantList ?? [];
1799
- if (!Array.isArray(variants) || !variants.length) return item;
1800
- const sellPrices = [];
1801
- const costPrices = [];
1802
- for (const v of variants) {
1803
- const rawSell = Number(v.sellPrice ?? v.salePrice ?? v.price);
1804
- const rawCost = Number(v.supplierPrice ?? v.buyPrice ?? v.cost);
1805
- const sell = rawSell > 100 ? rawSell / 100 : rawSell;
1806
- const cost = rawCost > 100 ? rawCost / 100 : rawCost;
1807
- if (Number.isFinite(sell) && sell > 0) sellPrices.push(sell);
1808
- if (Number.isFinite(cost) && cost > 0) costPrices.push(cost);
1809
- }
1810
- const enrichedItem = { ...item };
1811
- const supplyProductId = detailData?.supplyProductId ?? item?.supplyProductId;
1812
- const supplyAppId = Number(detailData?.supplyAppId ?? item?.supplyAppId ?? 1);
1813
- if (supplyProductId) {
1814
- if (supplyAppId === 2) {
1815
- enrichedItem.source_url = `https://www.temu.com/product/${supplyProductId}.html`;
1816
- } else if (supplyAppId === 3) {
1817
- enrichedItem.source_url = `https://detail.1688.com/offer/${supplyProductId}.html`;
1818
- } else {
1819
- enrichedItem.source_url = `https://www.aliexpress.com/item/${supplyProductId}.html`;
1820
- }
1821
- }
1822
- if (costPrices.length) {
1823
- enrichedItem.cost_range = `$${Math.min(...costPrices).toFixed(2)} \u2013 $${Math.max(...costPrices).toFixed(2)}`;
1824
- }
1825
- if (sellPrices.length) {
1826
- enrichedItem.sell_price_range = `$${Math.min(...sellPrices).toFixed(2)} \u2013 $${Math.max(...sellPrices).toFixed(2)}`;
1827
- enrichedItem.no_markup = sellPrices.every((s, i) => Math.abs(s - (costPrices[i] ?? s)) < 0.01);
1828
- }
1829
- enrichedItem.variant_count = variants.length;
1830
- let totalStock = 0;
1831
- const lowStockVariants = [];
1832
- for (const v of variants) {
1833
- const stock = Number(v.stock ?? v.quantity ?? v.inventory ?? 0);
1834
- const safeStock = Number.isFinite(stock) ? stock : 0;
1835
- totalStock += safeStock;
1836
- if (safeStock <= 5) {
1837
- lowStockVariants.push({
1838
- id: String(v.id ?? v.variantId ?? v.skuId ?? ""),
1839
- sku: String(v.sku ?? v.skuAttr ?? v.optionName ?? v.title ?? ""),
1840
- stock: safeStock
1841
- });
1842
- }
1843
- }
1844
- enrichedItem.total_stock = totalStock;
1845
- if (lowStockVariants.length > 0) {
1846
- enrichedItem.low_stock_variants = lowStockVariants;
1847
- enrichedItem.low_stock_warning = `${lowStockVariants.length} of ${variants.length} variants have stock \u2264 5`;
1848
- }
1849
- return enrichedItem;
1850
- } catch (err) {
1851
- log7.warn({ err, itemId }, "Failed to enrich import list item");
1852
- return item;
1853
- }
1854
- })
1855
- );
1856
- const enrichedItems = enriched.map((r) => r.status === "fulfilled" ? r.value : items[0]);
1857
- const cleanItems = enrichedItems.map((item) => ({
1858
- id: item.id ?? item.importListId,
1859
- title: item.title ?? "(untitled)",
1860
- source_url: item.source_url,
1861
- cost_range: item.cost_range,
1862
- sell_price_range: item.sell_price_range,
1863
- no_markup: item.no_markup,
1864
- variant_count: item.variant_count,
1865
- total_stock: item.total_stock,
1866
- low_stock_warning: item.low_stock_warning,
1867
- low_stock_variants: item.low_stock_variants
1868
- }));
1869
- return { items: cleanItems, total: result?.data?.total ?? cleanItems.length };
1870
- }
1871
- const rawItems = result?.data ?? [];
1872
- if (Array.isArray(rawItems)) {
1873
- return { items: rawItems.map((item) => ({
1874
- id: item.id ?? item.importListId,
1875
- title: item.title ?? "(untitled)"
1876
- })), total: rawItems.length };
1877
- }
1878
- return compactToolResult(result);
1879
- }
1880
- });
1881
- tools.dsers_my_products = aiTool({
1882
- description: "Get products already pushed to stores.",
1883
- inputSchema: z2.object({
1884
- page: z2.number().optional(),
1885
- pageSize: z2.number().optional(),
1886
- keyword: z2.string().optional(),
1887
- storeId: z2.string().optional()
1888
- }),
1889
- execute: async (input) => {
1890
- audit("dsers_my_products", "dsers:product", input);
1891
- const result = await withTimeout(getMyProducts(dsers, input), 3e4, "My products");
1892
- audit("dsers_my_products", "dsers:product", void 0, "success");
1893
- return compactToolResult(result);
1894
- }
1895
- });
1896
1461
  return tools;
1897
1462
  }
1898
1463
  };
1899
1464
 
1900
- // src/gateway/gateway.ts
1901
- import { MemoryJobStore as MemoryJobStore2 } from "@lofder/dsers-mcp-product/dist/job-store-memory.js";
1902
-
1903
1465
  // src/web/server.ts
1904
- init_logger();
1905
1466
  import { createServer } from "http";
1906
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync7, statSync, readdirSync } from "fs";
1467
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync6, statSync, readdirSync } from "fs";
1907
1468
  import { join as join7, dirname as dirname2, extname } from "path";
1908
1469
  import { fileURLToPath } from "url";
1909
1470
  import { homedir as homedir3 } from "os";
@@ -1932,7 +1493,7 @@ function findWebDir() {
1932
1493
  join7(thisDir, "..", "..", "dist", "web")
1933
1494
  ];
1934
1495
  for (const d of candidates) {
1935
- if (existsSync7(join7(d, "index.html"))) return d;
1496
+ if (existsSync6(join7(d, "index.html"))) return d;
1936
1497
  }
1937
1498
  throw new Error(
1938
1499
  `Web build not found (index.html). Looked in:
@@ -1971,7 +1532,7 @@ var WebChatServer = class _WebChatServer {
1971
1532
  }
1972
1533
  tryListen(tryPort, webDir) {
1973
1534
  return new Promise((resolve) => {
1974
- const indexHtml = readFileSync5(join7(webDir, "index.html"), "utf-8");
1535
+ const indexHtml = readFileSync4(join7(webDir, "index.html"), "utf-8");
1975
1536
  const srv = createServer((req, res) => {
1976
1537
  if (req.url === "/health") {
1977
1538
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -1984,11 +1545,11 @@ var WebChatServer = class _WebChatServer {
1984
1545
  const fileName = urlPath.slice("/uploads/".length);
1985
1546
  if (/^[a-zA-Z0-9._-]+$/.test(fileName)) {
1986
1547
  const filePath2 = join7(uploadsDir, fileName);
1987
- if (existsSync7(filePath2) && statSync(filePath2).isFile()) {
1548
+ if (existsSync6(filePath2) && statSync(filePath2).isFile()) {
1988
1549
  const ext = extname(filePath2);
1989
1550
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
1990
1551
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "public, max-age=86400" });
1991
- res.end(readFileSync5(filePath2));
1552
+ res.end(readFileSync4(filePath2));
1992
1553
  return;
1993
1554
  }
1994
1555
  }
@@ -1998,12 +1559,12 @@ var WebChatServer = class _WebChatServer {
1998
1559
  }
1999
1560
  if (urlPath !== "/" && urlPath !== "/index.html") {
2000
1561
  const filePath2 = join7(webDir, urlPath);
2001
- if (existsSync7(filePath2) && statSync(filePath2).isFile()) {
1562
+ if (existsSync6(filePath2) && statSync(filePath2).isFile()) {
2002
1563
  const ext = extname(filePath2);
2003
1564
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
2004
1565
  const cacheHeader = urlPath.includes("/assets/") ? "public, max-age=31536000, immutable" : "no-cache";
2005
1566
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": cacheHeader });
2006
- res.end(readFileSync5(filePath2));
1567
+ res.end(readFileSync4(filePath2));
2007
1568
  return;
2008
1569
  }
2009
1570
  }
@@ -2217,7 +1778,7 @@ var WebChatServer = class _WebChatServer {
2217
1778
  // ─── File Browsing ───────────────────────────────────────────
2218
1779
  buildFileTree() {
2219
1780
  const configDir = join7(homedir3(), ".dsclaw");
2220
- if (!existsSync7(configDir)) return [];
1781
+ if (!existsSync6(configDir)) return [];
2221
1782
  const scanDir = (dirPath, depth = 0) => {
2222
1783
  if (depth > 5) return [];
2223
1784
  try {
@@ -2265,7 +1826,7 @@ var WebChatServer = class _WebChatServer {
2265
1826
  return;
2266
1827
  }
2267
1828
  try {
2268
- if (!existsSync7(filePath2) || !statSync(filePath2).isFile()) {
1829
+ if (!existsSync6(filePath2) || !statSync(filePath2).isFile()) {
2269
1830
  this.wsSend(ws, { type: "file_content", path: filePath2, content: "[\u6587\u4EF6\u4E0D\u5B58\u5728]" });
2270
1831
  return;
2271
1832
  }
@@ -2276,13 +1837,13 @@ var WebChatServer = class _WebChatServer {
2276
1837
  }
2277
1838
  const ext = extname(filePath2).toLowerCase();
2278
1839
  if (_WebChatServer.IMAGE_EXTS.has(ext)) {
2279
- const buf = readFileSync5(filePath2);
1840
+ const buf = readFileSync4(filePath2);
2280
1841
  const mime = _WebChatServer.IMAGE_MIME[ext] ?? "application/octet-stream";
2281
1842
  const dataUrl = `data:${mime};base64,${buf.toString("base64")}`;
2282
1843
  this.wsSend(ws, { type: "file_content", path: filePath2, content: dataUrl, isImage: true });
2283
1844
  return;
2284
1845
  }
2285
- const content = readFileSync5(filePath2, "utf-8");
1846
+ const content = readFileSync4(filePath2, "utf-8");
2286
1847
  this.wsSend(ws, { type: "file_content", path: filePath2, content });
2287
1848
  } catch (err) {
2288
1849
  const reason = err instanceof Error ? err.message : "unknown";
@@ -2306,7 +1867,6 @@ var WebChatServer = class _WebChatServer {
2306
1867
  };
2307
1868
 
2308
1869
  // src/channels/telegram.ts
2309
- init_logger();
2310
1870
  import { Bot, InlineKeyboard } from "grammy";
2311
1871
  import { nanoid as nanoid3 } from "nanoid";
2312
1872
  var log9 = createLogger("channel:telegram");
@@ -2432,23 +1992,22 @@ ${card.summary}`;
2432
1992
 
2433
1993
  // src/memory/file-provider.ts
2434
1994
  import {
2435
- readFileSync as readFileSync6,
1995
+ readFileSync as readFileSync5,
2436
1996
  writeFileSync as writeFileSync6,
2437
1997
  appendFileSync as appendFileSync2,
2438
- existsSync as existsSync8,
1998
+ existsSync as existsSync7,
2439
1999
  mkdirSync as mkdirSync7,
2440
2000
  readdirSync as readdirSync2
2441
2001
  } from "fs";
2442
2002
  import { join as join8 } from "path";
2443
2003
  import { randomUUID } from "crypto";
2444
- init_logger();
2445
2004
  var log10 = createLogger("memory:file");
2446
2005
  var MEMORY_DIR = join8(CONFIG_DIR, "memories");
2447
2006
  var FileMemoryProvider = class {
2448
2007
  name = "file";
2449
2008
  degraded = false;
2450
2009
  constructor() {
2451
- if (!existsSync8(MEMORY_DIR)) {
2010
+ if (!existsSync7(MEMORY_DIR)) {
2452
2011
  mkdirSync7(MEMORY_DIR, { recursive: true });
2453
2012
  }
2454
2013
  }
@@ -2484,8 +2043,8 @@ var FileMemoryProvider = class {
2484
2043
  const files = filters.userId ? [this.userFile(filters.userId)] : readdirSync2(MEMORY_DIR).filter((f) => f.endsWith(".jsonl")).map((f) => join8(MEMORY_DIR, f));
2485
2044
  const results = [];
2486
2045
  for (const file of files) {
2487
- if (!existsSync8(file)) continue;
2488
- const lines = readFileSync6(file, "utf-8").split("\n").filter((l) => l.trim());
2046
+ if (!existsSync7(file)) continue;
2047
+ const lines = readFileSync5(file, "utf-8").split("\n").filter((l) => l.trim());
2489
2048
  for (const line of lines) {
2490
2049
  try {
2491
2050
  const stored = JSON.parse(line);
@@ -2508,8 +2067,8 @@ var FileMemoryProvider = class {
2508
2067
  async update(id, content) {
2509
2068
  const files = readdirSync2(MEMORY_DIR).filter((f) => f.endsWith(".jsonl")).map((f) => join8(MEMORY_DIR, f));
2510
2069
  for (const file of files) {
2511
- if (!existsSync8(file)) continue;
2512
- const lines = readFileSync6(file, "utf-8").split("\n").filter((l) => l.trim());
2070
+ if (!existsSync7(file)) continue;
2071
+ const lines = readFileSync5(file, "utf-8").split("\n").filter((l) => l.trim());
2513
2072
  const updated = lines.map((line) => {
2514
2073
  try {
2515
2074
  const stored = JSON.parse(line);
@@ -2528,8 +2087,8 @@ var FileMemoryProvider = class {
2528
2087
  async delete(id) {
2529
2088
  const files = readdirSync2(MEMORY_DIR).filter((f) => f.endsWith(".jsonl")).map((f) => join8(MEMORY_DIR, f));
2530
2089
  for (const file of files) {
2531
- if (!existsSync8(file)) continue;
2532
- const lines = readFileSync6(file, "utf-8").split("\n").filter((l) => l.trim());
2090
+ if (!existsSync7(file)) continue;
2091
+ const lines = readFileSync5(file, "utf-8").split("\n").filter((l) => l.trim());
2533
2092
  const filtered = lines.filter((line) => {
2534
2093
  try {
2535
2094
  const stored = JSON.parse(line);
@@ -2547,12 +2106,7 @@ var FileMemoryProvider = class {
2547
2106
  }
2548
2107
  };
2549
2108
 
2550
- // src/gateway/gateway.ts
2551
- init_logger();
2552
- init_tracer();
2553
-
2554
2109
  // src/resilience/degradation.ts
2555
- init_logger();
2556
2110
  var log11 = createLogger("degradation");
2557
2111
  var states = /* @__PURE__ */ new Map();
2558
2112
  function markDegraded(service, reason) {
@@ -2597,7 +2151,6 @@ function degradationMessage(service) {
2597
2151
  }
2598
2152
 
2599
2153
  // src/context/token-counter.ts
2600
- init_logger();
2601
2154
  var log12 = createLogger("context:token");
2602
2155
  var encode = null;
2603
2156
  function countTokens(text) {
@@ -2609,7 +2162,6 @@ function countTokens(text) {
2609
2162
  }
2610
2163
 
2611
2164
  // src/context/context-budget.ts
2612
- init_logger();
2613
2165
  var log13 = createLogger("context:budget");
2614
2166
  var FALLBACK_MAX_MESSAGES = 20;
2615
2167
  var DEFAULT_BUDGET = {
@@ -2668,7 +2220,6 @@ function allocateBudget(systemPrompt, memories, history, config = {}) {
2668
2220
  }
2669
2221
 
2670
2222
  // src/context/compaction.ts
2671
- init_logger();
2672
2223
  var log14 = createLogger("context:compact");
2673
2224
  async function compactWithFlush(allMessages, keepCount, memory, userId) {
2674
2225
  if (allMessages.length <= keepCount) {
@@ -2757,49 +2308,48 @@ function extractKeyFacts(messages) {
2757
2308
 
2758
2309
  // src/context/history-store.ts
2759
2310
  import {
2760
- readFileSync as readFileSync7,
2311
+ readFileSync as readFileSync6,
2761
2312
  writeFileSync as writeFileSync7,
2762
- existsSync as existsSync9,
2313
+ existsSync as existsSync8,
2763
2314
  mkdirSync as mkdirSync8,
2764
- renameSync as renameSync2
2315
+ renameSync
2765
2316
  } from "fs";
2766
2317
  import { join as join9 } from "path";
2767
2318
  import {
2768
- randomBytes as randomBytes2,
2769
- createCipheriv as createCipheriv2,
2319
+ randomBytes as randomBytes3,
2320
+ createCipheriv as createCipheriv3,
2770
2321
  createDecipheriv as createDecipheriv2,
2771
- createHash as createHash3
2322
+ createHash as createHash4
2772
2323
  } from "crypto";
2773
- import { hostname as hostname2, userInfo as userInfo2 } from "os";
2774
- init_logger();
2324
+ import { hostname as hostname3, userInfo as userInfo3 } from "os";
2775
2325
  var log15 = createLogger("context:history");
2776
2326
  var HISTORY_DIR = join9(CONFIG_DIR, "history");
2777
2327
  var MAX_LINES = 200;
2778
2328
  var KEEP_AFTER_ROTATE = 100;
2779
- var ALG2 = "aes-256-gcm";
2780
- var IV_LEN2 = 12;
2781
- var TAG_LEN2 = 16;
2329
+ var ALG3 = "aes-256-gcm";
2330
+ var IV_LEN3 = 12;
2331
+ var TAG_LEN3 = 16;
2782
2332
  var KEY_SEED2 = "dsclaw-history-v1";
2783
- function deriveKey2() {
2333
+ function deriveKey3() {
2784
2334
  let user = "";
2785
2335
  try {
2786
- user = userInfo2().username;
2336
+ user = userInfo3().username;
2787
2337
  } catch {
2788
2338
  user = process.env["USER"] ?? process.env["USERNAME"] ?? "default";
2789
2339
  }
2790
- return createHash3("sha256").update(`${KEY_SEED2}:${hostname2()}:${user}`).digest();
2340
+ return createHash4("sha256").update(`${KEY_SEED2}:${hostname3()}:${user}`).digest();
2791
2341
  }
2792
2342
  function encryptStr(data) {
2793
- const key = deriveKey2();
2794
- const iv = randomBytes2(IV_LEN2);
2795
- const cipher = createCipheriv2(ALG2, key, iv, { authTagLength: TAG_LEN2 });
2343
+ const key = deriveKey3();
2344
+ const iv = randomBytes3(IV_LEN3);
2345
+ const cipher = createCipheriv3(ALG3, key, iv, { authTagLength: TAG_LEN3 });
2796
2346
  const ct = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
2797
2347
  const tag = cipher.getAuthTag();
2798
2348
  return Buffer.concat([iv, tag, ct]).toString("base64");
2799
2349
  }
2800
2350
  var cache = /* @__PURE__ */ new Map();
2801
2351
  function ensureDir() {
2802
- if (!existsSync9(HISTORY_DIR)) {
2352
+ if (!existsSync8(HISTORY_DIR)) {
2803
2353
  mkdirSync8(HISTORY_DIR, { recursive: true });
2804
2354
  }
2805
2355
  }
@@ -2812,7 +2362,7 @@ function writeEncryptedFile(fp, messages) {
2812
2362
  const encrypted = encryptStr(json);
2813
2363
  const tmp = fp + ".tmp";
2814
2364
  writeFileSync7(tmp, encrypted);
2815
- renameSync2(tmp, fp);
2365
+ renameSync(tmp, fp);
2816
2366
  }
2817
2367
  function appendHistory(sessionKey, ...messages) {
2818
2368
  const history = cache.get(sessionKey) ?? [];
@@ -3070,7 +2620,6 @@ var DSClawGateway = class {
3070
2620
  webChannel = null;
3071
2621
  memory;
3072
2622
  agents = /* @__PURE__ */ new Map();
3073
- jobStores = /* @__PURE__ */ new Map();
3074
2623
  dsersClients = /* @__PURE__ */ new Map();
3075
2624
  lastUserMessages = /* @__PURE__ */ new Map();
3076
2625
  running = false;
@@ -3297,6 +2846,8 @@ var DSClawGateway = class {
3297
2846
  session.dspiSessionState = void 0;
3298
2847
  session.state = "new";
3299
2848
  saveSession(userId, session);
2849
+ const agent = this.agents.get(userId);
2850
+ if (agent) await agent.destroy();
3300
2851
  this.agents.delete(userId);
3301
2852
  this.pushSettingsState(channel, userId, session);
3302
2853
  break;
@@ -3549,8 +3100,9 @@ var DSClawGateway = class {
3549
3100
  return;
3550
3101
  }
3551
3102
  if (cmd === "/logout") {
3103
+ const agent2 = this.agents.get(userId);
3104
+ if (agent2) await agent2.destroy();
3552
3105
  this.agents.delete(userId);
3553
- this.jobStores.delete(userId);
3554
3106
  this.dsersClients.delete(userId);
3555
3107
  this.lastUserMessages.delete(userId);
3556
3108
  for (const key of sessionHistories.keys()) {
@@ -3590,7 +3142,7 @@ var DSClawGateway = class {
3590
3142
  }
3591
3143
  return;
3592
3144
  }
3593
- const agent = this.getOrCreateAgent(userId, session);
3145
+ const agent = await this.getOrCreateAgent(userId, session);
3594
3146
  const sessionKey = `${channel.name}:${userId}`;
3595
3147
  touchSession(sessionKey);
3596
3148
  const history = sessionHistories.get(sessionKey) ?? [];
@@ -3839,7 +3391,7 @@ var DSClawGateway = class {
3839
3391
  result: "success"
3840
3392
  });
3841
3393
  }
3842
- getOrCreateAgent(userId, session) {
3394
+ async getOrCreateAgent(userId, session) {
3843
3395
  let agent = this.agents.get(userId);
3844
3396
  if (agent) return agent;
3845
3397
  const dsersConfig = createDSersConfig(session.dspiEmail ?? "unknown");
@@ -3857,12 +3409,10 @@ var DSClawGateway = class {
3857
3409
  };
3858
3410
  const dsersSession = session.dspiSessionId ? { sessionId: session.dspiSessionId, state: session.dspiSessionState ?? "" } : void 0;
3859
3411
  const authCb = () => this.triggerAuth(userId);
3860
- let jobStore = this.jobStores.get(userId);
3861
- if (!jobStore) {
3862
- jobStore = new MemoryJobStore2();
3863
- this.jobStores.set(userId, jobStore);
3412
+ agent = new DSClawCoreAgent(dsersClient, this.memory, llm, authCb);
3413
+ if (dsersSession) {
3414
+ await agent.initMCP(dsersSession);
3864
3415
  }
3865
- agent = new DSClawCoreAgent(dsersClient, this.memory, llm, dsersSession, authCb, jobStore);
3866
3416
  this.agents.set(userId, agent);
3867
3417
  if (!agent.mcpAvailable) {
3868
3418
  log16.warn({ userId }, "MCP unavailable for agent \u2014 notifying user");
@@ -3892,7 +3442,6 @@ var DSClawGateway = class {
3892
3442
  };
3893
3443
 
3894
3444
  // src/resilience/circuit-breaker.ts
3895
- init_logger();
3896
3445
  import CircuitBreaker from "opossum";
3897
3446
  var log17 = createLogger("circuit-breaker");
3898
3447
  var PRESETS = {
@@ -3957,7 +3506,6 @@ function getAllBreakerStatus() {
3957
3506
  }
3958
3507
 
3959
3508
  // src/resilience/retry.ts
3960
- init_logger();
3961
3509
  var log18 = createLogger("retry");
3962
3510
  var DEFAULT_OPTIONS = {
3963
3511
  maxRetries: 2,
@@ -4029,7 +3577,6 @@ var DSCLAW_MANIFEST = {
4029
3577
  };
4030
3578
 
4031
3579
  // src/plugins/compat.ts
4032
- init_logger();
4033
3580
  var log19 = createLogger("compat");
4034
3581
  var COMPAT_MATRIX = [
4035
3582
  {