@indigoai-us/hq-cli 5.345.92 → 5.345.94

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 (48) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/command-catalog.generated.d.ts +252 -0
  3. package/dist/command-catalog.generated.js +306 -0
  4. package/dist/command-registration-plan.d.ts +6 -0
  5. package/dist/command-registration-plan.js +1 -0
  6. package/dist/commands/capacity.d.ts +71 -0
  7. package/dist/commands/capacity.js +666 -0
  8. package/dist/commands/integrations-connect.js +64 -5
  9. package/dist/commands/lanes-accounts.d.ts +1 -0
  10. package/dist/commands/lanes-accounts.js +6 -0
  11. package/dist/lib/bot/api.d.ts +21 -0
  12. package/dist/lib/bot/api.js +26 -0
  13. package/dist/lib/bot/prompt.js +2 -1
  14. package/dist/lib/bot/room-mentions.d.ts +26 -0
  15. package/dist/lib/bot/room-mentions.js +40 -0
  16. package/dist/lib/bot/run.js +44 -6
  17. package/dist/lib/capacity/accounts.d.ts +36 -0
  18. package/dist/lib/capacity/accounts.js +90 -0
  19. package/dist/lib/capacity/collect.d.ts +33 -0
  20. package/dist/lib/capacity/collect.js +164 -0
  21. package/dist/lib/capacity/collectors/claude-oauth.d.ts +145 -0
  22. package/dist/lib/capacity/collectors/claude-oauth.js +1211 -0
  23. package/dist/lib/capacity/collectors/claude-statusline.d.ts +72 -0
  24. package/dist/lib/capacity/collectors/claude-statusline.js +436 -0
  25. package/dist/lib/capacity/collectors/codex.d.ts +120 -0
  26. package/dist/lib/capacity/collectors/codex.js +1134 -0
  27. package/dist/lib/capacity/collectors/grok.d.ts +86 -0
  28. package/dist/lib/capacity/collectors/grok.js +588 -0
  29. package/dist/lib/capacity/lane-login.d.ts +29 -0
  30. package/dist/lib/capacity/lane-login.js +91 -0
  31. package/dist/lib/capacity/ledger.d.ts +399 -0
  32. package/dist/lib/capacity/ledger.js +1441 -0
  33. package/dist/lib/capacity/project.d.ts +12 -0
  34. package/dist/lib/capacity/project.js +264 -0
  35. package/dist/lib/capacity/read.d.ts +62 -0
  36. package/dist/lib/capacity/read.js +264 -0
  37. package/dist/lib/capacity/refusal.d.ts +30 -0
  38. package/dist/lib/capacity/refusal.js +183 -0
  39. package/dist/lib/capacity/scheduler.d.ts +76 -0
  40. package/dist/lib/capacity/scheduler.js +800 -0
  41. package/dist/lib/capacity/sign-in.d.ts +103 -0
  42. package/dist/lib/capacity/sign-in.js +517 -0
  43. package/dist/lib/capacity/widget.d.ts +22 -0
  44. package/dist/lib/capacity/widget.js +418 -0
  45. package/dist/lib/lanes/spawn.js +12 -0
  46. package/dist/lib/mesh/live/daemon/run.d.ts +5 -0
  47. package/dist/lib/mesh/live/daemon/run.js +29 -1
  48. package/package.json +1 -1
