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.
Files changed (3) hide show
  1. package/README.md +15 -1
  2. package/lib/rpc.mjs +58 -0
  3. 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
- npx antigravity-sidecar-kit init my-sidecar
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",
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",