adhdev 0.6.21 → 0.6.22

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
@@ -362,9 +362,11 @@ var init_config = __esm({
362
362
  recentWorkspaceActivity: [],
363
363
  machineNickname: null,
364
364
  machineId: void 0,
365
+ machineSecret: null,
365
366
  cliHistory: [],
366
367
  providerSettings: {},
367
- ideSettings: {}
368
+ ideSettings: {},
369
+ disableUpstream: false
368
370
  };
369
371
  }
370
372
  });
@@ -5134,6 +5136,7 @@ var init_provider_loader = __esm({
5134
5136
  builtinDirs;
5135
5137
  userDir;
5136
5138
  upstreamDir;
5139
+ disableUpstream;
5137
5140
  watchers = [];
5138
5141
  logFn;
5139
5142
  versionArchive = null;
@@ -5152,6 +5155,7 @@ var init_provider_loader = __esm({
5152
5155
  }
5153
5156
  this.userDir = options?.userDir || path6.join(os7.homedir(), ".adhdev", "providers");
5154
5157
  this.upstreamDir = path6.join(this.userDir, ".upstream");
5158
+ this.disableUpstream = options?.disableUpstream ?? false;
5155
5159
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
5156
5160
  }
5157
5161
  log(msg) {
@@ -5168,11 +5172,13 @@ var init_provider_loader = __esm({
5168
5172
  loadAll() {
5169
5173
  this.providers.clear();
5170
5174
  let upstreamCount = 0;
5171
- if (fs5.existsSync(this.upstreamDir)) {
5175
+ if (!this.disableUpstream && fs5.existsSync(this.upstreamDir)) {
5172
5176
  upstreamCount = this.loadDir(this.upstreamDir);
5173
5177
  if (upstreamCount > 0) {
5174
5178
  this.log(`Loaded ${upstreamCount} upstream providers (auto-updated)`);
5175
5179
  }
5180
+ } else if (this.disableUpstream) {
5181
+ this.log("Upstream loading disabled (disableUpstream=true)");
5176
5182
  }
5177
5183
  if (fs5.existsSync(this.userDir)) {
5178
5184
  const userCount = this.loadDir(this.userDir, [".upstream"]);
@@ -5575,6 +5581,10 @@ var init_provider_loader = __esm({
5575
5581
  * @returns Whether an update occurred
5576
5582
  */
5577
5583
  async fetchLatest() {
5584
+ if (this.disableUpstream) {
5585
+ this.log("Upstream fetch skipped (disableUpstream=true)");
5586
+ return { updated: false };
5587
+ }
5578
5588
  const https = require("https");
5579
5589
  const { execSync: execSync7 } = require("child_process");
5580
5590
  const metaPath = path6.join(this.upstreamDir, _ProviderLoader.META_FILE);
@@ -6913,12 +6923,6 @@ var init_reporter = __esm({
6913
6923
  emitStatusEvent(event) {
6914
6924
  LOG.info("StatusEvent", `${event.event} (${event.providerType || event.ideType || ""})`);
6915
6925
  this.deps.serverConn?.sendMessage("status_event", event);
6916
- if (this.deps.p2p?.isConnected) {
6917
- try {
6918
- this.deps.p2p.sendStatusEvent?.(event);
6919
- } catch {
6920
- }
6921
- }
6922
6926
  }
6923
6927
  removeAgentTracking(_key) {
6924
6928
  }
@@ -6996,6 +7000,7 @@ var init_reporter = __esm({
6996
7000
  peers: p2p?.connectedPeerCount || 0,
6997
7001
  screenshotActive: p2p?.screenshotActive || false
6998
7002
  },
7003
+ screenshotUsage: this.deps.getScreenshotUsage?.() || null,
6999
7004
  connectedExtensions: [],
7000
7005
  detectedIdes: this.deps.detectedIdes || [],
7001
7006
  availableProviders: this.deps.providerLoader.getAll().map((p) => ({
@@ -29010,6 +29015,22 @@ var init_dev_server = __esm({
29010
29015
  lines.push("| focusEditor | `{ focused: true/false }` |");
29011
29016
  lines.push("| openPanel | `{ opened: true/false }` |");
29012
29017
  lines.push("");
29018
+ lines.push("## \u{1F534} CRITICAL: readChat `status` Lifecycle");
29019
+ lines.push("The `status` field in readChat controls how the dashboard and daemon auto-approve-loop behave.");
29020
+ lines.push("Getting this wrong will break the entire automation pipeline. The status MUST reflect the ACTUAL current state:");
29021
+ lines.push("");
29022
+ lines.push("| Status | When to use | How to detect |");
29023
+ lines.push("|---|---|---|");
29024
+ lines.push("| `idle` | AI is NOT generating, no approval needed | Default state. No stop button, no spinners, no approval pills/buttons |");
29025
+ lines.push("| `generating` | AI is actively streaming/thinking | ANY of: (1) Stop/Cancel button visible, (2) CSS animation (animate-spin/pulse/bounce), (3) floating state text like Thinking/Generating/Sailing, (4) streaming indicator class |");
29026
+ lines.push("| `waiting_approval` | AI stopped and needs user action | Actionable buttons like Run/Skip/Accept/Reject are visible AND clickable |");
29027
+ lines.push("");
29028
+ lines.push("### \u26A0\uFE0F Status Detection Gotchas (MUST READ!)");
29029
+ lines.push('1. **FALSE POSITIVES from old messages**: Chat history may contain text like "Command Awaiting Approval" from PAST turns. If you search the entire chat panel for this text, you will get false matches from parent divs whose innerText includes ALL child text. ONLY match small leaf elements (under 80 chars) or use explicit button/pill selectors.');
29030
+ lines.push('2. **Awaiting Approval pill without actions**: Some IDEs show a floating pill/banner saying "Awaiting Approval" that is just a scroll-to indicator (not an actual approval dialog). If this pill exists but NO actionable buttons (Run/Skip/Accept/Reject) exist anywhere in the panel, the status should be `idle`, NOT `waiting_approval`.');
29031
+ lines.push("3. **generating detection must be multi-signal**: Do NOT rely on just one indicator. Check ALL of: stop buttons, CSS animations, floating state labels, streaming classes. IDEs differ widely.");
29032
+ lines.push("4. **activeModal must include actions**: When `status` is `waiting_approval`, the `activeModal` object MUST include a non-empty `actions` array listing the button labels. If you cannot find any action buttons, the status is NOT `waiting_approval`.");
29033
+ lines.push("");
29013
29034
  lines.push("## Action");
29014
29035
  lines.push("1. Edit the script files to implement working code");
29015
29036
  lines.push("2. After editing, TEST each function using the DevConsole API (see below)");
@@ -29034,7 +29055,7 @@ var init_dev_server = __esm({
29034
29055
  lines.push("Once you save the file, test it by running:");
29035
29056
  lines.push("```bash");
29036
29057
  lines.push(`curl -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/providers/reload`);
29037
- lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/providers/${type}/scripts/run -H "Content-Type: application/json" -d '{"script": "readChat"}'`);
29058
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/scripts/run -H "Content-Type: application/json" -d '{"script": "readChat", "type": "${type}", "ideType": "${type}"}'`);
29038
29059
  lines.push("```");
29039
29060
  lines.push("");
29040
29061
  lines.push("### Task Workflow");
@@ -29046,12 +29067,44 @@ var init_dev_server = __esm({
29046
29067
  lines.push("### \u{1F525} Advanced UI Parsing (CRUCIAL for `readChat`)");
29047
29068
  lines.push("Your `readChat` must flawlessly parse complex UI elements (tables, code blocks, tool calls, and AI thoughts). The quality must match the `antigravity` reference.");
29048
29069
  lines.push("To achieve this, you MUST generate a live test scenario:");
29049
- lines.push("1. Early in your process, send a rich prompt to the IDE using the API:");
29050
- lines.push(' `curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/providers/${type}/scripts/run -H "Content-Type: application/json" -d \'{"script": "sendMessage", "params": {"text": "Write a python script, draw a markdown table, use a tool, and show your reasoning/thought process"}}\'`');
29070
+ lines.push(`1. Early in your process, send a rich prompt to the IDE using the API:`);
29071
+ lines.push(` \`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/scripts/run -H "Content-Type: application/json" -d '{"script": "sendMessage", "type": "${type}", "ideType": "${type}", "args": {"message": "Write a python script, draw a markdown table, use a tool, and show your reasoning/thought process"}}'\``);
29051
29072
  lines.push("2. Wait a few seconds for the IDE AI to generate these elements in the UI.");
29052
29073
  lines.push("3. Use CDP evaluate to deeply inspect the DOM structure of the newly generated tables, code blocks, thought blocks, and tool calls.");
29053
29074
  lines.push("4. Ensure `readChat` extracts `content` with precise markdown formatting (especially for tables/code) and assigns correct `kind` tags (`thought`, `tool`, `terminal`).");
29054
29075
  lines.push("");
29076
+ lines.push("## \u{1F9EA} MANDATORY: Status Integration Test");
29077
+ lines.push("Before finishing, you MUST run this end-to-end test to verify readChat status transitions work:");
29078
+ lines.push("");
29079
+ lines.push("### Step 1: Baseline \u2014 confirm idle");
29080
+ lines.push("```bash");
29081
+ lines.push(`curl -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/providers/reload`);
29082
+ lines.push(`RESULT=$(curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/scripts/run -H "Content-Type: application/json" -d '{"script": "readChat", "type": "${type}", "ideType": "${type}"}')`);
29083
+ lines.push(`echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); r=d.get('result',d); r=json.loads(r) if isinstance(r,str) else r; assert r.get('status')=='idle', f'Expected idle, got {r.get(chr(34)+chr(115)+chr(116)+chr(97)+chr(116)+chr(117)+chr(115)+chr(34))}'; print('Step 1 PASS: status=idle')"`);
29084
+ lines.push("```");
29085
+ lines.push("");
29086
+ lines.push("### Step 2: Send a message that triggers generation");
29087
+ lines.push("```bash");
29088
+ lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/scripts/run -H "Content-Type: application/json" -d '{"script": "sendMessage", "type": "${type}", "ideType": "${type}", "args": {"message": "Say hello in one word"}}'`);
29089
+ lines.push("sleep 2");
29090
+ lines.push("```");
29091
+ lines.push("");
29092
+ lines.push("### Step 3: Check generating OR completed");
29093
+ lines.push("The AI may still be generating OR may have finished already. Either generating or idle is acceptable:");
29094
+ lines.push("```bash");
29095
+ lines.push(`RESULT=$(curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/scripts/run -H "Content-Type: application/json" -d '{"script": "readChat", "type": "${type}", "ideType": "${type}"}')`);
29096
+ lines.push(`echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); r=d.get('result',d); r=json.loads(r) if isinstance(r,str) else r; s=r.get('status'); assert s in ('generating','idle','waiting_approval'), f'Unexpected: {s}'; print(f'Step 3 PASS: status={s}')"`);
29097
+ lines.push("```");
29098
+ lines.push("");
29099
+ lines.push("### Step 4: Wait for completion and verify new message");
29100
+ lines.push("```bash");
29101
+ lines.push("sleep 10");
29102
+ lines.push(`RESULT=$(curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/scripts/run -H "Content-Type: application/json" -d '{"script": "readChat", "type": "${type}", "ideType": "${type}"}')`);
29103
+ lines.push(`echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); r=d.get('result',d); r=json.loads(r) if isinstance(r,str) else r; s=r.get('status'); msgs=r.get('messages',[]); assert s=='idle', f'Expected idle, got {s}'; assert len(msgs)>0, 'No messages'; print(f'Step 4 PASS: status={s}, messages={len(msgs)}')"`);
29104
+ lines.push("```");
29105
+ lines.push("");
29106
+ lines.push("If ANY step fails, fix your implementation and re-run the test. Do NOT finish until all 4 steps pass.");
29107
+ lines.push("");
29055
29108
  if (userComment) {
29056
29109
  lines.push("## \u26A0\uFE0F User Instructions (HIGH PRIORITY)");
29057
29110
  lines.push("The user has provided the following additional instructions. Follow them strictly:");
@@ -29583,10 +29636,13 @@ var init_installer = __esm({
29583
29636
  // ../daemon-core/src/boot/daemon-lifecycle.ts
29584
29637
  async function initDaemonComponents(config2) {
29585
29638
  installGlobalInterceptor();
29639
+ const appConfig = loadConfig();
29640
+ const disableUpstream = appConfig.disableUpstream ?? false;
29586
29641
  const providerLoader = new ProviderLoader({
29587
- logFn: config2.providerLogFn
29642
+ logFn: config2.providerLogFn,
29643
+ disableUpstream
29588
29644
  });
29589
- if (!providerLoader.hasUpstream()) {
29645
+ if (!disableUpstream && !providerLoader.hasUpstream()) {
29590
29646
  LOG.info("Provider", "No upstream providers found \u2014 downloading from GitHub...");
29591
29647
  try {
29592
29648
  await providerLoader.fetchLatest();
@@ -30621,24 +30677,6 @@ ${e?.stack || ""}`);
30621
30677
  }
30622
30678
  return sentAny;
30623
30679
  }
30624
- /** Send status_event directly via P2P (generating_started/completed etc.) */
30625
- sendStatusEvent(event) {
30626
- const payload = JSON.stringify({
30627
- type: "status_event",
30628
- payload: event,
30629
- timestamp: Date.now()
30630
- });
30631
- let sentAny = false;
30632
- for (const peer of this.peers.values()) {
30633
- if (peer.state !== "connected" || !peer.dataChannel) continue;
30634
- try {
30635
- peer.dataChannel.sendMessage(payload);
30636
- sentAny = true;
30637
- } catch {
30638
- }
30639
- }
30640
- return sentAny;
30641
- }
30642
30680
  /** Broadcast PTY output to all connected peers */
30643
30681
  broadcastPtyOutput(cliType, data) {
30644
30682
  const prev = this.ptyScrollback.get(cliType) || "";
@@ -31047,6 +31085,15 @@ var init_screenshot_controller = __esm({
31047
31085
  LOG.info("Screenshot", `Daily budget reset (${this.dailyBudgetMinutes}min for today)`);
31048
31086
  }
31049
31087
  }
31088
+ /** Returns current usage stats for reporting to server/dashboard */
31089
+ getUsageStats() {
31090
+ this.checkBudgetReset();
31091
+ return {
31092
+ dailyUsedMinutes: Math.round(this.dailyUsedMs / 6e4),
31093
+ dailyBudgetMinutes: this.dailyBudgetMinutes,
31094
+ budgetExhausted: this.budgetExhausted
31095
+ };
31096
+ }
31050
31097
  // ─── Hash ─────────────────────────────────────
31051
31098
  /** FNV-1a hash of first 1KB + last 1KB for fast delta detection */
31052
31099
  static fnvHash(buf) {
@@ -31127,7 +31174,7 @@ var init_adhdev_daemon = __esm({
31127
31174
  fs11 = __toESM(require("fs"));
31128
31175
  path14 = __toESM(require("path"));
31129
31176
  import_chalk2 = __toESM(require("chalk"));
31130
- pkgVersion = "0.6.21";
31177
+ pkgVersion = "0.6.22";
31131
31178
  if (pkgVersion === "unknown") {
31132
31179
  try {
31133
31180
  const possiblePaths = [
@@ -31194,8 +31241,9 @@ ${err?.stack || ""}`);
31194
31241
  }
31195
31242
  writeDaemonPid(process.pid);
31196
31243
  const config2 = loadConfig();
31197
- if (!config2.connectionToken) {
31198
- console.log(import_chalk2.default.red("\n\u2717 No connection token found."));
31244
+ const authToken = config2.machineSecret || config2.connectionToken;
31245
+ if (!authToken) {
31246
+ console.log(import_chalk2.default.red("\n\u2717 No credentials found."));
31199
31247
  console.log(import_chalk2.default.gray(" Run `adhdev setup` first.\n"));
31200
31248
  process.exit(1);
31201
31249
  }
@@ -31245,7 +31293,7 @@ ${err?.stack || ""}`);
31245
31293
  const instanceId = `daemon_${config2.machineId}`;
31246
31294
  this.serverConn = new ServerConnection({
31247
31295
  serverUrl: options.serverUrl || config2.serverUrl,
31248
- token: config2.connectionToken,
31296
+ token: authToken,
31249
31297
  daemonVersion: pkgVersion,
31250
31298
  cliInfo: {
31251
31299
  type: "adhdev-daemon",
@@ -31337,7 +31385,8 @@ ${err?.stack || ""}`);
31337
31385
  detectedIdes: this.components.detectedIdes.value,
31338
31386
  ideType: this.ideType,
31339
31387
  daemonVersion: pkgVersion,
31340
- instanceManager: this.components.instanceManager
31388
+ instanceManager: this.components.instanceManager,
31389
+ getScreenshotUsage: () => this.screenshotController?.getUsageStats() || null
31341
31390
  });
31342
31391
  this.statusReporter.startReporting();
31343
31392
  this.components.instanceManager.onEvent((event) => {
@@ -31615,11 +31664,17 @@ async function quickSetup() {
31615
31664
  }
31616
31665
  markSetupComplete(["daemon"], ["adhdev"]);
31617
31666
  if (loginResult) {
31618
- updateConfig({
31667
+ const configUpdate = {
31619
31668
  connectionToken: loginResult.connectionToken,
31620
31669
  userEmail: loginResult.email,
31621
31670
  userName: loginResult.name
31622
- });
31671
+ };
31672
+ if (loginResult.machineId && loginResult.machineSecret) {
31673
+ configUpdate.machineId = loginResult.machineId;
31674
+ configUpdate.machineSecret = loginResult.machineSecret;
31675
+ console.log(import_chalk3.default.green(` \u2713 Machine registered`));
31676
+ }
31677
+ updateConfig(configUpdate);
31623
31678
  }
31624
31679
  await installCliOnly();
31625
31680
  await startDaemonFlow();
@@ -31655,7 +31710,16 @@ async function loginFlow() {
31655
31710
  let userCode;
31656
31711
  let verificationUrl;
31657
31712
  try {
31658
- const res = await fetch(`${SERVER_URL}/auth/cli/init`, { method: "POST" });
31713
+ const os17 = await import("os");
31714
+ const res = await fetch(`${SERVER_URL}/auth/cli/init`, {
31715
+ method: "POST",
31716
+ headers: { "Content-Type": "application/json" },
31717
+ body: JSON.stringify({
31718
+ hostname: os17.hostname(),
31719
+ platform: os17.platform(),
31720
+ arch: os17.arch()
31721
+ })
31722
+ });
31659
31723
  if (!res.ok) {
31660
31724
  spinner.fail("Failed to connect to server");
31661
31725
  return null;
@@ -31702,10 +31766,27 @@ async function loginFlow() {
31702
31766
  pollSpinner.succeed(`Authenticated as ${import_chalk3.default.bold(data.user?.email || "user")}`);
31703
31767
  return {
31704
31768
  connectionToken: data.connectionToken,
31769
+ machineId: data.machineId || void 0,
31770
+ machineSecret: data.machineSecret || void 0,
31705
31771
  email: data.user?.email,
31706
31772
  name: data.user?.name
31707
31773
  };
31708
31774
  }
31775
+ if (data.status === "limit_reached") {
31776
+ pollSpinner.fail("Machine limit reached");
31777
+ console.log();
31778
+ console.log(import_chalk3.default.red(` \u2717 Your ${import_chalk3.default.bold(data.plan || "free")} plan allows ${data.limit} machine(s), and you have ${data.current} registered.`));
31779
+ console.log();
31780
+ console.log(import_chalk3.default.yellow(" To fix this, do one of the following:"));
31781
+ console.log(import_chalk3.default.gray(" 1. Remove an unused machine from the dashboard:"));
31782
+ console.log(import_chalk3.default.gray(" https://adhf.dev/account \u2192 Registered Machines \u2192 \u2715 Remove"));
31783
+ console.log(import_chalk3.default.gray(" 2. Upgrade your plan:"));
31784
+ console.log(import_chalk3.default.gray(" https://adhf.dev/account?tab=billing"));
31785
+ console.log();
31786
+ console.log(import_chalk3.default.gray(" Then run `adhdev setup` again."));
31787
+ console.log();
31788
+ return null;
31789
+ }
31709
31790
  } catch {
31710
31791
  }
31711
31792
  }