Skip to content

Commit 126d544

Browse files
fazxesclaude
andcommitted
chore: add shared-schemas package and refactor type definitions
- Add new @snowflake/shared-schemas package for shared Zod schemas - Refactor agent-bridge to use shared schemas - Add SDK boundary schemas with z.looseObject() for flexibility - Add file path validation with security refinements - Add Zod error formatting utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c0b227c commit 126d544

33 files changed

Lines changed: 2042 additions & 946 deletions

‎agent-bridge/package.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
1616
"@anthropic-ai/claude-code": "^2.0.76",
1717
"@anthropic-ai/sdk": "^0.71.2",
18-
"@modelcontextprotocol/sdk": "^1.0.0"
18+
"@modelcontextprotocol/sdk": "^1.0.0",
19+
"@snowflake/shared-schemas": "workspace:*",
20+
"zod": "^4.2.1"
1921
},
2022
"devDependencies": {
2123
"@types/node": "^22.19.3",

‎agent-bridge/src/credentials.ts‎

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,12 @@
11
import { execSync } from 'child_process';
22

3+
import { formatZodError } from '@snowflake/shared-schemas';
4+
35
import { createLogger } from './logger.js';
6+
import { KeychainCredentialsSchema } from './schemas.js';
47

58
const logger = createLogger('ClaudeCredentials');
69

7-
/**
8-
* Keychain credentials structure from Claude Code CLI
9-
*/
10-
interface KeychainCredentials {
11-
claudeAiOauth?: {
12-
accessToken?: string;
13-
expiresAt?: string;
14-
};
15-
}
16-
17-
/**
18-
* Type guard to check if parsed JSON is valid KeychainCredentials
19-
*/
20-
function isKeychainCredentials(value: unknown): value is KeychainCredentials {
21-
if (typeof value !== 'object' || value === null) {
22-
return false;
23-
}
24-
const obj = value as Record<string, unknown>;
25-
if (obj.claudeAiOauth === undefined) {
26-
return true; // claudeAiOauth is optional
27-
}
28-
if (typeof obj.claudeAiOauth !== 'object' || obj.claudeAiOauth === null) {
29-
return false;
30-
}
31-
return true;
32-
}
33-
3410
/**
3511
* Reads OAuth token from macOS Keychain where Claude Code CLI stores credentials
3612
* @returns OAuth access token if valid and not expired, null otherwise
@@ -42,14 +18,18 @@ function getOAuthTokenFromKeychain(): string | null {
4218
encoding: 'utf-8',
4319
}).trim();
4420

45-
// Parse the JSON credentials structure
46-
const parsed: unknown = JSON.parse(output);
47-
if (!isKeychainCredentials(parsed)) {
48-
logger.debug('Invalid credentials structure in Keychain');
21+
// Parse and validate the JSON credentials structure with Zod
22+
const json: unknown = JSON.parse(output);
23+
const result = KeychainCredentialsSchema.safeParse(json);
24+
if (!result.success) {
25+
logger.debug(
26+
{ error: formatZodError(result.error) },
27+
'Invalid credentials structure in Keychain'
28+
);
4929
return null;
5030
}
5131

52-
const claudeAuth = parsed.claudeAiOauth;
32+
const claudeAuth = result.data.claudeAiOauth;
5333
if (claudeAuth === undefined) {
5434
logger.debug('No Claude OAuth credentials found in Keychain');
5535
return null;

‎agent-bridge/src/index.ts‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ process.env.PATH = Array.from(pathSet).join(':');
3030

3131
import * as readline from 'readline';
3232

33+
import { formatZodError } from '@snowflake/shared-schemas';
34+
3335
import {
3436
createAgent,
3537
deleteAgent,
@@ -45,6 +47,7 @@ import {
4547
updateCommand,
4648
} from './command-definitions.js';
4749
import { createLogger } from './logger.js';
50+
import { BridgeRequestSchema } from './schemas.js';
4851
import { SessionManager } from './session-manager.js';
4952
import {
5053
cleanupOldSessions,
@@ -163,9 +166,21 @@ function main(): void {
163166

164167
let request: BridgeRequest;
165168
try {
166-
request = JSON.parse(line) as BridgeRequest;
169+
const parsed: unknown = JSON.parse(line);
170+
const result = BridgeRequestSchema.safeParse(parsed);
171+
if (!result.success) {
172+
const errorMessage = formatZodError(result.error);
173+
logger.error({ error: errorMessage, line }, 'Invalid request schema');
174+
sendResponse({
175+
type: 'error',
176+
requestType: 'unknown',
177+
error: `Invalid request: ${errorMessage}`,
178+
});
179+
return;
180+
}
181+
request = result.data;
167182
} catch (error) {
168-
logger.error({ error, line }, 'Failed to parse request');
183+
logger.error({ error, line }, 'Failed to parse request JSON');
169184
sendResponse({
170185
type: 'error',
171186
requestType: 'unknown',

0 commit comments

Comments
 (0)