@gh-symphony/cli 0.0.17 → 0.0.19

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 (32) hide show
  1. package/README.md +105 -9
  2. package/dist/{chunk-EFMFGOWM.js → chunk-6CI3UUMH.js} +282 -57
  3. package/dist/chunk-C7G7RJ4G.js +146 -0
  4. package/dist/{chunk-MHIWAIVD.js → chunk-GKENCODJ.js} +141 -53
  5. package/dist/{project-557FE2GD.js → chunk-H2YXSYOZ.js} +108 -92
  6. package/dist/{chunk-TF3QNWNC.js → chunk-M3IFVLQS.js} +246 -212
  7. package/dist/{chunk-IWR4UQEJ.js → chunk-RN2PACNV.js} +350 -523
  8. package/dist/chunk-TILHWBP6.js +638 -0
  9. package/dist/{chunk-6HBZC3BE.js → chunk-XN5ABWZ6.js} +23 -5
  10. package/dist/{chunk-76QPITKI.js → chunk-Y6TYJMNT.js} +1 -1
  11. package/dist/{config-cmd-AZ7POMAA.js → config-cmd-DNXNL26Z.js} +3 -1
  12. package/dist/doctor-IYHCFXOZ.js +1126 -0
  13. package/dist/index.js +157 -19
  14. package/dist/init-KZT6YNOH.js +33 -0
  15. package/dist/{logs-6LNGT2GF.js → logs-6JKKYDGJ.js} +1 -1
  16. package/dist/project-DNALEWO3.js +22 -0
  17. package/dist/{recover-LVBI2TGH.js → recover-C3V2QAUB.js} +3 -3
  18. package/dist/repo-HDDE7OUI.js +321 -0
  19. package/dist/{run-WITYAYFZ.js → run-XI2S5Y4V.js} +3 -3
  20. package/dist/setup-K4CYYJBF.js +431 -0
  21. package/dist/{start-JUFKNL3N.js → start-M6IQGRFO.js} +5 -5
  22. package/dist/{status-3WK5BWRZ.js → status-QSCFVGRQ.js} +2 -2
  23. package/dist/{stop-AA3AP5M6.js → stop-7MFCBQVW.js} +2 -2
  24. package/dist/upgrade-F4VE4XBS.js +165 -0
  25. package/dist/{version-YVM2A25J.js → version-Y5RYNWMF.js} +1 -1
  26. package/dist/worker-entry.js +39 -11
  27. package/dist/workflow-TBIFY5MO.js +497 -0
  28. package/package.json +4 -4
  29. package/dist/chunk-JO3AXHQI.js +0 -130
  30. package/dist/chunk-TH5QPO3Y.js +0 -67
  31. package/dist/init-EZXQAXZM.js +0 -17
  32. package/dist/repo-R3XBIVAX.js +0 -121
