bgit-cli 2.0.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/.claude/settings.local.json +19 -0
- package/.env.local +2 -0
- package/CLAUDE_PLAN.md +621 -0
- package/IMPLEMENTATION_REPORT.md +1690 -0
- package/README.md +277 -0
- package/UNIVERSAL_PLAN.md +31 -0
- package/handcash.js +36 -0
- package/index.js +158 -0
- package/index.js.backup +69 -0
- package/lib/auth.js +273 -0
- package/lib/banner.js +17 -0
- package/lib/command-router.js +191 -0
- package/lib/commands.js +157 -0
- package/lib/config.js +438 -0
- package/lib/constants.js +57 -0
- package/lib/crypto.js +164 -0
- package/lib/oauth-server.js +300 -0
- package/lib/payment.js +287 -0
- package/lib/token-manager.js +179 -0
- package/package.json +45 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bgit Token Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages HandCash auth token lifecycle including validation and caching.
|
|
5
|
+
* Validates tokens against HandCash API with 1-hour cache to minimize API calls.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Token validation via HandCash API
|
|
9
|
+
* - 1-hour validation cache (reduces API calls)
|
|
10
|
+
* - Automatic re-auth trigger on invalid/expired tokens
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { HandCashConnect } = require('@handcash/handcash-connect');
|
|
14
|
+
const { HANDCASH_APP_ID, HANDCASH_APP_SECRET, TOKEN_VALIDATION_CACHE_TTL_MS } = require('./constants');
|
|
15
|
+
|
|
16
|
+
// Validation cache: { token: { valid: boolean, timestamp: number } }
|
|
17
|
+
const validationCache = new Map();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if cached validation is still valid (within TTL)
|
|
21
|
+
*
|
|
22
|
+
* @param {string} authToken - Token to check cache for
|
|
23
|
+
* @returns {boolean|null} true if valid, false if invalid, null if no cache
|
|
24
|
+
*/
|
|
25
|
+
function getCachedValidation(authToken) {
|
|
26
|
+
if (!validationCache.has(authToken)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const cached = validationCache.get(authToken);
|
|
31
|
+
const age = Date.now() - cached.timestamp;
|
|
32
|
+
|
|
33
|
+
if (age > TOKEN_VALIDATION_CACHE_TTL_MS) {
|
|
34
|
+
// Cache expired
|
|
35
|
+
validationCache.delete(authToken);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return cached.valid;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Cache token validation result
|
|
44
|
+
*
|
|
45
|
+
* @param {string} authToken - Token to cache
|
|
46
|
+
* @param {boolean} valid - Whether token is valid
|
|
47
|
+
*/
|
|
48
|
+
function setCachedValidation(authToken, valid) {
|
|
49
|
+
validationCache.set(authToken, {
|
|
50
|
+
valid,
|
|
51
|
+
timestamp: Date.now()
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Clear validation cache
|
|
57
|
+
* Useful after re-authentication
|
|
58
|
+
*/
|
|
59
|
+
function clearValidationCache() {
|
|
60
|
+
validationCache.clear();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validate auth token against HandCash API
|
|
65
|
+
* Uses cached result if available (1-hour TTL)
|
|
66
|
+
*
|
|
67
|
+
* @param {string} authToken - HandCash auth token to validate
|
|
68
|
+
* @returns {Promise<boolean>} true if valid, false otherwise
|
|
69
|
+
*/
|
|
70
|
+
async function isTokenValid(authToken) {
|
|
71
|
+
if (!authToken || typeof authToken !== 'string') {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check cache first
|
|
76
|
+
const cached = getCachedValidation(authToken);
|
|
77
|
+
if (cached !== null) {
|
|
78
|
+
return cached;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const handCashConnect = new HandCashConnect({
|
|
83
|
+
appId: HANDCASH_APP_ID,
|
|
84
|
+
appSecret: HANDCASH_APP_SECRET
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const account = handCashConnect.getAccountFromAuthToken(authToken);
|
|
88
|
+
|
|
89
|
+
// Try to get profile - this validates the token
|
|
90
|
+
const profile = await account.profile.getCurrentProfile();
|
|
91
|
+
|
|
92
|
+
if (profile && profile.handle) {
|
|
93
|
+
// Token is valid
|
|
94
|
+
setCachedValidation(authToken, true);
|
|
95
|
+
return true;
|
|
96
|
+
} else {
|
|
97
|
+
// Unexpected response
|
|
98
|
+
setCachedValidation(authToken, false);
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// Token is invalid or API error
|
|
103
|
+
console.error(`Token validation failed: ${error.message}`);
|
|
104
|
+
setCachedValidation(authToken, false);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get HandCash account profile
|
|
111
|
+
* Used to display auth status
|
|
112
|
+
*
|
|
113
|
+
* @param {string} authToken - HandCash auth token
|
|
114
|
+
* @returns {Promise<Object|null>} Profile object or null if error
|
|
115
|
+
*/
|
|
116
|
+
async function getProfile(authToken) {
|
|
117
|
+
try {
|
|
118
|
+
const handCashConnect = new HandCashConnect({
|
|
119
|
+
appId: HANDCASH_APP_ID,
|
|
120
|
+
appSecret: HANDCASH_APP_SECRET
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const account = handCashConnect.getAccountFromAuthToken(authToken);
|
|
124
|
+
const profile = await account.profile.getCurrentProfile();
|
|
125
|
+
|
|
126
|
+
return profile;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(`Failed to get profile: ${error.message}`);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get HandCash account balance
|
|
135
|
+
* Used to check if user has sufficient funds
|
|
136
|
+
*
|
|
137
|
+
* @param {string} authToken - HandCash auth token
|
|
138
|
+
* @returns {Promise<Object|null>} Balance object or null if error
|
|
139
|
+
*/
|
|
140
|
+
async function getBalance(authToken) {
|
|
141
|
+
try {
|
|
142
|
+
const handCashConnect = new HandCashConnect({
|
|
143
|
+
appId: HANDCASH_APP_ID,
|
|
144
|
+
appSecret: HANDCASH_APP_SECRET
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const account = handCashConnect.getAccountFromAuthToken(authToken);
|
|
148
|
+
const spendableBalance = await account.wallet.getSpendableBalance();
|
|
149
|
+
|
|
150
|
+
return spendableBalance;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(`Failed to get balance: ${error.message}`);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Check if token should be refreshed
|
|
159
|
+
* HandCash tokens don't expire, but this provides a hook for future refresh logic
|
|
160
|
+
*
|
|
161
|
+
* @param {string} authToken - HandCash auth token
|
|
162
|
+
* @returns {Promise<boolean>} true if token should be refreshed
|
|
163
|
+
*/
|
|
164
|
+
async function shouldRefresh(authToken) {
|
|
165
|
+
// HandCash Connect SDK doesn't currently support token refresh
|
|
166
|
+
// Tokens are long-lived and don't expire
|
|
167
|
+
// This function is a placeholder for future enhancement
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = {
|
|
172
|
+
isTokenValid,
|
|
173
|
+
getProfile,
|
|
174
|
+
getBalance,
|
|
175
|
+
shouldRefresh,
|
|
176
|
+
clearValidationCache,
|
|
177
|
+
getCachedValidation,
|
|
178
|
+
setCachedValidation
|
|
179
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bgit-cli",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Bitcoin-enabled Git wrapper with HandCash OAuth - Timestamp your commits on BitcoinSV",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bgit": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"preferGlobal": true,
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=16.0.0"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
15
|
+
"start": "node index.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"bitcoin",
|
|
19
|
+
"git",
|
|
20
|
+
"bsv",
|
|
21
|
+
"bitcoinsv",
|
|
22
|
+
"handcash",
|
|
23
|
+
"blockchain",
|
|
24
|
+
"timestamp",
|
|
25
|
+
"commit",
|
|
26
|
+
"version-control"
|
|
27
|
+
],
|
|
28
|
+
"author": "b0ase",
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"type": "commonjs",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@handcash/handcash-connect": "^0.8.11",
|
|
33
|
+
"chalk": "^4.1.2",
|
|
34
|
+
"express": "^4.18.2",
|
|
35
|
+
"open": "^8.4.2"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/yourusername/bgit.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/yourusername/bgit/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/yourusername/bgit#readme"
|
|
45
|
+
}
|