promptlock-cli 1.0.0 → 1.0.2

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 (3) hide show
  1. package/README.md +25 -0
  2. package/bundle/cli.js +51 -23
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -16,6 +16,31 @@ npx promptlock-cli connect
16
16
  That detects your agents, opens a pairing window for the phone, installs the
17
17
  hooks, and offers to keep a small background daemon running across reboots.
18
18
 
19
+ ### Updating from 1.0.0 or 1.0.1 when the phone finds no accessories
20
+
21
+ Version 1.0.2 keeps the Bluetooth name within eight ASCII bytes so the name and
22
+ service UUID fit together in the initial advertisement. Longer Mac names get a
23
+ stable label such as `PL12AB34`. The pairing command prints the label to look for;
24
+ you must still compare and confirm the six digits on both devices.
25
+
26
+ Close any earlier `connect`, `pair`, or foreground `start` command with Ctrl-C.
27
+ Then update the background helper before pairing:
28
+
29
+ ```
30
+ npx promptlock-cli@1.0.2 service install
31
+ npx promptlock-cli@1.0.2 pair
32
+ ```
33
+
34
+ Leave `pair` running while opening **Pair Mac** on the iPhone. This change does
35
+ not require a new iPhone build. Simply running a newer `pair` command against an
36
+ older running helper does not update its Bluetooth advertisement.
37
+
38
+ If discovery still fails, run `npx promptlock-cli@1.0.2 status` in a second
39
+ terminal. The `ble.localName` field identifies the updated helper's requested
40
+ advertising name. `advertising` means macOS accepted the request; it does not
41
+ prove that the phone received the complete advertisement. `centrals: 0` counts
42
+ no current subscribers, not the phone's entire connection history.
43
+
19
44
  ## Commands
20
45
 