@@ -0,0 +1,638 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/github/client.ts
4
+ var DEFAULT_API_URL = "https://api.github.com/graphql";
5
+ var REST_API_URL = "https://api.github.com";
6
+ var GitHubApiError = class extends Error {
7
+ constructor(message, status) {
8
+ super(message);
9
+ this.status = status;
10
+ this.name = "GitHubApiError";
11
+ }
12
+ };
13
+ var GitHubScopeError = class extends GitHubApiError {
14
+ constructor(message, requiredScopes, currentScopes) {
15
+ super(message);
16
+ this.requiredScopes = requiredScopes;
17
+ this.currentScopes = currentScopes;
18
+ this.name = "GitHubScopeError";
19
+ }
20
+ };
21
+ function createClient(token, options) {
22
+ return {
23
+ token,
24
+ apiUrl: options?.apiUrl ?? DEFAULT_API_URL,
25
+ fetchImpl: options?.fetchImpl ?? fetch
26
+ };
27
+ }
28
+ async function validateToken(client) {
29
+ const restUrl = client.apiUrl.replace("/graphql", "");
30
+ const baseUrl = restUrl === client.apiUrl ? REST_API_URL : restUrl;
31
+ const response = await client.fetchImpl(`${baseUrl}/user`, {
32
+ headers: {
33
+ authorization: `Bearer ${client.token}`,
34
+ accept: "application/vnd.github+json"
35
+ }
36
+ });
37
+ if (!response.ok) {
38
+ if (response.status === 401) {
39
+ throw new GitHubApiError("Invalid token: authentication failed.", 401);
40
+ }
41
+ throw new GitHubApiError(
42
+ `GitHub API error: ${response.status} ${response.statusText}`,
43
+ response.status
44
+ );
45
+ }
46
+ const scopes = response.headers.get("x-oauth-scopes")?.split(",").map((s) => s.trim()).filter(Boolean) ?? [];
47
+ const user = await response.json();
48
+ return {
49
+ login: user.login,
50
+ name: user.name,
51
+ scopes
52
+ };
53
+ }
54
+ function checkRequiredScopes(scopes) {
55
+ const required = ["repo", "read:org", "project"];
56
+ const normalizedScopes = scopes.map((s) => s.toLowerCase());
57
+ const missing = required.filter((r) => !normalizedScopes.includes(r));
58
+ return { valid: missing.length === 0, missing };
59
+ }
60
+ async function listUserProjects(client) {
61
+ const data = await graphql(
62
+ client,
63
+ VIEWER_PROJECTS_QUERY
64
+ );
65
+ const projects = [];
66
+ for (const node of data.viewer.projectsV2?.nodes ?? []) {
67
+ if (!node) continue;
68
+ projects.push(
69
+ normalizeProjectSummary(node, {
70
+ login: data.viewer.login,
71
+ type: "User"
72
+ })
73
+ );
74
+ }
75
+ for (const orgNode of data.viewer.organizations?.nodes ?? []) {
76
+ if (!orgNode) continue;
77
+ for (const projNode of orgNode.projectsV2?.nodes ?? []) {
78
+ if (!projNode) continue;
79
+ projects.push(
80
+ normalizeProjectSummary(projNode, {
81
+ login: orgNode.login,
82
+ type: "Organization"
83
+ })
84
+ );
85
+ }
86
+ }
87
+ return projects;
88
+ }
89
+ function normalizeProjectSummary(node, owner) {
90
+ return {
91
+ id: node.id,
92
+ title: node.title,
93
+ shortDescription: node.shortDescription ?? "",
94
+ url: node.url,
95
+ openItemCount: node.items?.totalCount ?? 0,
96
+ owner
97
+ };
98
+ }
99
+ async function getProjectDetail(client, projectId) {
100
+ const data = await graphql(
101
+ client,
102
+ PROJECT_DETAIL_QUERY,
103
+ { projectId }
104
+ );
105
+ const project = data.node;
106
+ if (!project || project.__typename !== "ProjectV2") {
107
+ throw new GitHubApiError(`Project not found: ${projectId}`);
108
+ }
109
+ const statusFields = [];
110
+ const textFields = [];
111
+ for (const field of project.fields?.nodes ?? []) {
112
+ if (!field) continue;
113
+ if (field.__typename === "ProjectV2SingleSelectField") {
114
+ statusFields.push({
115
+ id: field.id,
116
+ name: field.name,
117
+ options: (field.options ?? []).map((opt) => ({
118
+ id: opt.id,
119
+ name: opt.name,
120
+ description: opt.description ?? null,
121
+ color: opt.color ?? null
122
+ }))
123
+ });
124
+ } else if (field.__typename === "ProjectV2Field" && field.dataType) {
125
+ textFields.push({
126
+ id: field.id,
127
+ name: field.name,
128
+ dataType: field.dataType
129
+ });
130
+ }
131
+ }
132
+ const repoMap = /* @__PURE__ */ new Map();
133
+ let cursor = null;
134
+ let hasMore = true;
135
+ for (const item of project.items?.nodes ?? []) {
136
+ const repo = item?.content?.repository;
137
+ if (!repo) continue;
138
+ const key = `${repo.owner.login}/${repo.name}`;
139
+ if (!repoMap.has(key)) {
140
+ repoMap.set(key, {
141
+ owner: repo.owner.login,
142
+ name: repo.name,
143
+ url: repo.url,
144
+ cloneUrl: repo.url.endsWith(".git") ? repo.url : `${repo.url}.git`
145
+ });
146
+ }
147
+ }
148
+ hasMore = project.items?.pageInfo?.hasNextPage ?? false;
149
+ cursor = project.items?.pageInfo?.endCursor ?? null;
150
+ while (hasMore && cursor) {
151
+ const pageData = await graphql(
152
+ client,
153
+ PROJECT_ITEMS_PAGE_QUERY,
154
+ { projectId, cursor }
155
+ );
156
+ const items = pageData.node?.items;
157
+ if (!items) break;
158
+ for (const item of items.nodes ?? []) {
159
+ const repo = item?.content?.repository;
160
+ if (!repo) continue;
161
+ const key = `${repo.owner.login}/${repo.name}`;
162
+ if (!repoMap.has(key)) {
163
+ repoMap.set(key, {
164
+ owner: repo.owner.login,
165
+ name: repo.name,
166
+ url: repo.url,
167
+ cloneUrl: repo.url.endsWith(".git") ? repo.url : `${repo.url}.git`
168
+ });
169
+ }
170
+ }
171
+ hasMore = items.pageInfo?.hasNextPage ?? false;
172
+ cursor = items.pageInfo?.endCursor ?? null;
173
+ }
174
+ return {
175
+ id: project.id,
176
+ title: project.title,
177
+ url: project.url,
178
+ statusFields,
179
+ textFields,
180
+ linkedRepositories: [...repoMap.values()]
181
+ };
182
+ }
183
+ async function graphql(client, query, variables) {
184
+ const response = await client.fetchImpl(client.apiUrl, {
185
+ method: "POST",
186
+ headers: {
187
+ "content-type": "application/json",
188
+ authorization: `Bearer ${client.token}`
189
+ },
190
+ body: JSON.stringify({ query, variables })
191
+ });
192
+ if (!response.ok) {
193
+ const text = await response.text().catch(() => "");
194
+ throw new GitHubApiError(
195
+ `GitHub GraphQL request failed: ${response.status} ${response.statusText}. ${text}`,
196
+ response.status
197
+ );
198
+ }
199
+ const payload = await response.json();
200
+ if (payload.errors?.length) {
201
+ const scopeMessages = payload.errors.map((e) => e.message).filter((m) => m.includes("has not been granted the required scopes"));
202
+ if (scopeMessages.length > 0) {
203
+ const requiredScopes = /* @__PURE__ */ new Set();
204
+ let currentScopes = [];
205
+ for (const msg of scopeMessages) {
206
+ for (const match of msg.matchAll(
207
+ /requires one of the following scopes: \['([^']+)'\]/g
208
+ )) {
209
+ requiredScopes.add(match[1]);
210
+ }
211
+ if (currentScopes.length === 0) {
212
+ const currMatch = /has only been granted the: \[([^\]]+)\]/.exec(msg);
213
+ if (currMatch) {
214
+ currentScopes = currMatch[1].split(",").map((s) => s.trim().replace(/'/g, "")).filter(Boolean);
215
+ }
216
+ }
217
+ }
218
+ throw new GitHubScopeError(
219
+ "Token is missing required GitHub scopes.",
220
+ [...requiredScopes],
221
+ currentScopes
222
+ );
223
+ }
224
+ throw new GitHubApiError(
225
+ `GraphQL errors: ${payload.errors.map((e) => e.message).join("; ")}`
226
+ );
227
+ }
228
+ if (!payload.data) {
229
+ throw new GitHubApiError("GraphQL response missing data.");
230
+ }
231
+ return payload.data;
232
+ }
233
+ var VIEWER_PROJECTS_QUERY = `
234
+ query ViewerProjects {
235
+ viewer {
236
+ login
237
+ projectsV2(first: 50) {
238
+ nodes {
239
+ id
240
+ title
241
+ shortDescription
242
+ url
243
+ items { totalCount }
244
+ }
245
+ }
246
+ organizations(first: 20) {
247
+ nodes {
248
+ login
249
+ projectsV2(first: 50) {
250
+ nodes {
251
+ id
252
+ title
253
+ shortDescription
254
+ url
255
+ items { totalCount }
256
+ }
257
+ }
258
+ }
259
+ }
260
+ }
261
+ }
262
+ `;
263
+ var PROJECT_DETAIL_QUERY = `
264
+ query ProjectDetail($projectId: ID!) {
265
+ node(id: $projectId) {
266
+ __typename
267
+ ... on ProjectV2 {
268
+ id
269
+ title
270
+ url
271
+ fields(first: 50) {
272
+ nodes {
273
+ __typename
274
+ ... on ProjectV2SingleSelectField {
275
+ id
276
+ name
277
+ options {
278
+ id
279
+ name
280
+ description
281
+ color
282
+ }
283
+ }
284
+ ... on ProjectV2Field {
285
+ id
286
+ name
287
+ dataType
288
+ }
289
+ }
290
+ }
291
+ items(first: 100) {
292
+ nodes {
293
+ content {
294
+ __typename
295
+ ... on Issue {
296
+ repository {
297
+ name
298
+ url
299
+ owner { login }
300
+ }
301
+ }
302
+ ... on PullRequest {
303
+ repository {
304
+ name
305
+ url
306
+ owner { login }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ pageInfo {
312
+ endCursor
313
+ hasNextPage
314
+ }
315
+ }
316
+ }
317
+ }
318
+ }
319
+ `;
320
+ var PROJECT_ITEMS_PAGE_QUERY = `
321
+ query ProjectItemsPage($projectId: ID!, $cursor: String) {
322
+ node(id: $projectId) {
323
+ ... on ProjectV2 {
324
+ items(first: 100, after: $cursor) {
325
+ nodes {
326
+ content {
327
+ __typename
328
+ ... on Issue {
329
+ repository {
330
+ name
331
+ url
332
+ owner { login }
333
+ }
334
+ }
335
+ ... on PullRequest {
336
+ repository {
337
+ name
338
+ url
339
+ owner { login }
340
+ }
341
+ }
342
+ }
343
+ }
344
+ pageInfo {
345
+ endCursor
346
+ hasNextPage
347
+ }
348
+ }
349
+ }
350
+ }
351
+ }
352
+ `;
353
+
354
+ // src/github/gh-auth.ts
355
+ import { execFileSync, spawnSync } from "child_process";
356
+ var REQUIRED_GH_SCOPES = ["repo", "read:org", "project"];
357
+ var GhAuthError = class extends Error {
358
+ constructor(code, message) {
359
+ super(message);
360
+ this.code = code;
361
+ this.name = "GhAuthError";
362
+ }
363
+ };
364
+ function ghTokenReadErrorMessage() {
365
+ return "Failed to read a GitHub token from gh CLI. Run 'gh auth status' and try again.";
366
+ }
367
+ function missingGhScopesMessage(missing) {
368
+ return `Run 'gh auth refresh --scopes repo,read:org,project'. Missing scopes: ${missing.join(", ")}`;
369
+ }
370
+ function classifyTokenValidationError(error, source) {
371
+ if (error instanceof GhAuthError) {
372
+ return error;
373
+ }
374
+ if (error instanceof GitHubApiError) {
375
+ if (error.status === 401) {
376
+ return new GhAuthError(
377
+ source === "env" ? "invalid_token" : "token_failed",
378
+ source === "env" ? "GITHUB_GRAPHQL_TOKEN is invalid or expired." : ghTokenReadErrorMessage()
379
+ );
380
+ }
381
+ const prefix = source === "env" ? "GITHUB_GRAPHQL_TOKEN could not be validated" : "gh CLI token could not be validated";
382
+ return new GhAuthError("token_failed", `${prefix}: ${error.message}`);
383
+ }
384
+ if (error instanceof Error) {
385
+ const prefix = source === "env" ? "GITHUB_GRAPHQL_TOKEN could not be validated" : "gh CLI token could not be validated";
386
+ return new GhAuthError("token_failed", `${prefix}: ${error.message}`);
387
+ }
388
+ return new GhAuthError(
389
+ "token_failed",
390
+ source === "env" ? "GITHUB_GRAPHQL_TOKEN could not be validated." : "gh CLI token could not be validated."
391
+ );
392
+ }
393
+ function getEnvGitHubToken() {
394
+ const token = process.env.GITHUB_GRAPHQL_TOKEN?.trim();
395
+ return token ? token : null;
396
+ }
397
+ function checkGhInstalled(opts) {
398
+ const execImpl = opts?.execImpl ?? execFileSync;
399
+ try {
400
+ execImpl("gh", ["--version"], { stdio: "pipe" });
401
+ return true;
402
+ } catch (error) {
403
+ const execError = error;
404
+ if (execError.code === "ENOENT") {
405
+ return false;
406
+ }
407
+ throw error;
408
+ }
409
+ }
410
+ function checkGhAuthenticated(opts) {
411
+ const spawnImpl = opts?.spawnImpl ?? spawnSync;
412
+ const result = spawnImpl("gh", ["auth", "status"], {
413
+ encoding: "utf8",
414
+ stdio: ["pipe", "pipe", "pipe"]
415
+ });
416
+ if ((result.status ?? 1) !== 0) {
417
+ return { authenticated: false };
418
+ }
419
+ const login = parseLogin((result.stdout ?? "").toString());
420
+ return { authenticated: true, login };
421
+ }
422
+ function checkGhScopes(opts) {
423
+ const spawnImpl = opts?.spawnImpl ?? spawnSync;
424
+ const result = spawnImpl("gh", ["auth", "status"], {
425
+ encoding: "utf8",
426
+ stdio: ["pipe", "pipe", "pipe"]
427
+ });
428
+ const output = (result.stdout ?? "").toString();
429
+ const scopes = parseScopes(output);
430
+ if (scopes.length === 0) {
431
+ return { valid: true, missing: [], scopes: [] };
432
+ }
433
+ const normalized = scopes.map((scope) => scope.toLowerCase());
434
+ const missing = REQUIRED_GH_SCOPES.filter(
435
+ (scope) => !normalized.includes(scope)
436
+ );
437
+ return {
438
+ valid: missing.length === 0,
439
+ missing: [...missing],
440
+ scopes
441
+ };
442
+ }
443
+ function getGhToken(opts) {
444
+ const envToken = opts?.allowEnv === false ? null : getEnvGitHubToken();
445
+ if (envToken) {
446
+ return envToken;
447
+ }
448
+ return getGhTokenWithSource({
449
+ execImpl: opts?.execImpl,
450
+ envToken: void 0
451
+ }).token;
452
+ }
453
+ function getGhTokenWithSource(opts) {
454
+ const hasExplicitEnvToken = opts !== void 0 && Object.prototype.hasOwnProperty.call(opts, "envToken");
455
+ const envToken = hasExplicitEnvToken ? opts.envToken?.trim() ?? null : getEnvGitHubToken();
456
+ if (envToken) {
457
+ return { token: envToken, source: "env" };
458
+ }
459
+ const execImpl = opts?.execImpl ?? execFileSync;
460
+ try {
461
+ const token = execImpl("gh", ["auth", "token"], {
462
+ encoding: "utf8",
463
+ stdio: ["pipe", "pipe", "pipe"]
464
+ }).toString().trim();
465
+ if (!token) {
466
+ throw new GhAuthError("token_failed", ghTokenReadErrorMessage());
467
+ }
468
+ return { token, source: "gh" };
469
+ } catch (error) {
470
+ if (error instanceof GhAuthError) {
471
+ throw error;
472
+ }
473
+ throw new GhAuthError("token_failed", ghTokenReadErrorMessage());
474
+ }
475
+ }
476
+ async function validateGitHubToken(token, source, opts) {
477
+ const createClientImpl = opts?.createClientImpl ?? createClient;
478
+ const validateTokenImpl = opts?.validateTokenImpl ?? validateToken;
479
+ const checkRequiredScopesImpl = opts?.checkRequiredScopesImpl ?? checkRequiredScopes;
480
+ let viewer;
481
+ try {
482
+ const client = createClientImpl(token);
483
+ viewer = await validateTokenImpl(client);
484
+ } catch (error) {
485
+ throw classifyTokenValidationError(error, source);
486
+ }
487
+ const scopeCheck = checkRequiredScopesImpl(viewer.scopes);
488
+ if (!scopeCheck.valid) {
489
+ if (source === "env") {
490
+ throw new GhAuthError(
491
+ "missing_scopes",
492
+ `GITHUB_GRAPHQL_TOKEN is missing required scopes: ${scopeCheck.missing.join(", ")}`
493
+ );
494
+ }
495
+ throw new GhAuthError(
496
+ "missing_scopes",
497
+ missingGhScopesMessage(scopeCheck.missing)
498
+ );
499
+ }
500
+ return {
501
+ source,
502
+ token,
503
+ login: viewer.login,
504
+ scopes: viewer.scopes
505
+ };
506
+ }
507
+ async function resolveGitHubAuth(opts) {
508
+ const envToken = getEnvGitHubToken();
509
+ let envError = null;
510
+ if (envToken) {
511
+ try {
512
+ return await validateGitHubToken(envToken, "env", opts);
513
+ } catch (error) {
514
+ if (error instanceof GhAuthError) {
515
+ envError = error;
516
+ } else {
517
+ throw error;
518
+ }
519
+ }
520
+ }
521
+ try {
522
+ const auth = ensureGhAuth(opts);
523
+ return await validateGitHubToken(auth.token, "gh", opts);
524
+ } catch (error) {
525
+ if (envError && error instanceof GhAuthError) {
526
+ throw envError;
527
+ }
528
+ throw error;
529
+ }
530
+ }
531
+ function ensureGhAuth(opts) {
532
+ const execImpl = opts?.execImpl ?? execFileSync;
533
+ const spawnImpl = opts?.spawnImpl ?? spawnSync;
534
+ if (!checkGhInstalled({ execImpl })) {
535
+ throw new GhAuthError(
536
+ "not_installed",
537
+ "gh CLI is not installed. Install it from https://cli.github.com or set GITHUB_GRAPHQL_TOKEN."
538
+ );
539
+ }
540
+ const auth = checkGhAuthenticated({ spawnImpl });
541
+ if (!auth.authenticated) {
542
+ throw new GhAuthError(
543
+ "not_authenticated",
544
+ "Run 'gh auth login --scopes repo,read:org,project' or set GITHUB_GRAPHQL_TOKEN."
545
+ );
546
+ }
547
+ const scopeCheck = checkGhScopes({ spawnImpl });
548
+ if (!scopeCheck.valid) {
549
+ throw new GhAuthError(
550
+ "missing_scopes",
551
+ missingGhScopesMessage(scopeCheck.missing)
552
+ );
553
+ }
554
+ const { token } = getGhTokenWithSource({
555
+ execImpl,
556
+ envToken: void 0
557
+ });
558
+ return { login: auth.login ?? "unknown", token, source: "gh" };
559
+ }
560
+ function isInteractiveTerminal() {
561
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
562
+ }
563
+ function runGhAuthCommand(mode, opts) {
564
+ const spawnImpl = opts?.spawnImpl ?? spawnSync;
565
+ const command = `gh auth ${mode} --scopes ${REQUIRED_GH_SCOPES.join(",")}`;
566
+ const interactive = opts?.interactive ?? isInteractiveTerminal();
567
+ if (!interactive) {
568
+ return {
569
+ mode,
570
+ status: "manual",
571
+ command,
572
+ summary: `Interactive terminal not available. Run '${command}' manually.`
573
+ };
574
+ }
575
+ const result = spawnImpl(
576
+ "gh",
577
+ ["auth", mode, "--scopes", REQUIRED_GH_SCOPES.join(",")],
578
+ {
579
+ stdio: "inherit"
580
+ }
581
+ );
582
+ if ((result.status ?? 1) === 0) {
583
+ return {
584
+ mode,
585
+ status: "applied",
586
+ command,
587
+ summary: `Executed '${command}'.`
588
+ };
589
+ }
590
+ return {
591
+ mode,
592
+ status: "manual",
593
+ command,
594
+ summary: `Failed to complete '${command}' automatically. Re-run it manually.`
595
+ };
596
+ }
597
+ function runGhAuthLogin(opts) {
598
+ return runGhAuthCommand("login", opts);
599
+ }
600
+ function runGhAuthRefresh(opts) {
601
+ return runGhAuthCommand("refresh", opts);
602
+ }
603
+ function parseLogin(output) {
604
+ const matched = output.match(
605
+ /Logged in to github\.com account\s+\*?\*?([A-Za-z0-9_-]+)\*?\*?/i
606
+ );
607
+ return matched?.[1];
608
+ }
609
+ function parseScopes(output) {
610
+ const matched = output.match(/Token scopes:\s*(.+)/i);
611
+ if (!matched) {
612
+ return [];
613
+ }
614
+ return matched[1].split(",").map((scope) => scope.trim().replace(/^'+|'+$/g, "")).filter((scope) => scope.length > 0);
615
+ }
616
+
617
+ export {
618
+ GitHubApiError,
619
+ GitHubScopeError,
620
+ createClient,
621
+ validateToken,
622
+ checkRequiredScopes,
623
+ listUserProjects,
624
+ getProjectDetail,
625
+ REQUIRED_GH_SCOPES,
626
+ GhAuthError,
627
+ getEnvGitHubToken,
628
+ checkGhInstalled,
629
+ checkGhAuthenticated,
630
+ checkGhScopes,
631
+ getGhToken,
632
+ getGhTokenWithSource,
633
+ validateGitHubToken,
634
+ resolveGitHubAuth,
635
+ ensureGhAuth,
636
+ runGhAuthLogin,
637
+ runGhAuthRefresh
638
+ };
@@ -19,7 +19,7 @@ import {
19
19
  import {
20
20
  handleMissingManagedProjectConfig,
21
21
  resolveManagedProjectConfig
22
- } from "./chunk-TH5QPO3Y.js";
22
+ } from "./chunk-C7G7RJ4G.js";
23
23
 
