antigravity-tc 1.1.2 → 1.1.4

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.id.md ADDED
@@ -0,0 +1,146 @@
1
+ <div align="center">
2
+
3
+ <img src="images/antigravity-tc.jpg" alt="Antigravity Token Collector" width="860" />
4
+
5
+ # Antigravity Token Collector
6
+
7
+ **Kumpulkan donasi token Antigravity dari siapa saja — langsung masuk ke database [9Router](https://github.com/decolua/9router) Anda.**
8
+
9
+ [English](README.md) | Bahasa Indonesia (Tersedia dalam 2 Bahasa / Available in 2 Languages)
10
+
11
+ [![npm version](https://img.shields.io/npm/v/antigravity-tc.svg)](https://www.npmjs.com/package/antigravity-tc)
12
+ [![License: MIT](https://img.shields.io/badge/License-MIT-22c55e.svg)](LICENSE)
13
+ [![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-14b8a6.svg)](#-dukungan-platform)
14
+ [![Node](https://img.shields.io/badge/node-%3E%3D20-fdba74.svg)]()
15
+
16
+ Tray app + dashboard untuk mengelola donasi token: halaman donasi publik, portal yang bisa dikustom, multi bahasa, tunnel gratis tanpa expose IP, auto-start saat login, dan sync massal dua arah dengan 9Router.
17
+
18
+ </div>
19
+
20
+ ---
21
+
22
+ ## ⚡ Quick Start
23
+
24
+ ```bash
25
+ npm install -g antigravity-tc
26
+ antigravity-tc
27
+ ```
28
+
29
+ Dashboard terbuka di `http://localhost:3456/dashboard` — tray icon muncul di menubar.
30
+
31
+ > Password default: `1234567890`. Segera ganti di **Pengaturan → Keamanan**.
32
+ >
33
+ > Lupa password? Jalankan `antigravity-tc-reset-password`.
34
+
35
+ ## 🔄 Cara Kerja
36
+
37
+ 1. Jalankan `antigravity-tc`.
38
+ 2. Buat link donasi personal dari dashboard, atau aktifkan tunnel publik.
39
+ 3. Donatur login Google + tempel kode.
40
+ 4. Token dicek eligibility, di-refresh bila expired, lalu bisa disinkronkan ke 9Router.
41
+ 5. Halaman depan bisa disesuaikan dari **Pengaturan → Portal Settings**.
42
+
43
+ ## 🖼️ Tampilan
44
+
45
+ | Dashboard | Sync Massal | Pengaturan |
46
+ | :---: | :---: | :---: |
47
+ | ![Dashboard](images/Dashboard.jpg) | ![Sync](images/Sync.jpg) | ![Pengaturan](images/Pengaturan.jpg) |
48
+
49
+ ## 📋 Fitur
50
+
51
+ | Fitur | Keterangan |
52
+ | --- | --- |
53
+ | Halaman donasi publik | Login Google + tempel kode, token dicek eligibility lalu disimpan |
54
+ | Portal Settings | Atur layout dan kata-kata halaman depan; data disimpan di aplikasi, bukan DB 9Router |
55
+ | Multi bahasa | UI dashboard dan portal multibahasa (Bahasa Indonesia & English) |
56
+ | Generate link personal | `?for=Nama` untuk sapaan otomatis |
57
+ | Dashboard admin | Statistik, tabel live, search, filter status, sumber, dan tier, export/import CSV |
58
+ | Deteksi & Filter Tier | Menampilkan tier donasi (**Free**, **Plus**, **Pro**, **Ultra**) serta filter cepat di tabel |
59
+ | Export Go Keyring | Salin token donatur format `go-keyring-base64` untuk login paksa ke Antigravity / `antigravity-cli` |
60
+ | Sync ke 9Router | Upsert semua donation logs ke providerConnections 9Router |
61
+ | Sync dari 9Router | Ambil providerConnections 9Router ke donation logs lokal |
62
+ | Check ALL Eligibility | Cek semua row bertoken dengan jeda aman dan rekap status |
63
+ | Auto refresh token | Token expired dicoba refresh sebelum check eligibility |
64
+ | Tunnel publik gratis | Cloudflare Quick Tunnel untuk URL publik |
65
+ | Restart Services | Restart dari tray atau tombol Settings setelah perubahan config |
66
+ | Auto start | Berjalan otomatis mode tray saat OS login |
67
+ | Keamanan | Login JWT, secret unik per instalasi, tunnel terkunci selama password default |
68
+
69
+ ## 🚀 Login ke Official Apps (Antigravity & antigravity-cli)
70
+
71
+ Anda dapat menggunakan token donatur untuk langsung login ke aplikasi resmi **Antigravity** atau **antigravity-cli** tanpa perlu autentikasi ulang via browser:
72
+
73
+ 1. Di tabel donasi admin, klik tombol aksi (**...**) pada baris donatur &rarr; pilih **Go Keyring**.
74
+ 2. Salin string `go-keyring-base64:...` atau langsung gunakan perintah terminal yang disediakan:
75
+ - **macOS (Keychain)**:
76
+ ```bash
77
+ security add-generic-password -s gemini -a antigravity -w "<STRING_KEYRING>" -U
78
+ ```
79
+ - **Windows (CMD / PowerShell)**:
80
+ ```powershell
81
+ cmdkey /generic:"gemini:antigravity" /user:"antigravity" /pass:"<STRING_KEYRING>"
82
+ ```
83
+ *(Atau isi manual via **Windows Credential Manager** &rarr; Generic Credentials).*
84
+ 3. Buka Antigravity atau jalankan `antigravity-cli` — sesi login donatur langsung aktif.
85
+
86
+ ## 📁 Lokasi Data
87
+
88
+ ### Database 9Router
89
+
90
+ | OS | Lokasi Default |
91
+ | --- | --- |
92
+ | macOS / Linux | `~/.9router/db/data.sqlite` |
93
+ | Windows | `%APPDATA%/9router/db/data.sqlite` |
94
+ | Docker | `/app/data/db/data.sqlite` |
95
+
96
+ Path bisa diganti via **Dashboard → Pengaturan → Database 9Router** atau env `DB_PATH`. Perubahan path perlu restart aplikasi.
97
+
98
+ ### Data ATC
99
+
100
+ | OS | Lokasi |
101
+ | --- | --- |
102
+ | macOS / Linux | `~/.antigravity-tc/` |
103
+ | Windows | `%USERPROFILE%\.antigravity-tc\` |
104
+ | Docker | `/root/.antigravity-tc/` |
105
+
106
+ | Isi | Keterangan |
107
+ | --- | --- |
108
+ | `.env` | Konfigurasi lokal |
109
+ | `logs/donation-logs.sqlite` | Log donasi |
110
+ | `bin/cloudflared` | Binary tunnel |
111
+ | Config aplikasi | Password admin, portal settings, bahasa, auto-start, path DB |
112
+
113
+ ## 🖥️ Dukungan Platform
114
+
115
+ | Kemampuan | macOS | Linux | Windows | Docker |
116
+ | --- | :---: | :---: | :---: | :---: |
117
+ | Server + Dashboard + halaman donasi | ✅ | ✅ | ✅ | ✅ |
118
+ | Tray icon | ✅ | ✅ | ✅ | — |
119
+ | Auto-start saat login | ✅ | ✅ | ✅ | — |
120
+ | Cloudflare Tunnel | ✅ | ✅ | ✅ | ✅ |
121
+ | Path DB 9Router otomatis per OS | ✅ | ✅ | ✅ | ✅ |
122
+
123
+ ## 🔑 Password & `.env`
124
+
125
+ - Password default: `1234567890`.
126
+ - Prioritas password: dashboard > `APP_PASSWORD` > default.
127
+ - Kredensial OAuth publik Antigravity CLI sudah tertanam bawaan.
128
+ - Override `OAUTH_CLIENT_ID` / `OAUTH_CLIENT_SECRET` tersedia bila perlu.
129
+
130
+ ## ❓ Troubleshooting
131
+
132
+ | Masalah | Solusi |
133
+ | --- | --- |
134
+ | Lupa password admin | Jalankan `antigravity-tc-reset-password` |
135
+ | Tunnel tidak bisa diaktifkan | Ganti password default dulu |
136
+ | Tray icon tidak muncul | Pastikan binary `systray2/traybin/` executable |
137
+ | DB 9Router tidak ketemu | Cek path di Pengaturan → Database 9Router, lalu restart services |
138
+ | Refresh token gagal | Donatur perlu login ulang bila Google menolak refresh token |
139
+
140
+ ## 🛠️ Development
141
+
142
+ Lihat [dev.md](dev.md) dan [build.md](build.md).
143
+
144
+ ## 📄 License
145
+
146
+ [MIT](LICENSE) © decolua and contributors
package/README.md CHANGED
@@ -4,14 +4,16 @@
4
4
 
5
5
  # Antigravity Token Collector
6
6
 
7
- **Kumpulkan donasi token Antigravity dari siapa saja langsung masuk ke database [9Router](https://github.com/decolua/9router) Anda.**
7
+ **Collect donated Antigravity tokens from anyone and sync them into your [9Router](https://github.com/decolua/9router) database.**
8
+
9
+ English | [Bahasa Indonesia](README.id.md) (Tersedia dalam 2 Bahasa / Available in 2 Languages)
8
10
 
9
11
  [![npm version](https://img.shields.io/npm/v/antigravity-tc.svg)](https://www.npmjs.com/package/antigravity-tc)
10
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-22c55e.svg)](LICENSE)
11
- [![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-14b8a6.svg)](#-dukungan-platform)
13
+ [![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-14b8a6.svg)](#-platform-support)
12
14
  [![Node](https://img.shields.io/badge/node-%3E%3D20-fdba74.svg)]()
13
15
 
14
- Tray app + dashboard untuk mengelola donasi token: halaman donasi publik, tunnel gratis tanpa expose IP, auto-start saat login, dan sync massal ke 9Router.
16
+ A tray app and dashboard for managing donated Antigravity tokens: customizable public portal, multilingual copy, free public tunnel, login auto-start, eligibility checks, and two-way 9Router sync.
15
17
 
16
18
  </div>
17
19
 
@@ -24,112 +26,121 @@ npm install -g antigravity-tc
24
26
  antigravity-tc
25
27
  ```
26
28
 
27
- 🎉 Dashboard terbuka di `http://localhost:3456/dashboard` tray icon muncul di menubar.
28
-
29
- __That's it!__ 🚀
29
+ The dashboard opens at `http://localhost:3456/dashboard`, and the tray icon appears in your menu bar/system tray.
30
30
 
31
- > 💡 Password default: `1234567890` hint tampil di halaman login selama belum diganti. Segera ganti di **PengaturanKeamanan**.
31
+ > Default password: `1234567890`. Change it immediately in **SettingsSecurity**.
32
32
  >
33
- > 🔑 **Lupa password?** Jalankan `antigravity-tc-reset-password` di terminal untuk meresetnya kembali ke default.
34
-
35
- ## 🔄 Cara Kerja
33
+ > Forgot your password? Run `antigravity-tc-reset-password`.
36
34
 
37
- ```
38
- ┌──────────────────┐ ┌─────────────────────┐ ┌──────────────────┐
39
- │ Donatur │ │ ATC (Anda) │ │ 9Router │
40
- │ │ │ │ │ │
41
- │ Login Google ───┼──────▶ │ Cek eligibility ───┼──────▶ │ Token tersimpan │
42
- │ lewat link yang │ │ Refresh otomatis │ │ siap dipakai │
43
- │ Anda bagikan │ │ Anti-duplikat │ │ │
44
- └──────────────────┘ └─────────────────────┘ └──────────────────┘
45
- ```
35
+ ## 🔄 How It Works
46
36
 
47
- 1. Jalankan `antigravity-tc` — dashboard & tray aktif.
48
- 2. Buat **link donasi personal** dari dashboard (Generate Link), bagikan lewat chat mana pun — atau aktifkan **tunnel publik** gratis supaya bisa diakses dari luar tanpa expose IP.
49
- 3. Donatur login Google + tempel kode token otomatis dicek, di-refresh bila expired, lalu tersimpan ke 9Router.
37
+ 1. Run `antigravity-tc`.
38
+ 2. Generate a personal donation link from the dashboard, or enable the public tunnel.
39
+ 3. Donors sign in with Google and paste the Antigravity code.
40
+ 4. ATC checks eligibility, refreshes tokens when needed, and stores donation logs locally.
41
+ 5. Sync eligible tokens into 9Router when you are ready.
42
+ 6. Customize the public portal wording and layout from **Settings → Portal Settings**.
50
43
 
51
- ## 🖼️ Tampilan
44
+ ## 🖼️ Screenshots
52
45
 
53
- | Dashboard | Sync Massal | Pengaturan |
46
+ | Dashboard | Bulk Sync | Settings |
54
47
  | :---: | :---: | :---: |
55
- | ![Dashboard](images/Dashboard.jpg) | ![Sync](images/Sync.jpg) | ![Pengaturan](images/Pengaturan.jpg) |
48
+ | ![Dashboard](images/Dashboard.jpg) | ![Sync](images/Sync.jpg) | ![Settings](images/Pengaturan.jpg) |
56
49
 
57
- ## 📋 Fitur
50
+ ## 📋 Features
58
51
 
59
- | Fitur | Keterangan |
52
+ | Feature | Description |
60
53
  | --- | --- |
61
- | 🌐 Halaman donasi publik | Login Google + tempel kode, token dicek eligibility lalu disimpan |
62
- | 🔗 Generate link personal | `?for=Nama` sapaan otomatis untuk tiap calon donatur |
63
- | 📊 Dashboard admin | Statistik, tabel live, export & import CSV, generate link |
64
- | 🔄 Sync massal ke 9Router | Upsert semua data sekaligus dengan log terminal realtime |
65
- | Auto refresh token | Token expired otomatis di-refresh Google saat check eligibility |
66
- | 🌐 Tunnel publik gratis | Cloudflare Quick Tunnel URL `*.trycloudflare.com` siap dibagikan |
67
- | 🚀 Auto start | Berjalan otomatis (mode tray) saat OS login |
68
- | 🔒 Keamanan | Login JWT (secret unik per instalasi), tunnel terkunci selama password default |
69
-
70
- ## 📁 Data Location
71
-
72
- Lokasi default **mengikuti OS tempat package di-install** tanpa konfigurasi tambahan:
73
-
74
- ### Database 9Router (target penyimpanan token)
75
-
76
- | OS | Lokasi Default |
54
+ | Public donation portal | Google login + manual code paste flow for donors |
55
+ | Portal Settings | Customize layout and front-page wording; stored in ATC app config, not 9Router DB |
56
+ | Multilingual support | Fully localized dashboard and portal in English and Bahasa Indonesia |
57
+ | Personal donation links | `?for=Name` automatically customizes the greeting |
58
+ | Admin dashboard | Stats, live table, search, status/source/tier filters, CSV import/export |
59
+ | Tier Detection & Filter | Displays donor tier (**Free**, **Plus**, **Pro**, **Ultra**) with quick filters |
60
+ | Go Keyring Export | Export donor token as `go-keyring-base64` to force login into official Antigravity apps |
61
+ | Sync to 9Router | Upsert local donation logs into 9Router `providerConnections` |
62
+ | Sync from 9Router | Import 9Router `providerConnections` back into local donation logs |
63
+ | Check ALL Eligibility | Check all token rows with per-row delay and status recap |
64
+ | Token refresh | Expired access tokens are refreshed before eligibility checks when possible |
65
+ | Free public tunnel | Cloudflare Quick Tunnel gives you a public URL without exposing your IP |
66
+ | Restart Services | Restart from tray or Settings after config changes such as 9Router DB path |
67
+ | Auto-start | Launches in tray mode when the OS starts |
68
+ | Security | JWT admin login, per-install secret, tunnel blocked while default password is active |
69
+
70
+ ## 🚀 Log in to Official Apps (Antigravity & antigravity-cli)
71
+
72
+ You can use donated tokens to authenticate directly into the official **Antigravity** app or **antigravity-cli** without going through browser OAuth:
73
+
74
+ 1. In the admin donation table, click the action menu (**...**) for any donor &rarr; select **Go Keyring**.
75
+ 2. Copy the `go-keyring-base64:...` string or use the provided one-click terminal commands:
76
+ - **macOS (Keychain)**:
77
+ ```bash
78
+ security add-generic-password -s gemini -a antigravity -w "<KEYRING_STRING>" -U
79
+ ```
80
+ - **Windows (CMD / PowerShell)**:
81
+ ```powershell
82
+ cmdkey /generic:"gemini:antigravity" /user:"antigravity" /pass:"<KEYRING_STRING>"
83
+ ```
84
+ *(Or manually add under **Windows Credential Manager** &rarr; Generic Credentials).*
85
+ 3. Launch Antigravity or execute `antigravity-cli` — the donor session will be active immediately.
86
+
87
+ ## 📁 Data Locations
88
+
89
+ ### 9Router database
90
+
91
+ | OS | Default path |
77
92
  | --- | --- |
78
93
  | macOS / Linux | `~/.9router/db/data.sqlite` |
79
94
  | Windows | `%APPDATA%/9router/db/data.sqlite` |
80
- | Docker | `/app/data/db/data.sqlite` (mount volume untuk persist) |
95
+ | Docker | `/app/data/db/data.sqlite` |
81
96
 
82
- > Path ini bisa dioverride kapan saja via **Dashboard → Pengaturan → Database 9Router** (untuk instalasi non-standar) atau env `DB_PATH`.
97
+ You can override this path from **Dashboard → Settings9Router Database** or by setting `DB_PATH`. Restart services after changing it.
83
98
 
84
- ### Data ATC (log donasi, binary tunnel, config)
99
+ ### ATC app data
85
100
 
86
- | OS | Lokasi |
101
+ | OS | Path |
87
102
  | --- | --- |
88
103
  | macOS / Linux | `~/.antigravity-tc/` |
89
104
  | Windows | `%USERPROFILE%\.antigravity-tc\` |
90
- | Docker | `/root/.antigravity-tc/` (mount `$HOME/.antigravity-tc` untuk persist) |
105
+ | Docker | `/root/.antigravity-tc/` |
91
106
 
92
- | Isi | Keterangan |
107
+ | Content | Description |
93
108
  | --- | --- |
94
- | `.env` | Konfigurasi dibuat otomatis dari `env.example` saat pertama kali jalan |
95
- | `logs/donation-logs.sqlite` | Log donasi (auto-migrasi dari instalasi lama) |
96
- | `bin/cloudflared` | Binary tunnel (auto-download per OS) |
97
- | `logs/launcher.log` | Log saat auto-start |
109
+ | `.env` | Local environment configuration |
110
+ | `logs/donation-logs.sqlite` | Donation log database |
111
+ | `bin/cloudflared` | Downloaded tunnel binary |
112
+ | App config | Admin password, portal settings, language, auto-start, 9Router DB path |
98
113
 
99
- ## 🖥️ Dukungan Platform
114
+ ## 🖥️ Platform Support
100
115
 
101
- | Kemampuan | macOS | Linux | Windows | Docker |
116
+ | Capability | macOS | Linux | Windows | Docker |
102
117
  | --- | :---: | :---: | :---: | :---: |
103
- | Server + Dashboard + halaman donasi | ✅ | ✅ | ✅ | ✅ |
118
+ | Server + dashboard + public portal | ✅ | ✅ | ✅ | ✅ |
104
119
  | Tray icon | ✅ | ✅ | ✅ | — |
105
- | Auto-start saat login | ✅ | ✅ | ✅ | — (pakai restart policy) |
120
+ | Login auto-start | ✅ | ✅ | ✅ | — |
106
121
  | Cloudflare Tunnel | ✅ | ✅ | ✅ | ✅ |
107
- | Path DB 9Router otomatis per OS | ✅ | ✅ | ✅ | ✅ |
108
-
109
- > Docker: jalankan dengan `antigravity-tc --hidden` sebagai entrypoint dan mount volume data (lihat tabel di atas). Deteksi Docker otomatis memakai path `/app/data/db/data.sqlite`.
122
+ | Automatic 9Router DB path detection | ✅ | ✅ | ✅ | ✅ |
110
123
 
111
124
  ## 🔑 Password & `.env`
112
125
 
113
- - Password: default `1234567890` → ganti via dashboard, atau set `APP_PASSWORD` di `.env`.
114
- - Prioritas: password dari dashboard > `APP_PASSWORD` > default.
115
- - Kredensial OAuth **sudah tertanam bawaan** (memakai kredensial publik Antigravity CLI) `.env` hanya perlu diisi `PORT`/`APP_PASSWORD` bila mau. Override `OAUTH_CLIENT_ID`/`OAUTH_CLIENT_SECRET` tersedia untuk OAuth client sendiri.
126
+ - Default password: `1234567890`.
127
+ - Password priority: dashboard setting > `APP_PASSWORD` > default.
128
+ - The public Antigravity CLI OAuth credentials are bundled by default.
129
+ - You can still override `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` if needed.
116
130
 
117
131
  ## ❓ Troubleshooting
118
132
 
119
- | Masalah | Solusi |
133
+ | Problem | Fix |
120
134
  | --- | --- |
121
- | Lupa password admin | Jalankan `antigravity-tc-reset-password` (tambah `--yes` untuk tanpa konfirmasi) |
122
- | Tunnel tidak bisa diaktifkan | Ganti password default dulu di Pengaturan → Keamanan |
123
- | Tray icon tidak muncul | Pastikan binary `systray2/traybin/` executable |
124
- | Sudah berjalan tapi tidak bisa dibuka lagi | App single-instance cukup klik tray icon |
125
- | Refresh token gagal | Cek koneksi; kredensial OAuth sudah bawaan atau isi `OAUTH_CLIENT_ID` & `OAUTH_CLIENT_SECRET` di `.env` untuk client sendiri |
126
- | DB 9Router tidak ketemu | Cek path di Pengaturan → Database 9Router |
127
-
128
- ---
135
+ | Forgot admin password | Run `antigravity-tc-reset-password` |
136
+ | Tunnel cannot be enabled | Change the default password first |
137
+ | Tray icon missing | Make sure the `systray2/traybin/` binary is executable |
138
+ | 9Router DB not found | Check Settings 9Router Database, then restart services |
139
+ | Refresh token fails | The donor may need to sign in again if Google rejects the refresh token |
129
140
 
130
141
  ## 🛠️ Development
131
142
 
132
- Untuk menjalankan dari source, development, dan build/publish mandiri — lihat **[dev.md](dev.md)** dan **[build.md](build.md)**.
143
+ See [dev.md](dev.md) and [build.md](build.md).
133
144
 
134
145
  ## 📄 License
135
146
 
package/bin/restart.js ADDED
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const { execSync, spawn } = require('child_process');
6
+
7
+ const { ensureEnvFile } = require('../src/paths');
8
+ require('dotenv').config({ path: ensureEnvFile(), quiet: true });
9
+ const { getConfig } = require('../src/config');
10
+
11
+ const config = getConfig();
12
+ const port = Number(process.env.PORT || config.get('port') || 3456);
13
+ const LOCK_FILE_PATH = path.join(os.tmpdir(), 'antigravity-token-collector.lock');
14
+
15
+ function isPidAlive(pid) {
16
+ try {
17
+ process.kill(pid, 0);
18
+ return true;
19
+ } catch (e) {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ function findRunningPids() {
25
+ const pids = new Set();
26
+
27
+ // 1. Cek via lsof (port listener)
28
+ if (process.platform !== 'win32') {
29
+ try {
30
+ const lsofOut = execSync(`lsof -ti :${port}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
31
+ if (lsofOut) {
32
+ for (const line of lsofOut.split('\n')) {
33
+ const pid = parseInt(line.trim(), 10);
34
+ if (pid && pid !== process.pid) pids.add(pid);
35
+ }
36
+ }
37
+ } catch (e) {}
38
+
39
+ // 2. Cek via ps untuk proses node yang menjalankan index.js
40
+ try {
41
+ const psOut = execSync('ps -eo pid,command', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
42
+ for (const line of psOut.split('\n')) {
43
+ if (line.includes('index.js') && (line.includes('antigravity-tc') || line.includes('antigravity-token-collector'))) {
44
+ const match = line.trim().match(/^(\d+)/);
45
+ if (match) {
46
+ const pid = parseInt(match[1], 10);
47
+ if (pid && pid !== process.pid) pids.add(pid);
48
+ }
49
+ }
50
+ }
51
+ } catch (e) {}
52
+ } else {
53
+ // Windows fallback
54
+ try {
55
+ const netstatOut = execSync(`netstat -ano | findstr :${port}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
56
+ for (const line of netstatOut.split('\n')) {
57
+ const parts = line.trim().split(/\s+/);
58
+ const pid = parseInt(parts[parts.length - 1], 10);
59
+ if (pid && pid !== process.pid) pids.add(pid);
60
+ }
61
+ } catch (e) {}
62
+ }
63
+
64
+ return Array.from(pids);
65
+ }
66
+
67
+ async function stopRunningInstances() {
68
+ const pids = findRunningPids();
69
+ if (!pids.length) {
70
+ console.log('ℹ️ Tidak ada instance Antigravity-tc yang sedang aktif.');
71
+ return;
72
+ }
73
+
74
+ console.log(`🔄 Menghentikan instance Antigravity-tc yang sedang berjalan (PID: ${pids.join(', ')})...`);
75
+
76
+ for (const pid of pids) {
77
+ try {
78
+ process.kill(pid, 'SIGTERM');
79
+ } catch (e) {}
80
+ }
81
+
82
+ // Tunggu hingga proses benar-benar berhenti
83
+ const deadline = Date.now() + 2500;
84
+ while (Date.now() < deadline) {
85
+ const alive = pids.filter(isPidAlive);
86
+ if (!alive.length) break;
87
+ await new Promise((resolve) => setTimeout(resolve, 150));
88
+ }
89
+
90
+ // Force kill jika masih hidup
91
+ for (const pid of pids) {
92
+ if (isPidAlive(pid)) {
93
+ try {
94
+ process.kill(pid, 'SIGKILL');
95
+ } catch (e) {}
96
+ }
97
+ }
98
+
99
+ // Bersihkan stale lock file bila ada
100
+ try {
101
+ if (fs.existsSync(LOCK_FILE_PATH)) fs.unlinkSync(LOCK_FILE_PATH);
102
+ const lockDir = `${LOCK_FILE_PATH}.lock`;
103
+ if (fs.existsSync(lockDir)) fs.rmSync(lockDir, { recursive: true, force: true });
104
+ } catch (e) {}
105
+
106
+ console.log('✅ Instance lama berhasil dihentikan.');
107
+ }
108
+
109
+ async function main() {
110
+ await stopRunningInstances();
111
+
112
+ // Beri jeda sejenak agar OS melepaskan socket port sepenuhnya
113
+ await new Promise((resolve) => setTimeout(resolve, 400));
114
+
115
+ console.log('🚀 Memulai ulang Antigravity Token Collector...');
116
+
117
+ const indexPath = path.join(__dirname, '..', 'index.js');
118
+ const args = process.argv.slice(2);
119
+ const child = spawn(process.execPath, [indexPath, ...args], {
120
+ cwd: path.join(__dirname, '..'),
121
+ stdio: 'inherit',
122
+ env: process.env,
123
+ });
124
+
125
+ child.on('exit', (code) => {
126
+ process.exit(code || 0);
127
+ });
128
+ }
129
+
130
+ main().catch((err) => {
131
+ console.error('❌ Gagal merestart aplikasi:', err);
132
+ process.exit(1);
133
+ });
package/build.md CHANGED
@@ -35,6 +35,8 @@ web/dist/
35
35
  npm version major # 1.0.0 → 2.0.0 (breaking change)
36
36
  ```
37
37
 
38
+ > **Tips**: Jika git working tree belum di-commit, gunakan flag `--no-git-tag-version`, misal: `npm version 1.1.4 --no-git-tag-version`.
39
+
38
40
  3. **Lint bersih**: `npm run lint`
39
41
  4. **Verifikasi isi paket** (yang penting — pastikan secret tidak ikut):
40
42
 
package/index.js CHANGED
@@ -30,6 +30,7 @@ let releaseLock = null;
30
30
  let trayManager = null;
31
31
  let menuOpen = false;
32
32
  let isHandoffInProgress = false;
33
+ let isRestartInProgress = false;
33
34
 
34
35
  async function acquireSingleInstanceLock() {
35
36
  if (!fs.existsSync(LOCK_FILE_PATH)) {
@@ -47,7 +48,7 @@ async function acquireSingleInstanceLock() {
47
48
  }
48
49
  }
49
50
 
50
- async function cleanupAndExit(exitCode = 0) {
51
+ async function prepareShutdown() {
51
52
  try {
52
53
  const tunnelManager = getTunnelManager();
53
54
  if (tunnelManager) {
@@ -65,7 +66,13 @@ async function cleanupAndExit(exitCode = 0) {
65
66
 
66
67
  try {
67
68
  if (server.listening) {
68
- await new Promise((resolve) => server.close(() => resolve()));
69
+ await new Promise((resolve) => {
70
+ const timer = setTimeout(() => resolve(), 300);
71
+ server.close(() => {
72
+ clearTimeout(timer);
73
+ resolve();
74
+ });
75
+ });
69
76
  }
70
77
  } catch (error) {}
71
78
 
@@ -75,10 +82,40 @@ async function cleanupAndExit(exitCode = 0) {
75
82
  releaseLock = null;
76
83
  }
77
84
  } catch (error) {}
85
+ }
78
86
 
87
+ async function cleanupAndExit(exitCode = 0) {
88
+ await prepareShutdown();
79
89
  process.exit(exitCode);
80
90
  }
81
91
 
92
+ async function restartServices(port = configuredPort) {
93
+ if (isRestartInProgress) return;
94
+ isRestartInProgress = true;
95
+ await prepareShutdown();
96
+
97
+ // Beri jeda kecil agar OS melepaskan lock file sepenuhnya
98
+ await new Promise((resolve) => setTimeout(resolve, 500));
99
+
100
+ const logFile = '/tmp/antigravity-tc-restart.log';
101
+ const out = fs.openSync(logFile, 'a');
102
+ const err = fs.openSync(logFile, 'a');
103
+
104
+ fs.writeSync(out, `\n--- RESTART SPARKED AT ${new Date().toISOString()} ---\n`);
105
+
106
+ const child = spawn(process.execPath, [path.join(__dirname, 'index.js'), '--hidden'], {
107
+ cwd: process.cwd(),
108
+ detached: true,
109
+ stdio: ['ignore', out, err],
110
+ env: {
111
+ ...process.env,
112
+ PORT: String(port),
113
+ },
114
+ });
115
+ child.unref();
116
+ process.exit(0);
117
+ }
118
+
82
119
  async function releaseInstanceLock() {
83
120
  if (!releaseLock) return;
84
121
  try {
@@ -144,6 +181,7 @@ async function main() {
144
181
  trayManager = new TrayManager({ config, port });
145
182
  await trayManager.start();
146
183
  trayManager.on('exit', () => cleanupAndExit(0));
184
+ trayManager.on('restart', () => restartServices(port));
147
185
 
148
186
  const menuContext = {
149
187
  trayManager,
@@ -158,6 +196,10 @@ async function main() {
158
196
  }
159
197
  }
160
198
 
199
+ process.on('antigravity-restart', () => {
200
+ restartServices(configuredPort);
201
+ });
202
+
161
203
  process.on('SIGINT', () => {
162
204
  cleanupAndExit(0);
163
205
  });
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "antigravity-tc",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Antigravity Token Collector — tray app untuk mengumpulkan & mengelola donasi token Antigravity ke 9Router",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "antigravity-tc": "./index.js",
8
- "antigravity-tc-reset-password": "./bin/reset-password.js"
8
+ "antigravity-tc-reset-password": "./bin/reset-password.js",
9
+ "antigravity-tc-restart": "./bin/restart.js"
9
10
  },
10
11
  "files": [
11
12
  "index.js",
@@ -24,7 +25,8 @@
24
25
  "build:web": "vite build --config web/vite.config.mjs",
25
26
  "lint": "eslint .",
26
27
  "prepublishOnly": "npm run lint && npm run build:web",
27
- "start": "node index.js"
28
+ "start": "node index.js",
29
+ "restart": "node bin/restart.js"
28
30
  },
29
31
  "keywords": [
30
32
  "antigravity",