hacklab 26.921.2 → 26.921.3

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.
Files changed (3) hide show
  1. package/README.md +14 -55
  2. package/dist/index.js +11 -110
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,77 +1,36 @@
1
1
  # Hacklab CLI
2
2
 
3
- A CLI for updating your Hacklab bio and social links with your coding agent.
4
-
5
- Requires Node.js 22 or newer. Bun 1.4.2 or newer is also supported.
3
+ Sync coding agent usage to Hacklab. Requires Node.js 22+ or Bun 1.4.2+.
6
4
 
7
5
  ```sh
8
6
  npm install --global hacklab@beta
9
- hacklab --help
10
- ```
11
-
12
- For Bun, use `bun install --global hacklab@beta` and `bunx --bun hacklab@beta --help`.
13
-
14
- Copy the agent instructions from the signed-in onboarding dialog. They contain no credentials. When you upload, the CLI opens a browser page to sign in with GitHub and authorize the CLI through Better Auth. Check that the code matches your terminal before approving.
15
-
16
- ## Preview a profile
17
-
18
- Create a local JSON file containing a one-line `bio` (at most 160 characters) and a `socials` object. Supported social keys are `github`, `linkedin`, `x`, `instagram`, `youtube`, `huggingface`, `producthunt`, and `goodreads`. Use full HTTPS profile URLs. Omitted links remain unchanged; `null` removes a link.
19
-
20
- ```sh
21
- hacklab onboard --file /path/to/profile.json
22
- ```
23
-
24
- This validates and previews locally without making a network request.
25
-
26
- ## Upload after approval
27
-
28
- Review the exact bio and links first. Agents must ask the user for approval before using `--confirm-upload`.
29
-
30
- ```sh
31
- hacklab onboard --file /path/to/profile.json --confirm-upload
32
- ```
33
-
34
- Confirmed onboarding first syncs usage, then saves the profile, sharing one browser authorization. Approval must cover both the profile and the usage report. Preview mode does neither. If syncing fails, onboarding stops before saving the profile.
35
-
36
- ## Sync usage
37
-
38
- ```sh
39
7
  hacklab sync
40
8
  ```
41
9
 
42
- The CLI includes pinned `ccusage@20.0.24` and runs `ccusage session --json`. No separate ccusage installation is needed. Its stdout is posted unchanged to `/api/token-usage`. This report includes agent/session identifiers, tokens, costs, model breakdowns, and metadata, which can include project paths. Source code and transcript contents are not uploaded.
43
-
44
- The API accepts the exact `{ session, totals }` envelope. `token_usage` stores each session with the upstream camelCase field names and nested JSON unchanged; only the authenticated `user_id` is added by the server. Totals are validated but not stored because they can be derived from the session rows. Repeated syncs replace counts for the same user, agent, and period rather than adding them. Missing sessions are retained, so syncing another machine does not delete earlier uploads. Reports are limited to 20 MB. The first upload uses browser authorization. Later commands reuse the saved session, checking that it is still valid; expired or revoked sessions open the browser again. Credentials are stored per Hacklab origin in `~/.config/hacklab/credentials/`, with owner-only directory and file permissions on Unix. Run `hacklab logout` to forget local authorization for the current Hacklab server.
45
-
46
- ## Log in
47
-
48
- ```sh
49
- hacklab login
50
- ```
51
-
52
- Authorizes the CLI and saves the session without collecting or uploading usage. An existing valid session is reused. To check the authorization dialog again, run `hacklab logout` followed by `hacklab login`.
10
+ The first sync opens your browser to sign in and authorize the CLI. Check that the code matches your terminal before approving. Later commands reuse saved authorization until it expires or is revoked.
53
11
 
54
- ## Log out
12
+ ## Commands
55
13
 
56
14
  ```sh
57
- hacklab logout
58
- hacklab sync
15
+ hacklab sync # Sign in and upload usage
16
+ hacklab login # Sign in without uploading
17
+ hacklab logout # Clear saved authorization
18
+ hacklab --help
19
+ hacklab --version
59
20
  ```
60
21
 
61
- Logout clears the saved CLI credential for the current Hacklab server, even when offline. The next sync opens the authorization dialog on the Arena again. Your browser stays signed in. Other Hacklab servers' credentials are preserved; this command does not revoke the server session.
22
+ The CLI runs bundled `ccusage@20.0.24 session --json` and uploads its output to `/api/token-usage`. Reports include session identifiers, tokens, costs, model breakdowns, and metadata that may include project paths. Source code and transcripts are not uploaded. Reports are limited to 20 MB. Repeated syncs update existing sessions; sessions from other machines are retained.
62
23
 
63
- ## Local development
24
+ Credentials are stored per server in `~/.config/hacklab/credentials/`, with owner-only permissions on Unix. Logout removes the local credential for the current server; it does not sign out your browser or revoke the server session.
64
25
 
