@abitat_reece/cli 0.1.0 → 0.1.1

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/auth.js CHANGED
@@ -73,12 +73,21 @@ async function pollDeviceLogin(apiUrl, deviceLoginId, fetchFn) {
73
73
  method: "POST",
74
74
  headers: { "content-type": "application/json" },
75
75
  body: JSON.stringify({ deviceLoginId })
76
- });
76
+ }).catch(() => null);
77
+ if (!response) {
78
+ return { status: "pending" };
79
+ }
77
80
  if (!response.ok) {
81
+ if (isTransientPollStatus(response.status)) {
82
+ return { status: "pending" };
83
+ }
78
84
  throw new Error(`Unable to poll CLI login (${response.status})`);
79
85
  }
80
86
  return parsePollResponse(await response.json());
81
87
  }
88
+ function isTransientPollStatus(status) {
89
+ return status === 429 || status === 502 || status === 503 || status === 504;
90
+ }
82
91
  function parseStartResponse(value) {
83
92
  const candidate = value;
84
93
  if (typeof candidate.code !== "string" ||
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
3
  import { realpathSync } from "node:fs";
4
+ import { connect } from "node:net";
4
5
  import { homedir, hostname } from "node:os";
5
6
  import { resolve } from "node:path";
6
7
  import { fileURLToPath } from "node:url";
@@ -18,6 +19,7 @@ export function parseCommand(args) {
18
19
  export async function runCli(args, input = {}) {
19
20
  const parsed = parseCommand(args);
20
21
  const env = input.env ?? process.env;
22
+ const isEndpointListening = input.isEndpointListening ?? isTcpEndpointListening;
21
23
  const output = input.output ?? console.log;
22
24
  const openUrl = input.openUrl ?? openUrlInBrowser;
23
25
  const homeDir = input.homeDir ?? homedir();
@@ -64,6 +66,11 @@ export async function runCli(args, input = {}) {
64
66
  session
65
67
  });
66
68
  for (const process of result.startupPlan) {
69
+ const existingServerUrl = codexAppServerListenUrl(process);
70
+ if (existingServerUrl && (await isEndpointListening(existingServerUrl))) {
71
+ output(`Using existing Codex app server at ${existingServerUrl}`);
72
+ continue;
73
+ }
67
74
  (input.startProcess ?? startProcess)(process);
68
75
  }
69
76
  openUrl(session.apiUrl);
@@ -71,6 +78,34 @@ export async function runCli(args, input = {}) {
71
78
  output(`Open the iPhone app and pair from ${session.apiUrl}`);
72
79
  return 0;
73
80
  }
81
+ function codexAppServerListenUrl(process) {
82
+ if (process.name !== "codex-app-server") {
83
+ return null;
84
+ }
85
+ const listenIndex = process.args.indexOf("--listen");
86
+ return listenIndex >= 0 ? (process.args[listenIndex + 1] ?? null) : null;
87
+ }
88
+ function isTcpEndpointListening(url, timeoutMs = 250) {
89
+ return new Promise((resolve) => {
90
+ let settled = false;
91
+ const endpoint = new URL(url);
92
+ const socket = connect({
93
+ host: endpoint.hostname,
94
+ port: Number(endpoint.port || (endpoint.protocol === "wss:" ? 443 : 80))
95
+ });
96
+ const finish = (listening) => {
97
+ if (settled) {
98
+ return;
99
+ }
100
+ settled = true;
101
+ socket.destroy();
102
+ resolve(listening);
103
+ };
104
+ socket.setTimeout(timeoutMs, () => finish(false));
105
+ socket.on("connect", () => finish(true));
106
+ socket.on("error", () => finish(false));
107
+ });
108
+ }
74
109
  function startProcess(process) {
75
110
  const resolved = resolveStartupProcess(process);
76
111
  spawn(resolved.command, resolved.args, {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@abitat_reece/cli",
3
3
  "private": false,
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "description": "Remote Codex control from Mac and iPhone",
6
6
  "type": "module",
7
7
  "bin": {
@@ -15,7 +15,7 @@
15
15
  "node": ">=22"
16
16
  },
17
17
  "dependencies": {
18
- "@abitat_reece/host-daemon": "0.1.0"
18
+ "@abitat_reece/host-daemon": "0.1.1"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/node": "25.6.0",