@trekagent/claude 0.2.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +7 -4
  2. package/bin/cli.mjs +23 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,8 +6,9 @@ API token — so an agent starts reporting presence and working the ready-task f
6
6
  you restart Claude Code.
7
7
 
8
8
  If no token is already available, `init` **opens your browser** so you can sign in or create an
9
- account — the token is then created and delivered straight back to the installer over a localhost
10
- loopback listener. No copy-paste required.
9
+ account and **pick a project** — the token (and the selected project) are then delivered straight
10
+ back to the installer over a localhost loopback listener and written as `TREK_TOKEN` /
11
+ `TREK_PROJECT_ID`. No copy-paste required.
11
12
 
12
13
  ## Usage
13
14
 
@@ -46,8 +47,10 @@ npx @trekagent/claude init --uninstall
46
47
  3. A `trk_` token already wired into project `./.claude/settings.local.json` or user
47
48
  `~/.claude/settings.local.json` (skipped when `--login` is passed).
48
49
  4. **Browser login** (interactive terminals, unless `--no-browser`): opens the cockpit
49
- `cli-auth` page, you sign in / sign up, and the token is delivered back automatically over a
50
- loopback listener bound to `127.0.0.1`.
50
+ `cli-auth` page, you sign in / sign up and select a project, and the token plus the chosen
51
+ project are delivered back automatically over a loopback listener bound to `127.0.0.1`. The
52
+ selected project is written as `TREK_PROJECT_ID` (an explicit `--project-id` still applies to
53
+ the non-browser paths).
51
54
  5. Manual paste prompt — the fallback for `--no-browser`, non-interactive shells, or if the
52
55
  browser flow times out (3 min). Points you at **Settings → API tokens** in the cockpit.
53
56
 
