agy-cli-usage 0.3.0 → 0.3.1
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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/credentials.js +67 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
* use plain v* tags in release-please ([#12](https://github.com/abruption/agy-cli-usage/issues/12)) ([74b648d](https://github.com/abruption/agy-cli-usage/commit/74b648df24967f71a43095a80e7340a6b5ac2e39)), closes [#9](https://github.com/abruption/agy-cli-usage/issues/9)
|
|
12
12
|
|
|
13
|
+
## [0.3.1](https://github.com/abruption/agy-cli-usage/compare/v0.3.0...v0.3.1) (2026-06-24)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* read agy token from Windows Credential Manager ([#14](https://github.com/abruption/agy-cli-usage/issues/14)) ([e5cb274](https://github.com/abruption/agy-cli-usage/commit/e5cb274c6a0a6aa1ddf6b491d417bfc81c04916a)), closes [#13](https://github.com/abruption/agy-cli-usage/issues/13)
|
|
19
|
+
|
|
13
20
|
## [0.2.0](https://github.com/abruption/agy-cli-usage/compare/v0.1.0...v0.2.0) (2026-06-24)
|
|
14
21
|
|
|
15
22
|
### Features
|
package/package.json
CHANGED
package/src/credentials.js
CHANGED
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
// "auth_method": "consumer" }
|
|
9
9
|
//
|
|
10
10
|
// Read backends, tried in order:
|
|
11
|
-
// 1. @napi-rs/keyring native module (macOS /
|
|
11
|
+
// 1. @napi-rs/keyring native module (macOS / Linux Secret Service)
|
|
12
12
|
// 2. OS CLI fallback (`security` on macOS, `secret-tool` on Linux)
|
|
13
|
-
// 3.
|
|
13
|
+
// 3. Windows Credential Manager (CredRead via powershell.exe — go-keyring's
|
|
14
|
+
// target format differs from keyring-rs's)
|
|
15
|
+
// 4. File fallback (headless Linux: agy can't reach a keyring
|
|
14
16
|
// and writes the token to a plain-JSON file)
|
|
15
17
|
// If every backend fails, the caller falls back to the PTY path which drives
|
|
16
18
|
// `agy` itself.
|
|
@@ -73,6 +75,67 @@ function readViaCli() {
|
|
|
73
75
|
return null;
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
// On Windows, agy stores the token in Credential Manager via Go's
|
|
79
|
+
// zalando/go-keyring, whose target name is `service:account` ("gemini:antigravity").
|
|
80
|
+
// @napi-rs/keyring (keyring-rs) uses a different target format and can't find it,
|
|
81
|
+
// so we read the credential blob directly via the Win32 CredRead API through the
|
|
82
|
+
// built-in powershell.exe (no extra dependency).
|
|
83
|
+
const WIN_CRED_TARGET = `${KEYRING_SERVICE}:${KEYRING_ACCOUNT}`;
|
|
84
|
+
|
|
85
|
+
const PS_READ_CRED = `$ErrorActionPreference='Stop'
|
|
86
|
+
$sig=@'
|
|
87
|
+
using System;
|
|
88
|
+
using System.Runtime.InteropServices;
|
|
89
|
+
public class CredApi {
|
|
90
|
+
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
|
91
|
+
public static extern bool CredRead(string target, int type, int flags, out IntPtr cred);
|
|
92
|
+
[DllImport("advapi32.dll")] public static extern void CredFree(IntPtr cred);
|
|
93
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
94
|
+
public struct CREDENTIAL {
|
|
95
|
+
public int Flags; public int Type; public IntPtr TargetName; public IntPtr Comment;
|
|
96
|
+
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
|
97
|
+
public int CredentialBlobSize; public IntPtr CredentialBlob; public int Persist;
|
|
98
|
+
public int AttributeCount; public IntPtr Attributes; public IntPtr TargetAlias; public IntPtr UserName;
|
|
99
|
+
}
|
|
100
|
+
public static byte[] Read(string target){
|
|
101
|
+
IntPtr p; if(!CredRead(target,1,0,out p)) return null;
|
|
102
|
+
try {
|
|
103
|
+
var c=(CREDENTIAL)Marshal.PtrToStructure(p,typeof(CREDENTIAL));
|
|
104
|
+
var b=new byte[c.CredentialBlobSize];
|
|
105
|
+
if(c.CredentialBlobSize>0) Marshal.Copy(c.CredentialBlob,b,0,c.CredentialBlobSize);
|
|
106
|
+
return b;
|
|
107
|
+
} finally { CredFree(p); }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
'@
|
|
111
|
+
Add-Type -TypeDefinition $sig | Out-Null
|
|
112
|
+
$b=[CredApi]::Read('${WIN_CRED_TARGET}')
|
|
113
|
+
if($b -eq $null){ exit 1 }
|
|
114
|
+
[Console]::Out.Write([Convert]::ToBase64String($b))`;
|
|
115
|
+
|
|
116
|
+
function readViaWindowsCredman() {
|
|
117
|
+
if (process.platform !== 'win32') return null;
|
|
118
|
+
try {
|
|
119
|
+
const encoded = Buffer.from(PS_READ_CRED, 'utf16le').toString('base64');
|
|
120
|
+
const b64 = execFileSync(
|
|
121
|
+
'powershell.exe',
|
|
122
|
+
['-NoProfile', '-NonInteractive', '-EncodedCommand', encoded],
|
|
123
|
+
{ encoding: 'utf8' },
|
|
124
|
+
).trim();
|
|
125
|
+
if (!b64) return null;
|
|
126
|
+
const raw = Buffer.from(b64, 'base64');
|
|
127
|
+
// go-keyring writes the value as UTF-8; tolerate UTF-16LE just in case.
|
|
128
|
+
const utf8 = raw.toString('utf8');
|
|
129
|
+
const looksValid = (s) => s.startsWith(B64_PREFIX) || s.trimStart().startsWith('{');
|
|
130
|
+
if (looksValid(utf8)) return utf8;
|
|
131
|
+
const utf16 = raw.toString('utf16le');
|
|
132
|
+
if (looksValid(utf16)) return utf16;
|
|
133
|
+
return utf8;
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
76
139
|
// On headless Linux (no Secret Service) agy persists the token to a plain-JSON
|
|
77
140
|
// file instead of the keyring. Same payload shape, no `go-keyring-base64:` prefix.
|
|
78
141
|
function readViaFile() {
|
|
@@ -98,6 +161,8 @@ async function readRawSecret() {
|
|
|
98
161
|
if (fromNapi) return fromNapi;
|
|
99
162
|
const fromCli = readViaCli();
|
|
100
163
|
if (fromCli) return fromCli;
|
|
164
|
+
const fromWin = readViaWindowsCredman();
|
|
165
|
+
if (fromWin) return fromWin;
|
|
101
166
|
const fromFile = readViaFile();
|
|
102
167
|
if (fromFile) return fromFile;
|
|
103
168
|
return null;
|