65
- Building from source requires Bun 1.4.2 or newer; the published package runs with Node.js without Bun.
26
+ ## Development
66
27
 
67
- From the Hacklab repository:
28
+ Building requires Bun 1.4.2+.
68
29
 
69
30
  ```sh
70
31
  bun install --frozen-lockfile
71
32
  bun run --cwd packages/cli build:dev
72
- cd packages/cli
73
- bun link
74
- hacklab onboard --file /path/to/profile.json
33
+ bun run --cwd packages/cli start --help
75
34
  ```
76
35
 
77
- Source runs and `build:dev` always use `http://localhost:3000`. Production builds embed `BETTER_AUTH_URL` from the build environment or `apps/web/.env` / `.env.local`; it must be an HTTPS origin. Set the production URL before running `bun run build` or publishing. No `--url` override is supported. Credentials are saved locally for reuse and are never copied into the prompt. Development and production credentials are stored separately.
36
+ Source runs and development builds use `http://localhost:3000`. Production builds embed `BETTER_AUTH_URL` from the build environment or `apps/web/.env` / `.env.local`; it must be an HTTPS origin. No `--url` override is supported. Development and production credentials are stored separately.
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/index.ts
4
4
  import { parseArgs } from "node:util";
5
5
  // package.json
6
- var version = "26.921.2";
6
+ var version = "26.921.3";
7
7
 
8
8
  // src/auth.ts
9
9
  import { spawn } from "node:child_process";
@@ -123,12 +123,9 @@ Waiting for you to connect your account…`);
123
123
  throw new Error("CLI sign-in expired. Run the command again to retry.");
124
124
  }
125
125
 
126
- // src/onboard.ts
127
- import { readFile as readFile2, stat } from "node:fs/promises";
128
-
129
126
  // src/origin.ts
130
127
  function cliOrigin() {
131
- return "https://beta.hacklab.so";
128
+ return "https://hacklab.so";
132
129
  }
133
130
 
134
131
  // src/sync.ts
@@ -170,7 +167,7 @@ async function uploadUsage(origin, token, body) {
170
167
  throw new Error("Hacklab did not confirm the sync. Run hacklab sync to retry.");
171
168
  console.log(`Synced ${result.sessions} usage sessions to Hacklab.`);
172
169
  if (result.sessions === 0)
173
- console.log("No usage found. Use your coding agent, then run hacklab sync again to complete onboarding.");
170
+ console.log("No usage found. Use your coding agent, then run hacklab sync again.");
174
171
  }
175
172
  async function sync() {
176
173
  const origin = cliOrigin();
@@ -180,135 +177,39 @@ async function sync() {
180
177
  await uploadUsage(origin, token, body);
181
178
  }
182
179
 
183
- // src/onboard.ts
184
- async function onboard(options) {
185
- const origin = cliOrigin();
186
- const file = await stat(options.file);
187
- if (file.size > 8192)
188
- throw new Error("Profile must be smaller than 8 KB.");
189
- const contents = await readFile2(options.file);
190
- if (contents.byteLength > 8192)
191
- throw new Error("Profile must be smaller than 8 KB.");
192
- const profile = JSON.parse(contents.toString("utf8"));
193
- if (!profile || typeof profile !== "object" || Array.isArray(profile) || Object.keys(profile).some((key) => !["bio", "socials"].includes(key)) || typeof profile.bio !== "string" || !profile.bio.trim() || profile.bio.trim().length > 160 || /[\r\n]/.test(profile.bio) || !profile.socials || typeof profile.socials !== "object" || Array.isArray(profile.socials))
194
- throw new Error("Expected a one-line bio (1–160 characters) and a socials object, with no other fields.");
195
- const hosts = {
196
- github: ["github.com"],
197
- linkedin: ["linkedin.com"],
198
- x: ["x.com", "twitter.com"],
199
- instagram: ["instagram.com"],
200
- youtube: ["youtube.com"],
201
- huggingface: ["huggingface.co"],
202
- producthunt: ["producthunt.com"],
203
- goodreads: ["goodreads.com"]
204
- };
205
- for (const [platform, value] of Object.entries(profile.socials)) {
206
- if (!Object.hasOwn(hosts, platform))
207
- throw new Error(`Unsupported social platform: ${platform}`);
208
- if (value === null)
209
- continue;
210
- if (typeof value !== "string" || value.length > 500)
211
- throw new Error(`Invalid ${platform} profile URL.`);
212
- const url = new URL(value);
213
- if (url.protocol !== "https:" || url.username || url.password || url.port || url.pathname === "/" || !hosts[platform].includes(url.hostname.replace(/^www\./, ""))) {
214
- throw new Error(`Use an HTTPS ${platform} profile URL.`);
215
- }
216
- }
217
- const payload = JSON.stringify({ bio: profile.bio.trim(), socials: profile.socials }, null, 2);
218
- console.log(`Hacklab profile preview for ${origin}:
219
- ${payload}`);
220
- if (!options.confirmUpload) {
221
- console.log("Nothing uploaded. Ask the user to approve this exact profile and the first ccusage session sync, then run again with --confirm-upload.");
222
- return;
223
- }
224
- const usage = await collectUsage();
225
- const token = await authenticate(origin);
226
- await uploadUsage(origin, token, usage);
227
- const response = await fetch(`${origin}/api/onboarding`, {
228
- method: "POST",
229
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
230
- body: payload,
231
- redirect: "error",
232
- signal: AbortSignal.timeout(30000)
233
- });
234
- if (!response.ok) {
235
- throw new Error(`Hacklab rejected the profile (HTTP ${response.status}). Check the profile and sign-in before retrying.`);
236
- }
237
- const result = await response.json();
238
- if (result.ok !== true)
239
- throw new Error("Hacklab did not confirm the update. Check the website before retrying.");
240
- console.log("Your Hacklab bio and social links were saved.");
241
- }
242
-
243
180
  // src/index.ts
244
181
  var help = `Hacklab CLI
