Skip to content

Commit 414a6ae

Browse files
npm: the gitagent command and binary through @open-gitagent/gitagent
The package installs the prebuilt binary for the platform as an optional dependency (@open-gitagent/gitagent-<os>-<cpu>) and a gitagent command that runs it, so 2.x users update with npm as before. The ./cli export is kept, and the other 2.x exports remain importable as deprecated names that throw with the replacement. The release workflow publishes the platform packages, then the main package, with provenance.
1 parent 6f85e6e commit 414a6ae

13 files changed

Lines changed: 418 additions & 19 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,9 @@ jobs:
8080
node-version: "22"
8181
- name: TypeScript client
8282
working-directory: clients/typescript
83-
run: npm install --no-audit --no-fund && npm run check
83+
run: npm install --no-audit --no-fund --omit=optional && npm run build
84+
- name: The npm launcher runs the binary
85+
working-directory: clients/typescript
86+
run: |
87+
GITAGENT_BIN=../../target/debug/gitagent node bin/gitagent.js --version
88+
node --input-type=module -e "const m = await import('./dist/index.js'); if (typeof m.query !== 'function' || typeof m.loadAgent !== 'function') process.exit(1);"

‎.github/workflows/release.yml‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,44 @@ jobs:
4949
env:
5050
GH_TOKEN: ${{ github.token }}
5151
run: gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "$GITHUB_REF_NAME" --generate-notes
52+
53+
npm:
54+
needs: publish
55+
runs-on: ubuntu-latest
56+
permissions:
57+
contents: read
58+
id-token: write
59+
steps:
60+
- uses: actions/checkout@v4
61+
- uses: actions/download-artifact@v4
62+
with:
63+
path: dist
64+
merge-multiple: true
65+
- uses: actions/setup-node@v4
66+
with:
67+
node-version: "22"
68+
registry-url: "https://registry.npmjs.org"
69+
- name: Platform packages
70+
run: scripts/npm-packages.sh "$GITHUB_REF_NAME"
71+
- name: Main package
72+
working-directory: clients/typescript
73+
run: npm install --no-audit --no-fund --omit=optional && npm run build
74+
- name: Publish to npm
75+
env:
76+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
77+
run: |
78+
# A version with a pre-release part (3.1.0-rc.1) goes to the "next" tag, not "latest".
79+
tag=latest
80+
case "$GITHUB_REF_NAME" in *-*) tag=next ;; esac
81+
publish() {
82+
name=$(node -p "require('./$1/package.json').name")
83+
version=$(node -p "require('./$1/package.json').version")
84+
if npm view "$name@$version" version >/dev/null 2>&1; then
85+
echo "$name@$version is already published"
86+
else
87+
npm publish "./$1" --access public --provenance --tag "$tag"
88+
fi
89+
}
90+
# The platform packages first, so the main package's optional dependencies resolve.
91+
for dir in npm/*/; do publish "${dir%/}"; done
92+
publish clients/typescript

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ bench/jobs/
77
__pycache__/
88
*.pyc
99
dist/
10+
/npm/
11+
node_modules/
1012

1113
# The full run log, the dev/held-out split, the round history, the published
1214
# scores and the lesson store stay on the benchmark machine; the public proof

‎README.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,13 @@ Fork an agent. Branch a personality. `git log` its memory. Diff its rules. Revie
5757
curl -fsSL https://raw.githubusercontent.com/open-gitagent/gitagent/main/scripts/install.sh | sh
5858
```
5959

60-
This installs the binary for your system (Linux x86_64/arm64, macOS arm64/x86_64) into `~/.local/bin`; set `INSTALL_DIR` to change that. To build from source you need Rust (the version is pinned in `rust-toolchain.toml`):
60+
Or with npm, which also gives you the TypeScript SDK:
61+
62+
```sh
63+
npm install -g @open-gitagent/gitagent
64+
```
65+
66+
Either way you get the same binary for your system (Linux x86_64/arm64, macOS arm64/x86_64). The script puts it in `~/.local/bin` (set `INSTALL_DIR` to change that); npm installs the package for your platform, `@open-gitagent/gitagent-<os>-<cpu>`, and a `gitagent` command that runs it. To build from source you need Rust (the version is pinned in `rust-toolchain.toml`):
6167

6268
```sh
6369
cargo build --release # ./target/release/gitagent

‎clients/typescript/README.md‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
# gitagent for TypeScript
22

3-
Drives the gitagent binary from Node. The agent runs in the binary
4-
(`GITAGENT_BIN`, or `gitagent` on the PATH); this package only translates
5-
calls and messages over the IPC protocol (`gitagent --ipc`).
3+
The `gitagent` command and its TypeScript SDK.
4+
5+
```sh
6+
npm install -g @open-gitagent/gitagent # the gitagent command
7+
npm install @open-gitagent/gitagent # the SDK, in a project
8+
```
9+
10+
npm also installs the prebuilt binary for your platform
11+
(`@open-gitagent/gitagent-linux-x64`, `-linux-arm64`, `-darwin-arm64` or
12+
`-darwin-x64`). The agent runs in that binary; this package translates calls
13+
and messages over the IPC protocol (`gitagent --ipc`). To use another binary,
14+
set `GITAGENT_BIN` or pass `binary` to `query()`.
615

716
```ts
817
import { query, tool } from "@open-gitagent/gitagent";

‎clients/typescript/bin/gitagent.js‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env node
2+
// The `gitagent` command of the npm package: runs the binary installed for
3+
// this platform with the same arguments, and exits as it does.
4+
5+
import { spawn } from "node:child_process";
6+
import { findBinary, PLATFORM_PACKAGES } from "../dist/binary.js";
7+
8+
const binary = findBinary();
9+
if (!binary) {
10+
const platform = `${process.platform}-${process.arch}`;
11+
const pkg = PLATFORM_PACKAGES[platform];
12+
console.error(
13+
pkg
14+
? `gitagent: the binary package ${pkg} is not installed.\n` +
15+
"Reinstall without --omit=optional (or --no-optional), or set GITAGENT_BIN to a gitagent binary."
16+
: `gitagent: there is no prebuilt binary for ${platform}.\n` +
17+
`Prebuilt: ${Object.keys(PLATFORM_PACKAGES).join(", ")}. Build from source and set GITAGENT_BIN.`,
18+
);
19+
process.exit(2);
20+
}
21+
22+
const child = spawn(binary, process.argv.slice(2), { stdio: "inherit" });
23+
24+
// Ctrl-C and Ctrl-\ reach the binary directly from the terminal, so the
25+
// launcher only waits; signals sent to the launcher alone are passed on.
26+
for (const signal of ["SIGINT", "SIGQUIT"]) process.on(signal, () => {});
27+
for (const signal of ["SIGTERM", "SIGHUP"]) process.on(signal, () => child.kill(signal));
28+
29+
child.on("error", (e) => {
30+
console.error(`gitagent: cannot start ${binary}: ${e.message}`);
31+
process.exit(2);
32+
});
33+
child.on("exit", (code, signal) => {
34+
if (signal) {
35+
process.removeAllListeners(signal);
36+
process.kill(process.pid, signal);
37+
} else {
38+
process.exit(code ?? 1);
39+
}
40+
});

‎clients/typescript/package.json‎

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,45 @@
11
{
22
"name": "@open-gitagent/gitagent",
33
"version": "3.0.0",
4-
"description": "Drives the gitagent binary from TypeScript over its IPC protocol.",
4+
"description": "The gitagent command and its TypeScript SDK: the agent that lives in a git repository.",
5+
"author": "shreyaskapale",
6+
"license": "MIT",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/open-gitagent/gitagent.git",
10+
"directory": "clients/typescript"
11+
},
12+
"homepage": "https://github.com/open-gitagent/gitagent",
13+
"keywords": ["ai", "agent", "git", "cli", "sdk", "llm"],
514
"type": "module",
615
"main": "./dist/index.js",
716
"types": "./dist/index.d.ts",
17+
"bin": {
18+
"gitagent": "./bin/gitagent.js"
19+
},
820
"exports": {
921
".": {
10-
"import": "./dist/index.js",
11-
"types": "./dist/index.d.ts"
12-
}
22+
"types": "./dist/index.d.ts",
23+
"import": "./dist/index.js"
24+
},
25+
"./cli": "./bin/gitagent.js",
26+
"./package.json": "./package.json"
1327
},
14-
"files": ["dist", "README.md"],
28+
"files": ["dist", "bin", "README.md"],
1529
"scripts": {
1630
"build": "tsc",
17-
"check": "tsc --noEmit"
31+
"check": "tsc --noEmit",
32+
"prepublishOnly": "npm run build"
1833
},
1934
"engines": {
2035
"node": ">=20"
2136
},
22-
"license": "MIT",
37+
"optionalDependencies": {
38+
"@open-gitagent/gitagent-linux-x64": "3.0.0",
39+
"@open-gitagent/gitagent-linux-arm64": "3.0.0",
40+
"@open-gitagent/gitagent-darwin-arm64": "3.0.0",
41+
"@open-gitagent/gitagent-darwin-x64": "3.0.0"
42+
},
2343
"devDependencies": {
2444
"@types/node": "^22.0.0",
2545
"typescript": "^5.6.0"

‎clients/typescript/src/binary.ts‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Where the gitagent binary comes from. `npm install @open-gitagent/gitagent`
3+
* also installs the one platform package below that matches the machine,
4+
* which carries the prebuilt binary in `bin/gitagent`.
5+
*/
6+
7+
import { existsSync } from "node:fs";
8+
import { createRequire } from "node:module";
9+
import { homedir } from "node:os";
10+
import { join } from "node:path";
11+
12+
/** The npm package with the prebuilt binary, by `${process.platform}-${process.arch}`. */
13+
export const PLATFORM_PACKAGES: Record<string, string> = {
14+
"linux-x64": "@open-gitagent/gitagent-linux-x64",
15+
"linux-arm64": "@open-gitagent/gitagent-linux-arm64",
16+
"darwin-arm64": "@open-gitagent/gitagent-darwin-arm64",
17+
"darwin-x64": "@open-gitagent/gitagent-darwin-x64",
18+
};
19+
20+
/**
21+
* The binary to run: `GITAGENT_BIN`, else the platform package installed
22+
* with this one, else the install script's `~/.local/bin/gitagent`.
23+
* Undefined when none of them exists.
24+
*/
25+
export function findBinary(): string | undefined {
26+
const fromEnv = process.env.GITAGENT_BIN;
27+
if (fromEnv) return fromEnv;
28+
const pkg = PLATFORM_PACKAGES[`${process.platform}-${process.arch}`];
29+
if (pkg) {
30+
try {
31+
const path = createRequire(import.meta.url).resolve(`${pkg}/bin/gitagent`);
32+
if (existsSync(path)) return path;
33+
} catch {
34+
// Not installed, for example with --omit=optional.
35+
}
36+
}
37+
const installed = join(homedir(), ".local", "bin", "gitagent");
38+
return existsSync(installed) ? installed : undefined;
39+
}

0 commit comments

Comments
 (0)