24
24
  // src/commands/status.ts
25
25
  import { readFile } from "fs/promises";
@@ -30,7 +30,7 @@ var COL_ID = 24;
30
30
  var COL_STATUS = 14;
31
31
  var COL_PID = 8;
32
32
  var COL_AGE_TURN = 12;
33
- var COL_TOKENS = 10;
33
+ var COL_TOKENS = 17;
34
34
  var COL_SESSION = 14;
35
35
  var COL_ID_HEADER = COL_ID + 2;
36
36
  var identity = (s) => s;
@@ -63,6 +63,11 @@ function compactSessionId(id) {
63
63
  function fmtTokens(n) {
64
64
  return n.toLocaleString("en-US");
65
65
  }
66
+ function fmtTokenPair(delta, cumulative) {
67
+ const left = fmtTokens(delta ?? 0);
68
+ const right = fmtTokens(cumulative ?? delta ?? 0);
69
+ return `${left} / ${right}`;
70
+ }
66
71
  function fmtAge(startedAt, now) {
67
72
  if (!startedAt) return "0m";
68
73
  const diffMs = now - new Date(startedAt).getTime();
@@ -173,7 +178,10 @@ function activeRunRow(run, now, evtWidth, c) {
173
178
  const turn = run.turnCount ?? 0;
174
179
  const ageTurn = pad(`${age}/${turn}`, COL_AGE_TURN);
175
180
  const tokens = pad(
176
- fmtTokens(run.tokenUsage?.totalTokens ?? 0),
181
+ fmtTokenPair(
182
+ run.tokenUsage?.totalTokens,
183
+ run.tokenUsage?.cumulativeTotalTokens
184
+ ),
177
185
  COL_TOKENS,
178
186
  "right"
179
187
  );
@@ -260,6 +268,15 @@ function truncate(s, len) {
260
268
  if (s.length <= len) return s;
261
269
  return s.slice(0, len - 3) + "...";
262
270
  }
271
+ function formatTokenPair(delta, cumulative) {
272
+ return `${delta.toLocaleString("en-US")} / ${cumulative.toLocaleString("en-US")}`;
273
+ }
274
+ function resolveProjectTokenDelta(snapshot) {
275
+ return snapshot.activeRuns.reduce(
276
+ (sum, run) => sum + (run.tokenUsage?.totalTokens ?? 0),
277
+ 0
278
+ );
279
+ }
263
280
  function renderLegacyStatus(snapshot, noColor) {
264
281
  const apply = noColor ? (s) => stripAnsi(s) : (s) => s;
265
282
  const lines = [];
@@ -326,12 +343,13 @@ function renderLegacyStatus(snapshot, noColor) {
326
343
  lines.push("");
327
344
  }
328
345
  if (snapshot.codexTotals) {
346
+ const tokenDelta = resolveProjectTokenDelta(snapshot);
329
347
  const tokenStr = apply(
330
- `Tokens: ${snapshot.codexTotals.inputTokens} in / ${snapshot.codexTotals.outputTokens} out / ${snapshot.codexTotals.totalTokens} total`
348
+ `Tokens: ${formatTokenPair(tokenDelta, snapshot.codexTotals.totalTokens)} total`
331
349
  );
332
350
  lines.push(` ${tokenStr}`);
333
351
  } else {
334
- lines.push(" Tokens: 0 in / 0 out / 0 total");
352
+ lines.push(" Tokens: 0 / 0 total");
335
353
  }
336
354
  return lines.join("\n");
337
355
  }
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  handleMissingManagedProjectConfig,
4
4
  resolveManagedProjectConfig
5
- } from "./chunk-TH5QPO3Y.js";
5
+ } from "./chunk-C7G7RJ4G.js";
6
6
  import {
7
7
  daemonPidPath
8
8
  } from "./chunk-ROGRTUFI.js";