21
46
  ```
package/bundle/cli.js CHANGED
@@ -4077,8 +4077,15 @@ function timingSafeEqualStrings(a, b) {
4077
4077
  return crypto7.timingSafeEqual(x, y);
4078
4078
  }
4079
4079
 
4080
+ // src/transport/ble-name.ts
4081
+ var import_node_crypto = require("node:crypto");
4082
+ var import_node_os = require("node:os");
4083
+ function bleAdvertisementName(name = (0, import_node_os.hostname)().replace(/\.local$/i, "")) {
4084
+ if (/^[\x21-\x7e][\x20-\x7e]{0,7}$/.test(name)) return name;
4085
+ return `PL${(0, import_node_crypto.createHash)("sha256").update(name).digest("hex").slice(0, 6).toUpperCase()}`;
4086
+ }
4087
+
4080
4088
  // src/transport/ble-publisher.ts
4081
- var os5 = __toESM(require("node:os"));
4082
4089
  var import_core9 = __toESM(require_dist());
4083
4090
 
4084
4091
  // src/pairing-service.ts
@@ -4615,6 +4622,7 @@ var PairingService = class {
4615
4622
  this.send({
4616
4623
  e: "opened",
4617
4624
  windowMs: this.opts.windowMs ?? import_core8.PAIRING.windowMs,
4625
+ localName: this.opts.localName,
4618
4626
  bluetooth: bt.state,
4619
4627
  reason: bt.reason
4620
4628
  });
@@ -4631,6 +4639,7 @@ var PairingService = class {
4631
4639
  this.send({
4632
4640
  e: "opened",
4633
4641
  windowMs: this.window.remainingMs,
4642
+ localName: this.opts.localName,
4634
4643
  bluetooth: this.opts.bluetoothState?.().state ?? "unknown",
4635
4644
  reason: this.opts.bluetoothState?.().reason ?? null
4636
4645
  });
@@ -5428,7 +5437,11 @@ var BlePublisher = class {
5428
5437
  return this.pairing;
5429
5438
  }
5430
5439
  advertisedName() {
5431
- return this.opts.localName ?? os5.hostname().replace(/\.local$/i, "");
5440
+ return bleAdvertisementName(this.opts.localName);
5441
+ }
5442
+ /** Requested on-air name; a remote scan is still needed to verify reception. */
5443
+ get localName() {
5444
+ return this.advertisedName();
5432
5445
  }
5433
5446
  /**
5434
5447
  * Where the wifi link lives, computed the same way `LanServer.url` computes
@@ -5771,7 +5784,7 @@ var BlePublisher = class {
5771
5784
  // src/health.ts
5772
5785
  var crypto13 = __toESM(require("node:crypto"));
5773
5786
  var fs13 = __toESM(require("node:fs"));
5774
- var os11 = __toESM(require("node:os"));
5787
+ var os10 = __toESM(require("node:os"));
5775
5788
  var path13 = __toESM(require("node:path"));
5776
5789
  var import_core11 = __toESM(require_dist());
5777
5790
 
@@ -5783,7 +5796,7 @@ var path12 = __toESM(require("node:path"));
5783
5796
  var import_node_child_process6 = require("node:child_process");
5784
5797
  var crypto10 = __toESM(require("node:crypto"));
5785
5798
  var http2 = __toESM(require("node:http"));
5786
- var os6 = __toESM(require("node:os"));
5799
+ var os5 = __toESM(require("node:os"));
5787
5800
  var PairingError = class extends Error {
5788
5801
  constructor(failure, message) {
5789
5802
  super(message);
@@ -5812,7 +5825,7 @@ function openWithFinder(url) {
5812
5825
  async function pairWithRelay(opts) {
5813
5826
  const log = opts.log ?? ((m) => console.log(m));
5814
5827
  const base = opts.relayUrl.replace(/\/+$/, "");
5815
- const deviceName = opts.deviceName ?? os6.hostname();
5828
+ const deviceName = opts.deviceName ?? os5.hostname();
5816
5829
  const timeoutMs = opts.timeoutMs ?? 5 * 6e4;
5817
5830
  const { verifier, challenge } = createPkcePair();
5818
5831
  const state = crypto10.randomBytes(16).toString("base64url");
@@ -5935,12 +5948,12 @@ var path9 = __toESM(require("node:path"));
5935
5948
  // src/service/launchd.ts
5936
5949
  var import_node_child_process8 = require("node:child_process");
5937
5950
  var fs10 = __toESM(require("node:fs"));
5938
- var os8 = __toESM(require("node:os"));
5951
+ var os7 = __toESM(require("node:os"));
5939
5952
 
5940
5953
  // src/service/runtime.ts
5941
5954
  var import_node_child_process7 = require("node:child_process");
5942
5955
  var fs9 = __toESM(require("node:fs"));
5943
- var os7 = __toESM(require("node:os"));
5956
+ var os6 = __toESM(require("node:os"));
5944
5957
  var path8 = __toESM(require("node:path"));
5945
5958
  function shq(value) {
5946
5959
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -5951,7 +5964,7 @@ function nodeVersionOf(binary) {
5951
5964
  encoding: "utf8",
5952
5965
  timeout: 1e4,
5953
5966
  stdio: ["ignore", "pipe", "ignore"],
5954
- env: { PATH: "/usr/bin:/bin:/usr/sbin:/sbin", HOME: os7.homedir() }
5967
+ env: { PATH: "/usr/bin:/bin:/usr/sbin:/sbin", HOME: os6.homedir() }
5955
5968
  });
5956
5969
  const version = out.trim();
5957
5970
  return /^v\d+\./.test(version) ? version : null;
@@ -6075,7 +6088,7 @@ fi
6075
6088
  exec "$NODE" "$SCRIPT" start
6076
6089
  `;
6077
6090
  }
