haltija 1.3.0-beta.8 → 1.3.0-beta.9

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.
@@ -1387,6 +1387,13 @@ if (!gotTheLock) {
1387
1387
  // App lifecycle
1388
1388
  app.whenReady().then(async () => {
1389
1389
  console.log('[Haltija Desktop] App ready, starting initialization...')
1390
+
1391
+ // Clear the "manually quit" marker — user is explicitly starting Haltija,
1392
+ // so hj should resume auto-launching when needed.
1393
+ try {
1394
+ const quitMarker = path.join(os.homedir(), '.haltija', 'last-quit')
1395
+ if (fs.existsSync(quitMarker)) fs.rmSync(quitMarker, { force: true })
1396
+ } catch {}
1390
1397
 
1391
1398
  try {
1392
1399
  // Start or connect to server first
@@ -1444,6 +1451,13 @@ if (!gotTheLock) {
1444
1451
  }
1445
1452
  embeddedServers.length = 0
1446
1453
  }
1454
+ // Drop a marker so hj's auto-launch knows the user explicitly quit.
1455
+ // Cleared next time the user manually starts Haltija.
1456
+ try {
1457
+ const dir = path.join(os.homedir(), '.haltija')
1458
+ fs.mkdirSync(dir, { recursive: true })
1459
+ fs.writeFileSync(path.join(dir, 'last-quit'), String(Date.now()))
1460
+ } catch {}
1447
1461
  })
1448
1462
 
1449
1463
  // Handle certificate errors (for self-signed certs in dev)
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "haltija-desktop",
3
- "version": "1.3.0-beta.8",
3
+ "version": "1.3.0-beta.9",
4
+ "private": true,
4
5
  "description": "Haltija Desktop - God Mode Browser for AI Agents",
5
6
  "homepage": "https://github.com/tonioloewald/haltija",
6
7
  "author": {
@@ -46,7 +46,7 @@
46
46
  });
47
47
 
48
48
  // src/version.ts
49
- var VERSION = "1.3.0-beta.8";
49
+ var VERSION = "1.3.0-beta.9";
50
50
 
51
51
  // src/text-selector.ts
52
52
  var TEXT_PSEUDO_RE = /:(?:text-is|has-text|text)\(/;
@@ -6113,7 +6113,7 @@ ${elementSummary}${moreText}`;
6113
6113
  }
6114
6114
  } else if (isContentEditable) {
6115
6115
  document.execCommand("insertText", false, char);
6116
- } else {}
6116
+ }
6117
6117
  el.dispatchEvent(new InputEvent("input", {
6118
6118
  bubbles: true,
6119
6119
  inputType: "insertText",
@@ -18,6 +18,7 @@
18
18
 
19
19
  import { spawn } from 'child_process'
20
20
  import { existsSync, readFileSync, readdirSync, statSync } from 'fs'
21
+ import { homedir } from 'os'
21
22
  import { dirname, join } from 'path'
22
23
  import { fileURLToPath } from 'url'
23
24
  import { formatTree } from './format-tree.mjs'
@@ -455,28 +456,39 @@ async function isServerRunning(port) {
455
456
  }
456
457
 
457
458
  /**
458
- * Resolve the server path - checks for bundled binary first, then dev server.
459
+ * Resolve the server path. Search order:
460
+ * 1. Next to the hj binary (compiled distribution).
461
+ * 2. `../dist/server.js` (running from a source checkout).
462
+ * 3. Inside an installed `/Applications/Haltija.app` (the bundled
463
+ * server binary lives in Contents/Resources/haltija-server-<arch>).
464
+ * 4. Same under `~/Applications/`.
459
465
  * Exported for testing.
460
466
  */
461
467
  export function resolveServerPath() {
462
468
  const arch = process.arch === 'arm64' ? 'arm64' : 'x64'
463
469
  const execDir = dirname(process.execPath)
464
- const bundledServerPath = join(execDir, `haltija-server-${arch}`)
465
- const devServerPath = join(__dirname, '../dist/server.js')
466
-
467
- if (existsSync(bundledServerPath)) {
468
- return { type: 'bundled', path: bundledServerPath }
469
- } else if (existsSync(devServerPath)) {
470
- return { type: 'dev', path: devServerPath }
470
+ const candidates = [
471
+ { type: 'bundled', path: join(execDir, `haltija-server-${arch}`) },
472
+ { type: 'dev', path: join(__dirname, '../dist/server.js') },
473
+ { type: 'app', path: `/Applications/Haltija.app/Contents/Resources/haltija-server-${arch}` },
474
+ { type: 'app', path: join(homedir(), `Applications/Haltija.app/Contents/Resources/haltija-server-${arch}`) },
475
+ ]
476
+ for (const c of candidates) {
477
+ if (existsSync(c.path)) return c
471
478
  }
472
479
  return null
473
480
  }
474
481
 
475
482
  async function startServerInBackground(port) {
476
483
  const resolved = resolveServerPath()
477
-
484
+
478
485
  if (!resolved) {
479
- console.error('Error: Server not found. Run `bun run build` first.')
486
+ console.error('Error: no haltija server found.')
487
+ console.error('')
488
+ console.error('Install one of these:')
489
+ console.error(' • Haltija desktop app: https://github.com/tonioloewald/haltija/releases')
490
+ console.error(' • Or run a server in another shell: bunx haltija --server')
491
+ console.error(' • Or, if you are developing haltija from source: bun run build')
480
492
  process.exit(1)
481
493
  }
482
494
 
@@ -551,14 +563,31 @@ async function launchElectronApp() {
551
563
  }
552
564
 
553
565
  async function ensureBrowserConnected(port) {
566
+ let status
554
567
  try {
555
568
  const resp = await fetch(`http://localhost:${port}/status`, {
556
569
  signal: AbortSignal.timeout(2000)
557
570
  })
