@todoforai/edge 0.13.18 → 0.13.19

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.
Files changed (2) hide show
  1. package/dist/index.js +168 -54
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -47977,10 +47977,10 @@ function setConnectionContext(get) {
47977
47977
  function getConnectionEnv() {
47978
47978
  if (!getter)
47979
47979
  return {};
47980
- const { apiUrl, apiKey } = getter();
47981
- if (!apiUrl || !apiKey)
47980
+ const { apiUrl, sessionToken } = getter();
47981
+ if (!apiUrl || !sessionToken)
47982
47982
  return {};
47983
- return { TODOFORAI_API_URL: apiUrl, TODOFORAI_API_TOKEN: apiKey };
47983
+ return { TODOFORAI_API_URL: apiUrl, TODOFORAI_API_TOKEN: sessionToken };
47984
47984
  }
47985
47985
 
47986
47986
  // src/constants.ts
@@ -48033,7 +48033,8 @@ var EF = {
48033
48033
  FRONTEND_FILE_CHUNK_RESULT: "frontend:file_chunk_result"
48034
48034
  };
48035
48035
  var S2E = {
48036
- EDGE_CONFIG_UPDATE: "edge:config_update"
48036
+ EDGE_CONFIG_UPDATE: "edge:config_update",
48037
+ SESSION_TOKEN: "edge:session_token"
48037
48038
  };
48038
48039
  var msg = {
48039
48040
  edgeStatus(edgeId, status) {
@@ -48824,6 +48825,48 @@ var tool_catalog_default = {
48824
48825
  description: "Use to send WhatsApp messages/files/media/locations/polls from CLI. QR-code login via `mudslide login`.",
48825
48826
  versionCmd: "mudslide --version 2>/dev/null | head -1"
48826
48827
  },
48828
+ slack: {
48829
+ category: "development",
48830
+ pkg: "slack-cli",
48831
+ installer: "binary",
48832
+ label: "Slack",
48833
+ statusCmd: "slack-cli auth list 2>&1 | grep -q 'Team' && echo authenticated",
48834
+ loginCmd: "slack-cli login",
48835
+ credentialPaths: [
48836
+ "~/.slack/credentials.json"
48837
+ ],
48838
+ capabilities: "Build & deploy Slack apps, manage workspaces & triggers, run datastore queries, tail logs — official Slack CLI.",
48839
+ description: "Use for Slack platform development: scaffold/run/deploy Slack apps, manage triggers and datastores, manage workspace auth. `slack-cli login` to authenticate. Invoked as `slack-cli` to avoid colliding with the Slack desktop app's `slack` binary.",
48840
+ versionCmd: "slack-cli version 2>/dev/null | head -1",
48841
+ binName: "slack-cli",
48842
+ binary: {
48843
+ "linux-x86_64": {
48844
+ url: "https://github.com/slackapi/slack-cli/releases/download/v4.2.0/slack_cli_4.2.0_linux_amd64.tar.gz",
48845
+ archive: "tar.gz",
48846
+ extract: "bin/slack"
48847
+ },
48848
+ "linux-aarch64": {
48849
+ url: "https://github.com/slackapi/slack-cli/releases/download/v4.2.0/slack_cli_4.2.0_linux_arm64.tar.gz",
48850
+ archive: "tar.gz",
48851
+ extract: "bin/slack"
48852
+ },
48853
+ "darwin-x86_64": {
48854
+ url: "https://github.com/slackapi/slack-cli/releases/download/v4.2.0/slack_cli_4.2.0_macOS_amd64.tar.gz",
48855
+ archive: "tar.gz",
48856
+ extract: "bin/slack"
48857
+ },
48858
+ "darwin-aarch64": {
48859
+ url: "https://github.com/slackapi/slack-cli/releases/download/v4.2.0/slack_cli_4.2.0_macOS_arm64.tar.gz",
48860
+ archive: "tar.gz",
48861
+ extract: "bin/slack"
48862
+ },
48863
+ "windows-x86_64": {
48864
+ url: "https://github.com/slackapi/slack-cli/releases/download/v4.2.0/slack_cli_4.2.0_windows_64-bit.zip",
48865
+ archive: "zip",
48866
+ extract: "bin/slack.exe"
48867
+ }
48868
+ }
48869
+ },
48827
48870
  "telegram-send": {
48828
48871
  category: "messaging",
48829
48872
  pkg: "telegram-send",
@@ -49208,7 +49251,7 @@ var tool_catalog_default = {
49208
49251
  preinstallCloud: true,
49209
49252
  label: "Browser",
49210
49253
  capabilities: "Headless browser automation, web scraping, accessibility tree snapshots, screenshots, PDF generation",
49211
- description: "Headless browser. Use for JS-rendered pages, scraping, screenshots, PDFs, and automated flows. Default browser — prefer over `todoforai-browser` unless the user's own session is needed.",
49254
+ description: "Headless browser. Use for JS-rendered pages, scraping, screenshots, PDFs, and automated flows. Default browser — prefer over `todoforai-browser` unless the user's own session is needed. On a PC with a display, prefer a visible window the user can watch and interact with (needed for CAPTCHA/MFA/login): launch a separate Chrome with `google-chrome --remote-debugging-port=9222 --user-data-dir=$HOME/.config/google-chrome-cdp >/tmp/chrome-cdp.log 2>&1 &` (own data-dir, leaves the user's running Chrome untouched), then attach with `agent-browser --cdp 9222 <command>`. Use headless only on the cloud or when no display is available.",
49212
49255
  versionCmd: "agent-browser --version 2>/dev/null | head -1"
49213
49256
  },
49214
49257
  "todoforai-browser": {
@@ -49252,7 +49295,6 @@ var tool_catalog_default = {
49252
49295
  description: "Explore a codebase with a read-only sub-agent. Streams findings live.",
49253
49296
  versionCmd: "tfa-explore --version 2>/dev/null | head -1",
49254
49297
  installCmd: "bun add -g @todoforai/tfa-explore",
49255
- preinstall: true,
49256
49298
  preinstallCloud: true,
49257
49299
  internal: true
49258
49300
  },
@@ -49265,7 +49307,6 @@ var tool_catalog_default = {
49265
49307
  description: "Review a git diff with a read-only sub-agent.",
49266
49308
  versionCmd: "tfa-review --version 2>/dev/null | head -1",
49267
49309
  installCmd: "bun add -g @todoforai/tfa-review",
49268
- preinstall: true,
49269
49310
  preinstallCloud: true,
49270
49311
  internal: true
49271
49312
  },
@@ -49290,7 +49331,6 @@ var tool_catalog_default = {
49290
49331
  versionCmd: "tfa-vault --version 2>/dev/null | head -1",
49291
49332
  statusCmd: "tfa-vault whoami",
49292
49333
  installCmd: "bun add -g @todoforai/vault",
49293
- preinstall: true,
49294
49334
  preinstallCloud: true
49295
49335
  },
49296
49336
  ripgrep: {
@@ -49582,6 +49622,17 @@ function isToolInstalled(name) {
49582
49622
  }
49583
49623
  return whichWithTools(name) !== null;
49584
49624
  }
49625
+ function findReferencedTools(content) {
49626
+ const stripped = content.replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
49627
+ return Object.keys(TOOL_CATALOG).filter((name) => {
49628
+ const esc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
49629
+ const re = new RegExp(String.raw`(?:^|[|;&\n]|&&|\|\||` + String.raw`\$\(|` + "`" + String.raw`|xargs\s+|sudo\s+|env\s+)\s*` + esc + String.raw`\b(?!-)`, "m");
49630
+ return re.test(stripped);
49631
+ });
49632
+ }
49633
+ function findMissingTools(content) {
49634
+ return findReferencedTools(content).filter((name) => TOOL_CATALOG[name].installer !== "system" && !isToolInstalled(name));
49635
+ }
49585
49636
  async function installBinary(name) {
49586
49637
  const urlFunc = BINARY_URL_FUNCS[name];
49587
49638
  if (!urlFunc) {
@@ -49655,6 +49706,15 @@ function findFileRecursive(dir, names) {
49655
49706
  }
49656
49707
  return null;
49657
49708
  }
49709
+ function getInstallCommand(name) {
49710
+ const e = TOOL_CATALOG[name];
49711
+ return e.installCmd || {
49712
+ npm: `npm install --prefix ~/.todoforai/tools ${e.pkg}`,
49713
+ bun: `bun add --cwd ~/.todoforai/tools ${e.pkg}`,
49714
+ pip: `pip install ${e.pkg}`,
49715
+ binary: `download ${e.pkg}`
49716
+ }[e.installer] || `install ${e.pkg}`;
49717
+ }
49658
49718
  function installWithNpm(name, pkg) {
49659
49719
  const TIMEOUT_MS = 120000;
49660
49720
  log2("info", `Installing ${name} via npm (${pkg})`);
@@ -49756,11 +49816,11 @@ function installWithPip(name, pkg) {
49756
49816
  log2("info", `Installing ${name} via pip (${pkg})`);
49757
49817
  const args = useVenv ? ["-m", "pip", "install", pkg] : ["-m", "pip", "install", "--user", pkg];
49758
49818
  const result = spawnSync(python, args, { stdio: "pipe", timeout: 120000 });
49759
- if (result.signal) {
49760
- log2("error", `Failed to install ${name}: killed by ${result.signal}${result.signal === "SIGTERM" ? " (likely timed out after 120s)" : ""}`);
49761
- } else if (result.status !== 0) {
49762
- log2("error", `Failed to install ${name}: ${result.stderr?.toString() || result.stdout?.toString()}`);
49763
- }
49819
+ const stderr = result.stderr?.toString().trim() || "";
49820
+ if (result.signal)
49821
+ throw new Error(`pip install killed by ${result.signal}${result.signal === "SIGTERM" ? " (likely timed out after 120s)" : ""}`);
49822
+ if (result.status !== 0)
49823
+ throw new Error(`pip install failed (exit ${result.status}): ${stderr || result.stdout?.toString().trim() || "(empty)"}`);
49764
49824
  }
49765
49825
  var INSTALLERS = {
49766
49826
  npm: installWithNpm,
@@ -49829,6 +49889,17 @@ function uninstallTool(name) {
49829
49889
  return false;
49830
49890
  }
49831
49891
  }
49892
+ async function autoInstallMissingTools(content) {
49893
+ const lines = [];
49894
+ for (const name of findMissingTools(content)) {
49895
+ const ok = await ensureTool(name) && isToolInstalled(name);
49896
+ lines.push(`$ ${getInstallCommand(name)}
49897
+ [${ok ? "installed" : "install failed"}: ${name}]`);
49898
+ }
49899
+ return lines.length ? lines.join(`
49900
+ `) + `
49901
+ ` : "";
49902
+ }
49832
49903
  function execShellAsync(cmd, env, timeout) {
49833
49904
  return new Promise((resolve) => {
49834
49905
  execFile("sh", ["-c", cmd], { env, timeout, encoding: "utf-8", maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
@@ -51531,6 +51602,48 @@ async function readFdTarget(pid, fd3) {
51531
51602
  }
51532
51603
  var pauseDetector = new PtyPauseDetector;
51533
51604
 
51605
+ // ../../packages/shared-fbe/src/outputLimits.ts
51606
+ var MAX_RESULT_LINES = 100;
51607
+ var MAX_LINE_LEN = 300;
51608
+ var MAX_TOTAL_LEN = 1e5;
51609
+ var STREAM_FIRST = 1e4;
51610
+ var STREAM_LAST = 1e4;
51611
+ var RUN_OUTPUT_CAP = 256 * 1024;
51612
+ var OUTPUT_POLICIES = {
51613
+ safe: { firstLimit: STREAM_FIRST, lastLimit: STREAM_LAST, hardCap: STREAM_FIRST + STREAM_LAST },
51614
+ full: { firstLimit: Infinity, lastLimit: 0, hardCap: RUN_OUTPUT_CAP },
51615
+ raw: { firstLimit: Infinity, lastLimit: 0, hardCap: Infinity }
51616
+ };
51617
+ var DEFAULT_OUTPUT_MODE = "safe";
51618
+ function resolveOutputPolicy(mode) {
51619
+ return OUTPUT_POLICIES[mode] ?? OUTPUT_POLICIES[DEFAULT_OUTPUT_MODE];
51620
+ }
51621
+ function truncateLines(text, { maxLines = MAX_RESULT_LINES, maxLineLen = MAX_LINE_LEN, maxTotalLen = MAX_TOTAL_LEN } = {}, unit = "matches") {
51622
+ let lines = text.split(`
51623
+ `).filter((l) => l.trim());
51624
+ const overflow = lines.length - maxLines;
51625
+ if (overflow > 0)
51626
+ lines = lines.slice(0, maxLines);
51627
+ lines = lines.map((line) => line.length > maxLineLen ? line.slice(0, maxLineLen) + ` ...[+${line.length - maxLineLen} chars]` : line);
51628
+ let output = lines.join(`
51629
+ `);
51630
+ if (overflow > 0)
51631
+ output += `
51632
+ ... (${overflow} more ${unit} truncated)`;
51633
+ if (output.length > maxTotalLen)
51634
+ output = output.slice(0, maxTotalLen) + `
51635
+ ... (output truncated)`;
51636
+ return output;
51637
+ }
51638
+ function formatTruncationNotice(totalLen, firstLimit, lastPart) {
51639
+ const dropped = totalLen - firstLimit - lastPart.length;
51640
+ return `
51641
+
51642
+ ... [truncated ${dropped} chars] ...
51643
+
51644
+ ${lastPart}`;
51645
+ }
51646
+
51534
51647
  // src/shell.ts
51535
51648
  var IS_WIN = os6.platform() === "win32";
51536
51649
  var HAS_BUN = typeof globalThis.Bun !== "undefined";
@@ -51550,6 +51663,14 @@ function whichSync(name) {
51550
51663
  }
51551
51664
  return null;
51552
51665
  }
51666
+ function isAccessibleDir(p10) {
51667
+ try {
51668
+ fs8.accessSync(p10, fs8.constants.X_OK);
51669
+ return fs8.statSync(p10).isDirectory();
51670
+ } catch {
51671
+ return false;
51672
+ }
51673
+ }
51553
51674
  function getShellCommand(content) {
51554
51675
  if (!IS_WIN)
51555
51676
  return { shell: "/bin/bash", args: ["-c", content] };
@@ -51567,26 +51688,24 @@ function getShellCommand(content) {
51567
51688
  }
51568
51689
  return { shell: "cmd.exe", args: ["/c", "chcp 65001>nul && " + content] };
51569
51690
  }
51570
- var STREAM_FIRST = 1e4;
51571
- var STREAM_LAST = 1e4;
51572
51691
 
51573
51692
  class OutputBuffer {
51574
- firstLimit;
51575
- lastLimit;
51576
51693
  firstPart = "";
51577
51694
  lastPart = "";
51578
51695
  totalLen = 0;
51579
51696
  truncated = false;
51580
51697
  truncMsgSent = false;
51581
- constructor(firstLimit = STREAM_FIRST, lastLimit = STREAM_LAST) {
51582
- this.firstLimit = firstLimit;
51583
- this.lastLimit = lastLimit;
51698
+ headLimit;
51699
+ lastLimit;
51700
+ constructor(policy = OUTPUT_POLICIES[DEFAULT_OUTPUT_MODE]) {
51701
+ this.headLimit = Math.min(policy.firstLimit, policy.hardCap);
51702
+ this.lastLimit = Math.min(policy.lastLimit, policy.hardCap - this.headLimit);
51584
51703
  }
51585
51704
  append(text) {
51586
51705
  this.totalLen += text.length;
51587
51706
  let toStream = "";
51588
- if (this.firstPart.length < this.firstLimit) {
51589
- const remaining = this.firstLimit - this.firstPart.length;
51707
+ if (this.firstPart.length < this.headLimit) {
51708
+ const remaining = this.headLimit - this.firstPart.length;
51590
51709
  toStream = text.slice(0, remaining);
51591
51710
  this.firstPart += toStream;
51592
51711
  text = text.slice(remaining);
@@ -51594,19 +51713,14 @@ class OutputBuffer {
51594
51713
  if (text) {
51595
51714
  if (!this.truncated)
51596
51715
  this.truncated = true;
51597
- this.lastPart = (this.lastPart + text).slice(-this.lastLimit);
51716
+ this.lastPart = this.lastLimit > 0 ? (this.lastPart + text).slice(-this.lastLimit) : "";
51598
51717
  }
51599
51718
  return toStream;
51600
51719
  }
51601
51720
  getTruncationNotice() {
51602
51721
  if (this.truncated && !this.truncMsgSent) {
51603
51722
  this.truncMsgSent = true;
51604
- const dropped = this.totalLen - this.firstLimit - this.lastPart.length;
51605
- return `
51606
-
51607
- ... [truncated ${dropped} chars] ...
51608
-
51609
- ${this.lastPart}`;
51723
+ return formatTruncationNotice(this.totalLen, this.firstPart.length, this.lastPart);
51610
51724
  }
51611
51725
  return "";
51612
51726
  }
@@ -51634,12 +51748,12 @@ var processes = new Map;
51634
51748
  var outputBuffers = new Map;
51635
51749
  var completionResolvers = new Map;
51636
51750
  var exitedOutputByPid = new Map;
51637
- async function executeBlock(blockId, content, send, todoId, messageId, timeout, cwd, manual = false, runMode, edgeId, agentSettingsId = "", keepAliveOnTimeout = false) {
51751
+ async function executeBlock(blockId, content, send, todoId, messageId, timeout, cwd, manual = false, runMode, edgeId, agentSettingsId = "", keepAliveOnTimeout = false, outputMode = DEFAULT_OUTPUT_MODE) {
51638
51752
  if (processes.has(blockId)) {
51639
51753
  console.log(`[shell] killing existing process for blockId=${blockId}`);
51640
51754
  interruptBlock(blockId);
51641
51755
  }
51642
- const buf = new OutputBuffer;
51756
+ const buf = new OutputBuffer(resolveOutputPolicy(outputMode));
51643
51757
  outputBuffers.set(blockId, buf);
51644
51758
  try {
51645
51759
  const tmpDir = path5.join(os6.tmpdir(), "todoforai");
@@ -51647,10 +51761,16 @@ async function executeBlock(blockId, content, send, todoId, messageId, timeout,
51647
51761
  fs8.mkdirSync(tmpDir, { recursive: true });
51648
51762
  if (cwd) {
51649
51763
  const expanded = cwd.replace(/^~/, process.env.HOME || "~");
51650
- cwd = fs8.existsSync(expanded) && fs8.statSync(expanded).isDirectory() ? expanded : tmpDir;
51764
+ cwd = isAccessibleDir(expanded) ? expanded : tmpDir;
51651
51765
  } else {
51652
51766
  cwd = tmpDir;
51653
51767
  }
51768
+ const installNotice = await autoInstallMissingTools(content);
51769
+ if (installNotice) {
51770
+ const toStream = buf.append(installNotice);
51771
+ if (toStream)
51772
+ await send(msg.shellBlockResult(todoId, blockId, toStream, messageId));
51773
+ }
51654
51774
  await send({
51655
51775
  type: "BLOCK_UPDATE",
51656
51776
  payload: { todoId, blockId, messageId, updates: { status: "RUNNING" } }
@@ -51815,7 +51935,7 @@ async function executeBlock(blockId, content, send, todoId, messageId, timeout,
51815
51935
  spawnWithPipes();
51816
51936
  }
51817
51937
  } catch (e) {
51818
- await send(msg.shellBlockResult(todoId, blockId, `Error creating process: ${e.message}`, messageId));
51938
+ await send(msg.shellBlockResult(todoId, blockId, `Error creating process: ${e.message} (cwd: ${cwd})`, messageId));
51819
51939
  const resolver = completionResolvers.get(blockId);
51820
51940
  if (resolver) {
51821
51941
  resolver();
@@ -52392,7 +52512,7 @@ function detectContentType(output, cmd) {
52392
52512
  return { result: output };
52393
52513
  }
52394
52514
  register("execute_shell_command", async (args, client) => {
52395
- const { cmd, cwd = args.root_path ?? "", todoId = "", messageId = "", blockId = "", agentSettingsId = "", pid: resumePid = 0 } = args;
52515
+ const { cmd, cwd = args.root_path ?? "", todoId = "", messageId = "", blockId = "", agentSettingsId = "", pid: resumePid = 0, output: outputMode = DEFAULT_OUTPUT_MODE } = args;
52396
52516
  const timeout = Math.max(args.timeout ?? 120, client?.maxTimeout ?? 0);
52397
52517
  const canStream = !!(todoId && blockId && client);
52398
52518
  if (!canStream) {
@@ -52433,7 +52553,7 @@ register("execute_shell_command", async (args, client) => {
52433
52553
  }
52434
52554
  try {
52435
52555
  await send(msg.shellBlockStart(todoId, blockId, "execute", messageId));
52436
- await executeBlock(blockId, execCmd, send, todoId, messageId, timeout, cwd, false, "internal", undefined, agentSettingsId, true);
52556
+ await executeBlock(blockId, execCmd, send, todoId, messageId, timeout, cwd, false, "internal", undefined, agentSettingsId, true, outputMode);
52437
52557
  await waitForCompletion(blockId, (timeout + 5) * 1000);
52438
52558
  const rawOutput = getBlockRawOutput(blockId);
52439
52559
  let output = rawOutput ?? getBlockOutput(blockId);
@@ -52563,17 +52683,10 @@ register("search_files", async (args) => {
52563
52683
  });
52564
52684
  if (code === 0) {
52565
52685
  let output = stdout;
52566
- const lines = output.split(`
52567
- `).filter((l) => l.trim());
52568
- if (lines.length > head) {
52569
- output = lines.slice(0, head).join(`
52570
- `) + `
52571
- ... (${lines.length - head} more matches truncated)`;
52572
- }
52573
52686
  if ((cwd || searchPath) && output) {
52574
52687
  const searchBase = searchPath && fs11.existsSync(searchPath) && fs11.statSync(searchPath).isDirectory() ? searchPath : path8.dirname(searchPath);
52575
52688
  const bases = Array.from(new Set([cwd, searchBase].filter(Boolean)));
52576
- const lines2 = output.split(`
52689
+ const lines = output.split(`
52577
52690
  `).map((line) => {
52578
52691
  if (line.includes(":")) {
52579
52692
  const colonIdx = line.indexOf(":");
@@ -52583,21 +52696,14 @@ register("search_files", async (args) => {
52583
52696
  const candidates = [filePart, ...bases.map((b) => path8.relative(b, filePart))].filter((p11) => (p11.match(/\.\.\//g) || []).length <= 2);
52584
52697
  filePart = candidates.reduce((a, b) => a.length <= b.length ? a : b, filePart);
52585
52698
  } catch {}
52586
- let fullLine = filePart + rest;
52587
- if (fullLine.length > 300) {
52588
- fullLine = fullLine.slice(0, 300) + "...";
52589
- }
52590
- return fullLine;
52699
+ return filePart + rest;
52591
52700
  }
52592
52701
  return line;
52593
52702
  });
52594
- output = lines2.join(`
52703
+ output = lines.join(`
52595
52704
  `);
52596
52705
  }
52597
- if (output.length > 1e5)
52598
- output = output.slice(0, 1e5) + `
52599
- ... (output truncated)`;
52600
- return { result: output };
52706
+ return { result: truncateLines(output, { maxLines: head }) };
52601
52707
  }
52602
52708
  if (code === 1)
52603
52709
  return { result: "No matches found." };
@@ -52889,6 +52995,7 @@ class TODOforAIEdge {
52889
52995
  connected = false;
52890
52996
  edgeId = "";
52891
52997
  userId = "";
52998
+ sessionToken = "";
52892
52999
  debug;
52893
53000
  maxTimeout;
52894
53001
  wsUrl;
@@ -52914,7 +53021,7 @@ class TODOforAIEdge {
52914
53021
  this.wsUrl = getWsUrl(this.api.apiUrl);
52915
53022
  this.addWorkspacePath = config.addWorkspacePath;
52916
53023
  this.browserExtensionBridge = new BrowserExtensionBridge(this.debug);
52917
- setConnectionContext(() => ({ apiUrl: this.api.apiUrl, apiKey: this.api.apiKey }));
53024
+ setConnectionContext(() => ({ apiUrl: this.api.apiUrl, sessionToken: this.sessionToken }));
52918
53025
  }
52919
53026
  get apiUrl() {
52920
53027
  return this.api.apiUrl;
@@ -53124,6 +53231,13 @@ class TODOforAIEdge {
53124
53231
  case S2E.EDGE_CONFIG_UPDATE:
53125
53232
  run(async () => this.handleEdgeConfigUpdate(payload));
53126
53233
  break;
53234
+ case S2E.SESSION_TOKEN:
53235
+ if (typeof payload.token === "string" && payload.token.startsWith("dst_")) {
53236
+ this.sessionToken = payload.token;
53237
+ if (this.debug)
53238
+ console.log(`[recv] session token (expires in ${payload.expiresIn}s)`);
53239
+ }
53240
+ break;
53127
53241
  case FE.EDGE_CD:
53128
53242
  run(() => handleCd(payload, send, this.edgeConfig, (u) => this.updateConfig(u)));
53129
53243
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/edge",
3
- "version": "0.13.18",
3
+ "version": "0.13.19",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-edge": "dist/index.js"