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/cli/index.js CHANGED
@@ -1,12 +1,24 @@
1
1
  #!/usr/bin/env node
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __esm = (fn, res) => function __init() {
4
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
- };
2
+
3
+ // src/cli/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/cli/init.ts
7
+ import inquirer from "inquirer";
8
+
9
+ // src/gateway/config.ts
10
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
11
+ import { join } from "path";
12
+ import { homedir } from "os";
13
+ import { z } from "zod";
14
+
15
+ // src/shared/logger.ts
16
+ import pino from "pino";
6
17
 
7
18
  // src/shared/tracer.ts
8
19
  import { AsyncLocalStorage } from "async_hooks";
9
20
  import { nanoid } from "nanoid";
21
+ var storage = new AsyncLocalStorage();
10
22
  function runWithTrace(ctx, fn) {
11
23
  const traceId = ctx.traceId ?? nanoid(12);
12
24
  return storage.run({ traceId, ...ctx }, fn);
@@ -17,53 +29,27 @@ function getTraceContext() {
17
29
  function getTraceId() {
18
30
  return getTraceContext().traceId;
19
31
  }
20
- var storage;
21
- var init_tracer = __esm({
22
- "src/shared/tracer.ts"() {
23
- "use strict";
24
- storage = new AsyncLocalStorage();
25
- }
26
- });
27
32
 
28
33
  // src/shared/logger.ts
29
- import pino from "pino";
34
+ var isPkg = typeof process.pkg !== "undefined";
35
+ var rootLogger = pino({
36
+ level: process.env["LOG_LEVEL"] ?? "info",
37
+ transport: !isPkg && process.env["NODE_ENV"] !== "production" ? { target: "pino-pretty", options: { colorize: true } } : void 0,
38
+ mixin() {
39
+ const ctx = getTraceContext();
40
+ return {
41
+ traceId: ctx.traceId,
42
+ ...ctx.userId ? { userId: ctx.userId } : {},
43
+ ...ctx.channelId ? { channelId: ctx.channelId } : {},
44
+ ...ctx.agentId ? { agentId: ctx.agentId } : {}
45
+ };
46
+ }
47
+ });
30
48
  function createLogger(module) {
31
49
  return rootLogger.child({ module });
32
50
  }
33
- var isPkg, rootLogger;
34
- var init_logger = __esm({
35
- "src/shared/logger.ts"() {
36
- "use strict";
37
- init_tracer();
38
- isPkg = typeof process.pkg !== "undefined";
39
- rootLogger = pino({
40
- level: process.env["LOG_LEVEL"] ?? "info",
41
- transport: !isPkg && process.env["NODE_ENV"] !== "production" ? { target: "pino-pretty", options: { colorize: true } } : void 0,
42
- mixin() {
43
- const ctx = getTraceContext();
44
- return {
45
- traceId: ctx.traceId,
46
- ...ctx.userId ? { userId: ctx.userId } : {},
47
- ...ctx.channelId ? { channelId: ctx.channelId } : {},
48
- ...ctx.agentId ? { agentId: ctx.agentId } : {}
49
- };
50
- }
51
- });
52
- }
53
- });
54
-
55
- // src/cli/index.ts
56
- import { Command } from "commander";
57
-
58
- // src/cli/init.ts
59
- import inquirer from "inquirer";
60
51
 
61
52
  // src/gateway/config.ts
62
- init_logger();
63
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
64
- import { join } from "path";
65
- import { homedir } from "os";
66
- import { z } from "zod";
67
53
  var log = createLogger("config");
68
54
  var CONFIG_DIR = join(homedir(), ".dsclaw");
69
55
  var CONFIG_PATH = join(CONFIG_DIR, "config.json");
@@ -165,7 +151,6 @@ import {
165
151
  } from "fs";
166
152
  import { join as join2 } from "path";
167
153
  import { hostname, userInfo } from "os";
168
- init_logger();
169
154
  var log2 = createLogger("user-session");
170
155
  var ALG = "aes-256-gcm";
171
156
  var IV_LEN = 12;
@@ -279,7 +264,6 @@ function isOnboarding(state) {
279
264
  }
280
265
 
281
266
  // src/dsers/auth.ts
282
- init_logger();
283
267
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3, chmodSync } from "fs";
284
268
  import { dirname } from "path";
285
269
 
@@ -491,10 +475,6 @@ var DSersAuth = class {
491
475
  }
492
476
  };
493
477
 
494
- // src/dsers/client.ts
495
- init_logger();
496
- init_tracer();
497
-
498
478
  // src/shared/utils.ts
499
479
  import { createHash as createHash2 } from "crypto";