558
- const status = await resp.json()
571
+ status = await resp.json()
559
572
  if (status.ok) return true
560
573
  } catch { return false }
561
-
574
+
575
+ // Server is up but no tabs yet. If this server is hosted by the Haltija
576
+ // desktop app, it will open a tab itself when the agent's command hits
577
+ // (via __NEED_WINDOW__). Launching /Applications/Haltija.app on top would
578
+ // produce two app instances side by side — skip the launch.
579
+ if (status?.desktopApp) return true
580
+
581
+ // Respect "user explicitly quit" — don't auto-relaunch on every agent
582
+ // call. Cleared when the user starts Haltija manually.
583
+ try {
584
+ const quitMarker = join(homedir(), '.haltija', 'last-quit')
585
+ if (existsSync(quitMarker)) {
586
+ process.stderr.write('\x1b[2m(Haltija was quit by user; not auto-launching. Open Haltija manually to resume.)\x1b[0m\n')
587
+ return false
588
+ }
589
+ } catch {}
590
+
562
591
  // No windows connected — try to launch Electron app (macOS only)
563
592
  if (process.platform !== 'darwin') return false
564
593
 
@@ -611,6 +640,18 @@ export async function runSubcommand(subcommand, subArgs, port = '8700', options
611
640
 
612
641
  // Check if server is running, auto-start if not
613
642
  if (!(await isServerRunning(port))) {
643
+ // Respect "user manually quit Haltija" before we try to spawn anything.
644
+ // The marker is dropped by the desktop app on will-quit and cleared on
645
+ // its next launch — agent calls in between should not bring it back.
646
+ try {
647
+ const quitMarker = join(homedir(), '.haltija', 'last-quit')
648
+ if (!noLaunch && existsSync(quitMarker)) {
649
+ console.error('Haltija was quit by user; not auto-launching.')
650
+ console.error('Open Haltija manually to resume — or run `hj --no-launch` to bypass this check.')
651
+ process.exit(1)
652
+ }
653
+ } catch {}
654
+
614
655
  process.stderr.write('\x1b[2mStarting Haltija server...\x1b[0m')
615
656
  const started = await startServerInBackground(port)
616
657
  if (started) {
package/dist/component.js CHANGED
@@ -46,7 +46,7 @@
46
46
  });
47
47
 
48
48
  // src/version.ts
49
- var VERSION = "1.3.0-beta.8";
49
+ var VERSION = "1.3.0-beta.9";
50
50
 
51
51
  // src/text-selector.ts
52
52
  var TEXT_PSEUDO_RE = /:(?:text-is|has-text|text)\(/;
@@ -6113,7 +6113,7 @@ ${elementSummary}${moreText}`;
6113
6113
  }
6114
6114
  } else if (isContentEditable) {
6115
6115
  document.execCommand("insertText", false, char);
6116
- } else {}
6116
+ }
6117
6117
  el.dispatchEvent(new InputEvent("input", {
6118
6118
  bubbles: true,
6119
6119
  inputType: "insertText",
package/dist/hj.js CHANGED
@@ -5,6 +5,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
5
  // bin/cli-subcommand.mjs
6
6
  import { spawn } from "child_process";
7
7
  import { existsSync, readFileSync, readdirSync, statSync } from "fs";
8
+ import { homedir } from "os";
8
9
  import { dirname, join } from "path";
9
10
  import { fileURLToPath } from "url";
10
11
 
@@ -1235,19 +1236,27 @@ async function isServerRunning(port) {
1235
1236
  function resolveServerPath() {
1236
1237
  const arch = process.arch === "arm64" ? "arm64" : "x64";
1237
1238
  const execDir = dirname(process.execPath);
1238
- const bundledServerPath = join(execDir, `haltija-server-${arch}`);
1239
- const devServerPath = join(__dirname2, "../dist/server.js");
1240
- if (existsSync(bundledServerPath)) {
1241
- return { type: "bundled", path: bundledServerPath };
1242
- } else if (existsSync(devServerPath)) {
1243
- return { type: "dev", path: devServerPath };
1239
+ const candidates = [
1240
+ { type: "bundled", path: join(execDir, `haltija-server-${arch}`) },
1241
+ { type: "dev", path: join(__dirname2, "../dist/server.js") },
1242
+ { type: "app", path: `/Applications/Haltija.app/Contents/Resources/haltija-server-${arch}` },
1243
+ { type: "app", path: join(homedir(), `Applications/Haltija.app/Contents/Resources/haltija-server-${arch}`) }
1244
+ ];
1245
+ for (const c of candidates) {
1246
+ if (existsSync(c.path))
1247
+ return c;
1244
1248
  }
1245
1249
  return null;
1246
1250
  }
1247
1251
  async function startServerInBackground(port) {
1248
1252
  const resolved = resolveServerPath();
1249
1253
  if (!resolved) {
1250
- console.error("Error: Server not found. Run `bun run build` first.");
1254
+ console.error("Error: no haltija server found.");
1255
+ console.error("");
1256
+ console.error("Install one of these:");
1257
+ console.error(" • Haltija desktop app: https://github.com/tonioloewald/haltija/releases");
1258
+ console.error(" • Or run a server in another shell: bunx haltija --server");
1259
+ console.error(" • Or, if you are developing haltija from source: bun run build");
1251
1260
  process.exit(1);
1252
1261
  }
1253
1262
  let command, cmdArgs;
@@ -1305,16 +1314,27 @@ async function launchElectronApp() {
1305
1314
  return false;
1306
1315
  }
1307
1316
  async function ensureBrowserConnected(port) {
1317
+ let status;
1308
1318
  try {
1309
1319
  const resp = await fetch(`http://localhost:${port}/status`, {
1310
1320
  signal: AbortSignal.timeout(2000)
1311
1321
  });
