opencode-copilot-multi-fix 1.1.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/LICENSE +21 -0
- package/README.md +172 -0
- package/bin/cli.js +26 -0
- package/dist/commands/multi-copilot.d.ts +18 -0
- package/dist/commands/multi-copilot.d.ts.map +1 -0
- package/dist/commands/multi-copilot.js +403 -0
- package/dist/commands/multi-copilot.js.map +1 -0
- package/dist/config/writer.d.ts +22 -0
- package/dist/config/writer.d.ts.map +1 -0
- package/dist/config/writer.js +143 -0
- package/dist/config/writer.js.map +1 -0
- package/dist/discovery/accounts.d.ts +24 -0
- package/dist/discovery/accounts.d.ts.map +1 -0
- package/dist/discovery/accounts.js +128 -0
- package/dist/discovery/accounts.js.map +1 -0
- package/dist/discovery/models.d.ts +32 -0
- package/dist/discovery/models.d.ts.map +1 -0
- package/dist/discovery/models.js +124 -0
- package/dist/discovery/models.js.map +1 -0
- package/dist/discovery/username.d.ts +8 -0
- package/dist/discovery/username.d.ts.map +1 -0
- package/dist/discovery/username.js +47 -0
- package/dist/discovery/username.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +16 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +174 -0
- package/dist/provider.js.map +1 -0
- package/dist/storage/auth.d.ts +21 -0
- package/dist/storage/auth.d.ts.map +1 -0
- package/dist/storage/auth.js +130 -0
- package/dist/storage/auth.js.map +1 -0
- package/dist/storage/pool.d.ts +29 -0
- package/dist/storage/pool.d.ts.map +1 -0
- package/dist/storage/pool.js +152 -0
- package/dist/storage/pool.js.map +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +32 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/errors.d.ts +28 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +52 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/jsonc.d.ts +20 -0
- package/dist/utils/jsonc.d.ts.map +1 -0
- package/dist/utils/jsonc.js +91 -0
- package/dist/utils/jsonc.js.map +1 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +53 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +71 -0
- package/scripts/postinstall.js +86 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode GitHub Copilot Multi-Account Plugin
|
|
3
|
+
*
|
|
4
|
+
* Enables using multiple GitHub Copilot accounts simultaneously.
|
|
5
|
+
* Each account's models appear in the model selector with format:
|
|
6
|
+
* copilot-multi/username:model-name
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* - Single auth hook for provider "copilot-multi"
|
|
10
|
+
* - Custom fetch routes requests based on model ID
|
|
11
|
+
* - Account pool stored separately from OpenCode auth
|
|
12
|
+
* - Lazy loading - no blocking init
|
|
13
|
+
*
|
|
14
|
+
* Management:
|
|
15
|
+
* - Use CLI tool: opencode-copilot-multi list|remove|clear
|
|
16
|
+
*
|
|
17
|
+
* @author Valerio Fantozzi
|
|
18
|
+
* @license MIT
|
|
19
|
+
*/
|
|
20
|
+
import { logger } from './utils/logger.js';
|
|
21
|
+
import { createProviderLoader } from './provider.js';
|
|
22
|
+
import { getAccountPool } from './storage/pool.js';
|
|
23
|
+
import { detectAndAddNewAccount, syncAccounts } from './discovery/accounts.js';
|
|
24
|
+
import { PLUGIN_CONSTANTS } from './types.js';
|
|
25
|
+
/**
|
|
26
|
+
* Plugin entry point
|
|
27
|
+
*
|
|
28
|
+
* IMPORTANT: This function must return quickly!
|
|
29
|
+
* - No blocking HTTP calls
|
|
30
|
+
* - No heavy I/O operations
|
|
31
|
+
* - Defer everything to lazy loading
|
|
32
|
+
*/
|
|
33
|
+
export const plugin = async ({ client }) => {
|
|
34
|
+
logger.info('=== Copilot Multi-Account Plugin Starting ===');
|
|
35
|
+
logger.info(`Registering provider: ${PLUGIN_CONSTANTS.PROVIDER_NAME}`);
|
|
36
|
+
// Start background sync immediately (but don't await - let it run async)
|
|
37
|
+
// This will detect new accounts and update config
|
|
38
|
+
syncAccounts().catch((error) => {
|
|
39
|
+
logger.error('Background sync failed', {
|
|
40
|
+
error: error instanceof Error ? error.message : String(error)
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
auth: {
|
|
45
|
+
provider: PLUGIN_CONSTANTS.PROVIDER_NAME,
|
|
46
|
+
methods: [], // No auth methods - we manage auth internally
|
|
47
|
+
/**
|
|
48
|
+
* Loader is called when OpenCode needs to use this provider
|
|
49
|
+
* Returns custom fetch that handles multi-account routing
|
|
50
|
+
*/
|
|
51
|
+
loader: async (_getAuth, _provider) => {
|
|
52
|
+
logger.debug('Provider loader called');
|
|
53
|
+
// Check if we have accounts
|
|
54
|
+
const pool = await getAccountPool();
|
|
55
|
+
if (pool.accounts.length === 0) {
|
|
56
|
+
logger.warn('No accounts in pool. Use "opencode auth login" (GitHub Copilot) first.');
|
|
57
|
+
// Try to detect from current auth
|
|
58
|
+
await detectAndAddNewAccount();
|
|
59
|
+
}
|
|
60
|
+
return createProviderLoader();
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
/**
|
|
64
|
+
* Event hook for session events
|
|
65
|
+
* Used to trigger account sync on new sessions and detect logout
|
|
66
|
+
*/
|
|
67
|
+
event: async ({ event }) => {
|
|
68
|
+
if (event.type === 'session.created') {
|
|
69
|
+
logger.debug('New session created, syncing accounts...');
|
|
70
|
+
// Non-blocking sync to detect changes (new accounts or logout)
|
|
71
|
+
syncAccounts().catch(error => {
|
|
72
|
+
logger.error('Account sync failed', {
|
|
73
|
+
error: error instanceof Error ? error.message : String(error)
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
// Default export for OpenCode plugin loader
|
|
81
|
+
export default plugin;
|
|
82
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,MAAM,GAAW,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;IACjD,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IAE7D,MAAM,CAAC,IAAI,CAAC,yBAAyB,gBAAgB,CAAC,aAAa,EAAE,CAAC,CAAC;IAEvE,yEAAyE;IACzE,kDAAkD;IAClD,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7B,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE;YACrC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE;YACJ,QAAQ,EAAE,gBAAgB,CAAC,aAAa;YACxC,OAAO,EAAE,EAAE,EAAE,8CAA8C;YAE3D;;;eAGG;YACH,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;gBACpC,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBAEvC,4BAA4B;gBAC5B,MAAM,IAAI,GAAG,MAAM,cAAc,EAAE,CAAC;gBAEpC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC/B,MAAM,CAAC,IAAI,CAAC,wEAAwE,CAAC,CAAC;oBACtF,kCAAkC;oBAClC,MAAM,sBAAsB,EAAE,CAAC;gBACjC,CAAC;gBAED,OAAO,oBAAoB,EAAE,CAAC;YAChC,CAAC;SACF;QAED;;;WAGG;QACH,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YACzB,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBACrC,MAAM,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;gBACzD,+DAA+D;gBAC/D,YAAY,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;oBAC3B,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE;wBAClC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;qBAC9D,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,4CAA4C;AAC5C,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider implementation with custom fetch for multi-account routing
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Create the provider loader with custom fetch
|
|
6
|
+
* This is what OpenCode calls when using copilot-multi provider
|
|
7
|
+
*/
|
|
8
|
+
export declare function createProviderLoader(): {
|
|
9
|
+
baseURL: "https://api.githubcopilot.com";
|
|
10
|
+
apiKey: string;
|
|
11
|
+
/**
|
|
12
|
+
* Custom fetch that routes requests to the correct account
|
|
13
|
+
*/
|
|
14
|
+
fetch(input: Request | string | URL, init?: RequestInit): Promise<Response>;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkIH;;;GAGG;AACH,wBAAgB,oBAAoB;;;IAMhC;;OAEG;iBACgB,OAAO,GAAG,MAAM,GAAG,GAAG,SAAS,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;EA2EpF"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider implementation with custom fetch for multi-account routing
|
|
3
|
+
*/
|
|
4
|
+
import { logger } from './utils/logger.js';
|
|
5
|
+
import { AccountNotFoundError, InvalidModelIdError, TokenRefreshError } from './utils/errors.js';
|
|
6
|
+
import { getAccountPool, updateAccountInPool } from './storage/pool.js';
|
|
7
|
+
import { COPILOT_HEADERS, PLUGIN_CONSTANTS } from './types.js';
|
|
8
|
+
// Token refresh mutex to prevent concurrent refreshes
|
|
9
|
+
const refreshLocks = new Map();
|
|
10
|
+
/**
|
|
11
|
+
* Parse model ID to extract username and real model
|
|
12
|
+
* Format: "username:model-id" -> { username: "username", model: "model-id" }
|
|
13
|
+
*/
|
|
14
|
+
function parseModelId(modelId) {
|
|
15
|
+
const colonIndex = modelId.indexOf(':');
|
|
16
|
+
if (colonIndex === -1) {
|
|
17
|
+
throw new InvalidModelIdError(modelId);
|
|
18
|
+
}
|
|
19
|
+
const username = modelId.slice(0, colonIndex);
|
|
20
|
+
const model = modelId.slice(colonIndex + 1);
|
|
21
|
+
if (!username || !model) {
|
|
22
|
+
throw new InvalidModelIdError(modelId);
|
|
23
|
+
}
|
|
24
|
+
return { username, model };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Refresh token for an account if needed
|
|
28
|
+
*/
|
|
29
|
+
async function refreshTokenIfNeeded(account) {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const expiresAt = account.auth.expires;
|
|
32
|
+
const bufferMs = PLUGIN_CONSTANTS.TOKEN_REFRESH_BUFFER_MS;
|
|
33
|
+
// expires: 0 means the token never expires (gho_* tokens)
|
|
34
|
+
if (expiresAt === 0) {
|
|
35
|
+
logger.debug(`Token never expires for ${account.username} (gho_* token)`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Check if token is still valid
|
|
39
|
+
if (expiresAt > now + bufferMs) {
|
|
40
|
+
logger.debug(`Token valid for ${account.username}`, {
|
|
41
|
+
expiresIn: Math.round((expiresAt - now) / 1000 / 60) + ' minutes'
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
logger.info(`Token expired or expiring soon for ${account.username}, refreshing...`);
|
|
46
|
+
// Check for existing refresh in progress
|
|
47
|
+
const existingLock = refreshLocks.get(account.id);
|
|
48
|
+
if (existingLock) {
|
|
49
|
+
logger.debug(`Waiting for existing refresh for ${account.username}`);
|
|
50
|
+
await existingLock;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// Create refresh lock
|
|
54
|
+
const refreshPromise = (async () => {
|
|
55
|
+
try {
|
|
56
|
+
const response = await fetch('https://github.com/login/oauth/access_token', {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: {
|
|
59
|
+
'Content-Type': 'application/json',
|
|
60
|
+
'Accept': 'application/json',
|
|
61
|
+
},
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
client_id: 'Iv1.b507a08c87ecfe98', // VS Code client ID
|
|
64
|
+
grant_type: 'refresh_token',
|
|
65
|
+
refresh_token: account.auth.refresh,
|
|
66
|
+
}),
|
|
67
|
+
});
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`Token refresh failed: ${response.status}`);
|
|
70
|
+
}
|
|
71
|
+
const data = await response.json();
|
|
72
|
+
if (data.error || !data.access_token) {
|
|
73
|
+
throw new Error(data.error || 'No access token in response');
|
|
74
|
+
}
|
|
75
|
+
// Update account with new tokens
|
|
76
|
+
account.auth.access = data.access_token;
|
|
77
|
+
if (data.refresh_token) {
|
|
78
|
+
account.auth.refresh = data.refresh_token;
|
|
79
|
+
}
|
|
80
|
+
account.auth.expires = Date.now() + (data.expires_in || 28800) * 1000;
|
|
81
|
+
// Persist updated account
|
|
82
|
+
await updateAccountInPool(account);
|
|
83
|
+
logger.info(`Token refreshed for ${account.username}`, {
|
|
84
|
+
expiresIn: Math.round((account.auth.expires - Date.now()) / 1000 / 60) + ' minutes'
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
logger.error(`Token refresh failed for ${account.username}`, {
|
|
89
|
+
error: error instanceof Error ? error.message : String(error)
|
|
90
|
+
});
|
|
91
|
+
throw new TokenRefreshError(account.username, error instanceof Error ? error : undefined);
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
refreshLocks.delete(account.id);
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
97
|
+
refreshLocks.set(account.id, refreshPromise);
|
|
98
|
+
await refreshPromise;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create the provider loader with custom fetch
|
|
102
|
+
* This is what OpenCode calls when using copilot-multi provider
|
|
103
|
+
*/
|
|
104
|
+
export function createProviderLoader() {
|
|
105
|
+
logger.info('createProviderLoader called - returning config with baseURL:', { baseURL: PLUGIN_CONSTANTS.BASE_URL });
|
|
106
|
+
return {
|
|
107
|
+
baseURL: PLUGIN_CONSTANTS.BASE_URL,
|
|
108
|
+
apiKey: '', // Not used - we inject auth in fetch
|
|
109
|
+
/**
|
|
110
|
+
* Custom fetch that routes requests to the correct account
|
|
111
|
+
*/
|
|
112
|
+
async fetch(input, init) {
|
|
113
|
+
logger.info('Custom fetch called', { input: String(input) });
|
|
114
|
+
const startTime = Date.now();
|
|
115
|
+
try {
|
|
116
|
+
// Parse request body to get model
|
|
117
|
+
let body = {};
|
|
118
|
+
if (init?.body && typeof init.body === 'string') {
|
|
119
|
+
body = JSON.parse(init.body);
|
|
120
|
+
}
|
|
121
|
+
const modelId = body.model;
|
|
122
|
+
if (!modelId) {
|
|
123
|
+
throw new Error('No model specified in request');
|
|
124
|
+
}
|
|
125
|
+
logger.debug(`Request for model: ${modelId}`);
|
|
126
|
+
// Parse model ID to get username and real model
|
|
127
|
+
const { username, model } = parseModelId(modelId);
|
|
128
|
+
logger.debug(`Routing to account: ${username}, model: ${model}`);
|
|
129
|
+
// Load account pool and find account
|
|
130
|
+
const pool = await getAccountPool();
|
|
131
|
+
const account = pool.accounts.find(a => a.username === username);
|
|
132
|
+
if (!account) {
|
|
133
|
+
throw new AccountNotFoundError(username);
|
|
134
|
+
}
|
|
135
|
+
// Refresh token if needed
|
|
136
|
+
await refreshTokenIfNeeded(account);
|
|
137
|
+
// Replace model in body with the real model name
|
|
138
|
+
body.model = model;
|
|
139
|
+
// Build headers
|
|
140
|
+
const headers = new Headers(init?.headers);
|
|
141
|
+
headers.set('Authorization', `Bearer ${account.auth.access}`);
|
|
142
|
+
// Add Copilot headers
|
|
143
|
+
for (const [key, value] of Object.entries(COPILOT_HEADERS)) {
|
|
144
|
+
headers.set(key, value);
|
|
145
|
+
}
|
|
146
|
+
// Update last used timestamp
|
|
147
|
+
account.lastUsed = Date.now();
|
|
148
|
+
// Make the actual request
|
|
149
|
+
const response = await fetch(input, {
|
|
150
|
+
...init,
|
|
151
|
+
body: JSON.stringify(body),
|
|
152
|
+
headers,
|
|
153
|
+
});
|
|
154
|
+
const duration = Date.now() - startTime;
|
|
155
|
+
logger.info(`Request completed`, {
|
|
156
|
+
account: username,
|
|
157
|
+
model,
|
|
158
|
+
status: response.status,
|
|
159
|
+
duration: `${duration}ms`
|
|
160
|
+
});
|
|
161
|
+
return response;
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
const duration = Date.now() - startTime;
|
|
165
|
+
logger.error(`Request failed`, {
|
|
166
|
+
error: error instanceof Error ? error.message : String(error),
|
|
167
|
+
duration: `${duration}ms`
|
|
168
|
+
});
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage for reading OpenCode's auth.json
|
|
3
|
+
*/
|
|
4
|
+
import type { OAuthData } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Read GitHub Copilot auth from OpenCode's auth.json
|
|
7
|
+
* Returns null if not found or invalid
|
|
8
|
+
*/
|
|
9
|
+
export declare function getGitHubCopilotAuth(): Promise<OAuthData | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a stable ID from refresh token (hash first 16 chars)
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateAccountId(refreshToken: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Register copilot-multi provider in OpenCode's auth.json
|
|
16
|
+
* This is required so OpenCode knows to call our auth loader
|
|
17
|
+
*
|
|
18
|
+
* @returns true if registration was successful, false otherwise
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerProviderInAuthJson(): Promise<boolean>;
|
|
21
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/storage/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA4B7C;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA4CtE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED;;;;;GAKG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,OAAO,CAAC,CAgDnE"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage for reading OpenCode's auth.json
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* Get path to OpenCode's auth.json
|
|
10
|
+
* Checks multiple possible locations for cross-platform compatibility.
|
|
11
|
+
* OpenCode stores auth.json at ~/.local/share/opencode/auth.json on all platforms,
|
|
12
|
+
* but some older installations may use ~/.opencode/auth.json on Windows.
|
|
13
|
+
*/
|
|
14
|
+
function getAuthPath() {
|
|
15
|
+
const homeDir = os.homedir();
|
|
16
|
+
const xdgDataHome = process.env.XDG_DATA_HOME;
|
|
17
|
+
if (xdgDataHome) {
|
|
18
|
+
return path.join(xdgDataHome, 'opencode', 'auth.json');
|
|
19
|
+
}
|
|
20
|
+
// Check both possible locations (OpenCode uses XDG path on Windows too)
|
|
21
|
+
const xdgPath = path.join(homeDir, '.local', 'share', 'opencode', 'auth.json');
|
|
22
|
+
const fallbackPath = path.join(homeDir, '.opencode', 'auth.json');
|
|
23
|
+
// Use whichever exists (prioritize XDG standard path)
|
|
24
|
+
if (fs.existsSync(xdgPath))
|
|
25
|
+
return xdgPath;
|
|
26
|
+
if (fs.existsSync(fallbackPath))
|
|
27
|
+
return fallbackPath;
|
|
28
|
+
// Default to XDG path for new installations
|
|
29
|
+
return xdgPath;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read GitHub Copilot auth from OpenCode's auth.json
|
|
33
|
+
* Returns null if not found or invalid
|
|
34
|
+
*/
|
|
35
|
+
export async function getGitHubCopilotAuth() {
|
|
36
|
+
const authPath = getAuthPath();
|
|
37
|
+
try {
|
|
38
|
+
if (!fs.existsSync(authPath)) {
|
|
39
|
+
logger.debug('Auth file not found', { path: authPath });
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const content = fs.readFileSync(authPath, 'utf-8');
|
|
43
|
+
const authData = JSON.parse(content);
|
|
44
|
+
const copilotAuth = authData['github-copilot'];
|
|
45
|
+
if (!copilotAuth) {
|
|
46
|
+
logger.debug('No github-copilot entry in auth.json');
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (copilotAuth.type !== 'oauth') {
|
|
50
|
+
logger.debug('github-copilot auth is not oauth type', { type: copilotAuth.type });
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
// Validate required fields
|
|
54
|
+
// Note: expires can be 0 for non-expiring tokens (gho_* tokens)
|
|
55
|
+
if (!copilotAuth.refresh || !copilotAuth.access || copilotAuth.expires === undefined) {
|
|
56
|
+
logger.warn('github-copilot auth missing required fields');
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
type: 'oauth',
|
|
61
|
+
refresh: copilotAuth.refresh,
|
|
62
|
+
access: copilotAuth.access,
|
|
63
|
+
expires: copilotAuth.expires,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logger.error('Failed to read auth.json', {
|
|
68
|
+
error: error instanceof Error ? error.message : String(error)
|
|
69
|
+
});
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generate a stable ID from refresh token (hash first 16 chars)
|
|
75
|
+
*/
|
|
76
|
+
export function generateAccountId(refreshToken) {
|
|
77
|
+
// Simple hash for ID - just use first 16 chars encoded
|
|
78
|
+
const prefix = refreshToken.slice(0, 16);
|
|
79
|
+
return Buffer.from(prefix).toString('base64').replace(/[^a-zA-Z0-9]/g, '').slice(0, 12);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Register copilot-multi provider in OpenCode's auth.json
|
|
83
|
+
* This is required so OpenCode knows to call our auth loader
|
|
84
|
+
*
|
|
85
|
+
* @returns true if registration was successful, false otherwise
|
|
86
|
+
*/
|
|
87
|
+
export async function registerProviderInAuthJson() {
|
|
88
|
+
const authPath = getAuthPath();
|
|
89
|
+
try {
|
|
90
|
+
// Read existing auth.json or create empty object
|
|
91
|
+
let authData = {};
|
|
92
|
+
if (fs.existsSync(authPath)) {
|
|
93
|
+
const content = fs.readFileSync(authPath, 'utf-8');
|
|
94
|
+
authData = JSON.parse(content);
|
|
95
|
+
logger.debug('Read existing auth.json', { path: authPath });
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
logger.debug('auth.json not found, will create new one', { path: authPath });
|
|
99
|
+
}
|
|
100
|
+
// Check if copilot-multi is already registered
|
|
101
|
+
if (authData['copilot-multi']) {
|
|
102
|
+
logger.debug('copilot-multi already registered in auth.json');
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
// Add copilot-multi provider entry
|
|
106
|
+
// Empty credentials since we manage auth internally via our loader
|
|
107
|
+
authData['copilot-multi'] = {
|
|
108
|
+
type: 'oauth',
|
|
109
|
+
refresh: '',
|
|
110
|
+
access: '',
|
|
111
|
+
expires: 0
|
|
112
|
+
};
|
|
113
|
+
// Ensure directory exists
|
|
114
|
+
const authDir = path.dirname(authPath);
|
|
115
|
+
if (!fs.existsSync(authDir)) {
|
|
116
|
+
fs.mkdirSync(authDir, { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
// Write updated auth.json
|
|
119
|
+
fs.writeFileSync(authPath, JSON.stringify(authData, null, 2), { mode: 0o600 });
|
|
120
|
+
logger.info('Registered copilot-multi in auth.json', { path: authPath });
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
logger.error('Failed to register provider in auth.json', {
|
|
125
|
+
error: error instanceof Error ? error.message : String(error)
|
|
126
|
+
});
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/storage/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account pool storage with lazy loading singleton
|
|
3
|
+
*/
|
|
4
|
+
import type { AccountPool, Account } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Get account pool (lazy loaded, cached)
|
|
7
|
+
*/
|
|
8
|
+
export declare function getAccountPool(): Promise<AccountPool>;
|
|
9
|
+
/**
|
|
10
|
+
* Save account pool to disk
|
|
11
|
+
*/
|
|
12
|
+
export declare function saveAccountPool(pool: AccountPool): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Add account to pool (if not exists)
|
|
15
|
+
*/
|
|
16
|
+
export declare function addAccountToPool(account: Account): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* Update account in pool
|
|
19
|
+
*/
|
|
20
|
+
export declare function updateAccountInPool(account: Account): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Find account by username
|
|
23
|
+
*/
|
|
24
|
+
export declare function findAccountByUsername(username: string): Promise<Account | undefined>;
|
|
25
|
+
/**
|
|
26
|
+
* Invalidate pool cache (for testing or forced reload)
|
|
27
|
+
*/
|
|
28
|
+
export declare function invalidatePoolCache(): void;
|
|
29
|
+
//# sourceMappingURL=pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/storage/pool.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA8ExD;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC,CAa3D;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBtE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAkBzE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAWzE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAG1F;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account pool storage with lazy loading singleton
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import { PoolCorruptedError } from '../utils/errors.js';
|
|
9
|
+
import { PLUGIN_CONSTANTS } from '../types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Get path to the account pool file
|
|
12
|
+
*/
|
|
13
|
+
function getPoolPath() {
|
|
14
|
+
const homeDir = os.homedir();
|
|
15
|
+
const dataDir = process.env.XDG_DATA_HOME
|
|
16
|
+
? path.join(process.env.XDG_DATA_HOME, 'opencode')
|
|
17
|
+
: path.join(homeDir, '.local', 'share', 'opencode');
|
|
18
|
+
return path.join(dataDir, PLUGIN_CONSTANTS.POOL_FILE);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create an empty pool
|
|
22
|
+
*/
|
|
23
|
+
function createEmptyPool() {
|
|
24
|
+
return {
|
|
25
|
+
version: PLUGIN_CONSTANTS.POOL_VERSION,
|
|
26
|
+
accounts: [],
|
|
27
|
+
lastUpdated: Date.now(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate pool structure
|
|
32
|
+
*/
|
|
33
|
+
function isValidPool(data) {
|
|
34
|
+
if (!data || typeof data !== 'object')
|
|
35
|
+
return false;
|
|
36
|
+
const pool = data;
|
|
37
|
+
return (typeof pool.version === 'number' &&
|
|
38
|
+
Array.isArray(pool.accounts) &&
|
|
39
|
+
typeof pool.lastUpdated === 'number');
|
|
40
|
+
}
|
|
41
|
+
// Singleton cache
|
|
42
|
+
let poolInstance = null;
|
|
43
|
+
let loadPromise = null;
|
|
44
|
+
/**
|
|
45
|
+
* Load account pool from disk
|
|
46
|
+
*/
|
|
47
|
+
async function loadPoolFromDisk() {
|
|
48
|
+
const poolPath = getPoolPath();
|
|
49
|
+
try {
|
|
50
|
+
if (!fs.existsSync(poolPath)) {
|
|
51
|
+
logger.info('Pool file not found, creating empty pool');
|
|
52
|
+
return createEmptyPool();
|
|
53
|
+
}
|
|
54
|
+
const content = fs.readFileSync(poolPath, 'utf-8');
|
|
55
|
+
const data = JSON.parse(content);
|
|
56
|
+
if (!isValidPool(data)) {
|
|
57
|
+
logger.warn('Pool file has invalid structure, creating new pool');
|
|
58
|
+
// Backup corrupted file
|
|
59
|
+
const backupPath = `${poolPath}.backup.${Date.now()}`;
|
|
60
|
+
fs.renameSync(poolPath, backupPath);
|
|
61
|
+
return createEmptyPool();
|
|
62
|
+
}
|
|
63
|
+
logger.info(`Loaded pool with ${data.accounts.length} accounts`);
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error instanceof SyntaxError) {
|
|
68
|
+
logger.error('Pool file is corrupted (invalid JSON)');
|
|
69
|
+
throw new PoolCorruptedError(poolPath);
|
|
70
|
+
}
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get account pool (lazy loaded, cached)
|
|
76
|
+
*/
|
|
77
|
+
export async function getAccountPool() {
|
|
78
|
+
if (poolInstance) {
|
|
79
|
+
return poolInstance;
|
|
80
|
+
}
|
|
81
|
+
if (!loadPromise) {
|
|
82
|
+
loadPromise = loadPoolFromDisk().then(pool => {
|
|
83
|
+
poolInstance = pool;
|
|
84
|
+
return pool;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return loadPromise;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Save account pool to disk
|
|
91
|
+
*/
|
|
92
|
+
export async function saveAccountPool(pool) {
|
|
93
|
+
const poolPath = getPoolPath();
|
|
94
|
+
const poolDir = path.dirname(poolPath);
|
|
95
|
+
// Ensure directory exists
|
|
96
|
+
if (!fs.existsSync(poolDir)) {
|
|
97
|
+
fs.mkdirSync(poolDir, { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
// Update timestamp
|
|
100
|
+
pool.lastUpdated = Date.now();
|
|
101
|
+
// Write with pretty formatting
|
|
102
|
+
fs.writeFileSync(poolPath, JSON.stringify(pool, null, 2));
|
|
103
|
+
fs.chmodSync(poolPath, 0o600); // Secure permissions
|
|
104
|
+
// Update cache
|
|
105
|
+
poolInstance = pool;
|
|
106
|
+
logger.info('Pool saved', { accounts: pool.accounts.length });
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Add account to pool (if not exists)
|
|
110
|
+
*/
|
|
111
|
+
export async function addAccountToPool(account) {
|
|
112
|
+
const pool = await getAccountPool();
|
|
113
|
+
// Check if already exists (by ID or username)
|
|
114
|
+
const exists = pool.accounts.some(a => a.id === account.id || a.username === account.username);
|
|
115
|
+
if (exists) {
|
|
116
|
+
logger.debug('Account already in pool', { username: account.username });
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
pool.accounts.push(account);
|
|
120
|
+
await saveAccountPool(pool);
|
|
121
|
+
logger.info('Account added to pool', { username: account.username });
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Update account in pool
|
|
126
|
+
*/
|
|
127
|
+
export async function updateAccountInPool(account) {
|
|
128
|
+
const pool = await getAccountPool();
|
|
129
|
+
const index = pool.accounts.findIndex(a => a.id === account.id);
|
|
130
|
+
if (index === -1) {
|
|
131
|
+
logger.warn('Account not found for update', { id: account.id });
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
pool.accounts[index] = account;
|
|
135
|
+
await saveAccountPool(pool);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Find account by username
|
|
139
|
+
*/
|
|
140
|
+
export async function findAccountByUsername(username) {
|
|
141
|
+
const pool = await getAccountPool();
|
|
142
|
+
return pool.accounts.find(a => a.username === username);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Invalidate pool cache (for testing or forced reload)
|
|
146
|
+
*/
|
|
147
|
+
export function invalidatePoolCache() {
|
|
148
|
+
poolInstance = null;
|
|
149
|
+
loadPromise = null;
|
|
150
|
+
logger.debug('Pool cache invalidated');
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/storage/pool.ts"],"names":[],"mappings":"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"}
|