claude-code-sync 0.1.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/LICENSE +21 -0
- package/README.md +105 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +227 -0
- package/dist/index.d.ts +137 -0
- package/dist/index.js +334 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wayne Sutton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# claude-code-sync
|
|
2
|
+
|
|
3
|
+
Sync your Claude Code sessions to [OpenSync](https://github.com/waynesutton/opensync) dashboard. Track coding sessions, analyze tool usage, and monitor token consumption across projects.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g claude-code-sync
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### 1. Get Your API Key
|
|
14
|
+
|
|
15
|
+
1. Log into your OpenSync dashboard
|
|
16
|
+
2. Go to **Settings**
|
|
17
|
+
3. Click **Generate API Key**
|
|
18
|
+
4. Copy the key (starts with `osk_`)
|
|
19
|
+
|
|
20
|
+
### 2. Configure the Plugin
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
claude-code-sync login
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Enter when prompted:
|
|
27
|
+
- **Convex URL**: Your deployment URL (e.g., `https://your-project.convex.cloud`)
|
|
28
|
+
- **API Key**: Your API key from Settings (e.g., `osk_abc123...`)
|
|
29
|
+
|
|
30
|
+
### 3. Add to Claude Code
|
|
31
|
+
|
|
32
|
+
Add the plugin to your Claude Code configuration. Sessions will sync automatically.
|
|
33
|
+
|
|
34
|
+
## CLI Commands
|
|
35
|
+
|
|
36
|
+
| Command | Description |
|
|
37
|
+
|---------|-------------|
|
|
38
|
+
| `claude-code-sync login` | Configure Convex URL and API Key |
|
|
39
|
+
| `claude-code-sync logout` | Clear stored credentials |
|
|
40
|
+
| `claude-code-sync status` | Show connection status |
|
|
41
|
+
| `claude-code-sync config` | Show current configuration |
|
|
42
|
+
| `claude-code-sync set <key> <value>` | Update a config value |
|
|
43
|
+
|
|
44
|
+
### Configuration Options
|
|
45
|
+
|
|
46
|
+
| Option | Type | Default | Description |
|
|
47
|
+
|--------|------|---------|-------------|
|
|
48
|
+
| `autoSync` | boolean | `true` | Automatically sync sessions |
|
|
49
|
+
| `syncToolCalls` | boolean | `true` | Include tool call details |
|
|
50
|
+
| `syncThinking` | boolean | `false` | Include thinking traces |
|
|
51
|
+
|
|
52
|
+
Set options with:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
claude-code-sync set syncThinking true
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Environment Variables
|
|
59
|
+
|
|
60
|
+
You can also configure via environment variables:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export CLAUDE_SYNC_CONVEX_URL="https://your-project.convex.cloud"
|
|
64
|
+
export CLAUDE_SYNC_API_KEY="osk_your_api_key"
|
|
65
|
+
export CLAUDE_SYNC_AUTO_SYNC="true"
|
|
66
|
+
export CLAUDE_SYNC_TOOL_CALLS="true"
|
|
67
|
+
export CLAUDE_SYNC_THINKING="false"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## What Gets Synced
|
|
71
|
+
|
|
72
|
+
| Data | Description |
|
|
73
|
+
|------|-------------|
|
|
74
|
+
| Session metadata | Project path, working directory, git branch, timestamps |
|
|
75
|
+
| User prompts | Your messages to Claude |
|
|
76
|
+
| Assistant responses | Claude's responses |
|
|
77
|
+
| Tool calls | Which tools were used and their outcomes |
|
|
78
|
+
| Token usage | Input and output token counts |
|
|
79
|
+
| Model info | Which Claude model was used |
|
|
80
|
+
| Cost estimate | Estimated session cost |
|
|
81
|
+
|
|
82
|
+
## Privacy
|
|
83
|
+
|
|
84
|
+
- All data goes to YOUR Convex deployment
|
|
85
|
+
- Sensitive fields are automatically redacted
|
|
86
|
+
- Full file contents are not synced, only paths
|
|
87
|
+
- Thinking traces are off by default
|
|
88
|
+
- You control what gets synced via configuration
|
|
89
|
+
|
|
90
|
+
## Requirements
|
|
91
|
+
|
|
92
|
+
- Node.js 18 or later
|
|
93
|
+
- Claude Code CLI
|
|
94
|
+
- A deployed OpenSync backend
|
|
95
|
+
|
|
96
|
+
## Links
|
|
97
|
+
|
|
98
|
+
- [claude-code-sync Repository](https://github.com/waynesutton/claude-code-sync)
|
|
99
|
+
- [OpenSync Backend](https://github.com/waynesutton/opensync)
|
|
100
|
+
- [OpenSync Dashboard](https://opensyncsessions.netlify.app)
|
|
101
|
+
- [npm Package](https://www.npmjs.com/package/claude-code-sync)
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Claude Code Sync CLI
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* login - Configure Convex URL and API Key
|
|
8
|
+
* logout - Clear stored credentials
|
|
9
|
+
* status - Show connection status
|
|
10
|
+
* config - Show current configuration
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
const commander_1 = require("commander");
|
|
47
|
+
const readline = __importStar(require("readline"));
|
|
48
|
+
const index_1 = require("./index");
|
|
49
|
+
const program = new commander_1.Command();
|
|
50
|
+
program
|
|
51
|
+
.name("claude-code-sync")
|
|
52
|
+
.description("Sync Claude Code sessions to OpenSync dashboard")
|
|
53
|
+
.version("0.1.0");
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Helper Functions
|
|
56
|
+
// ============================================================================
|
|
57
|
+
function prompt(question) {
|
|
58
|
+
const rl = readline.createInterface({
|
|
59
|
+
input: process.stdin,
|
|
60
|
+
output: process.stdout,
|
|
61
|
+
});
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
rl.question(question, (answer) => {
|
|
64
|
+
rl.close();
|
|
65
|
+
resolve(answer.trim());
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function maskApiKey(key) {
|
|
70
|
+
if (key.length <= 8)
|
|
71
|
+
return "****";
|
|
72
|
+
return key.substring(0, 4) + "****" + key.substring(key.length - 4);
|
|
73
|
+
}
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Commands
|
|
76
|
+
// ============================================================================
|
|
77
|
+
program
|
|
78
|
+
.command("login")
|
|
79
|
+
.description("Configure Convex URL and API Key")
|
|
80
|
+
.action(async () => {
|
|
81
|
+
console.log("\n🔐 Claude Code Sync - Login\n");
|
|
82
|
+
console.log("Get your API key from your OpenSync dashboard:");
|
|
83
|
+
console.log(" 1. Go to Settings");
|
|
84
|
+
console.log(" 2. Click 'Generate API Key'");
|
|
85
|
+
console.log(" 3. Copy the key (starts with osk_)\n");
|
|
86
|
+
const convexUrl = await prompt("Convex URL (e.g., https://your-project.convex.cloud): ");
|
|
87
|
+
if (!convexUrl) {
|
|
88
|
+
console.error("❌ Convex URL is required");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
if (!convexUrl.includes("convex.cloud") && !convexUrl.includes("convex.site")) {
|
|
92
|
+
console.error("❌ Invalid Convex URL. Must contain convex.cloud or convex.site");
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
const apiKey = await prompt("API Key (osk_...): ");
|
|
96
|
+
if (!apiKey) {
|
|
97
|
+
console.error("❌ API Key is required");
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
if (!apiKey.startsWith("osk_")) {
|
|
101
|
+
console.error("❌ Invalid API Key. Must start with osk_");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const config = {
|
|
105
|
+
convexUrl,
|
|
106
|
+
apiKey,
|
|
107
|
+
autoSync: true,
|
|
108
|
+
syncToolCalls: true,
|
|
109
|
+
syncThinking: false,
|
|
110
|
+
};
|
|
111
|
+
// Test connection
|
|
112
|
+
console.log("\n⏳ Testing connection...");
|
|
113
|
+
const client = new index_1.SyncClient(config);
|
|
114
|
+
const connected = await client.testConnection();
|
|
115
|
+
if (!connected) {
|
|
116
|
+
console.error("❌ Could not connect to Convex backend");
|
|
117
|
+
console.error(" Check your URL and try again");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
// Save config
|
|
121
|
+
(0, index_1.saveConfig)(config);
|
|
122
|
+
console.log("\n✅ Configuration saved!");
|
|
123
|
+
console.log(` URL: ${convexUrl}`);
|
|
124
|
+
console.log(` Key: ${maskApiKey(apiKey)}`);
|
|
125
|
+
console.log("\n📦 Add the plugin to your Claude Code config to start syncing.\n");
|
|
126
|
+
});
|
|
127
|
+
program
|
|
128
|
+
.command("logout")
|
|
129
|
+
.description("Clear stored credentials")
|
|
130
|
+
.action(() => {
|
|
131
|
+
(0, index_1.clearConfig)();
|
|
132
|
+
console.log("✅ Credentials cleared");
|
|
133
|
+
});
|
|
134
|
+
program
|
|
135
|
+
.command("status")
|
|
136
|
+
.description("Show connection status")
|
|
137
|
+
.action(async () => {
|
|
138
|
+
const config = (0, index_1.loadConfig)();
|
|
139
|
+
console.log("\n📊 Claude Code Sync - Status\n");
|
|
140
|
+
if (!config) {
|
|
141
|
+
console.log("❌ Not configured");
|
|
142
|
+
console.log(" Run 'claude-code-sync login' to set up\n");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
console.log("Configuration:");
|
|
146
|
+
console.log(` Convex URL: ${config.convexUrl}`);
|
|
147
|
+
console.log(` API Key: ${maskApiKey(config.apiKey)}`);
|
|
148
|
+
console.log(` Auto Sync: ${config.autoSync !== false ? "enabled" : "disabled"}`);
|
|
149
|
+
console.log(` Tool Calls: ${config.syncToolCalls !== false ? "enabled" : "disabled"}`);
|
|
150
|
+
console.log(` Thinking: ${config.syncThinking ? "enabled" : "disabled"}`);
|
|
151
|
+
console.log("\n⏳ Testing connection...");
|
|
152
|
+
const client = new index_1.SyncClient(config);
|
|
153
|
+
const connected = await client.testConnection();
|
|
154
|
+
if (connected) {
|
|
155
|
+
console.log("✅ Connected to Convex backend\n");
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.log("❌ Could not connect to Convex backend\n");
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
program
|
|
163
|
+
.command("config")
|
|
164
|
+
.description("Show current configuration")
|
|
165
|
+
.option("--json", "Output as JSON")
|
|
166
|
+
.action((options) => {
|
|
167
|
+
const config = (0, index_1.loadConfig)();
|
|
168
|
+
if (!config) {
|
|
169
|
+
if (options.json) {
|
|
170
|
+
console.log(JSON.stringify({ configured: false }));
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.log("Not configured. Run 'claude-code-sync login' to set up.");
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (options.json) {
|
|
178
|
+
console.log(JSON.stringify({
|
|
179
|
+
configured: true,
|
|
180
|
+
convexUrl: config.convexUrl,
|
|
181
|
+
apiKey: maskApiKey(config.apiKey),
|
|
182
|
+
autoSync: config.autoSync !== false,
|
|
183
|
+
syncToolCalls: config.syncToolCalls !== false,
|
|
184
|
+
syncThinking: config.syncThinking === true,
|
|
185
|
+
}, null, 2));
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
console.log("\n📋 Current Configuration\n");
|
|
189
|
+
console.log(`Convex URL: ${config.convexUrl}`);
|
|
190
|
+
console.log(`API Key: ${maskApiKey(config.apiKey)}`);
|
|
191
|
+
console.log(`Auto Sync: ${config.autoSync !== false}`);
|
|
192
|
+
console.log(`Tool Calls: ${config.syncToolCalls !== false}`);
|
|
193
|
+
console.log(`Thinking: ${config.syncThinking === true}`);
|
|
194
|
+
console.log(`\nConfig file: ~/.config/claude-code-sync/config.json\n`);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
program
|
|
198
|
+
.command("set <key> <value>")
|
|
199
|
+
.description("Set a configuration value")
|
|
200
|
+
.action((key, value) => {
|
|
201
|
+
const config = (0, index_1.loadConfig)();
|
|
202
|
+
if (!config) {
|
|
203
|
+
console.error("Not configured. Run 'claude-code-sync login' first.");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
const validKeys = ["autoSync", "syncToolCalls", "syncThinking"];
|
|
207
|
+
if (!validKeys.includes(key)) {
|
|
208
|
+
console.error(`Invalid key. Valid keys: ${validKeys.join(", ")}`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
const boolValue = value === "true" || value === "1" || value === "yes";
|
|
212
|
+
// Type-safe config update
|
|
213
|
+
if (key === "autoSync") {
|
|
214
|
+
config.autoSync = boolValue;
|
|
215
|
+
}
|
|
216
|
+
else if (key === "syncToolCalls") {
|
|
217
|
+
config.syncToolCalls = boolValue;
|
|
218
|
+
}
|
|
219
|
+
else if (key === "syncThinking") {
|
|
220
|
+
config.syncThinking = boolValue;
|
|
221
|
+
}
|
|
222
|
+
(0, index_1.saveConfig)(config);
|
|
223
|
+
console.log(`✅ Set ${key} = ${boolValue}`);
|
|
224
|
+
});
|
|
225
|
+
// Parse and run
|
|
226
|
+
program.parse();
|
|
227
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBOzs7Ozs7OztHQVFHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVILHlDQUFvQztBQUNwQyxtREFBcUM7QUFDckMsbUNBTWlCO0FBRWpCLE1BQU0sT0FBTyxHQUFHLElBQUksbUJBQU8sRUFBRSxDQUFDO0FBRTlCLE9BQU87S0FDSixJQUFJLENBQUMsa0JBQWtCLENBQUM7S0FDeEIsV0FBVyxDQUFDLGlEQUFpRCxDQUFDO0tBQzlELE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUVwQiwrRUFBK0U7QUFDL0UsbUJBQW1CO0FBQ25CLCtFQUErRTtBQUUvRSxTQUFTLE1BQU0sQ0FBQyxRQUFnQjtJQUM5QixNQUFNLEVBQUUsR0FBRyxRQUFRLENBQUMsZUFBZSxDQUFDO1FBQ2xDLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSztRQUNwQixNQUFNLEVBQUUsT0FBTyxDQUFDLE1BQU07S0FDdkIsQ0FBQyxDQUFDO0lBRUgsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1FBQzdCLEVBQUUsQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUU7WUFDL0IsRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3pCLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsR0FBVztJQUM3QixJQUFJLEdBQUcsQ0FBQyxNQUFNLElBQUksQ0FBQztRQUFFLE9BQU8sTUFBTSxDQUFDO0lBQ25DLE9BQU8sR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsTUFBTSxHQUFHLEdBQUcsQ0FBQyxTQUFT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RSxDQUFDLENBQUM7QUFDN0MsQ0FBQyxDQUFDLENBQUM7QUFFTCxnQkFBZ0I7QUFDaEIsT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiIyEvdXNyL2Jpbi9lbnYgbm9kZVxuXG4vKipcbiAqIENsYXVkZSBDb2RlIFN5bmMgQ0xJXG4gKlxuICogQ29tbWFuZHM6XG4gKiAgIGxvZ2luICAgLSBDb25maWd1cmUgQ29udmV4IFVSTCBhbmQgQVBJIEtleVxuICogICBsb2dvdXQgIC0gQ2xlYXIgc3RvcmVkIGNyZWRlbnRpYWxzXG4gKiAgIHN0YXR1cyAgLSBTaG93IGNvbm5lY3Rpb24gc3RhdHVzXG4gKiAgIGNvbmZpZyAgLSBTaG93IGN1cnJlbnQgY29uZmlndXJhdGlvblxuICovXG5cbmltcG9ydCB7IENvbW1hbmQgfSBmcm9tIFwiY29tbWFuZGVyXCI7XG5pbXBvcnQgKiBhcyByZWFkbGluZSBmcm9tIFwicmVhZGxpbmVcIjtcbmltcG9ydCB7XG4gIGxvYWRDb25maWcsXG4gIHNhdmVDb25maWcsXG4gIGNsZWFyQ29uZmlnLFxuICBTeW5jQ2xpZW50LFxuICBDb25maWcsXG59IGZyb20gXCIuL2luZGV4XCI7XG5cbmNvbnN0IHByb2dyYW0gPSBuZXcgQ29tbWFuZCgpO1xuXG5wcm9ncmFtXG4gIC5uYW1lKFwiY2xhdWRlLWNvZGUtc3luY1wiKVxuICAuZGVzY3JpcHRpb24oXCJTeW5jIENsYXVkZSBDb2RlIHNlc3Npb25zIHRvIE9wZW5TeW5jIGRhc2hib2FyZFwiKVxuICAudmVyc2lvbihcIjAuMS4wXCIpO1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBIZWxwZXIgRnVuY3Rpb25zXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmZ1bmN0aW9uIHByb21wdChxdWVzdGlvbjogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgcmwgPSByZWFkbGluZS5jcmVhdGVJbnRlcmZhY2Uoe1xuICAgIGlucHV0OiBwcm9jZXNzLnN0ZGluLFxuICAgIG91dHB1dDogcHJvY2Vzcy5zdGRvdXQsXG4gIH0pO1xuXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgIHJsLnF1ZXN0aW9uKHF1ZXN0aW9uLCAoYW5zd2VyKSA9PiB7XG4gICAgICBybC5jbG9zZSgpO1xuICAgICAgcmVzb2x2ZShhbnN3ZXIudHJpbSgpKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbmZ1bmN0aW9uIG1hc2tBcGlLZXkoa2V5OiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoa2V5Lmxlbmd0aCA8PSA4KSByZXR1cm4gXCIqKioqXCI7XG4gIHJldHVybiBrZXkuc3Vic3RyaW5nKDAsIDQpICsgXCIqKioqXCIgKyBrZXkuc3Vic3RyaW5nKGtleS5sZW5ndGggLSA0KTtcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQ29tbWFuZHNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxucHJvZ3JhbVxuICAuY29tbWFuZChcImxvZ2luXCIpXG4gIC5kZXNjcmlwdGlvbihcIkNvbmZpZ3VyZSBDb252ZXggVVJMIGFuZCBBUEkgS2V5XCIpXG4gIC5hY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgIGNvbnNvbGUubG9nKFwiXFxu8J+UkCBDbGF1ZGUgQ29kZSBTeW5jIC0gTG9naW5cXG5cIik7XG4gICAgY29uc29sZS5sb2coXCJHZXQgeW91ciBBUEkga2V5IGZyb20geW91ciBPcGVuU3luYyBkYXNoYm9hcmQ6XCIpO1xuICAgIGNvbnNvbGUubG9nKFwiICAxLiBHbyB0byBTZXR0aW5nc1wiKTtcbiAgICBjb25zb2xlLmxvZyhcIiAgMi4gQ2xpY2sgJ0dlbmVyYXRlIEFQSSBLZXknXCIpO1xuICAgIGNvbnNvbGUubG9nKFwiICAzLiBDb3B5IHRoZSBrZXkgKHN0YXJ0cyB3aXRoIG9za18pXFxuXCIpO1xuXG4gICAgY29uc3QgY29udmV4VXJsID0gYXdhaXQgcHJvbXB0KFwiQ29udmV4IFVSTCAoZS5nLiwgaHR0cHM6Ly95b3VyLXByb2plY3QuY29udmV4LmNsb3VkKTogXCIpO1xuXG4gICAgaWYgKCFjb252ZXhVcmwpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoXCLinYwgQ29udmV4IFVSTCBpcyByZXF1aXJlZFwiKTtcbiAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICB9XG5cbiAgICBpZiAoIWNvbnZleFVybC5pbmNsdWRlcyhcImNvbnZleC5jbG91ZFwiKSAmJiAhY29udmV4VXJsLmluY2x1ZGVzKFwiY29udmV4LnNpdGVcIikpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoXCLinYwgSW52YWxpZCBDb252ZXggVVJMLiBNdXN0IGNvbnRhaW4gY29udmV4LmNsb3VkIG9yIGNvbnZleC5zaXRlXCIpO1xuICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgIH1cblxuICAgIGNvbnN0IGFwaUtleSA9IGF3YWl0IHByb21wdChcIkFQSSBLZXkgKG9za18uLi4pOiBcIik7XG5cbiAgICBpZiAoIWFwaUtleSkge1xuICAgICAgY29uc29sZS5lcnJvcihcIuKdjCBBUEkgS2V5IGlzIHJlcXVpcmVkXCIpO1xuICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgIH1cblxuICAgIGlmICghYXBpS2V5LnN0YXJ0c1dpdGgoXCJvc2tfXCIpKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwi4p2MIEludmFsaWQgQVBJIEtleS4gTXVzdCBzdGFydCB3aXRoIG9za19cIik7XG4gICAgICBwcm9jZXNzLmV4aXQoMSk7XG4gICAgfVxuXG4gICAgY29uc3QgY29uZmlnOiBDb25maWcgPSB7XG4gICAgICBjb252ZXhVcmwsXG4gICAgICBhcGlLZXksXG4gICAgICBhdXRvU3luYzogdHJ1ZSxcbiAgICAgIHN5bmNUb29sQ2FsbHM6IHRydWUsXG4gICAgICBzeW5jVGhpbmtpbmc6IGZhbHNlLFxuICAgIH07XG5cbiAgICAvLyBUZXN0IGNvbm5lY3Rpb25cbiAgICBjb25zb2xlLmxvZyhcIlxcbuKPsyBUZXN0aW5nIGNvbm5lY3Rpb24uLi5cIik7XG4gICAgY29uc3QgY2xpZW50ID0gbmV3IFN5bmNDbGllbnQoY29uZmlnKTtcbiAgICBjb25zdCBjb25uZWN0ZWQgPSBhd2FpdCBjbGllbnQudGVzdENvbm5lY3Rpb24oKTtcblxuICAgIGlmICghY29ubmVjdGVkKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwi4p2MIENvdWxkIG5vdCBjb25uZWN0IHRvIENvbnZleCBiYWNrZW5kXCIpO1xuICAgICAgY29uc29sZS5lcnJvcihcIiAgIENoZWNrIHlvdXIgVVJMIGFuZCB0cnkgYWdhaW5cIik7XG4gICAgICBwcm9jZXNzLmV4aXQoMSk7XG4gICAgfVxuXG4gICAgLy8gU2F2ZSBjb25maWdcbiAgICBzYXZlQ29uZmlnKGNvbmZpZyk7XG4gICAgY29uc29sZS5sb2coXCJcXG7inIUgQ29uZmlndXJhdGlvbiBzYXZlZCFcIik7XG4gICAgY29uc29sZS5sb2coYCAgIFVSTDogJHtjb252ZXhVcmx9YCk7XG4gICAgY29uc29sZS5sb2coYCAgIEtleTogJHttYXNrQXBpS2V5KGFwaUtleSl9YCk7XG4gICAgY29uc29sZS5sb2coXCJcXG7wn5OmIEFkZCB0aGUgcGx1Z2luIHRvIHlvdXIgQ2xhdWRlIENvZGUgY29uZmlnIHRvIHN0YXJ0IHN5bmNpbmcuXFxuXCIpO1xuICB9KTtcblxucHJvZ3JhbVxuICAuY29tbWFuZChcImxvZ291dFwiKVxuICAuZGVzY3JpcHRpb24oXCJDbGVhciBzdG9yZWQgY3JlZGVudGlhbHNcIilcbiAgLmFjdGlvbigoKSA9PiB7XG4gICAgY2xlYXJDb25maWcoKTtcbiAgICBjb25zb2xlLmxvZyhcIuKchSBDcmVkZW50aWFscyBjbGVhcmVkXCIpO1xuICB9KTtcblxucHJvZ3JhbVxuICAuY29tbWFuZChcInN0YXR1c1wiKVxuICAuZGVzY3JpcHRpb24oXCJTaG93IGNvbm5lY3Rpb24gc3RhdHVzXCIpXG4gIC5hY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGNvbmZpZyA9IGxvYWRDb25maWcoKTtcblxuICAgIGNvbnNvbGUubG9nKFwiXFxu8J+TiiBDbGF1ZGUgQ29kZSBTeW5jIC0gU3RhdHVzXFxuXCIpO1xuXG4gICAgaWYgKCFjb25maWcpIHtcbiAgICAgIGNvbnNvbGUubG9nKFwi4p2MIE5vdCBjb25maWd1cmVkXCIpO1xuICAgICAgY29uc29sZS5sb2coXCIgICBSdW4gJ2NsYXVkZS1jb2RlLXN5bmMgbG9naW4nIHRvIHNldCB1cFxcblwiKTtcbiAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICB9XG5cbiAgICBjb25zb2xlLmxvZyhcIkNvbmZpZ3VyYXRpb246XCIpO1xuICAgIGNvbnNvbGUubG9nKGAgIENvbnZleCBVUkw6ICR7Y29uZmlnLmNvbnZleFVybH1gKTtcbiAgICBjb25zb2xlLmxvZyhgICBBUEkgS2V5OiAgICAke21hc2tBcGlLZXkoY29uZmlnLmFwaUtleSl9YCk7XG4gICAgY29uc29sZS5sb2coYCAgQXV0byBTeW5jOiAgJHtjb25maWcuYXV0b1N5bmMgIT09IGZhbHNlID8gXCJlbmFibGVkXCIgOiBcImRpc2FibGVkXCJ9YCk7XG4gICAgY29uc29sZS5sb2coYCAgVG9vbCBDYWxsczogJHtjb25maWcuc3luY1Rvb2xDYWxscyAhPT0gZmFsc2UgPyBcImVuYWJsZWRcIiA6IFwiZGlzYWJsZWRcIn1gKTtcbiAgICBjb25zb2xlLmxvZyhgICBUaGlua2luZzogICAke2NvbmZpZy5zeW5jVGhpbmtpbmcgPyBcImVuYWJsZWRcIiA6IFwiZGlzYWJsZWRcIn1gKTtcblxuICAgIGNvbnNvbGUubG9nKFwiXFxu4o+zIFRlc3RpbmcgY29ubmVjdGlvbi4uLlwiKTtcbiAgICBjb25zdCBjbGllbnQgPSBuZXcgU3luY0NsaWVudChjb25maWcpO1xuICAgIGNvbnN0IGNvbm5lY3RlZCA9IGF3YWl0IGNsaWVudC50ZXN0Q29ubmVjdGlvbigpO1xuXG4gICAgaWYgKGNvbm5lY3RlZCkge1xuICAgICAgY29uc29sZS5sb2coXCLinIUgQ29ubmVjdGVkIHRvIENvbnZleCBiYWNrZW5kXFxuXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zb2xlLmxvZyhcIuKdjCBDb3VsZCBub3QgY29ubmVjdCB0byBDb252ZXggYmFja2VuZFxcblwiKTtcbiAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICB9XG4gIH0pO1xuXG5wcm9ncmFtXG4gIC5jb21tYW5kKFwiY29uZmlnXCIpXG4gIC5kZXNjcmlwdGlvbihcIlNob3cgY3VycmVudCBjb25maWd1cmF0aW9uXCIpXG4gIC5vcHRpb24oXCItLWpzb25cIiwgXCJPdXRwdXQgYXMgSlNPTlwiKVxuICAuYWN0aW9uKChvcHRpb25zOiB7IGpzb24/OiBib29sZWFuIH0pID0+IHtcbiAgICBjb25zdCBjb25maWcgPSBsb2FkQ29uZmlnKCk7XG5cbiAgICBpZiAoIWNvbmZpZykge1xuICAgICAgaWYgKG9wdGlvbnMuanNvbikge1xuICAgICAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7IGNvbmZpZ3VyZWQ6IGZhbHNlIH0pKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiTm90IGNvbmZpZ3VyZWQuIFJ1biAnY2xhdWRlLWNvZGUtc3luYyBsb2dpbicgdG8gc2V0IHVwLlwiKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAob3B0aW9ucy5qc29uKSB7XG4gICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgSlNPTi5zdHJpbmdpZnkoXG4gICAgICAgICAge1xuICAgICAgICAgICAgY29uZmlndXJlZDogdHJ1ZSxcbiAgICAgICAgICAgIGNvbnZleFVybDogY29uZmlnLmNvbnZleFVybCxcbiAgICAgICAgICAgIGFwaUtleTogbWFza0FwaUtleShjb25maWcuYXBpS2V5KSxcbiAgICAgICAgICAgIGF1dG9TeW5jOiBjb25maWcuYXV0b1N5bmMgIT09IGZhbHNlLFxuICAgICAgICAgICAgc3luY1Rvb2xDYWxsczogY29uZmlnLnN5bmNUb29sQ2FsbHMgIT09IGZhbHNlLFxuICAgICAgICAgICAgc3luY1RoaW5raW5nOiBjb25maWcuc3luY1RoaW5raW5nID09PSB0cnVlLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgbnVsbCxcbiAgICAgICAgICAyXG4gICAgICAgIClcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnNvbGUubG9nKFwiXFxu8J+TiyBDdXJyZW50IENvbmZpZ3VyYXRpb25cXG5cIik7XG4gICAgICBjb25zb2xlLmxvZyhgQ29udmV4IFVSTDogICR7Y29uZmlnLmNvbnZleFVybH1gKTtcbiAgICAgIGNvbnNvbGUubG9nKGBBUEkgS2V5OiAgICAgJHttYXNrQXBpS2V5KGNvbmZpZy5hcGlLZXkpfWApO1xuICAgICAgY29uc29sZS5sb2coYEF1dG8gU3luYzogICAke2NvbmZpZy5hdXRvU3luYyAhPT0gZmFsc2V9YCk7XG4gICAgICBjb25zb2xlLmxvZyhgVG9vbCBDYWxsczogICR7Y29uZmlnLnN5bmNUb29sQ2FsbHMgIT09IGZhbHNlfWApO1xuICAgICAgY29uc29sZS5sb2coYFRoaW5raW5nOiAgICAke2NvbmZpZy5zeW5jVGhpbmtpbmcgPT09IHRydWV9YCk7XG4gICAgICBjb25zb2xlLmxvZyhgXFxuQ29uZmlnIGZpbGU6IH4vLmNvbmZpZy9jbGF1ZGUtY29kZS1zeW5jL2NvbmZpZy5qc29uXFxuYCk7XG4gICAgfVxuICB9KTtcblxucHJvZ3JhbVxuICAuY29tbWFuZChcInNldCA8a2V5PiA8dmFsdWU+XCIpXG4gIC5kZXNjcmlwdGlvbihcIlNldCBhIGNvbmZpZ3VyYXRpb24gdmFsdWVcIilcbiAgLmFjdGlvbigoa2V5OiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICBjb25zdCBjb25maWcgPSBsb2FkQ29uZmlnKCk7XG5cbiAgICBpZiAoIWNvbmZpZykge1xuICAgICAgY29uc29sZS5lcnJvcihcIk5vdCBjb25maWd1cmVkLiBSdW4gJ2NsYXVkZS1jb2RlLXN5bmMgbG9naW4nIGZpcnN0LlwiKTtcbiAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICB9XG5cbiAgICBjb25zdCB2YWxpZEtleXMgPSBbXCJhdXRvU3luY1wiLCBcInN5bmNUb29sQ2FsbHNcIiwgXCJzeW5jVGhpbmtpbmdcIl07XG4gICAgaWYgKCF2YWxpZEtleXMuaW5jbHVkZXMoa2V5KSkge1xuICAgICAgY29uc29sZS5lcnJvcihgSW52YWxpZCBrZXkuIFZhbGlkIGtleXM6ICR7dmFsaWRLZXlzLmpvaW4oXCIsIFwiKX1gKTtcbiAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICB9XG5cbiAgICBjb25zdCBib29sVmFsdWUgPSB2YWx1ZSA9PT0gXCJ0cnVlXCIgfHwgdmFsdWUgPT09IFwiMVwiIHx8IHZhbHVlID09PSBcInllc1wiO1xuICAgIFxuICAgIC8vIFR5cGUtc2FmZSBjb25maWcgdXBkYXRlXG4gICAgaWYgKGtleSA9PT0gXCJhdXRvU3luY1wiKSB7XG4gICAgICBjb25maWcuYXV0b1N5bmMgPSBib29sVmFsdWU7XG4gICAgfSBlbHNlIGlmIChrZXkgPT09IFwic3luY1Rvb2xDYWxsc1wiKSB7XG4gICAgICBjb25maWcuc3luY1Rvb2xDYWxscyA9IGJvb2xWYWx1ZTtcbiAgICB9IGVsc2UgaWYgKGtleSA9PT0gXCJzeW5jVGhpbmtpbmdcIikge1xuICAgICAgY29uZmlnLnN5bmNUaGlua2luZyA9IGJvb2xWYWx1ZTtcbiAgICB9XG5cbiAgICBzYXZlQ29uZmlnKGNvbmZpZyk7XG4gICAgY29uc29sZS5sb2coYOKchSBTZXQgJHtrZXl9ID0gJHtib29sVmFsdWV9YCk7XG4gIH0pO1xuXG4vLyBQYXJzZSBhbmQgcnVuXG5wcm9ncmFtLnBhcnNlKCk7XG4iXX0=
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Sync Plugin
|
|
3
|
+
*
|
|
4
|
+
* Syncs Claude Code sessions to OpenSync dashboard.
|
|
5
|
+
* Uses API Key authentication (no browser OAuth required).
|
|
6
|
+
*
|
|
7
|
+
* Install: npm install -g claude-code-sync
|
|
8
|
+
* Configure: claude-code-sync login
|
|
9
|
+
*/
|
|
10
|
+
export interface Config {
|
|
11
|
+
convexUrl: string;
|
|
12
|
+
apiKey: string;
|
|
13
|
+
autoSync?: boolean;
|
|
14
|
+
syncToolCalls?: boolean;
|
|
15
|
+
syncThinking?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface SessionData {
|
|
18
|
+
sessionId: string;
|
|
19
|
+
source: "claude-code";
|
|
20
|
+
title?: string;
|
|
21
|
+
projectPath?: string;
|
|
22
|
+
projectName?: string;
|
|
23
|
+
cwd?: string;
|
|
24
|
+
gitBranch?: string;
|
|
25
|
+
gitRepo?: string;
|
|
26
|
+
model?: string;
|
|
27
|
+
startType?: "new" | "resume" | "continue";
|
|
28
|
+
endReason?: "user_stop" | "max_turns" | "error" | "completed";
|
|
29
|
+
thinkingEnabled?: boolean;
|
|
30
|
+
permissionMode?: string;
|
|
31
|
+
mcpServers?: string[];
|
|
32
|
+
messageCount?: number;
|
|
33
|
+
toolCallCount?: number;
|
|
34
|
+
tokenUsage?: {
|
|
35
|
+
input: number;
|
|
36
|
+
output: number;
|
|
37
|
+
};
|
|
38
|
+
costEstimate?: number;
|
|
39
|
+
startedAt?: string;
|
|
40
|
+
endedAt?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface MessageData {
|
|
43
|
+
sessionId: string;
|
|
44
|
+
messageId: string;
|
|
45
|
+
source: "claude-code";
|
|
46
|
+
role: "user" | "assistant" | "system";
|
|
47
|
+
content?: string;
|
|
48
|
+
thinkingContent?: string;
|
|
49
|
+
toolName?: string;
|
|
50
|
+
toolArgs?: Record<string, unknown>;
|
|
51
|
+
toolResult?: string;
|
|
52
|
+
durationMs?: number;
|
|
53
|
+
tokenCount?: number;
|
|
54
|
+
timestamp?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface ToolUseData {
|
|
57
|
+
sessionId: string;
|
|
58
|
+
toolName: string;
|
|
59
|
+
toolArgs?: Record<string, unknown>;
|
|
60
|
+
result?: string;
|
|
61
|
+
success?: boolean;
|
|
62
|
+
durationMs?: number;
|
|
63
|
+
timestamp?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface ClaudeCodeHooks {
|
|
66
|
+
SessionStart?: (data: SessionStartEvent) => void | Promise<void>;
|
|
67
|
+
UserPromptSubmit?: (data: UserPromptEvent) => void | Promise<void>;
|
|
68
|
+
PostToolUse?: (data: ToolUseEvent) => void | Promise<void>;
|
|
69
|
+
Stop?: (data: StopEvent) => void | Promise<void>;
|
|
70
|
+
SessionEnd?: (data: SessionEndEvent) => void | Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
export interface SessionStartEvent {
|
|
73
|
+
sessionId: string;
|
|
74
|
+
cwd: string;
|
|
75
|
+
model: string;
|
|
76
|
+
startType: "new" | "resume" | "continue";
|
|
77
|
+
thinkingEnabled?: boolean;
|
|
78
|
+
permissionMode?: string;
|
|
79
|
+
mcpServers?: string[];
|
|
80
|
+
}
|
|
81
|
+
export interface UserPromptEvent {
|
|
82
|
+
sessionId: string;
|
|
83
|
+
prompt: string;
|
|
84
|
+
timestamp: string;
|
|
85
|
+
}
|
|
86
|
+
export interface ToolUseEvent {
|
|
87
|
+
sessionId: string;
|
|
88
|
+
toolName: string;
|
|
89
|
+
args: Record<string, unknown>;
|
|
90
|
+
result: string;
|
|
91
|
+
success: boolean;
|
|
92
|
+
durationMs: number;
|
|
93
|
+
}
|
|
94
|
+
export interface StopEvent {
|
|
95
|
+
sessionId: string;
|
|
96
|
+
response: string;
|
|
97
|
+
tokenUsage: {
|
|
98
|
+
input: number;
|
|
99
|
+
output: number;
|
|
100
|
+
};
|
|
101
|
+
durationMs: number;
|
|
102
|
+
}
|
|
103
|
+
export interface SessionEndEvent {
|
|
104
|
+
sessionId: string;
|
|
105
|
+
endReason: "user_stop" | "max_turns" | "error" | "completed";
|
|
106
|
+
messageCount: number;
|
|
107
|
+
toolCallCount: number;
|
|
108
|
+
totalTokenUsage: {
|
|
109
|
+
input: number;
|
|
110
|
+
output: number;
|
|
111
|
+
};
|
|
112
|
+
costEstimate: number;
|
|
113
|
+
}
|
|
114
|
+
export declare function loadConfig(): Config | null;
|
|
115
|
+
export declare function saveConfig(config: Config): void;
|
|
116
|
+
export declare function clearConfig(): void;
|
|
117
|
+
export declare class SyncClient {
|
|
118
|
+
private config;
|
|
119
|
+
private sessionCache;
|
|
120
|
+
constructor(config: Config);
|
|
121
|
+
private request;
|
|
122
|
+
syncSession(session: SessionData): Promise<void>;
|
|
123
|
+
syncMessage(message: MessageData): Promise<void>;
|
|
124
|
+
syncBatch(sessions: SessionData[], messages: MessageData[]): Promise<void>;
|
|
125
|
+
testConnection(): Promise<boolean>;
|
|
126
|
+
getSessionState(sessionId: string): Partial<SessionData>;
|
|
127
|
+
updateSessionState(sessionId: string, updates: Partial<SessionData>): void;
|
|
128
|
+
clearSessionState(sessionId: string): void;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Claude Code Plugin Entry Point
|
|
132
|
+
*
|
|
133
|
+
* This function is called by Claude Code to register the plugin.
|
|
134
|
+
* It returns hook handlers that fire at key points in the session lifecycle.
|
|
135
|
+
*/
|
|
136
|
+
export declare function createPlugin(): ClaudeCodeHooks | null;
|
|
137
|
+
export default createPlugin;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code Sync Plugin
|
|
4
|
+
*
|
|
5
|
+
* Syncs Claude Code sessions to OpenSync dashboard.
|
|
6
|
+
* Uses API Key authentication (no browser OAuth required).
|
|
7
|
+
*
|
|
8
|
+
* Install: npm install -g claude-code-sync
|
|
9
|
+
* Configure: claude-code-sync login
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.SyncClient = void 0;
|
|
46
|
+
exports.loadConfig = loadConfig;
|
|
47
|
+
exports.saveConfig = saveConfig;
|
|
48
|
+
exports.clearConfig = clearConfig;
|
|
49
|
+
exports.createPlugin = createPlugin;
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const os = __importStar(require("os"));
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Configuration
|
|
55
|
+
// ============================================================================
|
|
56
|
+
const CONFIG_DIR = path.join(os.homedir(), ".config", "claude-code-sync");
|
|
57
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
58
|
+
function loadConfig() {
|
|
59
|
+
// Check environment variables first
|
|
60
|
+
const envUrl = process.env.CLAUDE_SYNC_CONVEX_URL;
|
|
61
|
+
const envKey = process.env.CLAUDE_SYNC_API_KEY;
|
|
62
|
+
if (envUrl && envKey) {
|
|
63
|
+
return {
|
|
64
|
+
convexUrl: normalizeConvexUrl(envUrl),
|
|
65
|
+
apiKey: envKey,
|
|
66
|
+
autoSync: process.env.CLAUDE_SYNC_AUTO_SYNC !== "false",
|
|
67
|
+
syncToolCalls: process.env.CLAUDE_SYNC_TOOL_CALLS !== "false",
|
|
68
|
+
syncThinking: process.env.CLAUDE_SYNC_THINKING === "true",
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Fall back to config file
|
|
72
|
+
try {
|
|
73
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
74
|
+
const data = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
75
|
+
const config = JSON.parse(data);
|
|
76
|
+
config.convexUrl = normalizeConvexUrl(config.convexUrl);
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error("Error loading config:", error);
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
function saveConfig(config) {
|
|
86
|
+
try {
|
|
87
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
88
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error("Error saving config:", error);
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function clearConfig() {
|
|
98
|
+
try {
|
|
99
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
100
|
+
fs.unlinkSync(CONFIG_FILE);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error("Error clearing config:", error);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function normalizeConvexUrl(url) {
|
|
108
|
+
// Convert .convex.cloud to .convex.site for API calls
|
|
109
|
+
return url.replace(".convex.cloud", ".convex.site");
|
|
110
|
+
}
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// Sync Client
|
|
113
|
+
// ============================================================================
|
|
114
|
+
class SyncClient {
|
|
115
|
+
config;
|
|
116
|
+
sessionCache = new Map();
|
|
117
|
+
constructor(config) {
|
|
118
|
+
this.config = config;
|
|
119
|
+
}
|
|
120
|
+
async request(endpoint, data) {
|
|
121
|
+
const url = `${this.config.convexUrl}${endpoint}`;
|
|
122
|
+
const response = await fetch(url, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: {
|
|
125
|
+
"Content-Type": "application/json",
|
|
126
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify(data),
|
|
129
|
+
});
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
const text = await response.text();
|
|
132
|
+
throw new Error(`Sync failed: ${response.status} - ${text}`);
|
|
133
|
+
}
|
|
134
|
+
return response.json();
|
|
135
|
+
}
|
|
136
|
+
async syncSession(session) {
|
|
137
|
+
try {
|
|
138
|
+
await this.request("/sync/session", session);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
console.error("Failed to sync session:", error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async syncMessage(message) {
|
|
145
|
+
try {
|
|
146
|
+
await this.request("/sync/message", message);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error("Failed to sync message:", error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async syncBatch(sessions, messages) {
|
|
153
|
+
try {
|
|
154
|
+
await this.request("/sync/batch", { sessions, messages });
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error("Failed to sync batch:", error);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async testConnection() {
|
|
161
|
+
try {
|
|
162
|
+
const url = `${this.config.convexUrl}/health`;
|
|
163
|
+
const response = await fetch(url);
|
|
164
|
+
return response.ok;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Session state management
|
|
171
|
+
getSessionState(sessionId) {
|
|
172
|
+
return this.sessionCache.get(sessionId) || {};
|
|
173
|
+
}
|
|
174
|
+
updateSessionState(sessionId, updates) {
|
|
175
|
+
const current = this.sessionCache.get(sessionId) || {};
|
|
176
|
+
this.sessionCache.set(sessionId, { ...current, ...updates });
|
|
177
|
+
}
|
|
178
|
+
clearSessionState(sessionId) {
|
|
179
|
+
this.sessionCache.delete(sessionId);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
exports.SyncClient = SyncClient;
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// Plugin Export
|
|
185
|
+
// ============================================================================
|
|
186
|
+
/**
|
|
187
|
+
* Claude Code Plugin Entry Point
|
|
188
|
+
*
|
|
189
|
+
* This function is called by Claude Code to register the plugin.
|
|
190
|
+
* It returns hook handlers that fire at key points in the session lifecycle.
|
|
191
|
+
*/
|
|
192
|
+
function createPlugin() {
|
|
193
|
+
const config = loadConfig();
|
|
194
|
+
if (!config) {
|
|
195
|
+
console.log("[claude-code-sync] Not configured. Run 'claude-code-sync login' to set up.");
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
if (config.autoSync === false) {
|
|
199
|
+
console.log("[claude-code-sync] Auto-sync disabled in config.");
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const client = new SyncClient(config);
|
|
203
|
+
let messageCounter = 0;
|
|
204
|
+
let toolCallCounter = 0;
|
|
205
|
+
console.log("[claude-code-sync] Plugin loaded. Sessions will sync to OpenSync.");
|
|
206
|
+
return {
|
|
207
|
+
/**
|
|
208
|
+
* Called when a new session starts
|
|
209
|
+
*/
|
|
210
|
+
SessionStart: async (event) => {
|
|
211
|
+
messageCounter = 0;
|
|
212
|
+
toolCallCounter = 0;
|
|
213
|
+
const session = {
|
|
214
|
+
sessionId: event.sessionId,
|
|
215
|
+
source: "claude-code",
|
|
216
|
+
cwd: event.cwd,
|
|
217
|
+
model: event.model,
|
|
218
|
+
startType: event.startType,
|
|
219
|
+
thinkingEnabled: event.thinkingEnabled,
|
|
220
|
+
permissionMode: event.permissionMode,
|
|
221
|
+
mcpServers: event.mcpServers,
|
|
222
|
+
startedAt: new Date().toISOString(),
|
|
223
|
+
};
|
|
224
|
+
// Extract project info from cwd
|
|
225
|
+
if (event.cwd) {
|
|
226
|
+
session.projectPath = event.cwd;
|
|
227
|
+
session.projectName = path.basename(event.cwd);
|
|
228
|
+
// Try to get git info
|
|
229
|
+
try {
|
|
230
|
+
const gitDir = path.join(event.cwd, ".git");
|
|
231
|
+
if (fs.existsSync(gitDir)) {
|
|
232
|
+
const headFile = path.join(gitDir, "HEAD");
|
|
233
|
+
if (fs.existsSync(headFile)) {
|
|
234
|
+
const head = fs.readFileSync(headFile, "utf-8").trim();
|
|
235
|
+
if (head.startsWith("ref: refs/heads/")) {
|
|
236
|
+
session.gitBranch = head.replace("ref: refs/heads/", "");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// Ignore git errors
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
client.updateSessionState(event.sessionId, session);
|
|
246
|
+
await client.syncSession(session);
|
|
247
|
+
},
|
|
248
|
+
/**
|
|
249
|
+
* Called when user submits a prompt
|
|
250
|
+
*/
|
|
251
|
+
UserPromptSubmit: async (event) => {
|
|
252
|
+
messageCounter++;
|
|
253
|
+
const message = {
|
|
254
|
+
sessionId: event.sessionId,
|
|
255
|
+
messageId: `${event.sessionId}-msg-${messageCounter}`,
|
|
256
|
+
source: "claude-code",
|
|
257
|
+
role: "user",
|
|
258
|
+
content: event.prompt,
|
|
259
|
+
timestamp: event.timestamp || new Date().toISOString(),
|
|
260
|
+
};
|
|
261
|
+
await client.syncMessage(message);
|
|
262
|
+
},
|
|
263
|
+
/**
|
|
264
|
+
* Called after each tool use
|
|
265
|
+
*/
|
|
266
|
+
PostToolUse: async (event) => {
|
|
267
|
+
if (!config.syncToolCalls)
|
|
268
|
+
return;
|
|
269
|
+
toolCallCounter++;
|
|
270
|
+
messageCounter++;
|
|
271
|
+
const message = {
|
|
272
|
+
sessionId: event.sessionId,
|
|
273
|
+
messageId: `${event.sessionId}-tool-${toolCallCounter}`,
|
|
274
|
+
source: "claude-code",
|
|
275
|
+
role: "assistant",
|
|
276
|
+
toolName: event.toolName,
|
|
277
|
+
toolArgs: event.args,
|
|
278
|
+
toolResult: event.result,
|
|
279
|
+
durationMs: event.durationMs,
|
|
280
|
+
timestamp: new Date().toISOString(),
|
|
281
|
+
};
|
|
282
|
+
await client.syncMessage(message);
|
|
283
|
+
},
|
|
284
|
+
/**
|
|
285
|
+
* Called when Claude stops responding
|
|
286
|
+
*/
|
|
287
|
+
Stop: async (event) => {
|
|
288
|
+
messageCounter++;
|
|
289
|
+
const message = {
|
|
290
|
+
sessionId: event.sessionId,
|
|
291
|
+
messageId: `${event.sessionId}-msg-${messageCounter}`,
|
|
292
|
+
source: "claude-code",
|
|
293
|
+
role: "assistant",
|
|
294
|
+
content: event.response,
|
|
295
|
+
tokenCount: event.tokenUsage.input + event.tokenUsage.output,
|
|
296
|
+
durationMs: event.durationMs,
|
|
297
|
+
timestamp: new Date().toISOString(),
|
|
298
|
+
};
|
|
299
|
+
// Update session state with token usage
|
|
300
|
+
const currentState = client.getSessionState(event.sessionId);
|
|
301
|
+
const currentTokens = currentState.tokenUsage || { input: 0, output: 0 };
|
|
302
|
+
client.updateSessionState(event.sessionId, {
|
|
303
|
+
tokenUsage: {
|
|
304
|
+
input: currentTokens.input + event.tokenUsage.input,
|
|
305
|
+
output: currentTokens.output + event.tokenUsage.output,
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
await client.syncMessage(message);
|
|
309
|
+
},
|
|
310
|
+
/**
|
|
311
|
+
* Called when session ends
|
|
312
|
+
*/
|
|
313
|
+
SessionEnd: async (event) => {
|
|
314
|
+
const currentState = client.getSessionState(event.sessionId);
|
|
315
|
+
const session = {
|
|
316
|
+
...currentState,
|
|
317
|
+
sessionId: event.sessionId,
|
|
318
|
+
source: "claude-code",
|
|
319
|
+
endReason: event.endReason,
|
|
320
|
+
messageCount: event.messageCount,
|
|
321
|
+
toolCallCount: event.toolCallCount,
|
|
322
|
+
tokenUsage: event.totalTokenUsage,
|
|
323
|
+
costEstimate: event.costEstimate,
|
|
324
|
+
endedAt: new Date().toISOString(),
|
|
325
|
+
};
|
|
326
|
+
await client.syncSession(session);
|
|
327
|
+
client.clearSessionState(event.sessionId);
|
|
328
|
+
console.log(`[claude-code-sync] Session synced: ${event.messageCount} messages, ${event.toolCallCount} tool calls`);
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
// Default export for Claude Code plugin system
|
|
333
|
+
exports.default = createPlugin;
|
|
334
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7OztHQVFHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvSUgsZ0NBNEJDO0FBRUQsZ0NBVUM7QUFFRCxrQ0FRQztBQXdHRCxvQ0FxS0M7QUFqY0QsdUNBQXlCO0FBQ3pCLDJDQUE2QjtBQUM3Qix1Q0FBeUI7QUF5SHpCLCtFQUErRTtBQUMvRSxnQkFBZ0I7QUFDaEIsK0VBQStFO0FBRS9FLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFLFNBQVMsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO0FBQzFFLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLGFBQWEsQ0FBQyxDQUFDO0FBRXpELFNBQWdCLFVBQVU7SUFDeEIsb0NBQW9DO0lBQ3BDLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0JBQXNCLENBQUM7SUFDbEQsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQztJQUUvQyxJQUFJLE1BQU0sSUFBSSxNQUFNLEVBQUUsQ0FBQztRQUNyQixPQUFPO1lBQ0wsU0FBUyxFQUFFLGtCQUFrQixDQUFDLE1BQU0sQ0FBQztZQUNyQyxNQUFNLEVBQUUsTUFBTTtZQUNkLFFBQVEsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLHFCQUFxQixLQUFLLE9BQU87WUFDdkQsYUFBYSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0JBQXNCLEtBQUssT0FBTztZQUM3RCxZQUFZLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsS0FBSyxNQUFNO1NBQzFELENBQUM7SUFDSixDQUFDO0lBRUQsMkJBQTJCO0lBQzNCLElBQUksQ0FBQztRQUNILElBQUksRUFB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QUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ2xDLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFMUMsT0FBTyxDQUFDLEdBQUcsQ0FDVCxzQ0FBc0MsS0FBSyxDQUFDLFlBQVksY0FBYyxLQUFLLENBQUMsYUFBYSxhQUFhLENBQ3ZHLENBQUM7UUFDSixDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRCwrQ0FBK0M7QUFDL0Msa0JBQWUsWUFBWSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDbGF1ZGUgQ29kZSBTeW5jIFBsdWdpblxuICpcbiAqIFN5bmNzIENsYXVkZSBDb2RlIHNlc3Npb25zIHRvIE9wZW5TeW5jIGRhc2hib2FyZC5cbiAqIFVzZXMgQVBJIEtleSBhdXRoZW50aWNhdGlvbiAobm8gYnJvd3NlciBPQXV0aCByZXF1aXJlZCkuXG4gKlxuICogSW5zdGFsbDogbnBtIGluc3RhbGwgLWcgY2xhdWRlLWNvZGUtc3luY1xuICogQ29uZmlndXJlOiBjbGF1ZGUtY29kZS1zeW5jIGxvZ2luXG4gKi9cblxuaW1wb3J0ICogYXMgZnMgZnJvbSBcImZzXCI7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgKiBhcyBvcyBmcm9tIFwib3NcIjtcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gVHlwZXNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZXhwb3J0IGludGVyZmFjZSBDb25maWcge1xuICBjb252ZXhVcmw6IHN0cmluZztcbiAgYXBpS2V5OiBzdHJpbmc7XG4gIGF1dG9TeW5jPzogYm9vbGVhbjtcbiAgc3luY1Rvb2xDYWxscz86IGJvb2xlYW47XG4gIHN5bmNUaGlua2luZz86IGJvb2xlYW47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2Vzc2lvbkRhdGEge1xuICBzZXNzaW9uSWQ6IHN0cmluZztcbiAgc291cmNlOiBcImNsYXVkZS1jb2RlXCI7XG4gIHRpdGxlPzogc3RyaW5nO1xuICBwcm9qZWN0UGF0aD86IHN0cmluZztcbiAgcHJvamVjdE5hbWU/OiBzdHJpbmc7XG4gIGN3ZD86IHN0cmluZztcbiAgZ2l0QnJhbmNoPzogc3RyaW5nO1xuICBnaXRSZXBvPzogc3RyaW5nO1xuICBtb2RlbD86IHN0cmluZztcbiAgc3RhcnRUeXBlPzogXCJuZXdcIiB8IFwicmVzdW1lXCIgfCBcImNvbnRpbnVlXCI7XG4gIGVuZFJlYXNvbj86IFwidXNlcl9zdG9wXCIgfCBcIm1heF90dXJuc1wiIHwgXCJlcnJvclwiIHwgXCJjb21wbGV0ZWRcIjtcbiAgdGhpbmtpbmdFbmFibGVkPzogYm9vbGVhbjtcbiAgcGVybWlzc2lvbk1vZGU/OiBzdHJpbmc7XG4gIG1jcFNlcnZlcnM/OiBzdHJpbmdbXTtcbiAgbWVzc2FnZUNvdW50PzogbnVtYmVyO1xuICB0b29sQ2FsbENvdW50PzogbnVtYmVyO1xuICB0b2tlblVzYWdlPzoge1xuICAgIGlucHV0OiBudW1iZXI7XG4gICAgb3V0cHV0OiBudW1iZXI7XG4gIH07XG4gIGNvc3RFc3RpbWF0ZT86IG51bWJlcjtcbiAgc3RhcnRlZEF0Pzogc3RyaW5nO1xuICBlbmRlZEF0Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1lc3NhZ2VEYXRhIHtcbiAgc2Vzc2lvbklkOiBzdHJpbmc7XG4gIG1lc3NhZ2VJZDogc3RyaW5nO1xuICBzb3VyY2U6IFwiY2xhdWRlLWNvZGVcIjtcbiAgcm9sZTogXCJ1c2VyXCIgfCBcImFzc2lzdGFudFwiIHwgXCJzeXN0ZW1cIjtcbiAgY29udGVudD86IHN0cmluZztcbiAgdGhpbmtpbmdDb250ZW50Pzogc3RyaW5nO1xuICB0b29sTmFtZT86IHN0cmluZztcbiAgdG9vbEFyZ3M/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgdG9vbFJlc3VsdD86IHN0cmluZztcbiAgZHVyYXRpb25Ncz86IG51bWJlcjtcbiAgdG9rZW5Db3VudD86IG51bWJlcjtcbiAgdGltZXN0YW1wPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRvb2xVc2VEYXRhIHtcbiAgc2Vzc2lvbklkOiBzdHJpbmc7XG4gIHRvb2xOYW1lOiBzdHJpbmc7XG4gIHRvb2xBcmdzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gIHJlc3VsdD86IHN0cmluZztcbiAgc3VjY2Vzcz86IGJvb2xlYW47XG4gIGR1cmF0aW9uTXM/OiBudW1iZXI7XG4gIHRpbWVzdGFtcD86IHN0cmluZztcbn1cblxuLy8gQ2xhdWRlIENvZGUgSG9vayBUeXBlc1xuZXhwb3J0IGludGVyZmFjZSBDbGF1ZGVDb2RlSG9va3Mge1xuICBTZXNzaW9uU3RhcnQ/OiAoZGF0YTogU2Vzc2lvblN0YXJ0RXZlbnQpID0+IHZvaWQgfCBQcm9taXNlPHZvaWQ+O1xuICBVc2VyUHJvbXB0U3VibWl0PzogKGRhdGE6IFVzZXJQcm9tcHRFdmVudCkgPT4gdm9pZCB8IFByb21pc2U8dm9pZD47XG4gIFBvc3RUb29sVXNlPzogKGRhdGE6IFRvb2xVc2VFdmVudCkgPT4gdm9pZCB8IFByb21pc2U8dm9pZD47XG4gIFN0b3A/OiAoZGF0YTogU3RvcEV2ZW50KSA9PiB2b2lkIHwgUHJvbWlzZTx2b2lkPjtcbiAgU2Vzc2lvbkVuZD86IChkYXRhOiBTZXNzaW9uRW5kRXZlbnQpID0+IHZvaWQgfCBQcm9taXNlPHZvaWQ+O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFNlc3Npb25TdGFydEV2ZW50IHtcbiAgc2Vzc2lvbklkOiBzdHJpbmc7XG4gIGN3ZDogc3RyaW5nO1xuICBtb2RlbDogc3RyaW5nO1xuICBzdGFydFR5cGU6IFwibmV3XCIgfCBcInJlc3VtZVwiIHwgXCJjb250aW51ZVwiO1xuICB0aGlua2luZ0VuYWJsZWQ/OiBib29sZWFuO1xuICBwZXJtaXNzaW9uTW9kZT86IHN0cmluZztcbiAgbWNwU2VydmVycz86IHN0cmluZ1tdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFVzZXJQcm9tcHRFdmVudCB7XG4gIHNlc3Npb25JZDogc3RyaW5nO1xuICBwcm9tcHQ6IHN0cmluZztcbiAgdGltZXN0YW1wOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVG9vbFVzZUV2ZW50IHtcbiAgc2Vzc2lvbklkOiBzdHJpbmc7XG4gIHRvb2xOYW1lOiBzdHJpbmc7XG4gIGFyZ3M6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICByZXN1bHQ6IHN0cmluZztcbiAgc3VjY2VzczogYm9vbGVhbjtcbiAgZHVyYXRpb25NczogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFN0b3BFdmVudCB7XG4gIHNlc3Npb25JZDogc3RyaW5nO1xuICByZXNwb25zZTogc3RyaW5nO1xuICB0b2tlblVzYWdlOiB7XG4gICAgaW5wdXQ6IG51bWJlcjtcbiAgICBvdXRwdXQ6IG51bWJlcjtcbiAgfTtcbiAgZHVyYXRpb25NczogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFNlc3Npb25FbmRFdmVudCB7XG4gIHNlc3Npb25JZDogc3RyaW5nO1xuICBlbmRSZWFzb246IFwidXNlcl9zdG9wXCIgfCBcIm1heF90dXJuc1wiIHwgXCJlcnJvclwiIHwgXCJjb21wbGV0ZWRcIjtcbiAgbWVzc2FnZUNvdW50OiBudW1iZXI7XG4gIHRvb2xDYWxsQ291bnQ6IG51bWJlcjtcbiAgdG90YWxUb2tlblVzYWdlOiB7XG4gICAgaW5wdXQ6IG51bWJlcjtcbiAgICBvdXRwdXQ6IG51bWJlcjtcbiAgfTtcbiAgY29zdEVzdGltYXRlOiBudW1iZXI7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIENvbmZpZ3VyYXRpb25cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuY29uc3QgQ09ORklHX0RJUiA9IHBhdGguam9pbihvcy5ob21lZGlyKCksIFwiLmNvbmZpZ1wiLCBcImNsYXVkZS1jb2RlLXN5bmNcIik7XG5jb25zdCBDT05GSUdfRklMRSA9IHBhdGguam9pbihDT05GSUdfRElSLCBcImNvbmZpZy5qc29uXCIpO1xuXG5leHBvcnQgZnVuY3Rpb24gbG9hZENvbmZpZygpOiBDb25maWcgfCBudWxsIHtcbiAgLy8gQ2hlY2sgZW52aXJvbm1lbnQgdmFyaWFibGVzIGZpcnN0XG4gIGNvbnN0IGVudlVybCA9IHByb2Nlc3MuZW52LkNMQVVERV9TWU5DX0NPTlZFWF9VUkw7XG4gIGNvbnN0IGVudktleSA9IHByb2Nlc3MuZW52LkNMQVVERV9TWU5DX0FQSV9LRVk7XG5cbiAgaWYgKGVudlVybCAmJiBlbnZLZXkpIHtcbiAgICByZXR1cm4ge1xuICAgICAgY29udmV4VXJsOiBub3JtYWxpemVDb252ZXhVcmwoZW52VXJsKSxcbiAgICAgIGFwaUtleTogZW52S2V5LFxuICAgICAgYXV0b1N5bmM6IHByb2Nlc3MuZW52LkNMQVVERV9TWU5DX0FVVE9fU1lOQyAhPT0gXCJmYWxzZVwiLFxuICAgICAgc3luY1Rvb2xDYWxsczogcHJvY2Vzcy5lbnYuQ0xBVURFX1NZTkNfVE9PTF9DQUxMUyAhPT0gXCJmYWxzZVwiLFxuICAgICAgc3luY1RoaW5raW5nOiBwcm9jZXNzLmVudi5DTEFVREVfU1lOQ19USElOS0lORyA9PT0gXCJ0cnVlXCIsXG4gICAgfTtcbiAgfVxuXG4gIC8vIEZhbGwgYmFjayB0byBjb25maWcgZmlsZVxuICB0cnkge1xuICAgIGlmIChmcy5leGlzdHNTeW5jKENPTkZJR19GSUxFKSkge1xuICAgICAgY29uc3QgZGF0YSA9IGZzLnJlYWRGaWxlU3luYyhDT05GSUdfRklMRSwgXCJ1dGYtOFwiKTtcbiAgICAgIGNvbnN0IGNvbmZpZyA9IEpTT04ucGFyc2UoZGF0YSkgYXMgQ29uZmlnO1xuICAgICAgY29uZmlnLmNvbnZleFVybCA9IG5vcm1hbGl6ZUNvbnZleFVybChjb25maWcuY29udmV4VXJsKTtcbiAgICAgIHJldHVybiBjb25maWc7XG4gICAgfVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBsb2FkaW5nIGNvbmZpZzpcIiwgZXJyb3IpO1xuICB9XG5cbiAgcmV0dXJuIG51bGw7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzYXZlQ29uZmlnKGNvbmZpZzogQ29uZmlnKTogdm9pZCB7XG4gIHRyeSB7XG4gICAgaWYgKCFmcy5leGlzdHNTeW5jKENPTkZJR19ESVIpKSB7XG4gICAgICBmcy5ta2RpclN5bmMoQ09ORklHX0RJUiwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfVxuICAgIGZzLndyaXRlRmlsZVN5bmMoQ09ORklHX0ZJTEUsIEpTT04uc3RyaW5naWZ5KGNvbmZpZywgbnVsbCwgMikpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBzYXZpbmcgY29uZmlnOlwiLCBlcnJvcik7XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyQ29uZmlnKCk6IHZvaWQge1xuICB0cnkge1xuICAgIGlmIChmcy5leGlzdHNTeW5jKENPTkZJR19GSUxFKSkge1xuICAgICAgZnMudW5saW5rU3luYyhDT05GSUdfRklMRSk7XG4gICAgfVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnNvbGUuZXJyb3IoXCJFcnJvciBjbGVhcmluZyBjb25maWc6XCIsIGVycm9yKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBub3JtYWxpemVDb252ZXhVcmwodXJsOiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBDb252ZXJ0IC5jb252ZXguY2xvdWQgdG8gLmNvbnZleC5zaXRlIGZvciBBUEkgY2FsbHNcbiAgcmV0dXJuIHVybC5yZXBsYWNlKFwiLmNvbnZleC5jbG91ZFwiLCBcIi5jb252ZXguc2l0ZVwiKTtcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gU3luYyBDbGllbnRcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZXhwb3J0IGNsYXNzIFN5bmNDbGllbnQge1xuICBwcml2YXRlIGNvbmZpZzogQ29uZmlnO1xuICBwcml2YXRlIHNlc3Npb25DYWNoZTogTWFwPHN0cmluZywgUGFydGlhbDxTZXNzaW9uRGF0YT4+ID0gbmV3IE1hcCgpO1xuXG4gIGNvbnN0cnVjdG9yKGNvbmZpZzogQ29uZmlnKSB7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJlcXVlc3QoZW5kcG9pbnQ6IHN0cmluZywgZGF0YTogdW5rbm93bik6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIGNvbnN0IHVybCA9IGAke3RoaXMuY29uZmlnLmNvbnZleFVybH0ke2VuZHBvaW50fWA7XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHVybCwge1xuICAgICAgbWV0aG9kOiBcIlBPU1RcIixcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgICAgIEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHt0aGlzLmNvbmZpZy5hcGlLZXl9YCxcbiAgICAgIH0sXG4gICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShkYXRhKSxcbiAgICB9KTtcblxuICAgIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICAgIGNvbnN0IHRleHQgPSBhd2FpdCByZXNwb25zZS50ZXh0KCk7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFN5bmMgZmFpbGVkOiAke3Jlc3BvbnNlLnN0YXR1c30gLSAke3RleHR9YCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3BvbnNlLmpzb24oKTtcbiAgfVxuXG4gIGFzeW5jIHN5bmNTZXNzaW9uKHNlc3Npb246IFNlc3Npb25EYXRhKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMucmVxdWVzdChcIi9zeW5jL3Nlc3Npb25cIiwgc2Vzc2lvbik7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoXCJGYWlsZWQgdG8gc3luYyBzZXNzaW9uOlwiLCBlcnJvcik7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgc3luY01lc3NhZ2UobWVzc2FnZTogTWVzc2FnZURhdGEpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5yZXF1ZXN0KFwiL3N5bmMvbWVzc2FnZVwiLCBtZXNzYWdlKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihcIkZhaWxlZCB0byBzeW5jIG1lc3NhZ2U6XCIsIGVycm9yKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBzeW5jQmF0Y2goXG4gICAgc2Vzc2lvbnM6IFNlc3Npb25EYXRhW10sXG4gICAgbWVzc2FnZXM6IE1lc3NhZ2VEYXRhW11cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMucmVxdWVzdChcIi9zeW5jL2JhdGNoXCIsIHsgc2Vzc2lvbnMsIG1lc3NhZ2VzIH0pO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwiRmFpbGVkIHRvIHN5bmMgYmF0Y2g6XCIsIGVycm9yKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyB0ZXN0Q29ubmVjdGlvbigpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgdXJsID0gYCR7dGhpcy5jb25maWcuY29udmV4VXJsfS9oZWFsdGhgO1xuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh1cmwpO1xuICAgICAgcmV0dXJuIHJlc3BvbnNlLm9rO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8vIFNlc3Npb24gc3RhdGUgbWFuYWdlbWVudFxuICBnZXRTZXNzaW9uU3RhdGUoc2Vzc2lvbklkOiBzdHJpbmcpOiBQYXJ0aWFsPFNlc3Npb25EYXRhPiB7XG4gICAgcmV0dXJuIHRoaXMuc2Vzc2lvbkNhY2hlLmdldChzZXNzaW9uSWQpIHx8IHt9O1xuICB9XG5cbiAgdXBkYXRlU2Vzc2lvblN0YXRlKFxuICAgIHNlc3Npb25JZDogc3RyaW5nLFxuICAgIHVwZGF0ZXM6IFBhcnRpYWw8U2Vzc2lvbkRhdGE+XG4gICk6IHZvaWQge1xuICAgIGNvbnN0IGN1cnJlbnQgPSB0aGlzLnNlc3Npb25DYWNoZS5nZXQoc2Vzc2lvbklkKSB8fCB7fTtcbiAgICB0aGlzLnNlc3Npb25DYWNoZS5zZXQoc2Vzc2lvbklkLCB7IC4uLmN1cnJlbnQsIC4uLnVwZGF0ZXMgfSk7XG4gIH1cblxuICBjbGVhclNlc3Npb25TdGF0ZShzZXNzaW9uSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuc2Vzc2lvbkNhY2hlLmRlbGV0ZShzZXNzaW9uSWQpO1xuICB9XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFBsdWdpbiBFeHBvcnRcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBDbGF1ZGUgQ29kZSBQbHVnaW4gRW50cnkgUG9pbnRcbiAqXG4gKiBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCBieSBDbGF1ZGUgQ29kZSB0byByZWdpc3RlciB0aGUgcGx1Z2luLlxuICogSXQgcmV0dXJucyBob29rIGhhbmRsZXJzIHRoYXQgZmlyZSBhdCBrZXkgcG9pbnRzIGluIHRoZSBzZXNzaW9uIGxpZmVjeWNsZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVBsdWdpbigpOiBDbGF1ZGVDb2RlSG9va3MgfCBudWxsIHtcbiAgY29uc3QgY29uZmlnID0gbG9hZENvbmZpZygpO1xuXG4gIGlmICghY29uZmlnKSB7XG4gICAgY29uc29sZS5sb2coXG4gICAgICBcIltjbGF1ZGUtY29kZS1zeW5jXSBOb3QgY29uZmlndXJlZC4gUnVuICdjbGF1ZGUtY29kZS1zeW5jIGxvZ2luJyB0byBzZXQgdXAuXCJcbiAgICApO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgaWYgKGNvbmZpZy5hdXRvU3luYyA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhcIltjbGF1ZGUtY29kZS1zeW5jXSBBdXRvLXN5bmMgZGlzYWJsZWQgaW4gY29uZmlnLlwiKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGNvbnN0IGNsaWVudCA9IG5ldyBTeW5jQ2xpZW50KGNvbmZpZyk7XG4gIGxldCBtZXNzYWdlQ291bnRlciA9IDA7XG4gIGxldCB0b29sQ2FsbENvdW50ZXIgPSAwO1xuXG4gIGNvbnNvbGUubG9nKFwiW2NsYXVkZS1jb2RlLXN5bmNdIFBsdWdpbiBsb2FkZWQuIFNlc3Npb25zIHdpbGwgc3luYyB0byBPcGVuU3luYy5cIik7XG5cbiAgcmV0dXJuIHtcbiAgICAvKipcbiAgICAgKiBDYWxsZWQgd2hlbiBhIG5ldyBzZXNzaW9uIHN0YXJ0c1xuICAgICAqL1xuICAgIFNlc3Npb25TdGFydDogYXN5bmMgKGV2ZW50OiBTZXNzaW9uU3RhcnRFdmVudCkgPT4ge1xuICAgICAgbWVzc2FnZUNvdW50ZXIgPSAwO1xuICAgICAgdG9vbENhbGxDb3VudGVyID0gMDtcblxuICAgICAgY29uc3Qgc2Vzc2lvbjogU2Vzc2lvbkRhdGEgPSB7XG4gICAgICAgIHNlc3Npb25JZDogZXZlbnQuc2Vzc2lvbklkLFxuICAgICAgICBzb3VyY2U6IFwiY2xhdWRlLWNvZGVcIixcbiAgICAgICAgY3dkOiBldmVudC5jd2QsXG4gICAgICAgIG1vZGVsOiBldmVudC5tb2RlbCxcbiAgICAgICAgc3RhcnRUeXBlOiBldmVudC5zdGFydFR5cGUsXG4gICAgICAgIHRoaW5raW5nRW5hYmxlZDogZXZlbnQudGhpbmtpbmdFbmFibGVkLFxuICAgICAgICBwZXJtaXNzaW9uTW9kZTogZXZlbnQucGVybWlzc2lvbk1vZGUsXG4gICAgICAgIG1jcFNlcnZlcnM6IGV2ZW50Lm1jcFNlcnZlcnMsXG4gICAgICAgIHN0YXJ0ZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgfTtcblxuICAgICAgLy8gRXh0cmFjdCBwcm9qZWN0IGluZm8gZnJvbSBjd2RcbiAgICAgIGlmIChldmVudC5jd2QpIHtcbiAgICAgICAgc2Vzc2lvbi5wcm9qZWN0UGF0aCA9IGV2ZW50LmN3ZDtcbiAgICAgICAgc2Vzc2lvbi5wcm9qZWN0TmFtZSA9IHBhdGguYmFzZW5hbWUoZXZlbnQuY3dkKTtcblxuICAgICAgICAvLyBUcnkgdG8gZ2V0IGdpdCBpbmZvXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZ2l0RGlyID0gcGF0aC5qb2luKGV2ZW50LmN3ZCwgXCIuZ2l0XCIpO1xuICAgICAgICAgIGlmIChmcy5leGlzdHNTeW5jKGdpdERpcikpIHtcbiAgICAgICAgICAgIGNvbnN0IGhlYWRGaWxlID0gcGF0aC5qb2luKGdpdERpciwgXCJIRUFEXCIpO1xuICAgICAgICAgICAgaWYgKGZzLmV4aXN0c1N5bmMoaGVhZEZpbGUpKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGhlYWQgPSBmcy5yZWFkRmlsZVN5bmMoaGVhZEZpbGUsIFwidXRmLThcIikudHJpbSgpO1xuICAgICAgICAgICAgICBpZiAoaGVhZC5zdGFydHNXaXRoKFwicmVmOiByZWZzL2hlYWRzL1wiKSkge1xuICAgICAgICAgICAgICAgIHNlc3Npb24uZ2l0QnJhbmNoID0gaGVhZC5yZXBsYWNlKFwicmVmOiByZWZzL2hlYWRzL1wiLCBcIlwiKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gSWdub3JlIGdpdCBlcnJvcnNcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjbGllbnQudXBkYXRlU2Vzc2lvblN0YXRlKGV2ZW50LnNlc3Npb25JZCwgc2Vzc2lvbik7XG4gICAgICBhd2FpdCBjbGllbnQuc3luY1Nlc3Npb24oc2Vzc2lvbik7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIENhbGxlZCB3aGVuIHVzZXIgc3VibWl0cyBhIHByb21wdFxuICAgICAqL1xuICAgIFVzZXJQcm9tcHRTdWJtaXQ6IGFzeW5jIChldmVudDogVXNlclByb21wdEV2ZW50KSA9PiB7XG4gICAgICBtZXNzYWdlQ291bnRlcisrO1xuXG4gICAgICBjb25zdCBtZXNzYWdlOiBNZXNzYWdlRGF0YSA9IHtcbiAgICAgICAgc2Vzc2lvbklkOiBldmVudC5zZXNzaW9uSWQsXG4gICAgICAgIG1lc3NhZ2VJZDogYCR7ZXZlbnQuc2Vzc2lvbklkfS1tc2ctJHttZXNzYWdlQ291bnRlcn1gLFxuICAgICAgICBzb3VyY2U6IFwiY2xhdWRlLWNvZGVcIixcbiAgICAgICAgcm9sZTogXCJ1c2VyXCIsXG4gICAgICAgIGNvbnRlbnQ6IGV2ZW50LnByb21wdCxcbiAgICAgICAgdGltZXN0YW1wOiBldmVudC50aW1lc3RhbXAgfHwgbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgfTtcblxuICAgICAgYXdhaXQgY2xpZW50LnN5bmNNZXNzYWdlKG1lc3NhZ2UpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDYWxsZWQgYWZ0ZXIgZWFjaCB0b29sIHVzZVxuICAgICAqL1xuICAgIFBvc3RUb29sVXNlOiBhc3luYyAoZXZlbnQ6IFRvb2xVc2VFdmVudCkgPT4ge1xuICAgICAgaWYgKCFjb25maWcuc3luY1Rvb2xDYWxscykgcmV0dXJuO1xuXG4gICAgICB0b29sQ2FsbENvdW50ZXIrKztcbiAgICAgIG1lc3NhZ2VDb3VudGVyKys7XG5cbiAgICAgIGNvbnN0IG1lc3NhZ2U6IE1lc3NhZ2VEYXRhID0ge1xuICAgICAgICBzZXNzaW9uSWQ6IGV2ZW50LnNlc3Npb25JZCxcbiAgICAgICAgbWVzc2FnZUlkOiBgJHtldmVudC5zZXNzaW9uSWR9LXRvb2wtJHt0b29sQ2FsbENvdW50ZXJ9YCxcbiAgICAgICAgc291cmNlOiBcImNsYXVkZS1jb2RlXCIsXG4gICAgICAgIHJvbGU6IFwiYXNzaXN0YW50XCIsXG4gICAgICAgIHRvb2xOYW1lOiBldmVudC50b29sTmFtZSxcbiAgICAgICAgdG9vbEFyZ3M6IGV2ZW50LmFyZ3MsXG4gICAgICAgIHRvb2xSZXN1bHQ6IGV2ZW50LnJlc3VsdCxcbiAgICAgICAgZHVyYXRpb25NczogZXZlbnQuZHVyYXRpb25NcyxcbiAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICB9O1xuXG4gICAgICBhd2FpdCBjbGllbnQuc3luY01lc3NhZ2UobWVzc2FnZSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIENhbGxlZCB3aGVuIENsYXVkZSBzdG9wcyByZXNwb25kaW5nXG4gICAgICovXG4gICAgU3RvcDogYXN5bmMgKGV2ZW50OiBTdG9wRXZlbnQpID0+IHtcbiAgICAgIG1lc3NhZ2VDb3VudGVyKys7XG5cbiAgICAgIGNvbnN0IG1lc3NhZ2U6IE1lc3NhZ2VEYXRhID0ge1xuICAgICAgICBzZXNzaW9uSWQ6IGV2ZW50LnNlc3Npb25JZCxcbiAgICAgICAgbWVzc2FnZUlkOiBgJHtldmVudC5zZXNzaW9uSWR9LW1zZy0ke21lc3NhZ2VDb3VudGVyfWAsXG4gICAgICAgIHNvdXJjZTogXCJjbGF1ZGUtY29kZVwiLFxuICAgICAgICByb2xlOiBcImFzc2lzdGFudFwiLFxuICAgICAgICBjb250ZW50OiBldmVudC5yZXNwb25zZSxcbiAgICAgICAgdG9rZW5Db3VudDogZXZlbnQudG9rZW5Vc2FnZS5pbnB1dCArIGV2ZW50LnRva2VuVXNhZ2Uub3V0cHV0LFxuICAgICAgICBkdXJhdGlvbk1zOiBldmVudC5kdXJhdGlvbk1zLFxuICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgIH07XG5cbiAgICAgIC8vIFVwZGF0ZSBzZXNzaW9uIHN0YXRlIHdpdGggdG9rZW4gdXNhZ2VcbiAgICAgIGNvbnN0IGN1cnJlbnRTdGF0ZSA9IGNsaWVudC5nZXRTZXNzaW9uU3RhdGUoZXZlbnQuc2Vzc2lvbklkKTtcbiAgICAgIGNvbnN0IGN1cnJlbnRUb2tlbnMgPSBjdXJyZW50U3RhdGUudG9rZW5Vc2FnZSB8fCB7IGlucHV0OiAwLCBvdXRwdXQ6IDAgfTtcbiAgICAgIGNsaWVudC51cGRhdGVTZXNzaW9uU3RhdGUoZXZlbnQuc2Vzc2lvbklkLCB7XG4gICAgICAgIHRva2VuVXNhZ2U6IHtcbiAgICAgICAgICBpbnB1dDogY3VycmVudFRva2Vucy5pbnB1dCArIGV2ZW50LnRva2VuVXNhZ2UuaW5wdXQsXG4gICAgICAgICAgb3V0cHV0OiBjdXJyZW50VG9rZW5zLm91dHB1dCArIGV2ZW50LnRva2VuVXNhZ2Uub3V0cHV0LFxuICAgICAgICB9LFxuICAgICAgfSk7XG5cbiAgICAgIGF3YWl0IGNsaWVudC5zeW5jTWVzc2FnZShtZXNzYWdlKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQ2FsbGVkIHdoZW4gc2Vzc2lvbiBlbmRzXG4gICAgICovXG4gICAgU2Vzc2lvbkVuZDogYXN5bmMgKGV2ZW50OiBTZXNzaW9uRW5kRXZlbnQpID0+IHtcbiAgICAgIGNvbnN0IGN1cnJlbnRTdGF0ZSA9IGNsaWVudC5nZXRTZXNzaW9uU3RhdGUoZXZlbnQuc2Vzc2lvbklkKTtcblxuICAgICAgY29uc3Qgc2Vzc2lvbjogU2Vzc2lvbkRhdGEgPSB7XG4gICAgICAgIC4uLmN1cnJlbnRTdGF0ZSxcbiAgICAgICAgc2Vzc2lvbklkOiBldmVudC5zZXNzaW9uSWQsXG4gICAgICAgIHNvdXJjZTogXCJjbGF1ZGUtY29kZVwiLFxuICAgICAgICBlbmRSZWFzb246IGV2ZW50LmVuZFJlYXNvbixcbiAgICAgICAgbWVzc2FnZUNvdW50OiBldmVudC5tZXNzYWdlQ291bnQsXG4gICAgICAgIHRvb2xDYWxsQ291bnQ6IGV2ZW50LnRvb2xDYWxsQ291bnQsXG4gICAgICAgIHRva2VuVXNhZ2U6IGV2ZW50LnRvdGFsVG9rZW5Vc2FnZSxcbiAgICAgICAgY29zdEVzdGltYXRlOiBldmVudC5jb3N0RXN0aW1hdGUsXG4gICAgICAgIGVuZGVkQXQ6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgIH07XG5cbiAgICAgIGF3YWl0IGNsaWVudC5zeW5jU2Vzc2lvbihzZXNzaW9uKTtcbiAgICAgIGNsaWVudC5jbGVhclNlc3Npb25TdGF0ZShldmVudC5zZXNzaW9uSWQpO1xuXG4gICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgYFtjbGF1ZGUtY29kZS1zeW5jXSBTZXNzaW9uIHN5bmNlZDogJHtldmVudC5tZXNzYWdlQ291bnR9IG1lc3NhZ2VzLCAke2V2ZW50LnRvb2xDYWxsQ291bnR9IHRvb2wgY2FsbHNgXG4gICAgICApO1xuICAgIH0sXG4gIH07XG59XG5cbi8vIERlZmF1bHQgZXhwb3J0IGZvciBDbGF1ZGUgQ29kZSBwbHVnaW4gc3lzdGVtXG5leHBvcnQgZGVmYXVsdCBjcmVhdGVQbHVnaW47XG4iXX0=
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-code-sync",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Sync your Claude Code sessions to OpenSync dashboard",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"claude-code-sync": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"test": "echo \"No tests yet\" && exit 0"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"claude",
|
|
18
|
+
"claude-code",
|
|
19
|
+
"sync",
|
|
20
|
+
"opensync",
|
|
21
|
+
"ai",
|
|
22
|
+
"coding",
|
|
23
|
+
"sessions",
|
|
24
|
+
"convex"
|
|
25
|
+
],
|
|
26
|
+
"author": "Wayne Sutton",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/waynesutton/claude-code-sync.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/waynesutton/claude-code-sync/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/waynesutton/claude-code-sync#readme",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"commander": "^12.1.0",
|
|
38
|
+
"node-fetch": "^3.3.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.11.0",
|
|
42
|
+
"typescript": "^5.3.3"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md"
|
|
50
|
+
]
|
|
51
|
+
}
|