+
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions packages/create-agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pnpm create-agents my-agent-directory --project-id my-project-id --anthropic-key
- `--project-id <project-id>` - Project identifier for your agents
- `--openai-key <openai-key>` - OpenAI API key (optional)
- `--anthropic-key <anthropic-key>` - Anthropic API key (recommended)
- `--disable-telemetry` - Disable anonymous error tracking

## What's Created

Expand Down Expand Up @@ -150,6 +151,43 @@ LOG_LEVEL=debug
- `apps/run-api/.env` - Run API configuration
- `src/<project-id>/.env` - CLI configuration

## Telemetry

`create-agents` collects anonymous error reports to help us improve the tool. On first run, you'll be asked for consent.

### What We Collect
- Anonymous error reports and stack traces
- Template usage (e.g., "weather-project")
- Success/failure status

### What We Do NOT Collect
- File contents or code
- API keys or credentials
- Personal information
- Usernames or file paths (automatically scrubbed)

### Opt Out

You can disable telemetry in several ways:

**1. Command line flag:**
```bash
npx create-agents --disable-telemetry
```

**2. Environment variable:**
```bash
DO_NOT_TRACK=1 npx create-agents
```

**3. During first run:**
When prompted, select "No" to disable telemetry

Your telemetry preferences are saved to `~/.inkeep/telemetry-config.json`

### Privacy Policy
For more information, see our [Privacy Policy](https://inkeep.com/privacy)

## Learn More

- 📚 [Documentation](https://docs.inkeep.com)
1 change: 1 addition & 0 deletions packages/create-agents/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"license": "SEE LICENSE IN LICENSE.md",
"dependencies": {
"@clack/prompts": "^0.11.0",
"@sentry/node": "^10.17.0",
"commander": "^12.0.0",
"degit": "^2.8.4",
"fs-extra": "^11.0.0",
Expand Down
168 changes: 168 additions & 0 deletions packages/create-agents/src/__tests__/errorTracking.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import fs from 'fs-extra';
import os from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import {
disableTelemetry,
enableTelemetry,
getTelemetryConfig,
saveTelemetryConfig,
} from '../errorTracking';

// Mock @sentry/node
vi.mock('@sentry/node', () => ({
init: vi.fn(),
captureException: vi.fn(),
captureMessage: vi.fn(),
addBreadcrumb: vi.fn(),
setContext: vi.fn(),
close: vi.fn().mockResolvedValue(undefined),
}));

// Mock fs-extra
vi.mock('fs-extra');

const TELEMETRY_CONFIG_DIR = path.join(os.homedir(), '.inkeep');
const TELEMETRY_CONFIG_FILE = path.join(TELEMETRY_CONFIG_DIR, 'telemetry-config.json');

describe('Error Tracking', () => {
beforeEach(() => {
vi.clearAllMocks();

// Mock fs-extra methods
vi.mocked(fs.existsSync).mockReturnValue(false);
vi.mocked(fs.readFileSync).mockReturnValue('{}');
vi.mocked(fs.writeFileSync).mockImplementation(() => {});
vi.mocked(fs.ensureDirSync).mockImplementation(() => {});
});

afterEach(() => {
vi.restoreAllMocks();
});

describe('getTelemetryConfig', () => {
it('should return default config when file does not exist', () => {
vi.mocked(fs.existsSync).mockReturnValue(false);

const config = getTelemetryConfig();

expect(config).toEqual({
enabled: true,
askedConsent: false,
});
});

it('should load config from file when it exists', async () => {
// Need to clear the cached config first by reimporting
vi.resetModules();

const mockConfig = {
enabled: false,
askedConsent: true,
userId: 'test-user-id',
};

vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));

// Re-import to get fresh module
const { getTelemetryConfig: freshGetTelemetryConfig } = await import('../errorTracking.js');
const config = freshGetTelemetryConfig();

expect(config).toEqual(mockConfig);
expect(fs.readFileSync).toHaveBeenCalledWith(TELEMETRY_CONFIG_FILE, 'utf-8');
});

it('should return default config when file read fails', () => {
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockImplementation(() => {
throw new Error('Read error');
});

const config = getTelemetryConfig();

expect(config).toEqual({
enabled: true,
askedConsent: false,
});
});
});

describe('saveTelemetryConfig', () => {
it('should save config to file', () => {
const config = {
enabled: true,
askedConsent: true,
userId: 'test-user',
};

saveTelemetryConfig(config);

expect(fs.ensureDirSync).toHaveBeenCalledWith(TELEMETRY_CONFIG_DIR);
expect(fs.writeFileSync).toHaveBeenCalledWith(
TELEMETRY_CONFIG_FILE,
JSON.stringify(config, null, 2)
);
});

it('should handle save errors gracefully', () => {
vi.mocked(fs.writeFileSync).mockImplementation(() => {
throw new Error('Write error');
});

const config = {
enabled: true,
askedConsent: true,
};

// Should not throw
expect(() => saveTelemetryConfig(config)).not.toThrow();
});
});

describe('disableTelemetry', () => {
it('should disable telemetry and save config', () => {
vi.mocked(fs.existsSync).mockReturnValue(false);

disableTelemetry();

expect(fs.writeFileSync).toHaveBeenCalledWith(
TELEMETRY_CONFIG_FILE,
expect.stringContaining('"enabled": false')
);
expect(fs.writeFileSync).toHaveBeenCalledWith(
TELEMETRY_CONFIG_FILE,
expect.stringContaining('"askedConsent": true')
);
});
});

describe('enableTelemetry', () => {
it('should enable telemetry and save config', () => {
vi.mocked(fs.existsSync).mockReturnValue(false);

enableTelemetry();

expect(fs.writeFileSync).toHaveBeenCalledWith(
TELEMETRY_CONFIG_FILE,
expect.stringContaining('"enabled": true')
);
expect(fs.writeFileSync).toHaveBeenCalledWith(
TELEMETRY_CONFIG_FILE,
expect.stringContaining('"askedConsent": true')
);
});
});

describe('initErrorTracking', () => {
it('should not initialize in test environment', async () => {
const Sentry = await import('@sentry/node');

// Import and call initErrorTracking
const { initErrorTracking } = await import('../errorTracking.js');
initErrorTracking('1.0.0');

expect(Sentry.init).not.toHaveBeenCalled();
});
});
});
8 changes: 8 additions & 0 deletions packages/create-agents/src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ vi.mock('../templates');
vi.mock('@clack/prompts');
vi.mock('child_process');
vi.mock('util');
vi.mock('../errorTracking', () => ({
addBreadcrumb: vi.fn(),
captureError: vi.fn(),
captureMessage: vi.fn(),
getTelemetryConfig: vi.fn().mockReturnValue({ enabled: true, askedConsent: true }),
saveTelemetryConfig: vi.fn(),
disableTelemetry: vi.fn(),
}));

// Setup default mocks
const mockSpinner = {
Expand Down
Loading
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载