245
182
 
246
- Get started:
247
- npm install -g hacklab@beta
248
- hacklab sync
249
-
250
183
  Usage:
251
- hacklab sync
252
- hacklab onboard --file <profile.json> [--confirm-upload]
253
- hacklab login
254
- hacklab logout
255
- hacklab --help
256
- hacklab --version
257
-
258
- Optional profile update:
259
- --file Local JSON containing bio and socials
260
- --confirm-upload Upload after approval of the profile and first usage sync
261
-
262
- Without --confirm-upload, onboard validates and previews locally without uploading.
263
- Confirmed onboarding also runs the first usage sync.
264
- Sync signs in automatically and uploads unmodified ccusage session --json data.
265
- Onboarding completes when Hacklab receives your usage.
266
- The first upload opens your browser to authorize the CLI.
267
- Later uploads reuse saved authorization until it expires or is revoked.
268
- Login authorizes the CLI without uploading any data.
269
- Logout clears saved authorization for this Hacklab server.
270
- Requires Node.js 22 or newer. Bun 1.4.2 or newer is also supported.`;
184
+ hacklab sync Sign in and upload usage
185
+ hacklab login Sign in without uploading
186
+ hacklab logout Clear saved authorization
187
+ hacklab --help Show help
188
+ hacklab --version Show version`;
271
189
  try {
272
190
  const { values, positionals } = parseArgs({
273
191
  args: process.argv.slice(2),
274
192
  options: {
275
193
  help: { type: "boolean", short: "h" },
276
- version: { type: "boolean", short: "v" },
277
- file: { type: "string" },
278
- "confirm-upload": { type: "boolean" }
194
+ version: { type: "boolean", short: "v" }
279
195
  },
280
196
  allowPositionals: true
281
197
  });
282
- if (positionals.length > 1 || positionals[0] && !["onboard", "sync", "login", "logout"].includes(positionals[0])) {
283
- throw new Error("Unknown command. Use hacklab onboard, hacklab sync, hacklab login, hacklab logout, or hacklab --help.");
198
+ if (positionals.length > 1 || positionals[0] && !["sync", "login", "logout"].includes(positionals[0])) {
199
+ throw new Error("Unknown command. Run hacklab --help for usage.");
284
200
  }
285
201
  if (values.help) {
286
202
  console.log(help);
287
203
  } else if (values.version) {
288
204
  console.log(version);
289
- } else if (positionals[0] === "onboard") {
290
- if (!values.file)
291
- throw new Error("Pass --file with the local profile JSON to preview or upload.");
292
- await onboard({
293
- file: values.file,
294
- confirmUpload: values["confirm-upload"] ?? false
295
- });
296
205
  } else if (positionals[0] === "login") {
297
- if (values.file || values["confirm-upload"])
298
- throw new Error("Profile options require the onboard command.");
299
206
  await authenticate(cliOrigin());
300
207
  console.log("Signed in to Hacklab. Run hacklab sync to upload usage.");
301
208
  } else if (positionals[0] === "logout") {
302
- if (values.file || values["confirm-upload"])
303
- throw new Error("Profile options require the onboard command.");
304
209
  await removeCredential(cliOrigin());
305
210
  console.log("Saved CLI authorization cleared. Run hacklab sync to sign in again.");
306
211
  } else if (positionals[0] === "sync") {
307
- if (values.file || values["confirm-upload"])
308
- throw new Error("Profile options require the onboard command.");
309
212
  await sync();
310
- } else if (values.file || values["confirm-upload"]) {
311
- throw new Error("Profile options require the onboard command.");
312
213
  } else {
313
214
  console.log(help);
314
215
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hacklab",
3
- "version": "26.921.2",
4
- "description": "CLI for agent-assisted Hacklab profile onboarding",
3
+ "version": "26.921.3",
4
+ "description": "CLI for syncing coding agent usage to Hacklab",
5
5
  "bin": {
6
6
  "hacklab": "./dist/index.js"
7
7
  },