@axiomatic-labs/claudeflow 2.0.25 → 2.0.27
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/bin/cli.js +11 -0
- package/lib/auth.js +125 -11
- package/lib/init.js +1 -1
- package/lib/update.js +1 -1
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -12,6 +12,16 @@ switch (command) {
|
|
|
12
12
|
case 'version':
|
|
13
13
|
require('../lib/version.js')();
|
|
14
14
|
break;
|
|
15
|
+
case 'logout': {
|
|
16
|
+
const ui = require('../lib/ui.js');
|
|
17
|
+
const { clearCachedToken } = require('../lib/auth.js');
|
|
18
|
+
if (clearCachedToken()) {
|
|
19
|
+
ui.success('Token removed from ~/.claudeflow/config.json');
|
|
20
|
+
} else {
|
|
21
|
+
ui.info('No cached token found.');
|
|
22
|
+
}
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
15
25
|
case '--version':
|
|
16
26
|
case '-v':
|
|
17
27
|
console.log(`claudeflow v${require('../package.json').version}`);
|
|
@@ -25,6 +35,7 @@ switch (command) {
|
|
|
25
35
|
console.log(` claudeflow ${ui.CYAN}init${ui.RESET} Install the latest template files`);
|
|
26
36
|
console.log(` claudeflow ${ui.CYAN}update${ui.RESET} Update templates to latest version`);
|
|
27
37
|
console.log(` claudeflow ${ui.CYAN}version${ui.RESET} Show version info`);
|
|
38
|
+
console.log(` claudeflow ${ui.CYAN}logout${ui.RESET} Remove cached GitHub token`);
|
|
28
39
|
console.log('');
|
|
29
40
|
console.log(` ${ui.BOLD}Options:${ui.RESET}`);
|
|
30
41
|
console.log(` --version, -v Show CLI version`);
|
package/lib/auth.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
const { execSync } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const readline = require('readline');
|
|
5
|
+
const https = require('https');
|
|
2
6
|
|
|
3
7
|
const VALID_TOKEN_PATTERNS = [
|
|
4
8
|
/^ghp_[A-Za-z0-9_]+$/, // Personal access token
|
|
@@ -9,6 +13,9 @@ const VALID_TOKEN_PATTERNS = [
|
|
|
9
13
|
/^[0-9a-f]{40}$/, // Classic 40-char hex token
|
|
10
14
|
];
|
|
11
15
|
|
|
16
|
+
const CONFIG_DIR = path.join(require('os').homedir(), '.claudeflow');
|
|
17
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
|
|
18
|
+
|
|
12
19
|
function sanitizeToken(raw) {
|
|
13
20
|
if (!raw || typeof raw !== 'string') return null;
|
|
14
21
|
const clean = raw.replace(/[\x00-\x1F\x7F]/g, '').trim();
|
|
@@ -17,6 +24,85 @@ function sanitizeToken(raw) {
|
|
|
17
24
|
return clean;
|
|
18
25
|
}
|
|
19
26
|
|
|
27
|
+
function readCachedToken() {
|
|
28
|
+
try {
|
|
29
|
+
const data = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
30
|
+
return sanitizeToken(data.githubToken);
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function cacheToken(token) {
|
|
37
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
38
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify({ githubToken: token }, null, 2) + '\n', {
|
|
39
|
+
mode: 0o600,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function clearCachedToken() {
|
|
44
|
+
try {
|
|
45
|
+
fs.unlinkSync(CONFIG_PATH);
|
|
46
|
+
return true;
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function validateToken(token) {
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
const req = https.get(
|
|
55
|
+
'https://api.github.com/repos/axiomatic-labs/axiomatic-cli',
|
|
56
|
+
{
|
|
57
|
+
headers: {
|
|
58
|
+
Authorization: `token ${token}`,
|
|
59
|
+
'User-Agent': 'claudeflow-cli',
|
|
60
|
+
Accept: 'application/vnd.github.v3+json',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
(res) => {
|
|
64
|
+
// Drain the response body so the socket can close
|
|
65
|
+
res.resume();
|
|
66
|
+
resolve(res.statusCode === 200);
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
req.on('error', () => resolve(false));
|
|
70
|
+
req.setTimeout(10000, () => {
|
|
71
|
+
req.destroy();
|
|
72
|
+
resolve(false);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function promptForToken() {
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
const ui = require('./ui.js');
|
|
80
|
+
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(` ${ui.YELLOW}No GitHub token found.${ui.RESET}`);
|
|
83
|
+
console.log('');
|
|
84
|
+
console.log(` To use Claudeflow, you need a GitHub Personal Access Token`);
|
|
85
|
+
console.log(` with access to the ${ui.BOLD}axiomatic-labs/axiomatic-cli${ui.RESET} repository.`);
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log(` Create one at: ${ui.CYAN}https://github.com/settings/tokens/new${ui.RESET}`);
|
|
88
|
+
console.log(` Required scope: ${ui.BOLD}repo${ui.RESET} (Full control of private repositories)`);
|
|
89
|
+
console.log('');
|
|
90
|
+
|
|
91
|
+
const rl = readline.createInterface({
|
|
92
|
+
input: process.stdin,
|
|
93
|
+
output: process.stdout,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
rl.question(` Paste your token: `, (answer) => {
|
|
97
|
+
rl.close();
|
|
98
|
+
resolve(answer || null);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Handle Ctrl+C / closed stream
|
|
102
|
+
rl.on('close', () => resolve(null));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
20
106
|
function getGitHubToken() {
|
|
21
107
|
// Try gh CLI first
|
|
22
108
|
try {
|
|
@@ -31,21 +117,49 @@ function getGitHubToken() {
|
|
|
31
117
|
if (token) return token;
|
|
32
118
|
}
|
|
33
119
|
|
|
120
|
+
// Try cached token
|
|
121
|
+
const cached = readCachedToken();
|
|
122
|
+
if (cached) return cached;
|
|
123
|
+
|
|
34
124
|
return null;
|
|
35
125
|
}
|
|
36
126
|
|
|
37
|
-
function requireAuth() {
|
|
127
|
+
async function requireAuth() {
|
|
128
|
+
const ui = require('./ui.js');
|
|
129
|
+
|
|
130
|
+
// Fast path: gh CLI, env var, or cached token
|
|
38
131
|
const token = getGitHubToken();
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
132
|
+
if (token) return token;
|
|
133
|
+
|
|
134
|
+
// Interactive prompt with validation loop
|
|
135
|
+
for (let attempts = 0; attempts < 3; attempts++) {
|
|
136
|
+
const raw = await promptForToken();
|
|
137
|
+
if (!raw) {
|
|
138
|
+
// User cancelled (Ctrl+C or empty input)
|
|
139
|
+
ui.error('Authentication cancelled.');
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const token = sanitizeToken(raw);
|
|
144
|
+
if (!token) {
|
|
145
|
+
ui.error('Invalid token format. Expected a GitHub PAT (ghp_..., github_pat_..., etc.).');
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
ui.step('Validating token...');
|
|
150
|
+
const valid = await validateToken(token);
|
|
151
|
+
if (!valid) {
|
|
152
|
+
ui.error('Token invalid or no access to the repository.');
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
cacheToken(token);
|
|
157
|
+
ui.success(`Token saved to ~/.claudeflow/config.json`);
|
|
158
|
+
return token;
|
|
47
159
|
}
|
|
48
|
-
|
|
160
|
+
|
|
161
|
+
ui.error('Too many failed attempts.');
|
|
162
|
+
process.exit(1);
|
|
49
163
|
}
|
|
50
164
|
|
|
51
|
-
module.exports = { getGitHubToken, requireAuth };
|
|
165
|
+
module.exports = { getGitHubToken, requireAuth, clearCachedToken };
|
package/lib/init.js
CHANGED
package/lib/update.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiomatic-labs/claudeflow",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.27",
|
|
4
4
|
"description": "Claudeflow — AI-powered development toolkit for Claude Code. Skills, agents, hooks, and quality gates that ship production apps.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"claudeflow": "./bin/cli.js"
|