@questionbase/deskfree 0.4.3 → 0.4.4

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.d.ts CHANGED
@@ -26,6 +26,8 @@ interface OrchestratorQueryOptions {
26
26
  model: string;
27
27
  /** Agent SDK session ID to resume (for multi-turn conversations). */
28
28
  sessionId?: string;
29
+ /** Path to user's Claude Code executable (uses bundled CLI if omitted). */
30
+ claudeCodePath?: string;
29
31
  }
30
32
  /**
31
33
  * Run the orchestrator agent. Returns an async iterable of SDKMessage events.
@@ -40,6 +42,8 @@ interface HeartbeatOptions {
40
42
  prompt: string;
41
43
  orchestratorServer: McpSdkServerConfigWithInstance;
42
44
  model: string;
45
+ /** Path to user's Claude Code executable (uses bundled CLI if omitted). */
46
+ claudeCodePath?: string;
43
47
  }
44
48
  /**
45
49
  * Run a one-shot heartbeat query (no session persistence).
@@ -390,6 +394,8 @@ interface RuntimeConfig extends LocalConfig {
390
394
  duskHour: number | null;
391
395
  /** IANA timezone string (e.g. 'America/New_York') for sleep scheduling */
392
396
  timezone: string | null;
397
+ /** Absolute path to the user's Claude Code executable (resolved for 'claude-code' provider) */
398
+ claudeCodePath: string | undefined;
393
399
  }
394
400
  /**
395
401
  * Load local config from environment variables.
@@ -401,6 +407,10 @@ declare function loadConfig(): LocalConfig;
401
407
  /**
402
408
  * Merge local config with API-bootstrapped config.
403
409
  * Env var overrides take precedence over API values for debugging/testing.
410
+ *
411
+ * When running multiple agents on the same machine, stateDir and toolsDir
412
+ * are automatically namespaced by botId (unless explicitly overridden via
413
+ * DESKFREE_STATE_DIR / DESKFREE_TOOLS_DIR env vars).
404
414
  */
405
415
  declare function mergeWithRemoteConfig(local: LocalConfig, remote: RuntimeBootstrapConfig): RuntimeConfig;
406
416
 
package/dist/index.js CHANGED
@@ -3,9 +3,9 @@ import { query, tool, createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'
3
3
  import { createRequire as createRequire$1 } from 'module';
4
4
  import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, unlinkSync, createWriteStream } from 'fs';
5
5
  import { join, dirname, extname } from 'path';
6
+ import { execFileSync, execFile } from 'child_process';
6
7
  import { z } from 'zod';
7
8
  import { appendFile, readFile, mkdir, unlink } from 'fs/promises';
8
- import { execFile } from 'child_process';
9
9
  import { randomUUID } from 'crypto';
10
10
  import { Readable } from 'stream';
11
11
  import { pipeline } from 'stream/promises';
@@ -11295,7 +11295,7 @@ var DISALLOWED_BUILTIN_TOOLS = [
11295
11295
  "Agent"
11296
11296
  ];
