@autter/cli 1.6.8
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 +32 -0
- package/bin/autter.js +45 -0
- package/install.js +236 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# @autter/cli
|
|
2
|
+
|
|
3
|
+
**[Autter](https://autter.dev) is an open source git extension that records which lines of your code were written by AI** — tied to the agent, model, and prompt that produced them.
|
|
4
|
+
|
|
5
|
+
This package is a thin bootstrapper: it downloads the official native `autter` binary for your platform (macOS, Linux, or Windows; x64 or arm64) into `~/.autter/bin`, verifies its checksum against the GitHub release, and exposes the `autter` command through your npm global bin. The binary location is shared with the [shell installers](https://docs.autter.dev/cli/install), so hooks and the background service's self-updates keep working no matter how autter was installed.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @autter/cli
|
|
11
|
+
autter onboard
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or bootstrap without a global install:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @autter/cli onboard
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The package version matches the CLI release, so `npm install -g @autter/cli@<version>` installs that exact release.
|
|
21
|
+
|
|
22
|
+
## Environment variables
|
|
23
|
+
|
|
24
|
+
- `AUTTER_NPM_SKIP_DOWNLOAD=1` — skip the postinstall binary download (it happens on first run instead).
|
|
25
|
+
- `AUTTER_RELEASE_TAG=vX.Y.Z` — install a specific release.
|
|
26
|
+
- `AUTTER_NO_INSTALL_PING=1` — disable the anonymous install-count ping (OS, architecture, and version only).
|
|
27
|
+
|
|
28
|
+
## Links
|
|
29
|
+
|
|
30
|
+
- [Documentation](https://docs.autter.dev)
|
|
31
|
+
- [GitHub repository](https://github.com/Autter-dev/autter-cli)
|
|
32
|
+
- [Other install methods](https://docs.autter.dev/cli/install) (`curl`, PowerShell, Nix)
|
package/bin/autter.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// Launcher for the native autter binary at ~/.autter/bin. The fast path is a
|
|
5
|
+
// single existence check; the download in install.js only runs when the
|
|
6
|
+
// postinstall was skipped (--ignore-scripts, offline CI) or the binary was
|
|
7
|
+
// removed.
|
|
8
|
+
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const { spawnSync } = require('node:child_process');
|
|
11
|
+
const { binaryDest, ensureBinary } = require('../install.js');
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
let bin = binaryDest();
|
|
15
|
+
if (!fs.existsSync(bin)) {
|
|
16
|
+
try {
|
|
17
|
+
bin = (await ensureBinary()).bin;
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error(`autter: could not install the native binary: ${err.message}`);
|
|
20
|
+
console.error('autter: alternative installer: curl -fsSL https://api.autter.dev/install.sh | bash');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Let the child own Ctrl-C: without this the node wrapper dies on SIGINT
|
|
26
|
+
// while autter (e.g. `autter onboard`) is still restoring its terminal.
|
|
27
|
+
const noop = () => {};
|
|
28
|
+
process.on('SIGINT', noop);
|
|
29
|
+
process.on('SIGTERM', noop);
|
|
30
|
+
|
|
31
|
+
const result = spawnSync(bin, process.argv.slice(2), { stdio: 'inherit' });
|
|
32
|
+
if (result.error) {
|
|
33
|
+
console.error(`autter: failed to run ${bin}: ${result.error.message}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
if (result.signal) {
|
|
37
|
+
process.removeListener('SIGINT', noop);
|
|
38
|
+
process.removeListener('SIGTERM', noop);
|
|
39
|
+
process.kill(process.pid, result.signal);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
main();
|
package/install.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// npm bootstrapper for the autter CLI.
|
|
5
|
+
//
|
|
6
|
+
// npm distributes a thin launcher, not the binary itself: this script fetches
|
|
7
|
+
// the official release binary for the current platform into ~/.autter/bin --
|
|
8
|
+
// the same location the curl/PowerShell installers use and the location the
|
|
9
|
+
// autter daemon self-upgrades in place. Keeping one canonical binary path
|
|
10
|
+
// means hooks, the daemon, and every install method stay in agreement, and
|
|
11
|
+
// the npm global bin directory (already on PATH) provides the `autter`
|
|
12
|
+
// command via bin/autter.js.
|
|
13
|
+
//
|
|
14
|
+
// The package version is stamped to the CLI release tag at publish time, so
|
|
15
|
+
// `npm i -g @autter/cli@1.7.0` fetches the v1.7.0 binaries. The in-repo placeholder
|
|
16
|
+
// version (0.0.0-dev) resolves to the latest release instead.
|
|
17
|
+
//
|
|
18
|
+
// Escape hatches:
|
|
19
|
+
// AUTTER_NPM_SKIP_DOWNLOAD=1 skip the postinstall download entirely
|
|
20
|
+
// AUTTER_RELEASE_TAG=vX.Y.Z install a specific release (parity with install.sh)
|
|
21
|
+
// AUTTER_NO_INSTALL_PING=1 disable the anonymous install ping
|
|
22
|
+
|
|
23
|
+
const crypto = require('node:crypto');
|
|
24
|
+
const fs = require('node:fs');
|
|
25
|
+
const os = require('node:os');
|
|
26
|
+
const path = require('node:path');
|
|
27
|
+
const { execFileSync } = require('node:child_process');
|
|
28
|
+
|
|
29
|
+
const pkg = require('./package.json');
|
|
30
|
+
|
|
31
|
+
const REPO = 'autter-dev/autter-cli';
|
|
32
|
+
// Public write-only project token, same one embedded in install.sh; used for
|
|
33
|
+
// a single anonymous install-count event. Opt out with AUTTER_NO_INSTALL_PING=1.
|
|
34
|
+
const INSTALL_PING_API_KEY = 'phc_aWveMd1bPhuEYtFnCS1G2IHgln3iGQqjfIdkfnuolxI';
|
|
35
|
+
const INSTALL_PING_HOST = 'https://us.i.posthog.com';
|
|
36
|
+
|
|
37
|
+
const OS_NAMES = { darwin: 'macos', linux: 'linux', win32: 'windows' };
|
|
38
|
+
const CPU_NAMES = { x64: 'x64', arm64: 'arm64' };
|
|
39
|
+
|
|
40
|
+
function assetName() {
|
|
41
|
+
const osName = OS_NAMES[process.platform];
|
|
42
|
+
const cpuName = CPU_NAMES[process.arch];
|
|
43
|
+
if (!osName || !cpuName) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`no prebuilt autter binary for ${process.platform}/${process.arch}. ` +
|
|
46
|
+
'See https://github.com/autter-dev/autter-cli#installation for supported platforms.'
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return `autter-${osName}-${cpuName}${osName === 'windows' ? '.exe' : ''}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function installDir() {
|
|
53
|
+
return path.join(os.homedir(), '.autter', 'bin');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function binaryDest() {
|
|
57
|
+
return path.join(installDir(), process.platform === 'win32' ? 'autter.exe' : 'autter');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function releaseTag() {
|
|
61
|
+
const override = process.env.AUTTER_RELEASE_TAG;
|
|
62
|
+
if (override && override !== 'latest') return override;
|
|
63
|
+
if (pkg.version === '0.0.0-dev') return 'latest';
|
|
64
|
+
return `v${pkg.version}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function downloadUrl(tag, file) {
|
|
68
|
+
return tag === 'latest'
|
|
69
|
+
? `https://github.com/${REPO}/releases/latest/download/${file}`
|
|
70
|
+
: `https://github.com/${REPO}/releases/download/${tag}/${file}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function fetchBuffer(url) {
|
|
74
|
+
const res = await fetch(url, { redirect: 'follow' });
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
throw new Error(`download failed (HTTP ${res.status}) for ${url}`);
|
|
77
|
+
}
|
|
78
|
+
return Buffer.from(await res.arrayBuffer());
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Verify against the release's checksums.txt (`<sha256> <asset>` lines).
|
|
82
|
+
// Releases before v1.6.8 shipped no checksums file; skip with a warning then.
|
|
83
|
+
async function verifyChecksum(buf, asset, tag) {
|
|
84
|
+
let checksums;
|
|
85
|
+
try {
|
|
86
|
+
checksums = (await fetchBuffer(downloadUrl(tag, 'checksums.txt'))).toString('utf8');
|
|
87
|
+
} catch {
|
|
88
|
+
console.warn(`autter: no checksums.txt for release ${tag}; skipping checksum verification`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const entry = checksums
|
|
92
|
+
.split('\n')
|
|
93
|
+
.map((line) => line.trim().split(/\s+/))
|
|
94
|
+
.find((parts) => parts[1] === asset);
|
|
95
|
+
if (!entry) {
|
|
96
|
+
throw new Error(`no checksum entry for ${asset} in release ${tag}`);
|
|
97
|
+
}
|
|
98
|
+
const actual = crypto.createHash('sha256').update(buf).digest('hex');
|
|
99
|
+
if (actual !== entry[0]) {
|
|
100
|
+
throw new Error(`checksum mismatch for ${asset}: expected ${entry[0]}, got ${actual}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// `autter --version` prints the bare version ("1.6.8", or "1.6.8 (debug)").
|
|
105
|
+
function installedVersion(bin) {
|
|
106
|
+
try {
|
|
107
|
+
const out = execFileSync(bin, ['--version'], { encoding: 'utf8', timeout: 10_000 });
|
|
108
|
+
return out.trim().split(/\s+/)[0] || null;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function reportInstallPing(tag) {
|
|
115
|
+
if (process.env.AUTTER_NO_INSTALL_PING === '1') return Promise.resolve();
|
|
116
|
+
const safeTag = String(tag).replace(/[^A-Za-z0-9._-]/g, '');
|
|
117
|
+
console.log(
|
|
118
|
+
'Counting this install with an anonymous ping (OS, architecture, and version only). ' +
|
|
119
|
+
'Set AUTTER_NO_INSTALL_PING=1 to disable.'
|
|
120
|
+
);
|
|
121
|
+
return fetch(`${INSTALL_PING_HOST}/capture/`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: { 'Content-Type': 'application/json' },
|
|
124
|
+
signal: AbortSignal.timeout(5_000),
|
|
125
|
+
body: JSON.stringify({
|
|
126
|
+
api_key: INSTALL_PING_API_KEY,
|
|
127
|
+
event: 'install_script_run',
|
|
128
|
+
distinct_id: crypto.randomUUID(),
|
|
129
|
+
properties: {
|
|
130
|
+
os: OS_NAMES[process.platform],
|
|
131
|
+
arch: CPU_NAMES[process.arch],
|
|
132
|
+
release_tag: safeTag,
|
|
133
|
+
trigger: 'install',
|
|
134
|
+
source: 'npm',
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
}).catch(() => {});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Make sure the native binary exists at ~/.autter/bin, downloading it when
|
|
141
|
+
// missing or (for a pinned package version) when a different version is
|
|
142
|
+
// present. Returns { bin, downloaded }.
|
|
143
|
+
async function ensureBinary() {
|
|
144
|
+
const asset = assetName(); // throws early on unsupported platforms
|
|
145
|
+
const dest = binaryDest();
|
|
146
|
+
const tag = releaseTag();
|
|
147
|
+
|
|
148
|
+
if (fs.existsSync(dest)) {
|
|
149
|
+
if (tag === 'latest') {
|
|
150
|
+
// An existing install is authoritative; the autter daemon keeps it fresh.
|
|
151
|
+
return { bin: dest, downloaded: false };
|
|
152
|
+
}
|
|
153
|
+
const current = installedVersion(dest);
|
|
154
|
+
if (current === tag.replace(/^v/, '')) {
|
|
155
|
+
return { bin: dest, downloaded: false };
|
|
156
|
+
}
|
|
157
|
+
console.log(`autter: replacing version ${current ?? 'unknown'} at ${dest} with ${tag}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log(`autter: downloading ${asset} (release: ${tag})...`);
|
|
161
|
+
const buf = await fetchBuffer(downloadUrl(tag, asset));
|
|
162
|
+
if (buf.length === 0) {
|
|
163
|
+
throw new Error('downloaded file is empty');
|
|
164
|
+
}
|
|
165
|
+
await verifyChecksum(buf, asset, tag);
|
|
166
|
+
|
|
167
|
+
fs.mkdirSync(installDir(), { recursive: true });
|
|
168
|
+
const tmp = path.join(installDir(), `autter.tmp.${process.pid}`);
|
|
169
|
+
fs.writeFileSync(tmp, buf, { mode: 0o755 });
|
|
170
|
+
try {
|
|
171
|
+
fs.renameSync(tmp, dest);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
// Windows cannot rename over a running binary; retry after removing it.
|
|
174
|
+
try {
|
|
175
|
+
fs.rmSync(dest, { force: true });
|
|
176
|
+
fs.renameSync(tmp, dest);
|
|
177
|
+
} catch {
|
|
178
|
+
fs.rmSync(tmp, { force: true });
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (process.platform === 'darwin') {
|
|
184
|
+
try {
|
|
185
|
+
execFileSync('xattr', ['-d', 'com.apple.quarantine', dest], { stdio: 'ignore' });
|
|
186
|
+
} catch {
|
|
187
|
+
// Attribute not present or xattr unavailable; nothing to clean.
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await reportInstallPing(tag);
|
|
192
|
+
return { bin: dest, downloaded: true };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// postinstall entry point. Never fails the surrounding `npm install`: on any
|
|
196
|
+
// error it warns and defers to bin/autter.js, which retries on first run.
|
|
197
|
+
async function main() {
|
|
198
|
+
if (process.env.AUTTER_NPM_SKIP_DOWNLOAD === '1') {
|
|
199
|
+
console.log('autter: AUTTER_NPM_SKIP_DOWNLOAD=1, skipping binary download');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let result;
|
|
204
|
+
try {
|
|
205
|
+
result = await ensureBinary();
|
|
206
|
+
} catch (err) {
|
|
207
|
+
console.warn(`autter: could not install the native binary now (${err.message})`);
|
|
208
|
+
console.warn('autter: it will be downloaded on first run instead');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (!result.downloaded) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Mirror install.sh: wire up agent/editor integrations right away. The
|
|
217
|
+
// daemon re-checks hooks daily, so a failure here is non-fatal.
|
|
218
|
+
try {
|
|
219
|
+
execFileSync(result.bin, ['install-hooks'], { stdio: 'inherit', timeout: 120_000 });
|
|
220
|
+
} catch {
|
|
221
|
+
console.warn("autter: could not set up IDE/agent hooks; run 'autter install-hooks' manually");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
console.log('');
|
|
225
|
+
console.log(`autter installed at ${result.bin}`);
|
|
226
|
+
console.log("Run 'autter onboard' to finish setup (local-only mode or connect to autter.dev).");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
module.exports = { assetName, binaryDest, ensureBinary, releaseTag };
|
|
230
|
+
|
|
231
|
+
if (require.main === module) {
|
|
232
|
+
main().catch((err) => {
|
|
233
|
+
// Belt and braces: postinstall must never break `npm install`.
|
|
234
|
+
console.warn(`autter: postinstall failed (${err.message})`);
|
|
235
|
+
});
|
|
236
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@autter/cli",
|
|
3
|
+
"version": "1.6.8",
|
|
4
|
+
"description": "Git-native AI authorship tracking: records which lines were written by AI agents and links them to the agent, model, and prompt. This package is a thin bootstrapper that installs the native autter binary.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"homepage": "https://autter.dev",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Autter-dev/autter-cli.git",
|
|
10
|
+
"directory": "npm"
|
|
11
|
+
},
|
|
12
|
+
"bugs": "https://github.com/Autter-dev/autter-cli/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ai",
|
|
15
|
+
"git",
|
|
16
|
+
"authorship",
|
|
17
|
+
"attribution",
|
|
18
|
+
"blame",
|
|
19
|
+
"coding-agents",
|
|
20
|
+
"claude",
|
|
21
|
+
"copilot",
|
|
22
|
+
"cursor",
|
|
23
|
+
"codex"
|
|
24
|
+
],
|
|
25
|
+
"bin": {
|
|
26
|
+
"autter": "bin/autter.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"bin/autter.js",
|
|
30
|
+
"install.js",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"postinstall": "node install.js"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"os": [
|
|
40
|
+
"darwin",
|
|
41
|
+
"linux",
|
|
42
|
+
"win32"
|
|
43
|
+
],
|
|
44
|
+
"cpu": [
|
|
45
|
+
"x64",
|
|
46
|
+
"arm64"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|