@teamvibe/poller 0.1.40 → 0.1.42
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/dist/cli/reset.js +96 -26
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli/reset.js
CHANGED
|
@@ -7,28 +7,6 @@ const DEFAULT_DATA_DIR = `${process.env['HOME']}/.teamvibe`;
|
|
|
7
7
|
function getBaseBrainPath() {
|
|
8
8
|
return process.env['BASE_BRAIN_PATH'] || `${DEFAULT_DATA_DIR}/base-brain`;
|
|
9
9
|
}
|
|
10
|
-
function cleanClaudeSessionLocks() {
|
|
11
|
-
const baseBrainPath = getBaseBrainPath();
|
|
12
|
-
let cleaned = 0;
|
|
13
|
-
// Claude Code stores session locks in the config dir (which is base-brain for poller)
|
|
14
|
-
// Look for .lock files in tasks/ and projects/ subdirectories
|
|
15
|
-
const lockDirs = [
|
|
16
|
-
path.join(baseBrainPath, 'tasks'),
|
|
17
|
-
path.join(baseBrainPath, 'projects'),
|
|
18
|
-
];
|
|
19
|
-
for (const dir of lockDirs) {
|
|
20
|
-
if (!fs.existsSync(dir))
|
|
21
|
-
continue;
|
|
22
|
-
cleanLocksRecursive(dir);
|
|
23
|
-
}
|
|
24
|
-
// Also check ~/.claude for lock files (default Claude config dir)
|
|
25
|
-
const claudeDir = `${process.env['HOME']}/.claude`;
|
|
26
|
-
if (fs.existsSync(claudeDir)) {
|
|
27
|
-
cleaned += cleanLocksRecursive(path.join(claudeDir, 'tasks'));
|
|
28
|
-
cleaned += cleanLocksRecursive(path.join(claudeDir, 'projects'));
|
|
29
|
-
}
|
|
30
|
-
return cleaned;
|
|
31
|
-
}
|
|
32
10
|
function cleanLocksRecursive(dir) {
|
|
33
11
|
if (!fs.existsSync(dir))
|
|
34
12
|
return 0;
|
|
@@ -47,6 +25,25 @@ function cleanLocksRecursive(dir) {
|
|
|
47
25
|
}
|
|
48
26
|
return cleaned;
|
|
49
27
|
}
|
|
28
|
+
function cleanClaudeSessionLocks() {
|
|
29
|
+
const baseBrainPath = getBaseBrainPath();
|
|
30
|
+
let cleaned = 0;
|
|
31
|
+
const lockDirs = [
|
|
32
|
+
path.join(baseBrainPath, 'tasks'),
|
|
33
|
+
path.join(baseBrainPath, 'projects'),
|
|
34
|
+
];
|
|
35
|
+
for (const dir of lockDirs) {
|
|
36
|
+
if (!fs.existsSync(dir))
|
|
37
|
+
continue;
|
|
38
|
+
cleaned += cleanLocksRecursive(dir);
|
|
39
|
+
}
|
|
40
|
+
const claudeDir = `${process.env['HOME']}/.claude`;
|
|
41
|
+
if (fs.existsSync(claudeDir)) {
|
|
42
|
+
cleaned += cleanLocksRecursive(path.join(claudeDir, 'tasks'));
|
|
43
|
+
cleaned += cleanLocksRecursive(path.join(claudeDir, 'projects'));
|
|
44
|
+
}
|
|
45
|
+
return cleaned;
|
|
46
|
+
}
|
|
50
47
|
function killClaudeProcesses() {
|
|
51
48
|
try {
|
|
52
49
|
const output = execSync('pgrep -f "claude.*--print" 2>/dev/null || true', {
|
|
@@ -70,6 +67,77 @@ function killClaudeProcesses() {
|
|
|
70
67
|
return 0;
|
|
71
68
|
}
|
|
72
69
|
}
|
|
70
|
+
/** Read poller token and API URL from the launchd plist or env */
|
|
71
|
+
function getPollerAuth() {
|
|
72
|
+
// Try env vars first
|
|
73
|
+
const apiUrl = process.env['TEAMVIBE_API_URL'];
|
|
74
|
+
const token = process.env['TEAMVIBE_POLLER_TOKEN'];
|
|
75
|
+
if (apiUrl && token)
|
|
76
|
+
return { apiUrl, token };
|
|
77
|
+
// Try reading from launchd plist
|
|
78
|
+
if (!fs.existsSync(PLIST_PATH))
|
|
79
|
+
return null;
|
|
80
|
+
try {
|
|
81
|
+
const plistContent = fs.readFileSync(PLIST_PATH, 'utf-8');
|
|
82
|
+
const envVars = {};
|
|
83
|
+
// Parse EnvironmentVariables from plist XML
|
|
84
|
+
const envMatch = plistContent.match(/<key>EnvironmentVariables<\/key>\s*<dict>([\s\S]*?)<\/dict>/);
|
|
85
|
+
if (envMatch?.[1]) {
|
|
86
|
+
const keyValuePairs = envMatch[1].matchAll(/<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g);
|
|
87
|
+
for (const match of keyValuePairs) {
|
|
88
|
+
if (match[1] && match[2] !== undefined) {
|
|
89
|
+
envVars[match[1]] = match[2];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const plistApiUrl = envVars['TEAMVIBE_API_URL'];
|
|
94
|
+
const plistToken = envVars['TEAMVIBE_POLLER_TOKEN'];
|
|
95
|
+
if (plistApiUrl && plistToken)
|
|
96
|
+
return { apiUrl: plistApiUrl, token: plistToken };
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Ignore plist parse errors
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
async function purgeQueue() {
|
|
104
|
+
const auth = getPollerAuth();
|
|
105
|
+
if (!auth) {
|
|
106
|
+
console.log(' Cannot purge queue: no auth credentials found');
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
// Authenticate to get SQS queue URL and AWS credentials
|
|
111
|
+
const response = await fetch(`${auth.apiUrl}/auth`, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: {
|
|
114
|
+
Authorization: `Bearer ${auth.token}`,
|
|
115
|
+
'Content-Type': 'application/json',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
console.log(` Auth failed (${response.status}), cannot purge queue`);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const authData = (await response.json());
|
|
123
|
+
const { SQSClient, PurgeQueueCommand } = await import('@aws-sdk/client-sqs');
|
|
124
|
+
const sqsClient = new SQSClient({
|
|
125
|
+
region: authData.config.region,
|
|
126
|
+
credentials: {
|
|
127
|
+
accessKeyId: authData.credentials.accessKeyId,
|
|
128
|
+
secretAccessKey: authData.credentials.secretAccessKey,
|
|
129
|
+
sessionToken: authData.credentials.sessionToken,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
await sqsClient.send(new PurgeQueueCommand({ QueueUrl: authData.config.sqsQueueUrl }));
|
|
133
|
+
console.log(` Purged queue: ${authData.config.sqsQueueUrl}`);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.log(` Failed to purge queue: ${error instanceof Error ? error.message : error}`);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
73
141
|
export async function reset() {
|
|
74
142
|
const isServiceInstalled = fs.existsSync(PLIST_PATH);
|
|
75
143
|
console.log('Resetting TeamVibe Poller...\n');
|
|
@@ -94,7 +162,6 @@ export async function reset() {
|
|
|
94
162
|
}
|
|
95
163
|
else {
|
|
96
164
|
console.log(` Killed ${killed} process(es)`);
|
|
97
|
-
// Give processes time to exit and release locks
|
|
98
165
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
99
166
|
}
|
|
100
167
|
// Step 3: Clean session lock files
|
|
@@ -106,13 +173,16 @@ export async function reset() {
|
|
|
106
173
|
else {
|
|
107
174
|
console.log(` Cleaned ${cleaned} lock file(s)`);
|
|
108
175
|
}
|
|
109
|
-
// Step 4:
|
|
176
|
+
// Step 4: Purge SQS queue to clear stuck messages
|
|
177
|
+
console.log('\n4. Purging SQS queue...');
|
|
178
|
+
await purgeQueue();
|
|
179
|
+
// Step 5: Restart service if it was installed
|
|
110
180
|
if (isServiceInstalled) {
|
|
111
|
-
console.log('\
|
|
181
|
+
console.log('\n5. Restarting service...');
|
|
112
182
|
start();
|
|
113
183
|
}
|
|
114
184
|
else {
|
|
115
|
-
console.log('\
|
|
185
|
+
console.log('\n5. No service to restart');
|
|
116
186
|
}
|
|
117
187
|
console.log('\nReset complete!');
|
|
118
188
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const command = process.argv[2];
|
|
3
|
-
if (command && ['install', 'uninstall', 'update', 'logs', 'status', 'start', 'stop', 'restart', '--help', '-h', '--version', '-v'].includes(command)) {
|
|
3
|
+
if (command && ['install', 'uninstall', 'update', 'logs', 'status', 'start', 'stop', 'restart', 'reset', '--help', '-h', '--version', '-v'].includes(command)) {
|
|
4
4
|
const { handleCommand } = await import('./cli/commands.js');
|
|
5
5
|
await handleCommand(command);
|
|
6
6
|
}
|