augteam 1.1.2
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/SETUP.md +62 -0
- package/bin/auggw.js +2 -0
- package/bin/augteam.js +3 -0
- package/bin/icbauggw.js +3 -0
- package/package.json +25 -0
- package/publish.js +69 -0
- package/src/commands/fetch.js +145 -0
- package/src/commands/login.js +62 -0
- package/src/commands/logout.js +8 -0
- package/src/commands/status.js +57 -0
- package/src/commands/switch.js +44 -0
- package/src/commands/update.js +33 -0
- package/src/index.js +50 -0
- package/src/utils/api.js +74 -0
- package/src/utils/auth.js +11 -0
- package/src/utils/paths.js +12 -0
- package/src/utils/token.js +30 -0
- package/src/utils/update.js +24 -0
package/SETUP.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Hướng dẫn cài đặt CLI - Augment Session Rotator
|
|
2
|
+
|
|
3
|
+
## Yêu cầu
|
|
4
|
+
|
|
5
|
+
- Node.js >= 18
|
|
6
|
+
|
|
7
|
+
## Cài đặt
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @itd2902/auggw
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Chạy từ source (local dev)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd cli
|
|
17
|
+
npm link
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Sau đó dùng `auggw` như bình thường. Mọi thay đổi trong source sẽ có hiệu lực ngay.
|
|
21
|
+
|
|
22
|
+
Gỡ link khi không cần:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd cli
|
|
26
|
+
npm unlink -g
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Đăng nhập
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
auggw login
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Nhập username và password được cấp. Token sẽ lưu tại `~/gg/token`.
|
|
36
|
+
|
|
37
|
+
## Sử dụng
|
|
38
|
+
|
|
39
|
+
### Chuyển session
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
auggw switch
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Lệnh này lấy account tiếp theo từ pool và ghi vào `~/.augment/session.json`. Chạy lại mỗi khi cần đổi account.
|
|
46
|
+
|
|
47
|
+
### Xem trạng thái
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
auggw status
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Hiển thị account đang dùng, credit usage và số lượng account trong pool.
|
|
54
|
+
|
|
55
|
+
## Lưu ý
|
|
56
|
+
|
|
57
|
+
- Sau khi `switch`, restart lại Augment extension để nó đọc session mới.
|
|
58
|
+
- Nếu gặp lỗi `Session expired`, chạy lại `auggw login`.
|
|
59
|
+
- Nếu cần trỏ sang server khác (dev/test), set env:
|
|
60
|
+
```bash
|
|
61
|
+
auggw_API_URL=http://localhost:3000 auggw login
|
|
62
|
+
```
|
package/bin/auggw.js
ADDED
package/bin/augteam.js
ADDED
package/bin/icbauggw.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "augteam",
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "CLI tool for rotating Augment session accounts",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"augteam": "./bin/augteam.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"augment",
|
|
12
|
+
"session",
|
|
13
|
+
"rotate",
|
|
14
|
+
"cli"
|
|
15
|
+
],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18.0.0"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"commander": "^13.1.0",
|
|
23
|
+
"dotenv": "^17.3.1"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/publish.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
const CLI_DIR = path.join(__dirname);
|
|
7
|
+
const PKG_PATH = path.join(CLI_DIR, 'package.json');
|
|
8
|
+
const API_PATH = path.join(CLI_DIR, 'src', 'utils', 'api.js');
|
|
9
|
+
|
|
10
|
+
const packages = [
|
|
11
|
+
{
|
|
12
|
+
name: '@itd2902/auggw',
|
|
13
|
+
bin: { auggw: './bin/auggw.js' },
|
|
14
|
+
access: '--access public',
|
|
15
|
+
apiUrl: 'https://auggw.quangit.site',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'icbauggw',
|
|
19
|
+
bin: { icbauggw: './bin/icbauggw.js' },
|
|
20
|
+
access: '',
|
|
21
|
+
apiUrl: 'https://icbauggw.quangit.site',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'augteam',
|
|
25
|
+
bin: { augteam: './bin/augteam.js' },
|
|
26
|
+
access: '',
|
|
27
|
+
apiUrl: 'https://augteam.quangit.site',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const version = process.argv[2];
|
|
32
|
+
if (!version) {
|
|
33
|
+
console.error('Usage: node publish.js <version>');
|
|
34
|
+
console.error('Example: node publish.js 1.0.2');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const originalPkg = fs.readFileSync(PKG_PATH, 'utf8');
|
|
39
|
+
const originalApi = fs.readFileSync(API_PATH, 'utf8');
|
|
40
|
+
|
|
41
|
+
for (const pkg of packages) {
|
|
42
|
+
const json = JSON.parse(originalPkg);
|
|
43
|
+
json.name = pkg.name;
|
|
44
|
+
json.version = version;
|
|
45
|
+
json.bin = pkg.bin;
|
|
46
|
+
|
|
47
|
+
fs.writeFileSync(PKG_PATH, JSON.stringify(json, null, 2) + '\n');
|
|
48
|
+
|
|
49
|
+
// Swap DEFAULT_API_URL
|
|
50
|
+
const apiContent = originalApi.replace(
|
|
51
|
+
/const DEFAULT_API_URL = ".*?";/,
|
|
52
|
+
`const DEFAULT_API_URL = "${pkg.apiUrl}";`
|
|
53
|
+
);
|
|
54
|
+
fs.writeFileSync(API_PATH, apiContent);
|
|
55
|
+
|
|
56
|
+
console.log(`\nPublishing ${pkg.name}@${version} → ${pkg.apiUrl}`);
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
execSync(`npm publish ${pkg.access}`.trim(), { cwd: CLI_DIR, stdio: 'inherit' });
|
|
60
|
+
console.log(`${pkg.name}@${version} published.`);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.error(`Failed to publish ${pkg.name}:`, err.message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Restore originals
|
|
67
|
+
fs.writeFileSync(PKG_PATH, originalPkg);
|
|
68
|
+
fs.writeFileSync(API_PATH, originalApi);
|
|
69
|
+
console.log('\nFiles restored.');
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const readline = require("readline");
|
|
5
|
+
const { apiRequest } = require("../utils/api");
|
|
6
|
+
const { requireAuth } = require("../utils/auth");
|
|
7
|
+
|
|
8
|
+
function ask(question) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
const rl = readline.createInterface({
|
|
11
|
+
input: process.stdin,
|
|
12
|
+
output: process.stdout,
|
|
13
|
+
});
|
|
14
|
+
rl.question(question, (answer) => {
|
|
15
|
+
rl.close();
|
|
16
|
+
resolve(answer.trim());
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getAugmentDir() {
|
|
22
|
+
return path.join(os.homedir(), ".augment");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getClaudeDir() {
|
|
26
|
+
return path.join(os.homedir(), ".claude");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeFile(filePath, content) {
|
|
30
|
+
const dir = path.dirname(filePath);
|
|
31
|
+
if (!fs.existsSync(dir)) {
|
|
32
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function installToAugment(data) {
|
|
38
|
+
const baseDir = getAugmentDir();
|
|
39
|
+
let count = 0;
|
|
40
|
+
|
|
41
|
+
for (const cmd of data.commands) {
|
|
42
|
+
writeFile(path.join(baseDir, "commands", cmd.name), cmd.content);
|
|
43
|
+
count++;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const skill of data.skills) {
|
|
47
|
+
writeFile(path.join(baseDir, "skills", skill.name, "SKILL.md"), skill.content);
|
|
48
|
+
count++;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (const agent of data.agents || []) {
|
|
52
|
+
writeFile(path.join(baseDir, "agents", agent.name), agent.content);
|
|
53
|
+
count++;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return count;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function installToClaude(data) {
|
|
60
|
+
const baseDir = getClaudeDir();
|
|
61
|
+
let count = 0;
|
|
62
|
+
|
|
63
|
+
for (const cmd of data.commands) {
|
|
64
|
+
writeFile(path.join(baseDir, "commands", cmd.name), cmd.content);
|
|
65
|
+
count++;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const skill of data.skills) {
|
|
69
|
+
writeFile(path.join(baseDir, "commands", skill.name, "SKILL.md"), skill.content);
|
|
70
|
+
count++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const agent of data.agents || []) {
|
|
74
|
+
writeFile(path.join(baseDir, "agents", agent.name), agent.content);
|
|
75
|
+
count++;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return count;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function fetchPrompts() {
|
|
82
|
+
console.log("📦 Fetch prompts (commands & skills)\n");
|
|
83
|
+
console.log(" 1) Augment only (~/.augment/)");
|
|
84
|
+
console.log(" 2) Claude only (~/.claude/)");
|
|
85
|
+
console.log(" 3) Both\n");
|
|
86
|
+
|
|
87
|
+
const choice = await ask("Chọn (1/2/3): ");
|
|
88
|
+
|
|
89
|
+
if (!["1", "2", "3"].includes(choice)) {
|
|
90
|
+
console.error("❌ Lựa chọn không hợp lệ. Vui lòng chọn 1, 2 hoặc 3.");
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log("\n⏳ Fetching prompts from server...");
|
|
95
|
+
const data = await apiRequest("GET", "/api/prompts");
|
|
96
|
+
|
|
97
|
+
const totalItems = data.commands.length + data.skills.length + (data.agents || []).length;
|
|
98
|
+
if (totalItems === 0) {
|
|
99
|
+
console.log("ℹ️ Không có prompts nào trên server.");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log(
|
|
104
|
+
` Found: ${data.commands.length} command(s), ${data.skills.length} skill(s), ${(data.agents || []).length} agent(s)\n`,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (choice === "1" || choice === "3") {
|
|
108
|
+
const count = installToAugment(data);
|
|
109
|
+
console.log(`✅ Augment: ${count} file(s) → ${getAugmentDir()}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (choice === "2" || choice === "3") {
|
|
113
|
+
const count = installToClaude(data);
|
|
114
|
+
console.log(`✅ Claude: ${count} file(s) → ${getClaudeDir()}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log("\n🎉 Done!");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function fetchCommand(options) {
|
|
121
|
+
requireAuth();
|
|
122
|
+
|
|
123
|
+
if (!options.prompts) {
|
|
124
|
+
console.log("Usage: auggw fetch --prompts");
|
|
125
|
+
console.log("\nOptions:");
|
|
126
|
+
console.log(" --prompts Fetch commands & skills to ~/.augment and/or ~/.claude");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
if (options.prompts) {
|
|
132
|
+
await fetchPrompts();
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (err.status === 401) {
|
|
136
|
+
console.error("❌ Session expired. Please login again: auggw login");
|
|
137
|
+
} else {
|
|
138
|
+
console.error(`❌ Error: ${err.message}`);
|
|
139
|
+
}
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = fetchCommand;
|
|
145
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const { apiRequest } = require('../utils/api');
|
|
3
|
+
const { saveToken } = require('../utils/token');
|
|
4
|
+
|
|
5
|
+
function prompt(question, hidden = false) {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
const rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (hidden) {
|
|
13
|
+
// Mask password input
|
|
14
|
+
const stdin = process.stdin;
|
|
15
|
+
const onData = (char) => {
|
|
16
|
+
char = char.toString();
|
|
17
|
+
if (char === '\n' || char === '\r' || char === '\u0004') return;
|
|
18
|
+
process.stdout.clearLine(0);
|
|
19
|
+
process.stdout.cursorTo(0);
|
|
20
|
+
process.stdout.write(question + '*'.repeat(rl.line.length));
|
|
21
|
+
};
|
|
22
|
+
stdin.on('data', onData);
|
|
23
|
+
rl.question(question, (answer) => {
|
|
24
|
+
stdin.removeListener('data', onData);
|
|
25
|
+
rl.close();
|
|
26
|
+
console.log();
|
|
27
|
+
resolve(answer);
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
rl.question(question, (answer) => {
|
|
31
|
+
rl.close();
|
|
32
|
+
resolve(answer);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function loginCommand() {
|
|
39
|
+
try {
|
|
40
|
+
const username = await prompt('Username: ');
|
|
41
|
+
const password = await prompt('Password: ', true);
|
|
42
|
+
|
|
43
|
+
if (!username || !password) {
|
|
44
|
+
console.error('❌ Username and password are required');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const data = await apiRequest('POST', '/api/auth/login', { username, password });
|
|
49
|
+
saveToken(data.token);
|
|
50
|
+
console.log('✅ Logged in successfully! Token saved.');
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err.status === 401) {
|
|
53
|
+
console.error('❌ Login failed: Invalid credentials');
|
|
54
|
+
} else {
|
|
55
|
+
console.error(`❌ Error: ${err.message}`);
|
|
56
|
+
}
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = loginCommand;
|
|
62
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { apiRequest } = require("../utils/api");
|
|
2
|
+
const { requireAuth } = require("../utils/auth");
|
|
3
|
+
|
|
4
|
+
async function statusCommand() {
|
|
5
|
+
requireAuth();
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const data = await apiRequest("GET", "/api/session/status");
|
|
9
|
+
|
|
10
|
+
if (!data.current) {
|
|
11
|
+
console.log("ℹ️ No active session. Run: auggw switch");
|
|
12
|
+
console.log();
|
|
13
|
+
} else {
|
|
14
|
+
console.log("📋 Current Account");
|
|
15
|
+
console.log(` Name: ${data.current.name}`);
|
|
16
|
+
console.log(` Token: ...${data.current.accessToken}`);
|
|
17
|
+
console.log(
|
|
18
|
+
` Status: ${data.current.isLimited ? "🔴 Limited" : "🟢 Active"}`,
|
|
19
|
+
);
|
|
20
|
+
if (data.current.creditTotal) {
|
|
21
|
+
const remaining = data.current.creditRemaining || 0;
|
|
22
|
+
const total = data.current.creditTotal;
|
|
23
|
+
const used = total - remaining;
|
|
24
|
+
const pct = Math.round((used / total) * 100);
|
|
25
|
+
const barWidth = 30;
|
|
26
|
+
const filled = Math.round((pct / 100) * barWidth);
|
|
27
|
+
const empty = barWidth - filled;
|
|
28
|
+
const color = pct > 90 ? "\x1b[31m" : pct > 70 ? "\x1b[33m" : "\x1b[32m";
|
|
29
|
+
const reset = "\x1b[0m";
|
|
30
|
+
const bar = `${color}${"█".repeat(filled)}${"\x1b[90m"}${"░".repeat(empty)}${reset}`;
|
|
31
|
+
console.log(
|
|
32
|
+
` Credit: ${remaining.toLocaleString()} / ${total.toLocaleString()} remaining`,
|
|
33
|
+
);
|
|
34
|
+
console.log(
|
|
35
|
+
` ${bar} ${color}${pct}% used${reset}`,
|
|
36
|
+
);
|
|
37
|
+
} else {
|
|
38
|
+
console.log(` Credit: \x1b[90mN/A — account chưa login trên admin panel\x1b[0m`);
|
|
39
|
+
}
|
|
40
|
+
console.log();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log("📊 Pool Status");
|
|
44
|
+
console.log(` Active: ${data.pool.active} accounts`);
|
|
45
|
+
console.log(` Limited: ${data.pool.limited} accounts`);
|
|
46
|
+
console.log(` Total: ${data.pool.total} accounts`);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err.status === 401) {
|
|
49
|
+
console.error("❌ Session expired. Please login again: auggw login");
|
|
50
|
+
} else {
|
|
51
|
+
console.error(`❌ Error: ${err.message}`);
|
|
52
|
+
}
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = statusCommand;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { apiRequest } = require("../utils/api");
|
|
4
|
+
const { requireAuth } = require("../utils/auth");
|
|
5
|
+
const { getSessionPath } = require("../utils/paths");
|
|
6
|
+
|
|
7
|
+
async function switchCommand() {
|
|
8
|
+
requireAuth();
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const data = await apiRequest("GET", "/api/session/next");
|
|
12
|
+
|
|
13
|
+
const sessionPath = getSessionPath();
|
|
14
|
+
const sessionDir = path.dirname(sessionPath);
|
|
15
|
+
|
|
16
|
+
// Create ~/.augment/ directory if it doesn't exist
|
|
17
|
+
if (!fs.existsSync(sessionDir)) {
|
|
18
|
+
fs.mkdirSync(sessionDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Write session file
|
|
22
|
+
const session = {
|
|
23
|
+
accessToken: data.accessToken,
|
|
24
|
+
tenantURL: data.tenantURL,
|
|
25
|
+
scopes: data.scopes,
|
|
26
|
+
};
|
|
27
|
+
fs.writeFileSync(sessionPath, JSON.stringify(session, null, 2), "utf8");
|
|
28
|
+
|
|
29
|
+
console.log("✅ Session switched!");
|
|
30
|
+
console.log(` Account: ${data.accountName}`);
|
|
31
|
+
console.log(` Tenant: ${data.tenantURL}`);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
if (err.status === 401) {
|
|
34
|
+
console.error("❌ Session expired. Please login again: auggw login");
|
|
35
|
+
} else if (err.status === 503) {
|
|
36
|
+
console.error(`❌ ${err.message}`);
|
|
37
|
+
} else {
|
|
38
|
+
console.error(`❌ Error: ${err.message}`);
|
|
39
|
+
}
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = switchCommand;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const pkg = require("../../package.json");
|
|
3
|
+
|
|
4
|
+
async function updateCommand() {
|
|
5
|
+
console.log(`Current version: ${pkg.version}`);
|
|
6
|
+
console.log("Checking for updates...\n");
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const resp = await fetch(`https://registry.npmjs.org/${pkg.name}/latest`, {
|
|
10
|
+
signal: AbortSignal.timeout(5000),
|
|
11
|
+
});
|
|
12
|
+
if (!resp.ok) {
|
|
13
|
+
console.error("❌ Failed to check for updates.");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const data = await resp.json();
|
|
17
|
+
|
|
18
|
+
if (!data.version || data.version === pkg.version) {
|
|
19
|
+
console.log("✅ Already on the latest version.");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(`⬆ Updating: ${pkg.version} → ${data.version}\n`);
|
|
24
|
+
const cmd = `npm i -g ${pkg.name}@latest`;
|
|
25
|
+
execSync(cmd, { stdio: "inherit" });
|
|
26
|
+
console.log(`\n✅ Updated to ${data.version}`);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error(`❌ Update failed: ${err.message}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = updateCommand;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const { Command } = require('commander');
|
|
2
|
+
const loginCommand = require('./commands/login');
|
|
3
|
+
const logoutCommand = require('./commands/logout');
|
|
4
|
+
const switchCommand = require('./commands/switch');
|
|
5
|
+
const statusCommand = require('./commands/status');
|
|
6
|
+
const updateCommand = require('./commands/update');
|
|
7
|
+
const fetchCommand = require('./commands/fetch');
|
|
8
|
+
const { checkUpdate } = require('./utils/update');
|
|
9
|
+
const pkg = require('../package.json');
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name(Object.keys(pkg.bin)[0] || 'auggw')
|
|
15
|
+
.description('CLI tool for rotating Augment session accounts')
|
|
16
|
+
.version(pkg.version);
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command('login')
|
|
20
|
+
.description('Authenticate with the session rotator backend')
|
|
21
|
+
.action(loginCommand);
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command('logout')
|
|
25
|
+
.description('Remove saved token and log out')
|
|
26
|
+
.action(logoutCommand);
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command('switch')
|
|
30
|
+
.description('Switch to the next available Augment session')
|
|
31
|
+
.action(switchCommand);
|
|
32
|
+
|
|
33
|
+
program
|
|
34
|
+
.command('status')
|
|
35
|
+
.description('View current account and pool status')
|
|
36
|
+
.action(statusCommand);
|
|
37
|
+
|
|
38
|
+
program
|
|
39
|
+
.command('update')
|
|
40
|
+
.description('Update CLI to the latest version')
|
|
41
|
+
.action(updateCommand);
|
|
42
|
+
|
|
43
|
+
program
|
|
44
|
+
.command('fetch')
|
|
45
|
+
.description('Fetch resources from server')
|
|
46
|
+
.option('--prompts', 'Fetch commands & skills to ~/.augment and/or ~/.claude')
|
|
47
|
+
.action(fetchCommand);
|
|
48
|
+
|
|
49
|
+
program.parseAsync().then(() => checkUpdate());
|
|
50
|
+
|
package/src/utils/api.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const os = require("os");
|
|
2
|
+
const { loadToken } = require("./token");
|
|
3
|
+
|
|
4
|
+
const DEFAULT_API_URL = "https://augteam.quangit.site";
|
|
5
|
+
// const DEFAULT_API_URL = "http://localhost:3000";
|
|
6
|
+
|
|
7
|
+
function getBaseURL() {
|
|
8
|
+
return process.env.auggw_API_URL || DEFAULT_API_URL;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getLocalIP() {
|
|
12
|
+
const interfaces = os.networkInterfaces();
|
|
13
|
+
// Skip virtual/VPN adapters — prefer real Ethernet/Wi-Fi
|
|
14
|
+
const skipPatterns =
|
|
15
|
+
/tailscale|vpn|virtual|vethernet|bluetooth|loopback|docker|vbox|vmware|wsl/i;
|
|
16
|
+
|
|
17
|
+
// First pass: find IP from real adapters (Ethernet, Wi-Fi)
|
|
18
|
+
for (const [name, addrs] of Object.entries(interfaces)) {
|
|
19
|
+
if (skipPatterns.test(name)) continue;
|
|
20
|
+
for (const iface of addrs) {
|
|
21
|
+
if (iface.family === "IPv4" && !iface.internal) {
|
|
22
|
+
return iface.address;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Fallback: any non-internal IPv4
|
|
27
|
+
for (const addrs of Object.values(interfaces)) {
|
|
28
|
+
for (const iface of addrs) {
|
|
29
|
+
if (iface.family === "IPv4" && !iface.internal) {
|
|
30
|
+
return iface.address;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return "127.0.0.1";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function apiRequest(method, path, body = null) {
|
|
38
|
+
const url = `${getBaseURL()}${path}`;
|
|
39
|
+
const token = loadToken();
|
|
40
|
+
|
|
41
|
+
const headers = {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
"X-Client-IP": getLocalIP(),
|
|
44
|
+
"X-Client-Hostname": os.hostname(),
|
|
45
|
+
};
|
|
46
|
+
if (token) {
|
|
47
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const options = { method, headers };
|
|
51
|
+
if (body) {
|
|
52
|
+
options.body = JSON.stringify(body);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const res = await fetch(url, options);
|
|
57
|
+
const data = await res.json();
|
|
58
|
+
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
const err = new Error(data.error || `HTTP ${res.status}`);
|
|
61
|
+
err.status = res.status;
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return data;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
if (err.cause && err.cause.code === "ECONNREFUSED") {
|
|
68
|
+
throw new Error(`Could not connect to server at ${getBaseURL()}`);
|
|
69
|
+
}
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = { apiRequest, getBaseURL };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const os = require("os");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
function getTokenPath() {
|
|
5
|
+
return path.join(os.homedir(), ".auggw", "token");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function getSessionPath() {
|
|
9
|
+
return path.join(os.homedir(), ".augment", "session.json");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = { getTokenPath, getSessionPath };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { getTokenPath } = require('./paths');
|
|
4
|
+
|
|
5
|
+
function saveToken(token) {
|
|
6
|
+
const tokenPath = getTokenPath();
|
|
7
|
+
const dir = path.dirname(tokenPath);
|
|
8
|
+
if (!fs.existsSync(dir)) {
|
|
9
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
fs.writeFileSync(tokenPath, token, 'utf8');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function loadToken() {
|
|
15
|
+
const tokenPath = getTokenPath();
|
|
16
|
+
if (!fs.existsSync(tokenPath)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return fs.readFileSync(tokenPath, 'utf8').trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function clearToken() {
|
|
23
|
+
const tokenPath = getTokenPath();
|
|
24
|
+
if (fs.existsSync(tokenPath)) {
|
|
25
|
+
fs.unlinkSync(tokenPath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { saveToken, loadToken, clearToken };
|
|
30
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const pkg = require("../../package.json");
|
|
2
|
+
|
|
3
|
+
async function checkUpdate() {
|
|
4
|
+
try {
|
|
5
|
+
const resp = await fetch(`https://registry.npmjs.org/${pkg.name}/latest`, {
|
|
6
|
+
signal: AbortSignal.timeout(3000),
|
|
7
|
+
});
|
|
8
|
+
if (!resp.ok) return;
|
|
9
|
+
const data = await resp.json();
|
|
10
|
+
if (data.version && data.version !== pkg.version) {
|
|
11
|
+
const cmd = pkg.name.startsWith("@")
|
|
12
|
+
? `npm i -g ${pkg.name}`
|
|
13
|
+
: `npm i -g ${pkg.name}`;
|
|
14
|
+
console.log(
|
|
15
|
+
`\n\x1b[33m⬆ Update available: ${pkg.version} → ${data.version}\x1b[0m`,
|
|
16
|
+
);
|
|
17
|
+
console.log(` Run: \x1b[36m${cmd}\x1b[0m\n`);
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
// Silently ignore — no network, timeout, etc.
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { checkUpdate };
|