octiqflow 0.2.9 → 0.2.10

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 (2) hide show
  1. package/lib/cli.cjs +53 -7
  2. package/package.json +5 -5
package/lib/cli.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  const fs = require('node:fs');
2
+ const os = require('node:os');
2
3
  const path = require('node:path');
3
4
  const { spawn } = require('node:child_process');
4
5
  const { resolveRuntime, runForeground } = require('./runtime.cjs');
@@ -64,12 +65,47 @@ function parseOptions(args) {
64
65
  return { options, rest };
65
66
  }
66
67
 
67
- async function localUrl(port, fetchImpl = fetch) {
68
+ /** The active profile's data root, resolved as the server does (profile.rs):
69
+ * `~/.octiqflow/config.json` names the base and the active profile. */
70
+ function profileRoot(home = os.homedir()) {
71
+ try {
72
+ const pointer = JSON.parse(fs.readFileSync(path.join(home, '.octiqflow', 'config.json'), 'utf8'));
73
+ const base = String(pointer.base || '').trim();
74
+ const active = String(pointer.active || '').trim();
75
+ if (base && active) return path.join(base, active);
76
+ } catch (_error) {
77
+ // No pointer yet: the server uses the default profile too.
78
+ }
79
+ return path.join(home, '.octiqflow', 'profiles', 'default');
80
+ }
81
+
82
+ /** The browser token, read where the server keeps it.
83
+ *
84
+ * The server hands it out over HTTP (`/token`) only to a Cloudflare Access
85
+ * sign-in: a request from this machine alone could be any local process,
86
+ * agents included. This CLI is the person's own command, run as them, so it
87
+ * reads their profile's `web.json` the way the Connect page tells a person
88
+ * to. `OCTIQ_WEB_TOKEN` wins, as it does for the server. */
89
+ function readToken({ env = process.env, home = os.homedir() } = {}) {
90
+ const fromEnv = String(env.OCTIQ_WEB_TOKEN || '').trim();
91
+ if (fromEnv) return fromEnv;
92
+ const file = path.join(profileRoot(home), 'web.json');
93
+ let token = '';
94
+ try {
95
+ token = String(JSON.parse(fs.readFileSync(file, 'utf8')).token || '').trim();
96
+ } catch (_error) {
97
+ // Reported below with the path, which is what the person needs.
98
+ }
99
+ if (!token) throw new Error(`no token in ${file}`);
100
+ return token;
101
+ }
102
+
103
+ /** The link that signs a browser in, once the server answers. */
104
+ async function localUrl(port, fetchImpl = fetch, readTokenImpl = readToken) {
68
105
  const origin = `http://127.0.0.1:${port}`;
69
- const tokenResponse = await fetchImpl(`${origin}/token`);
70
- if (!tokenResponse.ok) throw new Error(`server answered ${tokenResponse.status} at /token`);
71
- const token = (await tokenResponse.text()).trim();
72
- return `${origin}/?token=${encodeURIComponent(token)}`;
106
+ const health = await fetchImpl(`${origin}/healthz`);
107
+ if (!health.ok) throw new Error(`server answered ${health.status} at /healthz`);
108
+ return `${origin}/?token=${encodeURIComponent(readTokenImpl())}`;
73
109
  }
74
110
 
75
111
  async function waitForUrl(port, options = {}) {
@@ -79,7 +115,7 @@ async function waitForUrl(port, options = {}) {
79
115
  for (let attempt = 0; attempt < attempts; attempt += 1) {
80
116
  try {
81
117
  const response = await fetchImpl(`http://127.0.0.1:${port}/healthz`);
82
- if (response.ok) return localUrl(port, fetchImpl);
118
+ if (response.ok) return localUrl(port, fetchImpl, options.readToken);
83
119
  } catch (_error) {
84
120
  // launchd has accepted the job but the socket is not ready yet.
85
121
  }
@@ -186,4 +222,14 @@ async function main(args, dependencies = {}) {
186
222
  throw new Error(`unknown command: ${command}; run octiqflow --help`);
187
223
  }
188
224
 
189
- module.exports = { HELP, localUrl, main, openBrowser, packageVersion, parseOptions, waitForUrl };
225
+ module.exports = {
226
+ HELP,
227
+ localUrl,
228
+ main,
229
+ openBrowser,
230
+ packageVersion,
231
+ parseOptions,
232
+ profileRoot,
233
+ readToken,
234
+ waitForUrl,
235
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octiqflow",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Agent workflow orchestrator with real browser terminals",
5
5
  "bin": {
6
6
  "octiqflow": "bin/octiqflow.cjs"
@@ -14,10 +14,10 @@
14
14
  "node": ">=18"
15
15
  },
16
16
  "optionalDependencies": {
17
- "octiqflow-darwin-arm64": "0.2.9",
18
- "octiqflow-darwin-x64": "0.2.9",
19
- "octiqflow-linux-x64": "0.2.9",
20
- "@kyson8101/octiqflow-win32-x64": "0.2.9"
17
+ "octiqflow-darwin-arm64": "0.2.10",
18
+ "octiqflow-darwin-x64": "0.2.10",
19
+ "octiqflow-linux-x64": "0.2.10",
20
+ "@kyson8101/octiqflow-win32-x64": "0.2.10"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",