@that-company/dat 0.1.30

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 ADDED
@@ -0,0 +1,30 @@
1
+ # D.A.T. CLI
2
+
3
+ Install D.A.T. with npm:
4
+
5
+ ```sh
6
+ npm i -g @that-company/dat
7
+ ```
8
+
9
+ Then sign in:
10
+
11
+ ```sh
12
+ dat login
13
+ ```
14
+
15
+ This npm package is a thin installer. During `postinstall`, it downloads the
16
+ matching prebuilt `dat` binary from GitHub Releases, verifies it against
17
+ `SHA256SUMS`, and exposes it as the `dat` command.
18
+
19
+ Alternative install:
20
+
21
+ ```sh
22
+ curl -fsSL https://thatcompany.ai/install.sh | sh
23
+ ```
24
+
25
+ ## Environment Overrides
26
+
27
+ - `DAT_RELEASE_REPO`: release repo, default `theSalted/dat-releases`
28
+ - `DAT_INSTALL_BASE`: full release asset base URL
29
+ - `DAT_CHANNEL`: release path under `/releases`, default `download/v<package version>`
30
+ - `DAT_SKIP_INSTALL=1`: skip binary download during npm install
package/bin/dat.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { spawnSync } = require("node:child_process");
5
+ const { existsSync } = require("node:fs");
6
+ const { join } = require("node:path");
7
+
8
+ const binary = join(__dirname, "..", "vendor", process.platform === "win32" ? "dat.exe" : "dat");
9
+
10
+ if (!existsSync(binary)) {
11
+ console.error("dat binary is missing. Reinstall with: npm rebuild -g @thatcompany/dat");
12
+ process.exit(1);
13
+ }
14
+
15
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
16
+
17
+ if (result.error) {
18
+ console.error(`failed to run dat: ${result.error.message}`);
19
+ process.exit(1);
20
+ }
21
+
22
+ if (result.signal) {
23
+ process.kill(process.pid, result.signal);
24
+ }
25
+
26
+ process.exit(result.status === null ? 1 : result.status);
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@that-company/dat",
3
+ "version": "0.1.30",
4
+ "description": "Installer for the D.A.T. CLI",
5
+ "bin": {
6
+ "dat": "bin/dat.js"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node scripts/install.js"
10
+ },
11
+ "files": [
12
+ "bin",
13
+ "scripts",
14
+ "README.md"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/that-company/that.git",
19
+ "directory": "npm/dat"
20
+ },
21
+ "homepage": "https://thatcompany.ai",
22
+ "bugs": {
23
+ "url": "https://github.com/that-company/that/issues"
24
+ },
25
+ "keywords": [
26
+ "dat",
27
+ "agent",
28
+ "training",
29
+ "machine-learning",
30
+ "cli"
31
+ ],
32
+ "license": "UNLICENSED",
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { createHash } = require("node:crypto");
5
+ const { createWriteStream, existsSync, mkdirSync, readFileSync, renameSync, rmSync, chmodSync } = require("node:fs");
6
+ const { get } = require("node:https");
7
+ const { tmpdir } = require("node:os");
8
+ const { join } = require("node:path");
9
+ const { URL } = require("node:url");
10
+
11
+ const pkg = require("../package.json");
12
+
13
+ const REPO = process.env.DAT_RELEASE_REPO || "theSalted/dat-releases";
14
+ const CHANNEL = process.env.DAT_CHANNEL || `download/v${pkg.version}`;
15
+ const RELEASE_BASE = trimTrailingSlash(
16
+ process.env.DAT_INSTALL_BASE || `https://github.com/${REPO}/releases/${CHANNEL}`,
17
+ );
18
+ const PKG_ROOT = join(__dirname, "..");
19
+ const VENDOR_DIR = join(PKG_ROOT, "vendor");
20
+ const BIN_PATH = join(VENDOR_DIR, process.platform === "win32" ? "dat.exe" : "dat");
21
+
22
+ main().catch((error) => {
23
+ console.error(`dat install failed: ${error instanceof Error ? error.message : String(error)}`);
24
+ process.exit(1);
25
+ });
26
+
27
+ async function main() {
28
+ if (process.env.DAT_SKIP_INSTALL === "1") {
29
+ console.error("DAT_SKIP_INSTALL=1; skipping dat binary download");
30
+ return;
31
+ }
32
+
33
+ const asset = releaseAssetName(process.platform, process.arch);
34
+ mkdirSync(VENDOR_DIR, { recursive: true });
35
+
36
+ const tempDir = join(tmpdir(), `dat-npm-${process.pid}-${Date.now()}`);
37
+ mkdirSync(tempDir, { recursive: true });
38
+ try {
39
+ const tempBin = join(tempDir, asset);
40
+ const tempSums = join(tempDir, "SHA256SUMS");
41
+ const assetUrl = `${RELEASE_BASE}/${asset}`;
42
+ const sumsUrl = `${RELEASE_BASE}/SHA256SUMS`;
43
+
44
+ console.error(`downloading ${asset} from ${RELEASE_BASE}`);
45
+ await download(assetUrl, tempBin);
46
+ await download(sumsUrl, tempSums);
47
+ verifyChecksum(tempBin, tempSums, asset);
48
+
49
+ renameSync(tempBin, BIN_PATH);
50
+ chmodSync(BIN_PATH, 0o755);
51
+ console.error(`installed dat ${pkg.version} -> ${BIN_PATH}`);
52
+ } finally {
53
+ rmSync(tempDir, { recursive: true, force: true });
54
+ }
55
+ }
56
+
57
+ function releaseAssetName(platform, arch) {
58
+ let os;
59
+ switch (platform) {
60
+ case "darwin":
61
+ os = "darwin";
62
+ break;
63
+ case "linux":
64
+ os = "linux";
65
+ break;
66
+ default:
67
+ throw new Error(`unsupported platform ${platform}; dat npm install currently supports macOS and Linux`);
68
+ }
69
+
70
+ let cpu;
71
+ switch (arch) {
72
+ case "arm64":
73
+ cpu = "arm64";
74
+ break;
75
+ case "x64":
76
+ cpu = "x64";
77
+ break;
78
+ default:
79
+ throw new Error(`unsupported architecture ${arch}; dat npm install supports arm64 and x64`);
80
+ }
81
+
82
+ return `dat-${os}-${cpu}`;
83
+ }
84
+
85
+ function trimTrailingSlash(value) {
86
+ return value.endsWith("/") ? trimTrailingSlash(value.slice(0, -1)) : value;
87
+ }
88
+
89
+ function download(url, dest, redirects = 0) {
90
+ if (redirects > 5) {
91
+ return Promise.reject(new Error(`too many redirects while downloading ${url}`));
92
+ }
93
+
94
+ return new Promise((resolve, reject) => {
95
+ const request = get(new URL(url), (response) => {
96
+ const status = response.statusCode || 0;
97
+ const location = response.headers.location;
98
+ if (status >= 300 && status < 400 && location) {
99
+ response.resume();
100
+ const next = new URL(location, url).toString();
101
+ download(next, dest, redirects + 1).then(resolve, reject);
102
+ return;
103
+ }
104
+ if (status !== 200) {
105
+ response.resume();
106
+ reject(new Error(`GET ${url} -> HTTP ${status}`));
107
+ return;
108
+ }
109
+ const file = createWriteStream(dest, { mode: 0o600 });
110
+ response.pipe(file);
111
+ file.on("finish", () => file.close(resolve));
112
+ file.on("error", reject);
113
+ });
114
+ request.on("error", reject);
115
+ request.setTimeout(120_000, () => {
116
+ request.destroy(new Error(`timeout downloading ${url}`));
117
+ });
118
+ });
119
+ }
120
+
121
+ function verifyChecksum(filePath, sumsPath, asset) {
122
+ if (!existsSync(sumsPath)) {
123
+ throw new Error("SHA256SUMS was not downloaded");
124
+ }
125
+ const line = readFileSync(sumsPath, "utf8")
126
+ .split(/\r?\n/)
127
+ .find((entry) => entry.trim().endsWith(` ${asset}`) || entry.trim().endsWith(` ${asset}`));
128
+ if (!line) {
129
+ throw new Error(`SHA256SUMS has no entry for ${asset}`);
130
+ }
131
+ const expected = line.trim().split(/\s+/)[0];
132
+ const actual = createHash("sha256").update(readFileSync(filePath)).digest("hex");
133
+ if (actual !== expected) {
134
+ throw new Error(`checksum mismatch for ${asset} (expected ${expected}, got ${actual})`);
135
+ }
136
+ console.error("checksum ok");
137
+ }