@theharshitsingh/ao 0.10.0
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 +26 -0
- package/bin/ao.js +36 -0
- package/install.js +23 -0
- package/lib/bootstrap.js +163 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Agent Orchestrator (npm bootstrapper)
|
|
2
|
+
|
|
3
|
+
Install the Agent Orchestrator desktop app from npm:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i -g @theharshitsingh/ao
|
|
7
|
+
ao
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
`npm i -g` installs a small **bootstrapper**, not the app itself. On install it
|
|
11
|
+
downloads the signed desktop bundle for your platform from the matching GitHub
|
|
12
|
+
Release, verifies its SHA-256, and unpacks it. Running `ao` launches the app.
|
|
13
|
+
|
|
14
|
+
This is a one-time delivery channel. Once the app is installed it updates itself
|
|
15
|
+
through its built-in updater. `npm update` is **not** the update path.
|
|
16
|
+
|
|
17
|
+
## Supported platforms
|
|
18
|
+
|
|
19
|
+
This build ships `darwin-arm64` (Apple Silicon). Other platforms fail the install
|
|
20
|
+
with a pointer to the [Releases page](https://github.com/aoagents/agent-orchestrator/releases),
|
|
21
|
+
where direct installers (`.dmg` / `.deb` / `.exe`) live.
|
|
22
|
+
|
|
23
|
+
## `--ignore-scripts`
|
|
24
|
+
|
|
25
|
+
If you install with `npm install --ignore-scripts`, the postinstall download is
|
|
26
|
+
skipped; the first `ao` run downloads the bundle instead. Either way it works.
|
package/bin/ao.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// `ao`: open the Agent Orchestrator desktop app. Self-healing: if the bundle is
|
|
5
|
+
// missing (e.g. installed with --ignore-scripts, or postinstall deferred the
|
|
6
|
+
// download), fetch it first, then launch. The app is the product; this is just
|
|
7
|
+
// the launcher.
|
|
8
|
+
|
|
9
|
+
const { spawn } = require("node:child_process");
|
|
10
|
+
const { ensureBundle, appLaunchPath } = require("../lib/bootstrap");
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
await ensureBundle();
|
|
14
|
+
const app = appLaunchPath();
|
|
15
|
+
|
|
16
|
+
let child;
|
|
17
|
+
if (process.platform === "darwin") {
|
|
18
|
+
// `open` hands the .app to LaunchServices so it runs as a normal GUI app.
|
|
19
|
+
child = spawn("open", [app, ...process.argv.slice(2)], {
|
|
20
|
+
detached: true,
|
|
21
|
+
stdio: "ignore",
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
// linux: launch the packaged executable directly.
|
|
25
|
+
child = spawn(`${app}/agent-orchestrator`, process.argv.slice(2), {
|
|
26
|
+
detached: true,
|
|
27
|
+
stdio: "ignore",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
child.unref();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
main().catch((err) => {
|
|
34
|
+
console.error(err.message);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
package/install.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// postinstall: fetch the platform bundle eagerly so the first `ao` is instant.
|
|
4
|
+
// On an unsupported platform we fail loudly and non-zero (no silent half-install);
|
|
5
|
+
// on a transient download failure we DON'T fail the npm install, because the `ao`
|
|
6
|
+
// launcher will retry the fetch on first run (this also covers --ignore-scripts).
|
|
7
|
+
|
|
8
|
+
const { ensureBundle, unsupportedError, assetName } = require("./lib/bootstrap");
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
if (!assetName()) {
|
|
12
|
+
console.error(String(unsupportedError().message));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
await ensureBundle();
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.error(`Agent Orchestrator: deferred download to first run (${err.message})`);
|
|
19
|
+
// Non-fatal: bin/ao.js will fetch on first launch.
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
main();
|
package/lib/bootstrap.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Shared bootstrapper logic: detect platform -> map to a GitHub Release asset ->
|
|
4
|
+
// download -> verify SHA-256 -> extract -> expose the app path. Used by both the
|
|
5
|
+
// postinstall (install.js) and the `ao` launcher (bin/ao.js), so a user who
|
|
6
|
+
// installed with --ignore-scripts still gets a working app on first run.
|
|
7
|
+
//
|
|
8
|
+
// Zero runtime dependencies: Node 20+ global fetch + built-ins only.
|
|
9
|
+
|
|
10
|
+
const fs = require("node:fs");
|
|
11
|
+
const path = require("node:path");
|
|
12
|
+
const crypto = require("node:crypto");
|
|
13
|
+
const { spawnSync } = require("node:child_process");
|
|
14
|
+
const { Readable } = require("node:stream");
|
|
15
|
+
const { pipeline } = require("node:stream/promises");
|
|
16
|
+
|
|
17
|
+
const pkg = require("../package.json");
|
|
18
|
+
const cfg = pkg.ao;
|
|
19
|
+
|
|
20
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
21
|
+
const vendorDir = path.join(packageRoot, "vendor");
|
|
22
|
+
|
|
23
|
+
function platformKey() {
|
|
24
|
+
return `${process.platform}-${process.arch}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function assetName() {
|
|
28
|
+
return cfg.assets[platformKey()];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function releasesPage() {
|
|
32
|
+
return `https://github.com/${cfg.releaseRepo}/releases`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function downloadUrl(name) {
|
|
36
|
+
return `https://github.com/${cfg.releaseRepo}/releases/download/${cfg.releaseTag}/${name}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The unpacked app bundle's launch target, per OS. Only darwin ships an asset
|
|
40
|
+
// today; linux is mapped for when its archive lands.
|
|
41
|
+
function appLaunchPath() {
|
|
42
|
+
if (process.platform === "darwin") {
|
|
43
|
+
return path.join(vendorDir, "Agent Orchestrator.app");
|
|
44
|
+
}
|
|
45
|
+
// linux: the maker-zip/tar lays the packaged app dir down directly.
|
|
46
|
+
return path.join(vendorDir, "agent-orchestrator");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isInstalled() {
|
|
50
|
+
return fs.existsSync(appLaunchPath());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function unsupportedError() {
|
|
54
|
+
return new Error(
|
|
55
|
+
`Agent Orchestrator has no npm bundle for ${platformKey()}.\n` +
|
|
56
|
+
`Install the desktop app directly instead: ${releasesPage()}`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Parse a `<sha256> <filename>` line out of a SHA256SUMS file.
|
|
61
|
+
function expectedSha(sumsText, name) {
|
|
62
|
+
for (const line of sumsText.split("\n")) {
|
|
63
|
+
const m = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
|
|
64
|
+
if (m && path.basename(m[2]) === name) return m[1].toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function fetchWithRetry(url, tries = 3) {
|
|
70
|
+
let lastErr;
|
|
71
|
+
for (let i = 0; i < tries; i++) {
|
|
72
|
+
try {
|
|
73
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
74
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} for ${url}`);
|
|
75
|
+
return res;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
lastErr = err;
|
|
78
|
+
// ponytail: linear backoff, fine for a 3-shot install download.
|
|
79
|
+
await new Promise((r) => setTimeout(r, 750 * (i + 1)));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
throw lastErr;
|
|
83
|
+
}
|
|
84
|
+
// ponytail: no HTTP(S)_PROXY plumbing yet. Add an undici ProxyAgent dispatcher
|
|
85
|
+
// here if corp-proxy installs need it; the GitHub CDN is reachable directly for
|
|
86
|
+
// the common case.
|
|
87
|
+
|
|
88
|
+
async function downloadToFile(url, dest) {
|
|
89
|
+
const res = await fetchWithRetry(url);
|
|
90
|
+
await pipeline(Readable.fromWeb(res.body), fs.createWriteStream(dest));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function sha256File(file) {
|
|
94
|
+
const hash = crypto.createHash("sha256");
|
|
95
|
+
hash.update(fs.readFileSync(file));
|
|
96
|
+
return hash.digest("hex");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function extract(archive, dest) {
|
|
100
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
101
|
+
if (archive.endsWith(".zip")) {
|
|
102
|
+
// ditto preserves the code signature / xattrs of the signed .app better
|
|
103
|
+
// than unzip. Present on every macOS.
|
|
104
|
+
const tool = process.platform === "darwin" ? "ditto" : "unzip";
|
|
105
|
+
const args =
|
|
106
|
+
tool === "ditto" ? ["-x", "-k", archive, dest] : ["-q", archive, "-d", dest];
|
|
107
|
+
const r = spawnSync(tool, args, { stdio: "inherit" });
|
|
108
|
+
if (r.status !== 0) throw new Error(`extract failed (${tool} exit ${r.status})`);
|
|
109
|
+
} else {
|
|
110
|
+
const r = spawnSync("tar", ["-xzf", archive, "-C", dest], { stdio: "inherit" });
|
|
111
|
+
if (r.status !== 0) throw new Error(`extract failed (tar exit ${r.status})`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Idempotent: returns the app path, downloading+verifying+extracting only if the
|
|
116
|
+
// bundle is not already present. Throws loudly (never half-installs) on any
|
|
117
|
+
// unsupported platform, missing asset, or checksum mismatch.
|
|
118
|
+
async function ensureBundle({ quiet = false } = {}) {
|
|
119
|
+
if (isInstalled()) return appLaunchPath();
|
|
120
|
+
|
|
121
|
+
const name = assetName();
|
|
122
|
+
if (!name) throw unsupportedError();
|
|
123
|
+
|
|
124
|
+
const log = quiet ? () => {} : (m) => console.error(m);
|
|
125
|
+
const tmp = fs.mkdtempSync(path.join(require("node:os").tmpdir(), "ao-dl-"));
|
|
126
|
+
const archive = path.join(tmp, name);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
log(`Agent Orchestrator: downloading ${name} ...`);
|
|
130
|
+
await downloadToFile(downloadUrl(name), archive);
|
|
131
|
+
|
|
132
|
+
log("Agent Orchestrator: verifying checksum ...");
|
|
133
|
+
const sumsRes = await fetchWithRetry(downloadUrl(cfg.checksums));
|
|
134
|
+
const want = expectedSha(await sumsRes.text(), name);
|
|
135
|
+
if (!want) throw new Error(`no ${name} entry in ${cfg.checksums}`);
|
|
136
|
+
const got = sha256File(archive);
|
|
137
|
+
if (got !== want) {
|
|
138
|
+
throw new Error(`checksum mismatch for ${name}\n expected ${want}\n got ${got}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
log("Agent Orchestrator: unpacking ...");
|
|
142
|
+
fs.rmSync(vendorDir, { recursive: true, force: true });
|
|
143
|
+
extract(archive, vendorDir);
|
|
144
|
+
if (!isInstalled()) {
|
|
145
|
+
throw new Error(`unpacked archive did not contain the expected app at ${appLaunchPath()}`);
|
|
146
|
+
}
|
|
147
|
+
log("Agent Orchestrator: ready.");
|
|
148
|
+
return appLaunchPath();
|
|
149
|
+
} finally {
|
|
150
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
ensureBundle,
|
|
156
|
+
isInstalled,
|
|
157
|
+
appLaunchPath,
|
|
158
|
+
assetName,
|
|
159
|
+
platformKey,
|
|
160
|
+
expectedSha,
|
|
161
|
+
releasesPage,
|
|
162
|
+
unsupportedError,
|
|
163
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theharshitsingh/ao",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Bootstrapper for the Agent Orchestrator desktop app. Downloads the signed app for your platform and launches it.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/aoagents/agent-orchestrator",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ao": "bin/ao.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"postinstall": "node install.js",
|
|
12
|
+
"test": "node test/selfcheck.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"bin/",
|
|
16
|
+
"lib/",
|
|
17
|
+
"install.js",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"ao": {
|
|
27
|
+
"releaseRepo": "harshitsinghbhandari/agent-orchestrator",
|
|
28
|
+
"releaseTag": "npm-bootstrap-0.10.0",
|
|
29
|
+
"checksums": "SHA256SUMS",
|
|
30
|
+
"assets": {
|
|
31
|
+
"darwin-arm64": "agent-orchestrator-darwin-arm64.zip"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|