@orgloop/connector-claude-code 0.1.8 → 0.7.6
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 +37 -10
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/source.d.ts +6 -17
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +5 -147
- package/dist/source.js.map +1 -1
- package/package.json +3 -2
- package/dist/__tests__/source.test.d.ts +0 -2
- package/dist/__tests__/source.test.d.ts.map +0 -1
- package/dist/__tests__/source.test.js +0 -411
- package/dist/__tests__/source.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @orgloop/connector-claude-code
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Note:** This package is now a backward-compatible alias for [`@orgloop/connector-coding-agent`](../coding-agent/README.md). New projects should use `@orgloop/connector-coding-agent` directly, which supports any coding agent harness via the `platform` config field.
|
|
4
|
+
|
|
5
|
+
Captures Claude Code session lifecycle events via a webhook handler. Instead of polling an external API, this connector exposes an HTTP endpoint that receives POST requests from Claude Code hook scripts (start + stop).
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -30,13 +32,18 @@ sources:
|
|
|
30
32
|
|
|
31
33
|
## Events emitted
|
|
32
34
|
|
|
33
|
-
Events
|
|
35
|
+
Events follow the normalized lifecycle contract in `event.payload.lifecycle` and `event.payload.session`.
|
|
36
|
+
|
|
37
|
+
Non-terminal phases emit `resource.changed`. Terminal phases emit `actor.stopped`.
|
|
34
38
|
|
|
35
39
|
### Event kind
|
|
36
40
|
|
|
37
41
|
| Platform event | Trigger | Description |
|
|
38
42
|
|---|---|---|
|
|
39
|
-
| `session.
|
|
43
|
+
| `session.started` | Claude Code session starts | Session launched (start hook) |
|
|
44
|
+
| `session.completed` | Claude Code session ends with exit 0 | Session completed successfully |
|
|
45
|
+
| `session.failed` | Claude Code session ends with non-zero exit | Session failed |
|
|
46
|
+
| `session.stopped` | Claude Code session ends via signal | Session stopped/cancelled |
|
|
40
47
|
|
|
41
48
|
### Example event payload
|
|
42
49
|
|
|
@@ -48,13 +55,29 @@ Events are emitted as OrgLoop `actor.stopped` type.
|
|
|
48
55
|
"type": "actor.stopped",
|
|
49
56
|
"provenance": {
|
|
50
57
|
"platform": "claude-code",
|
|
51
|
-
"platform_event": "session.
|
|
58
|
+
"platform_event": "session.completed",
|
|
52
59
|
"author": "claude-code",
|
|
53
60
|
"author_type": "bot",
|
|
54
61
|
"session_id": "sess-abc123",
|
|
55
62
|
"working_directory": "/home/user/my-project"
|
|
56
63
|
},
|
|
57
64
|
"payload": {
|
|
65
|
+
"lifecycle": {
|
|
66
|
+
"phase": "completed",
|
|
67
|
+
"terminal": true,
|
|
68
|
+
"outcome": "success",
|
|
69
|
+
"reason": "exit_code_0",
|
|
70
|
+
"dedupe_key": "claude-code:sess-abc123:completed"
|
|
71
|
+
},
|
|
72
|
+
"session": {
|
|
73
|
+
"id": "sess-abc123",
|
|
74
|
+
"adapter": "claude-code",
|
|
75
|
+
"harness": "claude-code",
|
|
76
|
+
"cwd": "/home/user/my-project",
|
|
77
|
+
"started_at": "2025-01-15T10:28:00.000Z",
|
|
78
|
+
"ended_at": "2025-01-15T10:30:00.000Z",
|
|
79
|
+
"exit_status": 0
|
|
80
|
+
},
|
|
58
81
|
"session_id": "sess-abc123",
|
|
59
82
|
"working_directory": "/home/user/my-project",
|
|
60
83
|
"duration_seconds": 120,
|
|
@@ -66,7 +89,7 @@ Events are emitted as OrgLoop `actor.stopped` type.
|
|
|
66
89
|
|
|
67
90
|
### Webhook request format
|
|
68
91
|
|
|
69
|
-
POST a JSON body to the connector's webhook endpoint
|
|
92
|
+
POST a JSON body to the connector's webhook endpoint. The same format is used for start and stop hooks.
|
|
70
93
|
|
|
71
94
|
```json
|
|
72
95
|
{
|
|
@@ -81,10 +104,12 @@ POST a JSON body to the connector's webhook endpoint:
|
|
|
81
104
|
| Field | Type | Required | Description |
|
|
82
105
|
|-------|------|----------|-------------|
|
|
83
106
|
| `session_id` | `string` | yes | Claude Code session identifier |
|
|
84
|
-
| `working_directory` | `string` |
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
107
|
+
| `working_directory` | `string` | no | Working directory of the session |
|
|
108
|
+
| `cwd` | `string` | no | Alias for `working_directory` |
|
|
109
|
+
| `duration_seconds` | `number` | no | Session duration in seconds |
|
|
110
|
+
| `exit_status` | `number` | no | Process exit code (0 = success) |
|
|
87
111
|
| `summary` | `string` | no | Optional session summary text |
|
|
112
|
+
| `hook_type` | `string` | no | `start` or `stop` (defaults to `stop`) |
|
|
88
113
|
| `timestamp` | `string` | no | Optional ISO 8601 timestamp |
|
|
89
114
|
|
|
90
115
|
## Example route
|
|
@@ -108,11 +133,13 @@ routes:
|
|
|
108
133
|
|
|
109
134
|
- **No API tokens needed** -- this connector receives events via HTTP webhook.
|
|
110
135
|
- A Claude Code **Stop hook** must be configured to POST session data to the OrgLoop webhook endpoint when a session exits.
|
|
111
|
-
-
|
|
136
|
+
- An optional **Start hook** can emit `session.started` events on session launch.
|
|
137
|
+
- The connector registration includes setup metadata for both hooks. You can install them with:
|
|
112
138
|
```bash
|
|
113
139
|
orgloop hook claude-code-stop
|
|
140
|
+
orgloop hook claude-code-start
|
|
114
141
|
```
|
|
115
|
-
This registers
|
|
142
|
+
This registers hooks in Claude Code's settings that send session data to OrgLoop.
|
|
116
143
|
|
|
117
144
|
## Environment Variables
|
|
118
145
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @orgloop/connector-claude-code —
|
|
2
|
+
* @orgloop/connector-claude-code — Backward-compatible alias.
|
|
3
|
+
*
|
|
4
|
+
* This package now delegates to @orgloop/connector-coding-agent.
|
|
5
|
+
* The connector ID remains 'claude-code' for existing configs.
|
|
3
6
|
*/
|
|
4
7
|
import type { ConnectorRegistration } from '@orgloop/sdk';
|
|
8
|
+
export { CodingAgentSource as ClaudeCodeSource } from '@orgloop/connector-coding-agent';
|
|
5
9
|
export default function register(): ConnectorRegistration;
|
|
6
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExF,MAAM,CAAC,OAAO,UAAU,QAAQ,IAAI,qBAAqB,CA8BxD"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @orgloop/connector-claude-code —
|
|
2
|
+
* @orgloop/connector-claude-code — Backward-compatible alias.
|
|
3
|
+
*
|
|
4
|
+
* This package now delegates to @orgloop/connector-coding-agent.
|
|
5
|
+
* The connector ID remains 'claude-code' for existing configs.
|
|
3
6
|
*/
|
|
4
|
-
import {
|
|
7
|
+
import { CodingAgentSource } from '@orgloop/connector-coding-agent';
|
|
8
|
+
export { CodingAgentSource as ClaudeCodeSource } from '@orgloop/connector-coding-agent';
|
|
5
9
|
export default function register() {
|
|
6
10
|
return {
|
|
7
11
|
id: 'claude-code',
|
|
8
|
-
source:
|
|
12
|
+
source: CodingAgentSource,
|
|
9
13
|
setup: {
|
|
10
14
|
env_vars: [
|
|
11
15
|
{
|
|
@@ -21,6 +25,12 @@ export default function register() {
|
|
|
21
25
|
platform: 'claude-code',
|
|
22
26
|
command: 'orgloop hook claude-code-stop',
|
|
23
27
|
},
|
|
28
|
+
{
|
|
29
|
+
id: 'claude-code-start-hook',
|
|
30
|
+
description: 'Install a Start hook in Claude Code settings so session launches notify OrgLoop (optional)',
|
|
31
|
+
platform: 'claude-code',
|
|
32
|
+
command: 'orgloop hook claude-code-start',
|
|
33
|
+
},
|
|
24
34
|
],
|
|
25
35
|
},
|
|
26
36
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGpE,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExF,MAAM,CAAC,OAAO,UAAU,QAAQ;IAC/B,OAAO;QACN,EAAE,EAAE,aAAa;QACjB,MAAM,EAAE,iBAAiB;QACzB,KAAK,EAAE;YACN,QAAQ,EAAE;gBACT;oBACC,IAAI,EAAE,4BAA4B;oBAClC,WAAW,EAAE,iEAAiE;oBAC9E,QAAQ,EAAE,KAAK;iBACf;aACD;YACD,YAAY,EAAE;gBACb;oBACC,EAAE,EAAE,uBAAuB;oBAC3B,WAAW,EACV,6EAA6E;oBAC9E,QAAQ,EAAE,aAAa;oBACvB,OAAO,EAAE,+BAA+B;iBACxC;gBACD;oBACC,EAAE,EAAE,wBAAwB;oBAC5B,WAAW,EACV,4FAA4F;oBAC7F,QAAQ,EAAE,aAAa;oBACvB,OAAO,EAAE,gCAAgC;iBACzC;aACD;SACD;KACD,CAAC;AACH,CAAC"}
|
package/dist/source.d.ts
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Claude Code source connector —
|
|
2
|
+
* Claude Code source connector — backward-compatible re-export.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The implementation has moved to @orgloop/connector-coding-agent.
|
|
5
|
+
* This module re-exports the generalized CodingAgentSource as ClaudeCodeSource
|
|
6
|
+
* for backward compatibility.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
readonly id = "claude-code";
|
|
10
|
-
private sourceId;
|
|
11
|
-
private secret?;
|
|
12
|
-
private bufferPath?;
|
|
13
|
-
private pendingEvents;
|
|
14
|
-
init(config: SourceConfig): Promise<void>;
|
|
15
|
-
poll(_checkpoint: string | null): Promise<PollResult>;
|
|
16
|
-
webhook(): WebhookHandler;
|
|
17
|
-
shutdown(): Promise<void>;
|
|
18
|
-
private persistEvent;
|
|
19
|
-
private loadBufferedEvents;
|
|
20
|
-
}
|
|
8
|
+
export type { CodingAgentSessionPayload as ClaudeCodeSessionPayload, CodingAgentSourceConfig as ClaudeCodeSourceConfig, } from '@orgloop/connector-coding-agent';
|
|
9
|
+
export { CodingAgentSource as ClaudeCodeSource } from '@orgloop/connector-coding-agent';
|
|
21
10
|
//# sourceMappingURL=source.d.ts.map
|
package/dist/source.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,YAAY,EACX,yBAAyB,IAAI,wBAAwB,EACrD,uBAAuB,IAAI,sBAAsB,GACjD,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC"}
|
package/dist/source.js
CHANGED
|
@@ -1,151 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Claude Code source connector —
|
|
2
|
+
* Claude Code source connector — backward-compatible re-export.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The implementation has moved to @orgloop/connector-coding-agent.
|
|
5
|
+
* This module re-exports the generalized CodingAgentSource as ClaudeCodeSource
|
|
6
|
+
* for backward compatibility.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
-
import { join } from 'node:path';
|
|
10
|
-
import { buildEvent } from '@orgloop/sdk';
|
|
11
|
-
/** Resolve env var references like ${WEBHOOK_SECRET} */
|
|
12
|
-
function resolveEnvVar(value) {
|
|
13
|
-
const match = value.match(/^\$\{(.+)\}$/);
|
|
14
|
-
if (match) {
|
|
15
|
-
const envValue = process.env[match[1]];
|
|
16
|
-
if (!envValue) {
|
|
17
|
-
throw new Error(`Environment variable ${match[1]} is not set`);
|
|
18
|
-
}
|
|
19
|
-
return envValue;
|
|
20
|
-
}
|
|
21
|
-
return value;
|
|
22
|
-
}
|
|
23
|
-
export class ClaudeCodeSource {
|
|
24
|
-
id = 'claude-code';
|
|
25
|
-
sourceId = 'claude-code';
|
|
26
|
-
secret;
|
|
27
|
-
bufferPath;
|
|
28
|
-
pendingEvents = [];
|
|
29
|
-
async init(config) {
|
|
30
|
-
this.sourceId = config.id;
|
|
31
|
-
const cfg = config.config;
|
|
32
|
-
if (cfg.secret) {
|
|
33
|
-
this.secret = resolveEnvVar(cfg.secret);
|
|
34
|
-
}
|
|
35
|
-
if (cfg.buffer_dir) {
|
|
36
|
-
const dir = resolveEnvVar(cfg.buffer_dir);
|
|
37
|
-
if (!existsSync(dir)) {
|
|
38
|
-
mkdirSync(dir, { recursive: true });
|
|
39
|
-
}
|
|
40
|
-
this.bufferPath = join(dir, `claude-code-${this.sourceId}.jsonl`);
|
|
41
|
-
this.loadBufferedEvents();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async poll(_checkpoint) {
|
|
45
|
-
// Drain any events received via webhook since last poll
|
|
46
|
-
let events;
|
|
47
|
-
if (this.bufferPath) {
|
|
48
|
-
events = this.loadBufferedEvents();
|
|
49
|
-
// Clear the buffer file
|
|
50
|
-
writeFileSync(this.bufferPath, '');
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
events = [...this.pendingEvents];
|
|
54
|
-
this.pendingEvents = [];
|
|
55
|
-
}
|
|
56
|
-
const checkpoint = events.length > 0 ? events[events.length - 1].timestamp : new Date().toISOString();
|
|
57
|
-
return { events, checkpoint };
|
|
58
|
-
}
|
|
59
|
-
webhook() {
|
|
60
|
-
return async (req, res) => {
|
|
61
|
-
if (req.method !== 'POST') {
|
|
62
|
-
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
63
|
-
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
64
|
-
return [];
|
|
65
|
-
}
|
|
66
|
-
const body = await readBody(req);
|
|
67
|
-
// HMAC validation if secret is configured
|
|
68
|
-
if (this.secret) {
|
|
69
|
-
const signature = req.headers['x-hub-signature-256'] ?? req.headers['x-signature'];
|
|
70
|
-
if (!signature) {
|
|
71
|
-
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
72
|
-
res.end(JSON.stringify({ error: 'Missing signature' }));
|
|
73
|
-
return [];
|
|
74
|
-
}
|
|
75
|
-
const expected = `sha256=${createHmac('sha256', this.secret).update(body).digest('hex')}`;
|
|
76
|
-
const sigBuffer = Buffer.from(signature);
|
|
77
|
-
const expectedBuffer = Buffer.from(expected);
|
|
78
|
-
if (sigBuffer.length !== expectedBuffer.length ||
|
|
79
|
-
!timingSafeEqual(sigBuffer, expectedBuffer)) {
|
|
80
|
-
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
81
|
-
res.end(JSON.stringify({ error: 'Invalid signature' }));
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
try {
|
|
86
|
-
const payload = JSON.parse(body);
|
|
87
|
-
// Accept cwd as alias for working_directory (Claude Code stop hook sends cwd)
|
|
88
|
-
const workingDirectory = payload.working_directory ?? payload.cwd ?? '';
|
|
89
|
-
const event = buildEvent({
|
|
90
|
-
source: this.sourceId,
|
|
91
|
-
type: 'actor.stopped',
|
|
92
|
-
provenance: {
|
|
93
|
-
platform: 'claude-code',
|
|
94
|
-
platform_event: 'session.exited',
|
|
95
|
-
author: 'claude-code',
|
|
96
|
-
author_type: 'bot',
|
|
97
|
-
session_id: payload.session_id,
|
|
98
|
-
working_directory: workingDirectory,
|
|
99
|
-
},
|
|
100
|
-
payload: {
|
|
101
|
-
session_id: payload.session_id,
|
|
102
|
-
working_directory: workingDirectory,
|
|
103
|
-
duration_seconds: payload.duration_seconds,
|
|
104
|
-
exit_status: payload.exit_status,
|
|
105
|
-
summary: payload.summary ?? '',
|
|
106
|
-
transcript_path: payload.transcript_path ?? '',
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
this.persistEvent(event);
|
|
110
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
111
|
-
res.end(JSON.stringify({ ok: true, event_id: event.id }));
|
|
112
|
-
return [event];
|
|
113
|
-
}
|
|
114
|
-
catch {
|
|
115
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
116
|
-
res.end(JSON.stringify({ error: 'Invalid JSON payload' }));
|
|
117
|
-
return [];
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
async shutdown() {
|
|
122
|
-
this.pendingEvents = [];
|
|
123
|
-
}
|
|
124
|
-
persistEvent(event) {
|
|
125
|
-
if (this.bufferPath) {
|
|
126
|
-
appendFileSync(this.bufferPath, `${JSON.stringify(event)}\n`);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
this.pendingEvents.push(event);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
loadBufferedEvents() {
|
|
133
|
-
if (!this.bufferPath || !existsSync(this.bufferPath)) {
|
|
134
|
-
return [];
|
|
135
|
-
}
|
|
136
|
-
const content = readFileSync(this.bufferPath, 'utf-8').trim();
|
|
137
|
-
if (!content) {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
return content.split('\n').map((line) => JSON.parse(line));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function readBody(req) {
|
|
144
|
-
return new Promise((resolve, reject) => {
|
|
145
|
-
const chunks = [];
|
|
146
|
-
req.on('data', (chunk) => chunks.push(chunk));
|
|
147
|
-
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
148
|
-
req.on('error', reject);
|
|
149
|
-
});
|
|
150
|
-
}
|
|
8
|
+
export { CodingAgentSource as ClaudeCodeSource } from '@orgloop/connector-coding-agent';
|
|
151
9
|
//# sourceMappingURL=source.js.map
|
package/dist/source.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orgloop/connector-claude-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"description": "OrgLoop Claude Code connector — hook-based exit notifications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@orgloop/
|
|
9
|
+
"@orgloop/connector-coding-agent": "0.7.6",
|
|
10
|
+
"@orgloop/sdk": "0.7.6"
|
|
10
11
|
},
|
|
11
12
|
"orgloop": {
|
|
12
13
|
"type": "connector",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"source.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/source.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,411 +0,0 @@
|
|
|
1
|
-
import { createHmac } from 'node:crypto';
|
|
2
|
-
import { EventEmitter } from 'node:events';
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
7
|
-
import { ClaudeCodeSource } from '../source.js';
|
|
8
|
-
const TEST_SECRET = 'test-webhook-secret-key';
|
|
9
|
-
function createMockRequest(body, method = 'POST', headers = {}) {
|
|
10
|
-
const req = new EventEmitter();
|
|
11
|
-
req.method = method;
|
|
12
|
-
req.headers = { ...headers };
|
|
13
|
-
// Simulate incoming data
|
|
14
|
-
setTimeout(() => {
|
|
15
|
-
req.emit('data', Buffer.from(body));
|
|
16
|
-
req.emit('end');
|
|
17
|
-
}, 0);
|
|
18
|
-
return req;
|
|
19
|
-
}
|
|
20
|
-
function createMockResponse() {
|
|
21
|
-
const res = {
|
|
22
|
-
statusCode: 200,
|
|
23
|
-
body: '',
|
|
24
|
-
writeHead(code, _headers) {
|
|
25
|
-
res.statusCode = code;
|
|
26
|
-
return res;
|
|
27
|
-
},
|
|
28
|
-
end(data) {
|
|
29
|
-
res.body = data ?? '';
|
|
30
|
-
return res;
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
return res;
|
|
34
|
-
}
|
|
35
|
-
function signPayload(body, secret) {
|
|
36
|
-
return `sha256=${createHmac('sha256', secret).update(body).digest('hex')}`;
|
|
37
|
-
}
|
|
38
|
-
const samplePayload = {
|
|
39
|
-
session_id: 'sess-123',
|
|
40
|
-
working_directory: '/tmp/test',
|
|
41
|
-
duration_seconds: 120,
|
|
42
|
-
exit_status: 0,
|
|
43
|
-
summary: 'Task completed',
|
|
44
|
-
};
|
|
45
|
-
describe('ClaudeCodeSource', () => {
|
|
46
|
-
it('initializes without error', async () => {
|
|
47
|
-
const source = new ClaudeCodeSource();
|
|
48
|
-
await source.init({
|
|
49
|
-
id: 'claude-code',
|
|
50
|
-
connector: '@orgloop/connector-claude-code',
|
|
51
|
-
config: { hook_type: 'post-exit' },
|
|
52
|
-
});
|
|
53
|
-
expect(source.id).toBe('claude-code');
|
|
54
|
-
});
|
|
55
|
-
it('returns empty events on initial poll', async () => {
|
|
56
|
-
const source = new ClaudeCodeSource();
|
|
57
|
-
await source.init({
|
|
58
|
-
id: 'claude-code',
|
|
59
|
-
connector: '@orgloop/connector-claude-code',
|
|
60
|
-
config: {},
|
|
61
|
-
});
|
|
62
|
-
const result = await source.poll(null);
|
|
63
|
-
expect(result.events).toHaveLength(0);
|
|
64
|
-
expect(result.checkpoint).toBeDefined();
|
|
65
|
-
});
|
|
66
|
-
it('receives webhook events and returns them on poll', async () => {
|
|
67
|
-
const source = new ClaudeCodeSource();
|
|
68
|
-
await source.init({
|
|
69
|
-
id: 'claude-code',
|
|
70
|
-
connector: '@orgloop/connector-claude-code',
|
|
71
|
-
config: {},
|
|
72
|
-
});
|
|
73
|
-
const handler = source.webhook();
|
|
74
|
-
const payload = {
|
|
75
|
-
session_id: 'sess-123',
|
|
76
|
-
working_directory: '/tmp/test',
|
|
77
|
-
duration_seconds: 120,
|
|
78
|
-
exit_status: 0,
|
|
79
|
-
summary: 'Task completed',
|
|
80
|
-
};
|
|
81
|
-
const req = createMockRequest(JSON.stringify(payload));
|
|
82
|
-
const res = createMockResponse();
|
|
83
|
-
const events = await handler(req, res);
|
|
84
|
-
expect(res.statusCode).toBe(200);
|
|
85
|
-
expect(events).toHaveLength(1);
|
|
86
|
-
expect(events[0].type).toBe('actor.stopped');
|
|
87
|
-
expect(events[0].source).toBe('claude-code');
|
|
88
|
-
expect(events[0].provenance.platform).toBe('claude-code');
|
|
89
|
-
expect(events[0].payload.session_id).toBe('sess-123');
|
|
90
|
-
// Now poll should drain the events
|
|
91
|
-
const result = await source.poll(null);
|
|
92
|
-
expect(result.events).toHaveLength(1);
|
|
93
|
-
expect(result.events[0].id).toBe(events[0].id);
|
|
94
|
-
// Second poll should be empty
|
|
95
|
-
const result2 = await source.poll(result.checkpoint);
|
|
96
|
-
expect(result2.events).toHaveLength(0);
|
|
97
|
-
});
|
|
98
|
-
it('rejects non-POST requests', async () => {
|
|
99
|
-
const source = new ClaudeCodeSource();
|
|
100
|
-
await source.init({
|
|
101
|
-
id: 'claude-code',
|
|
102
|
-
connector: '@orgloop/connector-claude-code',
|
|
103
|
-
config: {},
|
|
104
|
-
});
|
|
105
|
-
const handler = source.webhook();
|
|
106
|
-
const req = createMockRequest('', 'GET');
|
|
107
|
-
const res = createMockResponse();
|
|
108
|
-
const events = await handler(req, res);
|
|
109
|
-
expect(res.statusCode).toBe(405);
|
|
110
|
-
expect(events).toHaveLength(0);
|
|
111
|
-
});
|
|
112
|
-
it('rejects invalid JSON', async () => {
|
|
113
|
-
const source = new ClaudeCodeSource();
|
|
114
|
-
await source.init({
|
|
115
|
-
id: 'claude-code',
|
|
116
|
-
connector: '@orgloop/connector-claude-code',
|
|
117
|
-
config: {},
|
|
118
|
-
});
|
|
119
|
-
const handler = source.webhook();
|
|
120
|
-
const req = createMockRequest('not-json');
|
|
121
|
-
const res = createMockResponse();
|
|
122
|
-
const events = await handler(req, res);
|
|
123
|
-
expect(res.statusCode).toBe(400);
|
|
124
|
-
expect(events).toHaveLength(0);
|
|
125
|
-
});
|
|
126
|
-
it('cleans up on shutdown', async () => {
|
|
127
|
-
const source = new ClaudeCodeSource();
|
|
128
|
-
await source.init({
|
|
129
|
-
id: 'claude-code',
|
|
130
|
-
connector: '@orgloop/connector-claude-code',
|
|
131
|
-
config: {},
|
|
132
|
-
});
|
|
133
|
-
// Add an event via webhook
|
|
134
|
-
const handler = source.webhook();
|
|
135
|
-
const req = createMockRequest(JSON.stringify({
|
|
136
|
-
session_id: 'sess-456',
|
|
137
|
-
working_directory: '/tmp',
|
|
138
|
-
duration_seconds: 60,
|
|
139
|
-
exit_status: 0,
|
|
140
|
-
}));
|
|
141
|
-
const res = createMockResponse();
|
|
142
|
-
await handler(req, res);
|
|
143
|
-
await source.shutdown();
|
|
144
|
-
// After shutdown, pending events should be cleared
|
|
145
|
-
const result = await source.poll(null);
|
|
146
|
-
expect(result.events).toHaveLength(0);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
describe('ClaudeCodeSource HMAC validation', () => {
|
|
150
|
-
it('accepts webhook with valid HMAC signature via x-hub-signature-256', async () => {
|
|
151
|
-
const source = new ClaudeCodeSource();
|
|
152
|
-
await source.init({
|
|
153
|
-
id: 'claude-code',
|
|
154
|
-
connector: '@orgloop/connector-claude-code',
|
|
155
|
-
config: { secret: TEST_SECRET },
|
|
156
|
-
});
|
|
157
|
-
const handler = source.webhook();
|
|
158
|
-
const body = JSON.stringify(samplePayload);
|
|
159
|
-
const signature = signPayload(body, TEST_SECRET);
|
|
160
|
-
const req = createMockRequest(body, 'POST', { 'x-hub-signature-256': signature });
|
|
161
|
-
const res = createMockResponse();
|
|
162
|
-
const events = await handler(req, res);
|
|
163
|
-
expect(res.statusCode).toBe(200);
|
|
164
|
-
expect(events).toHaveLength(1);
|
|
165
|
-
expect(events[0].type).toBe('actor.stopped');
|
|
166
|
-
expect(events[0].payload.session_id).toBe('sess-123');
|
|
167
|
-
});
|
|
168
|
-
it('accepts webhook with valid HMAC signature via x-signature', async () => {
|
|
169
|
-
const source = new ClaudeCodeSource();
|
|
170
|
-
await source.init({
|
|
171
|
-
id: 'claude-code',
|
|
172
|
-
connector: '@orgloop/connector-claude-code',
|
|
173
|
-
config: { secret: TEST_SECRET },
|
|
174
|
-
});
|
|
175
|
-
const handler = source.webhook();
|
|
176
|
-
const body = JSON.stringify(samplePayload);
|
|
177
|
-
const signature = signPayload(body, TEST_SECRET);
|
|
178
|
-
const req = createMockRequest(body, 'POST', { 'x-signature': signature });
|
|
179
|
-
const res = createMockResponse();
|
|
180
|
-
const events = await handler(req, res);
|
|
181
|
-
expect(res.statusCode).toBe(200);
|
|
182
|
-
expect(events).toHaveLength(1);
|
|
183
|
-
});
|
|
184
|
-
it('rejects webhook with invalid HMAC signature', async () => {
|
|
185
|
-
const source = new ClaudeCodeSource();
|
|
186
|
-
await source.init({
|
|
187
|
-
id: 'claude-code',
|
|
188
|
-
connector: '@orgloop/connector-claude-code',
|
|
189
|
-
config: { secret: TEST_SECRET },
|
|
190
|
-
});
|
|
191
|
-
const handler = source.webhook();
|
|
192
|
-
const body = JSON.stringify(samplePayload);
|
|
193
|
-
const badSignature = signPayload(body, 'wrong-secret');
|
|
194
|
-
const req = createMockRequest(body, 'POST', { 'x-hub-signature-256': badSignature });
|
|
195
|
-
const res = createMockResponse();
|
|
196
|
-
const events = await handler(req, res);
|
|
197
|
-
expect(res.statusCode).toBe(401);
|
|
198
|
-
expect(events).toHaveLength(0);
|
|
199
|
-
expect(JSON.parse(res.body).error).toBe('Invalid signature');
|
|
200
|
-
});
|
|
201
|
-
it('rejects webhook with missing signature when secret is configured', async () => {
|
|
202
|
-
const source = new ClaudeCodeSource();
|
|
203
|
-
await source.init({
|
|
204
|
-
id: 'claude-code',
|
|
205
|
-
connector: '@orgloop/connector-claude-code',
|
|
206
|
-
config: { secret: TEST_SECRET },
|
|
207
|
-
});
|
|
208
|
-
const handler = source.webhook();
|
|
209
|
-
const body = JSON.stringify(samplePayload);
|
|
210
|
-
const req = createMockRequest(body, 'POST');
|
|
211
|
-
const res = createMockResponse();
|
|
212
|
-
const events = await handler(req, res);
|
|
213
|
-
expect(res.statusCode).toBe(401);
|
|
214
|
-
expect(events).toHaveLength(0);
|
|
215
|
-
expect(JSON.parse(res.body).error).toBe('Missing signature');
|
|
216
|
-
});
|
|
217
|
-
it('accepts all requests when no secret is configured (backward compat)', async () => {
|
|
218
|
-
const source = new ClaudeCodeSource();
|
|
219
|
-
await source.init({
|
|
220
|
-
id: 'claude-code',
|
|
221
|
-
connector: '@orgloop/connector-claude-code',
|
|
222
|
-
config: {},
|
|
223
|
-
});
|
|
224
|
-
const handler = source.webhook();
|
|
225
|
-
const body = JSON.stringify(samplePayload);
|
|
226
|
-
const req = createMockRequest(body, 'POST');
|
|
227
|
-
const res = createMockResponse();
|
|
228
|
-
const events = await handler(req, res);
|
|
229
|
-
expect(res.statusCode).toBe(200);
|
|
230
|
-
expect(events).toHaveLength(1);
|
|
231
|
-
});
|
|
232
|
-
it('resolves secret from env var reference', async () => {
|
|
233
|
-
const envKey = 'TEST_CLAUDE_CODE_SECRET';
|
|
234
|
-
process.env[envKey] = TEST_SECRET;
|
|
235
|
-
try {
|
|
236
|
-
const source = new ClaudeCodeSource();
|
|
237
|
-
await source.init({
|
|
238
|
-
id: 'claude-code',
|
|
239
|
-
connector: '@orgloop/connector-claude-code',
|
|
240
|
-
config: { secret: `\${${envKey}}` },
|
|
241
|
-
});
|
|
242
|
-
const handler = source.webhook();
|
|
243
|
-
const body = JSON.stringify(samplePayload);
|
|
244
|
-
const signature = signPayload(body, TEST_SECRET);
|
|
245
|
-
const req = createMockRequest(body, 'POST', { 'x-hub-signature-256': signature });
|
|
246
|
-
const res = createMockResponse();
|
|
247
|
-
const events = await handler(req, res);
|
|
248
|
-
expect(res.statusCode).toBe(200);
|
|
249
|
-
expect(events).toHaveLength(1);
|
|
250
|
-
}
|
|
251
|
-
finally {
|
|
252
|
-
delete process.env[envKey];
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
it('rejects tampered body even with valid-format signature', async () => {
|
|
256
|
-
const source = new ClaudeCodeSource();
|
|
257
|
-
await source.init({
|
|
258
|
-
id: 'claude-code',
|
|
259
|
-
connector: '@orgloop/connector-claude-code',
|
|
260
|
-
config: { secret: TEST_SECRET },
|
|
261
|
-
});
|
|
262
|
-
const handler = source.webhook();
|
|
263
|
-
const originalBody = JSON.stringify(samplePayload);
|
|
264
|
-
const signature = signPayload(originalBody, TEST_SECRET);
|
|
265
|
-
// Tamper with the body after signing
|
|
266
|
-
const tamperedPayload = { ...samplePayload, exit_status: 1 };
|
|
267
|
-
const tamperedBody = JSON.stringify(tamperedPayload);
|
|
268
|
-
const req = createMockRequest(tamperedBody, 'POST', {
|
|
269
|
-
'x-hub-signature-256': signature,
|
|
270
|
-
});
|
|
271
|
-
const res = createMockResponse();
|
|
272
|
-
const events = await handler(req, res);
|
|
273
|
-
expect(res.statusCode).toBe(401);
|
|
274
|
-
expect(events).toHaveLength(0);
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
describe('ClaudeCodeSource buffer persistence', () => {
|
|
278
|
-
let bufferDir;
|
|
279
|
-
beforeEach(() => {
|
|
280
|
-
bufferDir = join(tmpdir(), `orgloop-test-buffer-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
281
|
-
mkdirSync(bufferDir, { recursive: true });
|
|
282
|
-
});
|
|
283
|
-
afterEach(() => {
|
|
284
|
-
if (existsSync(bufferDir)) {
|
|
285
|
-
rmSync(bufferDir, { recursive: true, force: true });
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
it('persists events to JSONL file on disk', async () => {
|
|
289
|
-
const source = new ClaudeCodeSource();
|
|
290
|
-
await source.init({
|
|
291
|
-
id: 'test-src',
|
|
292
|
-
connector: '@orgloop/connector-claude-code',
|
|
293
|
-
config: { buffer_dir: bufferDir },
|
|
294
|
-
});
|
|
295
|
-
const handler = source.webhook();
|
|
296
|
-
const body = JSON.stringify(samplePayload);
|
|
297
|
-
const req = createMockRequest(body);
|
|
298
|
-
const res = createMockResponse();
|
|
299
|
-
await handler(req, res);
|
|
300
|
-
expect(res.statusCode).toBe(200);
|
|
301
|
-
// Verify the JSONL file exists and has content
|
|
302
|
-
const bufferPath = join(bufferDir, 'claude-code-test-src.jsonl');
|
|
303
|
-
expect(existsSync(bufferPath)).toBe(true);
|
|
304
|
-
const content = readFileSync(bufferPath, 'utf-8').trim();
|
|
305
|
-
const lines = content.split('\n');
|
|
306
|
-
expect(lines).toHaveLength(1);
|
|
307
|
-
const persisted = JSON.parse(lines[0]);
|
|
308
|
-
expect(persisted.type).toBe('actor.stopped');
|
|
309
|
-
expect(persisted.payload.session_id).toBe('sess-123');
|
|
310
|
-
});
|
|
311
|
-
it('poll drains buffer and clears file', async () => {
|
|
312
|
-
const source = new ClaudeCodeSource();
|
|
313
|
-
await source.init({
|
|
314
|
-
id: 'test-src',
|
|
315
|
-
connector: '@orgloop/connector-claude-code',
|
|
316
|
-
config: { buffer_dir: bufferDir },
|
|
317
|
-
});
|
|
318
|
-
const handler = source.webhook();
|
|
319
|
-
// Send two events
|
|
320
|
-
for (const sid of ['sess-001', 'sess-002']) {
|
|
321
|
-
const body = JSON.stringify({ ...samplePayload, session_id: sid });
|
|
322
|
-
const req = createMockRequest(body);
|
|
323
|
-
const res = createMockResponse();
|
|
324
|
-
await handler(req, res);
|
|
325
|
-
}
|
|
326
|
-
const result = await source.poll(null);
|
|
327
|
-
expect(result.events).toHaveLength(2);
|
|
328
|
-
expect(result.events[0].payload.session_id).toBe('sess-001');
|
|
329
|
-
expect(result.events[1].payload.session_id).toBe('sess-002');
|
|
330
|
-
// Buffer file should be cleared
|
|
331
|
-
const bufferPath = join(bufferDir, 'claude-code-test-src.jsonl');
|
|
332
|
-
const content = readFileSync(bufferPath, 'utf-8');
|
|
333
|
-
expect(content).toBe('');
|
|
334
|
-
// Second poll should be empty
|
|
335
|
-
const result2 = await source.poll(result.checkpoint);
|
|
336
|
-
expect(result2.events).toHaveLength(0);
|
|
337
|
-
});
|
|
338
|
-
it('survives crash — new instance reads buffered events', async () => {
|
|
339
|
-
// First instance writes events
|
|
340
|
-
const source1 = new ClaudeCodeSource();
|
|
341
|
-
await source1.init({
|
|
342
|
-
id: 'test-src',
|
|
343
|
-
connector: '@orgloop/connector-claude-code',
|
|
344
|
-
config: { buffer_dir: bufferDir },
|
|
345
|
-
});
|
|
346
|
-
const handler1 = source1.webhook();
|
|
347
|
-
const body = JSON.stringify(samplePayload);
|
|
348
|
-
const req = createMockRequest(body);
|
|
349
|
-
const res = createMockResponse();
|
|
350
|
-
await handler1(req, res);
|
|
351
|
-
// Simulate crash — don't poll or shutdown, just abandon the instance
|
|
352
|
-
// Second instance picks up buffered events via poll
|
|
353
|
-
const source2 = new ClaudeCodeSource();
|
|
354
|
-
await source2.init({
|
|
355
|
-
id: 'test-src',
|
|
356
|
-
connector: '@orgloop/connector-claude-code',
|
|
357
|
-
config: { buffer_dir: bufferDir },
|
|
358
|
-
});
|
|
359
|
-
const result = await source2.poll(null);
|
|
360
|
-
expect(result.events).toHaveLength(1);
|
|
361
|
-
expect(result.events[0].type).toBe('actor.stopped');
|
|
362
|
-
expect(result.events[0].payload.session_id).toBe('sess-123');
|
|
363
|
-
});
|
|
364
|
-
it('creates buffer directory if it does not exist', async () => {
|
|
365
|
-
const nestedDir = join(bufferDir, 'nested', 'deep');
|
|
366
|
-
expect(existsSync(nestedDir)).toBe(false);
|
|
367
|
-
const source = new ClaudeCodeSource();
|
|
368
|
-
await source.init({
|
|
369
|
-
id: 'test-src',
|
|
370
|
-
connector: '@orgloop/connector-claude-code',
|
|
371
|
-
config: { buffer_dir: nestedDir },
|
|
372
|
-
});
|
|
373
|
-
expect(existsSync(nestedDir)).toBe(true);
|
|
374
|
-
});
|
|
375
|
-
it('works with HMAC and buffer persistence together', async () => {
|
|
376
|
-
const source = new ClaudeCodeSource();
|
|
377
|
-
await source.init({
|
|
378
|
-
id: 'test-src',
|
|
379
|
-
connector: '@orgloop/connector-claude-code',
|
|
380
|
-
config: { secret: TEST_SECRET, buffer_dir: bufferDir },
|
|
381
|
-
});
|
|
382
|
-
const handler = source.webhook();
|
|
383
|
-
const body = JSON.stringify(samplePayload);
|
|
384
|
-
const signature = signPayload(body, TEST_SECRET);
|
|
385
|
-
const req = createMockRequest(body, 'POST', { 'x-hub-signature-256': signature });
|
|
386
|
-
const res = createMockResponse();
|
|
387
|
-
const events = await handler(req, res);
|
|
388
|
-
expect(res.statusCode).toBe(200);
|
|
389
|
-
expect(events).toHaveLength(1);
|
|
390
|
-
// Verify persisted to disk
|
|
391
|
-
const bufferPath = join(bufferDir, 'claude-code-test-src.jsonl');
|
|
392
|
-
expect(existsSync(bufferPath)).toBe(true);
|
|
393
|
-
// Poll reads from disk
|
|
394
|
-
const result = await source.poll(null);
|
|
395
|
-
expect(result.events).toHaveLength(1);
|
|
396
|
-
expect(result.events[0].payload.session_id).toBe('sess-123');
|
|
397
|
-
});
|
|
398
|
-
it('handles empty buffer file gracefully', async () => {
|
|
399
|
-
const bufferPath = join(bufferDir, 'claude-code-test-src.jsonl');
|
|
400
|
-
writeFileSync(bufferPath, '');
|
|
401
|
-
const source = new ClaudeCodeSource();
|
|
402
|
-
await source.init({
|
|
403
|
-
id: 'test-src',
|
|
404
|
-
connector: '@orgloop/connector-claude-code',
|
|
405
|
-
config: { buffer_dir: bufferDir },
|
|
406
|
-
});
|
|
407
|
-
const result = await source.poll(null);
|
|
408
|
-
expect(result.events).toHaveLength(0);
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
//# sourceMappingURL=source.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"source.test.js","sourceRoot":"","sources":["../../src/__tests__/source.test.ts"],"names":[],"mappings":"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"}
|