@ai-devkit/agent-manager 0.37.0 → 0.38.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.
- package/dist/AgentManager.d.ts +0 -4
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +2 -2
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/capacity/anthropic.test.js +115 -0
- package/dist/__tests__/capacity/anthropic.test.js.map +1 -0
- package/dist/__tests__/capacity/boundaries.test.js +45 -0
- package/dist/__tests__/capacity/boundaries.test.js.map +1 -0
- package/dist/__tests__/capacity/claude.test.js +3 -3
- package/dist/__tests__/capacity/claude.test.js.map +1 -1
- package/dist/__tests__/capacity/codex.test.js +3 -1
- package/dist/__tests__/capacity/codex.test.js.map +1 -1
- package/dist/__tests__/capacity/openai.test.js +21 -12
- package/dist/__tests__/capacity/openai.test.js.map +1 -1
- package/dist/__tests__/capacity/wham.test.js +1 -1
- package/dist/__tests__/capacity/wham.test.js.map +1 -1
- package/dist/__tests__/capacity/zai.test.js +7 -8
- package/dist/__tests__/capacity/zai.test.js.map +1 -1
- package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js +2 -1
- package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.js.map +1 -1
- package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js +7 -15
- package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js.map +1 -1
- package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js +3 -1
- package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js +11 -26
- package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js.map +1 -1
- package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js +3 -7
- package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js.map +1 -1
- package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js +5 -11
- package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js.map +1 -1
- package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js +9 -19
- package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js.map +1 -1
- package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js +3 -7
- package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js.map +1 -1
- package/dist/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.js +31 -10
- package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -0
- package/dist/__tests__/harnesses/runtimeProfiles.test.js +89 -0
- package/dist/__tests__/harnesses/runtimeProfiles.test.js.map +1 -0
- package/dist/__tests__/print/DurableAgent.test.js +4 -6
- package/dist/__tests__/print/DurableAgent.test.js.map +1 -1
- package/dist/__tests__/print/DurableAgentRepository.test.js +1 -12
- package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -1
- package/dist/__tests__/runtime/ManagedAgentRuntime.test.js.map +1 -1
- package/dist/__tests__/terminal/TmuxManager.test.js +1 -1
- package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -1
- package/dist/capacity/index.d.ts +9 -19
- package/dist/capacity/index.d.ts.map +1 -1
- package/dist/capacity/index.js +11 -4
- package/dist/capacity/index.js.map +1 -1
- package/dist/capacity/providers/anthropic.d.ts +9 -0
- package/dist/capacity/providers/anthropic.d.ts.map +1 -0
- package/dist/capacity/{claude.js → providers/anthropic.js} +10 -128
- package/dist/capacity/providers/anthropic.js.map +1 -0
- package/dist/capacity/providers/openai-platform.d.ts +14 -0
- package/dist/capacity/providers/openai-platform.d.ts.map +1 -0
- package/dist/capacity/providers/openai-platform.js +120 -0
- package/dist/capacity/providers/openai-platform.js.map +1 -0
- package/dist/capacity/providers/openai-subscription.d.ts +20 -0
- package/dist/capacity/providers/openai-subscription.d.ts.map +1 -0
- package/dist/capacity/providers/openai-subscription.js +129 -0
- package/dist/capacity/providers/openai-subscription.js.map +1 -0
- package/dist/capacity/providers/zai.d.ts +8 -0
- package/dist/capacity/providers/zai.d.ts.map +1 -0
- package/dist/capacity/{zai.js → providers/zai.js} +17 -62
- package/dist/capacity/providers/zai.js.map +1 -0
- package/dist/capacity/sources/claude.d.ts +9 -0
- package/dist/capacity/sources/claude.d.ts.map +1 -0
- package/dist/capacity/sources/claude.js +17 -0
- package/dist/capacity/sources/claude.js.map +1 -0
- package/dist/capacity/{codex.d.ts → sources/codex.d.ts} +5 -9
- package/dist/capacity/sources/codex.d.ts.map +1 -0
- package/dist/capacity/{codex.js → sources/codex.js} +31 -84
- package/dist/capacity/sources/codex.js.map +1 -0
- package/dist/capacity/sources/pi.d.ts +30 -0
- package/dist/capacity/sources/pi.d.ts.map +1 -0
- package/dist/capacity/sources/pi.js +81 -0
- package/dist/capacity/sources/pi.js.map +1 -0
- package/dist/capacity/types.d.ts +2 -0
- package/dist/capacity/types.d.ts.map +1 -1
- package/dist/capacity/types.js +1 -1
- package/dist/capacity/types.js.map +1 -1
- package/dist/harnesses/claude/credentials.d.ts +10 -0
- package/dist/harnesses/claude/credentials.d.ts.map +1 -0
- package/dist/harnesses/claude/credentials.js +112 -0
- package/dist/harnesses/claude/credentials.js.map +1 -0
- package/dist/harnesses/claude/readiness.d.ts +3 -0
- package/dist/harnesses/claude/readiness.d.ts.map +1 -0
- package/dist/harnesses/claude/readiness.js +63 -0
- package/dist/harnesses/claude/readiness.js.map +1 -0
- package/dist/harnesses/codex/credentials.d.ts +15 -0
- package/dist/harnesses/codex/credentials.d.ts.map +1 -0
- package/dist/harnesses/codex/credentials.js +58 -0
- package/dist/harnesses/codex/credentials.js.map +1 -0
- package/dist/harnesses/codex/readiness.d.ts +3 -0
- package/dist/harnesses/codex/readiness.d.ts.map +1 -0
- package/dist/harnesses/codex/readiness.js +60 -0
- package/dist/harnesses/codex/readiness.js.map +1 -0
- package/dist/harnesses/copilot/readiness.d.ts +3 -0
- package/dist/harnesses/copilot/readiness.d.ts.map +1 -0
- package/dist/harnesses/copilot/readiness.js +39 -0
- package/dist/harnesses/copilot/readiness.js.map +1 -0
- package/dist/harnesses/opencode/readiness.d.ts +3 -0
- package/dist/harnesses/opencode/readiness.d.ts.map +1 -0
- package/dist/harnesses/opencode/readiness.js +50 -0
- package/dist/harnesses/opencode/readiness.js.map +1 -0
- package/dist/harnesses/pi/credentials.d.ts +22 -0
- package/dist/harnesses/pi/credentials.d.ts.map +1 -0
- package/dist/harnesses/pi/credentials.js +98 -0
- package/dist/harnesses/pi/credentials.js.map +1 -0
- package/dist/harnesses/pi/readiness.d.ts +3 -0
- package/dist/harnesses/pi/readiness.d.ts.map +1 -0
- package/dist/harnesses/pi/readiness.js +96 -0
- package/dist/harnesses/pi/readiness.js.map +1 -0
- package/dist/harnesses/readiness/AgentReadiness.d.ts +6 -0
- package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -0
- package/dist/harnesses/readiness/AgentReadiness.js +68 -0
- package/dist/harnesses/readiness/AgentReadiness.js.map +1 -0
- package/dist/harnesses/readiness/checks.d.ts +31 -0
- package/dist/harnesses/readiness/checks.d.ts.map +1 -0
- package/dist/harnesses/readiness/checks.js +270 -0
- package/dist/harnesses/readiness/checks.js.map +1 -0
- package/dist/{readiness/AgentReadiness.d.ts → harnesses/readiness/types.d.ts} +19 -10
- package/dist/harnesses/readiness/types.d.ts.map +1 -0
- package/dist/harnesses/readiness/types.js +4 -0
- package/dist/harnesses/readiness/types.js.map +1 -0
- package/dist/harnesses/runtimeProfiles.d.ts +11 -0
- package/dist/harnesses/runtimeProfiles.d.ts.map +1 -0
- package/dist/harnesses/runtimeProfiles.js +99 -0
- package/dist/harnesses/runtimeProfiles.js.map +1 -0
- package/dist/index.d.ts +16 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -23
- package/dist/index.js.map +1 -1
- package/dist/runtime/ManagedAgentRuntime.d.ts +3 -3
- package/dist/runtime/ManagedAgentRuntime.d.ts.map +1 -1
- package/dist/runtime/ManagedAgentRuntime.js +6 -20
- package/dist/runtime/ManagedAgentRuntime.js.map +1 -1
- package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -1
- package/dist/runtime/herdr/HerdrAgentDiscovery.js +2 -43
- package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -1
- package/dist/runtime/tmux/PidPolling.d.ts +1 -1
- package/dist/runtime/tmux/PidPolling.d.ts.map +1 -1
- package/dist/runtime/tmux/PidPolling.js.map +1 -1
- package/dist/runtime/tmux/TmuxAgentRuntime.d.ts +1 -1
- package/dist/runtime/tmux/TmuxAgentRuntime.d.ts.map +1 -1
- package/dist/runtime/tmux/TmuxAgentRuntime.js +1 -1
- package/dist/runtime/tmux/TmuxAgentRuntime.js.map +1 -1
- package/dist/runtime/tmux/TmuxManager.d.ts +12 -0
- package/dist/runtime/tmux/TmuxManager.d.ts.map +1 -0
- package/dist/{terminal → runtime/tmux}/TmuxManager.js +9 -28
- package/dist/runtime/tmux/TmuxManager.js.map +1 -0
- package/dist/runtime/types.d.ts +1 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/terminal/TerminalFocusManager.d.ts +4 -32
- package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
- package/dist/terminal/TerminalFocusManager.js +17 -267
- package/dist/terminal/TerminalFocusManager.js.map +1 -1
- package/dist/terminal/TtyWriter.d.ts +1 -44
- package/dist/terminal/TtyWriter.d.ts.map +1 -1
- package/dist/terminal/TtyWriter.js +11 -347
- package/dist/terminal/TtyWriter.js.map +1 -1
- package/dist/terminal/backends/index.d.ts +5 -0
- package/dist/terminal/backends/index.d.ts.map +1 -0
- package/dist/terminal/backends/index.js +19 -0
- package/dist/terminal/backends/index.js.map +1 -0
- package/dist/terminal/backends/iterm2.d.ts +3 -0
- package/dist/terminal/backends/iterm2.d.ts.map +1 -0
- package/dist/terminal/backends/iterm2.js +135 -0
- package/dist/terminal/backends/iterm2.js.map +1 -0
- package/dist/terminal/backends/shared.d.ts +7 -0
- package/dist/terminal/backends/shared.d.ts.map +1 -0
- package/dist/terminal/backends/shared.js +35 -0
- package/dist/terminal/backends/shared.js.map +1 -0
- package/dist/terminal/backends/terminal-app.d.ts +3 -0
- package/dist/terminal/backends/terminal-app.d.ts.map +1 -0
- package/dist/terminal/backends/terminal-app.js +129 -0
- package/dist/terminal/backends/terminal-app.js.map +1 -0
- package/dist/terminal/backends/tmux.d.ts +3 -0
- package/dist/terminal/backends/tmux.d.ts.map +1 -0
- package/dist/terminal/backends/tmux.js +76 -0
- package/dist/terminal/backends/tmux.js.map +1 -0
- package/dist/terminal/backends/wezterm.d.ts +3 -0
- package/dist/terminal/backends/wezterm.d.ts.map +1 -0
- package/dist/terminal/backends/wezterm.js +71 -0
- package/dist/terminal/backends/wezterm.js.map +1 -0
- package/dist/terminal/types.d.ts +21 -0
- package/dist/terminal/types.d.ts.map +1 -0
- package/dist/terminal/types.js +10 -0
- package/dist/terminal/types.js.map +1 -0
- package/package.json +1 -1
- package/src/AgentManager.ts +1 -1
- package/src/__tests__/capacity/anthropic.test.ts +105 -0
- package/src/__tests__/capacity/boundaries.test.ts +36 -0
- package/src/__tests__/capacity/claude.test.ts +3 -8
- package/src/__tests__/capacity/codex.test.ts +12 -35
- package/src/__tests__/capacity/fixtures/openai-codex-auth.json +1 -1
- package/src/__tests__/capacity/openai.test.ts +15 -15
- package/src/__tests__/capacity/wham.test.ts +1 -1
- package/src/__tests__/capacity/zai.test.ts +27 -62
- package/src/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.ts +2 -2
- package/src/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.ts +1 -1
- package/src/__tests__/harnesses/codex/durable/CodexCliProbe.test.ts +14 -19
- package/src/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.ts +3 -3
- package/src/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.ts +24 -30
- package/src/__tests__/harnesses/codex/durable/CodexPrintRunner.test.ts +4 -8
- package/src/__tests__/harnesses/pi/durable/PiCliProbe.test.ts +7 -11
- package/src/__tests__/harnesses/pi/durable/PiPrintAgentService.test.ts +19 -22
- package/src/__tests__/harnesses/pi/durable/PiPrintRunner.test.ts +7 -8
- package/src/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.ts +36 -9
- package/src/__tests__/harnesses/runtimeProfiles.test.ts +110 -0
- package/src/__tests__/print/DurableAgent.test.ts +4 -13
- package/src/__tests__/print/DurableAgentRepository.test.ts +1 -13
- package/src/__tests__/runtime/ManagedAgentRuntime.test.ts +1 -1
- package/src/__tests__/terminal/TmuxManager.test.ts +1 -1
- package/src/capacity/index.ts +29 -23
- package/src/capacity/{claude.ts → providers/anthropic.ts} +16 -131
- package/src/capacity/providers/openai-platform.ts +150 -0
- package/src/capacity/{wham.ts → providers/openai-subscription.ts} +86 -8
- package/src/capacity/{zai.ts → providers/zai.ts} +29 -111
- package/src/capacity/sources/claude.ts +30 -0
- package/src/capacity/{codex.ts → sources/codex.ts} +54 -124
- package/src/capacity/sources/pi.ts +131 -0
- package/src/capacity/types.ts +3 -0
- package/src/harnesses/claude/credentials.ts +122 -0
- package/src/harnesses/claude/readiness.ts +69 -0
- package/src/harnesses/codex/credentials.ts +81 -0
- package/src/harnesses/codex/readiness.ts +72 -0
- package/src/harnesses/copilot/readiness.ts +33 -0
- package/src/harnesses/opencode/readiness.ts +48 -0
- package/src/harnesses/pi/credentials.ts +129 -0
- package/src/harnesses/pi/readiness.ts +104 -0
- package/src/harnesses/readiness/AgentReadiness.ts +87 -0
- package/src/harnesses/readiness/checks.ts +336 -0
- package/src/harnesses/readiness/types.ts +90 -0
- package/src/{utils/agents.ts → harnesses/runtimeProfiles.ts} +34 -38
- package/src/index.ts +15 -128
- package/src/runtime/ManagedAgentRuntime.ts +7 -22
- package/src/runtime/herdr/HerdrAgentDiscovery.ts +3 -19
- package/src/runtime/tmux/PidPolling.ts +1 -1
- package/src/runtime/tmux/TmuxAgentRuntime.ts +1 -1
- package/src/{terminal → runtime/tmux}/TmuxManager.ts +9 -30
- package/src/runtime/types.ts +1 -1
- package/src/terminal/TerminalFocusManager.ts +19 -292
- package/src/terminal/TtyWriter.ts +16 -336
- package/src/terminal/backends/index.ts +21 -0
- package/src/terminal/backends/iterm2.ts +126 -0
- package/src/terminal/backends/shared.ts +42 -0
- package/src/terminal/backends/terminal-app.ts +122 -0
- package/src/terminal/backends/tmux.ts +62 -0
- package/src/terminal/backends/wezterm.ts +65 -0
- package/src/terminal/types.ts +22 -0
- package/dist/__tests__/readiness/AgentReadiness.test.js.map +0 -1
- package/dist/__tests__/utils/agents.test.js +0 -93
- package/dist/__tests__/utils/agents.test.js.map +0 -1
- package/dist/capacity/claude.d.ts +0 -17
- package/dist/capacity/claude.d.ts.map +0 -1
- package/dist/capacity/claude.js.map +0 -1
- package/dist/capacity/codex.d.ts.map +0 -1
- package/dist/capacity/codex.js.map +0 -1
- package/dist/capacity/openai.d.ts +0 -47
- package/dist/capacity/openai.d.ts.map +0 -1
- package/dist/capacity/openai.js +0 -307
- package/dist/capacity/openai.js.map +0 -1
- package/dist/capacity/wham.d.ts +0 -16
- package/dist/capacity/wham.d.ts.map +0 -1
- package/dist/capacity/wham.js +0 -66
- package/dist/capacity/wham.js.map +0 -1
- package/dist/capacity/zai.d.ts +0 -14
- package/dist/capacity/zai.d.ts.map +0 -1
- package/dist/capacity/zai.js.map +0 -1
- package/dist/readiness/AgentReadiness.d.ts.map +0 -1
- package/dist/readiness/AgentReadiness.js +0 -614
- package/dist/readiness/AgentReadiness.js.map +0 -1
- package/dist/terminal/TmuxManager.d.ts +0 -28
- package/dist/terminal/TmuxManager.d.ts.map +0 -1
- package/dist/terminal/TmuxManager.js.map +0 -1
- package/dist/terminal/index.d.ts +0 -5
- package/dist/terminal/index.d.ts.map +0 -1
- package/dist/terminal/index.js +0 -5
- package/dist/terminal/index.js.map +0 -1
- package/dist/utils/agents.d.ts +0 -16
- package/dist/utils/agents.d.ts.map +0 -1
- package/dist/utils/agents.js +0 -117
- package/dist/utils/agents.js.map +0 -1
- package/dist/utils/index.d.ts +0 -8
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -6
- package/dist/utils/index.js.map +0 -1
- package/src/__tests__/utils/agents.test.ts +0 -105
- package/src/capacity/openai.ts +0 -355
- package/src/readiness/AgentReadiness.ts +0 -730
- package/src/terminal/index.ts +0 -4
- package/src/utils/index.ts +0 -14
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
9
|
+
|
|
10
|
+
export type ClaudeCredentialOptions = {
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
12
|
+
readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
|
|
13
|
+
now?: () => Date;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
platform?: NodeJS.Platform;
|
|
16
|
+
keychainRead?: (service: string) => Promise<string | null>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type Credential = { token: string | null; expired: boolean };
|
|
20
|
+
|
|
21
|
+
function record(value: unknown): Record<string, unknown> | null {
|
|
22
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
23
|
+
? (value as Record<string, unknown>)
|
|
24
|
+
: null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function nonEmptyText(value: unknown): string | null {
|
|
28
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function credentialsPath(options: ClaudeCredentialOptions): string {
|
|
32
|
+
const env = options.env ?? process.env;
|
|
33
|
+
const configured = nonEmptyText(env.CLAUDE_CONFIG_DIR);
|
|
34
|
+
if (configured) {
|
|
35
|
+
const profileRoot = isAbsolute(configured)
|
|
36
|
+
? configured
|
|
37
|
+
: resolve(options.cwd ?? process.cwd(), configured);
|
|
38
|
+
return join(profileRoot, ".credentials.json");
|
|
39
|
+
}
|
|
40
|
+
return join(env.HOME || homedir(), ".claude", ".credentials.json");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function credentialFromRoot(root: Record<string, unknown> | null, now: Date): Credential {
|
|
44
|
+
const oauth = record(root?.claudeAiOauth);
|
|
45
|
+
const token = nonEmptyText(oauth?.accessToken);
|
|
46
|
+
if (!token) return { token: null, expired: false };
|
|
47
|
+
const expired =
|
|
48
|
+
typeof oauth?.expiresAt === "number" &&
|
|
49
|
+
Number.isFinite(oauth.expiresAt) &&
|
|
50
|
+
oauth.expiresAt <= now.getTime();
|
|
51
|
+
return { token: expired ? null : token, expired };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function credentialFromPayload(payload: string | null, now: Date): Credential {
|
|
55
|
+
if (!payload) return { token: null, expired: false };
|
|
56
|
+
try {
|
|
57
|
+
return credentialFromRoot(record(JSON.parse(payload)), now);
|
|
58
|
+
} catch {
|
|
59
|
+
return { token: null, expired: false };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function readProfileCredential(
|
|
64
|
+
options: ClaudeCredentialOptions,
|
|
65
|
+
now: Date,
|
|
66
|
+
): Promise<Credential> {
|
|
67
|
+
try {
|
|
68
|
+
const payload = await (options.readFile ?? readFile)(credentialsPath(options), "utf8");
|
|
69
|
+
return credentialFromPayload(payload, now);
|
|
70
|
+
} catch {
|
|
71
|
+
return { token: null, expired: false };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function readClaudeKeychain(service: string): Promise<string | null> {
|
|
76
|
+
try {
|
|
77
|
+
const { stdout } = await execFileAsync(
|
|
78
|
+
"/usr/bin/security",
|
|
79
|
+
["find-generic-password", "-s", service, "-w"],
|
|
80
|
+
{ encoding: "utf8", timeout: 1500, maxBuffer: 1024 * 1024 },
|
|
81
|
+
);
|
|
82
|
+
return nonEmptyText(stdout);
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function readKeychainCredential(
|
|
89
|
+
options: ClaudeCredentialOptions,
|
|
90
|
+
now: Date,
|
|
91
|
+
): Promise<Credential> {
|
|
92
|
+
const env = options.env ?? process.env;
|
|
93
|
+
const usesDefaultProfile = !nonEmptyText(env.CLAUDE_CONFIG_DIR);
|
|
94
|
+
if (!usesDefaultProfile || (options.platform ?? process.platform) !== "darwin") {
|
|
95
|
+
return { token: null, expired: false };
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const payload = await (options.keychainRead ?? readClaudeKeychain)(CLAUDE_KEYCHAIN_SERVICE);
|
|
99
|
+
return credentialFromPayload(payload, now);
|
|
100
|
+
} catch {
|
|
101
|
+
return { token: null, expired: false };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function resolveClaudeAnthropicToken(
|
|
106
|
+
options: ClaudeCredentialOptions = {},
|
|
107
|
+
): Promise<string> {
|
|
108
|
+
const env = options.env ?? process.env;
|
|
109
|
+
const environmentToken = nonEmptyText(env.CLAUDE_CODE_OAUTH_TOKEN);
|
|
110
|
+
if (environmentToken) return environmentToken;
|
|
111
|
+
|
|
112
|
+
const now = options.now?.() ?? new Date();
|
|
113
|
+
const profileCredential = await readProfileCredential(options, now);
|
|
114
|
+
if (profileCredential.token) return profileCredential.token;
|
|
115
|
+
|
|
116
|
+
const keychainCredential = await readKeychainCredential(options, now);
|
|
117
|
+
if (keychainCredential.token) return keychainCredential.token;
|
|
118
|
+
if (profileCredential.expired || keychainCredential.expired) {
|
|
119
|
+
throw new Error("Claude OAuth credentials expired");
|
|
120
|
+
}
|
|
121
|
+
throw new Error("Claude OAuth credentials not found or malformed");
|
|
122
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
record,
|
|
4
|
+
registrationCheck,
|
|
5
|
+
scriptCheck,
|
|
6
|
+
worstReadinessStatus,
|
|
7
|
+
} from "../readiness/checks.js";
|
|
8
|
+
import type {
|
|
9
|
+
AuthReadinessCheck,
|
|
10
|
+
HarnessReadinessProfile,
|
|
11
|
+
IntegrationReadinessCheck,
|
|
12
|
+
ReadinessRuntime,
|
|
13
|
+
} from "../readiness/types.js";
|
|
14
|
+
|
|
15
|
+
async function authCheck(runtime: ReadinessRuntime): Promise<AuthReadinessCheck> {
|
|
16
|
+
try {
|
|
17
|
+
const result = await runtime.runCommand("claude", ["auth", "status", "--json"]);
|
|
18
|
+
const parsed = record(JSON.parse(result.stdout));
|
|
19
|
+
const authenticated = parsed?.loggedIn === true || parsed?.authenticated === true;
|
|
20
|
+
const unauthenticated = parsed?.loggedIn === false || parsed?.authenticated === false;
|
|
21
|
+
return {
|
|
22
|
+
state: authenticated ? "authenticated" : unauthenticated ? "unauthenticated" : "unknown",
|
|
23
|
+
source: "claude auth status --json",
|
|
24
|
+
provider: null,
|
|
25
|
+
availableProviders: [],
|
|
26
|
+
status: authenticated ? "pass" : unauthenticated ? "fail" : "warn",
|
|
27
|
+
errors: authenticated
|
|
28
|
+
? []
|
|
29
|
+
: [unauthenticated ? "Claude is not authenticated" : "Claude authentication is unknown"],
|
|
30
|
+
};
|
|
31
|
+
} catch {
|
|
32
|
+
return {
|
|
33
|
+
state: "unknown",
|
|
34
|
+
source: "claude auth status --json",
|
|
35
|
+
provider: null,
|
|
36
|
+
availableProviders: [],
|
|
37
|
+
status: "warn",
|
|
38
|
+
errors: ["Claude authentication probe failed"],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function integrationCheck(runtime: ReadinessRuntime): Promise<IntegrationReadinessCheck> {
|
|
44
|
+
const script = await scriptCheck(
|
|
45
|
+
join(runtime.homeDir, ".claude", "hooks", "claude-prompt-hook.js"),
|
|
46
|
+
join(runtime.assetRoot ?? "", "claude", "claude-prompt-hook.js"),
|
|
47
|
+
runtime,
|
|
48
|
+
);
|
|
49
|
+
const registration = await registrationCheck(
|
|
50
|
+
join(runtime.homeDir, ".claude", "settings.json"),
|
|
51
|
+
"PreToolUse",
|
|
52
|
+
"node ~/.claude/hooks/claude-prompt-hook.js",
|
|
53
|
+
runtime,
|
|
54
|
+
);
|
|
55
|
+
const installed = script.status === "pass" && registration.status === "pass";
|
|
56
|
+
return {
|
|
57
|
+
label: "ai-devkit hook",
|
|
58
|
+
installed,
|
|
59
|
+
status: worstReadinessStatus([script.status, registration.status]),
|
|
60
|
+
errors: [...script.errors, ...registration.errors],
|
|
61
|
+
details: { promptScript: script, registration },
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const claudeReadiness: HarnessReadinessProfile = {
|
|
66
|
+
configDir: ".claude",
|
|
67
|
+
auth: authCheck,
|
|
68
|
+
integration: integrationCheck,
|
|
69
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
type UnknownRecord = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
export type CodexCredentialOptions = {
|
|
7
|
+
env?: NodeJS.ProcessEnv;
|
|
8
|
+
readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
|
|
9
|
+
now?: () => Date;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type CodexCredentials = {
|
|
13
|
+
personalAccessToken: string | null;
|
|
14
|
+
oauth: { access: string; accountId: string } | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function record(value: unknown): UnknownRecord | null {
|
|
18
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
19
|
+
? (value as UnknownRecord)
|
|
20
|
+
: null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function finiteNumber(value: unknown): number | null {
|
|
24
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function nonEmptyText(value: unknown): string | null {
|
|
28
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveCodexAuthPath(env: NodeJS.ProcessEnv = process.env): string {
|
|
32
|
+
const root = env.CODEX_HOME || join(env.HOME || "", ".codex");
|
|
33
|
+
return join(root, "auth.json");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function jwtExpiry(token: string): number | null {
|
|
37
|
+
const part = token.split(".")[1];
|
|
38
|
+
if (!part) return null;
|
|
39
|
+
try {
|
|
40
|
+
return finiteNumber(record(JSON.parse(Buffer.from(part, "base64url").toString("utf8")))?.exp);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function staleOAuth(tokens: UnknownRecord, token: string, now: Date): boolean {
|
|
47
|
+
const metadata = tokens.expires_at ?? tokens.expiresAt ?? tokens.expiry;
|
|
48
|
+
let expiry: number | null = finiteNumber(metadata);
|
|
49
|
+
if (typeof metadata === "string") {
|
|
50
|
+
const parsed = Date.parse(metadata);
|
|
51
|
+
expiry = Number.isNaN(parsed) ? null : parsed / 1000;
|
|
52
|
+
}
|
|
53
|
+
expiry ??= jwtExpiry(token);
|
|
54
|
+
return expiry !== null && expiry <= now.getTime() / 1000;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function resolveCodexCredentials(
|
|
58
|
+
options: CodexCredentialOptions = {},
|
|
59
|
+
): Promise<CodexCredentials | null> {
|
|
60
|
+
let auth: UnknownRecord;
|
|
61
|
+
try {
|
|
62
|
+
const contents = await (options.readFile ?? readFile)(
|
|
63
|
+
resolveCodexAuthPath(options.env),
|
|
64
|
+
"utf8",
|
|
65
|
+
);
|
|
66
|
+
auth = record(JSON.parse(contents)) ?? {};
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const personalAccessToken = nonEmptyText(auth.personal_access_token);
|
|
72
|
+
const tokens = record(auth.tokens);
|
|
73
|
+
const access = nonEmptyText(tokens?.access_token);
|
|
74
|
+
const accountId = nonEmptyText(tokens?.account_id);
|
|
75
|
+
const now = options.now?.() ?? new Date();
|
|
76
|
+
const oauth =
|
|
77
|
+
tokens && access && accountId && !staleOAuth(tokens, access, now)
|
|
78
|
+
? { access, accountId }
|
|
79
|
+
: null;
|
|
80
|
+
return { personalAccessToken, oauth };
|
|
81
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
displayHome,
|
|
4
|
+
mappingCheck,
|
|
5
|
+
registrationCheck,
|
|
6
|
+
scriptCheck,
|
|
7
|
+
worstReadinessStatus,
|
|
8
|
+
} from "../readiness/checks.js";
|
|
9
|
+
import type {
|
|
10
|
+
AuthReadinessCheck,
|
|
11
|
+
HarnessReadinessProfile,
|
|
12
|
+
IntegrationReadinessCheck,
|
|
13
|
+
ReadinessRuntime,
|
|
14
|
+
} from "../readiness/types.js";
|
|
15
|
+
|
|
16
|
+
async function authCheck(runtime: ReadinessRuntime): Promise<AuthReadinessCheck> {
|
|
17
|
+
try {
|
|
18
|
+
const value = await runtime.codexAuth();
|
|
19
|
+
return {
|
|
20
|
+
state: value === true ? "authenticated" : value === false ? "unauthenticated" : "unknown",
|
|
21
|
+
source: displayHome(join(runtime.homeDir, ".codex", "auth.json"), runtime.homeDir),
|
|
22
|
+
provider: null,
|
|
23
|
+
availableProviders: [],
|
|
24
|
+
status: value === true ? "pass" : value === false ? "fail" : "warn",
|
|
25
|
+
errors:
|
|
26
|
+
value === true
|
|
27
|
+
? []
|
|
28
|
+
: [value === false ? "Codex is not authenticated" : "Codex authentication is unknown"],
|
|
29
|
+
};
|
|
30
|
+
} catch {
|
|
31
|
+
return {
|
|
32
|
+
state: "unknown",
|
|
33
|
+
source: "~/.codex/auth.json",
|
|
34
|
+
provider: null,
|
|
35
|
+
availableProviders: [],
|
|
36
|
+
status: "warn",
|
|
37
|
+
errors: ["Codex authentication probe failed"],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function integrationCheck(runtime: ReadinessRuntime): Promise<IntegrationReadinessCheck> {
|
|
43
|
+
const script = await scriptCheck(
|
|
44
|
+
join(runtime.homeDir, ".codex", "hooks", "codex-session-mapping.cjs"),
|
|
45
|
+
join(runtime.assetRoot ?? "", "codex", "codex-session-mapping.cjs"),
|
|
46
|
+
runtime,
|
|
47
|
+
);
|
|
48
|
+
const registration = await registrationCheck(
|
|
49
|
+
join(runtime.homeDir, ".codex", "hooks.json"),
|
|
50
|
+
"SessionStart",
|
|
51
|
+
"node ~/.codex/hooks/codex-session-mapping.cjs",
|
|
52
|
+
runtime,
|
|
53
|
+
);
|
|
54
|
+
const mappingFile = await mappingCheck(
|
|
55
|
+
join(runtime.homeDir, ".codex", "ai-devkit", "sessions.json"),
|
|
56
|
+
runtime,
|
|
57
|
+
);
|
|
58
|
+
const installed = script.status === "pass" && registration.status === "pass";
|
|
59
|
+
return {
|
|
60
|
+
label: "ai-devkit hook",
|
|
61
|
+
installed,
|
|
62
|
+
status: worstReadinessStatus([script.status, registration.status, mappingFile.status]),
|
|
63
|
+
errors: [...script.errors, ...registration.errors, ...mappingFile.errors],
|
|
64
|
+
details: { sessionMappingScript: script, registration, mappingFile },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const codexReadiness: HarnessReadinessProfile = {
|
|
69
|
+
configDir: ".codex",
|
|
70
|
+
auth: authCheck,
|
|
71
|
+
integration: integrationCheck,
|
|
72
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuthReadinessCheck,
|
|
3
|
+
HarnessReadinessProfile,
|
|
4
|
+
ReadinessRuntime,
|
|
5
|
+
} from "../readiness/types.js";
|
|
6
|
+
|
|
7
|
+
async function authCheck(runtime: ReadinessRuntime): Promise<AuthReadinessCheck> {
|
|
8
|
+
try {
|
|
9
|
+
await runtime.runCommand("gh", ["auth", "status", "--hostname", "github.com"]);
|
|
10
|
+
return {
|
|
11
|
+
state: "authenticated",
|
|
12
|
+
source: "gh auth status --hostname github.com",
|
|
13
|
+
provider: "github",
|
|
14
|
+
availableProviders: ["GitHub"],
|
|
15
|
+
status: "pass",
|
|
16
|
+
errors: [],
|
|
17
|
+
};
|
|
18
|
+
} catch {
|
|
19
|
+
return {
|
|
20
|
+
state: "unknown",
|
|
21
|
+
source: "gh auth status --hostname github.com",
|
|
22
|
+
provider: null,
|
|
23
|
+
availableProviders: ["GitHub"],
|
|
24
|
+
status: "warn",
|
|
25
|
+
errors: ["Copilot authentication could not be verified through GitHub CLI"],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const copilotReadiness: HarnessReadinessProfile = {
|
|
31
|
+
configDir: ".copilot",
|
|
32
|
+
auth: authCheck,
|
|
33
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuthReadinessCheck,
|
|
3
|
+
HarnessReadinessProfile,
|
|
4
|
+
ReadinessRuntime,
|
|
5
|
+
} from "../readiness/types.js";
|
|
6
|
+
|
|
7
|
+
const ANSI_ESCAPE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
|
|
8
|
+
|
|
9
|
+
function authProviderNames(output: string): string[] {
|
|
10
|
+
const names = new Set<string>();
|
|
11
|
+
for (const line of output.replace(ANSI_ESCAPE_PATTERN, "").split(/\r?\n/)) {
|
|
12
|
+
const match = /^\s*[●*+-]\s+(.+?)\s*$/.exec(line);
|
|
13
|
+
if (!match) continue;
|
|
14
|
+
const name = match[1].replace(/\s+(?:api|oauth|[\w-]*token|[A-Z][A-Z0-9_]+)$/i, "").trim();
|
|
15
|
+
if (name && !/^\d+\s+(?:credentials?|environment variables?)$/i.test(name)) names.add(name);
|
|
16
|
+
}
|
|
17
|
+
return [...names].sort();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function authCheck(runtime: ReadinessRuntime): Promise<AuthReadinessCheck> {
|
|
21
|
+
try {
|
|
22
|
+
const result = await runtime.runCommand("opencode", ["auth", "list"]);
|
|
23
|
+
const availableProviders = authProviderNames(result.stdout);
|
|
24
|
+
const authenticated = availableProviders.length > 0;
|
|
25
|
+
return {
|
|
26
|
+
state: authenticated ? "authenticated" : "unauthenticated",
|
|
27
|
+
source: "opencode auth list",
|
|
28
|
+
provider: null,
|
|
29
|
+
availableProviders,
|
|
30
|
+
status: authenticated ? "pass" : "fail",
|
|
31
|
+
errors: authenticated ? [] : ["OpenCode has no configured credentials"],
|
|
32
|
+
};
|
|
33
|
+
} catch {
|
|
34
|
+
return {
|
|
35
|
+
state: "unknown",
|
|
36
|
+
source: "opencode auth list",
|
|
37
|
+
provider: null,
|
|
38
|
+
availableProviders: [],
|
|
39
|
+
status: "warn",
|
|
40
|
+
errors: ["OpenCode authentication probe failed"],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const openCodeReadiness: HarnessReadinessProfile = {
|
|
46
|
+
configDir: ".config/opencode",
|
|
47
|
+
auth: authCheck,
|
|
48
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
type UnknownRecord = Record<string, unknown>;
|
|
6
|
+
|
|
7
|
+
export type PiCredentialOptions = {
|
|
8
|
+
env?: NodeJS.ProcessEnv;
|
|
9
|
+
readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type PiAnthropicCredential = {
|
|
13
|
+
kind: "oauth";
|
|
14
|
+
access: string;
|
|
15
|
+
expiresMs: number | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type PiOpenAiCredential =
|
|
19
|
+
| { kind: "platform"; key: string }
|
|
20
|
+
| { kind: "oauth"; access: string; accountId: string; expiresMs: number | null };
|
|
21
|
+
|
|
22
|
+
const OPENAI_CREDENTIALS_MISSING =
|
|
23
|
+
"OpenAI credentials not found; set OPENAI_API_KEY, configure the Pi openai provider, or log in to OpenAI via Pi";
|
|
24
|
+
const ZAI_CREDENTIAL_MISSING =
|
|
25
|
+
"z.ai API key not found; set Z_AI_API_KEY or configure Pi provider zai";
|
|
26
|
+
|
|
27
|
+
function record(value: unknown): UnknownRecord | null {
|
|
28
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
29
|
+
? (value as UnknownRecord)
|
|
30
|
+
: null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function nonEmptyText(value: unknown): string | null {
|
|
34
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function epochMilliseconds(value: unknown): number | null {
|
|
38
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return null;
|
|
39
|
+
return value > 1e12 ? value : value * 1000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function readPiAuthRoot(options: PiCredentialOptions): Promise<UnknownRecord> {
|
|
43
|
+
const env = options.env ?? process.env;
|
|
44
|
+
const authPath = join(env.HOME || homedir(), ".pi", "agent", "auth.json");
|
|
45
|
+
const contents = await (options.readFile ?? readFile)(authPath, "utf8");
|
|
46
|
+
const parsed = record(JSON.parse(contents));
|
|
47
|
+
if (!parsed) throw new Error("Pi auth file is malformed");
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function resolvePiAnthropicCredential(
|
|
52
|
+
options: PiCredentialOptions = {},
|
|
53
|
+
): Promise<PiAnthropicCredential> {
|
|
54
|
+
let root: UnknownRecord;
|
|
55
|
+
try {
|
|
56
|
+
root = await readPiAuthRoot(options);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (record(error)?.code === "ENOENT") {
|
|
59
|
+
throw new Error("Pi Anthropic OAuth credentials not found");
|
|
60
|
+
}
|
|
61
|
+
throw new Error("Pi Anthropic auth file is malformed");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const credential = record(root.anthropic);
|
|
65
|
+
if (!credential) throw new Error("Pi Anthropic OAuth credentials not found");
|
|
66
|
+
const access = credential.type === "oauth" ? nonEmptyText(credential.access) : null;
|
|
67
|
+
if (!access) throw new Error("Pi Anthropic OAuth credential is malformed");
|
|
68
|
+
return {
|
|
69
|
+
kind: "oauth",
|
|
70
|
+
access,
|
|
71
|
+
expiresMs: epochMilliseconds(credential.expires),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function resolvePiOpenAiCredential(
|
|
76
|
+
options: PiCredentialOptions = {},
|
|
77
|
+
): Promise<PiOpenAiCredential> {
|
|
78
|
+
const env = options.env ?? process.env;
|
|
79
|
+
const environmentKey = nonEmptyText(env.OPENAI_API_KEY);
|
|
80
|
+
if (environmentKey) return { kind: "platform", key: environmentKey };
|
|
81
|
+
|
|
82
|
+
let root: UnknownRecord;
|
|
83
|
+
try {
|
|
84
|
+
root = await readPiAuthRoot(options);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (record(error)?.code === "ENOENT") throw new Error(OPENAI_CREDENTIALS_MISSING);
|
|
87
|
+
throw new Error("OpenAI Pi auth file is malformed");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const platform = record(root.openai);
|
|
91
|
+
if (platform) {
|
|
92
|
+
const key = platform.type === "api_key" ? nonEmptyText(platform.key) : null;
|
|
93
|
+
if (!key) throw new Error("OpenAI Pi auth file has a malformed openai credential");
|
|
94
|
+
return { kind: "platform", key };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const oauth = record(root["openai-codex"]);
|
|
98
|
+
const access = oauth?.type === "oauth" ? nonEmptyText(oauth.access) : null;
|
|
99
|
+
const accountId = nonEmptyText(oauth?.accountId);
|
|
100
|
+
if (oauth && access && accountId) {
|
|
101
|
+
return {
|
|
102
|
+
kind: "oauth",
|
|
103
|
+
access,
|
|
104
|
+
accountId,
|
|
105
|
+
expiresMs: epochMilliseconds(oauth.expires),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
throw new Error(OPENAI_CREDENTIALS_MISSING);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function resolvePiZaiApiKey(options: PiCredentialOptions = {}): Promise<string> {
|
|
112
|
+
const env = options.env ?? process.env;
|
|
113
|
+
const environmentKey = nonEmptyText(env.Z_AI_API_KEY);
|
|
114
|
+
if (environmentKey) return environmentKey;
|
|
115
|
+
|
|
116
|
+
let root: UnknownRecord;
|
|
117
|
+
try {
|
|
118
|
+
root = await readPiAuthRoot(options);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (record(error)?.code === "ENOENT") throw new Error(ZAI_CREDENTIAL_MISSING);
|
|
121
|
+
throw new Error("z.ai Pi auth file is malformed");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const credential = record(root.zai);
|
|
125
|
+
if (!credential) throw new Error(ZAI_CREDENTIAL_MISSING);
|
|
126
|
+
const key = credential.type === "api_key" ? nonEmptyText(credential.key) : null;
|
|
127
|
+
if (!key) throw new Error("z.ai Pi auth file has a malformed zai credential");
|
|
128
|
+
return key;
|
|
129
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
displayHome,
|
|
4
|
+
mappingCheck,
|
|
5
|
+
nonEmpty,
|
|
6
|
+
record,
|
|
7
|
+
worstReadinessStatus,
|
|
8
|
+
} from "../readiness/checks.js";
|
|
9
|
+
import type {
|
|
10
|
+
AuthReadinessCheck,
|
|
11
|
+
HarnessReadinessProfile,
|
|
12
|
+
IntegrationReadinessCheck,
|
|
13
|
+
ReadinessRuntime,
|
|
14
|
+
} from "../readiness/types.js";
|
|
15
|
+
|
|
16
|
+
function providerNames(parsed: Record<string, unknown>): string[] {
|
|
17
|
+
const names = new Set<string>();
|
|
18
|
+
if (nonEmpty(parsed.provider)) names.add((parsed.provider as string).trim());
|
|
19
|
+
const providers = record(parsed.providers);
|
|
20
|
+
if (providers) {
|
|
21
|
+
for (const name of Object.keys(providers)) {
|
|
22
|
+
if (name.trim()) names.add(name);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
26
|
+
if (name === "provider" || name === "providers") continue;
|
|
27
|
+
if (record(value) && name.trim()) names.add(name);
|
|
28
|
+
}
|
|
29
|
+
return [...names].sort();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function authCheck(runtime: ReadinessRuntime): Promise<AuthReadinessCheck> {
|
|
33
|
+
const sourcePath = join(runtime.homeDir, ".pi", "agent", "auth.json");
|
|
34
|
+
try {
|
|
35
|
+
const parsed = record(JSON.parse(await runtime.readFile(sourcePath)));
|
|
36
|
+
if (!parsed) throw new Error("invalid");
|
|
37
|
+
const availableProviders = providerNames(parsed);
|
|
38
|
+
const provider = nonEmpty(parsed.provider) ? (parsed.provider as string).trim() : null;
|
|
39
|
+
const authenticated = availableProviders.length > 0;
|
|
40
|
+
return {
|
|
41
|
+
state: authenticated ? "authenticated" : "unauthenticated",
|
|
42
|
+
source: displayHome(sourcePath, runtime.homeDir),
|
|
43
|
+
provider,
|
|
44
|
+
availableProviders,
|
|
45
|
+
status: authenticated ? "pass" : "fail",
|
|
46
|
+
errors: authenticated ? [] : ["Pi credential file has no configured model provider"],
|
|
47
|
+
};
|
|
48
|
+
} catch {
|
|
49
|
+
return {
|
|
50
|
+
state: "unauthenticated",
|
|
51
|
+
source: displayHome(sourcePath, runtime.homeDir),
|
|
52
|
+
provider: null,
|
|
53
|
+
availableProviders: [],
|
|
54
|
+
status: "fail",
|
|
55
|
+
errors: ["Pi credential file is missing or invalid"],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isSessionTrackerListed(output: string): boolean {
|
|
61
|
+
const normalized = output.toLowerCase();
|
|
62
|
+
return (
|
|
63
|
+
normalized.includes("@ai-devkit/pi-session-tracker") ||
|
|
64
|
+
/\bsession[\s-]+tracker\b/.test(normalized)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function integrationCheck(runtime: ReadinessRuntime): Promise<IntegrationReadinessCheck> {
|
|
69
|
+
let installed = false;
|
|
70
|
+
try {
|
|
71
|
+
const result = await runtime.runCommand("pi", ["list"]);
|
|
72
|
+
installed = isSessionTrackerListed(result.stdout);
|
|
73
|
+
} catch {
|
|
74
|
+
// Safe fixed result below.
|
|
75
|
+
}
|
|
76
|
+
const mapping = await mappingCheck(
|
|
77
|
+
join(runtime.homeDir, ".pi", "agent", "sessions.json"),
|
|
78
|
+
runtime,
|
|
79
|
+
);
|
|
80
|
+
const mappingStatus = mapping.valid || !mapping.present ? "pass" : "fail";
|
|
81
|
+
const status = worstReadinessStatus([installed ? "pass" : "fail", mappingStatus]);
|
|
82
|
+
return {
|
|
83
|
+
label: "ai-devkit plugin",
|
|
84
|
+
installed,
|
|
85
|
+
status,
|
|
86
|
+
errors: [
|
|
87
|
+
...(!installed ? ["Pi session tracker is not registered"] : []),
|
|
88
|
+
...(mappingStatus === "fail" ? mapping.errors : []),
|
|
89
|
+
],
|
|
90
|
+
details: {
|
|
91
|
+
package: "@ai-devkit/pi-session-tracker",
|
|
92
|
+
registryPath: mapping.path,
|
|
93
|
+
registryValid: mapping.valid,
|
|
94
|
+
invalidEntries: mapping.invalidEntries,
|
|
95
|
+
staleEntries: mapping.staleEntries,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const piReadiness: HarnessReadinessProfile = {
|
|
101
|
+
configDir: ".pi",
|
|
102
|
+
auth: authCheck,
|
|
103
|
+
integration: integrationCheck,
|
|
104
|
+
};
|