neoagent 2.3.1-beta.102 → 2.3.1-beta.104
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/lib/manager.js
CHANGED
|
@@ -797,7 +797,7 @@ async function cmdLoginClaudeCode() {
|
|
|
797
797
|
const data = JSON.parse(raw);
|
|
798
798
|
const token = data?.claudeAiOauthTokens?.accessToken;
|
|
799
799
|
if (token) {
|
|
800
|
-
upsertEnvValue('
|
|
800
|
+
upsertEnvValue('CLAUDE_CODE_OAUTH_TOKEN', token);
|
|
801
801
|
logOk('Imported access token from Claude CLI credentials store');
|
|
802
802
|
logInfo('Restarting NeoAgent to apply credentials...');
|
|
803
803
|
cmdRestart();
|
|
@@ -806,13 +806,34 @@ async function cmdLoginClaudeCode() {
|
|
|
806
806
|
} catch { }
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
-
//
|
|
809
|
+
// Browser-based PKCE OAuth flow.
|
|
810
|
+
// client_id is the metadata URL per claude.ai's dynamic client registration.
|
|
811
|
+
// Redirect URIs registered: http://localhost/callback and http://127.0.0.1/callback (port 80).
|
|
812
|
+
// Per RFC 8252 §7.3, servers SHOULD allow any loopback port — we try high ports first
|
|
813
|
+
// and fall back to 80 if everything else is occupied.
|
|
810
814
|
const http = require('http');
|
|
811
815
|
const { URL: NodeURL } = require('url');
|
|
816
|
+
const net = require('net');
|
|
812
817
|
|
|
813
|
-
const clientId = '
|
|
814
|
-
const
|
|
815
|
-
|
|
818
|
+
const clientId = 'https://claude.ai/oauth/claude-code-client-metadata';
|
|
819
|
+
const SCOPES = 'user:inference user:profile org:create_api_key user:sessions:claude_code offline_access';
|
|
820
|
+
|
|
821
|
+
// Find a free port (prefer high ports to avoid permission issues; fall back to 80)
|
|
822
|
+
const preferredPorts = [65432, 58080, 54321, 49152, 80];
|
|
823
|
+
let redirectPort = null;
|
|
824
|
+
|
|
825
|
+
for (const p of preferredPorts) {
|
|
826
|
+
const free = await new Promise((res) => {
|
|
827
|
+
const probe = net.createServer();
|
|
828
|
+
probe.listen(p, '127.0.0.1', () => { probe.close(() => res(true)); });
|
|
829
|
+
probe.on('error', () => res(false));
|
|
830
|
+
});
|
|
831
|
+
if (free) { redirectPort = p; break; }
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if (!redirectPort) throw new Error('Could not find a free local port for the OAuth callback.');
|
|
835
|
+
|
|
836
|
+
const redirectUri = `http://127.0.0.1${redirectPort === 80 ? '' : `:${redirectPort}`}/callback`;
|
|
816
837
|
|
|
817
838
|
// Generate PKCE verifier and challenge
|
|
818
839
|
const codeVerifier = crypto.randomBytes(48).toString('base64url');
|
|
@@ -821,13 +842,16 @@ async function cmdLoginClaudeCode() {
|
|
|
821
842
|
.update(codeVerifier)
|
|
822
843
|
.digest('base64url');
|
|
823
844
|
|
|
845
|
+
const state = crypto.randomBytes(16).toString('hex');
|
|
846
|
+
|
|
824
847
|
const authUrl = new URL('https://claude.ai/oauth/authorize');
|
|
825
848
|
authUrl.searchParams.set('response_type', 'code');
|
|
826
849
|
authUrl.searchParams.set('client_id', clientId);
|
|
827
850
|
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
828
|
-
authUrl.searchParams.set('scope',
|
|
851
|
+
authUrl.searchParams.set('scope', SCOPES);
|
|
829
852
|
authUrl.searchParams.set('code_challenge', codeChallenge);
|
|
830
853
|
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
854
|
+
authUrl.searchParams.set('state', state);
|
|
831
855
|
|
|
832
856
|
console.log(`\n ${COLORS.cyan}Opening browser for Claude Code authorization...${COLORS.reset}`);
|
|
833
857
|
console.log(` ${COLORS.dim}If the browser doesn't open, visit:${COLORS.reset}`);
|
|
@@ -848,8 +872,9 @@ async function cmdLoginClaudeCode() {
|
|
|
848
872
|
|
|
849
873
|
const server = http.createServer((req, res) => {
|
|
850
874
|
try {
|
|
851
|
-
const reqUrl = new NodeURL(req.url,
|
|
875
|
+
const reqUrl = new NodeURL(req.url, redirectUri);
|
|
852
876
|
const code = reqUrl.searchParams.get('code');
|
|
877
|
+
const returnedState = reqUrl.searchParams.get('state');
|
|
853
878
|
const error = reqUrl.searchParams.get('error');
|
|
854
879
|
|
|
855
880
|
if (error) {
|
|
@@ -861,6 +886,15 @@ async function cmdLoginClaudeCode() {
|
|
|
861
886
|
return;
|
|
862
887
|
}
|
|
863
888
|
|
|
889
|
+
if (returnedState && returnedState !== state) {
|
|
890
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
891
|
+
res.end('<html><body><h2>Authorization failed.</h2><p>State mismatch. You can close this tab.</p></body></html>');
|
|
892
|
+
clearTimeout(timeout);
|
|
893
|
+
server.close();
|
|
894
|
+
reject(new Error('OAuth state mismatch — possible CSRF attempt.'));
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
|
|
864
898
|
if (code) {
|
|
865
899
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
866
900
|
res.end('<html><body><h2>Authorization successful!</h2><p>You can close this tab and return to the terminal.</p></body></html>');
|
|
@@ -884,7 +918,7 @@ async function cmdLoginClaudeCode() {
|
|
|
884
918
|
});
|
|
885
919
|
|
|
886
920
|
logInfo('Exchanging authorization code for access token...');
|
|
887
|
-
const tokenRes = await fetch('https://claude.ai/
|
|
921
|
+
const tokenRes = await fetch('https://claude.ai/oauth/token', {
|
|
888
922
|
method: 'POST',
|
|
889
923
|
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
|
890
924
|
body: JSON.stringify({
|
|
@@ -907,11 +941,11 @@ async function cmdLoginClaudeCode() {
|
|
|
907
941
|
throw new Error('Token exchange succeeded but no access_token was returned.');
|
|
908
942
|
}
|
|
909
943
|
|
|
910
|
-
upsertEnvValue('
|
|
944
|
+
upsertEnvValue('CLAUDE_CODE_OAUTH_TOKEN', accessToken);
|
|
911
945
|
if (tokenData.refresh_token) {
|
|
912
946
|
upsertEnvValue('CLAUDE_CODE_REFRESH_TOKEN', tokenData.refresh_token);
|
|
913
947
|
}
|
|
914
|
-
logOk('Saved Claude Code
|
|
948
|
+
logOk('Saved Claude Code OAuth token to .env');
|
|
915
949
|
logInfo('Restarting NeoAgent to apply credentials...');
|
|
916
950
|
cmdRestart();
|
|
917
951
|
}
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2f506dae7123235dcafdcda0f78d6a13
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"42d3d75a56efe1a2e9902f52dc8006099c45d9
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "2650676693" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|
|
@@ -129338,7 +129338,7 @@ r===$&&A.b()
|
|
|
129338
129338
|
o.push(A.ii(p,A.iY(!1,new A.a3(B.tM,A.dT(new A.cI(B.he,new A.a5V(r,p),p),p,p),p),!1,B.I,!0),p,p,0,0,0,p))}r=!1
|
|
129339
129339
|
if(!s.ay)if(!s.ch){r=s.e
|
|
129340
129340
|
r===$&&A.b()
|
|
129341
|
-
r=B.b.t("
|
|
129341
|
+
r=B.b.t("mpacvqjv-54ec71e").length!==0&&r.b}if(r){r=s.d
|
|
129342
129342
|
r===$&&A.b()
|
|
129343
129343
|
r=r.ag&&!r.V?84:0
|
|
129344
129344
|
q=s.e
|
|
@@ -134146,7 +134146,7 @@ $S:236}
|
|
|
134146
134146
|
A.Ys.prototype={}
|
|
134147
134147
|
A.Rr.prototype={
|
|
134148
134148
|
mT(a){var s=this
|
|
134149
|
-
if(B.b.t("
|
|
134149
|
+
if(B.b.t("mpacvqjv-54ec71e").length===0||s.a!=null)return
|
|
134150
134150
|
s.A5()
|
|
134151
134151
|
s.a=A.q1(B.PP,new A.b58(s))},
|
|
134152
134152
|
A5(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f
|
|
@@ -134164,7 +134164,7 @@ if(!t.f.b(k)){s=1
|
|
|
134164
134164
|
break}i=J.Z(k,"buildId")
|
|
134165
134165
|
h=i==null?null:B.b.t(J.r(i))
|
|
134166
134166
|
j=h==null?"":h
|
|
134167
|
-
if(J.bm(j)===0||J.d(j,"
|
|
134167
|
+
if(J.bm(j)===0||J.d(j,"mpacvqjv-54ec71e")){s=1
|
|
134168
134168
|
break}n.b=!0
|
|
134169
134169
|
n.D()
|
|
134170
134170
|
p=2
|
|
@@ -134181,7 +134181,7 @@ case 2:return A.i(o.at(-1),r)}})
|
|
|
134181
134181
|
return A.k($async$A5,r)},
|
|
134182
134182
|
vb(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1
|
|
134183
134183
|
var $async$vb=A.h(function(a2,a3){if(a2===1){o.push(a3)
|
|
134184
|
-
s=p}for(;;)switch(s){case 0:if(B.b.t("
|
|
134184
|
+
s=p}for(;;)switch(s){case 0:if(B.b.t("mpacvqjv-54ec71e").length===0||n.c){s=1
|
|
134185
134185
|
break}n.c=!0
|
|
134186
134186
|
n.D()
|
|
134187
134187
|
p=4
|
|
@@ -33,7 +33,7 @@ class ClaudeCodeProvider extends AnthropicProvider {
|
|
|
33
33
|
'claude-haiku-4-5-20251001': 200000,
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const authToken = config.apiKey || process.env.
|
|
36
|
+
const authToken = config.apiKey || process.env.CLAUDE_CODE_OAUTH_TOKEN || readClaudeCliToken();
|
|
37
37
|
if (!authToken) {
|
|
38
38
|
console.warn('[ClaudeCode] No access token. Run `neoagent login claude-code` to authenticate.');
|
|
39
39
|
}
|
|
@@ -83,7 +83,7 @@ const AI_PROVIDER_DEFINITIONS = Object.freeze({
|
|
|
83
83
|
id: 'claude-code',
|
|
84
84
|
label: 'Claude Code',
|
|
85
85
|
description: 'Claude models via Claude Code subscription. Login with `neoagent login claude-code`.',
|
|
86
|
-
envKey: '
|
|
86
|
+
envKey: 'CLAUDE_CODE_OAUTH_TOKEN',
|
|
87
87
|
supportsApiKey: true,
|
|
88
88
|
supportsBaseUrl: false,
|
|
89
89
|
defaultEnabled: false,
|