11297
11297
  function runOrchestrator(opts) {
11298
- const { prompt, orchestratorServer, model, sessionId } = opts;
11298
+ const { prompt, orchestratorServer, model, sessionId, claudeCodePath } = opts;
11299
11299
  return query({
11300
11300
  prompt,
11301
11301
  options: {
@@ -11307,6 +11307,7 @@ function runOrchestrator(opts) {
11307
11307
  },
11308
11308
  systemPrompt: DESKFREE_AGENT_DIRECTIVE,
11309
11309
  model,
11310
+ ...claudeCodePath ? { pathToClaudeCodeExecutable: claudeCodePath } : {},
11310
11311
  maxTurns: MAX_ORCHESTRATOR_TURNS,
11311
11312
  permissionMode: "bypassPermissions",
11312
11313
  allowDangerouslySkipPermissions: true,
@@ -11334,12 +11335,13 @@ function runOrchestrator(opts) {
11334
11335
  });
11335
11336
  }
11336
11337
  function runHeartbeat(opts) {
11337
- const { prompt, orchestratorServer, model } = opts;
11338
+ const { prompt, orchestratorServer, model, claudeCodePath } = opts;
11338
11339
  return query({
11339
11340
  prompt,
11340
11341
  options: {
11341
11342
  systemPrompt: DESKFREE_AGENT_DIRECTIVE,
11342
11343
  model,
11344
+ ...claudeCodePath ? { pathToClaudeCodeExecutable: claudeCodePath } : {},
11343
11345
  maxTurns: MAX_ORCHESTRATOR_TURNS,
11344
11346
  permissionMode: "bypassPermissions",
11345
11347
  allowDangerouslySkipPermissions: true,
@@ -11450,8 +11452,27 @@ function loadConfig() {
11450
11452
  };
11451
11453
  }
11452
11454
  function mergeWithRemoteConfig(local, remote) {
11455
+ const stateDirOverridden = !!process.env["DESKFREE_STATE_DIR"];
11456
+ const toolsDirOverridden = !!process.env["DESKFREE_TOOLS_DIR"];
11457
+ const stateDir = stateDirOverridden ? local.stateDir : isDocker ? local.stateDir : `.deskfree/${remote.botId}/state`;
11458
+ const toolsDir = toolsDirOverridden ? local.toolsDir : isDocker ? local.toolsDir : `.deskfree/${remote.botId}/tools`;
11459
+ let claudeCodePath;
11460
+ if (remote.provider === "claude-code") {
11461
+ try {
11462
+ claudeCodePath = execFileSync("which", ["claude"], {
11463
+ encoding: "utf8"
11464
+ }).trim();
11465
+ } catch {
11466
+ throw new Error(
11467
+ 'Provider is "claude-code" but the `claude` CLI was not found on PATH. Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code'
11468
+ );
11469
+ }
11470
+ }
11453
11471
  return {
11454
11472
  ...local,
11473
+ stateDir,
11474
+ toolsDir,
11475
+ claudeCodePath,
11455
11476
  wsUrl: process.env["DESKFREE_WS_URL"] ?? remote.wsUrl,
11456
11477
  model: process.env["DESKFREE_MODEL"] ?? remote.model,
11457
11478
  awsRegion: process.env["AWS_REGION"] ?? remote.awsRegion,
@@ -12860,7 +12881,8 @@ async function routeMessage(message, client, deps, sessionStore, config) {
12860
12881
  prompt,
12861
12882
  orchestratorServer: deps.createOrchestratorServer(),
12862
12883
  model: deps.model,
12863
- sessionId: existingSessionId
12884
+ sessionId: existingSessionId,
12885
+ claudeCodePath: deps.claudeCodePath
12864
12886
  });
12865
12887
  let fullText = "";
12866
12888
  let capturedSessionId = null;
@@ -13538,7 +13560,16 @@ function startHealthServer(port, log) {
13538
13560
  log.info(`Health server listening on port ${port}`);
13539
13561
  });
13540
13562
  server.on("error", (err) => {
13541
- log.warn(`Health server error: ${err.message}`);
13563
+ if (err.code === "EADDRINUSE") {
13564
+ log.info(`Port ${port} in use, requesting OS-assigned port...`);
13565
+ server.listen(0, () => {
13566
+ const addr = server.address();
13567
+ const assignedPort = typeof addr === "object" && addr ? addr.port : "unknown";
13568
+ log.info(`Health server listening on port ${assignedPort}`);
13569
+ });
13570
+ } else {
13571
+ log.warn(`Health server error: ${err.message}`);
13572
+ }
13542
13573
  });
13543
13574
  return {
13544
13575
  close() {
@@ -13546,7 +13577,7 @@ function startHealthServer(port, log) {
13546
13577
  }
13547
13578
  };
13548
13579
  }
13549
- function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log) {
13580
+ function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log, claudeCodePath) {
13550
13581
  if (intervalMs <= 0) return;
13551
13582
  let running = false;
13552
13583
  async function tick() {
@@ -13557,7 +13588,8 @@ function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log) {
13557
13588
  const result = runHeartbeat({
13558
13589
  prompt: DESKFREE_HEARTBEAT_DIRECTIVE,
13559
13590
  orchestratorServer: createOrchServer(),
13560
- model
13591
+ model,
13592
+ claudeCodePath
13561
13593
  });
13562
13594
  for await (const _ of result) {
13563
13595
  }
@@ -13579,8 +13611,6 @@ async function startAgent(opts) {
13579
13611
  const log = opts?.log ?? createLogger("agent", localConfig.logLevel);
13580
13612
  const abortController = new AbortController();
13581
13613
  log.info("DeskFree Agent Runtime starting...");
13582
- mkdirSync(localConfig.stateDir, { recursive: true });
13583
- mkdirSync(localConfig.toolsDir, { recursive: true });
13584
13614
  const client = new DeskFreeClient(localConfig.botToken, localConfig.apiUrl);
13585
13615
  const errorReporter = initErrorReporter(client, log);
13586
13616
  initializeHealth("unknown");
@@ -13593,12 +13623,15 @@ async function startAgent(opts) {
13593
13623
  wsUrl: config.wsUrl,
13594
13624
  model: config.model,
13595
13625
  region: config.awsRegion,
13596
- tools: config.tools.length
13626
+ tools: config.tools.length,
13627
+ botId: config.botId
13597
13628
  });
13598
13629
  } catch (err) {
13599
13630
  const msg = err instanceof Error ? err.message : String(err);
13600
13631
  throw new Error(`Failed to bootstrap config from API: ${msg}`);
13601
13632
  }
13633
+ mkdirSync(config.stateDir, { recursive: true });
13634
+ mkdirSync(config.toolsDir, { recursive: true });
13602
13635
  if (config.tools.length > 0) {
13603
13636
  log.info(`Installing ${config.tools.length} tool package(s)...`);
13604
13637
  await installTools(config.tools, config.toolsDir, log);
@@ -13679,7 +13712,8 @@ async function startAgent(opts) {
13679
13712
  {
13680
13713
  createOrchestratorServer: createOrchServer,
13681
13714
  workerManager,
13682
- model: config.model
13715
+ model: config.model,
13716
+ claudeCodePath: config.claudeCodePath
13683
13717
  },
13684
13718
  sessionStore,
13685
13719
  {
@@ -13694,7 +13728,8 @@ async function startAgent(opts) {
13694
13728
  config.model,
13695
13729
  config.heartbeatIntervalMs,
13696
13730
  abortController.signal,
13697
- log
13731
+ log,
13732
+ config.claudeCodePath
13698
13733
  );
13699
13734
  if (config.memoryFileId && config.sleepHour !== null && config.timezone) {
13700
13735
  const memoryFileId = config.memoryFileId;