500
480
  function sleep(ms) {
@@ -510,7 +490,6 @@ function parseRetryAfter(value) {
510
490
  }
511
491
 
512
492
  // src/resilience/rate-limiter.ts
513
- init_logger();
514
493
  import pLimit from "p-limit";
515
494
  var log4 = createLogger("rate-limiter");
516
495
  var DEFAULT_OUTBOUND_LIMITS = {
@@ -792,7 +771,6 @@ function findAllChromium() {
792
771
  }
793
772
 
794
773
  // src/dsers/browser-auth.ts
795
- init_logger();
796
774
  var log6 = createLogger("dsers:browser-auth");
797
775
  var DSERS_LOGIN_URL = "https://accounts.dsers.com/accounts/login";
798
776
  var LOGIN_API_PATTERN = "/account-user-bff/v1/users/login";
@@ -1020,7 +998,6 @@ async function loginViaCDP() {
1020
998
  }
1021
999
 
1022
1000
  // src/agents/core-agent.ts
1023
- init_logger();
1024
1001
  import { generateText, streamText, tool as aiTool, stepCountIs } from "ai";
1025
1002
  import { createOpenAI } from "@ai-sdk/openai";
1026
1003
  import { createAnthropic } from "@ai-sdk/anthropic";
@@ -1030,7 +1007,6 @@ import { z as z2 } from "zod";
1030
1007
  // src/shared/audit.ts
1031
1008
  import { appendFileSync, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
1032
1009
  import { join as join5 } from "path";
1033
- init_tracer();
1034
1010
  var AUDIT_DIR = join5(CONFIG_DIR, "audit");
1035
1011
  function ensureAuditDir() {
1036
1012
  if (!existsSync5(AUDIT_DIR)) {
@@ -1052,61 +1028,57 @@ function writeAuditLog(entry) {
1052
1028
  }
1053
1029
  }
1054
1030
 
1055
- // src/dsers/product.ts
1056
- async function getImportList(client, params) {
1057
- const filtered = params ? Object.fromEntries(Object.entries(params).filter(([, v]) => v != null)) : {};
1058
- return client.get("/dsers-product-bff/import-list", filtered);
1059
- }
1060
- async function getImportListItem(client, id) {
1061
- return client.get(`/dsers-product-bff/import-list/${id}`);
1062
- }
1063
- async function getMyProducts(client, params) {
1064
- return client.get("/dsers-product-bff/my-product", params);
1065
- }
1066
-
1067
1031
  // src/agents/core-agent.ts
1068
- import { configFromToken } from "@lofder/dsers-mcp-product/dist/dsers/config.js";
1069
- import { buildProvider } from "@lofder/dsers-mcp-product/dist/provider.js";
1070
- import { ImportFlowService } from "@lofder/dsers-mcp-product/dist/service.js";
1071
- import { MemoryJobStore } from "@lofder/dsers-mcp-product/dist/job-store-memory.js";
1072
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, renameSync, existsSync as existsSync6 } from "fs";
1032
+ import { createMCPClient } from "@ai-sdk/mcp";
1033
+ import { Experimental_StdioMCPTransport as StdioTransport } from "@ai-sdk/mcp/mcp-stdio";
1034
+
1035
+ // src/dsers/mcp-token-bridge.ts
1036
+ import { randomBytes as randomBytes2, createCipheriv as createCipheriv2, createHash as createHash3 } from "crypto";
1037
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, chmodSync as chmodSync2 } from "fs";
1073
1038
  import { join as join6 } from "path";
1074
- import { homedir as homedir2 } from "os";
1075
- var log7 = createLogger("agent:core");
1076
- var JOB_ID_MAP_DIR = join6(homedir2(), ".dsclaw");
1077
- var JOB_ID_MAP_FILE = join6(JOB_ID_MAP_DIR, "job-id-map.json");
1078
- var JOB_ID_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
1079
- function loadJobIdMap() {
1080
- const map = /* @__PURE__ */ new Map();
1039
+ import { homedir as homedir2, hostname as hostname2, userInfo as userInfo2 } from "os";
1040
+ var ALG2 = "aes-256-gcm";
1041
+ var IV_LEN2 = 12;
1042
+ var TAG_LEN2 = 16;
1043
+ var PACKAGE_SEED = "dsers-mcp-product-v1";
1044
+ var DSERS_BASE_URL = "https://bff-api-gw.dsers.com";
1045
+ function deriveKey2() {
1046
+ const host = hostname2();
1047
+ let user = "";
1081
1048
  try {
1082
- if (!existsSync6(JOB_ID_MAP_FILE)) return map;
1083
- const raw = JSON.parse(readFileSync4(JOB_ID_MAP_FILE, "utf-8"));
1084
- const now = Date.now();
1085
- for (const [short, entry] of Object.entries(raw)) {
1086
- if (now - entry.ts < JOB_ID_TTL_MS) {
1087
- map.set(short, entry.full);
1088
- }
1089
- }
1090
- } catch (err) {
1091
- log7.warn({ err }, "Failed to load job ID map from disk");
1049
+ user = userInfo2().username;
1050
+ } catch {
1051
+ user = process.env["USER"] ?? process.env["USERNAME"] ?? "default";
1092
1052
  }
1093
- return map;
1053
+ return createHash3("sha256").update(`${PACKAGE_SEED}:${host}:${user}`).digest();
1054
+ }
1055
+ function encrypt2(data) {
1056
+ const key = deriveKey2();
1057
+ const iv = randomBytes2(IV_LEN2);
1058
+ const cipher = createCipheriv2(ALG2, key, iv, { authTagLength: TAG_LEN2 });
1059
+ const ct = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
1060
+ const tag = cipher.getAuthTag();
1061
+ return Buffer.concat([iv, tag, ct]).toString("base64");
1094
1062
  }
1095
- function persistJobIdMap(map) {
1063
+ function writeMCPToken(sessionId, state) {
1064
+ const dir = join6(homedir2(), ".dsers-mcp");
1065
+ mkdirSync5(dir, { recursive: true, mode: 448 });
1066
+ const payload = JSON.stringify({
1067
+ session_id: sessionId,
1068
+ state,
1069
+ base_url: DSERS_BASE_URL,
1070
+ ts: Date.now()
1071
+ });
1072
+ const file = join6(dir, "credentials");
1073
+ writeFileSync4(file, encrypt2(payload), "utf-8");
1096
1074
  try {
1097
- mkdirSync5(JOB_ID_MAP_DIR, { recursive: true });
1098
- const obj = {};
1099
- const now = Date.now();
1100
- for (const [short, full] of map) {
1101
- obj[short] = { full, ts: now };
1102
- }
1103
- const tmp = JOB_ID_MAP_FILE + ".tmp";
1104
- writeFileSync4(tmp, JSON.stringify(obj));
1105
- renameSync(tmp, JOB_ID_MAP_FILE);
1106
- } catch (err) {
1107
- log7.warn({ err }, "Failed to persist job ID map");
1075
+ chmodSync2(file, 384);
1076
+ } catch {
1108
1077
  }
1109
1078
  }
1079
+
1080
+ // src/agents/core-agent.ts
1081
+ var log7 = createLogger("agent:core");
1110
1082
  function normalizeBaseUrl(url) {
1111
1083
  if (!url) return void 0;
1112
1084
  let u = url.trim();
@@ -1148,87 +1120,6 @@ function withToolAwareTimeout(promise, baseMs, isToolActive, label) {
1148
1120
  });
1149
1121
  return Promise.race([promise.finally(cleanup2), timeoutPromise]);
1150
1122
  }
1151
- var MCP_DROP_KEYS = /* @__PURE__ */ new Set([
1152
- "image_urls",
1153
- "description_html_snippet",
1154
- "effective_rules_snapshot",
1155
- "requested_rules",
1156
- "original_draft",
1157
- "resolved_source_url",
1158
- "resolver_mode",
1159
- "tags_before",
1160
- "tags_after"
1161
- ]);
1162
- function compactToolResult(result, jobIdMap) {
1163
- if (!result || typeof result !== "object") return result;
1164
- const isMcpPreview = "job_id" in result && ("title_after" in result || "status" in result);
1165
- if (isMcpPreview) {
1166
- const out2 = {};
1167
- for (const [k, v] of Object.entries(result)) {
1168
- if (MCP_DROP_KEYS.has(k)) continue;
1169
- out2[k] = v;
1170
- }
1171
- if (out2.job_id && typeof out2.job_id === "string") {
1172
- const dotIdx = out2.job_id.indexOf(".");
1173
- if (dotIdx > 0) {
1174
- const short = out2.job_id.slice(0, dotIdx);
1175
- if (jobIdMap) jobIdMap.set(short, out2.job_id);
1176
- out2.job_id = short;
1177
- }
1178
- }
1179
- if (out2.skus && Array.isArray(out2.skus) && out2.skus.length > 1) {
1180
- const [header, ...rows] = out2.skus;
1181
- out2.variant_summary = rows.slice(0, 5).map((row) => {
1182
- const obj = {};
1183
- header.forEach((key, i) => {
1184
- obj[key] = row[i];
1185
- });
1186
- return obj;
1187
- });
1188
- if (rows.length > 5) {
1189
- out2.variant_summary_note = `Showing 5 of ${rows.length} variants`;
1190
- }
1191
- delete out2.skus;
1192
- }
1193
- if (out2.title_after) {
1194
- out2._title_modified = true;
1195
- out2._display_title = out2.title_after;
1196
- }
1197
- if (out2.desc_changed) {
1198
- out2._description_modified = true;
1199
- }
1200
- if (out2.warnings?.length > 5) out2.warnings = out2.warnings.slice(0, 5);
1201
- if (out2.stores) {
1202
- out2.stores = out2.stores.map((s) => ({
1203
- store_ref: s.store_ref,
1204
- display_name: s.display_name,
1205
- platform: s.platform
1206
- }));
1207
- }
1208
- if (out2.account_info) {
1209
- const ai = out2.account_info;
1210
- out2.account_info = { plan: ai.plan, limits: ai.limits, aliexpress_auth: ai.aliexpress_auth };
1211
- }
1212
- return out2;
1213
- }
1214
- const json = JSON.stringify(result);
1215
- if (json.length <= 4e3) return result;
1216
- const out = {};
1217
- for (const [k, v] of Object.entries(result)) {
1218
- if (MCP_DROP_KEYS.has(k)) continue;
1219
- if (Array.isArray(v) && v.length > 20) {
1220
- out[k] = v.slice(0, 20);
1221
- out[`_${k}_truncated`] = `${v.length} total, showing first 20`;
1222
- } else {
1223
- out[k] = v;
1224
- }
1225
- }
1226
- const outJson = JSON.stringify(out);
1227
- if (outJson.length > 8e3) {
1228
- return { _truncated: true, _original_size: json.length, summary: outJson.slice(0, 6e3) + "..." };
1229
- }
1230
- return out;
1231
- }
1232
1123
  function buildModel(llm) {
1233
1124
  const baseURL = normalizeBaseUrl(llm.baseUrl);
1234
1125
  if (baseURL || llm.provider === "other") {
@@ -1343,42 +1234,41 @@ var DSClawCoreAgent = class {
1343
1234
  memory;
1344
1235
  llm;
1345
1236
  customTools = /* @__PURE__ */ new Map();
1346
- importService = null;
1347
- jobIdMap = loadJobIdMap();
1237
+ mcpClient = null;
1348
1238
  mcpAvailable = false;
1349
1239
  authCallback;
1350
- constructor(dsers, memory, llm, dsersSession, authCallback, jobStore) {
1240
+ constructor(dsers, memory, llm, authCallback) {
1351
1241
  this.dsers = dsers;
1352
1242
  this.memory = memory;
1353
1243
  this.llm = llm;
1354
1244
  this.authCallback = authCallback;
1355
- if (dsersSession?.sessionId) {
1356
- try {
1357
- const mcpConfig = configFromToken(dsersSession.sessionId, dsersSession.state);
1358
- const provider = buildProvider(mcpConfig);
1359
- this.importService = new ImportFlowService(provider, jobStore ?? new MemoryJobStore());
1360
- this.mcpAvailable = true;
1361
- log7.info("MCP ImportFlowService initialized");
1362
- } catch (err) {
1363
- log7.warn({ err }, "Failed to init MCP ImportFlowService \u2014 product tools unavailable");
1364
- }
1245
+ }
1246
+ async initMCP(dsersSession) {
1247
+ try {
1248
+ writeMCPToken(dsersSession.sessionId, dsersSession.state);
1249
+ this.mcpClient = await createMCPClient({
1250
+ transport: new StdioTransport({
1251
+ command: "npx",
1252
+ args: ["-y", "@lofder/dsers-mcp-product"]
1253
+ })
1254
+ });
1255
+ this.mcpAvailable = true;
1256
+ log7.info("MCP client connected via stdio");
1257
+ } catch (err) {
1258
+ log7.warn({ err }, "Failed to connect MCP \u2014 product tools unavailable");
1259
+ }
1260
+ }
1261
+ async destroy() {
1262
+ if (this.mcpClient) {
1263
+ await this.mcpClient.close();
1264
+ this.mcpClient = null;
1265
+ this.mcpAvailable = false;
1365
1266
  }
1366
1267
  }
1367
1268
  getSystemPrompt() {
1368
1269
  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.";
1369
1270
  return SYSTEM_PROMPT.replace("{{LANGUAGE_RULE}}", `Language rule: ${rule}`);
1370
1271
  }
1371
- shortenJobId(fullId) {
1372
- const dotIdx = fullId.indexOf(".");
1373
- if (dotIdx <= 0) return fullId;
1374
- const short = fullId.slice(0, dotIdx);
1375
- this.jobIdMap.set(short, fullId);
1376
- persistJobIdMap(this.jobIdMap);
1377
- return short;
1378
- }
1379
- resolveJobId(maybeShort) {
1380
- return this.jobIdMap.get(maybeShort) ?? maybeShort;
1381
- }
1382
1272
  registerTool(t2) {
1383
1273
  this.customTools.set(t2.name, t2);
1384
1274
  }
@@ -1405,7 +1295,7 @@ ${memories.map((m2) => `- ${m2.content}`).join("\n")}` : "";
1405
1295
  ),
1406
1296
  { role: "user", content: message }
1407
1297
  ];
1408
- const tools = this.buildAITools(context);
1298
+ const tools = await this.buildAITools(context);
1409
1299
  log7.info(
1410
1300
  { userId: context.userId, messageLen: message.length, toolCount: Object.keys(tools).length },
1411
1301
  "Processing message"
@@ -1457,9 +1347,9 @@ ${memories.map((m2) => `- ${m2.content}`).join("\n")}` : "";
1457
1347
  processStream(message, context, onStatus, callbacks, attachments) {
1458
1348
  const model = buildModel(this.llm);
1459
1349
  const memories = this.memory.search(message, { userId: context.userId, limit: 5 }).catch(() => []);
1460
- const tools = this.buildAITools(context);
1350
+ const toolsPromise = this.buildAITools(context);
1461
1351
  const self = this;
1462
- const streamPromise = memories.then((mems) => {
1352
+ const streamPromise = Promise.all([memories, toolsPromise]).then(([mems, tools]) => {
1463
1353
  const memoryContext = mems.length > 0 ? `
1464
1354
 
1465
1355
  Relevant memories:
@@ -1595,207 +1485,13 @@ ${mems.map((m2) => `- ${m2.content}`).join("\n")}` : "";
1595
1485
  });
1596
1486
  return { textStream, response };
1597
1487
  }
1598
- buildAITools(context) {
1599
- const dsers = this.dsers;
1600
- const svc = this.importService;
1601
- const jmap = this.jobIdMap;
1602
- const resolveJid = (id) => this.resolveJobId(id);
1603
- const audit = (action, target, params, result = "pending") => writeAuditLog({ userId: context.userId, agentId: this.id, action, target, params, result });
1488
+ async buildAITools(_context) {
1604
1489
  const tools = {};
1605
- if (svc) {
1606
- tools.dsers_store_discover = aiTool({
1607
- description: "Get connected stores, account info (plan/limits), and rule capabilities. Call first before import/push.",
1608
- inputSchema: z2.object({
1609
- target_store: z2.string().optional().describe("Filter by store ID or name")
1610
- }),
1611
- execute: async (input) => {
1612
- audit("dsers_store_discover", "mcp");
1613
- const result = await withTimeout(svc.getRuleCapabilities(input), 6e4, "Store discovery");
1614
- audit("dsers_store_discover", "mcp", void 0, "success");
1615
- return compactToolResult(result, jmap);
1616
- }
1617
- });
1618
- tools.dsers_product_import = aiTool({
1619
- 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.",
1620
- inputSchema: z2.object({
1621
- source_url: z2.string().optional().describe("Supplier product URL"),
1622
- source_urls_json: z2.string().optional().describe("Batch: JSON array of URLs"),
1623
- job_id: z2.string().optional().describe("Existing job ID for rule updates"),
1624
- source_hint: z2.string().optional().describe("auto|aliexpress|alibaba|accio"),
1625
- country: z2.string().default("US").describe("Country code"),
1626
- target_store: z2.string().optional().describe("Store ID or name"),
1627
- visibility_mode: z2.string().optional().describe("backend_only|sell_immediately"),
1628
- rules_json: z2.string().optional().describe(
1629
- "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?}]."
1630
- )
1631
- }),
1632
- execute: async (input) => {
1633
- audit("dsers_product_import", "mcp", input);
1634
- const payload = {};
1635
- if (input.job_id && !input.source_url && !input.source_urls_json) {
1636
- payload.job_id = resolveJid(input.job_id);
1637
- if (input.rules_json) {
1638
- try {
1639
- payload.rules = JSON.parse(input.rules_json);
1640
- } catch {
1641
- throw new Error(
1642
- 'Invalid JSON in rules_json. Expected: {"pricing":{"mode":"fixed_price","fixed_price":9.99}} or {"content":{"title_override":"New Title"}}'
1643
- );
1644
- }
1645
- } else {
1646
- payload._keep_existing_rules = true;
1647
- }
1648
- if (input.target_store) payload.target_store = input.target_store;
1649
- if (input.visibility_mode) payload.visibility_mode = input.visibility_mode;
1650
- const result2 = await withTimeout(svc.reapplyRules(payload), 12e4, "Rule reapply");
1651
- audit("dsers_product_import", "mcp", void 0, "success");
1652
- return compactToolResult(result2, jmap);
1653
- }
1654
- if (input.source_url) payload.source_url = input.source_url;
1655
- if (input.source_urls_json) {
1656
- try {
1657
- payload.source_urls = JSON.parse(input.source_urls_json);
1658
- } catch {
1659
- throw new Error("Invalid JSON in source_urls_json.");
1660
- }
1661
- }
1662
- if (input.source_hint) payload.source_hint = input.source_hint;
1663
- if (input.country) payload.country = input.country;
1664
- if (input.target_store) payload.target_store = input.target_store;
1665
- payload.visibility_mode = input.visibility_mode || "backend_only";
1666
- if (input.rules_json) {
1667
- try {
1668
- payload.rules = JSON.parse(input.rules_json);
1669
- } catch {
1670
- throw new Error("Invalid JSON in rules_json.");
1671
- }
1672
- }
1673
- const result = await withTimeout(svc.prepareImportCandidate(payload), 12e4, "Product import");
1674
- audit("dsers_product_import", "mcp", void 0, "success");
1675
- return compactToolResult(result, jmap);
1676
- }
1677
- });
1678
- tools.dsers_product_preview = aiTool({
1679
- description: "Reload preview for an imported job. Returns prices, variants, images.",
1680
- inputSchema: z2.object({
1681
- job_id: z2.string().describe("Job ID from dsers_product_import"),
1682
- variant_offset: z2.number().optional().describe("Start index for variant pagination"),
1683
- variant_limit: z2.number().optional().describe("Max variants to return")
1684
- }),
1685
- execute: async (input) => {
1686
- audit("dsers_product_preview", "mcp", input);
1687
- const result = await withTimeout(svc.getImportPreview({ ...input, job_id: resolveJid(input.job_id) }), 3e4, "Preview");
1688
- audit("dsers_product_preview", "mcp", void 0, "success");
1689
- return compactToolResult(result, jmap);
1690
- }
1691
- });
1692
- tools.dsers_store_push = aiTool({
1693
- 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.",
1694
- inputSchema: z2.object({
1695
- job_id: z2.string().optional().describe("Job ID"),
1696
- job_ids_json: z2.string().optional().describe("Batch: JSON array of job IDs"),
1697
- target_store: z2.string().optional().describe("Store ID or name from dsers_store_discover"),
1698
- target_stores_json: z2.string().optional().describe("Multi-store: JSON array of store names"),
1699
- visibility_mode: z2.string().optional().describe("backend_only|sell_immediately"),
1700
- force_push: z2.boolean().optional().describe("Override safety checks"),
1701
- push_options_json: z2.string().optional().describe(
1702
- '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"}'
1703
- )
1704
- }),
1705
- execute: async (input) => {
1706
- audit("dsers_store_push", "mcp", input);
1707
- const payload = {};
1708
- if (input.job_ids_json) {
1709
- try {
1710
- const ids = JSON.parse(input.job_ids_json);
1711
- payload.job_ids = ids.map(resolveJid);
1712
- } catch {
1713
- throw new Error("Invalid JSON in job_ids_json");
1714
- }
1715
- } else if (input.job_id) {
1716
- payload.job_id = resolveJid(input.job_id);
1717
- }
1718
- if (input.target_store) payload.target_store = input.target_store;
1719
- if (input.target_stores_json) {
1720
- try {
1721
- payload.target_stores = JSON.parse(input.target_stores_json);
1722
- } catch {
1723
- throw new Error("Invalid JSON in target_stores_json");
1724
- }
1725
- }
1726
- if (input.visibility_mode) payload.visibility_mode = input.visibility_mode;
1727
- if (input.force_push) payload.force_push = true;
1728
- if (input.push_options_json) {
1729
- try {
1730
- payload.push_options = JSON.parse(input.push_options_json);
1731
- } catch {
1732
- throw new Error("Invalid JSON in push_options_json");
1733
- }
1734
- }
1735
- const result = await withTimeout(svc.confirmPushToStore(payload), 18e4, "Push to store");
1736
- audit("dsers_store_push", "mcp", void 0, "success");
1737
- return compactToolResult(result, jmap);
1738
- }
1739
- });
1740
- tools.dsers_rules_validate = aiTool({
1741
- description: "Validate pricing/content/image rules before importing.",
1742
- inputSchema: z2.object({
1743
- rules_json: z2.string().describe("Rules as JSON string"),
1744
- target_store: z2.string().optional().describe("Store ID or name")
1745
- }),
1746
- execute: async (input) => {
1747
- audit("dsers_rules_validate", "mcp");
1748
- let rules;
1749
- try {
1750
- rules = JSON.parse(input.rules_json);
1751
- } catch {
1752
- throw new Error("Invalid JSON in rules_json");
1753
- }
1754
- const result = await withTimeout(svc.validateRules({ rules, target_store: input.target_store ?? null }), 3e4, "Rule validation");
1755
- audit("dsers_rules_validate", "mcp", void 0, "success");
1756
- return compactToolResult(result, jmap);
1757
- }
1758
- });
1759
- tools.dsers_job_status = aiTool({
1760
- description: "Check job status: preview_ready \u2192 push_requested \u2192 completed/failed.",
1761
- inputSchema: z2.object({
1762
- job_id: z2.string().describe("Job ID")
1763
- }),
1764
- execute: async (input) => {
1765
- audit("dsers_job_status", "mcp", input);
1766
- const result = await withTimeout(svc.getJobStatus({ ...input, job_id: resolveJid(input.job_id) }), 3e4, "Job status");
1767
- audit("dsers_job_status", "mcp", void 0, "success");
1768
- return compactToolResult(result, jmap);
1769
- }
1770
- });
1771
- tools.dsers_product_visibility = aiTool({
1772
- description: "Set job visibility: backend_only (draft) or sell_immediately (live).",
1773
- inputSchema: z2.object({
1774
- job_id: z2.string().describe("Job ID"),
1775
- visibility_mode: z2.string().describe("backend_only|sell_immediately")
1776
- }),
1777
- execute: async (input) => {
1778
- audit("dsers_product_visibility", "mcp", input);
1779
- const result = await withTimeout(svc.setProductVisibility({ ...input, job_id: resolveJid(input.job_id) }), 3e4, "Set visibility");
1780
- audit("dsers_product_visibility", "mcp", void 0, "success");
1781
- return compactToolResult(result, jmap);
1782
- }
1783
- });
1784
- tools.dsers_product_delete = aiTool({
1785
- description: "Delete product from import list. Call without confirm first, then with confirm=true after user approves.",
1786
- inputSchema: z2.object({
1787
- import_item_id: z2.string().describe("Item ID from dsers_import_list_search"),
1788
- confirm: z2.boolean().optional().describe("true only after user approves")
1789
- }),
1790
- execute: async (input) => {
1791
- audit("dsers_product_delete", "mcp", input);
1792
- const result = await withTimeout(svc.deleteImportItem(input), 3e4, "Delete import item");
1793
- audit("dsers_product_delete", "mcp", void 0, "success");
1794
- return compactToolResult(result, jmap);
1795
- }
1796
- });
1490
+ if (this.mcpClient) {
1491
+ const mcpTools = await this.mcpClient.tools();
1492
+ Object.assign(tools, mcpTools);
1797
1493
  } else {
1798
- log7.warn("MCP ImportFlowService not available \u2014 no MCP tools registered");
1494
+ log7.warn("MCP client not connected \u2014 no product tools available");
1799
1495
  }
1800
1496
  if (this.authCallback) {
1801
1497
  tools.dsers_authorize = aiTool({
@@ -1807,139 +1503,13 @@ ${mems.map((m2) => `- ${m2.content}`).join("\n")}` : "";
1807
1503
  }
1808
1504
  });
1809
1505
  }
1810
- tools.dsers_import_list_search = aiTool({
1811
- description: "Search import list. Returns items with id, source_url, cost_range, sell_price_range. Use source_url with dsers_product_import for modifications.",
1812
- inputSchema: z2.object({
1813
- page: z2.number().optional().describe("Page number (default 1)"),
1814
- pageSize: z2.number().optional().describe("Items per page (default 20)"),
1815
- keyword: z2.string().optional().describe("Search keyword")
1816
- }),
1817
- execute: async (input) => {
1818
- audit("dsers_import_list_search", "dsers:product", input);
1819
- const result = await withTimeout(getImportList(dsers, input), 3e4, "Search import list");
1820
- audit("dsers_import_list_search", "dsers:product", void 0, "success");
1821
- const items = result?.data?.list ?? result?.data ?? [];
1822
- if (Array.isArray(items) && items.length > 0) {
1823
- const enriched = await Promise.allSettled(
1824
- items.slice(0, 10).map(async (item) => {
1825
- const itemId = item.id ?? item.importListId;
1826
- if (!itemId) return item;
1827
- try {
1828
- const detail = await withTimeout(
1829
- getImportListItem(dsers, String(itemId)),
1830
- 15e3,
1831
- "Item detail"
1832
- );
1833
- const detailData = detail?.data ?? detail;
1834
- const variants = detailData?.variants ?? detailData?.skuList ?? detailData?.variantList ?? [];
1835
- if (!Array.isArray(variants) || !variants.length) return item;
1836
- const sellPrices = [];
1837
- const costPrices = [];
1838
- for (const v of variants) {
1839
- const rawSell = Number(v.sellPrice ?? v.salePrice ?? v.price);
1840
- const rawCost = Number(v.supplierPrice ?? v.buyPrice ?? v.cost);
1841
- const sell = rawSell > 100 ? rawSell / 100 : rawSell;
1842
- const cost = rawCost > 100 ? rawCost / 100 : rawCost;
1843
- if (Number.isFinite(sell) && sell > 0) sellPrices.push(sell);
1844
- if (Number.isFinite(cost) && cost > 0) costPrices.push(cost);
1845
- }
1846
- const enrichedItem = { ...item };
1847
- const supplyProductId = detailData?.supplyProductId ?? item?.supplyProductId;
1848
- const supplyAppId = Number(detailData?.supplyAppId ?? item?.supplyAppId ?? 1);
1849
- if (supplyProductId) {
1850
- if (supplyAppId === 2) {
1851
- enrichedItem.source_url = `https://www.temu.com/product/${supplyProductId}.html`;
1852
- } else if (supplyAppId === 3) {
1853
- enrichedItem.source_url = `https://detail.1688.com/offer/${supplyProductId}.html`;
1854
- } else {
1855
- enrichedItem.source_url = `https://www.aliexpress.com/item/${supplyProductId}.html`;
1856
- }
1857
- }
1858
- if (costPrices.length) {
1859
- enrichedItem.cost_range = `$${Math.min(...costPrices).toFixed(2)} \u2013 $${Math.max(...costPrices).toFixed(2)}`;
1860
- }
1861
- if (sellPrices.length) {
1862
- enrichedItem.sell_price_range = `$${Math.min(...sellPrices).toFixed(2)} \u2013 $${Math.max(...sellPrices).toFixed(2)}`;
1863
- enrichedItem.no_markup = sellPrices.every((s, i) => Math.abs(s - (costPrices[i] ?? s)) < 0.01);
1864
- }
1865
- enrichedItem.variant_count = variants.length;
1866
- let totalStock = 0;
1867
- const lowStockVariants = [];
1868
- for (const v of variants) {
1869
- const stock = Number(v.stock ?? v.quantity ?? v.inventory ?? 0);
1870
- const safeStock = Number.isFinite(stock) ? stock : 0;
1871
- totalStock += safeStock;
1872
- if (safeStock <= 5) {
1873
- lowStockVariants.push({
1874
- id: String(v.id ?? v.variantId ?? v.skuId ?? ""),
1875
- sku: String(v.sku ?? v.skuAttr ?? v.optionName ?? v.title ?? ""),
1876
- stock: safeStock
1877
- });
1878
- }
1879
- }
1880
- enrichedItem.total_stock = totalStock;
1881
- if (lowStockVariants.length > 0) {
1882
- enrichedItem.low_stock_variants = lowStockVariants;
1883
- enrichedItem.low_stock_warning = `${lowStockVariants.length} of ${variants.length} variants have stock \u2264 5`;
1884
- }
1885
- return enrichedItem;
1886
- } catch (err) {
1887
- log7.warn({ err, itemId }, "Failed to enrich import list item");
1888
- return item;
1889
- }
1890
- })
1891
- );
1892
- const enrichedItems = enriched.map((r) => r.status === "fulfilled" ? r.value : items[0]);
1893
- const cleanItems = enrichedItems.map((item) => ({
1894
- id: item.id ?? item.importListId,
1895
- title: item.title ?? "(untitled)",
1896
- source_url: item.source_url,
1897
- cost_range: item.cost_range,
1898
- sell_price_range: item.sell_price_range,
1899
- no_markup: item.no_markup,
1900
- variant_count: item.variant_count,
1901
- total_stock: item.total_stock,
1902
- low_stock_warning: item.low_stock_warning,
1903
- low_stock_variants: item.low_stock_variants
1904
- }));
1905
- return { items: cleanItems, total: result?.data?.total ?? cleanItems.length };
1906
- }
1907
- const rawItems = result?.data ?? [];
1908
- if (Array.isArray(rawItems)) {
1909
- return { items: rawItems.map((item) => ({
1910
- id: item.id ?? item.importListId,
1911
- title: item.title ?? "(untitled)"
1912
- })), total: rawItems.length };
1913
- }
1914
- return compactToolResult(result);
1915
- }
1916
- });
1917
- tools.dsers_my_products = aiTool({
1918
- description: "Get products already pushed to stores.",
1919
- inputSchema: z2.object({
1920
- page: z2.number().optional(),
1921
- pageSize: z2.number().optional(),
1922
- keyword: z2.string().optional(),
1923
- storeId: z2.string().optional()
1924
- }),
1925
- execute: async (input) => {
1926
- audit("dsers_my_products", "dsers:product", input);
1927
- const result = await withTimeout(getMyProducts(dsers, input), 3e4, "My products");
1928
- audit("dsers_my_products", "dsers:product", void 0, "success");
1929
- return compactToolResult(result);
1930
- }
1931
- });
1932
1506
  return tools;
1933
1507
  }
1934
1508
  };
1935
1509
 
1936
- // src/gateway/gateway.ts
1937
- import { MemoryJobStore as MemoryJobStore2 } from "@lofder/dsers-mcp-product/dist/job-store-memory.js";
1938
-
1939
1510
  // src/web/server.ts
1940
- init_logger();
1941
1511
  import { createServer } from "http";
1942
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync7, statSync, readdirSync } from "fs";
1512
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync6, statSync, readdirSync } from "fs";
1943
1513
  import { join as join7, dirname as dirname2, extname } from "path";
1944
1514
  import { fileURLToPath } from "url";
1945
1515
  import { homedir as homedir3 } from "os";
@@ -1968,7 +1538,7 @@ function findWebDir() {
1968
1538
  join7(thisDir, "..", "..", "dist", "web")
1969
1539
  ];
1970
1540
  for (const d of candidates) {
1971
- if (existsSync7(join7(d, "index.html"))) return d;
1541
+ if (existsSync6(join7(d, "index.html"))) return d;
1972
1542
  }
1973
1543
  throw new Error(
1974
1544
  `Web build not found (index.html). Looked in:
@@ -2007,7 +1577,7 @@ var WebChatServer = class _WebChatServer {
2007
1577
  }
2008
1578
  tryListen(tryPort, webDir) {
2009
1579
  return new Promise((resolve) => {
2010
- const indexHtml = readFileSync5(join7(webDir, "index.html"), "utf-8");
1580
+ const indexHtml = readFileSync4(join7(webDir, "index.html"), "utf-8");
2011
1581
  const srv = createServer((req, res) => {
2012
1582
  if (req.url === "/health") {
2013
1583
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -2020,11 +1590,11 @@ var WebChatServer = class _WebChatServer {
2020
1590
  const fileName = urlPath.slice("/uploads/".length);
2021
1591
  if (/^[a-zA-Z0-9._-]+$/.test(fileName)) {
2022
1592
  const filePath2 = join7(uploadsDir, fileName);
2023
- if (existsSync7(filePath2) && statSync(filePath2).isFile()) {
1593
+ if (existsSync6(filePath2) && statSync(filePath2).isFile()) {
2024
1594
  const ext = extname(filePath2);
2025
1595
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
2026
1596
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "public, max-age=86400" });
2027
- res.end(readFileSync5(filePath2));
1597
+ res.end(readFileSync4(filePath2));
2028
1598
  return;
2029
1599
  }
2030
1600
  }
@@ -2034,12 +1604,12 @@ var WebChatServer = class _WebChatServer {
2034
1604
  }
2035
1605
  if (urlPath !== "/" && urlPath !== "/index.html") {
2036
1606
  const filePath2 = join7(webDir, urlPath);
2037
- if (existsSync7(filePath2) && statSync(filePath2).isFile()) {
1607
+ if (existsSync6(filePath2) && statSync(filePath2).isFile()) {
2038
1608
  const ext = extname(filePath2);
2039
1609
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
2040
1610
  const cacheHeader = urlPath.includes("/assets/") ? "public, max-age=31536000, immutable" : "no-cache";
2041
1611
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": cacheHeader });
2042
- res.end(readFileSync5(filePath2));
1612
+ res.end(readFileSync4(filePath2));
2043
1613
  return;
2044
1614
  }
2045
1615
  }
@@ -2253,7 +1823,7 @@ var WebChatServer = class _WebChatServer {
2253
1823
  // ─── File Browsing ───────────────────────────────────────────
2254
1824
  buildFileTree() {
2255
1825
  const configDir = join7(homedir3(), ".dsclaw");
2256
- if (!existsSync7(configDir)) return [];
1826
+ if (!existsSync6(configDir)) return [];
2257
1827
  const scanDir = (dirPath, depth = 0) => {
2258
1828
  if (depth > 5) return [];
2259
1829
  try {
@@ -2301,7 +1871,7 @@ var WebChatServer = class _WebChatServer {
2301
1871
  return;
2302
1872
  }
2303
1873
  try {
2304
- if (!existsSync7(filePath2) || !statSync(filePath2).isFile()) {
1874
+ if (!existsSync6(filePath2) || !statSync(filePath2).isFile()) {
2305
1875
  this.wsSend(ws, { type: "file_content", path: filePath2, content: "[\u6587\u4EF6\u4E0D\u5B58\u5728]" });
2306
1876
  return;
2307
1877
  }
@@ -2312,13 +1882,13 @@ var WebChatServer = class _WebChatServer {
2312
1882
  }
2313
1883
  const ext = extname(filePath2).toLowerCase();
2314
1884
  if (_WebChatServer.IMAGE_EXTS.has(ext)) {
2315
- const buf = readFileSync5(filePath2);
1885
+ const buf = readFileSync4(filePath2);
2316
1886
  const mime = _WebChatServer.IMAGE_MIME[ext] ?? "application/octet-stream";
2317
1887
  const dataUrl = `data:${mime};base64,${buf.toString("base64")}`;
2318
1888
  this.wsSend(ws, { type: "file_content", path: filePath2, content: dataUrl, isImage: true });
2319
1889
  return;
2320
1890
  }
2321
- const content = readFileSync5(filePath2, "utf-8");
1891
+ const content = readFileSync4(filePath2, "utf-8");
2322
1892
  this.wsSend(ws, { type: "file_content", path: filePath2, content });
2323
1893
  } catch (err) {
2324
1894
  const reason = err instanceof Error ? err.message : "unknown";
@@ -2342,7 +1912,6 @@ var WebChatServer = class _WebChatServer {
2342
1912
  };
2343
1913
 
2344
1914
  // src/channels/telegram.ts
2345
- init_logger();
2346
1915
  import { Bot, InlineKeyboard } from "grammy";
2347
1916
  import { nanoid as nanoid3 } from "nanoid";
2348
1917
  var log9 = createLogger("channel:telegram");
@@ -2468,23 +2037,22 @@ ${card.summary}`;
2468
2037
 
2469
2038
  // src/memory/file-provider.ts
2470
2039
  import {
2471
- readFileSync as readFileSync6,
2040
+ readFileSync as readFileSync5,
2472
2041
  writeFileSync as writeFileSync6,
2473
2042
  appendFileSync as appendFileSync2,
2474
- existsSync as existsSync8,
2043
+ existsSync as existsSync7,
2475
2044
  mkdirSync as mkdirSync7,
2476
2045
  readdirSync as readdirSync2
2477
2046
  } from "fs";
2478
2047
  import { join as join8 } from "path";
2479
2048
  import { randomUUID } from "crypto";
2480
- init_logger();
2481
2049
  var log10 = createLogger("memory:file");
2482
2050
  var MEMORY_DIR = join8(CONFIG_DIR, "memories");
2483
2051
  var FileMemoryProvider = class {
2484
2052
  name = "file";
2485
2053
  degraded = false;
2486
2054
  constructor() {
2487
- if (!existsSync8(MEMORY_DIR)) {
2055
+ if (!existsSync7(MEMORY_DIR)) {
2488
2056
  mkdirSync7(MEMORY_DIR, { recursive: true });
2489
2057
  }
2490
2058
  }
@@ -2520,8 +2088,8 @@ var FileMemoryProvider = class {
2520
2088
  const files = filters.userId ? [this.userFile(filters.userId)] : readdirSync2(MEMORY_DIR).filter((f) => f.endsWith(".jsonl")).map((f) => join8(MEMORY_DIR, f));
2521
2089
  const results = [];
2522
2090
  for (const file of files) {
2523
- if (!existsSync8(file)) continue;
2524
- const lines = readFileSync6(file, "utf-8").split("\n").filter((l) => l.trim());
2091
+ if (!existsSync7(file)) continue;
2092
+ const lines = readFileSync5(file, "utf-8").split("\n").filter((l) => l.trim());
2525
2093
  for (const line of lines) {
2526
2094
  try {
2527
2095
  const stored = JSON.parse(line);
@@ -2544,8 +2112,8 @@ var FileMemoryProvider = class {
2544
2112
  async update(id, content) {
2545
2113
  const files = readdirSync2(MEMORY_DIR).filter((f) => f.endsWith(".jsonl")).map((f) => join8(MEMORY_DIR, f));
2546
2114
  for (const file of files) {
2547
- if (!existsSync8(file)) continue;
2548
- const lines = readFileSync6(file, "utf-8").split("\n").filter((l) => l.trim());
2115
+ if (!existsSync7(file)) continue;
2116
+ const lines = readFileSync5(file, "utf-8").split("\n").filter((l) => l.trim());
2549
2117
  const updated = lines.map((line) => {
2550
2118
  try {
2551
2119
  const stored = JSON.parse(line);
@@ -2564,8 +2132,8 @@ var FileMemoryProvider = class {
2564
2132
  async delete(id) {
2565
2133
  const files = readdirSync2(MEMORY_DIR).filter((f) => f.endsWith(".jsonl")).map((f) => join8(MEMORY_DIR, f));
2566
2134
  for (const file of files) {
2567
- if (!existsSync8(file)) continue;
2568
- const lines = readFileSync6(file, "utf-8").split("\n").filter((l) => l.trim());
2135
+ if (!existsSync7(file)) continue;
2136
+ const lines = readFileSync5(file, "utf-8").split("\n").filter((l) => l.trim());
2569
2137
  const filtered = lines.filter((line) => {
2570
2138
  try {
2571
2139
  const stored = JSON.parse(line);
@@ -2583,12 +2151,7 @@ var FileMemoryProvider = class {
2583
2151
  }
2584
2152
  };
2585
2153
 
2586
- // src/gateway/gateway.ts
2587
- init_logger();
2588
- init_tracer();
2589
-
2590
2154
  // src/resilience/degradation.ts
2591
- init_logger();
2592
2155
  var log11 = createLogger("degradation");
2593
2156
  var states = /* @__PURE__ */ new Map();
2594
2157
  function isDegraded(service) {
@@ -2606,7 +2169,6 @@ function degradationMessage(service) {
2606
2169
  }
2607
2170
 
2608
2171
  // src/context/token-counter.ts
2609
- init_logger();
2610
2172
  var log12 = createLogger("context:token");
2611
2173
  var encode = null;
2612
2174
  function countTokens(text) {
@@ -2618,7 +2180,6 @@ function countTokens(text) {
2618
2180
  }
2619
2181
 
2620
2182
  // src/context/context-budget.ts
2621
- init_logger();
2622
2183
  var log13 = createLogger("context:budget");
2623
2184
  var FALLBACK_MAX_MESSAGES = 20;
2624
2185
  var DEFAULT_BUDGET = {
@@ -2677,7 +2238,6 @@ function allocateBudget(systemPrompt, memories, history, config = {}) {
2677
2238
  }
2678
2239
 
2679
2240
  // src/context/compaction.ts
2680
- init_logger();
2681
2241
  var log14 = createLogger("context:compact");
2682
2242
  async function compactWithFlush(allMessages, keepCount, memory, userId) {
2683
2243
  if (allMessages.length <= keepCount) {
@@ -2766,49 +2326,48 @@ function extractKeyFacts(messages) {
2766
2326
 
2767
2327
  // src/context/history-store.ts
2768
2328
  import {
2769
- readFileSync as readFileSync7,
2329
+ readFileSync as readFileSync6,
2770
2330
  writeFileSync as writeFileSync7,
2771
- existsSync as existsSync9,
2331
+ existsSync as existsSync8,
2772
2332
  mkdirSync as mkdirSync8,
2773
- renameSync as renameSync2
2333
+ renameSync
2774
2334
  } from "fs";
2775
2335
  import { join as join9 } from "path";
2776
2336
  import {
2777
- randomBytes as randomBytes2,
2778
- createCipheriv as createCipheriv2,
2337
+ randomBytes as randomBytes3,
2338
+ createCipheriv as createCipheriv3,
2779
2339
  createDecipheriv as createDecipheriv2,
2780
- createHash as createHash3
2340
+ createHash as createHash4
2781
2341
  } from "crypto";
2782
- import { hostname as hostname2, userInfo as userInfo2 } from "os";
2783
- init_logger();
2342
+ import { hostname as hostname3, userInfo as userInfo3 } from "os";
2784
2343
  var log15 = createLogger("context:history");
2785
2344
  var HISTORY_DIR = join9(CONFIG_DIR, "history");
2786
2345
  var MAX_LINES = 200;
2787
2346
  var KEEP_AFTER_ROTATE = 100;
2788
- var ALG2 = "aes-256-gcm";
2789
- var IV_LEN2 = 12;
2790
- var TAG_LEN2 = 16;
2347
+ var ALG3 = "aes-256-gcm";
2348
+ var IV_LEN3 = 12;
2349
+ var TAG_LEN3 = 16;
2791
2350
  var KEY_SEED2 = "dsclaw-history-v1";
2792
- function deriveKey2() {
2351
+ function deriveKey3() {
2793
2352
  let user = "";
2794
2353
  try {
2795
- user = userInfo2().username;
2354
+ user = userInfo3().username;
2796
2355
  } catch {
2797
2356
  user = process.env["USER"] ?? process.env["USERNAME"] ?? "default";
2798
2357
  }
2799
- return createHash3("sha256").update(`${KEY_SEED2}:${hostname2()}:${user}`).digest();
2358
+ return createHash4("sha256").update(`${KEY_SEED2}:${hostname3()}:${user}`).digest();
2800
2359
  }
2801
2360
  function encryptStr(data) {
2802
- const key = deriveKey2();
2803
- const iv = randomBytes2(IV_LEN2);
2804
- const cipher = createCipheriv2(ALG2, key, iv, { authTagLength: TAG_LEN2 });
2361
+ const key = deriveKey3();
2362
+ const iv = randomBytes3(IV_LEN3);
2363
+ const cipher = createCipheriv3(ALG3, key, iv, { authTagLength: TAG_LEN3 });
2805
2364
  const ct = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
2806
2365
  const tag = cipher.getAuthTag();
2807
2366
  return Buffer.concat([iv, tag, ct]).toString("base64");
2808
2367
  }
2809
2368
  var cache = /* @__PURE__ */ new Map();
2810
2369
  function ensureDir() {
2811
- if (!existsSync9(HISTORY_DIR)) {
2370
+ if (!existsSync8(HISTORY_DIR)) {
2812
2371
  mkdirSync8(HISTORY_DIR, { recursive: true });
2813
2372
  }
2814
2373
  }
@@ -2821,7 +2380,7 @@ function writeEncryptedFile(fp, messages) {
2821
2380
  const encrypted = encryptStr(json);
2822
2381
  const tmp = fp + ".tmp";
2823
2382
  writeFileSync7(tmp, encrypted);
2824
- renameSync2(tmp, fp);
2383
+ renameSync(tmp, fp);
2825
2384
  }
2826
2385
  function appendHistory(sessionKey, ...messages) {
2827
2386
  const history = cache.get(sessionKey) ?? [];
@@ -3079,7 +2638,6 @@ var DSClawGateway = class {
3079
2638
  webChannel = null;
3080
2639
  memory;
3081
2640
  agents = /* @__PURE__ */ new Map();
3082
- jobStores = /* @__PURE__ */ new Map();
3083
2641
  dsersClients = /* @__PURE__ */ new Map();
3084
2642
  lastUserMessages = /* @__PURE__ */ new Map();
3085
2643
  running = false;
@@ -3306,6 +2864,8 @@ var DSClawGateway = class {
3306
2864
  session.dspiSessionState = void 0;
3307
2865
  session.state = "new";
3308
2866
  saveSession(userId, session);
2867
+ const agent = this.agents.get(userId);
2868
+ if (agent) await agent.destroy();
3309
2869
  this.agents.delete(userId);
3310
2870
  this.pushSettingsState(channel, userId, session);
3311
2871
  break;
@@ -3558,8 +3118,9 @@ var DSClawGateway = class {
3558
3118
  return;
3559
3119
  }
3560
3120
  if (cmd === "/logout") {
3121
+ const agent2 = this.agents.get(userId);
3122
+ if (agent2) await agent2.destroy();
3561
3123
  this.agents.delete(userId);
3562
- this.jobStores.delete(userId);
3563
3124
  this.dsersClients.delete(userId);
3564
3125
  this.lastUserMessages.delete(userId);
3565
3126
  for (const key of sessionHistories.keys()) {
@@ -3599,7 +3160,7 @@ var DSClawGateway = class {
3599
3160
  }
3600
3161
  return;
3601
3162
  }
3602
- const agent = this.getOrCreateAgent(userId, session);
3163
+ const agent = await this.getOrCreateAgent(userId, session);
3603
3164
  const sessionKey = `${channel.name}:${userId}`;
3604
3165
  touchSession(sessionKey);
3605
3166
  const history = sessionHistories.get(sessionKey) ?? [];
@@ -3848,7 +3409,7 @@ var DSClawGateway = class {
3848
3409
  result: "success"
3849
3410
  });
3850
3411
  }
3851
- getOrCreateAgent(userId, session) {
3412
+ async getOrCreateAgent(userId, session) {
3852
3413
  let agent = this.agents.get(userId);
3853
3414
  if (agent) return agent;
3854
3415
  const dsersConfig = createDSersConfig(session.dspiEmail ?? "unknown");
@@ -3866,12 +3427,10 @@ var DSClawGateway = class {
3866
3427
  };
3867
3428
  const dsersSession = session.dspiSessionId ? { sessionId: session.dspiSessionId, state: session.dspiSessionState ?? "" } : void 0;
3868
3429
  const authCb = () => this.triggerAuth(userId);
3869
- let jobStore = this.jobStores.get(userId);
3870
- if (!jobStore) {
3871
- jobStore = new MemoryJobStore2();
3872
- this.jobStores.set(userId, jobStore);
3430
+ agent = new DSClawCoreAgent(dsersClient, this.memory, llm, authCb);
3431
+ if (dsersSession) {
3432
+ await agent.initMCP(dsersSession);
3873
3433
  }
3874
- agent = new DSClawCoreAgent(dsersClient, this.memory, llm, dsersSession, authCb, jobStore);
3875
3434
  this.agents.set(userId, agent);
3876
3435
  if (!agent.mcpAvailable) {
3877
3436
  log16.warn({ userId }, "MCP unavailable for agent \u2014 notifying user");
@@ -3901,7 +3460,7 @@ var DSClawGateway = class {
3901
3460
  };
3902
3461
 
3903
3462
  // src/cli/pid.ts
3904
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, unlinkSync as unlinkSync2, existsSync as existsSync10 } from "fs";
3463
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, unlinkSync as unlinkSync2, existsSync as existsSync9 } from "fs";
3905
3464
  import { join as join10 } from "path";
3906
3465
  var PID_PATH = join10(CONFIG_DIR, "dsclaw.pid");
3907
3466
  function writePid(port) {
@@ -3914,9 +3473,9 @@ function writePid(port) {
3914
3473
  writeFileSync8(PID_PATH, JSON.stringify(info, null, 2), { mode: 384 });
3915
3474
  }
3916
3475
  function readPid() {
3917
- if (!existsSync10(PID_PATH)) return null;
3476
+ if (!existsSync9(PID_PATH)) return null;
3918
3477
  try {
3919
- const raw = readFileSync8(PID_PATH, "utf-8");
3478
+ const raw = readFileSync7(PID_PATH, "utf-8");
3920
3479
  return JSON.parse(raw);
3921
3480
  } catch {
3922
3481
  return null;
@@ -3924,7 +3483,7 @@ function readPid() {
3924
3483
  }
3925
3484
  function removePid() {
3926
3485
  try {
3927
- if (existsSync10(PID_PATH)) unlinkSync2(PID_PATH);
3486
+ if (existsSync9(PID_PATH)) unlinkSync2(PID_PATH);
3928
3487
  } catch {
3929
3488
  }
3930
3489
  }
@@ -3991,7 +3550,6 @@ end tell`;
3991
3550
  }
3992
3551
 
3993
3552
  // src/cli/start.ts
3994
- init_logger();
3995
3553
  var log17 = createLogger("cli:start");
3996
3554
  async function startCommand(opts) {
3997
3555
  try {
@@ -4107,7 +3665,7 @@ function statusCommand() {
4107
3665
  }
4108
3666
 
4109
3667
  // src/cli/reset.ts
4110
- import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
3668
+ import { existsSync as existsSync10, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
4111
3669
  import { join as join11 } from "path";
4112
3670
  function resetCommand(opts) {
4113
3671
  const running = getRunningInstance();
@@ -4119,7 +3677,7 @@ function resetCommand(opts) {
4119
3677
  }
4120
3678
  const sessionsDir = join11(CONFIG_DIR, "sessions");
4121
3679
  let cleared = 0;
4122
- if (existsSync11(sessionsDir)) {
3680
+ if (existsSync10(sessionsDir)) {
4123
3681
  const files = readdirSync3(sessionsDir);
4124
3682
  for (const f of files) {
4125
3683
  try {
@@ -4132,7 +3690,7 @@ function resetCommand(opts) {
4132
3690
  console.log(`
4133
3691
  Cleared ${cleared} session(s).`);
4134
3692
  if (opts.hard) {
4135
- if (existsSync11(CONFIG_PATH)) {
3693
+ if (existsSync10(CONFIG_PATH)) {
4136
3694
  try {
4137
3695
  unlinkSync3(CONFIG_PATH);
4138
3696
  console.log(" Deleted config file.");
@@ -4141,7 +3699,7 @@ function resetCommand(opts) {
4141
3699
  }
4142
3700
  }
4143
3701
  const pidPath = join11(CONFIG_DIR, "dsclaw.pid");
4144
- if (existsSync11(pidPath)) {
3702
+ if (existsSync10(pidPath)) {
4145
3703
  try {
4146
3704
  unlinkSync3(pidPath);
4147
3705
  } catch {
@@ -4217,7 +3775,7 @@ async function doctorCommand() {
4217
3775
 
4218
3776
  // src/cli/index.ts
4219
3777
  var program = new Command();
4220
- program.name("dsclaw").description("AI-powered dropshipping agent \u2014 chat with your DSers store.").version("0.1.7");
3778
+ program.name("dsclaw").description("AI-powered dropshipping agent \u2014 chat with your DSers store.").version("0.1.8");
4221
3779
  program.action(() => startCommand({}));
4222
3780
  program.command("init").description("Setup wizard \u2014 configure Telegram (optional)").action(initCommand);
4223
3781
  program.command("start").description("Start the DSClaw bot").option("-c, --config <path>", "Path to config file").option("--no-open", "Don't auto-open browser").action(startCommand);