canary-agent 0.1.6 → 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 +159 -78
- 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
|
@@ -8,10 +8,95 @@ var import_node_os = require("os");
|
|
|
8
8
|
var import_node_readline = require("readline");
|
|
9
9
|
var import_meta = {};
|
|
10
10
|
var DEFAULT_ENDPOINT = "https://canary-production-89d8.up.railway.app";
|
|
11
|
+
function parseCliArgs() {
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
let apiKey = "";
|
|
14
|
+
let endpoint = "";
|
|
15
|
+
let local = false;
|
|
16
|
+
let cursor = false;
|
|
17
|
+
let skipSkill = false;
|
|
18
|
+
for (let i = 0; i < args.length; i++) {
|
|
19
|
+
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
20
|
+
else if (args[i].startsWith("--api-key=")) apiKey = args[i].split("=").slice(1).join("=");
|
|
21
|
+
else if (args[i] === "--endpoint" && args[i + 1]) endpoint = args[++i];
|
|
22
|
+
else if (args[i].startsWith("--endpoint=")) endpoint = args[i].split("=").slice(1).join("=");
|
|
23
|
+
else if (args[i] === "--local") local = true;
|
|
24
|
+
else if (args[i] === "--cursor") cursor = true;
|
|
25
|
+
else if (args[i] === "--no-skill") skipSkill = true;
|
|
26
|
+
}
|
|
27
|
+
return { apiKey, endpoint, local, cursor, skipSkill, isNonInteractive: !!apiKey };
|
|
28
|
+
}
|
|
11
29
|
function ask(rl, question) {
|
|
12
30
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
13
31
|
}
|
|
14
|
-
|
|
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 } : {}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function mergeJsonFile(path, key, serverName, entry) {
|
|
43
|
+
let existing = {};
|
|
44
|
+
if ((0, import_node_fs.existsSync)(path)) {
|
|
45
|
+
try {
|
|
46
|
+
existing = JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const merged = {
|
|
51
|
+
...existing,
|
|
52
|
+
[key]: {
|
|
53
|
+
...existing[key] || {},
|
|
54
|
+
[serverName]: entry
|
|
55
|
+
}
|
|
56
|
+
};
|
|
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));
|
|
76
|
+
return configPath;
|
|
77
|
+
}
|
|
78
|
+
function installSkill() {
|
|
79
|
+
try {
|
|
80
|
+
const thisDir = typeof __dirname !== "undefined" ? __dirname : new URL(".", import_meta.url).pathname;
|
|
81
|
+
const pkgRoot = (0, import_node_path.join)(thisDir, "..");
|
|
82
|
+
const skillSrc = (0, import_node_path.join)(pkgRoot, "skills", "canary-feedback");
|
|
83
|
+
const claudeDir = (0, import_node_path.join)((0, import_node_os.homedir)(), ".claude");
|
|
84
|
+
const skillDest = (0, import_node_path.join)(claudeDir, "skills", "canary-feedback");
|
|
85
|
+
if ((0, import_node_fs.existsSync)((0, import_node_path.join)(skillSrc, "SKILL.md"))) {
|
|
86
|
+
(0, import_node_fs.mkdirSync)(skillDest, { recursive: true });
|
|
87
|
+
for (const file of ["SKILL.md", "manifest.yaml", "mcp-config.json"]) {
|
|
88
|
+
const src = (0, import_node_path.join)(skillSrc, file);
|
|
89
|
+
if ((0, import_node_fs.existsSync)(src)) {
|
|
90
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.join)(skillDest, file), (0, import_node_fs.readFileSync)(src, "utf8"));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return skillDest;
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
async function runInteractive() {
|
|
15
100
|
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
16
101
|
console.log("");
|
|
17
102
|
console.log(" Canary Agent Setup");
|
|
@@ -19,93 +104,89 @@ async function main() {
|
|
|
19
104
|
console.log("");
|
|
20
105
|
const apiKey = await ask(rl, " Enter your Canary API key (cnry_sk_...): ");
|
|
21
106
|
if (!apiKey.trim()) {
|
|
22
|
-
console.log("
|
|
107
|
+
console.log(" No API key provided. You can set CANARY_API_KEY env var later.\n");
|
|
23
108
|
}
|
|
24
|
-
const customEndpoint = await ask(
|
|
25
|
-
rl,
|
|
26
|
-
` Backend endpoint [${DEFAULT_ENDPOINT}]: `
|
|
27
|
-
);
|
|
109
|
+
const customEndpoint = await ask(rl, ` Backend endpoint [${DEFAULT_ENDPOINT}]: `);
|
|
28
110
|
const endpoint = customEndpoint.trim() || DEFAULT_ENDPOINT;
|
|
29
111
|
console.log("");
|
|
30
|
-
console.log(" Where
|
|
31
|
-
console.log(" 1) ~/.claude/mcp.json
|
|
32
|
-
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");
|
|
33
117
|
console.log("");
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
let configPath;
|
|
48
|
-
if (configChoice.trim() === "2") {
|
|
49
|
-
configPath = (0, import_node_path.join)(process.cwd(), ".mcp.json");
|
|
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));
|
|
50
129
|
} else {
|
|
51
|
-
|
|
52
|
-
(0, import_node_fs.mkdirSync)(claudeDir, { recursive: true });
|
|
53
|
-
configPath = (0, import_node_path.join)(claudeDir, "mcp.json");
|
|
130
|
+
paths.push(writeClaudeGlobalMcp(apiKey.trim(), endpoint));
|
|
54
131
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
existing = JSON.parse((0, import_node_fs.readFileSync)(configPath, "utf8"));
|
|
59
|
-
} catch {
|
|
60
|
-
}
|
|
132
|
+
for (const p of paths) {
|
|
133
|
+
console.log(` Wrote MCP config: ${p}`);
|
|
61
134
|
}
|
|
62
|
-
const merged = {
|
|
63
|
-
...existing,
|
|
64
|
-
mcpServers: {
|
|
65
|
-
...existing.mcpServers || {},
|
|
66
|
-
...mcpConfig.mcpServers
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
(0, import_node_fs.writeFileSync)(configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
70
|
-
console.log(`
|
|
71
|
-
Wrote MCP config to: ${configPath}`);
|
|
72
135
|
console.log("");
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
console.log(` Could not install skill: ${err.message}`);
|
|
98
|
-
}
|
|
136
|
+
const doSkill = await ask(rl, " Install the canary-feedback skill for Claude Code? (Y/n): ");
|
|
137
|
+
if (doSkill.trim().toLowerCase() !== "n") {
|
|
138
|
+
const dest = installSkill();
|
|
139
|
+
if (dest) console.log(` Installed skill: ${dest}`);
|
|
140
|
+
else console.log(" Skill files not found in package. Skipping.");
|
|
141
|
+
}
|
|
142
|
+
rl.close();
|
|
143
|
+
printDone(paths);
|
|
144
|
+
}
|
|
145
|
+
function runNonInteractive(cli2) {
|
|
146
|
+
console.log("");
|
|
147
|
+
console.log(" Canary Agent Setup");
|
|
148
|
+
console.log(" ==================");
|
|
149
|
+
console.log("");
|
|
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));
|
|
99
160
|
}
|
|
161
|
+
for (const p of paths) {
|
|
162
|
+
console.log(` Wrote MCP config: ${p}`);
|
|
163
|
+
}
|
|
164
|
+
if (!cli2.skipSkill) {
|
|
165
|
+
const dest = installSkill();
|
|
166
|
+
if (dest) console.log(` Installed skill: ${dest}`);
|
|
167
|
+
}
|
|
168
|
+
printDone(paths);
|
|
169
|
+
}
|
|
170
|
+
function printDone(paths) {
|
|
171
|
+
console.log("");
|
|
172
|
+
console.log(" Setup complete!");
|
|
100
173
|
console.log("");
|
|
101
|
-
console.log("
|
|
102
|
-
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)");
|
|
177
|
+
console.log("");
|
|
178
|
+
console.log(" Next steps:");
|
|
179
|
+
console.log(" 1. Restart Claude Code / Cursor");
|
|
103
180
|
console.log(" 2. The canary-journal MCP server will auto-start");
|
|
104
|
-
console.log(" 3. Claude will
|
|
181
|
+
console.log(" 3. Claude will annotate API calls and submit feedback automatically");
|
|
105
182
|
console.log("");
|
|
106
|
-
rl.close();
|
|
107
183
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
184
|
+
var cli = parseCliArgs();
|
|
185
|
+
if (cli.isNonInteractive) {
|
|
186
|
+
runNonInteractive(cli);
|
|
187
|
+
} else {
|
|
188
|
+
runInteractive().catch((err) => {
|
|
189
|
+
console.error("Setup failed:", err.message);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
});
|
|
192
|
+
}
|