antigravity-sidecar-kit 1.0.3 → 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/README.md +15 -1
- package/lib/rpc.mjs +58 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Google Antigravity's built-in `sidecar_sdk` provides a minimal `SidecarApp` loop
|
|
|
13
13
|
Generate a complete, Hangar-compatible Antigravity Auxiliary Pane sidecar in one command:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
|
|
16
|
+
npm create antigravity-sidecar my-sidecar
|
|
17
17
|
cd antigravity-my-sidecar
|
|
18
18
|
npm install
|
|
19
19
|
npm run setup
|
|
@@ -21,6 +21,8 @@ npm run setup
|
|
|
21
21
|
|
|
22
22
|
Options: `--dir <target-dir>` to choose the output folder, `--force` to overwrite an existing scaffold.
|
|
23
23
|
|
|
24
|
+
`npm create antigravity-sidecar` runs the [`create-antigravity-sidecar`](https://www.npmjs.com/package/create-antigravity-sidecar) launcher. `npx antigravity-sidecar-kit init my-sidecar` does the same thing.
|
|
25
|
+
|
|
24
26
|
What gets generated:
|
|
25
27
|
- `package.json` — ESM configuration with `setup`, `start`, `uninstall`, and `test` scripts.
|
|
26
28
|
- `sidecar.json` — Antigravity `SidecarManager` manifest (`SIDECAR_UI_ENTRYPOINT_AUX_PANE`).
|
|
@@ -88,6 +90,18 @@ await manageSidecar('my-sidecar', 'SIDECAR_ACTION_RESTART');
|
|
|
88
90
|
const customRes = await callLsRpc('GetMcpServerStates', {});
|
|
89
91
|
```
|
|
90
92
|
|
|
93
|
+
- **Cached & Deduplicated Calls (`createLsRpcCache`)**: Opt-in wrapper for RPCs your UI polls. Concurrent calls with the same method and body share one in-flight request, and successful results are kept for a per-method TTL (failures are never cached). The cache lives in your sidecar's Node process, so every iframe of that sidecar shares it. Cached results are shared objects: treat them as read-only.
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
import { createLsRpcCache } from 'antigravity-sidecar-kit/rpc';
|
|
97
|
+
|
|
98
|
+
const lsCache = createLsRpcCache({
|
|
99
|
+
ttlMs: { GetUserStatus: 30_000, GetCascadeTrajectorySteps: 2_000 },
|
|
100
|
+
defaultTtlMs: 0, // unlisted methods: only merge concurrent calls
|
|
101
|
+
});
|
|
102
|
+
const status = await lsCache.call('GetUserStatus', {});
|
|
103
|
+
lsCache.invalidate('GetUserStatus'); // after a write that changes it; invalidate() clears all
|
|
104
|
+
```
|
|
91
105
|
- **Automatic Credential Persistence**: When imported inside a running sidecar daemon (`ANTIGRAVITY_SIDECAR_WEB_PORT` + `ANTIGRAVITY_LS_ADDRESS`), `persistSidecarRuntime` caches `{ pid, webPort, lsAddress, csrfToken }` to `~/.gemini/antigravity/sidecar_data/<id>/data/runtime.json` (owner-only, mode `0600` on macOS/Linux) so CLI tools (`setup.mjs`) invoked from external terminals can talk to the Language Server even when env vars aren't exported in the user's shell.
|
|
92
106
|
- **Fallback Process Restarter (`killSidecarProcessByPortOrPid`)**: If the Language Server RPC is unreachable from an external terminal, sends `SIGTERM` to the sidecar so Antigravity's `"restart_policy": "always"` respawns the updated version. It only kills a process it can verify: the `runtime.json` PID must still own the recorded `webPort`, and when the port only comes from `logs/sidecar.log`, only `node` processes listening on it are stopped. A stale `runtime.json` is ignored.
|
|
93
107
|
|
package/lib/rpc.mjs
CHANGED
|
@@ -258,6 +258,64 @@ export async function callLsRpc(method, body = {}, options = {}) {
|
|
|
258
258
|
return parsed;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Opt-in cache in front of callLsRpc. Concurrent calls with the same method and body share one
|
|
263
|
+
* in-flight request, and successful results are kept for that method's TTL. Failures are never
|
|
264
|
+
* cached. Cached results are shared between callers, so treat them as read-only.
|
|
265
|
+
*
|
|
266
|
+
* @param {object} [opts]
|
|
267
|
+
* @param {Record<string, number>} [opts.ttlMs] Per-method TTL in ms; 0 only merges in-flight calls.
|
|
268
|
+
* @param {number} [opts.defaultTtlMs] TTL for methods missing from `ttlMs`.
|
|
269
|
+
* @param {Function} [opts.rpcFn] Underlying call, `(method, body, options) => Promise`.
|
|
270
|
+
* @param {Function} [opts.now] Clock, for tests.
|
|
271
|
+
* @returns {{ call: Function, invalidate: Function }} `invalidate(...methods)` drops those
|
|
272
|
+
* methods' entries (in flight or cached), or every entry when called with no arguments.
|
|
273
|
+
*/
|
|
274
|
+
export function createLsRpcCache({ ttlMs = {}, defaultTtlMs = 0, rpcFn = callLsRpc, now = Date.now } = {}) {
|
|
275
|
+
const entries = new Map();
|
|
276
|
+
|
|
277
|
+
function call(method, body = {}, options = {}) {
|
|
278
|
+
// One cache line per Language Server; the token, timeout and fetchFn don't change the answer
|
|
279
|
+
const key = `${getLsConfig(options).lsAddress}\n${method}\n${JSON.stringify(body)}`;
|
|
280
|
+
const hit = entries.get(key);
|
|
281
|
+
if (hit && (hit.pending || now() < hit.expiresAt)) return hit.promise;
|
|
282
|
+
|
|
283
|
+
const ttl = ttlMs[method] ?? defaultTtlMs;
|
|
284
|
+
const entry = { method, pending: true, expiresAt: 0, promise: null };
|
|
285
|
+
entry.promise = Promise.resolve()
|
|
286
|
+
.then(() => rpcFn(method, body, options))
|
|
287
|
+
.then(
|
|
288
|
+
(result) => {
|
|
289
|
+
// invalidate() ran during the call: keep the stale answer out of the cache
|
|
290
|
+
if (entries.get(key) === entry) {
|
|
291
|
+
entry.pending = false;
|
|
292
|
+
entry.expiresAt = now() + ttl;
|
|
293
|
+
if (ttl <= 0) entries.delete(key);
|
|
294
|
+
}
|
|
295
|
+
return result;
|
|
296
|
+
},
|
|
297
|
+
(err) => {
|
|
298
|
+
if (entries.get(key) === entry) entries.delete(key);
|
|
299
|
+
throw err;
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
entries.set(key, entry);
|
|
303
|
+
return entry.promise;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function invalidate(...methods) {
|
|
307
|
+
if (methods.length === 0) {
|
|
308
|
+
entries.clear();
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
for (const [key, entry] of entries) {
|
|
312
|
+
if (methods.includes(entry.method)) entries.delete(key);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return { call, invalidate };
|
|
317
|
+
}
|
|
318
|
+
|
|
261
319
|
export const getSidecars = (options = {}) => callLsRpc('GetSidecars', {}, options);
|
|
262
320
|
export const manageSidecar = (sidecarId, action, options = {}) =>
|
|
263
321
|
callLsRpc('ManageSidecar', { action, sidecarId }, options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antigravity-sidecar-kit",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Zero-dependency SDK and CLI scaffolder for Google Antigravity Auxiliary Pane sidecars (Connect RPC client, host theme & Tailwind proxy, BOM-safe config store, workspace URI matcher, and npx-safe installer).",
|
|
6
6
|
"license": "MIT",
|