@@ -0,0 +1,164 @@
1
+ /**
2
+ * One-shot capacity collection (US-011).
3
+ *
4
+ * The daemon is the resident collector. This runs the same readers once
5
+ * into whatever ledger root the caller passes, so a check never writes
6
+ * ~/.hq/capacity unless that root is requested.
7
+ */
8
+ import { execFile } from "node:child_process";
9
+ import * as fs from "node:fs";
10
+ import * as path from "node:path";
11
+ import { listCapacityAccounts } from "./accounts.js";
12
+ import { ingestCodexResult, isCodexProcessRunning, latestCodexSessionWriteAt, readCodexAppServer, readInstalledCodexVersion, shouldReadAppServer, tailCodexSessions, } from "./collectors/codex.js";
13
+ import { collectClaudeOAuth, } from "./collectors/claude-oauth.js";
14
+ import { consumeClaudeStatuslineSpool } from "./collectors/claude-statusline.js";
15
+ import { collectGrok } from "./collectors/grok.js";
16
+ import { capacityRoot, rebuildNow, } from "./ledger.js";
17
+ export async function collectCapacityOnce(options = {}) {
18
+ const env = options.env ?? process.env;
19
+ const root = options.root ?? capacityRoot(env, options.home);
20
+ const ledger = {
21
+ root,
22
+ env,
23
+ home: options.home,
24
+ ...(options.now ? { now: options.now } : {}),
25
+ };
26
+ const accounts = options.accounts
27
+ ? [...options.accounts]
28
+ : listCapacityAccounts({
29
+ home: options.home,
30
+ hqRoot: options.hqRoot,
31
+ hostname: options.hostname,
32
+ env,
33
+ });
34
+ const claude = accounts.filter((account) => account.provider === "claude");
35
+ const codex = accounts.filter((account) => account.provider === "codex");
36
+ const grok = accounts.filter((account) => account.provider === "grok");
37
+ await collectClaudeOAuth({
38
+ ledger,
39
+ now: options.now,
40
+ allowDisabled: true,
41
+ accounts: claude.map((account) => ({ label: account.label, home: account.home })),
42
+ ...options.claude,
43
+ });
44
+ await collectCodexHomes(codex, ledger, options);
45
+ await collectGrok({
46
+ ledger,
47
+ now: options.now,
48
+ allowDisabled: true,
49
+ accounts: grok.map((account) => ({ label: account.label, home: account.home })),
50
+ ...options.grok,
51
+ });
52
+ if (options.includeSpool !== false) {
53
+ consumeClaudeStatuslineSpool({
54
+ root,
55
+ now: options.now ? options.now() : new Date(),
56
+ accounts: claude.map((account) => ({ label: account.label, home: account.home })),
57
+ });
58
+ }
59
+ return rebuildNow(ledger);
60
+ }
61
+ async function collectCodexHomes(accounts, ledger, options) {
62
+ const groups = new Map();
63
+ for (const account of accounts) {
64
+ const key = path.resolve(account.home);
65
+ const group = groups.get(key) ?? { home: account.home, labels: [] };
66
+ if (!group.labels.includes(account.label))
67
+ group.labels.push(account.label);
68
+ groups.set(key, group);
69
+ }
70
+ let cliVersion = options.codex?.cliVersion;
71
+ if (!cliVersion) {
72
+ try {
73
+ cliVersion = await readInstalledCodexVersion(options.codex?.spawn);
74
+ }
75
+ catch {
76
+ cliVersion = "unknown";
77
+ }
78
+ }
79
+ const now = options.now ? options.now() : new Date();
80
+ const processes = options.codex?.skipAppServer ? "codex" : await processListAsync();
81
+ const root = ledger.root;
82
+ let state = root ? readTailState(root) : undefined;
83
+ for (const group of groups.values()) {
84
+ const label = group.labels.find((item) => item !== "default") ?? group.labels[0] ?? "default";
85
+ const sessionsDir = path.join(group.home, "sessions");
86
+ try {
87
+ const tailed = tailCodexSessions({
88
+ sessionsDir,
89
+ state,
90
+ cliVersion,
91
+ now,
92
+ recentDays: 2,
93
+ accountId: `home:${path.resolve(group.home)}`,
94
+ accountLabel: label,
95
+ accountLabels: group.labels,
96
+ accountHome: group.home,
97
+ });
98
+ ingestCodexResult(tailed, ledger);
99
+ state = tailed.state;
100
+ if (root)
101
+ writeTailState(root, state);
102
+ }
103
+ catch {
104
+ // One home's tail must not stop the others.
105
+ }
106
+ if (options.codex?.skipAppServer)
107
+ continue;
108
+ const lastWrite = latestCodexSessionWriteAt(sessionsDir);
109
+ if (!shouldReadAppServer({
110
+ lastSessionWriteAt: lastWrite,
111
+ codexProcessRunning: isCodexProcessRunning(processes),
112
+ now: now.getTime(),
113
+ }))
114
+ continue;
115
+ try {
116
+ const result = await readCodexAppServer({
117
+ cliVersion,
118
+ now,
119
+ home: group.home,
120
+ accountId: `home:${path.resolve(group.home)}`,
121
+ accountLabel: label,
122
+ accountLabels: group.labels,
123
+ accountHome: group.home,
124
+ spawn: options.codex?.spawn,
125
+ timeoutMs: 20_000,
126
+ });
127
+ ingestCodexResult(result, ledger);
128
+ }
129
+ catch {
130
+ // A refused or busy app-server is not a credential read.
131
+ }
132
+ }
133
+ }
134
+ const TAIL_STATE_FILE = "codex-tail-state.json";
135
+ function readTailState(root) {
136
+ try {
137
+ const parsed = JSON.parse(fs.readFileSync(path.join(root, TAIL_STATE_FILE), "utf8"));
138
+ if (!parsed || typeof parsed !== "object" || !parsed.files)
139
+ return undefined;
140
+ return parsed;
141
+ }
142
+ catch {
143
+ return undefined;
144
+ }
145
+ }
146
+ function writeTailState(root, state) {
147
+ const dest = path.join(root, TAIL_STATE_FILE);
148
+ const tmp = `${dest}.${process.pid}.tmp`;
149
+ fs.mkdirSync(root, { recursive: true, mode: 0o700 });
150
+ fs.writeFileSync(tmp, `${JSON.stringify(state)}\n`, { mode: 0o600 });
151
+ fs.renameSync(tmp, dest);
152
+ fs.chmodSync(dest, 0o600);
153
+ }
154
+ function processListAsync() {
155
+ return new Promise((resolve) => {
156
+ execFile("ps", ["-ax", "-o", "command="], { timeout: 5_000, encoding: "utf8" }, (error, stdout) => {
157
+ if (error)
158
+ resolve("");
159
+ else
160
+ resolve(typeof stdout === "string" ? stdout : "");
161
+ });
162
+ });
163
+ }
164
+ //# sourceMappingURL=collect.js.map
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Claude OAuth usage collector (US-005).
3
+ *
4
+ * Reads the Claude Code credential read-only (macOS Keychain item
5
+ * `Claude Code-credentials`, plus `Claude Code-credentials-<hash>` seats,
6
+ * or the credentials JSON under ~/.claude/ on Linux). GETs
7
+ * https://api.anthropic.com/api/oauth/usage with the oauth-2025-04-20 beta
8
+ * header. This module does not refresh the token, write the Keychain or
9
+ * ~/.claude, call an OAuth token endpoint, or invoke the claude CLI except
10
+ * `claude --version` when the caller does not pass cliVersion.
11
+ *
12
+ * The legacy shape (seven_day_opus populated, no limits[]) is drift.
13
+ * observedAt is the response time. One collect appends every account's
14
+ * readings and failures in one rebuild.
15
+ */
16
+ import { type LedgerOptions, type NowView, type ParserFailureEvent, type ReadingInput } from "../ledger.js";
17
+ export declare const CLAUDE_OAUTH_PARSER_VERSION = "claude-oauth-usage.1";
18
+ export declare const CLAUDE_OAUTH_PROVIDER = "claude";
19
+ export declare const CLAUDE_OAUTH_SOURCE: "oauth-usage";
20
+ export declare const CLAUDE_OAUTH_URL = "https://api.anthropic.com/api/oauth/usage";
21
+ export declare const CLAUDE_OAUTH_PROFILE_URL = "https://api.anthropic.com/api/oauth/profile";
22
+ export declare const CLAUDE_OAUTH_BETA = "oauth-2025-04-20";
23
+ export declare const CLAUDE_ORG_CACHE_MS: number;
24
+ export declare const CLAUDE_ORG_CACHE_FILE = "claude-org-cache.json";
25
+ export declare const CLAUDE_OAUTH_POLL_MS: number;
26
+ /** Freshness is shorter than the 5 minute tick so the next tick is not skipped. */
27
+ export declare const CLAUDE_OAUTH_FRESH_MS: number;
28
+ export declare const CLAUDE_OAUTH_KEYCHAIN_BACKOFF_MS: number;
29
+ export declare const CLAUDE_OAUTH_FETCH_TIMEOUT_MS = 10000;
30
+ export declare const CLAUDE_OAUTH_SECURITY_TIMEOUT_MS = 5000;
31
+ export declare const CLAUDE_OAUTH_POLL_FILE = "claude-oauth-last-call.json";
32
+ export declare const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
33
+ export interface ClaudeOAuthFetchInit {
34
+ method: "GET";
35
+ headers: Record<string, string>;
36
+ redirect: "manual";
37
+ signal?: AbortSignal;
38
+ }
39
+ export type ClaudeOAuthFetch = (url: string, init: ClaudeOAuthFetchInit) => Promise<{
40
+ status: number;
41
+ text: () => Promise<string>;
42
+ headers?: {
43
+ get(name: string): string | null;
44
+ };
45
+ }>;
46
+ export interface SecurityExecResult {
47
+ status: number | null;
48
+ stdout: string;
49
+ stderr: string;
50
+ timedOut: boolean;
51
+ }
52
+ export type SecurityExec = (args: readonly string[], timeoutMs: number) => SecurityExecResult;
53
+ export type ClaudeKeychainStatus = "no-prompt" | "prompt" | "timeout" | "error";
54
+ export interface ClaudeCredentialAccount {
55
+ token: string;
56
+ /** Already hashed. Raw org ids and keychain account names are not accepted here. */
57
+ accountId: string;
58
+ expiresAt?: number | null;
59
+ scopes?: readonly string[] | null;
60
+ planType?: string | null;
61
+ }
62
+ export interface ReadClaudeCredentialsResult {
63
+ keychain: ClaudeKeychainStatus;
64
+ accounts: ClaudeCredentialAccount[];
65
+ }
66
+ export type ReadClaudeCredentials = () => ReadClaudeCredentialsResult;
67
+ export interface ClaudeOAuthCollectResult {
68
+ readings: ReadingInput[];
69
+ failures: ParserFailureEvent[];
70
+ view: NowView | null;
71
+ skipped?: "disabled" | "rate-limit" | "keychain-backoff";
72
+ keychain: ClaudeKeychainStatus;
73
+ httpStatus: number | null;
74
+ accountIds: string[];
75
+ }
76
+ export interface CollectClaudeOAuthOptions {
77
+ /** Linux credentials JSON. When set, the Keychain is not read. */
78
+ credentialsPath?: string;
79
+ /** Replaces Keychain and credentials-file discovery. */
80
+ readCredentials?: ReadClaudeCredentials;
81
+ /** Replaces `security`. Used by the default Keychain reader. */
82
+ securityExec?: SecurityExec;
83
+ endpoint?: string;
84
+ cliVersion?: string;
85
+ readVersion?: () => string;
86
+ now?: () => Date;
87
+ fetchImpl?: ClaudeOAuthFetch;
88
+ ledger?: LedgerOptions;
89
+ platform?: NodeJS.Platform;
90
+ /** Keychain account attribute used when discovery is not available. */
91
+ keychainAccount?: string;
92
+ /**
93
+ * When set, read each home instead of the unscoped keychain dump.
94
+ * One entry per label. Homes that resolve to the same path are one read.
95
+ */
96
+ accounts?: readonly {
97
+ label: string;
98
+ home: string;
99
+ }[];
100
+ /** One-shot collect reads while the resident collector stays disabled. */
101
+ allowDisabled?: boolean;
102
+ }
103
+ interface MapOptions {
104
+ observedAt: string;
105
+ cliVersion: string;
106
+ accountId: string;
107
+ planType: string | null;
108
+ accountLabel?: string;
109
+ accountLabels?: string[];
110
+ organization?: string | null;
111
+ accountHome?: string;
112
+ }
113
+ export declare function claudeOAuthPollPath(root: string): string;
114
+ export declare function defaultClaudeCredentialsPath(home?: string): string;
115
+ /**
116
+ * Claude Code's keychain service for a config dir.
117
+ * The default home uses `Claude Code-credentials`. Any other resolved
118
+ * path uses that name plus the first 8 hex chars of SHA-256(utf8 path).
119
+ */
120
+ export declare function claudeKeychainServiceName(configDir: string, defaultConfigDir?: string): string;
121
+ export declare function parseClaudeCliVersion(text: string): string | null;
122
+ /** Map a usage JSON body. Does not read credentials or call the network. */
123
+ export declare function mapClaudeOAuthUsage(body: unknown, options: MapOptions): {
124
+ readings: ReadingInput[];
125
+ failures: ParserFailureEvent[];
126
+ };
127
+ export declare function collectClaudeOAuth(options?: CollectClaudeOAuthOptions): Promise<ClaudeOAuthCollectResult>;
128
+ /**
129
+ * Ledger account id → hashed profile account id. Rows with no captured
130
+ * identity are omitted. The raw uuid is never returned.
131
+ */
132
+ export declare function claudeIdentityKeys(root: string): ReadonlyMap<string, string>;
133
+ /**
134
+ * Claude names an individual plan's organization "<email>'s Organization".
135
+ * The ledger never stores an email, so such a name becomes the email's
136
+ * domain (for example "getindigo.ai"), or "Personal" for a consumer mail
137
+ * domain. Any other name containing "@" is dropped.
138
+ */
139
+ export declare function displayOrganization(name: string): string | null;
140
+ export declare function claudeSeatAccountId(seat: string, orgId: string | null): string;
141
+ export declare function readInstalledClaudeVersion(readVersion?: () => string): string;
142
+ /** Async `claude --version` for the daemon. The sync reader stays for one-shot callers. */
143
+ export declare function readInstalledClaudeVersionAsync(): Promise<string>;
144
+ export {};
145
+ //# sourceMappingURL=claude-oauth.d.ts.map