6078
- function fallbackNodePaths(home = os7.homedir()) {
6091
+ function fallbackNodePaths(home = os6.homedir()) {
6079
6092
  return [
6080
6093
  "/opt/homebrew/bin/node",
6081
6094
  "/usr/local/bin/node",
@@ -6084,10 +6097,14 @@ function fallbackNodePaths(home = os7.homedir()) {
6084
6097
  path8.join(home, ".local", "bin", "node")
6085
6098
  ];
6086
6099
  }
6100
+ function defaultScriptPath() {
6101
+ const candidates = [path8.join(__dirname, "..", "cli.js"), path8.join(__dirname, "cli.js")];
6102
+ return candidates.find((p) => fs9.existsSync(p)) ?? candidates[0];
6103
+ }
6087
6104
  function installRuntime(opts = {}) {
6088
6105
  const root = opts.dir ?? RUNTIME_DIR;
6089
6106
  const bin = path8.join(root, "bin");
6090
- const scriptPath = opts.scriptPath ?? path8.join(__dirname, "..", "cli.js");
6107
+ const scriptPath = opts.scriptPath ?? defaultScriptPath();
6091
6108
  if (!fs9.existsSync(scriptPath)) {
6092
6109
  throw new Error(`the daemon is not built: ${scriptPath} is missing. Run \`npm run build\` first.`);
6093
6110
  }
@@ -6203,7 +6220,7 @@ function plistXml(opts) {
6203
6220
  <string>Background</string>
6204
6221
 
6205
6222
  <key>WorkingDirectory</key>
6206
- <string>${escapeXml(os8.homedir())}</string>
6223
+ <string>${escapeXml(os7.homedir())}</string>
6207
6224
 
6208
6225
  <!-- Only what the daemon writes before it opens its own rotated log. -->
6209
6226
  <key>StandardOutPath</key>
@@ -6435,7 +6452,7 @@ async function cmdServiceStatus(log = (m) => console.log(m)) {
6435
6452
 
6436
6453
  // src/pairing.ts
6437
6454
  var http3 = __toESM(require("node:http"));
6438
- var os9 = __toESM(require("node:os"));
6455
+ var os8 = __toESM(require("node:os"));
6439
6456
  async function readStatus(port, secret, timeoutMs = 1500) {
6440
6457
  return new Promise((resolve2) => {
6441
6458
  const req = http3.request(
@@ -6484,7 +6501,12 @@ var LocalPairing = class {
6484
6501
  if (existing !== null) {
6485
6502
  const url = typeof existing.lan === "string" ? existing.lan : null;
6486
6503
  const ble = existing.ble;
6487
- return this.target(url, false, ble ? ble.state !== "unavailable" : false);
6504
+ return this.target(
6505
+ url,
6506
+ false,
6507
+ ble ? ble.state !== "unavailable" : false,
6508
+ ble?.localName ?? os8.hostname().replace(/\.local$/i, "")
6509
+ );
6488
6510
  }
6489
6511
  if (!this.startIfDown) return this.target(null, false, bleSupported());
6490
6512
  const bridge = new Bridge({ config: this.config, persist: false, log: this.log, ble: true });
@@ -6537,7 +6559,7 @@ var LocalPairing = class {
6537
6559
  * `packages/core/src/link.ts`, and a QR is not where a protocol constant
6538
6560
  * belongs.
6539
6561
  */
6540
- target(url, startedDaemon, ble) {
6562
+ target(url, startedDaemon, ble, localName = bleLocalName()) {
6541
6563
  const resolved = url ?? `ws://127.0.0.1:${this.config.lanPort}`;
6542
6564
  const params = [
6543
6565
  `url=${encodeURIComponent(resolved)}`,
@@ -6545,7 +6567,7 @@ var LocalPairing = class {
6545
6567
  ];
6546
6568
  if (ble) {
6547
6569
  params.push("ble=1");
6548
- params.push(`blename=${encodeURIComponent(bleLocalName())}`);
6570
+ params.push(`blename=${encodeURIComponent(localName)}`);
6549
6571
  }
6550
6572
  return {
6551
6573
  transport: "lan",
@@ -6564,7 +6586,7 @@ var LocalPairing = class {
6564
6586
  }
6565
6587
  };
6566
6588
  function bleLocalName() {
6567
- return os9.hostname().replace(/\.local$/i, "");
6589
+ return bleAdvertisementName();
6568
6590
  }
6569
6591
 
6570
6592
  // src/pairing-cli.ts
@@ -6606,6 +6628,7 @@ async function runPairingWindow(opts) {
6606
6628
  const onEvent = (event) => {
6607
6629
  switch (event.e) {
6608
6630
  case "opened":
6631
+ if (event.localName) log(` Look for ${event.localName} in the iPhone's accessory picker.`);
6609
6632
  if (event.bluetooth !== "advertising" && event.bluetooth !== "starting") {
6610
6633
  log(` Bluetooth: ${event.bluetooth}${event.reason ? ` \u2014 ${event.reason}` : ""}`);
6611
6634
  }
@@ -6729,7 +6752,7 @@ function restartManagedService(log) {
6729
6752
 
6730
6753
  // src/connection-test.ts
6731
6754
  var crypto12 = __toESM(require("node:crypto"));
6732
- var os10 = __toESM(require("node:os"));
6755
+ var os9 = __toESM(require("node:os"));
6733
6756
  var path11 = __toESM(require("node:path"));
6734
6757
 
6735
6758
  // src/simulate.ts
@@ -6965,7 +6988,7 @@ async function runConnectionTest(opts) {
6965
6988
  const log = opts.log ?? ((m) => console.log(m));
6966
6989
  const speed = opts.speedFactor ?? 1;
6967
6990
  const sessionId = `warpfocus-test-${crypto12.randomUUID().slice(0, 8)}`;
6968
- const workspaceRoot = path11.join(os10.tmpdir(), TEST_PROJECT_NAME);
6991
+ const workspaceRoot = path11.join(os9.tmpdir(), TEST_PROJECT_NAME);
6969
6992
  const problems = [];
6970
6993
  let delivered = true;
6971
6994
  let phones = null;
@@ -7856,7 +7879,7 @@ function untrusted(agent) {
7856
7879
  return trust === "untrusted" || trust === "partial";
7857
7880
  }
7858
7881
  function macId() {
7859
- return crypto13.createHash("sha256").update(`warpfocus:${os11.hostname()}:${os11.userInfo().username}`).digest("hex").slice(0, 32);
7882
+ return crypto13.createHash("sha256").update(`warpfocus:${os10.hostname()}:${os10.userInfo().username}`).digest("hex").slice(0, 32);
7860
7883
  }
7861
7884
  function daemonVersion() {
7862
7885
  try {
@@ -7886,7 +7909,7 @@ function deviceHealthFrom(facts, opts) {
7886
7909
  };
7887
7910
  }
7888
7911
  function hostLabel() {
7889
- const raw = os11.hostname().replace(/\.local$/i, "").trim();
7912
+ const raw = os10.hostname().replace(/\.local$/i, "").trim();
7890
7913
  return raw.length > 0 ? raw : null;
7891
7914
  }
7892
7915
  function healthChanged(a, b) {
@@ -8180,7 +8203,12 @@ var Bridge = class {
8180
8203
  receiver: this.receiver.address,
8181
8204
  lan: this.lan.url,
8182
8205
  cloud: this.cloud === null ? null : { url: this.cloud.url, state: this.cloud.state },
8183
- ble: this.ble === null ? null : { state: this.ble.state, reason: this.ble.reason, centrals: this.ble.centralCount },
8206
+ ble: this.ble === null ? null : {
8207
+ state: this.ble.state,
8208
+ reason: this.ble.reason,
8209
+ centrals: this.ble.centralCount,
8210
+ localName: this.ble.localName
8211
+ },
8184
8212
  health: this.health,
8185
8213
  // How many phones are attached. `connect` reads this to decide whether it
8186
8214
  // can honestly claim the connection test was observed.
@@ -8413,7 +8441,7 @@ var Bridge = class {
8413
8441
 
8414
8442
  // src/doctor.ts
8415
8443
  var crypto17 = __toESM(require("node:crypto"));
8416
- var os12 = __toESM(require("node:os"));
8444
+ var os11 = __toESM(require("node:os"));
8417
8445
  var path16 = __toESM(require("node:path"));
8418
8446
 
8419
8447
  // src/simulate-claude-code.ts
@@ -9088,7 +9116,7 @@ function formatRow(row) {
9088
9116
  }
9089
9117
  async function roundTrip(provider, port, secret) {
9090
9118
  const tag = `warpfocus-doctor-${crypto17.randomUUID().slice(0, 8)}`;
9091
- const root = path16.join(os12.tmpdir(), tag);
9119
+ const root = path16.join(os11.tmpdir(), tag);
9092
9120
  const sessionId = tag;
9093
9121
  const build = (cursorStep, claudeStep, codexStep) => {
9094
9122
  if (provider === "cursor") return cursorPayload(cursorStep, {}, { sessionId, workspaceRoot: root });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptlock-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "PromptLock for the Mac: watches Cursor, Claude Code and Codex, and tells your iPhone when an agent is working so it can unlock your apps only while one is.",
5
5
  "license": "MIT",
6
6
  "author": "Mark Guggenheim",