dsh-agy-link 0.1.2

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 ADDED
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ ## 0.1.2 (2026-08-18)
4
+
5
+ - Fixed prompt assembly for returning sessions: the last-assistant scan now
6
+ stops at the newest assistant message, and the history digest covers exactly
7
+ the foreign turns since the watermark (previously it could re-send stale
8
+ history or swallow intermediate turns into the trailing prompt).
9
+ - Hardened the stream loop: spawn failures release the concurrency slot and
10
+ surface as `PROCESS_EXIT` instead of hanging; the collector is fail-safe.
11
+ - `/agy doctor` now exports the real last-run stdout ring (parser wiring).
12
+ - `agy_ask` one-shot runs pass `--print-timeout` consistent with the watchdog.
13
+
14
+ ## 0.1.1 (2026-08-18)
15
+
16
+ - Fixed cordis activation in app-less profiles (optional services are now
17
+ resolved via `ctx.get`, not `?`-suffixed inject entries).
18
+ - Added `providerRetryPolicy`: AUTH failures fail fast (no quota-burning
19
+ retries); transient process failures retry once.
20
+
21
+ ## 0.1.0 (2026-08-18)
22
+
23
+ Initial release.
24
+
25
+ - `antigravity` LLM provider route over `agy -p --output-format stream-json`.
26
+ - Streaming text / thinking / tool-activity mapping with token usage.
27
+ - Session ↔ conversation binding with history digest on first contact.
28
+ - Model discovery (`agy models`) with Gemini effort folding and fallback
29
+ catalog.
30
+ - Google login: `/agy auth` + `/agy auth-code` + GUI panel (QR + paste).
31
+ - `/agy` command family: status / auth / auth-code / models / mode /
32
+ effort / clear / doctor / help.
33
+ - `agy_ask` delegation tool (optional).
34
+ - Permission modes: plan / accept-edits / skip with red-flag UI on skip.
35
+ - `/agy doctor` diagnostics export with auth redaction.
36
+ - Auxiliary calls (compaction / session-title) via one-shot plan-mode turns.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-agy-link contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # dsh-agy-link
2
+
3
+ [![CI](https://img.shields.io/badge/CI-github--actions-blue)](.github/workflows/ci.yml) [![npm](https://img.shields.io/badge/npm-dsh--agy--link-red)](https://www.npmjs.com/package/dsh-agy-link)
4
+
5
+ Bring **Google Antigravity models into [DeepSeek Harness (DSH)]** — chat,
6
+ thinking, tool activity, and token usage from your Antigravity subscription,
7
+ driven by the official `agy` CLI.
8
+
9
+ English | [简体中文](README.zh.md)
10
+
11
+ ## What you get
12
+
13
+ - **A model route, not a proxy** — registers the `antigravity` provider in
14
+ DSH's model config; pick any Antigravity model from the `/model` picker.
15
+ - **Full streaming** — text, thinking (reasoning), and agy tool activity
16
+ (annotated as reasoning blocks), mapped onto DSH's native chunk protocol.
17
+ - **Session continuity** — each DSH session binds to a native agy
18
+ conversation (`--conversation`), so multi-turn context rides agy history
19
+ instead of re-sending everything.
20
+ - **Token usage** — input/output/thinking/cache tokens surface in DSH
21
+ usage accounting.
22
+ - **In-GUI Google login** — `/agy auth` prints the consent URL; the sidebar
23
+ panel adds a QR code and a paste box for the authorization code.
24
+ - **`agy_ask` tool** — let any DSH model delegate a one-shot task to an
25
+ Antigravity model (the AskAntigravity pattern).
26
+ - **`/agy` commands** — `status`, `auth`, `auth-code`, `models`, `mode`,
27
+ `effort`, `clear`, `doctor`, `help`.
28
+ - **Dormant-safe** — no agy binary? Not signed in? The plugin loads anyway
29
+ and tells you what to fix.
30
+
31
+ ## Requirements
32
+
33
+ - DSH (DeepSeek Harness) with a web profile
34
+ - Node.js >= 24
35
+ - The official `agy` CLI >= 1.1.8, installed and on `PATH`
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ dsh plugin --profile web add dsh-agy-link
41
+ ```
42
+
43
+ Restart DSH, then run `/agy status` in the GUI.
44
+
45
+ ## Login (one time)
46
+
47
+ 1. `/agy auth` — prints a Google consent URL.
48
+ 2. Open it, approve access, copy the authorization code.
49
+ 3. `/agy auth-code <code>` — or use the sidebar panel (QR + paste box).
50
+
51
+ The plugin never reads, copies, or moves
52
+ `~/.gemini/antigravity-cli/antigravity-oauth-token`; login is driven
53
+ entirely through the official CLI's own flow.
54
+
55
+ ## Configuration
56
+
57
+ Config lives in the `agy-link` plugin entry (edit via `/plugin` or the
58
+ profile patch layer). Environment variables override the file:
59
+
60
+ | Key | Env | Default | Meaning |
61
+ | --- | --- | --- | --- |
62
+ | enabled | `DSH_AGY_ENABLED` | `true` | master switch |
63
+ | agyBin | `DSH_AGY_BIN` | auto | explicit agy binary path |
64
+ | permissionMode | `DSH_AGY_MODE` | `skip` | `skip` / `plan` / `accept-edits` (below) |
65
+ | defaultModel | `DSH_AGY_DEFAULT_MODEL` | `(agy default)` | model slug |
66
+ | defaultEffort | `DSH_AGY_DEFAULT_EFFORT` | `(model default)` | `low` / `medium` / `high` |
67
+ | timeoutMs | `DSH_AGY_TIMEOUT_MS` | `600000` | per-turn watchdog |
68
+ | extraArgs | `DSH_AGY_EXTRA_ARGS` | — | extra agy flags, space-separated |
69
+
70
+ ### Permission modes — read this
71
+
72
+ agy runs its **own** tool loop with its own file edits and shell commands.
73
+ The bridge maps that to one of three modes:
74
+
75
+ - **plan** — read-only; the safe default for trying the bridge.
76
+ - **accept-edits** — agy may edit files without asking.
77
+ - **skip** — `--dangerously-skip-permissions`: agy runs **every** tool
78
+ without approval. Non-interactive DSH turns need this (or plan) because
79
+ agy permission prompts hang print mode, but it means real unattended
80
+ write/exec access. The settings panel shows this button in red.
81
+
82
+ DSH-side tools and permissions are unaffected — this only governs what the
83
+ spawned agy process may do in its workspace.
84
+
85
+ ## How it works
86
+
87
+ One DSH model call = one short-lived `agy -p --output-format stream-json`
88
+ process. The NDJSON event stream is parsed, normalized, and mapped to
89
+ DSH StreamChunks: thinking → reasoning blocks, text → text blocks, tool
90
+ activity → annotated reasoning blocks, and the result envelope → usage +
91
+ finish. Conversation ids are discovered from the stream itself with a
92
+ conversations-directory snapshot diff as fallback. No reverse-engineered
93
+ database scraping, no protobuf decoding, no token file access.
94
+
95
+ ## Known gaps
96
+
97
+ See [docs/KNOWN-GAPS.md](docs/KNOWN-GAPS.md). Notably: images are not
98
+ forwarded (agy print mode is text-only), and DSH tools are not exposed to
99
+ agy (its MCP reverse bridge is future work).
100
+
101
+ ## Disclaimer & risk note
102
+
103
+ This plugin spawns the **official, unmodified** `agy` binary and consumes
104
+ its public print-mode output. It does not decode internal databases or
105
+ touch OAuth tokens. Automating a subscription CLI may still conflict with
106
+ the provider's terms of service (Google Antigravity ToS §6 context); use
107
+ it at your own risk, and stop if asked to.
108
+
109
+ ## License
110
+
111
+ MIT
112
+
113
+ [DeepSeek Harness (DSH)]: https://npmjs.com/package/@deepseek-ai/dsh
package/README.zh.md ADDED
@@ -0,0 +1,101 @@
1
+ # dsh-agy-link(简体中文)
2
+
3
+ 把 **Google Antigravity 模型接入 [DeepSeek Harness (DSH)]** —— 由官方
4
+ `agy` CLI 驱动,完整支持对话输出、思考(thinking)、工具活动与 token
5
+ 用量,全部走你的 Antigravity 订阅。
6
+
7
+ [English](README.md) | 简体中文
8
+
9
+ ## 你能得到什么
10
+
11
+ - **模型路由而非代理** —— 在 DSH 模型配置里注册 `antigravity` 提供方,
12
+ `/model` 选择器里直接挑任意 Antigravity 模型。
13
+ - **完整流式输出** —— 文本、思考、agy 工具活动(注记为 reasoning 块),
14
+ 全部映射到 DSH 原生流协议。
15
+ - **会话连续性** —— 每个 DSH 会话绑定原生 agy 会话(`--conversation`),
16
+ 多轮上下文由 agy 自己的历史承载,不重发全量。
17
+ - **token 用量** —— 输入/输出/思考/缓存 token 全部进入 DSH 用量统计。
18
+ - **GUI 内 Google 登录** —— `/agy auth` 打印授权 URL;侧栏面板提供
19
+ 二维码 + 授权码粘贴框。
20
+ - **`agy_ask` 工具** —— 任何 DSH 模型都可以把一次性任务委托给
21
+ Antigravity 模型(AskAntigravity 模式)。
22
+ - **`/agy` 命令族** —— `status` / `auth` / `auth-code` / `models` /
23
+ `mode` / `effort` / `clear` / `doctor` / `help`。
24
+ - **休眠安全** —— 没装 agy?没登录?插件照常加载并告诉你怎么修。
25
+
26
+ ## 环境要求
27
+
28
+ - DSH(DeepSeek Harness)web profile
29
+ - Node.js >= 24
30
+ - 官方 `agy` CLI >= 1.1.8,已安装并在 `PATH` 上
31
+
32
+ ## 安装
33
+
34
+ ```bash
35
+ dsh plugin --profile web add dsh-agy-link
36
+ ```
37
+
38
+ 重启 DSH,在 GUI 里执行 `/agy status`。
39
+
40
+ ## 登录(一次性)
41
+
42
+ 1. `/agy auth` —— 打印 Google 授权 URL。
43
+ 2. 打开链接、批准访问、复制授权码。
44
+ 3. `/agy auth-code <授权码>` —— 或用侧栏面板(二维码 + 粘贴框)。
45
+
46
+ 插件绝不读取/复制/移动
47
+ `~/.gemini/antigravity-cli/antigravity-oauth-token`;登录完全通过官方
48
+ CLI 自己的流程完成。
49
+
50
+ ## 配置
51
+
52
+ 配置在 `agy-link` 插件条目里(`/plugin` 或 profile patch 层编辑)。
53
+ 环境变量优先于文件:
54
+
55
+ | 键 | 环境变量 | 默认值 | 含义 |
56
+ | --- | --- | --- | --- |
57
+ | enabled | `DSH_AGY_ENABLED` | `true` | 总开关 |
58
+ | agyBin | `DSH_AGY_BIN` | 自动 | 显式 agy 路径 |
59
+ | permissionMode | `DSH_AGY_MODE` | `skip` | `skip` / `plan` / `accept-edits`(见下) |
60
+ | defaultModel | `DSH_AGY_DEFAULT_MODEL` | `(agy 默认)` | 模型 slug |
61
+ | defaultEffort | `DSH_AGY_DEFAULT_EFFORT` | `(模型默认)` | `low` / `medium` / `high` |
62
+ | timeoutMs | `DSH_AGY_TIMEOUT_MS` | `600000` | 单轮看门狗 |
63
+ | extraArgs | `DSH_AGY_EXTRA_ARGS` | — | 附加 agy 参数(空格分隔) |
64
+
65
+ ### 权限模式 —— 必读
66
+
67
+ agy 有**自己的**工具循环,会自己做文件编辑和 shell 执行。桥接层映射为
68
+ 三种模式:
69
+
70
+ - **plan** —— 只读,体验桥接的安全默认。
71
+ - **accept-edits** —— agy 可以不经询问改文件。
72
+ - **skip** —— `--dangerously-skip-permissions`:agy **所有**工具免审批。
73
+ 非交互 DSH 轮次需要它(或 plan),因为 agy 的权限提示会卡死 print
74
+ 模式;但这意味着真实的无人值守 写/执行 权限。设置面板里这个按钮是
75
+ 红色的。
76
+
77
+ DSH 侧的工具与权限不受影响 —— 这里只约束 agy 子进程在自己工作区能做什么。
78
+
79
+ ## 工作原理
80
+
81
+ 一次 DSH 模型调用 = 一个短生命周期 `agy -p --output-format stream-json`
82
+ 进程。NDJSON 事件流被解析、归一化并映射为 DSH StreamChunk:思考 →
83
+ reasoning 块,文本 → text 块,工具活动 → 注记 reasoning 块,结果信封 →
84
+ usage + finish。会话 id 优先取自流本身,conversations 目录快照对比作为
85
+ 兜底。不逆向数据库、不解码 protobuf、不碰 token 文件。
86
+
87
+ ## 已知差距
88
+
89
+ 见 [docs/KNOWN-GAPS.md](docs/KNOWN-GAPS.md)。要点:图片不转发(agy print
90
+ 模式纯文本);DSH 工具不暴露给 agy(MCP 反向桥接是后续工作)。
91
+
92
+ ## 免责与风险提示
93
+
94
+ 本插件只启动**官方未修改**的 `agy` 二进制并消费其公开 print 输出,
95
+ 不解码内部数据库、不触碰 OAuth token。自动化订阅 CLI 仍可能与提供方
96
+ 服务条款冲突(Google Antigravity ToS §6 语境);风险自负,被要求停止时
97
+ 请停止。
98
+
99
+ ## 许可证
100
+
101
+ MIT
@@ -0,0 +1,14 @@
1
+ # dsh-agy-link bundle patch — inserts the antigravity (agy CLI) provider
2
+ # plugin entry into the composition. Users install via:
3
+ # dsh plugin --profile web add dsh-agy-link
4
+ - insert:
5
+ - id: agy-link
6
+ name: 'dsh-agy-link'
7
+ config:
8
+ enabled: !!js "process.env.DSH_AGY_ENABLED ?? 'true'"
9
+ agyBin: !!js "process.env.DSH_AGY_BIN ?? ''"
10
+ # permissionMode: skip | plan | accept-edits (skip passes
11
+ # --dangerously-skip-permissions; see README security notes)
12
+ permissionMode: !!js "process.env.DSH_AGY_MODE ?? 'skip'"
13
+ defaultModel: !!js "process.env.DSH_AGY_DEFAULT_MODEL ?? ''"
14
+ defaultEffort: !!js "process.env.DSH_AGY_DEFAULT_EFFORT ?? ''"
package/dist/client.js ADDED
@@ -0,0 +1,262 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-agy-link",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region src/client/index.ts
8
+ const { createElement: h, useState, useEffect } = require("react");
9
+ const reactDom = require("react-dom");
10
+ const win = globalThis;
11
+ const bodyEl = win.document?.body;
12
+ const portalToBody = bodyEl != null && reactDom.createPortal ? (node) => reactDom.createPortal(node, bodyEl) : (node) => node;
13
+ const name = "dsh-agy-link-client";
14
+ const inject = ["slots"];
15
+ const base = win.location?.origin ?? "";
16
+ async function getStatus() {
17
+ try {
18
+ const res = await win.fetch?.(base + "/plugins/agy-link/status");
19
+ if (!res || !res.ok) return null;
20
+ return await res.json();
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ async function postJson(path, body) {
26
+ try {
27
+ return await win.fetch?.(base + path, {
28
+ method: "POST",
29
+ headers: { "Content-Type": "application/json" },
30
+ body: JSON.stringify(body)
31
+ }) ?? null;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+ const S = {
37
+ row: {
38
+ display: "flex",
39
+ alignItems: "center",
40
+ gap: "8px",
41
+ marginBottom: "8px"
42
+ },
43
+ btn: {
44
+ padding: "4px 10px",
45
+ borderRadius: "6px",
46
+ border: "1px solid rgba(128,128,128,0.4)",
47
+ background: "rgba(128,128,128,0.12)",
48
+ color: "inherit",
49
+ cursor: "pointer",
50
+ fontSize: "12px"
51
+ },
52
+ btnPrimary: {
53
+ padding: "6px 12px",
54
+ borderRadius: "6px",
55
+ border: "1px solid rgba(64,140,255,0.6)",
56
+ background: "rgba(64,140,255,0.25)",
57
+ color: "inherit",
58
+ cursor: "pointer",
59
+ fontSize: "13px"
60
+ },
61
+ input: {
62
+ flex: "1",
63
+ padding: "6px 8px",
64
+ borderRadius: "6px",
65
+ border: "1px solid rgba(128,128,128,0.4)",
66
+ background: "transparent",
67
+ color: "inherit",
68
+ fontSize: "12px"
69
+ },
70
+ muted: {
71
+ color: "#9aa0a6",
72
+ fontSize: "12px"
73
+ }
74
+ };
75
+ function apply(ctx) {
76
+ const AgyPanel = () => {
77
+ const [open, setOpen] = useState(false);
78
+ const [status, setStatus] = useState(null);
79
+ const [code, setCode] = useState("");
80
+ const [busy, setBusy] = useState(false);
81
+ const [notice, setNotice] = useState("");
82
+ useEffect(() => {
83
+ if (!open) return;
84
+ let alive = true;
85
+ const tick = async () => {
86
+ const st = await getStatus();
87
+ if (alive) setStatus(st);
88
+ };
89
+ tick();
90
+ const timer = setInterval(tick, 3e3);
91
+ return () => {
92
+ alive = false;
93
+ clearInterval(timer);
94
+ };
95
+ }, [open]);
96
+ const dot = status === null ? "#9aa0a6" : status.dormantReason ? "#f5a623" : status.auth?.phase === "pending" || status.auth?.phase === "failed" || status.auth?.phase === "submitting" ? "#f5a623" : status.lastRun && !status.lastRun.ok ? "#e5484d" : "#30a46c";
97
+ const startAuth = async () => {
98
+ setBusy(true);
99
+ setNotice("");
100
+ await postJson("/plugins/agy-link/auth", {});
101
+ const st = await getStatus();
102
+ setStatus(st);
103
+ setBusy(false);
104
+ };
105
+ const submitCode = async () => {
106
+ if (code.trim() === "") return;
107
+ setBusy(true);
108
+ await postJson("/plugins/agy-link/auth-code", { code: code.trim() });
109
+ setCode("");
110
+ const st = await getStatus();
111
+ setStatus(st);
112
+ setNotice(st?.auth?.message ?? "");
113
+ setBusy(false);
114
+ };
115
+ const setCfg = async (key, value) => {
116
+ setBusy(true);
117
+ await postJson("/plugins/agy-link/config", {
118
+ key,
119
+ value
120
+ });
121
+ const st = await getStatus();
122
+ setStatus(st);
123
+ setBusy(false);
124
+ };
125
+ const badge = h("button", {
126
+ type: "button",
127
+ onClick: () => setOpen(!open),
128
+ style: {
129
+ background: "transparent",
130
+ border: "none",
131
+ cursor: "pointer",
132
+ padding: "2px 6px",
133
+ fontSize: "14px",
134
+ lineHeight: 1
135
+ },
136
+ title: "Antigravity (agy) bridge"
137
+ }, h("span", { style: {
138
+ display: "inline-block",
139
+ width: "9px",
140
+ height: "9px",
141
+ borderRadius: "50%",
142
+ background: dot
143
+ } }));
144
+ if (!open) return badge;
145
+ const needsAuth = status !== null && !status.dormantReason && status.auth !== void 0 && status.auth.phase !== "ok";
146
+ const dormantLine = h("div", null, "Bridge dormant: ", status?.dormantReason ?? "");
147
+ const infoBlock = h("div", { style: {
148
+ ...S.muted,
149
+ lineHeight: 1.5
150
+ } }, h("div", null, "agy: ", status?.bin ?? "not found", " — v", status?.version ?? "?"), h("div", null, "auth: ", status?.auth?.phase ?? "unknown"), h("div", null, "models: ", String(status?.catalog?.count ?? 0), " — ", status?.catalog?.source ?? ""), h("div", null, "bindings: ", String(status?.bindings ?? 0)), h("div", null, "last run: ", status?.lastRun ? (status.lastRun.ok ? "ok" : status.lastRun.code) + " — " + status.lastRun.model : "none"));
151
+ const modeRow = h("div", { style: {
152
+ ...S.row,
153
+ marginTop: "8px"
154
+ } }, h("span", { style: S.muted }, "mode:"), h("button", {
155
+ type: "button",
156
+ style: status?.permissionMode === "plan" ? S.btnPrimary : S.btn,
157
+ onClick: () => void setCfg("permissionMode", "plan")
158
+ }, "plan"), h("button", {
159
+ type: "button",
160
+ style: status?.permissionMode === "accept-edits" ? S.btnPrimary : S.btn,
161
+ onClick: () => void setCfg("permissionMode", "accept-edits")
162
+ }, "accept-edits"), h("button", {
163
+ type: "button",
164
+ style: status?.permissionMode === "skip" ? {
165
+ ...S.btn,
166
+ color: "#e5484d",
167
+ borderColor: "rgba(229,72,77,0.6)"
168
+ } : S.btn,
169
+ onClick: () => void setCfg("permissionMode", "skip")
170
+ }, "skip"));
171
+ const effortRow = h("div", { style: S.row }, h("span", { style: S.muted }, "effort:"), h("button", {
172
+ type: "button",
173
+ style: status?.defaultEffort === "low" ? S.btnPrimary : S.btn,
174
+ onClick: () => void setCfg("defaultEffort", "low")
175
+ }, "low"), h("button", {
176
+ type: "button",
177
+ style: status?.defaultEffort === "medium" ? S.btnPrimary : S.btn,
178
+ onClick: () => void setCfg("defaultEffort", "medium")
179
+ }, "medium"), h("button", {
180
+ type: "button",
181
+ style: status?.defaultEffort === "high" ? S.btnPrimary : S.btn,
182
+ onClick: () => void setCfg("defaultEffort", "high")
183
+ }, "high"), h("button", {
184
+ type: "button",
185
+ style: status?.defaultEffort === "" ? S.btnPrimary : S.btn,
186
+ onClick: () => void setCfg("defaultEffort", "")
187
+ }, "auto"));
188
+ const skipWarn = h("div", { style: S.muted }, "skip runs agy tools with --dangerously-skip-permissions (no approval prompts). plan is the safe read-only default.");
189
+ const authBlock = h("div", null, h("div", { style: {
190
+ fontWeight: 600,
191
+ marginBottom: "4px"
192
+ } }, "Google login required"), h("div", null, "agy is not signed in. Start the login flow, scan the QR or open the URL, then paste the authorization code below."), h("img", {
193
+ src: base + "/plugins/agy-link/qr",
194
+ alt: "auth QR",
195
+ width: 200,
196
+ height: 200,
197
+ style: {
198
+ display: "block",
199
+ margin: "8px auto"
200
+ }
201
+ }), h("div", { style: { wordBreak: "break-all" } }, status?.auth?.url ?? ""), h("div", { style: S.row }, h("input", {
202
+ style: S.input,
203
+ value: code,
204
+ placeholder: "authorization code",
205
+ onChange: (e) => setCode(e.target.value)
206
+ }), h("button", {
207
+ type: "button",
208
+ style: S.btnPrimary,
209
+ disabled: busy,
210
+ onClick: () => void submitCode()
211
+ }, "Submit code"), h("button", {
212
+ type: "button",
213
+ style: S.btn,
214
+ disabled: busy,
215
+ onClick: () => void startAuth()
216
+ }, "Restart login")), notice !== "" ? h("div", null, notice) : null);
217
+ const body = status?.dormantReason ? dormantLine : needsAuth ? authBlock : h("div", null, infoBlock, modeRow, effortRow, skipWarn);
218
+ const header = h("div", { style: {
219
+ display: "flex",
220
+ justifyContent: "space-between",
221
+ alignItems: "center",
222
+ marginBottom: "8px"
223
+ } }, h("span", { style: {
224
+ fontWeight: 600,
225
+ fontSize: "13px"
226
+ } }, "Antigravity bridge"), h("button", {
227
+ type: "button",
228
+ style: S.btn,
229
+ onClick: () => setOpen(false)
230
+ }, "close"));
231
+ const popover = h("div", { style: {
232
+ position: "fixed",
233
+ bottom: "56px",
234
+ left: "16px",
235
+ width: "300px",
236
+ maxHeight: "70vh",
237
+ overflowY: "auto",
238
+ padding: "12px 14px",
239
+ borderRadius: "10px",
240
+ border: "1px solid rgba(128,128,128,0.35)",
241
+ background: "rgba(28,28,32,0.98)",
242
+ boxShadow: "0 8px 32px rgba(0,0,0,0.5)",
243
+ zIndex: 1e3
244
+ } }, header, body);
245
+ return h("div", { style: { display: "contents" } }, badge, portalToBody(popover));
246
+ };
247
+ ctx.slots.inject("sidebar.footer.action", () => {
248
+ return ctx.slots.register({
249
+ name: "ui-sidebar",
250
+ id: "agy-link",
251
+ order: 30,
252
+ label: "Antigravity"
253
+ }, AgyPanel);
254
+ });
255
+ }
256
+ //#endregion
257
+ exports.apply = apply;
258
+ exports.inject = inject;
259
+ exports.name = name;
260
+ return module.exports;
261
+ }
262
+ });
@@ -0,0 +1,7 @@
1
+ import { Context } from "@deepseek-ai/cordis";
2
+ //#region src/index.d.ts
3
+ declare const name = "dsh-agy-link";
4
+ declare const inject: string[];
5
+ declare function apply(ctx: Context, entryConfig?: Record<string, unknown>): void;
6
+ //#endregion
7
+ export { apply, inject, name };