switch-acc-agy 0.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/README.md +382 -0
- package/package.json +31 -0
- package/switch-acc-agy +236 -0
package/README.md
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# switch-agy
|
|
2
|
+
|
|
3
|
+
`switch-agy` là tool switch account local cho Antigravity CLI (`agy`) trên
|
|
4
|
+
macOS.
|
|
5
|
+
|
|
6
|
+
Mục tiêu: sau khi bạn login thủ công mỗi account một lần, bạn có thể switch
|
|
7
|
+
account ngay từ terminal mà không cần mở browser hay nhập code lại:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
switch-acc-agy-to acc1
|
|
11
|
+
agy --prompt "hello"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Tool hoạt động bằng cách lưu và khôi phục 2 nơi chứa credential mà `agy` đang
|
|
15
|
+
dùng trên máy này:
|
|
16
|
+
|
|
17
|
+
- `~/.gemini/oauth_creds.json`
|
|
18
|
+
- macOS Keychain generic password `service=gemini`, `account=antigravity`
|
|
19
|
+
|
|
20
|
+
Tool không tự động login Google. Nó chỉ snapshot credential đã có sẵn trên máy
|
|
21
|
+
và restore lại khi cần switch.
|
|
22
|
+
|
|
23
|
+
## Yêu Cầu
|
|
24
|
+
|
|
25
|
+
- macOS
|
|
26
|
+
- Node.js và `npm`
|
|
27
|
+
- `agy` đã cài và chạy được
|
|
28
|
+
- Ít nhất một account Antigravity/Gemini đã login sẵn
|
|
29
|
+
|
|
30
|
+
Kiểm tra:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node --version
|
|
34
|
+
npm --version
|
|
35
|
+
command -v agy
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Cài Đặt
|
|
39
|
+
|
|
40
|
+
Cách gọn nhất trên máy mới là cài global qua `npm`. Không cần `cd` vào repo để
|
|
41
|
+
tạo symlink tay nữa.
|
|
42
|
+
|
|
43
|
+
Nếu máy có quyền pull repo qua GitHub SSH:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install -g git+ssh://git@github.com/XuanMaiHieu/switch-acc-agy.git
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Nếu đã clone repo sẵn trên máy:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install -g /path/to/switch-agy
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Verify:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
command -v switch-acc-agy
|
|
59
|
+
switch-acc-agy paths
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`npm` sẽ tự tạo các command global:
|
|
63
|
+
|
|
64
|
+
- `switch-acc-agy`
|
|
65
|
+
- `switch-acc-agy-save`
|
|
66
|
+
- `switch-acc-agy-to`
|
|
67
|
+
|
|
68
|
+
Nếu shell chưa nhận ra command ngay, mở terminal mới hoặc reload shell config.
|
|
69
|
+
|
|
70
|
+
Path kỳ vọng trên máy này:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
store: /Users/abc-dev/.switch-agy
|
|
74
|
+
profiles: /Users/abc-dev/.switch-agy/profiles
|
|
75
|
+
backups: /Users/abc-dev/.switch-agy/backups
|
|
76
|
+
oauth_creds: /Users/abc-dev/.gemini/oauth_creds.json
|
|
77
|
+
keychain_service: gemini
|
|
78
|
+
keychain_account: antigravity
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Setup Lần Đầu Cho Nhiều Account
|
|
82
|
+
|
|
83
|
+
Bạn cần login thủ công từng account một lần. Sau mỗi lần login, lưu trạng thái
|
|
84
|
+
credential hiện tại thành một profile.
|
|
85
|
+
|
|
86
|
+
### Lưu Account 1
|
|
87
|
+
|
|
88
|
+
Login account 1 bằng flow bình thường của Antigravity/Gemini. Kiểm tra account
|
|
89
|
+
đang chạy được:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
agy --prompt "say account one works"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Lưu lại:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
switch-acc-agy-save acc1
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Lưu Account 2
|
|
102
|
+
|
|
103
|
+
Logout hoặc đổi login thủ công sang account 2 bằng flow bình thường của
|
|
104
|
+
Antigravity/Gemini. Kiểm tra account đang chạy được:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
agy --prompt "say account two works"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Lưu lại:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
switch-acc-agy-save acc2
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Xem danh sách profile đã lưu:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
switch-acc-agy list
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Ví dụ output:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
acc1
|
|
126
|
+
acc2 *
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Dấu `*` là profile mà tool lưu hoặc switch gần nhất.
|
|
130
|
+
|
|
131
|
+
## Sử Dụng Hàng Ngày
|
|
132
|
+
|
|
133
|
+
Switch sang account 1:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
switch-acc-agy-to acc1
|
|
137
|
+
agy --prompt "hello from acc1"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Switch sang account 2:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
switch-acc-agy-to acc2
|
|
144
|
+
agy --prompt "hello from acc2"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Lưu ý quan trọng: sau khi switch, hãy chạy một process `agy` mới. Những session
|
|
148
|
+
`agy` đang mở từ trước có thể vẫn cache token cũ trong memory.
|
|
149
|
+
|
|
150
|
+
## Lệnh Có Sẵn
|
|
151
|
+
|
|
152
|
+
Lưu auth hiện tại thành profile:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
switch-acc-agy save <profile>
|
|
156
|
+
switch-acc-agy-save <profile>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Switch sang một profile đã lưu:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
switch-acc-agy to <profile>
|
|
163
|
+
switch-acc-agy-to <profile>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Liệt kê profile:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
switch-acc-agy list
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Xem profile được chọn gần nhất:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
switch-acc-agy current
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Xem path và Keychain identifier mà tool đang dùng:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
switch-acc-agy paths
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Xem help:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
switch-acc-agy --help
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Quy Tắc Đặt Tên Profile
|
|
191
|
+
|
|
192
|
+
Tên profile hợp lệ:
|
|
193
|
+
|
|
194
|
+
- Bắt đầu bằng chữ cái
|
|
195
|
+
- Chỉ dùng chữ cái, số, dấu gạch ngang, dấu gạch dưới
|
|
196
|
+
- Tối đa 32 ký tự
|
|
197
|
+
|
|
198
|
+
Ví dụ hợp lệ:
|
|
199
|
+
|
|
200
|
+
```text
|
|
201
|
+
acc1
|
|
202
|
+
work
|
|
203
|
+
personal
|
|
204
|
+
gmail_main
|
|
205
|
+
team-pro
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Không hợp lệ:
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
1acc
|
|
212
|
+
my.account
|
|
213
|
+
work/email
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Dữ Liệu Được Lưu Ở Đâu
|
|
217
|
+
|
|
218
|
+
Profiles:
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
~/.switch-agy/profiles/<profile>/
|
|
222
|
+
oauth_creds.json
|
|
223
|
+
keychain_secret
|
|
224
|
+
metadata.json
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Backups:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
~/.switch-agy/backups/<timestamp>/
|
|
231
|
+
oauth_creds.json
|
|
232
|
+
keychain_secret
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Credential live sẽ bị thay khi switch:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
~/.gemini/oauth_creds.json
|
|
239
|
+
macOS Keychain: service=gemini account=antigravity
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Tool cố gắng set quyền file credential là `0600` và quyền thư mục profile là
|
|
243
|
+
`0700`.
|
|
244
|
+
|
|
245
|
+
## Lưu Ý Bảo Mật
|
|
246
|
+
|
|
247
|
+
Profile đã lưu chứa OAuth credential thật. Hãy coi `~/.switch-agy` là dữ liệu
|
|
248
|
+
bí mật.
|
|
249
|
+
|
|
250
|
+
Không commit thư mục này:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
echo ".switch-agy/" >> ~/.gitignore_global
|
|
254
|
+
git config --global core.excludesfile ~/.gitignore_global
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Không paste `oauth_creds.json` hoặc `keychain_secret` vào chat, log, issue,
|
|
258
|
+
ticket.
|
|
259
|
+
|
|
260
|
+
Nếu một profile bị lộ, hãy revoke session Google đó, login thủ công lại, rồi
|
|
261
|
+
lưu lại profile:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
switch-acc-agy-save acc1
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Rollback
|
|
268
|
+
|
|
269
|
+
Mỗi lần switch, tool tự tạo backup:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
~/.switch-agy/backups/YYYYMMDD-HHMMSS
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Ví dụ rollback thủ công:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
backup="$HOME/.switch-agy/backups/20260520-223404"
|
|
279
|
+
cp "$backup/oauth_creds.json" "$HOME/.gemini/oauth_creds.json"
|
|
280
|
+
chmod 600 "$HOME/.gemini/oauth_creds.json"
|
|
281
|
+
security add-generic-password -U -a antigravity -s gemini -w "$(cat "$backup/keychain_secret")"
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Sau đó chạy một process `agy` mới:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
agy --prompt "hello"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Troubleshooting
|
|
291
|
+
|
|
292
|
+
### `switch-acc-agy-to: command not found`
|
|
293
|
+
|
|
294
|
+
Khả năng cao là thư mục global prefix của `npm` chưa nằm trong `PATH`.
|
|
295
|
+
|
|
296
|
+
Xem prefix hiện tại:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
npm prefix -g
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Thêm dòng này vào shell config:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
export PATH="$(npm prefix -g)/bin:$PATH"
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Sau đó restart terminal hoặc chạy:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
source ~/.zshrc
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### `Keychain item not found`
|
|
315
|
+
|
|
316
|
+
Account hiện tại chưa được Antigravity lưu vào Keychain item kỳ vọng.
|
|
317
|
+
|
|
318
|
+
Chạy:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
agy --prompt "hello"
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Nếu bị yêu cầu login thì login thủ công. Sau đó lưu profile:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
switch-acc-agy-save acc1
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### `agy` vẫn dùng account cũ
|
|
331
|
+
|
|
332
|
+
Đóng các session `agy` cũ và chạy process mới:
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
switch-acc-agy-to acc1
|
|
336
|
+
agy --prompt "which account is active?"
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Tool đã đổi credential local, nhưng process đang chạy có thể vẫn cache token
|
|
340
|
+
cũ.
|
|
341
|
+
|
|
342
|
+
### Browser vẫn mở sau khi switch
|
|
343
|
+
|
|
344
|
+
Profile đã lưu có thể thiếu credential, hết hạn, hoặc đã bị revoke.
|
|
345
|
+
|
|
346
|
+
Cách sửa: login thủ công lại account đó rồi ghi đè profile:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
switch-acc-agy-save acc1
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### `agy --prompt` không in output
|
|
353
|
+
|
|
354
|
+
Kiểm tra log mới nhất của Antigravity CLI:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
tail -120 ~/.gemini/antigravity-cli/cli.log
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Silent auth thành công thường có các dòng:
|
|
361
|
+
|
|
362
|
+
```text
|
|
363
|
+
ChainedAuth: authenticated via keyring (effective: keyring)
|
|
364
|
+
Print mode: silent auth succeeded
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Tóm Tắt Cơ Chế
|
|
368
|
+
|
|
369
|
+
`save`:
|
|
370
|
+
|
|
371
|
+
1. Đọc `~/.gemini/oauth_creds.json`.
|
|
372
|
+
2. Đọc Keychain item `service=gemini`, `account=antigravity`.
|
|
373
|
+
3. Lưu cả hai vào `~/.switch-agy/profiles/<profile>`.
|
|
374
|
+
|
|
375
|
+
`to`:
|
|
376
|
+
|
|
377
|
+
1. Backup credential live hiện tại.
|
|
378
|
+
2. Copy `oauth_creds.json` đã lưu vào `~/.gemini/oauth_creds.json`.
|
|
379
|
+
3. Thay Keychain item bằng secret đã lưu của profile.
|
|
380
|
+
4. Cập nhật `~/.switch-agy/current_profile`.
|
|
381
|
+
|
|
382
|
+
Sau đó `agy` sẽ silent auth qua Keychain và dùng account vừa restore.
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "switch-acc-agy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local account switcher for Antigravity CLI auth on macOS",
|
|
5
|
+
"bin": {
|
|
6
|
+
"switch-acc-agy": "switch-acc-agy",
|
|
7
|
+
"switch-acc-agy-save": "switch-acc-agy",
|
|
8
|
+
"switch-acc-agy-to": "switch-acc-agy"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"switch-acc-agy",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"os": [
|
|
18
|
+
"darwin"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"antigravity",
|
|
22
|
+
"auth",
|
|
23
|
+
"cli",
|
|
24
|
+
"gemini",
|
|
25
|
+
"switch"
|
|
26
|
+
],
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+ssh://git@github.com/XuanMaiHieu/switch-acc-agy.git"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/switch-acc-agy
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
APP_NAME="switch-acc-agy"
|
|
5
|
+
STORE_DIR="${SWITCH_AGY_STORE:-$HOME/.switch-agy}"
|
|
6
|
+
PROFILES_DIR="$STORE_DIR/profiles"
|
|
7
|
+
BACKUPS_DIR="$STORE_DIR/backups"
|
|
8
|
+
GEMINI_DIR="${GEMINI_DIR:-$HOME/.gemini}"
|
|
9
|
+
OAUTH_CREDS="$GEMINI_DIR/oauth_creds.json"
|
|
10
|
+
KEYCHAIN_SERVICE="${SWITCH_AGY_KEYCHAIN_SERVICE:-gemini}"
|
|
11
|
+
KEYCHAIN_ACCOUNT="${SWITCH_AGY_KEYCHAIN_ACCOUNT:-antigravity}"
|
|
12
|
+
|
|
13
|
+
usage() {
|
|
14
|
+
cat <<EOF
|
|
15
|
+
Usage:
|
|
16
|
+
$APP_NAME save <profile> Save current Antigravity/Gemini auth as profile
|
|
17
|
+
$APP_NAME to <profile> Switch current auth to saved profile
|
|
18
|
+
$APP_NAME list List saved profiles
|
|
19
|
+
$APP_NAME current Show active profile marker
|
|
20
|
+
$APP_NAME paths Show credential paths used by this tool
|
|
21
|
+
|
|
22
|
+
Aliases:
|
|
23
|
+
switch-acc-agy-save <profile>
|
|
24
|
+
switch-acc-agy-to <profile>
|
|
25
|
+
EOF
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
die() {
|
|
29
|
+
printf 'Error: %s\n' "$*" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
info() {
|
|
34
|
+
printf '%s\n' "$*"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
validate_profile() {
|
|
38
|
+
local name="${1:-}"
|
|
39
|
+
[[ "$name" =~ ^[A-Za-z][A-Za-z0-9_-]{0,31}$ ]] || \
|
|
40
|
+
die "invalid profile name '$name'. Use letters, numbers, hyphen, underscore; start with a letter; max 32 chars."
|
|
41
|
+
printf '%s' "$name" | tr '[:upper:]' '[:lower:]'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ensure_store() {
|
|
45
|
+
mkdir -p "$PROFILES_DIR" "$BACKUPS_DIR" "$GEMINI_DIR"
|
|
46
|
+
chmod 700 "$STORE_DIR" "$PROFILES_DIR" "$BACKUPS_DIR" 2>/dev/null || true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
profile_dir() {
|
|
50
|
+
local profile
|
|
51
|
+
profile="$(validate_profile "$1")"
|
|
52
|
+
printf '%s/%s' "$PROFILES_DIR" "$profile"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
require_security() {
|
|
56
|
+
command -v security >/dev/null 2>&1 || die "macOS security command not found"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
read_keychain_secret() {
|
|
60
|
+
require_security
|
|
61
|
+
security find-generic-password -a "$KEYCHAIN_ACCOUNT" -s "$KEYCHAIN_SERVICE" -w 2>/dev/null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
write_keychain_secret() {
|
|
65
|
+
local secret_file="$1"
|
|
66
|
+
require_security
|
|
67
|
+
[[ -s "$secret_file" ]] || die "missing saved keychain secret: $secret_file"
|
|
68
|
+
|
|
69
|
+
# Command substitution intentionally removes the display newline that
|
|
70
|
+
# `security -w` appends; the keychain password itself is the credential blob.
|
|
71
|
+
local secret
|
|
72
|
+
secret="$(LC_ALL=C cat "$secret_file")"
|
|
73
|
+
security add-generic-password \
|
|
74
|
+
-U \
|
|
75
|
+
-a "$KEYCHAIN_ACCOUNT" \
|
|
76
|
+
-s "$KEYCHAIN_SERVICE" \
|
|
77
|
+
-w "$secret" >/dev/null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
save_current_backup() {
|
|
81
|
+
local stamp backup_dir
|
|
82
|
+
stamp="$(date +%Y%m%d-%H%M%S)"
|
|
83
|
+
backup_dir="$BACKUPS_DIR/$stamp"
|
|
84
|
+
mkdir -p "$backup_dir"
|
|
85
|
+
chmod 700 "$backup_dir" 2>/dev/null || true
|
|
86
|
+
|
|
87
|
+
if [[ -f "$OAUTH_CREDS" ]]; then
|
|
88
|
+
cp "$OAUTH_CREDS" "$backup_dir/oauth_creds.json"
|
|
89
|
+
chmod 600 "$backup_dir/oauth_creds.json" 2>/dev/null || true
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if read_keychain_secret >"$backup_dir/keychain_secret" 2>/dev/null; then
|
|
93
|
+
chmod 600 "$backup_dir/keychain_secret" 2>/dev/null || true
|
|
94
|
+
else
|
|
95
|
+
rm -f "$backup_dir/keychain_secret"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
printf '%s' "$backup_dir"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
save_profile() {
|
|
102
|
+
local profile dir
|
|
103
|
+
profile="$(validate_profile "$1")"
|
|
104
|
+
dir="$(profile_dir "$profile")"
|
|
105
|
+
|
|
106
|
+
[[ -f "$OAUTH_CREDS" ]] || die "OAuth file not found: $OAUTH_CREDS"
|
|
107
|
+
read_keychain_secret >/dev/null || die "Keychain item not found: service=$KEYCHAIN_SERVICE account=$KEYCHAIN_ACCOUNT"
|
|
108
|
+
|
|
109
|
+
ensure_store
|
|
110
|
+
mkdir -p "$dir"
|
|
111
|
+
chmod 700 "$dir" 2>/dev/null || true
|
|
112
|
+
|
|
113
|
+
cp "$OAUTH_CREDS" "$dir/oauth_creds.json"
|
|
114
|
+
chmod 600 "$dir/oauth_creds.json" 2>/dev/null || true
|
|
115
|
+
|
|
116
|
+
read_keychain_secret >"$dir/keychain_secret"
|
|
117
|
+
chmod 600 "$dir/keychain_secret" 2>/dev/null || true
|
|
118
|
+
|
|
119
|
+
{
|
|
120
|
+
printf '{\n'
|
|
121
|
+
printf ' "profile": "%s",\n' "$profile"
|
|
122
|
+
printf ' "savedAt": "%s",\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
123
|
+
printf ' "oauthPath": "%s",\n' "$OAUTH_CREDS"
|
|
124
|
+
printf ' "keychainService": "%s",\n' "$KEYCHAIN_SERVICE"
|
|
125
|
+
printf ' "keychainAccount": "%s"\n' "$KEYCHAIN_ACCOUNT"
|
|
126
|
+
printf '}\n'
|
|
127
|
+
} >"$dir/metadata.json"
|
|
128
|
+
chmod 600 "$dir/metadata.json" 2>/dev/null || true
|
|
129
|
+
|
|
130
|
+
printf '%s\n' "$profile" >"$STORE_DIR/current_profile"
|
|
131
|
+
chmod 600 "$STORE_DIR/current_profile" 2>/dev/null || true
|
|
132
|
+
info "Saved profile '$profile'."
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
switch_profile() {
|
|
136
|
+
local profile dir backup_dir
|
|
137
|
+
profile="$(validate_profile "$1")"
|
|
138
|
+
dir="$(profile_dir "$profile")"
|
|
139
|
+
|
|
140
|
+
[[ -d "$dir" ]] || die "profile '$profile' not found"
|
|
141
|
+
[[ -f "$dir/oauth_creds.json" ]] || die "profile '$profile' missing oauth_creds.json"
|
|
142
|
+
[[ -f "$dir/keychain_secret" ]] || die "profile '$profile' missing keychain_secret"
|
|
143
|
+
|
|
144
|
+
ensure_store
|
|
145
|
+
backup_dir="$(save_current_backup)"
|
|
146
|
+
|
|
147
|
+
cp "$dir/oauth_creds.json" "$OAUTH_CREDS"
|
|
148
|
+
chmod 600 "$OAUTH_CREDS" 2>/dev/null || true
|
|
149
|
+
write_keychain_secret "$dir/keychain_secret"
|
|
150
|
+
|
|
151
|
+
printf '%s\n' "$profile" >"$STORE_DIR/current_profile"
|
|
152
|
+
chmod 600 "$STORE_DIR/current_profile" 2>/dev/null || true
|
|
153
|
+
|
|
154
|
+
info "Switched Antigravity auth to '$profile'."
|
|
155
|
+
info "Backup of previous auth: $backup_dir"
|
|
156
|
+
info "Run a new agy process for the switch to take effect."
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
list_profiles() {
|
|
160
|
+
ensure_store
|
|
161
|
+
local current=""
|
|
162
|
+
[[ -f "$STORE_DIR/current_profile" ]] && current="$(tr -d '\n' <"$STORE_DIR/current_profile")"
|
|
163
|
+
|
|
164
|
+
local found=0 dir name marker
|
|
165
|
+
for dir in "$PROFILES_DIR"/*; do
|
|
166
|
+
[[ -d "$dir" ]] || continue
|
|
167
|
+
found=1
|
|
168
|
+
name="$(basename "$dir")"
|
|
169
|
+
marker=""
|
|
170
|
+
[[ "$name" == "$current" ]] && marker=" *"
|
|
171
|
+
printf '%s%s\n' "$name" "$marker"
|
|
172
|
+
done
|
|
173
|
+
[[ "$found" -eq 1 ]] || info "No profiles saved."
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
show_current() {
|
|
177
|
+
if [[ -f "$STORE_DIR/current_profile" ]]; then
|
|
178
|
+
tr -d '\n' <"$STORE_DIR/current_profile"
|
|
179
|
+
printf '\n'
|
|
180
|
+
else
|
|
181
|
+
info "No active profile marker."
|
|
182
|
+
fi
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
show_paths() {
|
|
186
|
+
cat <<EOF
|
|
187
|
+
store: $STORE_DIR
|
|
188
|
+
profiles: $PROFILES_DIR
|
|
189
|
+
backups: $BACKUPS_DIR
|
|
190
|
+
oauth_creds: $OAUTH_CREDS
|
|
191
|
+
keychain_service: $KEYCHAIN_SERVICE
|
|
192
|
+
keychain_account: $KEYCHAIN_ACCOUNT
|
|
193
|
+
EOF
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
cmd="${1:-}"
|
|
197
|
+
alias_cmd=0
|
|
198
|
+
case "$(basename "$0")" in
|
|
199
|
+
switch-acc-agy-save)
|
|
200
|
+
cmd="save"
|
|
201
|
+
alias_cmd=1
|
|
202
|
+
;;
|
|
203
|
+
switch-acc-agy-to)
|
|
204
|
+
cmd="to"
|
|
205
|
+
alias_cmd=1
|
|
206
|
+
;;
|
|
207
|
+
esac
|
|
208
|
+
|
|
209
|
+
case "$cmd" in
|
|
210
|
+
save)
|
|
211
|
+
[[ "$alias_cmd" -eq 1 ]] || shift || true
|
|
212
|
+
[[ $# -eq 1 ]] || { usage; exit 2; }
|
|
213
|
+
save_profile "$1"
|
|
214
|
+
;;
|
|
215
|
+
to|switch)
|
|
216
|
+
[[ "$alias_cmd" -eq 1 ]] || shift || true
|
|
217
|
+
[[ $# -eq 1 ]] || { usage; exit 2; }
|
|
218
|
+
switch_profile "$1"
|
|
219
|
+
;;
|
|
220
|
+
list|ls)
|
|
221
|
+
list_profiles
|
|
222
|
+
;;
|
|
223
|
+
current)
|
|
224
|
+
show_current
|
|
225
|
+
;;
|
|
226
|
+
paths)
|
|
227
|
+
show_paths
|
|
228
|
+
;;
|
|
229
|
+
help|-h|--help|"")
|
|
230
|
+
usage
|
|
231
|
+
;;
|
|
232
|
+
*)
|
|
233
|
+
usage
|
|
234
|
+
exit 2
|
|
235
|
+
;;
|
|
236
|
+
esac
|