octiqflow 0.1.52
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/README.md +39 -0
- package/bin/octiqflow.cjs +12 -0
- package/lib/cli.cjs +171 -0
- package/lib/runtime.cjs +66 -0
- package/lib/service.cjs +177 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# OctiqFlow
|
|
2
|
+
|
|
3
|
+
OctiqFlow is an agent workflow orchestrator: real terminals, Claude Code,
|
|
4
|
+
Codex, and pi.dev in one browser control plane.
|
|
5
|
+
|
|
6
|
+
## Run now
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx octiqflow
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
This starts OctiqFlow in the foreground and prints the local URL. Node.js 18+
|
|
13
|
+
is the only build/runtime prerequisite; the matching native server and browser
|
|
14
|
+
client are installed automatically for macOS arm64/x64, Linux x64, or Windows
|
|
15
|
+
x64.
|
|
16
|
+
|
|
17
|
+
## Install on macOS
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g octiqflow
|
|
21
|
+
octiqflow install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The second command installs and starts a launchd user service, then opens the
|
|
25
|
+
authenticated local URL. It keeps running after the terminal closes and starts
|
|
26
|
+
again at login.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
octiqflow status
|
|
30
|
+
octiqflow restart
|
|
31
|
+
octiqflow open
|
|
32
|
+
octiqflow uninstall
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Uninstalling the service keeps profiles and transcripts under `~/.octiqflow`.
|
|
36
|
+
Agent CLIs are separate: install any of Claude Code, Codex, or pi.dev that you
|
|
37
|
+
want OctiqFlow to drive.
|
|
38
|
+
|
|
39
|
+
Project and source: <https://github.com/kyson8101/octiq-flow>
|
package/lib/cli.cjs
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { spawn } = require('node:child_process');
|
|
4
|
+
const { resolveRuntime, runForeground } = require('./runtime.cjs');
|
|
5
|
+
const {
|
|
6
|
+
installMacService,
|
|
7
|
+
restartMacService,
|
|
8
|
+
serviceStatus,
|
|
9
|
+
uninstallMacService,
|
|
10
|
+
} = require('./service.cjs');
|
|
11
|
+
|
|
12
|
+
const HELP = `OctiqFlow — agent workflow orchestration in your browser
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
octiqflow Run in the foreground
|
|
16
|
+
octiqflow install [options] Install and start the macOS background service
|
|
17
|
+
octiqflow uninstall Remove the macOS background service
|
|
18
|
+
octiqflow status [options] Check the service and HTTP endpoint
|
|
19
|
+
octiqflow restart Restart the macOS background service
|
|
20
|
+
octiqflow open [options] Open the local browser client
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--port <number> HTTP port (default: 1421)
|
|
24
|
+
--bind <address> Bind address (default: 127.0.0.1)
|
|
25
|
+
--no-open Do not open a browser after installation
|
|
26
|
+
-h, --help Show this help
|
|
27
|
+
-v, --version Show the package version
|
|
28
|
+
|
|
29
|
+
Node.js 18 or newer is required. Claude Code, Codex, and pi.dev are detected
|
|
30
|
+
separately; install whichever agent CLIs you want OctiqFlow to drive.
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
function packageVersion() {
|
|
34
|
+
const manifest = path.join(__dirname, '..', 'package.json');
|
|
35
|
+
return JSON.parse(fs.readFileSync(manifest, 'utf8')).version;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseOptions(args) {
|
|
39
|
+
const options = {
|
|
40
|
+
port: Number(process.env.OCTIQ_WEB_PORT || 1421),
|
|
41
|
+
bind: process.env.OCTIQ_WEB_BIND || '127.0.0.1',
|
|
42
|
+
open: true,
|
|
43
|
+
};
|
|
44
|
+
const rest = [];
|
|
45
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
46
|
+
const value = args[index];
|
|
47
|
+
if (value === '--port') {
|
|
48
|
+
const raw = args[++index];
|
|
49
|
+
if (raw == null) throw new Error('--port needs a value');
|
|
50
|
+
options.port = Number(raw);
|
|
51
|
+
} else if (value === '--bind') {
|
|
52
|
+
const raw = args[++index];
|
|
53
|
+
if (raw == null) throw new Error('--bind needs a value');
|
|
54
|
+
options.bind = raw;
|
|
55
|
+
} else if (value === '--no-open') {
|
|
56
|
+
options.open = false;
|
|
57
|
+
} else {
|
|
58
|
+
rest.push(value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535) {
|
|
62
|
+
throw new Error('--port must be an integer from 1 to 65535');
|
|
63
|
+
}
|
|
64
|
+
return { options, rest };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function localUrl(port, fetchImpl = fetch) {
|
|
68
|
+
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)}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function waitForUrl(port, options = {}) {
|
|
76
|
+
const attempts = options.attempts || 30;
|
|
77
|
+
const delay = options.delay || 250;
|
|
78
|
+
const fetchImpl = options.fetch || fetch;
|
|
79
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetchImpl(`http://127.0.0.1:${port}/healthz`);
|
|
82
|
+
if (response.ok) return localUrl(port, fetchImpl);
|
|
83
|
+
} catch (_error) {
|
|
84
|
+
// launchd has accepted the job but the socket is not ready yet.
|
|
85
|
+
}
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`the service started but did not listen on port ${port}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function openBrowser(url, platform = process.platform) {
|
|
92
|
+
const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open';
|
|
93
|
+
const args = platform === 'win32' ? ['/c', 'start', '', url] : [url];
|
|
94
|
+
const child = spawn(command, args, { detached: true, stdio: 'ignore' });
|
|
95
|
+
child.once('error', () => {});
|
|
96
|
+
child.unref();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function main(args, dependencies = {}) {
|
|
100
|
+
const output = dependencies.output || console;
|
|
101
|
+
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
102
|
+
output.log(HELP.trimEnd());
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
if (args.includes('--version') || args.includes('-v') || args[0] === 'version') {
|
|
106
|
+
output.log(packageVersion());
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const command = args[0] && !args[0].startsWith('-') ? args[0] : 'run';
|
|
111
|
+
const commandArgs = command === 'run' && args[0] !== 'run' ? args : args.slice(1);
|
|
112
|
+
const { options, rest } = parseOptions(commandArgs);
|
|
113
|
+
if (rest.length) throw new Error(`unknown argument: ${rest[0]}`);
|
|
114
|
+
|
|
115
|
+
if (command === 'run') {
|
|
116
|
+
const runtime = (dependencies.resolveRuntime || resolveRuntime)();
|
|
117
|
+
return (dependencies.runForeground || runForeground)(runtime, [], {
|
|
118
|
+
env: {
|
|
119
|
+
OCTIQ_WEB_BIND: options.bind,
|
|
120
|
+
OCTIQ_WEB_PORT: String(options.port),
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (command === 'install') {
|
|
125
|
+
const runtime = (dependencies.resolveRuntime || resolveRuntime)();
|
|
126
|
+
const details = (dependencies.installMacService || installMacService)(runtime, {
|
|
127
|
+
version: packageVersion(),
|
|
128
|
+
bind: options.bind,
|
|
129
|
+
port: options.port,
|
|
130
|
+
});
|
|
131
|
+
const url = await (dependencies.waitForUrl || waitForUrl)(options.port);
|
|
132
|
+
output.log(`OctiqFlow ${packageVersion()} is running as a background service.`);
|
|
133
|
+
output.log(url);
|
|
134
|
+
output.log(`Logs: ${details.logs}`);
|
|
135
|
+
if (options.open) (dependencies.openBrowser || openBrowser)(url);
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
if (command === 'uninstall') {
|
|
139
|
+
(dependencies.uninstallMacService || uninstallMacService)();
|
|
140
|
+
output.log('OctiqFlow background service removed. Your profiles and transcripts were kept.');
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
if (command === 'restart') {
|
|
144
|
+
(dependencies.restartMacService || restartMacService)();
|
|
145
|
+
const url = await (dependencies.waitForUrl || waitForUrl)(options.port);
|
|
146
|
+
output.log(`OctiqFlow restarted: ${url}`);
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
if (command === 'status') {
|
|
150
|
+
const installed = (dependencies.serviceStatus || serviceStatus)();
|
|
151
|
+
let url = null;
|
|
152
|
+
try {
|
|
153
|
+
url = await (dependencies.localUrl || localUrl)(options.port);
|
|
154
|
+
} catch (_error) {
|
|
155
|
+
// Status reports both facts instead of turning an unavailable endpoint
|
|
156
|
+
// into an exception that hides whether launchd owns the service.
|
|
157
|
+
}
|
|
158
|
+
output.log(`Service: ${installed ? 'installed' : 'not installed'}`);
|
|
159
|
+
output.log(`Endpoint: ${url || 'not responding'}`);
|
|
160
|
+
return installed && url ? 0 : 1;
|
|
161
|
+
}
|
|
162
|
+
if (command === 'open') {
|
|
163
|
+
const url = await (dependencies.localUrl || localUrl)(options.port);
|
|
164
|
+
(dependencies.openBrowser || openBrowser)(url);
|
|
165
|
+
output.log(url);
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
throw new Error(`unknown command: ${command}; run octiqflow --help`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = { HELP, localUrl, main, openBrowser, packageVersion, parseOptions, waitForUrl };
|
package/lib/runtime.cjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { spawn } = require('node:child_process');
|
|
4
|
+
|
|
5
|
+
const PLATFORM_PACKAGES = Object.freeze({
|
|
6
|
+
'darwin-arm64': 'octiqflow-darwin-arm64',
|
|
7
|
+
'darwin-x64': 'octiqflow-darwin-x64',
|
|
8
|
+
'linux-x64': 'octiqflow-linux-x64',
|
|
9
|
+
'win32-x64': '@kyson8101/octiqflow-win32-x64',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
function packageFor(platform = process.platform, arch = process.arch) {
|
|
13
|
+
const key = `${platform}-${arch}`;
|
|
14
|
+
const packageName = PLATFORM_PACKAGES[key];
|
|
15
|
+
if (!packageName) {
|
|
16
|
+
const supported = Object.keys(PLATFORM_PACKAGES).join(', ');
|
|
17
|
+
throw new Error(`unsupported platform ${key}; supported platforms: ${supported}`);
|
|
18
|
+
}
|
|
19
|
+
return packageName;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolveRuntime(options = {}) {
|
|
23
|
+
const platform = options.platform || process.platform;
|
|
24
|
+
const arch = options.arch || process.arch;
|
|
25
|
+
const resolve = options.resolve || require.resolve;
|
|
26
|
+
const exists = options.exists || fs.existsSync;
|
|
27
|
+
const packageName = packageFor(platform, arch);
|
|
28
|
+
let manifest;
|
|
29
|
+
try {
|
|
30
|
+
manifest = resolve(`${packageName}/package.json`);
|
|
31
|
+
} catch (_error) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`the runtime package ${packageName} is missing; reinstall without --omit=optional`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const root = path.dirname(manifest);
|
|
38
|
+
const binaryName = platform === 'win32' ? 'octiq-server.exe' : 'octiq-server';
|
|
39
|
+
const binary = path.join(root, 'bin', binaryName);
|
|
40
|
+
const client = path.join(root, 'bin', 'v2');
|
|
41
|
+
if (!exists(binary) || !exists(path.join(client, 'index.html'))) {
|
|
42
|
+
throw new Error(`${packageName} is incomplete; reinstall octiqflow`);
|
|
43
|
+
}
|
|
44
|
+
return { packageName, root, binary, client, platform, arch };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function runForeground(runtime, args = [], options = {}) {
|
|
48
|
+
const spawnImpl = options.spawn || spawn;
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const child = spawnImpl(runtime.binary, args, {
|
|
51
|
+
cwd: process.cwd(),
|
|
52
|
+
env: { ...process.env, OCTIQ_WEB: '1', ...(options.env || {}) },
|
|
53
|
+
stdio: 'inherit',
|
|
54
|
+
});
|
|
55
|
+
child.once('error', reject);
|
|
56
|
+
child.once('exit', (code, signal) => {
|
|
57
|
+
if (signal) {
|
|
58
|
+
process.kill(process.pid, signal);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
resolve(code == null ? 1 : code);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { PLATFORM_PACKAGES, packageFor, resolveRuntime, runForeground };
|
package/lib/service.cjs
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const os = require('node:os');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { spawnSync } = require('node:child_process');
|
|
5
|
+
|
|
6
|
+
const LABEL = 'com.kyson.octiqflow.server';
|
|
7
|
+
|
|
8
|
+
function xml(value) {
|
|
9
|
+
return String(value)
|
|
10
|
+
.replaceAll('&', '&')
|
|
11
|
+
.replaceAll('<', '<')
|
|
12
|
+
.replaceAll('>', '>')
|
|
13
|
+
.replaceAll('"', '"')
|
|
14
|
+
.replaceAll("'", ''');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function servicePaths(home = os.homedir(), version = 'development') {
|
|
18
|
+
const base = path.join(home, '.octiqflow');
|
|
19
|
+
return {
|
|
20
|
+
base,
|
|
21
|
+
logs: path.join(base, 'logs'),
|
|
22
|
+
runtime: path.join(base, 'runtimes', version),
|
|
23
|
+
plist: path.join(home, 'Library', 'LaunchAgents', `${LABEL}.plist`),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function plist({ binary, bind, port, home, shell }) {
|
|
28
|
+
const logs = path.join(home, '.octiqflow', 'logs');
|
|
29
|
+
const searchPath = [
|
|
30
|
+
path.join(home, '.local', 'bin'),
|
|
31
|
+
'/opt/homebrew/bin',
|
|
32
|
+
'/usr/local/bin',
|
|
33
|
+
'/usr/bin',
|
|
34
|
+
'/bin',
|
|
35
|
+
'/usr/sbin',
|
|
36
|
+
'/sbin',
|
|
37
|
+
].join(':');
|
|
38
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
39
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
40
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
41
|
+
<plist version="1.0">
|
|
42
|
+
<dict>
|
|
43
|
+
<key>Label</key><string>${LABEL}</string>
|
|
44
|
+
<key>ProgramArguments</key>
|
|
45
|
+
<array><string>${xml(binary)}</string></array>
|
|
46
|
+
<key>EnvironmentVariables</key>
|
|
47
|
+
<dict>
|
|
48
|
+
<key>OCTIQ_WEB</key><string>1</string>
|
|
49
|
+
<key>OCTIQ_WEB_BIND</key><string>${xml(bind)}</string>
|
|
50
|
+
<key>OCTIQ_WEB_PORT</key><string>${xml(port)}</string>
|
|
51
|
+
<key>PATH</key><string>${xml(searchPath)}</string>
|
|
52
|
+
<key>SHELL</key><string>${xml(shell)}</string>
|
|
53
|
+
</dict>
|
|
54
|
+
<key>RunAtLoad</key><true/>
|
|
55
|
+
<key>KeepAlive</key><true/>
|
|
56
|
+
<key>ThrottleInterval</key><integer>10</integer>
|
|
57
|
+
<key>ProcessType</key><string>Background</string>
|
|
58
|
+
<key>StandardOutPath</key><string>${xml(path.join(logs, 'server.log'))}</string>
|
|
59
|
+
<key>StandardErrorPath</key><string>${xml(path.join(logs, 'server.err.log'))}</string>
|
|
60
|
+
</dict>
|
|
61
|
+
</plist>
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function runLaunchctl(args, options = {}) {
|
|
66
|
+
const run = options.run || spawnSync;
|
|
67
|
+
return run('launchctl', args, { encoding: 'utf8', stdio: options.stdio || 'pipe' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function sleep(milliseconds) {
|
|
71
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function replaceDirectory(source, destination) {
|
|
75
|
+
const parent = path.dirname(destination);
|
|
76
|
+
const temporary = `${destination}.install-${process.pid}-${Date.now()}`;
|
|
77
|
+
fs.mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
78
|
+
fs.cpSync(source, temporary, { recursive: true });
|
|
79
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
80
|
+
fs.renameSync(temporary, destination);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function installMacService(runtime, options = {}) {
|
|
84
|
+
if ((options.platform || process.platform) !== 'darwin') {
|
|
85
|
+
throw new Error('background service installation is currently supported on macOS only');
|
|
86
|
+
}
|
|
87
|
+
const home = options.home || os.homedir();
|
|
88
|
+
const version = options.version || 'development';
|
|
89
|
+
const bind = options.bind || '127.0.0.1';
|
|
90
|
+
const port = Number(options.port || 1421);
|
|
91
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
92
|
+
throw new Error('port must be an integer from 1 to 65535');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const paths = servicePaths(home, version);
|
|
96
|
+
const binDir = path.join(paths.runtime, 'bin');
|
|
97
|
+
fs.mkdirSync(paths.logs, { recursive: true, mode: 0o700 });
|
|
98
|
+
fs.mkdirSync(binDir, { recursive: true, mode: 0o700 });
|
|
99
|
+
const installedBinary = path.join(binDir, 'octiq-server');
|
|
100
|
+
fs.copyFileSync(runtime.binary, installedBinary);
|
|
101
|
+
fs.chmodSync(installedBinary, 0o755);
|
|
102
|
+
replaceDirectory(runtime.client, path.join(binDir, 'v2'));
|
|
103
|
+
|
|
104
|
+
fs.mkdirSync(path.dirname(paths.plist), { recursive: true });
|
|
105
|
+
const body = plist({
|
|
106
|
+
binary: installedBinary,
|
|
107
|
+
bind,
|
|
108
|
+
port,
|
|
109
|
+
home,
|
|
110
|
+
shell: process.env.SHELL || '/bin/zsh',
|
|
111
|
+
});
|
|
112
|
+
const temporaryPlist = `${paths.plist}.install-${process.pid}`;
|
|
113
|
+
fs.writeFileSync(temporaryPlist, body, { mode: 0o600 });
|
|
114
|
+
fs.renameSync(temporaryPlist, paths.plist);
|
|
115
|
+
|
|
116
|
+
const domain = `gui/${options.uid == null ? process.getuid() : options.uid}`;
|
|
117
|
+
runLaunchctl(['bootout', `${domain}/${LABEL}`], options);
|
|
118
|
+
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
119
|
+
if (runLaunchctl(['print', `${domain}/${LABEL}`], options).status !== 0) break;
|
|
120
|
+
sleep(250);
|
|
121
|
+
}
|
|
122
|
+
let started = false;
|
|
123
|
+
let lastError = '';
|
|
124
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
125
|
+
const result = runLaunchctl(['bootstrap', domain, paths.plist], options);
|
|
126
|
+
if (result.status === 0) {
|
|
127
|
+
started = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
lastError = (result.stderr || result.stdout || '').trim();
|
|
131
|
+
sleep(500);
|
|
132
|
+
}
|
|
133
|
+
if (!started) {
|
|
134
|
+
throw new Error(`launchd could not start OctiqFlow${lastError ? `: ${lastError}` : ''}`);
|
|
135
|
+
}
|
|
136
|
+
return { ...paths, binary: installedBinary, bind, port };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function uninstallMacService(options = {}) {
|
|
140
|
+
if ((options.platform || process.platform) !== 'darwin') {
|
|
141
|
+
throw new Error('background service installation is currently supported on macOS only');
|
|
142
|
+
}
|
|
143
|
+
const home = options.home || os.homedir();
|
|
144
|
+
const paths = servicePaths(home);
|
|
145
|
+
const domain = `gui/${options.uid == null ? process.getuid() : options.uid}`;
|
|
146
|
+
runLaunchctl(['bootout', `${domain}/${LABEL}`], options);
|
|
147
|
+
fs.rmSync(paths.plist, { force: true });
|
|
148
|
+
return paths.plist;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function serviceStatus(options = {}) {
|
|
152
|
+
if ((options.platform || process.platform) !== 'darwin') return false;
|
|
153
|
+
const domain = `gui/${options.uid == null ? process.getuid() : options.uid}`;
|
|
154
|
+
return runLaunchctl(['print', `${domain}/${LABEL}`], options).status === 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function restartMacService(options = {}) {
|
|
158
|
+
if ((options.platform || process.platform) !== 'darwin') {
|
|
159
|
+
throw new Error('background service installation is currently supported on macOS only');
|
|
160
|
+
}
|
|
161
|
+
const domain = `gui/${options.uid == null ? process.getuid() : options.uid}`;
|
|
162
|
+
const result = runLaunchctl(['kickstart', '-k', `${domain}/${LABEL}`], options);
|
|
163
|
+
if (result.status !== 0) {
|
|
164
|
+
throw new Error('OctiqFlow service is not installed');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = {
|
|
169
|
+
LABEL,
|
|
170
|
+
installMacService,
|
|
171
|
+
plist,
|
|
172
|
+
restartMacService,
|
|
173
|
+
servicePaths,
|
|
174
|
+
serviceStatus,
|
|
175
|
+
uninstallMacService,
|
|
176
|
+
xml,
|
|
177
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "octiqflow",
|
|
3
|
+
"version": "0.1.52",
|
|
4
|
+
"description": "Agent workflow orchestrator with real browser terminals",
|
|
5
|
+
"bin": {
|
|
6
|
+
"octiqflow": "bin/octiqflow.cjs"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"lib",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"optionalDependencies": {
|
|
17
|
+
"octiqflow-darwin-arm64": "0.1.52",
|
|
18
|
+
"octiqflow-darwin-x64": "0.1.52",
|
|
19
|
+
"octiqflow-linux-x64": "0.1.52",
|
|
20
|
+
"@kyson8101/octiqflow-win32-x64": "0.1.52"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/kyson8101/octiq-flow.git"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/kyson8101/octiq-flow#readme",
|
|
27
|
+
"bugs": "https://github.com/kyson8101/octiq-flow/issues",
|
|
28
|
+
"license": "UNLICENSED",
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
}
|
|
32
|
+
}
|