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.
- package/lib/cli.cjs +53 -7
- 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
|
-
|
|
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
|
|
70
|
-
if (!
|
|
71
|
-
|
|
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 = {
|
|
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.
|
|
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.
|
|
18
|
-
"octiqflow-darwin-x64": "0.2.
|
|
19
|
-
"octiqflow-linux-x64": "0.2.
|
|
20
|
-
"@kyson8101/octiqflow-win32-x64": "0.2.
|
|
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",
|