dsclaw 0.1.0 → 0.1.1

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
@@ -50,7 +50,7 @@ function createLogger(module) {
50
50
 
51
51
  // src/gateway/config.ts
52
52
  var log = createLogger("config");
53
- var CONFIG_DIR = join(homedir(), ".dropclaw");
53
+ var CONFIG_DIR = join(homedir(), ".dsclaw");
54
54
  var CONFIG_PATH = join(CONFIG_DIR, "config.json");
55
55
  var ConfigSchema = z.object({
56
56
  port: z.number().default(3e3),
@@ -89,7 +89,7 @@ function configExists() {
89
89
 
90
90
  // src/cli/init.ts
91
91
  async function initCommand() {
92
- console.log("\n DropClaw Setup (optional)\n");
92
+ console.log("\n DSClaw Setup (optional)\n");
93
93
  console.log(" Web chat works out of the box \u2014 this is for extra settings.\n");
94
94
  if (configExists()) {
95
95
  const { overwrite } = await inquirer.prompt([
@@ -131,7 +131,7 @@ async function initCommand() {
131
131
  saveConfig(config);
132
132
  console.log(`
133
133
  Config saved to ${CONFIG_PATH}`);
134
- console.log(" Run 'dropclaw start' to launch.\n");
134
+ console.log(" Run 'dsclaw start' to launch.\n");
135
135
  }
136
136
 
137
137
  // src/gateway/user-session.ts
@@ -154,7 +154,7 @@ var log2 = createLogger("user-session");
154
154
  var ALG = "aes-256-gcm";
155
155
  var IV_LEN = 12;
156
156
  var TAG_LEN = 16;
157
- var KEY_SEED = "dropclaw-v1";
157
+ var KEY_SEED = "dsclaw-v1";
158
158
  var SESSIONS_DIR = join2(CONFIG_DIR, "sessions");
159
159
  function deriveKey() {
160
160
  let user = "";
@@ -239,14 +239,14 @@ var ErrorCategory = {
239
239
  FATAL: "FATAL",
240
240
  DEGRADED: "DEGRADED"
241
241
  };
242
- var DropClawError = class extends Error {
242
+ var DSClawError = class extends Error {
243
243
  category;
244
244
  code;
245
245
  retryable;
246
246
  details;
247
247
  constructor(opts) {
248
248
  super(opts.message);
249
- this.name = "DropClawError";
249
+ this.name = "DSClawError";
250
250
  this.category = opts.category;
251
251
  this.code = opts.code;
252
252
  this.retryable = opts.retryable;
@@ -301,7 +301,7 @@ var DSersAuth = class {
301
301
  }
302
302
  async login() {
303
303
  if (!this.config.email || !this.config.password) {
304
- throw new DropClawError({
304
+ throw new DSClawError({
305
305
  category: ErrorCategory.NEEDS_HUMAN,
306
306
  code: "DSERS_NO_CREDENTIALS",
307
307
  message: "DSers email and password not configured.",
@@ -322,7 +322,7 @@ var DSersAuth = class {
322
322
  );
323
323
  if (!resp.ok) {
324
324
  const body = await resp.text();
325
- throw new DropClawError({
325
+ throw new DSClawError({
326
326
  category: resp.status === 401 || resp.status === 403 ? ErrorCategory.NEEDS_HUMAN : ErrorCategory.RETRYABLE,
327
327
  code: `DSERS_LOGIN_${resp.status}`,
328
328
  message: `DSers login failed (HTTP ${resp.status}): ${body.slice(0, 200)}`,
@@ -332,7 +332,7 @@ var DSersAuth = class {
332
332
  const data = await resp.json();
333
333
  const inner = data["data"];
334
334
  if (!inner?.["sessionId"]) {
335
- throw new DropClawError({
335
+ throw new DSClawError({
336
336
  category: ErrorCategory.NEEDS_HUMAN,
337
337
  code: "DSERS_LOGIN_NO_SESSION",
338
338
  message: "DSers login succeeded but no session returned.",
@@ -528,7 +528,7 @@ var DSersClient = class {
528
528
  }
529
529
  if (resp.status >= 400) {
530
530
  const category = classifyHttpError(resp.status, bodyText);
531
- throw new DropClawError({
531
+ throw new DSClawError({
532
532
  category,
533
533
  code: `DSERS_API_${resp.status}`,
534
534
  message: `DSers API ${method} ${path} returned ${resp.status}: ${bodyText.slice(0, 300)}`,
@@ -659,7 +659,7 @@ function getDefaultModel(provider) {
659
659
  return "gpt-4o";
660
660
  }
661
661
  }
662
- var SYSTEM_PROMPT = `You are DropClaw, a friendly AI assistant that manages dropshipping stores through DSers.
662
+ var SYSTEM_PROMPT = `You are DSClaw, a friendly AI assistant that manages dropshipping stores through DSers.
663
663
  Your user has ZERO technical background \u2014 they are a small business owner or solo entrepreneur.
664
664
 
665
665
  COMMUNICATION RULES:
@@ -685,9 +685,9 @@ GUIDELINES:
685
685
  - For bulk operations, show a preview and ask for confirmation
686
686
  - Use simple summaries: "You have 15 products waiting to be pushed" instead of raw JSON
687
687
  - If the user seems confused, offer guided suggestions like "Would you like me to show your stores first?"`;
688
- var DropClawCoreAgent = class {
689
- id = "dropclaw-core";
690
- name = "DropClaw Core Agent";
688
+ var DSClawCoreAgent = class {
689
+ id = "dsclaw-core";
690
+ name = "DSClaw Core Agent";
691
691
  dsers;
692
692
  memory;
693
693
  llm;
@@ -999,6 +999,7 @@ var WebChatServer = class {
999
999
  server = null;
1000
1000
  wss = null;
1001
1001
  handler = null;
1002
+ connectHandler = null;
1002
1003
  connections = /* @__PURE__ */ new Map();
1003
1004
  _connected = false;
1004
1005
  port = 3e3;
@@ -1032,6 +1033,13 @@ var WebChatServer = class {
1032
1033
  this.wsSend(ws, { type: "session", userId: clientId });
1033
1034
  this.connections.set(clientId, ws);
1034
1035
  log7.info({ userId: clientId }, "Web client connected");
1036
+ if (this.connectHandler) {
1037
+ try {
1038
+ this.connectHandler(clientId);
1039
+ } catch (err) {
1040
+ log7.error({ error: err, userId: clientId }, "Connect handler failed");
1041
+ }
1042
+ }
1035
1043
  ws.on("message", async (raw) => {
1036
1044
  try {
1037
1045
  const msg = JSON.parse(raw.toString());
@@ -1094,6 +1102,9 @@ var WebChatServer = class {
1094
1102
  onMessage(handler) {
1095
1103
  this.handler = handler;
1096
1104
  }
1105
+ onConnect(handler) {
1106
+ this.connectHandler = handler;
1107
+ }
1097
1108
  async send(targetUserId, message) {
1098
1109
  if (message.card) {
1099
1110
  await this.sendCard(targetUserId, message.card);
@@ -1406,7 +1417,7 @@ function degradationMessage(service) {
1406
1417
  var log11 = createLogger("gateway");
1407
1418
  var sessionHistories = /* @__PURE__ */ new Map();
1408
1419
  var MAX_HISTORY = 20;
1409
- var DropClawGateway = class {
1420
+ var DSClawGateway = class {
1410
1421
  config;
1411
1422
  channels = [];
1412
1423
  webChannel = null;
@@ -1419,9 +1430,10 @@ var DropClawGateway = class {
1419
1430
  this.memory = new FileMemoryProvider();
1420
1431
  }
1421
1432
  async start() {
1422
- log11.info("Starting DropClaw Gateway...");
1433
+ log11.info("Starting DSClaw Gateway...");
1423
1434
  const web = new WebChatServer();
1424
1435
  web.onMessage((msg) => this.handleMessage(web, msg));
1436
+ web.onConnect((userId) => this.handleWebConnect(web, userId));
1425
1437
  await web.connect({ port: this.config.port });
1426
1438
  this.webChannel = web;
1427
1439
  this.channels.push(web);
@@ -1438,7 +1450,7 @@ var DropClawGateway = class {
1438
1450
  }
1439
1451
  }
1440
1452
  this.running = true;
1441
- log11.info("DropClaw Gateway started");
1453
+ log11.info("DSClaw Gateway started");
1442
1454
  }
1443
1455
  async handleMessage(channel, message) {
1444
1456
  await runWithTrace(
@@ -1478,6 +1490,15 @@ var DropClawGateway = class {
1478
1490
  }
1479
1491
  );
1480
1492
  }
1493
+ // ─── Auto-welcome on WebSocket connect ──────────────────────────
1494
+ handleWebConnect(channel, userId) {
1495
+ const session = loadSession(userId);
1496
+ if (session.state === "new") {
1497
+ this.onboardWelcome(channel, userId, session).catch((err) => {
1498
+ log11.error({ error: err, userId }, "Auto-welcome failed");
1499
+ });
1500
+ }
1501
+ }
1481
1502
  // ─── Onboarding State Machine ─────────────────────────────────────
1482
1503
  async handleOnboarding(channel, message, session) {
1483
1504
  const userId = message.userId;
@@ -1501,7 +1522,7 @@ var DropClawGateway = class {
1501
1522
  }
1502
1523
  }
1503
1524
  async onboardWelcome(channel, userId, session) {
1504
- const welcome = "Welcome to *DropClaw*! I'm your AI assistant for dropshipping.\n\nI can help you:\n- Import products from AliExpress, Temu, 1688\n- Push products to your Shopify/WooCommerce store\n- Check inventory, pricing rules, and orders\n- ...all through this chat!\n\nLet's get you set up. It takes about 1 minute.\n\n*Step 1/3:* What is your DSers account email?";
1525
+ const welcome = "Welcome to *DSClaw*! I'm your AI assistant for dropshipping.\n\nI can help you:\n- Import products from AliExpress, Temu, 1688\n- Push products to your Shopify/WooCommerce store\n- Check inventory, pricing rules, and orders\n- ...all through this chat!\n\nLet's get you set up. It takes about 1 minute.\n\n*Step 1/3:* What is your DSers account email?";
1505
1526
  await channel.send(userId, { text: welcome });
1506
1527
  session.state = "onboard_dsers_email";
1507
1528
  saveSession(userId, session);
@@ -1553,11 +1574,11 @@ var DropClawGateway = class {
1553
1574
  await channel.send(userId, {
1554
1575
  card: {
1555
1576
  title: "DSers Connected!",
1556
- summary: "Your DSers account is verified.\n\n*Step 3/3:* Choose your AI provider.\nThis powers my intelligence. You can get a free API key from any provider.",
1577
+ summary: "Your DSers account is verified.\n\n*Step 3/3:* Choose your AI provider and paste your API key.\nYou only need a key \u2014 DSClaw handles everything else.\n\nGet a free key here:\n- **OpenAI**: [platform.openai.com/api-keys](https://platform.openai.com/api-keys)\n- **Anthropic**: [console.anthropic.com/settings/keys](https://console.anthropic.com/settings/keys)\n- **Google** (recommended, free tier): [aistudio.google.com/apikey](https://aistudio.google.com/apikey)",
1557
1578
  actions: [
1558
1579
  { label: "OpenAI (GPT-4o)", callbackData: "llm:openai" },
1559
1580
  { label: "Anthropic (Claude)", callbackData: "llm:anthropic" },
1560
- { label: "Google (Gemini)", callbackData: "llm:google" }
1581
+ { label: "Google (Gemini) \u2605", callbackData: "llm:google" }
1561
1582
  ]
1562
1583
  }
1563
1584
  });
@@ -1588,11 +1609,11 @@ var DropClawGateway = class {
1588
1609
  await channel.send(userId, {
1589
1610
  card: {
1590
1611
  title: "Choose AI Provider",
1591
- summary: "Please select your AI provider first:",
1612
+ summary: "Please select your AI provider first.\n\nGet a free key:\n- **OpenAI**: [platform.openai.com/api-keys](https://platform.openai.com/api-keys)\n- **Anthropic**: [console.anthropic.com/settings/keys](https://console.anthropic.com/settings/keys)\n- **Google** (recommended): [aistudio.google.com/apikey](https://aistudio.google.com/apikey)",
1592
1613
  actions: [
1593
1614
  { label: "OpenAI (GPT-4o)", callbackData: "llm:openai" },
1594
1615
  { label: "Anthropic (Claude)", callbackData: "llm:anthropic" },
1595
- { label: "Google (Gemini)", callbackData: "llm:google" }
1616
+ { label: "Google (Gemini) \u2605", callbackData: "llm:google" }
1596
1617
  ]
1597
1618
  }
1598
1619
  });
@@ -1617,7 +1638,7 @@ var DropClawGateway = class {
1617
1638
  session.state = "ready";
1618
1639
  saveSession(userId, session);
1619
1640
  await channel.send(userId, {
1620
- text: `All set! DropClaw is ready.
1641
+ text: `All set! DSClaw is ready.
1621
1642
 
1622
1643
  Try these:
1623
1644
  - "Show me my stores"
@@ -1751,17 +1772,17 @@ Just type naturally \u2014 I understand everyday language!`
1751
1772
  apiKey: session.llmApiKey,
1752
1773
  model: session.llmModel ?? getDefaultModel(session.llmProvider)
1753
1774
  };
1754
- agent = new DropClawCoreAgent(dsersClient, this.memory, llm);
1775
+ agent = new DSClawCoreAgent(dsersClient, this.memory, llm);
1755
1776
  this.agents.set(userId, agent);
1756
1777
  return agent;
1757
1778
  }
1758
1779
  async stop() {
1759
- log11.info("Stopping DropClaw Gateway...");
1780
+ log11.info("Stopping DSClaw Gateway...");
1760
1781
  for (const ch of this.channels) {
1761
1782
  await ch.disconnect();
1762
1783
  }
1763
1784
  this.running = false;
1764
- log11.info("DropClaw Gateway stopped");
1785
+ log11.info("DSClaw Gateway stopped");
1765
1786
  }
1766
1787
  isRunning() {
1767
1788
  return this.running;
@@ -1780,18 +1801,18 @@ async function startCommand(opts) {
1780
1801
  try {
1781
1802
  const config = loadConfig(opts.config);
1782
1803
  const url = `http://localhost:${config.port}`;
1783
- console.log("\n DropClaw");
1804
+ console.log("\n DSClaw");
1784
1805
  console.log(` Web Chat: ${url}`);
1785
1806
  if (config.telegramBotToken) {
1786
1807
  console.log(" Telegram: enabled");
1787
1808
  }
1788
1809
  console.log();
1789
- const gateway = new DropClawGateway(config);
1810
+ const gateway = new DSClawGateway(config);
1790
1811
  await gateway.start();
1791
1812
  if (opts.open !== false) {
1792
1813
  await openBrowser(url);
1793
1814
  }
1794
- console.log(` DropClaw is running. Chat at ${url}
1815
+ console.log(` DSClaw is running \u2014 chat at ${url}
1795
1816
  `);
1796
1817
  const shutdown = async () => {
1797
1818
  log12.info("Shutting down gracefully...");
@@ -1812,7 +1833,7 @@ async function startCommand(opts) {
1812
1833
 
1813
1834
  // src/cli/doctor.ts
1814
1835
  async function doctorCommand() {
1815
- console.log("\n DropClaw Doctor\n");
1836
+ console.log("\n DSClaw Doctor\n");
1816
1837
  const results = [];
1817
1838
  if (configExists()) {
1818
1839
  try {
@@ -1842,7 +1863,7 @@ async function doctorCommand() {
1842
1863
  results.push({
1843
1864
  name: "Config file",
1844
1865
  status: "fail",
1845
- message: "Not found. Run 'dropclaw init' first."
1866
+ message: "Not found. Run 'dsclaw init' first."
1846
1867
  });
1847
1868
  }
1848
1869
  try {
@@ -1879,8 +1900,8 @@ var program = new Command();
1879
1900
  program.name("dsclaw").description("AI-powered dropshipping agent \u2014 chat with your DSers store.").version("0.1.0");
1880
1901
  program.action(() => startCommand({}));
1881
1902
  program.command("init").description("Setup wizard \u2014 configure Telegram (optional)").action(initCommand);
1882
- program.command("start").description("Start the DropClaw bot").option("-c, --config <path>", "Path to config file").option("--no-open", "Don't auto-open browser").action(startCommand);
1883
- program.command("stop").description("Stop the DropClaw bot (PM2)").action(async () => {
1903
+ 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);
1904
+ program.command("stop").description("Stop the DSClaw bot (PM2)").action(async () => {
1884
1905
  const { execSync } = await import("child_process");
1885
1906
  try {
1886
1907
  execSync("pm2 stop dsclaw", { stdio: "inherit" });