tab-agent 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/bin/tab-agent.js +40 -0
- package/cli/detect-extension.js +131 -0
- package/cli/setup.js +133 -0
- package/cli/start.js +19 -0
- package/cli/status.js +70 -0
- package/extension/content-script.js +510 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +40 -0
- package/extension/popup/popup.html +142 -0
- package/extension/popup/popup.js +104 -0
- package/extension/service-worker.js +471 -0
- package/extension/snapshot.js +194 -0
- package/package.json +25 -0
- package/relay/install-native-host.sh +57 -0
- package/relay/native-host-wrapper.cmd +3 -0
- package/relay/native-host-wrapper.sh +29 -0
- package/relay/native-host.js +128 -0
- package/relay/node_modules/.package-lock.json +29 -0
- package/relay/node_modules/ws/LICENSE +20 -0
- package/relay/node_modules/ws/README.md +548 -0
- package/relay/node_modules/ws/browser.js +8 -0
- package/relay/node_modules/ws/index.js +13 -0
- package/relay/node_modules/ws/lib/buffer-util.js +131 -0
- package/relay/node_modules/ws/lib/constants.js +19 -0
- package/relay/node_modules/ws/lib/event-target.js +292 -0
- package/relay/node_modules/ws/lib/extension.js +203 -0
- package/relay/node_modules/ws/lib/limiter.js +55 -0
- package/relay/node_modules/ws/lib/permessage-deflate.js +528 -0
- package/relay/node_modules/ws/lib/receiver.js +706 -0
- package/relay/node_modules/ws/lib/sender.js +602 -0
- package/relay/node_modules/ws/lib/stream.js +161 -0
- package/relay/node_modules/ws/lib/subprotocol.js +62 -0
- package/relay/node_modules/ws/lib/validation.js +152 -0
- package/relay/node_modules/ws/lib/websocket-server.js +554 -0
- package/relay/node_modules/ws/lib/websocket.js +1393 -0
- package/relay/node_modules/ws/package.json +69 -0
- package/relay/node_modules/ws/wrapper.mjs +8 -0
- package/relay/package-lock.json +36 -0
- package/relay/package.json +12 -0
- package/relay/server.js +114 -0
- package/skills/claude-code/tab-agent.md +53 -0
- package/skills/codex/tab-agent.md +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Tab Agent
|
|
2
|
+
|
|
3
|
+
Secure tab-level browser control for Claude Code and Codex — only the tabs you explicitly activate, not your entire browser.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
### 1. Load Extension
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/DrHB/tab-agent
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
1. Open `chrome://extensions`
|
|
13
|
+
2. Enable **Developer mode**
|
|
14
|
+
3. Click **Load unpacked** → select `extension/` folder
|
|
15
|
+
|
|
16
|
+
### 2. Setup
|
|
17
|
+
```bash
|
|
18
|
+
npx tab-agent setup
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
That's it! The setup auto-detects your extension and configures everything.
|
|
22
|
+
|
|
23
|
+
## Use
|
|
24
|
+
|
|
25
|
+
1. Click Tab Agent icon on any tab (turns green = active)
|
|
26
|
+
2. Ask Claude/Codex: "Use tab-agent to search Google for 'hello world'"
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
| Command | Description |
|
|
31
|
+
|---------|-------------|
|
|
32
|
+
| `tabs` | List activated tabs |
|
|
33
|
+
| `snapshot` | Get AI-readable page with refs [e1], [e2]... |
|
|
34
|
+
| `screenshot` | Capture viewport (or `fullPage: true` for full page) |
|
|
35
|
+
| `click` | Click element by ref |
|
|
36
|
+
| `fill` | Fill form field |
|
|
37
|
+
| `type` | Type text (with optional `submit: true`) |
|
|
38
|
+
| `press` | Press key (Enter, Escape, Tab, Arrow*) |
|
|
39
|
+
| `scroll` | Scroll page |
|
|
40
|
+
| `scrollintoview` | Scroll element into view |
|
|
41
|
+
| `navigate` | Go to URL |
|
|
42
|
+
| `wait` | Wait for text or selector |
|
|
43
|
+
| `evaluate` | Run JavaScript in page context |
|
|
44
|
+
| `batchfill` | Fill multiple fields at once |
|
|
45
|
+
| `dialog` | Handle alert/confirm/prompt |
|
|
46
|
+
|
|
47
|
+
## Manual Commands
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx tab-agent status # Check configuration
|
|
51
|
+
npx tab-agent start # Start relay manually
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Architecture
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Claude/Codex → WebSocket:9876 → Relay → Native Messaging → Extension → DOM
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
package/bin/tab-agent.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const command = process.argv[2];
|
|
3
|
+
const pkg = require('../package.json');
|
|
4
|
+
|
|
5
|
+
function showHelp() {
|
|
6
|
+
console.log(`
|
|
7
|
+
tab-agent - Browser control for Claude/Codex
|
|
8
|
+
|
|
9
|
+
Commands:
|
|
10
|
+
setup Auto-detect extension, register native host, install skills
|
|
11
|
+
start Start the relay server
|
|
12
|
+
status Check configuration status
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
npx tab-agent setup
|
|
16
|
+
npx tab-agent start
|
|
17
|
+
`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
switch (command) {
|
|
21
|
+
case 'setup':
|
|
22
|
+
require('../cli/setup.js');
|
|
23
|
+
break;
|
|
24
|
+
case 'start':
|
|
25
|
+
require('../cli/start.js');
|
|
26
|
+
break;
|
|
27
|
+
case 'status':
|
|
28
|
+
require('../cli/status.js');
|
|
29
|
+
break;
|
|
30
|
+
case '-v':
|
|
31
|
+
case '--version':
|
|
32
|
+
console.log(pkg.version);
|
|
33
|
+
break;
|
|
34
|
+
case undefined:
|
|
35
|
+
showHelp();
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
showHelp();
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// cli/detect-extension.js
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
// Support multiple browsers: Chrome, Brave, Edge, Chromium
|
|
7
|
+
function getAllBrowserExtensionPaths() {
|
|
8
|
+
const platform = os.platform();
|
|
9
|
+
const home = os.homedir();
|
|
10
|
+
const paths = [];
|
|
11
|
+
|
|
12
|
+
if (platform === 'darwin') {
|
|
13
|
+
const base = path.join(home, 'Library/Application Support');
|
|
14
|
+
paths.push(
|
|
15
|
+
path.join(base, 'Google/Chrome'),
|
|
16
|
+
path.join(base, 'Google/Chrome Canary'),
|
|
17
|
+
path.join(base, 'Chromium'),
|
|
18
|
+
path.join(base, 'BraveSoftware/Brave-Browser'),
|
|
19
|
+
path.join(base, 'Microsoft Edge'),
|
|
20
|
+
);
|
|
21
|
+
} else if (platform === 'linux') {
|
|
22
|
+
paths.push(
|
|
23
|
+
path.join(home, '.config/google-chrome'),
|
|
24
|
+
path.join(home, '.config/chromium'),
|
|
25
|
+
path.join(home, '.config/BraveSoftware/Brave-Browser'),
|
|
26
|
+
path.join(home, '.config/microsoft-edge'),
|
|
27
|
+
);
|
|
28
|
+
} else if (platform === 'win32') {
|
|
29
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
30
|
+
paths.push(
|
|
31
|
+
path.join(localAppData, 'Google/Chrome/User Data'),
|
|
32
|
+
path.join(localAppData, 'Chromium/User Data'),
|
|
33
|
+
path.join(localAppData, 'BraveSoftware/Brave-Browser/User Data'),
|
|
34
|
+
path.join(localAppData, 'Microsoft/Edge/User Data'),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Expand to include Default and Profile N directories
|
|
39
|
+
const expandedPaths = [];
|
|
40
|
+
for (const browserPath of paths) {
|
|
41
|
+
if (!fs.existsSync(browserPath)) continue;
|
|
42
|
+
|
|
43
|
+
const profiles = ['Default', ...fs.readdirSync(browserPath).filter(f => f.startsWith('Profile '))];
|
|
44
|
+
for (const profile of profiles) {
|
|
45
|
+
const extPath = path.join(browserPath, profile, 'Extensions');
|
|
46
|
+
if (fs.existsSync(extPath)) {
|
|
47
|
+
expandedPaths.push(extPath);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return expandedPaths;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function findTabAgentExtension() {
|
|
56
|
+
const extensionPaths = getAllBrowserExtensionPaths();
|
|
57
|
+
|
|
58
|
+
for (const extPath of extensionPaths) {
|
|
59
|
+
try {
|
|
60
|
+
const extIds = fs.readdirSync(extPath);
|
|
61
|
+
|
|
62
|
+
for (const extId of extIds) {
|
|
63
|
+
const extDir = path.join(extPath, extId);
|
|
64
|
+
if (!fs.statSync(extDir).isDirectory()) continue;
|
|
65
|
+
|
|
66
|
+
const versions = fs.readdirSync(extDir).filter(v => !v.startsWith('.'));
|
|
67
|
+
|
|
68
|
+
for (const version of versions) {
|
|
69
|
+
const manifestPath = path.join(extDir, version, 'manifest.json');
|
|
70
|
+
if (fs.existsSync(manifestPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
73
|
+
if (manifest.name === 'Tab Agent') {
|
|
74
|
+
return { extId, browser: extPath };
|
|
75
|
+
}
|
|
76
|
+
} catch (e) {}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function checkExistingManifest() {
|
|
86
|
+
const platform = os.platform();
|
|
87
|
+
const home = os.homedir();
|
|
88
|
+
let manifestPath;
|
|
89
|
+
|
|
90
|
+
if (platform === 'darwin') {
|
|
91
|
+
manifestPath = path.join(home, 'Library/Application Support/Google/Chrome/NativeMessagingHosts/com.tabagent.relay.json');
|
|
92
|
+
} else if (platform === 'linux') {
|
|
93
|
+
manifestPath = path.join(home, '.config/google-chrome/NativeMessagingHosts/com.tabagent.relay.json');
|
|
94
|
+
} else if (platform === 'win32') {
|
|
95
|
+
manifestPath = path.join(home, 'AppData/Local/Google/Chrome/User Data/NativeMessagingHosts/com.tabagent.relay.json');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (manifestPath && fs.existsSync(manifestPath)) {
|
|
99
|
+
try {
|
|
100
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
101
|
+
const origin = manifest.allowed_origins?.[0];
|
|
102
|
+
if (origin) {
|
|
103
|
+
const match = origin.match(/chrome-extension:\/\/([^/]+)/);
|
|
104
|
+
if (match) return match[1];
|
|
105
|
+
}
|
|
106
|
+
} catch (e) {}
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function promptForExtensionId() {
|
|
112
|
+
const readline = require('readline');
|
|
113
|
+
const rl = readline.createInterface({
|
|
114
|
+
input: process.stdin,
|
|
115
|
+
output: process.stdout
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
rl.question('Enter extension ID from chrome://extensions: ', (answer) => {
|
|
120
|
+
rl.close();
|
|
121
|
+
resolve(answer.trim());
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
findTabAgentExtension,
|
|
128
|
+
checkExistingManifest,
|
|
129
|
+
promptForExtensionId,
|
|
130
|
+
getAllBrowserExtensionPaths
|
|
131
|
+
};
|
package/cli/setup.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// cli/setup.js
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { findTabAgentExtension, checkExistingManifest, promptForExtensionId } = require('./detect-extension');
|
|
6
|
+
|
|
7
|
+
async function setup() {
|
|
8
|
+
console.log('Tab Agent Setup\n');
|
|
9
|
+
|
|
10
|
+
// 1. Detect extension ID
|
|
11
|
+
console.log('Detecting extension...');
|
|
12
|
+
let extensionId = null;
|
|
13
|
+
|
|
14
|
+
const found = findTabAgentExtension();
|
|
15
|
+
if (found) {
|
|
16
|
+
extensionId = found.extId;
|
|
17
|
+
console.log(`✓ Found extension: ${extensionId}`);
|
|
18
|
+
} else {
|
|
19
|
+
extensionId = checkExistingManifest();
|
|
20
|
+
if (extensionId) {
|
|
21
|
+
console.log(`✓ Found existing config: ${extensionId}`);
|
|
22
|
+
} else {
|
|
23
|
+
console.log('✗ Could not auto-detect extension');
|
|
24
|
+
console.log(' Make sure Tab Agent is loaded in chrome://extensions\n');
|
|
25
|
+
extensionId = await promptForExtensionId();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!extensionId || extensionId.length !== 32) {
|
|
30
|
+
console.error('Invalid extension ID');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 2. Install native messaging host
|
|
35
|
+
console.log('\nInstalling native messaging host...');
|
|
36
|
+
installNativeHost(extensionId);
|
|
37
|
+
console.log('✓ Native messaging host installed');
|
|
38
|
+
|
|
39
|
+
// 3. Install skills
|
|
40
|
+
console.log('\nInstalling skills...');
|
|
41
|
+
installSkills();
|
|
42
|
+
|
|
43
|
+
console.log('\n✓ Setup complete!\n');
|
|
44
|
+
console.log('Usage:');
|
|
45
|
+
console.log(' 1. Click Tab Agent icon on any tab (turns green)');
|
|
46
|
+
console.log(' 2. Ask Claude/Codex: "Use tab-agent to search Google"');
|
|
47
|
+
console.log('\nThe relay server starts automatically when needed.');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function installNativeHost(extensionId) {
|
|
51
|
+
const platform = os.platform();
|
|
52
|
+
const home = os.homedir();
|
|
53
|
+
const packageDir = path.dirname(__dirname);
|
|
54
|
+
|
|
55
|
+
let manifestDir;
|
|
56
|
+
let wrapperName;
|
|
57
|
+
|
|
58
|
+
if (platform === 'darwin') {
|
|
59
|
+
manifestDir = path.join(home, 'Library/Application Support/Google/Chrome/NativeMessagingHosts');
|
|
60
|
+
wrapperName = 'native-host-wrapper.sh';
|
|
61
|
+
} else if (platform === 'linux') {
|
|
62
|
+
manifestDir = path.join(home, '.config/google-chrome/NativeMessagingHosts');
|
|
63
|
+
wrapperName = 'native-host-wrapper.sh';
|
|
64
|
+
} else if (platform === 'win32') {
|
|
65
|
+
manifestDir = path.join(home, 'AppData/Local/Google/Chrome/User Data/NativeMessagingHosts');
|
|
66
|
+
wrapperName = 'native-host-wrapper.cmd';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fs.mkdirSync(manifestDir, { recursive: true });
|
|
70
|
+
|
|
71
|
+
const wrapperPath = path.join(packageDir, 'relay', wrapperName);
|
|
72
|
+
const manifest = {
|
|
73
|
+
name: 'com.tabagent.relay',
|
|
74
|
+
description: 'Tab Agent Native Messaging Host',
|
|
75
|
+
path: wrapperPath,
|
|
76
|
+
type: 'stdio',
|
|
77
|
+
allowed_origins: [`chrome-extension://${extensionId}/`]
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const manifestPath = path.join(manifestDir, 'com.tabagent.relay.json');
|
|
81
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
82
|
+
|
|
83
|
+
// Make wrapper executable (Unix only)
|
|
84
|
+
if (platform !== 'win32') {
|
|
85
|
+
fs.chmodSync(wrapperPath, '755');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Windows: also set registry key
|
|
89
|
+
if (platform === 'win32') {
|
|
90
|
+
const { execSync } = require('child_process');
|
|
91
|
+
const regPath = 'HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.tabagent.relay';
|
|
92
|
+
execSync(`reg add "${regPath}" /ve /t REG_SZ /d "${manifestPath}" /f`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function installSkills() {
|
|
97
|
+
const home = os.homedir();
|
|
98
|
+
const packageDir = path.dirname(__dirname);
|
|
99
|
+
const skillSource = path.join(packageDir, 'skills');
|
|
100
|
+
|
|
101
|
+
// Claude Code - always install
|
|
102
|
+
const claudeSkillDir = path.join(home, '.claude', 'skills');
|
|
103
|
+
const claudeSkillPath = path.join(claudeSkillDir, 'tab-agent.md');
|
|
104
|
+
fs.mkdirSync(claudeSkillDir, { recursive: true });
|
|
105
|
+
|
|
106
|
+
if (fs.existsSync(claudeSkillPath)) {
|
|
107
|
+
console.log(` Updating existing skill at ${claudeSkillPath}`);
|
|
108
|
+
}
|
|
109
|
+
fs.copyFileSync(
|
|
110
|
+
path.join(skillSource, 'claude-code', 'tab-agent.md'),
|
|
111
|
+
claudeSkillPath
|
|
112
|
+
);
|
|
113
|
+
console.log('✓ Installed Claude Code skill');
|
|
114
|
+
|
|
115
|
+
// Codex (only if .codex exists)
|
|
116
|
+
const codexDir = path.join(home, '.codex');
|
|
117
|
+
if (fs.existsSync(codexDir)) {
|
|
118
|
+
const codexSkillDir = path.join(codexDir, 'skills');
|
|
119
|
+
const codexSkillPath = path.join(codexSkillDir, 'tab-agent.md');
|
|
120
|
+
fs.mkdirSync(codexSkillDir, { recursive: true });
|
|
121
|
+
|
|
122
|
+
if (fs.existsSync(codexSkillPath)) {
|
|
123
|
+
console.log(` Updating existing skill at ${codexSkillPath}`);
|
|
124
|
+
}
|
|
125
|
+
fs.copyFileSync(
|
|
126
|
+
path.join(skillSource, 'codex', 'tab-agent.md'),
|
|
127
|
+
codexSkillPath
|
|
128
|
+
);
|
|
129
|
+
console.log('✓ Installed Codex skill');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
setup().catch(console.error);
|
package/cli/start.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// cli/start.js
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
|
|
5
|
+
const serverPath = path.join(__dirname, '..', 'relay', 'server.js');
|
|
6
|
+
const server = spawn('node', [serverPath], {
|
|
7
|
+
stdio: 'inherit',
|
|
8
|
+
detached: false
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
server.on('error', (err) => {
|
|
12
|
+
console.error('Failed to start server:', err.message);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
process.on('SIGINT', () => {
|
|
17
|
+
server.kill();
|
|
18
|
+
process.exit(0);
|
|
19
|
+
});
|
package/cli/status.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// cli/status.js
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const http = require('http');
|
|
6
|
+
|
|
7
|
+
function checkRelayServer() {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const req = http.get('http://localhost:9876/health', (res) => {
|
|
10
|
+
let data = '';
|
|
11
|
+
res.on('data', chunk => data += chunk);
|
|
12
|
+
res.on('end', () => {
|
|
13
|
+
try {
|
|
14
|
+
const json = JSON.parse(data);
|
|
15
|
+
resolve({ running: true, clients: json.clients });
|
|
16
|
+
} catch {
|
|
17
|
+
resolve({ running: false });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
req.on('error', () => resolve({ running: false }));
|
|
22
|
+
req.setTimeout(2000, () => {
|
|
23
|
+
req.destroy();
|
|
24
|
+
resolve({ running: false });
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function status() {
|
|
30
|
+
console.log('Tab Agent Status\n');
|
|
31
|
+
|
|
32
|
+
// Check native host
|
|
33
|
+
const home = os.homedir();
|
|
34
|
+
const platform = os.platform();
|
|
35
|
+
let manifestPath;
|
|
36
|
+
|
|
37
|
+
if (platform === 'darwin') {
|
|
38
|
+
manifestPath = path.join(home, 'Library/Application Support/Google/Chrome/NativeMessagingHosts/com.tabagent.relay.json');
|
|
39
|
+
} else if (platform === 'linux') {
|
|
40
|
+
manifestPath = path.join(home, '.config/google-chrome/NativeMessagingHosts/com.tabagent.relay.json');
|
|
41
|
+
} else if (platform === 'win32') {
|
|
42
|
+
manifestPath = path.join(home, 'AppData/Local/Google/Chrome/User Data/NativeMessagingHosts/com.tabagent.relay.json');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (fs.existsSync(manifestPath)) {
|
|
46
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
47
|
+
console.log('Native Host: Installed');
|
|
48
|
+
console.log(` Extension: ${manifest.allowed_origins[0]}`);
|
|
49
|
+
} else {
|
|
50
|
+
console.log('Native Host: Not installed (run: npx tab-agent setup)');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check skills
|
|
54
|
+
const claudeSkill = path.join(home, '.claude', 'skills', 'tab-agent.md');
|
|
55
|
+
const codexSkill = path.join(home, '.codex', 'skills', 'tab-agent.md');
|
|
56
|
+
|
|
57
|
+
console.log(`\nClaude Skill: ${fs.existsSync(claudeSkill) ? 'Installed' : 'Not installed'} ${claudeSkill}`);
|
|
58
|
+
console.log(`Codex Skill: ${fs.existsSync(codexSkill) ? 'Installed' : 'Not installed (optional)'} ${codexSkill}`);
|
|
59
|
+
|
|
60
|
+
// Check relay server
|
|
61
|
+
console.log('\nRelay Server:');
|
|
62
|
+
const relayStatus = await checkRelayServer();
|
|
63
|
+
if (relayStatus.running) {
|
|
64
|
+
console.log(` Status: Running (${relayStatus.clients} client${relayStatus.clients !== 1 ? 's' : ''})`);
|
|
65
|
+
} else {
|
|
66
|
+
console.log(' Status: Not running (starts automatically when needed)');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
status().catch(console.error);
|