1312
- const status = await resp.json();
1322
+ status = await resp.json();
1313
1323
  if (status.ok)
1314
1324
  return true;
1315
1325
  } catch {
1316
1326
  return false;
1317
1327
  }
1328
+ if (status?.desktopApp)
1329
+ return true;
1330
+ try {
1331
+ const quitMarker = join(homedir(), ".haltija", "last-quit");
1332
+ if (existsSync(quitMarker)) {
1333
+ process.stderr.write(`\x1B[2m(Haltija was quit by user; not auto-launching. Open Haltija manually to resume.)\x1B[0m
1334
+ `);
1335
+ return false;
1336
+ }
1337
+ } catch {}
1318
1338
  if (process.platform !== "darwin")
1319
1339
  return false;
1320
1340
  process.stderr.write("\x1B[2mLaunching Haltija browser...\x1B[0m");
@@ -1331,8 +1351,8 @@ async function ensureBrowserConnected(port) {
1331
1351
  const resp = await fetch(`http://localhost:${port}/status`, {
1332
1352
  signal: AbortSignal.timeout(1000)
1333
1353
  });
1334
- const status = await resp.json();
1335
- if (status.ok) {
1354
+ const status2 = await resp.json();
1355
+ if (status2.ok) {
1336
1356
  process.stderr.write(`\x1B[2m ready\x1B[0m
1337
1357
  `);
1338
1358
  return true;
@@ -1357,6 +1377,14 @@ async function runSubcommand(subcommand, subArgs, port = "8700", options = {}) {
1357
1377
  filteredArgs = [...filteredArgs.slice(0, windowIdx), ...filteredArgs.slice(windowIdx + 2)];
1358
1378
  }
1359
1379
  if (!await isServerRunning(port)) {
1380
+ try {
1381
+ const quitMarker = join(homedir(), ".haltija", "last-quit");
1382
+ if (!noLaunch && existsSync(quitMarker)) {
1383
+ console.error("Haltija was quit by user; not auto-launching.");
1384
+ console.error("Open Haltija manually to resume — or run `hj --no-launch` to bypass this check.");
1385
+ process.exit(1);
1386
+ }
1387
+ } catch {}
1360
1388
  process.stderr.write("\x1B[2mStarting Haltija server...\x1B[0m");
1361
1389
  const started = await startServerInBackground(port);
1362
1390
  if (started) {
@@ -1687,11 +1715,11 @@ function dim2(s) {
1687
1715
 
1688
1716
  // bin/hj.mjs
1689
1717
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
1690
- import { homedir } from "node:os";
1718
+ import { homedir as homedir2 } from "node:os";
1691
1719
  import { join as join2 } from "node:path";
1692
1720
  var args = process.argv.slice(2);
1693
1721
  function lookupNamedInstance(name) {
1694
- const path = join2(homedir(), ".haltija", "servers", `${name}.json`);
1722
+ const path = join2(homedir2(), ".haltija", "servers", `${name}.json`);
1695
1723
  if (!existsSync2(path))
1696
1724
  return null;
1697
1725
  let entry;
package/dist/index.js CHANGED
@@ -674,7 +674,7 @@ var injectorCode = `
674
674
  `;
675
675
 
676
676
  // src/version.ts
677
- var VERSION = "1.3.0-beta.8";
677
+ var VERSION = "1.3.0-beta.9";
678
678
 
679
679
  // src/embedded-assets.ts
680
680
  var APP_MD = `# Haltija App
@@ -8736,6 +8736,7 @@ async function handleRest(req) {
8736
8736
  serverVersion: SERVER_VERSION,
8737
8737
  recording: activeRecordings > 0,
8738
8738
  activeRecordings,
8739
+ desktopApp: isDesktopApp,
8739
8740
  browsers: browsers.size,
8740
8741
  agents: agents.size,
8741
8742
  mcp: {
package/dist/server.js CHANGED
@@ -674,7 +674,7 @@ var injectorCode = `
674
674
  `;
675
675
 
676
676
  // src/version.ts
677
- var VERSION = "1.3.0-beta.8";
677
+ var VERSION = "1.3.0-beta.9";
678
678
 
679
679
  // src/embedded-assets.ts
680
680
  var APP_MD = `# Haltija App
@@ -8736,6 +8736,7 @@ async function handleRest(req) {
8736
8736
  serverVersion: SERVER_VERSION,
8737
8737
  recording: activeRecordings > 0,
8738
8738
  activeRecordings,
8739
+ desktopApp: isDesktopApp,
8739
8740
  browsers: browsers.size,
8740
8741
  agents: agents.size,
8741
8742
  mcp: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haltija",
3
- "version": "1.3.0-beta.8",
3
+ "version": "1.3.0-beta.9",
4
4
  "description": "Browser control for AI agents - query DOM, click, type, run JS, watch mutations",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",