# AGENTS.md - Octocode Shared
> **Location**: `packages/octocode-shared/AGENTS.md`
AI agent guidance for the `octocode-shared` package - Shared utilities for credential management, session persistence, and platform detection across Octocode packages.
This file **overrides** the root [`AGENTS.md`](../../AGENTS.md) for work within this package.
---
## Overview
Octocode Shared provides common utilities used by multiple Octocode packages:
- **Credential Management**: Secure token storage with AES-256-GCM encryption
- **Session Persistence**: Session state with deferred writes and usage statistics
- **Platform Detection**: Cross-platform path and environment utilities
- **Keychain Integration**: Native file storage access via `file storage-napi`
**Key Consumers**: `octocode-cli`, `octocode-mcp`
---
## π οΈ Commands
All commands run from this package directory (`packages/octocode-shared/`).
| Task | Command | Description |
|------|---------|-------------|
| **Build** | `yarn build` | Lint + compile TypeScript |
| **Build (Dev)** | `yarn build:dev` | Compile without lint |
| **Clean** | `yarn clean` | Remove `dist/` directory |
| **Test** | `yarn test` | Run tests with coverage |
| **Test (Quiet)** | `yarn test:quiet` | Minimal test output |
| **Test (Watch)** | `yarn test:watch` | Watch mode for tests |
| **Lint** | `yarn lint` | ESLint check |
| **Lint (Fix)** | `yarn lint:fix` | Auto-fix linting issues |
| **Typecheck** | `yarn typecheck` | TypeScript type checking |
---
## π Documentation
Technical documentation for the shared utilities:
| Document | Description |
|----------|-------------|
| [`CREDENTIALS_ARCHITECTURE.md`](./docs/CREDENTIALS_ARCHITECTURE.md) | Token storage, encryption, file storage integration, refresh flow |
| [`SESSION_PERSISTENCE.md`](./docs/SESSION_PERSISTENCE.md) | Deferred writes, exit handlers, statistics tracking |
| [`API_REFERENCE.md`](./docs/API_REFERENCE.md) | Complete API documentation for all modules |
---
## π Package Structure
```
src/
βββ index.ts # Package exports
β
βββ credentials/ # π Secure credential storage
β βββ index.ts # Credentials module exports
β βββ file storage.ts # System file storage wrapper (internal)
β βββ storage.ts # AES-256-GCM encrypted storage
β βββ types.ts # Credential type definitions
β
βββ platform/ # π₯οΈ Platform utilities
β βββ index.ts # Platform module exports
β βββ platform.ts # OS detection & paths
β
βββ session/ # π Session persistence
βββ index.ts # Session module exports
βββ storage.ts # Session storage with deferred writes
βββ types.ts # Session type definitions
```
### Tests Structure
```
tests/
βββ credentials/
β βββ file storage.test.ts # Keychain integration tests
β βββ storage.test.ts # Credential storage tests
βββ platform/
β βββ platform.test.ts # Platform detection tests
βββ session/
βββ storage.test.ts # Session storage tests
```
---
## π¦ Module Exports
The package provides four entry points:
```typescript
// Main entry - all exports
import { ... } from 'octocode-shared';
// Credentials only
import { ... } from 'octocode-shared/credentials';
// Platform only
import { ... } from 'octocode-shared/platform';
// Session only
import { ... } from 'octocode-shared/session';
```
### Credentials Module
| Export | Type | Purpose |
|--------|------|---------|
| `storeCredentials` | Function | Store encrypted credentials |
| `getCredentials` | Function | Retrieve credentials (async, cached) |
| `getCredentialsSync` | Function | Retrieve credentials (sync, file only) |
| `deleteCredentials` | Function | Remove stored credentials |
| `getToken` | Function | Get token for a host (async) |
| `getTokenSync` | Function | Get token for a host (sync) |
| `getTokenWithRefresh` | Function | Get token with auto-refresh (recommended) |
| `resolveToken` | Function | Resolve token from env/storage |
| `resolveTokenWithRefresh` | Function | Resolve with auto-refresh |
| `resolveTokenFull` | Function | Full resolution with gh CLI fallback |
| `refreshAuthToken` | Function | Manually refresh an expired token |
| `updateToken` | Function | Update stored token |
| `invalidateCredentialsCache` | Function | Invalidate cached credentials |
| `listStoredHosts` | Function | List all stored hosts |
| `hasCredentials` | Function | Check if credentials exist |
| `isTokenExpired` | Function | Check token expiration |
| `isRefreshTokenExpired` | Function | Check refresh token expiration |
| `initializeSecureStorage` | Function | Initialize file storage-backed storage |
| `isSecureStorageAvailable` | Function | Check if secure storage works |
| `getTokenFromEnv` | Function | Get token from environment |
| `hasEnvToken` | Function | Check for env token |
| `OAuthToken` | Type | OAuth token structure |
| `StoredCredentials` | Type | Credential data structure |
| `TokenSource` | Type | Token origin (env/storage) |
| `GetCredentialsOptions` | Type | Options for getCredentials |
### Platform Module
| Export | Type | Purpose |
|--------|------|---------|
| `getPlatform()` | Function | Get current OS (`darwin`, `win32`, `linux`) |
| `getConfigPath()` | Function | Platform-specific config directory |
| `isWindows()` | Function | Windows detection |
| `isMacOS()` | Function | macOS detection |
| `isLinux()` | Function | Linux detection |
### Session Module
| Export | Type | Purpose |
|--------|------|---------|
| `readSession` | Function | Read current session from cache/disk |
| `writeSession` | Function | Write session (deferred to disk) |
| `getOrCreateSession` | Function | Get existing or create new session |
| `getSessionId` | Function | Get current session ID |
| `deleteSession` | Function | Delete session file |
| `flushSession` | Function | Flush pending writes to disk |
| `flushSessionSync` | Function | Sync flush for exit handlers |
| `updateSessionStats` | Function | Update session statistics |
| `incrementToolCalls` | Function | Increment tool call counter |
| `incrementPromptCalls` | Function | Increment prompt call counter |
| `incrementErrors` | Function | Increment error counter |
| `incrementRateLimits` | Function | Increment rate limit counter |
| `resetSessionStats` | Function | Reset all statistics |
| `SESSION_FILE` | Constant | Path to session file |
| `PersistedSession` | Type | Session data structure |
| `SessionStats` | Type | Usage statistics structure |
| `SessionUpdateResult` | Type | Update result type |
| `SessionOptions` | Type | Session creation options |
---
## π Credential Storage Architecture
### Encryption Details
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CREDENTIAL STORAGE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Token β AES-256-GCM Encryption β Base64 β File Storage β
β β
β Encryption Key: β
β βββ Stored in system file storage (via file storage-napi) β
β βββ Fallback: File-based key storage β
β β
β Storage Location: β
β βββ ~/.octocode/credentials.json β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
### Security Features
- **AES-256-GCM**: Authenticated encryption with associated data
- **Random IV**: Unique initialization vector per encryption
- **Keychain Integration**: Native OS file storage for encryption key
- **Secure Fallback**: File-based key when file storage unavailable
- **Token Resolution**: Automatic env β storage β gh CLI fallback chain
- **Auto-Refresh**: Octocode tokens refreshed automatically when expired (via `@octokit/oauth-methods`)
- **In-Memory Cache**: 5-minute TTL with automatic invalidation on credential updates
---
## π Session Storage Architecture
### Session Persistence
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SESSION STORAGE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β In-Memory Cache ββ Deferred Writes β File Storage β
β β
β Write Strategy: β
β βββ Writes are cached in memory β
β βββ Flushed to disk on timer or explicit flush β
β βββ Sync flush on process exit (SIGINT, SIGTERM) β
β β
β Storage Location: β
β βββ ~/.octocode/session.json β
β β
β Data Tracked: β
β βββ sessionId, createdAt, lastActiveAt β
β βββ stats: toolCalls, promptCalls, errors, rateLimits β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
### Session Features
- **Deferred Writes**: Batches writes for performance
- **In-Memory Caching**: Fast reads from memory
- **Exit Handlers**: Automatic flush on process exit
- **Statistics Tracking**: Tool calls, prompts, errors, rate limits
- **Atomic Counters**: Thread-safe stat increments
---
## π¦ Package Guidelines
These are the core principles for this shared package:
1. **Minimal Dependencies**: Only `file storage-napi` for file storage access.
2. **Cross-Platform**: Must work on macOS, Linux, and Windows.
3. **Type-Safe Exports**: Full TypeScript types with strict mode.
4. **Security First**: All credential operations use encryption.
5. **Performance**: Session writes are deferred for efficiency.
6. **Minimal API Surface**: Export only what's needed by consumers.
---
## ποΈ Architecture Patterns
### Token Resolution Flow
```
resolveTokenFull(options)
β
getTokenFromEnv() β Checked first (highest priority, NO REFRESH)
βββ 1. Check OCTOCODE_TOKEN
βββ 2. Check GH_TOKEN
βββ 3. Check GITHUB_TOKEN
βββ Return { token, source: 'env:*' } if found (user manages these)
β
getTokenWithRefresh(host) β ONLY OCTOCODE TOKENS ARE REFRESHED
βββ Read from in-memory cache (5-min TTL)
βββ Fallback to file storage or encrypted storage
βββ Auto-refresh if token expired (using @octokit/oauth-methods)
βββ Return { token, source: 'file storage'|'file' } if found
β
getGhCliToken(host) β Fallback (NO REFRESH - gh CLI manages its own tokens)
βββ Return { token, source: 'gh-cli' } if found
β
Return result or null
```
### Token Refresh Policy
| Token Source | Auto-Refresh? | Reason |
|--------------|---------------|--------|
| **Env vars** (`OCTOCODE_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN`) | β No | User-managed tokens |
| **Octocode credentials** (file storage/file) | β
If supported | GitHub App tokens only (see below) |
| **gh CLI token** | β No | gh CLI handles its own token refresh |
**Token Type Support:**
| Token Type | Expires? | Refresh Token? | Auto-Refresh? |
|------------|----------|----------------|---------------|
| **GitHub App user tokens** | β
8 hours | β
Yes | β
Yes |
| **OAuth App tokens** (classic) | β Never | β No | β N/A |
**Note:** Only tokens with a `refreshToken` field are auto-refreshed. OAuth App tokens never expire and don't need refresh. Octocode uses a GitHub App, so tokens from `octocode login` support auto-refresh.
### Credentials Caching
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β IN-MEMORY CREDENTIALS CACHE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Cache TTL: 5 minutes (matches token expiry buffer) β
β β
β Cache Invalidation: β
β βββ storeCredentials() β invalidates hostname β
β βββ deleteCredentials() β invalidates hostname β
β βββ updateToken() β calls storeCredentials() β
β βββ refreshAuthToken() β calls updateToken() β
β β
β Bypass Cache: β
β βββ getCredentials(host, { bypassCache: true }) β
β β
β Manual Invalidation: β
β βββ invalidateCredentialsCache(hostname?) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
### Session Write Flow
```
writeSession(session)
β
cachedSession = session
isDirty = true
β
registerExitHandlers() (once)
βββ SIGINT β flushSessionSync()
βββ SIGTERM β flushSessionSync()
βββ beforeExit β flushSessionSync()
β
startFlushTimer()
βββ setTimeout β flushSession() β writeSessionToDisk()
```
### Key Management
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β KEY MANAGEMENT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Primary: System Keychain (file storage-napi) β
β βββ Service: "octocode" β
β βββ Account: "encryption-key" β
β β
β Fallback: File-Based Key β
β βββ Location: ~/.octocode/.key β
β βββ Used when file storage unavailable β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
---
## π‘οΈ Safety & Permissions
### Package-Level Access
| Path | Access | Description |
|------|--------|-------------|
| `src/` | β
FULL | Source code |
| `tests/` | β
FULL | Test files |
| `*.json`, `*.config.*` | β οΈ ASK | Package configs |
| `dist/`, `coverage/`, `node_modules/` | β NEVER | Generated files |
### Protected Files
- **Never Modify**: `dist/`, `coverage/`, `node_modules/`
- **Ask Before Modifying**: `package.json`, `tsconfig.json`, `vitest.config.ts`
### Security Considerations
- **Key Isolation**: Encryption keys never leave the system file storage
- **No Plaintext Storage**: Tokens are always encrypted at rest
- **Env Variable Priority**: Environment tokens take precedence
- **Deferred Writes**: Session data is flushed safely on exit
---
## π§ͺ Testing Protocol
### Requirements
- **Coverage**: 90% required (Statements, Branches, Functions, Lines)
- **Framework**: Vitest with v8 coverage
### Test Categories
| Category | Path | Purpose |
|----------|------|---------|
| Unit | `tests/credentials/storage.test.ts` | Encryption/decryption, token management |
| Unit | `tests/credentials/file storage.test.ts` | Keychain integration |
| Unit | `tests/platform/platform.test.ts` | OS detection, path resolution |
| Unit | `tests/session/storage.test.ts` | Session persistence, stats, flushing |
### Running Tests
```bash
yarn test # Full test with coverage
yarn test:watch # Watch mode
yarn test:quiet # Minimal output
```
---
## π Development Notes
### Adding New Modules
1. Create module directory under `src/`
2. Add `index.ts` with exports
3. Update `src/index.ts` to re-export
4. Add export path in `package.json` exports field
5. Create corresponding test file
### Dependencies
| Dependency | Purpose |
|------------|---------|
| `file storage-napi` | Native file storage access |
| `@octokit/oauth-methods` | GitHub OAuth token refresh |
| `@octokit/request` | HTTP requests to GitHub API |
### Build Output
```
dist/
βββ index.js # Main entry
βββ index.d.ts # Type declarations
βββ credentials/ # Credentials module
β βββ index.js
β βββ index.d.ts
β βββ file storage.js # Internal file storage wrapper
β βββ storage.js
β βββ types.d.ts
βββ platform/ # Platform module
β βββ index.js
β βββ index.d.ts
β βββ platform.js
βββ session/ # Session module
βββ index.js
βββ index.d.ts
βββ storage.js
βββ types.d.ts
```