adhdev 0.6.21 → 0.6.23

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.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
  });
@@ -4966,6 +4968,7 @@ var init_provider_loader = __esm({
4966
4968
  builtinDirs;
4967
4969
  userDir;
4968
4970
  upstreamDir;
4971
+ disableUpstream;
4969
4972
  watchers = [];
4970
4973
  logFn;
4971
4974
  versionArchive = null;
@@ -4984,6 +4987,7 @@ var init_provider_loader = __esm({
4984
4987
  }
4985
4988
  this.userDir = options?.userDir || path6.join(os7.homedir(), ".adhdev", "providers");
4986
4989
  this.upstreamDir = path6.join(this.userDir, ".upstream");
4990
+ this.disableUpstream = options?.disableUpstream ?? false;
4987
4991
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
4988
4992
  }
4989
4993
  log(msg) {
@@ -5000,11 +5004,13 @@ var init_provider_loader = __esm({
5000
5004
  loadAll() {
5001
5005
  this.providers.clear();
5002
5006
  let upstreamCount = 0;
5003
- if (fs5.existsSync(this.upstreamDir)) {
5007
+ if (!this.disableUpstream && fs5.existsSync(this.upstreamDir)) {
5004
5008
  upstreamCount = this.loadDir(this.upstreamDir);
5005
5009
  if (upstreamCount > 0) {
5006
5010
  this.log(`Loaded ${upstreamCount} upstream providers (auto-updated)`);
5007
5011
  }
5012
+ } else if (this.disableUpstream) {
5013
+ this.log("Upstream loading disabled (disableUpstream=true)");
5008
5014
  }
5009
5015
  if (fs5.existsSync(this.userDir)) {
5010
5016
  const userCount = this.loadDir(this.userDir, [".upstream"]);
@@ -5407,6 +5413,10 @@ var init_provider_loader = __esm({
5407
5413
  * @returns Whether an update occurred
5408
5414
  */
5409
5415
  async fetchLatest() {
5416
+ if (this.disableUpstream) {
5417
+ this.log("Upstream fetch skipped (disableUpstream=true)");
5418
+ return { updated: false };
5419
+ }
5410
5420
  const https = require("https");
5411
5421
  const { execSync: execSync6 } = require("child_process");
5412
5422
  const metaPath = path6.join(this.upstreamDir, _ProviderLoader.META_FILE);
@@ -6717,12 +6727,6 @@ var init_reporter = __esm({
6717
6727
  emitStatusEvent(event) {
6718
6728
  LOG.info("StatusEvent", `${event.event} (${event.providerType || event.ideType || ""})`);
6719
6729
  this.deps.serverConn?.sendMessage("status_event", event);
6720
- if (this.deps.p2p?.isConnected) {
6721
- try {
6722
- this.deps.p2p.sendStatusEvent?.(event);
6723
- } catch {
6724
- }
6725
- }
6726
6730
  }
6727
6731
  removeAgentTracking(_key) {
6728
6732
  }
@@ -6800,6 +6804,7 @@ var init_reporter = __esm({
6800
6804
  peers: p2p?.connectedPeerCount || 0,
6801
6805
  screenshotActive: p2p?.screenshotActive || false
6802
6806
  },
6807
+ screenshotUsage: this.deps.getScreenshotUsage?.() || null,
6803
6808
  connectedExtensions: [],
6804
6809
  detectedIdes: this.deps.detectedIdes || [],
6805
6810
  availableProviders: this.deps.providerLoader.getAll().map((p) => ({
@@ -28813,6 +28818,22 @@ var init_dev_server = __esm({
28813
28818
  lines.push("| focusEditor | `{ focused: true/false }` |");
28814
28819
  lines.push("| openPanel | `{ opened: true/false }` |");
28815
28820
  lines.push("");
28821
+ lines.push("## \u{1F534} CRITICAL: readChat `status` Lifecycle");
28822
+ lines.push("The `status` field in readChat controls how the dashboard and daemon auto-approve-loop behave.");
28823
+ lines.push("Getting this wrong will break the entire automation pipeline. The status MUST reflect the ACTUAL current state:");
28824
+ lines.push("");
28825
+ lines.push("| Status | When to use | How to detect |");
28826
+ lines.push("|---|---|---|");
28827
+ lines.push("| `idle` | AI is NOT generating, no approval needed | Default state. No stop button, no spinners, no approval pills/buttons |");
28828
+ 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 |");
28829
+ lines.push("| `waiting_approval` | AI stopped and needs user action | Actionable buttons like Run/Skip/Accept/Reject are visible AND clickable |");
28830
+ lines.push("");
28831
+ lines.push("### \u26A0\uFE0F Status Detection Gotchas (MUST READ!)");
28832
+ 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.');
28833
+ 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`.');
28834
+ 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.");
28835
+ 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`.");
28836
+ lines.push("");
28816
28837
  lines.push("## Action");
28817
28838
  lines.push("1. Edit the script files to implement working code");
28818
28839
  lines.push("2. After editing, TEST each function using the DevConsole API (see below)");
@@ -28837,7 +28858,7 @@ var init_dev_server = __esm({
28837
28858
  lines.push("Once you save the file, test it by running:");
28838
28859
  lines.push("```bash");
28839
28860
  lines.push(`curl -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/providers/reload`);
28840
- 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"}'`);
28861
+ 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}"}'`);
28841
28862
  lines.push("```");
28842
28863
  lines.push("");
28843
28864
  lines.push("### Task Workflow");
@@ -28849,12 +28870,44 @@ var init_dev_server = __esm({
28849
28870
  lines.push("### \u{1F525} Advanced UI Parsing (CRUCIAL for `readChat`)");
28850
28871
  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.");
28851
28872
  lines.push("To achieve this, you MUST generate a live test scenario:");
28852
- lines.push("1. Early in your process, send a rich prompt to the IDE using the API:");
28853
- 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"}}\'`');
28873
+ lines.push(`1. Early in your process, send a rich prompt to the IDE using the API:`);
28874
+ 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"}}'\``);
28854
28875
  lines.push("2. Wait a few seconds for the IDE AI to generate these elements in the UI.");
28855
28876
  lines.push("3. Use CDP evaluate to deeply inspect the DOM structure of the newly generated tables, code blocks, thought blocks, and tool calls.");
28856
28877
  lines.push("4. Ensure `readChat` extracts `content` with precise markdown formatting (especially for tables/code) and assigns correct `kind` tags (`thought`, `tool`, `terminal`).");
28857
28878
  lines.push("");
28879
+ lines.push("## \u{1F9EA} MANDATORY: Status Integration Test");
28880
+ lines.push("Before finishing, you MUST run this end-to-end test to verify readChat status transitions work:");
28881
+ lines.push("");
28882
+ lines.push("### Step 1: Baseline \u2014 confirm idle");
28883
+ lines.push("```bash");
28884
+ lines.push(`curl -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/providers/reload`);
28885
+ 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}"}')`);
28886
+ 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')"`);
28887
+ lines.push("```");
28888
+ lines.push("");
28889
+ lines.push("### Step 2: Send a message that triggers generation");
28890
+ lines.push("```bash");
28891
+ 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"}}'`);
28892
+ lines.push("sleep 2");
28893
+ lines.push("```");
28894
+ lines.push("");
28895
+ lines.push("### Step 3: Check generating OR completed");
28896
+ lines.push("The AI may still be generating OR may have finished already. Either generating or idle is acceptable:");
28897
+ lines.push("```bash");
28898
+ 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}"}')`);
28899
+ 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}')"`);
28900
+ lines.push("```");
28901
+ lines.push("");
28902
+ lines.push("### Step 4: Wait for completion and verify new message");
28903
+ lines.push("```bash");
28904
+ lines.push("sleep 10");
28905
+ 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}"}')`);
28906
+ 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)}')"`);
28907
+ lines.push("```");
28908
+ lines.push("");
28909
+ lines.push("If ANY step fails, fix your implementation and re-run the test. Do NOT finish until all 4 steps pass.");
28910
+ lines.push("");
28858
28911
  if (userComment) {
28859
28912
  lines.push("## \u26A0\uFE0F User Instructions (HIGH PRIORITY)");
28860
28913
  lines.push("The user has provided the following additional instructions. Follow them strictly:");
@@ -29208,10 +29261,13 @@ var init_installer = __esm({
29208
29261
  // ../daemon-core/src/boot/daemon-lifecycle.ts
29209
29262
  async function initDaemonComponents(config2) {
29210
29263
  installGlobalInterceptor();
29264
+ const appConfig = loadConfig();
29265
+ const disableUpstream = appConfig.disableUpstream ?? false;
29211
29266
  const providerLoader = new ProviderLoader({
29212
- logFn: config2.providerLogFn
29267
+ logFn: config2.providerLogFn,
29268
+ disableUpstream
29213
29269
  });
29214
- if (!providerLoader.hasUpstream()) {
29270
+ if (!disableUpstream && !providerLoader.hasUpstream()) {
29215
29271
  LOG.info("Provider", "No upstream providers found \u2014 downloading from GitHub...");
29216
29272
  try {
29217
29273
  await providerLoader.fetchLatest();
@@ -30181,24 +30237,6 @@ ${e?.stack || ""}`);
30181
30237
  }
30182
30238
  return sentAny;
30183
30239
  }
30184
- /** Send status_event directly via P2P (generating_started/completed etc.) */
30185
- sendStatusEvent(event) {
30186
- const payload = JSON.stringify({
30187
- type: "status_event",
30188
- payload: event,
30189
- timestamp: Date.now()
30190
- });
30191
- let sentAny = false;
30192
- for (const peer of this.peers.values()) {
30193
- if (peer.state !== "connected" || !peer.dataChannel) continue;
30194
- try {
30195
- peer.dataChannel.sendMessage(payload);
30196
- sentAny = true;
30197
- } catch {
30198
- }
30199
- }
30200
- return sentAny;
30201
- }
30202
30240
  /** Broadcast PTY output to all connected peers */
30203
30241
  broadcastPtyOutput(cliType, data) {
30204
30242
  const prev = this.ptyScrollback.get(cliType) || "";
@@ -30607,6 +30645,15 @@ var init_screenshot_controller = __esm({
30607
30645
  LOG.info("Screenshot", `Daily budget reset (${this.dailyBudgetMinutes}min for today)`);
30608
30646
  }
30609
30647
  }
30648
+ /** Returns current usage stats for reporting to server/dashboard */
30649
+ getUsageStats() {
30650
+ this.checkBudgetReset();
30651
+ return {
30652
+ dailyUsedMinutes: Math.round(this.dailyUsedMs / 6e4),
30653
+ dailyBudgetMinutes: this.dailyBudgetMinutes,
30654
+ budgetExhausted: this.budgetExhausted
30655
+ };
30656
+ }
30610
30657
  // ─── Hash ─────────────────────────────────────
30611
30658
  /** FNV-1a hash of first 1KB + last 1KB for fast delta detection */
30612
30659
  static fnvHash(buf) {
@@ -30687,7 +30734,7 @@ var init_adhdev_daemon = __esm({
30687
30734
  fs11 = __toESM(require("fs"));
30688
30735
  path14 = __toESM(require("path"));
30689
30736
  import_chalk2 = __toESM(require("chalk"));
30690
- pkgVersion = "0.6.21";
30737
+ pkgVersion = "0.6.23";
30691
30738
  if (pkgVersion === "unknown") {
30692
30739
  try {
30693
30740
  const possiblePaths = [
@@ -30754,8 +30801,9 @@ ${err?.stack || ""}`);
30754
30801
  }
30755
30802
  writeDaemonPid(process.pid);
30756
30803
  const config2 = loadConfig();
30757
- if (!config2.connectionToken) {
30758
- console.log(import_chalk2.default.red("\n\u2717 No connection token found."));
30804
+ const authToken = config2.machineSecret || config2.connectionToken;
30805
+ if (!authToken) {
30806
+ console.log(import_chalk2.default.red("\n\u2717 No credentials found."));
30759
30807
  console.log(import_chalk2.default.gray(" Run `adhdev setup` first.\n"));
30760
30808
  process.exit(1);
30761
30809
  }
@@ -30805,7 +30853,7 @@ ${err?.stack || ""}`);
30805
30853
  const instanceId = `daemon_${config2.machineId}`;
30806
30854
  this.serverConn = new ServerConnection({
30807
30855
  serverUrl: options.serverUrl || config2.serverUrl,
30808
- token: config2.connectionToken,
30856
+ token: authToken,
30809
30857
  daemonVersion: pkgVersion,
30810
30858
  cliInfo: {
30811
30859
  type: "adhdev-daemon",
@@ -30897,7 +30945,8 @@ ${err?.stack || ""}`);
30897
30945
  detectedIdes: this.components.detectedIdes.value,
30898
30946
  ideType: this.ideType,
30899
30947
  daemonVersion: pkgVersion,
30900
- instanceManager: this.components.instanceManager
30948
+ instanceManager: this.components.instanceManager,
30949
+ getScreenshotUsage: () => this.screenshotController?.getUsageStats() || null
30901
30950
  });
30902
30951
  this.statusReporter.startReporting();
30903
30952
  this.components.instanceManager.onEvent((event) => {
@@ -31200,11 +31249,17 @@ async function quickSetup() {
31200
31249
  }
31201
31250
  markSetupComplete(["daemon"], ["adhdev"]);
31202
31251
  if (loginResult) {
31203
- updateConfig({
31252
+ const configUpdate = {
31204
31253
  connectionToken: loginResult.connectionToken,
31205
31254
  userEmail: loginResult.email,
31206
31255
  userName: loginResult.name
31207
- });
31256
+ };
31257
+ if (loginResult.machineId && loginResult.machineSecret) {
31258
+ configUpdate.machineId = loginResult.machineId;
31259
+ configUpdate.machineSecret = loginResult.machineSecret;
31260
+ console.log(import_chalk3.default.green(` \u2713 Machine registered`));
31261
+ }
31262
+ updateConfig(configUpdate);
31208
31263
  }
31209
31264
  await installCliOnly();
31210
31265
  await startDaemonFlow();
@@ -31240,7 +31295,16 @@ async function loginFlow() {
31240
31295
  let userCode;
31241
31296
  let verificationUrl;
31242
31297
  try {
31243
- const res = await fetch(`${SERVER_URL}/auth/cli/init`, { method: "POST" });
31298
+ const os17 = await import("os");
31299
+ const res = await fetch(`${SERVER_URL}/auth/cli/init`, {
31300
+ method: "POST",
31301
+ headers: { "Content-Type": "application/json" },
31302
+ body: JSON.stringify({
31303
+ hostname: os17.hostname(),
31304
+ platform: os17.platform(),
31305
+ arch: os17.arch()
31306
+ })
31307
+ });
31244
31308
  if (!res.ok) {
31245
31309
  spinner.fail("Failed to connect to server");
31246
31310
  return null;
@@ -31287,10 +31351,27 @@ async function loginFlow() {
31287
31351
  pollSpinner.succeed(`Authenticated as ${import_chalk3.default.bold(data.user?.email || "user")}`);
31288
31352
  return {
31289
31353
  connectionToken: data.connectionToken,
31354
+ machineId: data.machineId || void 0,
31355
+ machineSecret: data.machineSecret || void 0,
31290
31356
  email: data.user?.email,
31291
31357
  name: data.user?.name
31292
31358
  };
31293
31359
  }
31360
+ if (data.status === "limit_reached") {
31361
+ pollSpinner.fail("Machine limit reached");
31362
+ console.log();
31363
+ 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.`));
31364
+ console.log();
31365
+ console.log(import_chalk3.default.yellow(" To fix this, do one of the following:"));
31366
+ console.log(import_chalk3.default.gray(" 1. Remove an unused machine from the dashboard:"));
31367
+ console.log(import_chalk3.default.gray(" https://adhf.dev/account \u2192 Registered Machines \u2192 \u2715 Remove"));
31368
+ console.log(import_chalk3.default.gray(" 2. Upgrade your plan:"));
31369
+ console.log(import_chalk3.default.gray(" https://adhf.dev/account?tab=billing"));
31370
+ console.log();
31371
+ console.log(import_chalk3.default.gray(" Then run `adhdev setup` again."));
31372
+ console.log();
31373
+ return null;
31374
+ }
31294
31375
  } catch {
31295
31376
  }
31296
31377
  }