package/bin/cli.mjs CHANGED
@@ -146,8 +146,12 @@ display:flex;align-items:center;justify-content:center;height:100vh;margin:0}
146
146
  .card{text-align:center;max-width:30rem;padding:2rem}h1{font-size:1.4rem}p{color:#f28b82}</style></head>
147
147
  <body><div class="card"><h1>Trek CLI</h1><p>${String(msg).replace(/[<>&]/g, '')}</p></div></body></html>`;
148
148
 
149
- // Loopback browser-login flow. Resolves with a trk_ token, or null if it could
150
- // not complete (timeout / browser failure / user error) so the caller can fall back.
149
+ // Loopback browser-login flow. Resolves with { token, projectId } on success, or
150
+ // null if it could not complete (timeout / browser failure / user error) so the
151
+ // caller can fall back. The cockpit success callback contract is:
152
+ // success: http://127.0.0.1:<port>/callback?token=<trk_...>&project=<projectId>&state=<nonce>
153
+ // error: http://127.0.0.1:<port>/callback?error=<msg>&state=<nonce>
154
+ // Project selection is mandatory in the cockpit, so `project` is normally present.
151
155
  function browserLogin(flags) {
152
156
  return new Promise((resolve) => {
153
157
  const expectedState = randomUUID();
@@ -176,6 +180,7 @@ function browserLogin(flags) {
176
180
  const token = url.searchParams.get('token');
177
181
  const state = url.searchParams.get('state');
178
182
  const error = url.searchParams.get('error');
183
+ const projectId = url.searchParams.get('project') || '';
179
184
 
180
185
  // A mismatched state must NOT resolve — keep waiting.
181
186
  if (state !== expectedState) {
@@ -195,7 +200,7 @@ function browserLogin(flags) {
195
200
  if (looksLikeToken(token)) {
196
201
  res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
197
202
  res.end(SUCCESS_HTML);
198
- finish(token);
203
+ finish({ token, projectId });
199
204
  return;
200
205
  }
201
206
 
@@ -246,24 +251,28 @@ async function pasteToken() {
246
251
  return tok;
247
252
  }
248
253
 
254
+ // Resolve a token (and, where available, a project id) following this precedence.
255
+ // Always returns { token, projectId }; projectId is '' for every path except a
256
+ // successful browser login, where the cockpit's mandatory project selection is
257
+ // delivered back over the loopback callback.
249
258
  async function resolveToken(flags) {
250
259
  // 1. explicit flag
251
- if (typeof flags.token === 'string' && flags.token) return flags.token;
260
+ if (typeof flags.token === 'string' && flags.token) return { token: flags.token, projectId: '' };
252
261
  // 2. environment
253
- if (process.env.TREK_TOKEN) { skip('using TREK_TOKEN from environment'); return process.env.TREK_TOKEN; }
262
+ if (process.env.TREK_TOKEN) { skip('using TREK_TOKEN from environment'); return { token: process.env.TREK_TOKEN, projectId: '' }; }
254
263
  // 3. reuse an existing token from settings (unless --login forces re-auth)
255
264
  if (!flags.login) {
256
265
  const existing = detectExistingToken();
257
- if (existing) { skip(`reusing TREK_TOKEN from ${rel(existing.path)}`); return existing.token; }
266
+ if (existing) { skip(`reusing TREK_TOKEN from ${rel(existing.path)}`); return { token: existing.token, projectId: '' }; }
258
267
  }
259
268
  // 4. browser login (interactive, unless --no-browser)
260
269
  if (process.stdin.isTTY && !flags['no-browser']) {
261
- const tok = await browserLogin(flags);
262
- if (looksLikeToken(tok)) { ok('signed in via browser'); return tok; }
270
+ const result = await browserLogin(flags);
271
+ if (result && looksLikeToken(result.token)) { ok('signed in via browser'); return { token: result.token, projectId: result.projectId || '' }; }
263
272
  warn('falling back to manual token entry.');
264
273
  }
265
274
  // 5. manual paste fallback (also the --no-browser / non-TTY path)
266
- return pasteToken();
275
+ return { token: await pasteToken(), projectId: '' };
267
276
  }
268
277
 
269
278
  // --- settings.local.json: merge env block ----------------------------------
@@ -316,7 +325,7 @@ function installPlugin(bin, marketplaceRepo) {
316
325
  async function init(flags) {
317
326
  const userScope = !!flags.user;
318
327
  const apiUrl = (typeof flags['api-url'] === 'string' && flags['api-url']) || DEFAULT_API_URL;
319
- const projectId = typeof flags['project-id'] === 'string' ? flags['project-id'] : process.env.TREK_PROJECT_ID || '';
328
+ const flagProjectId = typeof flags['project-id'] === 'string' ? flags['project-id'] : process.env.TREK_PROJECT_ID || '';
320
329
  const marketplaceRepo = (typeof flags.marketplace === 'string' && flags.marketplace) || DEFAULT_MARKETPLACE_REPO;
321
330
 
322
331
  log(c.bold(`\nTrek installer — ${userScope ? 'user' : 'project'} scope`));
@@ -332,7 +341,10 @@ async function init(flags) {
332
341
  installPlugin(bin, marketplaceRepo);
333
342
  }
334
343
 
335
- const token = await resolveToken(flags);
344
+ const { token, projectId: browserProjectId } = await resolveToken(flags);
345
+ // The browser flow's mandatory project selection wins; otherwise fall back to
346
+ // the explicit --project-id / $TREK_PROJECT_ID resolution.
347
+ const projectId = browserProjectId || flagProjectId;
336
348
  const claudeDir = userScope ? join(homedir(), '.claude') : join(CWD, '.claude');
337
349
  writeTokenEnv(claudeDir, apiUrl, token, projectId);
338
350
  if (!userScope) ensureGitignore(CWD, ['.claude/settings.local.json']);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trekagent/claude",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "One-command installer for Trek: installs the Trek Claude Code plugin (skill + hooks + MCP) from the Trek marketplace and wires your API token.",
5
5
  "private": false,
6
6
  "type": "module",