canary-agent 0.1.7 → 0.2.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/README.md +73 -75
- package/dist/openclaw.cjs +1 -1
- package/dist/openclaw.cjs.map +1 -1
- package/dist/openclaw.js +1 -1
- package/dist/openclaw.js.map +1 -1
- package/dist/setup.cjs +95 -59
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,43 @@
|
|
|
1
1
|
# canary-agent
|
|
2
2
|
|
|
3
|
-
API observability SDK for AI agents.
|
|
3
|
+
API observability SDK for AI agents. Captures HTTP telemetry and structured feedback from AI agent sessions.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### Option A: Paste into Claude Code (easiest)
|
|
8
|
+
|
|
9
|
+
Copy this prompt and paste it into Claude Code:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Install and configure canary-agent for me. Run these two commands:
|
|
13
|
+
|
|
14
|
+
1. npm install canary-agent
|
|
15
|
+
2. npx canary-setup --api-key PASTE_YOUR_KEY_HERE --local
|
|
16
|
+
|
|
17
|
+
Then verify that .mcp.json was created with the canary-journal MCP server entry,
|
|
18
|
+
and that ~/.claude/skills/canary-feedback/SKILL.md exists. Tell me when it's ready.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Replace `PASTE_YOUR_KEY_HERE` with your actual API key (`cnry_sk_...`).
|
|
22
|
+
|
|
23
|
+
### Option B: One command
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install canary-agent && npx canary-setup --api-key cnry_sk_YOUR_KEY --local
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This writes `.mcp.json` (MCP server config) and installs the Claude Code skill. Restart Claude Code and you're done.
|
|
30
|
+
|
|
31
|
+
### Option C: Interactive setup
|
|
6
32
|
|
|
7
33
|
```bash
|
|
8
34
|
npm install canary-agent
|
|
35
|
+
npx canary-setup
|
|
9
36
|
```
|
|
10
37
|
|
|
11
|
-
|
|
38
|
+
Walks you through API key, endpoint, config location (Claude Code / Cursor / both), and skill installation.
|
|
12
39
|
|
|
13
|
-
###
|
|
40
|
+
### Option D: Manual
|
|
14
41
|
|
|
15
42
|
Add to your project's `.mcp.json`:
|
|
16
43
|
|
|
@@ -21,108 +48,79 @@ Add to your project's `.mcp.json`:
|
|
|
21
48
|
"command": "npx",
|
|
22
49
|
"args": ["-y", "canary-agent"],
|
|
23
50
|
"env": {
|
|
24
|
-
"CANARY_API_KEY": "
|
|
25
|
-
"CANARY_ENDPOINT": "https://api.canary.dev"
|
|
51
|
+
"CANARY_API_KEY": "cnry_sk_YOUR_KEY"
|
|
26
52
|
}
|
|
27
53
|
}
|
|
28
54
|
}
|
|
29
55
|
}
|
|
30
56
|
```
|
|
31
57
|
|
|
32
|
-
|
|
58
|
+
## What gets installed
|
|
33
59
|
|
|
34
|
-
|
|
35
|
-
|
|
60
|
+
| Component | Purpose | Location |
|
|
61
|
+
|-----------|---------|----------|
|
|
62
|
+
| **MCP server** | Provides `canary_annotate` + `canary_report` tools | `.mcp.json` or `~/.claude/mcp.json` |
|
|
63
|
+
| **Skill** | Tells Claude *when and how* to use the tools | `~/.claude/skills/canary-feedback/` |
|
|
36
64
|
|
|
37
|
-
|
|
65
|
+
Both are needed for Claude Code. The MCP server provides the tools; the skill provides the instructions.
|
|
38
66
|
|
|
39
|
-
|
|
40
|
-
import { init, shutdown, survey } from 'canary-agent';
|
|
41
|
-
|
|
42
|
-
// Initialize — patches http/https and fetch automatically
|
|
43
|
-
init({
|
|
44
|
-
apiKey: process.env.CANARY_API_KEY,
|
|
45
|
-
endpoint: 'https://api.canary.dev',
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// All HTTP calls to tracked APIs are now captured automatically
|
|
49
|
-
const res = await fetch('https://api.stripe.com/v1/charges');
|
|
67
|
+
## Setup flags
|
|
50
68
|
|
|
51
|
-
// Optional: submit manual feedback
|
|
52
|
-
survey({
|
|
53
|
-
worked: true,
|
|
54
|
-
context: 'Payment processed successfully',
|
|
55
|
-
provider: 'stripe',
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// At shutdown, auto-reports are generated
|
|
59
|
-
await shutdown();
|
|
60
69
|
```
|
|
70
|
+
npx canary-setup [options]
|
|
61
71
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| `sessionId` | `CANARY_SESSION_ID` | Link to a specific session |
|
|
69
|
-
| `agentName` | `CANARY_AGENT_NAME` | Identify this agent in multi-agent setups |
|
|
70
|
-
| `autoReport` | — | Auto-generate feedback at shutdown (default: `true`) |
|
|
71
|
-
| `autoFlush` | — | Start periodic flush timer (default: `true`) |
|
|
72
|
-
|
|
73
|
-
## Session Correlation
|
|
74
|
-
|
|
75
|
-
Use `runWithSession()` to scope API calls to a session:
|
|
72
|
+
--api-key KEY Your Canary API key (enables non-interactive mode)
|
|
73
|
+
--local Write .mcp.json in current directory (default: ~/.claude/mcp.json)
|
|
74
|
+
--cursor Write config for Cursor (~/.cursor/mcp.json)
|
|
75
|
+
--endpoint URL Custom backend URL
|
|
76
|
+
--no-skill Skip skill installation
|
|
77
|
+
```
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
import { runWithSession } from 'canary-agent';
|
|
79
|
+
## How it works
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
```
|
|
82
|
+
Your agent (Claude Code, Cursor, etc.)
|
|
83
|
+
├── canary_annotate → saves notes locally (~/.canary/annotations/)
|
|
84
|
+
└── canary_report → submits feedback to Canary backend
|
|
85
|
+
↓
|
|
86
|
+
Dashboard shows reports, quality scores, credits
|
|
84
87
|
```
|
|
85
88
|
|
|
86
|
-
##
|
|
89
|
+
## SDK (programmatic)
|
|
87
90
|
|
|
88
|
-
|
|
89
|
-
npx canary-agent --api-key=YOUR_KEY --endpoint=https://api.canary.dev
|
|
90
|
-
```
|
|
91
|
+
For embedding in your own Node.js app:
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
```typescript
|
|
94
|
+
import { init, shutdown, survey } from 'canary-agent';
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
CANARY_API_KEY=your_key npx canary-agent
|
|
96
|
-
```
|
|
96
|
+
init({ apiKey: process.env.CANARY_API_KEY });
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
// All HTTP calls are now captured automatically
|
|
99
|
+
const res = await fetch('https://api.stripe.com/v1/charges');
|
|
99
100
|
|
|
100
|
-
|
|
101
|
+
survey({ worked: true, context: 'Payment processed', provider: 'stripe' });
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
{
|
|
104
|
-
"mcpServers": {
|
|
105
|
-
"canary-journal": {
|
|
106
|
-
"command": "npx",
|
|
107
|
-
"args": ["-y", "canary-agent"],
|
|
108
|
-
"env": {
|
|
109
|
-
"CANARY_API_KEY": "shared_team_key",
|
|
110
|
-
"CANARY_AGENT_NAME": "checkout-agent"
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
103
|
+
await shutdown();
|
|
115
104
|
```
|
|
116
105
|
|
|
117
106
|
## OpenClaw Integration
|
|
118
107
|
|
|
119
|
-
For OpenClaw
|
|
108
|
+
For OpenClaw agents, import the auto-init module:
|
|
120
109
|
|
|
121
110
|
```typescript
|
|
122
111
|
import 'canary-agent/openclaw';
|
|
123
112
|
```
|
|
124
113
|
|
|
125
|
-
This automatically initializes
|
|
114
|
+
This automatically initializes when `OPENCLAW_SESSION_ID` and `CANARY_API_KEY` are set. HTTP interception works automatically in the OpenClaw process.
|
|
115
|
+
|
|
116
|
+
## Configuration
|
|
117
|
+
|
|
118
|
+
| Option | Env Var | Description |
|
|
119
|
+
|--------|---------|-------------|
|
|
120
|
+
| `apiKey` | `CANARY_API_KEY` | Your API key (required, starts with `cnry_sk_`) |
|
|
121
|
+
| `endpoint` | `CANARY_ENDPOINT` | Backend URL (default: production) |
|
|
122
|
+
| `sessionId` | `CANARY_SESSION_ID` | Link to a specific session |
|
|
123
|
+
| `agentName` | `CANARY_AGENT_NAME` | Identify this agent in multi-agent setups |
|
|
126
124
|
|
|
127
125
|
## Tracked Providers
|
|
128
126
|
|
package/dist/openclaw.cjs
CHANGED
|
@@ -11,7 +11,7 @@ var sessionId = process.env.OPENCLAW_SESSION_ID;
|
|
|
11
11
|
if (apiKey && sessionId) {
|
|
12
12
|
_chunkDRKEGQ7Ucjs.init.call(void 0, {
|
|
13
13
|
apiKey,
|
|
14
|
-
endpoint: process.env.CANARY_ENDPOINT || "https://
|
|
14
|
+
endpoint: process.env.CANARY_ENDPOINT || "https://canary-production-89d8.up.railway.app"
|
|
15
15
|
});
|
|
16
16
|
_chunkDRKEGQ7Ucjs.setFrameworkSession.call(void 0, sessionId);
|
|
17
17
|
const cleanup = () => {
|
package/dist/openclaw.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/stalapaneni/conductor/workspaces/canary/brasilia-v1/canary/dist/openclaw.cjs","../src/openclaw.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACWA,IAAM,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,cAAA;AAC3B,IAAM,UAAA,EAAY,OAAA,CAAQ,GAAA,CAAI,mBAAA;AAE9B,GAAA,CAAI,OAAA,GAAU,SAAA,EAAW;AACvB,EAAA,oCAAA;AAAK,IACH,MAAA;AAAA,IACA,QAAA,EAAU,OAAA,CAAQ,GAAA,CAAI,gBAAA,GAAmB;AAAA,EAC3C,CAAC,CAAA;AACD,EAAA,mDAAA,SAA6B,CAAA;AAE7B,EAAA,MAAM,QAAA,EAAU,CAAA,EAAA,GAAM;AACpB,IAAA,qDAAA,CAAsB;AACtB,IAAA,wCAAA,CAAS,CAAE,KAAA,CAAM,CAAA,EAAA,GAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EAC3B,CAAA;AAEA,EAAA,OAAA,CAAQ,EAAA,CAAG,YAAA,EAAc,OAAO,CAAA;AAChC,EAAA,OAAA,CAAQ,EAAA,CAAG,SAAA,EAAW,OAAO,CAAA;AAC7B,EAAA,OAAA,CAAQ,EAAA,CAAG,QAAA,EAAU,OAAO,CAAA;AAC9B;ADXA;AACE;AACA;AACA;AACF,0JAAC","file":"/Users/stalapaneni/conductor/workspaces/canary/brasilia-v1/canary/dist/openclaw.cjs","sourcesContent":[null,"/**\n * OpenClaw auto-init entry point.\n * Import this module in OpenClaw skills to automatically initialize Canary.\n *\n * Environment variables:\n * CANARY_API_KEY — required\n * CANARY_ENDPOINT — backend URL (default: https://api.canary.dev)\n * OPENCLAW_SESSION_ID — used as framework_session_id\n * CANARY_AGENT_NAME — agent identifier\n */\n\nimport {\n clearFrameworkSession,\n init,\n shutdown,\n setFrameworkSession,\n} from \"./index.js\";\n\nconst apiKey = process.env.CANARY_API_KEY;\nconst sessionId = process.env.OPENCLAW_SESSION_ID;\n\nif (apiKey && sessionId) {\n init({\n apiKey,\n endpoint: process.env.CANARY_ENDPOINT || \"https://
|
|
1
|
+
{"version":3,"sources":["/Users/stalapaneni/conductor/workspaces/canary/brasilia-v1/canary/dist/openclaw.cjs","../src/openclaw.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACWA,IAAM,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,cAAA;AAC3B,IAAM,UAAA,EAAY,OAAA,CAAQ,GAAA,CAAI,mBAAA;AAE9B,GAAA,CAAI,OAAA,GAAU,SAAA,EAAW;AACvB,EAAA,oCAAA;AAAK,IACH,MAAA;AAAA,IACA,QAAA,EAAU,OAAA,CAAQ,GAAA,CAAI,gBAAA,GAAmB;AAAA,EAC3C,CAAC,CAAA;AACD,EAAA,mDAAA,SAA6B,CAAA;AAE7B,EAAA,MAAM,QAAA,EAAU,CAAA,EAAA,GAAM;AACpB,IAAA,qDAAA,CAAsB;AACtB,IAAA,wCAAA,CAAS,CAAE,KAAA,CAAM,CAAA,EAAA,GAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EAC3B,CAAA;AAEA,EAAA,OAAA,CAAQ,EAAA,CAAG,YAAA,EAAc,OAAO,CAAA;AAChC,EAAA,OAAA,CAAQ,EAAA,CAAG,SAAA,EAAW,OAAO,CAAA;AAC7B,EAAA,OAAA,CAAQ,EAAA,CAAG,QAAA,EAAU,OAAO,CAAA;AAC9B;ADXA;AACE;AACA;AACA;AACF,0JAAC","file":"/Users/stalapaneni/conductor/workspaces/canary/brasilia-v1/canary/dist/openclaw.cjs","sourcesContent":[null,"/**\n * OpenClaw auto-init entry point.\n * Import this module in OpenClaw skills to automatically initialize Canary.\n *\n * Environment variables:\n * CANARY_API_KEY — required\n * CANARY_ENDPOINT — backend URL (default: https://api.canary.dev)\n * OPENCLAW_SESSION_ID — used as framework_session_id\n * CANARY_AGENT_NAME — agent identifier\n */\n\nimport {\n clearFrameworkSession,\n init,\n shutdown,\n setFrameworkSession,\n} from \"./index.js\";\n\nconst apiKey = process.env.CANARY_API_KEY;\nconst sessionId = process.env.OPENCLAW_SESSION_ID;\n\nif (apiKey && sessionId) {\n init({\n apiKey,\n endpoint: process.env.CANARY_ENDPOINT || \"https://canary-production-89d8.up.railway.app\",\n });\n setFrameworkSession(sessionId);\n\n const cleanup = () => {\n clearFrameworkSession();\n shutdown().catch(() => {});\n };\n\n process.on(\"beforeExit\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n process.on(\"SIGINT\", cleanup);\n}\n\nexport { init, shutdown, setFrameworkSession } from \"./index.js\";\n"]}
|
package/dist/openclaw.js
CHANGED
|
@@ -11,7 +11,7 @@ var sessionId = process.env.OPENCLAW_SESSION_ID;
|
|
|
11
11
|
if (apiKey && sessionId) {
|
|
12
12
|
init({
|
|
13
13
|
apiKey,
|
|
14
|
-
endpoint: process.env.CANARY_ENDPOINT || "https://
|
|
14
|
+
endpoint: process.env.CANARY_ENDPOINT || "https://canary-production-89d8.up.railway.app"
|
|
15
15
|
});
|
|
16
16
|
setFrameworkSession(sessionId);
|
|
17
17
|
const cleanup = () => {
|
package/dist/openclaw.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/openclaw.ts"],"sourcesContent":["/**\n * OpenClaw auto-init entry point.\n * Import this module in OpenClaw skills to automatically initialize Canary.\n *\n * Environment variables:\n * CANARY_API_KEY — required\n * CANARY_ENDPOINT — backend URL (default: https://api.canary.dev)\n * OPENCLAW_SESSION_ID — used as framework_session_id\n * CANARY_AGENT_NAME — agent identifier\n */\n\nimport {\n clearFrameworkSession,\n init,\n shutdown,\n setFrameworkSession,\n} from \"./index.js\";\n\nconst apiKey = process.env.CANARY_API_KEY;\nconst sessionId = process.env.OPENCLAW_SESSION_ID;\n\nif (apiKey && sessionId) {\n init({\n apiKey,\n endpoint: process.env.CANARY_ENDPOINT || \"https://
|
|
1
|
+
{"version":3,"sources":["../src/openclaw.ts"],"sourcesContent":["/**\n * OpenClaw auto-init entry point.\n * Import this module in OpenClaw skills to automatically initialize Canary.\n *\n * Environment variables:\n * CANARY_API_KEY — required\n * CANARY_ENDPOINT — backend URL (default: https://api.canary.dev)\n * OPENCLAW_SESSION_ID — used as framework_session_id\n * CANARY_AGENT_NAME — agent identifier\n */\n\nimport {\n clearFrameworkSession,\n init,\n shutdown,\n setFrameworkSession,\n} from \"./index.js\";\n\nconst apiKey = process.env.CANARY_API_KEY;\nconst sessionId = process.env.OPENCLAW_SESSION_ID;\n\nif (apiKey && sessionId) {\n init({\n apiKey,\n endpoint: process.env.CANARY_ENDPOINT || \"https://canary-production-89d8.up.railway.app\",\n });\n setFrameworkSession(sessionId);\n\n const cleanup = () => {\n clearFrameworkSession();\n shutdown().catch(() => {});\n };\n\n process.on(\"beforeExit\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n process.on(\"SIGINT\", cleanup);\n}\n\nexport { init, shutdown, setFrameworkSession } from \"./index.js\";\n"],"mappings":";;;;;;;;AAkBA,IAAM,SAAS,QAAQ,IAAI;AAC3B,IAAM,YAAY,QAAQ,IAAI;AAE9B,IAAI,UAAU,WAAW;AACvB,OAAK;AAAA,IACH;AAAA,IACA,UAAU,QAAQ,IAAI,mBAAmB;AAAA,EAC3C,CAAC;AACD,sBAAoB,SAAS;AAE7B,QAAM,UAAU,MAAM;AACpB,0BAAsB;AACtB,aAAS,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC3B;AAEA,UAAQ,GAAG,cAAc,OAAO;AAChC,UAAQ,GAAG,WAAW,OAAO;AAC7B,UAAQ,GAAG,UAAU,OAAO;AAC9B;","names":[]}
|
package/dist/setup.cjs
CHANGED
|
@@ -13,56 +13,66 @@ function parseCliArgs() {
|
|
|
13
13
|
let apiKey = "";
|
|
14
14
|
let endpoint = "";
|
|
15
15
|
let local = false;
|
|
16
|
+
let cursor = false;
|
|
16
17
|
let skipSkill = false;
|
|
17
18
|
for (let i = 0; i < args.length; i++) {
|
|
18
19
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
19
|
-
else if (args[i].startsWith("--api-key=")) apiKey = args[i].split("=")
|
|
20
|
+
else if (args[i].startsWith("--api-key=")) apiKey = args[i].split("=").slice(1).join("=");
|
|
20
21
|
else if (args[i] === "--endpoint" && args[i + 1]) endpoint = args[++i];
|
|
21
|
-
else if (args[i].startsWith("--endpoint=")) endpoint = args[i].split("=")
|
|
22
|
+
else if (args[i].startsWith("--endpoint=")) endpoint = args[i].split("=").slice(1).join("=");
|
|
22
23
|
else if (args[i] === "--local") local = true;
|
|
24
|
+
else if (args[i] === "--cursor") cursor = true;
|
|
23
25
|
else if (args[i] === "--no-skill") skipSkill = true;
|
|
24
26
|
}
|
|
25
|
-
return { apiKey, endpoint, local, skipSkill, isNonInteractive: !!apiKey };
|
|
27
|
+
return { apiKey, endpoint, local, cursor, skipSkill, isNonInteractive: !!apiKey };
|
|
26
28
|
}
|
|
27
29
|
function ask(rl, question) {
|
|
28
30
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
29
31
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
CANARY_API_KEY: apiKey || "YOUR_API_KEY_HERE",
|
|
38
|
-
...endpoint !== DEFAULT_ENDPOINT ? { CANARY_ENDPOINT: endpoint } : {}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
32
|
+
function buildMcpEntry(apiKey, endpoint) {
|
|
33
|
+
return {
|
|
34
|
+
command: "npx",
|
|
35
|
+
args: ["-y", "canary-agent"],
|
|
36
|
+
env: {
|
|
37
|
+
CANARY_API_KEY: apiKey || "YOUR_API_KEY_HERE",
|
|
38
|
+
...endpoint !== DEFAULT_ENDPOINT ? { CANARY_ENDPOINT: endpoint } : {}
|
|
41
39
|
}
|
|
42
40
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
configPath = (0, import_node_path.join)(process.cwd(), ".mcp.json");
|
|
46
|
-
} else {
|
|
47
|
-
const claudeDir = (0, import_node_path.join)((0, import_node_os.homedir)(), ".claude");
|
|
48
|
-
(0, import_node_fs.mkdirSync)(claudeDir, { recursive: true });
|
|
49
|
-
configPath = (0, import_node_path.join)(claudeDir, "mcp.json");
|
|
50
|
-
}
|
|
41
|
+
}
|
|
42
|
+
function mergeJsonFile(path, key, serverName, entry) {
|
|
51
43
|
let existing = {};
|
|
52
|
-
if ((0, import_node_fs.existsSync)(
|
|
44
|
+
if ((0, import_node_fs.existsSync)(path)) {
|
|
53
45
|
try {
|
|
54
|
-
existing = JSON.parse((0, import_node_fs.readFileSync)(
|
|
46
|
+
existing = JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
55
47
|
} catch {
|
|
56
48
|
}
|
|
57
49
|
}
|
|
58
50
|
const merged = {
|
|
59
51
|
...existing,
|
|
60
|
-
|
|
61
|
-
...existing
|
|
62
|
-
|
|
52
|
+
[key]: {
|
|
53
|
+
...existing[key] || {},
|
|
54
|
+
[serverName]: entry
|
|
63
55
|
}
|
|
64
56
|
};
|
|
65
|
-
(0, import_node_fs.writeFileSync)(
|
|
57
|
+
(0, import_node_fs.writeFileSync)(path, JSON.stringify(merged, null, 2) + "\n");
|
|
58
|
+
}
|
|
59
|
+
function writeClaudeGlobalMcp(apiKey, endpoint) {
|
|
60
|
+
const claudeDir = (0, import_node_path.join)((0, import_node_os.homedir)(), ".claude");
|
|
61
|
+
(0, import_node_fs.mkdirSync)(claudeDir, { recursive: true });
|
|
62
|
+
const configPath = (0, import_node_path.join)(claudeDir, "mcp.json");
|
|
63
|
+
mergeJsonFile(configPath, "mcpServers", "canary-journal", buildMcpEntry(apiKey, endpoint));
|
|
64
|
+
return configPath;
|
|
65
|
+
}
|
|
66
|
+
function writeProjectMcp(apiKey, endpoint) {
|
|
67
|
+
const configPath = (0, import_node_path.join)(process.cwd(), ".mcp.json");
|
|
68
|
+
mergeJsonFile(configPath, "mcpServers", "canary-journal", buildMcpEntry(apiKey, endpoint));
|
|
69
|
+
return configPath;
|
|
70
|
+
}
|
|
71
|
+
function writeCursorMcp(apiKey, endpoint) {
|
|
72
|
+
const cursorDir = (0, import_node_path.join)((0, import_node_os.homedir)(), ".cursor");
|
|
73
|
+
(0, import_node_fs.mkdirSync)(cursorDir, { recursive: true });
|
|
74
|
+
const configPath = (0, import_node_path.join)(cursorDir, "mcp.json");
|
|
75
|
+
mergeJsonFile(configPath, "mcpServers", "canary-journal", buildMcpEntry(apiKey, endpoint));
|
|
66
76
|
return configPath;
|
|
67
77
|
}
|
|
68
78
|
function installSkill() {
|
|
@@ -94,60 +104,86 @@ async function runInteractive() {
|
|
|
94
104
|
console.log("");
|
|
95
105
|
const apiKey = await ask(rl, " Enter your Canary API key (cnry_sk_...): ");
|
|
96
106
|
if (!apiKey.trim()) {
|
|
97
|
-
console.log("
|
|
107
|
+
console.log(" No API key provided. You can set CANARY_API_KEY env var later.\n");
|
|
98
108
|
}
|
|
99
109
|
const customEndpoint = await ask(rl, ` Backend endpoint [${DEFAULT_ENDPOINT}]: `);
|
|
100
110
|
const endpoint = customEndpoint.trim() || DEFAULT_ENDPOINT;
|
|
101
111
|
console.log("");
|
|
102
|
-
console.log(" Where
|
|
103
|
-
console.log(" 1) ~/.claude/mcp.json
|
|
104
|
-
console.log(" 2) .mcp.json
|
|
112
|
+
console.log(" Where are you using Canary?");
|
|
113
|
+
console.log(" 1) Claude Code (global ~/.claude/mcp.json)");
|
|
114
|
+
console.log(" 2) Claude Code (project .mcp.json)");
|
|
115
|
+
console.log(" 3) Cursor");
|
|
116
|
+
console.log(" 4) All of the above");
|
|
105
117
|
console.log("");
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
118
|
+
const choice = await ask(rl, " Choice [1]: ");
|
|
119
|
+
const paths = [];
|
|
120
|
+
const c = choice.trim();
|
|
121
|
+
if (c === "2") {
|
|
122
|
+
paths.push(writeProjectMcp(apiKey.trim(), endpoint));
|
|
123
|
+
} else if (c === "3") {
|
|
124
|
+
paths.push(writeCursorMcp(apiKey.trim(), endpoint));
|
|
125
|
+
} else if (c === "4") {
|
|
126
|
+
paths.push(writeClaudeGlobalMcp(apiKey.trim(), endpoint));
|
|
127
|
+
paths.push(writeProjectMcp(apiKey.trim(), endpoint));
|
|
128
|
+
paths.push(writeCursorMcp(apiKey.trim(), endpoint));
|
|
129
|
+
} else {
|
|
130
|
+
paths.push(writeClaudeGlobalMcp(apiKey.trim(), endpoint));
|
|
131
|
+
}
|
|
132
|
+
for (const p of paths) {
|
|
133
|
+
console.log(` Wrote MCP config: ${p}`);
|
|
134
|
+
}
|
|
111
135
|
console.log("");
|
|
112
136
|
const doSkill = await ask(rl, " Install the canary-feedback skill for Claude Code? (Y/n): ");
|
|
113
137
|
if (doSkill.trim().toLowerCase() !== "n") {
|
|
114
138
|
const dest = installSkill();
|
|
115
|
-
if (dest) {
|
|
116
|
-
|
|
117
|
-
} else {
|
|
118
|
-
console.log(" Skill files not found in package. Skipping.");
|
|
119
|
-
}
|
|
139
|
+
if (dest) console.log(` Installed skill: ${dest}`);
|
|
140
|
+
else console.log(" Skill files not found in package. Skipping.");
|
|
120
141
|
}
|
|
121
142
|
rl.close();
|
|
122
|
-
|
|
143
|
+
printDone(paths);
|
|
123
144
|
}
|
|
124
|
-
function runNonInteractive(
|
|
145
|
+
function runNonInteractive(cli2) {
|
|
125
146
|
console.log("");
|
|
126
147
|
console.log(" Canary Agent Setup");
|
|
127
148
|
console.log(" ==================");
|
|
128
149
|
console.log("");
|
|
129
|
-
const endpoint =
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
150
|
+
const endpoint = cli2.endpoint || DEFAULT_ENDPOINT;
|
|
151
|
+
const paths = [];
|
|
152
|
+
if (cli2.cursor) {
|
|
153
|
+
paths.push(writeCursorMcp(cli2.apiKey, endpoint));
|
|
154
|
+
}
|
|
155
|
+
if (cli2.local) {
|
|
156
|
+
paths.push(writeProjectMcp(cli2.apiKey, endpoint));
|
|
157
|
+
}
|
|
158
|
+
if (!cli2.local && !cli2.cursor) {
|
|
159
|
+
paths.push(writeClaudeGlobalMcp(cli2.apiKey, endpoint));
|
|
160
|
+
}
|
|
161
|
+
for (const p of paths) {
|
|
162
|
+
console.log(` Wrote MCP config: ${p}`);
|
|
163
|
+
}
|
|
164
|
+
if (!cli2.skipSkill) {
|
|
133
165
|
const dest = installSkill();
|
|
134
|
-
if (dest) {
|
|
135
|
-
console.log(` Installed skill to: ${dest}`);
|
|
136
|
-
}
|
|
166
|
+
if (dest) console.log(` Installed skill: ${dest}`);
|
|
137
167
|
}
|
|
138
|
-
|
|
168
|
+
printDone(paths);
|
|
139
169
|
}
|
|
140
|
-
function
|
|
170
|
+
function printDone(paths) {
|
|
171
|
+
console.log("");
|
|
172
|
+
console.log(" Setup complete!");
|
|
173
|
+
console.log("");
|
|
174
|
+
console.log(" What was installed:");
|
|
175
|
+
console.log(" - MCP server config (canary_annotate + canary_report tools)");
|
|
176
|
+
console.log(" - canary-feedback skill (tells Claude when/how to use the tools)");
|
|
141
177
|
console.log("");
|
|
142
|
-
console.log("
|
|
143
|
-
console.log(" 1.
|
|
178
|
+
console.log(" Next steps:");
|
|
179
|
+
console.log(" 1. Restart Claude Code / Cursor");
|
|
144
180
|
console.log(" 2. The canary-journal MCP server will auto-start");
|
|
145
|
-
console.log(" 3. Claude will
|
|
181
|
+
console.log(" 3. Claude will annotate API calls and submit feedback automatically");
|
|
146
182
|
console.log("");
|
|
147
183
|
}
|
|
148
|
-
var
|
|
149
|
-
if (
|
|
150
|
-
runNonInteractive(
|
|
184
|
+
var cli = parseCliArgs();
|
|
185
|
+
if (cli.isNonInteractive) {
|
|
186
|
+
runNonInteractive(cli);
|
|
151
187
|
} else {
|
|
152
188
|
runInteractive().catch((err) => {
|
|
153
189
|
console.error("Setup failed:", err.message);
|