opencode-copilot-statusline 0.0.0 → 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/LICENSE +21 -0
- package/README.md +54 -0
- package/README.zh-CN.md +54 -0
- package/dist/index.js +139 -0
- package/dist/rpc.js +32 -0
- package/dist/tui.js +187 -0
- package/package.json +57 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lnwu
|
|
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,54 @@
|
|
|
1
|
+
# 📊 opencode-copilot-statusline
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/opencode-copilot-statusline)
|
|
4
|
+
[](https://github.com/lnwu/opencode-statusline-plugins/blob/main/packages/opencode-copilot-statusline/LICENSE)
|
|
5
|
+
|
|
6
|
+
**English** | [简体中文](https://github.com/lnwu/opencode-statusline-plugins/blob/main/packages/opencode-copilot-statusline/README.zh-CN.md)
|
|
7
|
+
|
|
8
|
+
Display your GitHub Copilot quota in the OpenCode TUI footer statusline.
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- Live used-quota percentage in the prompt footer (`Copilot 87%`)
|
|
13
|
+
- Monthly reset countdown when the footer details are expanded (or the terminal is wide enough)
|
|
14
|
+
- Color feedback: subdued normally, info at ≥70%, error at ≥90%
|
|
15
|
+
- Only appears for sessions whose model provider is `github-copilot`
|
|
16
|
+
- Usage refreshes about once per minute
|
|
17
|
+
|
|
18
|
+
## 📋 Requirements
|
|
19
|
+
|
|
20
|
+
- OpenCode@2
|
|
21
|
+
- A GitHub Copilot subscription connected to OpenCode (`opencode auth login github-copilot`)
|
|
22
|
+
|
|
23
|
+
## 🚀 Install
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
opencode plugin add opencode-copilot-statusline
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 📈 What the percentage means
|
|
30
|
+
|
|
31
|
+
The statusline picks the quota window that limits your plan:
|
|
32
|
+
|
|
33
|
+
| Plan | Source | Example |
|
|
34
|
+
| ------------------------------------------------ | ------------------------------------------------------ | -------------- |
|
|
35
|
+
| Pro / Business / Enterprise | **premium requests** used this month | `Copilot 87%` |
|
|
36
|
+
| Copilot Free (no premium-request quota) | **chat** allowance | `Copilot 6%` |
|
|
37
|
+
| Unlimited premium requests | no finite quota | `Copilot ∞` |
|
|
38
|
+
| Not signed in, credential unresolvable, API down | no data | `Copilot —` |
|
|
39
|
+
|
|
40
|
+
The countdown targets the monthly quota reset date.
|
|
41
|
+
|
|
42
|
+
## 🩺 Troubleshooting
|
|
43
|
+
|
|
44
|
+
- **The statusline never shows up** — it is hidden unless the session's model provider is `github-copilot`. Switch to a GitHub Copilot model in the session.
|
|
45
|
+
- **It shows `Copilot —` instead of numbers** — the `github-copilot` credential could not be resolved. Run `opencode auth login github-copilot`, then check the plugin is enabled again.
|
|
46
|
+
- **It shows `Copilot ∞`** — your plan reports no finite premium-request limit.
|
|
47
|
+
|
|
48
|
+
## 📜 Changelog
|
|
49
|
+
|
|
50
|
+
See the [releases](https://github.com/lnwu/opencode-statusline-plugins/releases) for the release history.
|
|
51
|
+
|
|
52
|
+
## 📄 License
|
|
53
|
+
|
|
54
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# 📊 opencode-copilot-statusline
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/opencode-copilot-statusline)
|
|
4
|
+
[](https://github.com/lnwu/opencode-statusline-plugins/blob/main/packages/opencode-copilot-statusline/LICENSE)
|
|
5
|
+
|
|
6
|
+
[English](https://github.com/lnwu/opencode-statusline-plugins/blob/main/packages/opencode-copilot-statusline/README.md) | **简体中文**
|
|
7
|
+
|
|
8
|
+
在 OpenCode TUI 底栏显示 GitHub Copilot 配额用量。
|
|
9
|
+
|
|
10
|
+
## ✨ 功能
|
|
11
|
+
|
|
12
|
+
- 在输入框底栏实时显示配额已用百分比(`Copilot 87%`)
|
|
13
|
+
- 展开底栏详情(或终端足够宽)时显示每月重置倒计时
|
|
14
|
+
- 颜色提示:正常为弱化色,≥70% 变提示色,≥90% 变错误色
|
|
15
|
+
- 仅当会话模型 provider 为 `github-copilot` 时显示
|
|
16
|
+
- 用量约每分钟自动刷新一次
|
|
17
|
+
|
|
18
|
+
## 📋 环境要求
|
|
19
|
+
|
|
20
|
+
- OpenCode@2
|
|
21
|
+
- 已在 OpenCode 登录 GitHub Copilot(`opencode auth login github-copilot`)
|
|
22
|
+
|
|
23
|
+
## 🚀 安装
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
opencode plugin add opencode-copilot-statusline
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 📈 百分比的含义
|
|
30
|
+
|
|
31
|
+
状态栏会自动选取限制你当前计划的配额:
|
|
32
|
+
|
|
33
|
+
| 计划 | 来源 | 示例 |
|
|
34
|
+
| ------------------------------------------- | ----------------------------- | ------------- |
|
|
35
|
+
| Pro / Business / Enterprise | 本月已用的 **premium requests** | `Copilot 87%` |
|
|
36
|
+
| Copilot Free(没有 premium requests 配额) | **chat** 额度 | `Copilot 6%` |
|
|
37
|
+
| premium requests 无限 | 没有有限配额 | `Copilot ∞` |
|
|
38
|
+
| 未登录、凭证无法解析、接口异常 | 无数据 | `Copilot —` |
|
|
39
|
+
|
|
40
|
+
倒计时指向每月配额重置日期。
|
|
41
|
+
|
|
42
|
+
## 🩺 疑难排查
|
|
43
|
+
|
|
44
|
+
- **底栏始终不显示** —— 仅当会话模型 provider 为 `github-copilot` 时才显示,请先在会话中切换到 GitHub Copilot 模型。
|
|
45
|
+
- **只显示 `Copilot —` 而没有数字** —— 无法解析 `github-copilot` 凭据。请运行 `opencode auth login github-copilot`,然后确认插件已重新启用。
|
|
46
|
+
- **显示 `Copilot ∞`** —— 你的计划没有有限的 premium requests 上限。
|
|
47
|
+
|
|
48
|
+
## 📜 变更日志
|
|
49
|
+
|
|
50
|
+
完整发布历史见 [Releases](https://github.com/lnwu/opencode-statusline-plugins/releases)。
|
|
51
|
+
|
|
52
|
+
## 📄 许可证
|
|
53
|
+
|
|
54
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/index.ts
|
|
3
|
+
import { Plugin } from "@opencode/plugin";
|
|
4
|
+
|
|
5
|
+
// src/rpc.ts
|
|
6
|
+
import { Rpc } from "@opencode/plugin/rpc";
|
|
7
|
+
var usageSchema = {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
usedPercent: { type: "number" },
|
|
11
|
+
remaining: { type: "number" },
|
|
12
|
+
entitlement: { type: "number" },
|
|
13
|
+
unlimited: { type: "boolean" },
|
|
14
|
+
resetsAt: { type: "string" }
|
|
15
|
+
},
|
|
16
|
+
required: ["usedPercent", "remaining", "entitlement", "unlimited"],
|
|
17
|
+
additionalProperties: false
|
|
18
|
+
};
|
|
19
|
+
var UsageRpc = Rpc.define({
|
|
20
|
+
id: "opencode-copilot-statusline",
|
|
21
|
+
methods: {
|
|
22
|
+
get: {
|
|
23
|
+
input: { type: "object", properties: {}, additionalProperties: false },
|
|
24
|
+
output: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: { usage: usageSchema },
|
|
27
|
+
additionalProperties: false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
events: {}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// src/usage.ts
|
|
35
|
+
var WINDOW_IDS = ["premium_interactions", "chat"];
|
|
36
|
+
function parseUsage(body) {
|
|
37
|
+
if (!isRecord(body))
|
|
38
|
+
return;
|
|
39
|
+
const snapshots = body.quota_snapshots;
|
|
40
|
+
if (!isRecord(snapshots))
|
|
41
|
+
return;
|
|
42
|
+
const resetsAt = typeof body.quota_reset_date_utc === "string" && body.quota_reset_date_utc || (typeof body.quota_reset_date === "string" ? body.quota_reset_date : undefined);
|
|
43
|
+
for (const id of WINDOW_IDS) {
|
|
44
|
+
const window = snapshots[id];
|
|
45
|
+
if (!isRecord(window))
|
|
46
|
+
continue;
|
|
47
|
+
const usage = toUsage(window, resetsAt);
|
|
48
|
+
if (usage)
|
|
49
|
+
return usage;
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
function toUsage(window, resetsAt) {
|
|
54
|
+
const reset = resetsAt ? { resetsAt } : {};
|
|
55
|
+
if (window.unlimited === true) {
|
|
56
|
+
return { usedPercent: 0, remaining: 0, entitlement: 0, unlimited: true, ...reset };
|
|
57
|
+
}
|
|
58
|
+
if (window.has_quota === false)
|
|
59
|
+
return;
|
|
60
|
+
const entitlement = asNumber(window.entitlement);
|
|
61
|
+
if (entitlement !== undefined && entitlement <= 0)
|
|
62
|
+
return;
|
|
63
|
+
const remaining = asNumber(window.remaining);
|
|
64
|
+
let percent;
|
|
65
|
+
if (entitlement !== undefined && remaining !== undefined) {
|
|
66
|
+
percent = (1 - remaining / entitlement) * 100;
|
|
67
|
+
} else if (typeof window.percent_remaining === "number") {
|
|
68
|
+
percent = 100 - window.percent_remaining;
|
|
69
|
+
}
|
|
70
|
+
if (percent === undefined || !Number.isFinite(percent))
|
|
71
|
+
return;
|
|
72
|
+
return {
|
|
73
|
+
usedPercent: Math.min(100, Math.max(0, Math.round(percent))),
|
|
74
|
+
remaining: remaining ?? 0,
|
|
75
|
+
entitlement: entitlement ?? 0,
|
|
76
|
+
unlimited: false,
|
|
77
|
+
...reset
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function isRecord(value) {
|
|
81
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
82
|
+
}
|
|
83
|
+
function asNumber(value) {
|
|
84
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/index.ts
|
|
88
|
+
var INTEGRATION_ID = "github-copilot";
|
|
89
|
+
var GITHUB_API = "https://api.github.com";
|
|
90
|
+
var API_VERSION = "2025-04-01";
|
|
91
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
92
|
+
function apiBase(credential) {
|
|
93
|
+
const enterprise = credential.metadata?.enterpriseUrl;
|
|
94
|
+
if (typeof enterprise === "string" && enterprise) {
|
|
95
|
+
return `https://api.${enterprise.replace(/\/+$/, "")}`;
|
|
96
|
+
}
|
|
97
|
+
return GITHUB_API;
|
|
98
|
+
}
|
|
99
|
+
var src_default = Plugin.define({
|
|
100
|
+
id: "opencode-copilot-statusline",
|
|
101
|
+
async setup(ctx) {
|
|
102
|
+
async function fetchUsage() {
|
|
103
|
+
try {
|
|
104
|
+
const connection = await ctx.integration.connection.active(INTEGRATION_ID);
|
|
105
|
+
if (!connection)
|
|
106
|
+
return;
|
|
107
|
+
const credential = await ctx.integration.connection.resolve(connection);
|
|
108
|
+
if (credential?.type !== "oauth" && credential?.type !== "key")
|
|
109
|
+
return;
|
|
110
|
+
const token = credential.type === "oauth" ? credential.access || credential.refresh : credential.key;
|
|
111
|
+
if (!token)
|
|
112
|
+
return;
|
|
113
|
+
const res = await fetch(`${apiBase(credential)}/copilot_internal/user`, {
|
|
114
|
+
headers: {
|
|
115
|
+
Accept: "application/json",
|
|
116
|
+
Authorization: `Bearer ${token}`,
|
|
117
|
+
"X-GitHub-Api-Version": API_VERSION,
|
|
118
|
+
"User-Agent": "opencode-copilot-statusline"
|
|
119
|
+
},
|
|
120
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
121
|
+
});
|
|
122
|
+
if (!res.ok)
|
|
123
|
+
return;
|
|
124
|
+
return parseUsage(await res.json());
|
|
125
|
+
} catch {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
await ctx.rpc.register(UsageRpc, {
|
|
130
|
+
get: async () => {
|
|
131
|
+
const usage = await fetchUsage();
|
|
132
|
+
return usage ? { usage } : {};
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
export {
|
|
138
|
+
src_default as default
|
|
139
|
+
};
|
package/dist/rpc.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/rpc.ts
|
|
3
|
+
import { Rpc } from "@opencode/plugin/rpc";
|
|
4
|
+
var usageSchema = {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
usedPercent: { type: "number" },
|
|
8
|
+
remaining: { type: "number" },
|
|
9
|
+
entitlement: { type: "number" },
|
|
10
|
+
unlimited: { type: "boolean" },
|
|
11
|
+
resetsAt: { type: "string" }
|
|
12
|
+
},
|
|
13
|
+
required: ["usedPercent", "remaining", "entitlement", "unlimited"],
|
|
14
|
+
additionalProperties: false
|
|
15
|
+
};
|
|
16
|
+
var UsageRpc = Rpc.define({
|
|
17
|
+
id: "opencode-copilot-statusline",
|
|
18
|
+
methods: {
|
|
19
|
+
get: {
|
|
20
|
+
input: { type: "object", properties: {}, additionalProperties: false },
|
|
21
|
+
output: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: { usage: usageSchema },
|
|
24
|
+
additionalProperties: false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
events: {}
|
|
29
|
+
});
|
|
30
|
+
export {
|
|
31
|
+
UsageRpc
|
|
32
|
+
};
|
package/dist/tui.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/tui.tsx
|
|
3
|
+
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
4
|
+
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
5
|
+
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
6
|
+
import { effect as _$effect } from "@opentui/solid";
|
|
7
|
+
import { insert as _$insert } from "@opentui/solid";
|
|
8
|
+
import { setProp as _$setProp } from "@opentui/solid";
|
|
9
|
+
import { createElement as _$createElement } from "@opentui/solid";
|
|
10
|
+
import { Plugin, usePlugin } from "@opencode/plugin/tui";
|
|
11
|
+
import { useTerminalDimensions } from "@opentui/solid";
|
|
12
|
+
import { createEffect, createSignal, onCleanup, Show } from "solid-js";
|
|
13
|
+
|
|
14
|
+
// src/rpc.ts
|
|
15
|
+
import { Rpc } from "@opencode/plugin/rpc";
|
|
16
|
+
var usageSchema = {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
usedPercent: { type: "number" },
|
|
20
|
+
remaining: { type: "number" },
|
|
21
|
+
entitlement: { type: "number" },
|
|
22
|
+
unlimited: { type: "boolean" },
|
|
23
|
+
resetsAt: { type: "string" }
|
|
24
|
+
},
|
|
25
|
+
required: ["usedPercent", "remaining", "entitlement", "unlimited"],
|
|
26
|
+
additionalProperties: false
|
|
27
|
+
};
|
|
28
|
+
var UsageRpc = Rpc.define({
|
|
29
|
+
id: "opencode-copilot-statusline",
|
|
30
|
+
methods: {
|
|
31
|
+
get: {
|
|
32
|
+
input: { type: "object", properties: {}, additionalProperties: false },
|
|
33
|
+
output: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: { usage: usageSchema },
|
|
36
|
+
additionalProperties: false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
events: {}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// src/tui.tsx
|
|
44
|
+
var INTERVAL_MS = 60000;
|
|
45
|
+
var DETAILED_WIDTH = 125;
|
|
46
|
+
var COPILOT_PROVIDER_ID = "github-copilot";
|
|
47
|
+
function countdown(resetsAt, now) {
|
|
48
|
+
const ms = new Date(resetsAt).getTime() - now;
|
|
49
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
50
|
+
return;
|
|
51
|
+
const minutes = Math.ceil(ms / 60000);
|
|
52
|
+
const days = Math.floor(minutes / 1440);
|
|
53
|
+
const hours = Math.floor(minutes % 1440 / 60);
|
|
54
|
+
const mins = minutes % 60;
|
|
55
|
+
if (days > 0)
|
|
56
|
+
return `${days}d${hours}h`;
|
|
57
|
+
if (hours > 0)
|
|
58
|
+
return `${hours}h${mins}m`;
|
|
59
|
+
return `${mins}m`;
|
|
60
|
+
}
|
|
61
|
+
function Segment(props) {
|
|
62
|
+
const ctx = usePlugin();
|
|
63
|
+
const fg = () => {
|
|
64
|
+
const t = ctx.theme;
|
|
65
|
+
if (props.usage.unlimited)
|
|
66
|
+
return t.text.subdued;
|
|
67
|
+
if (props.usage.usedPercent >= 90)
|
|
68
|
+
return t.text.feedback.error.default;
|
|
69
|
+
if (props.usage.usedPercent >= 70)
|
|
70
|
+
return t.text.feedback.info.default;
|
|
71
|
+
return t.text.subdued;
|
|
72
|
+
};
|
|
73
|
+
const text = () => {
|
|
74
|
+
if (props.usage.unlimited)
|
|
75
|
+
return "\u221E";
|
|
76
|
+
const left = props.detailed && props.usage.resetsAt ? countdown(props.usage.resetsAt, Date.now()) : undefined;
|
|
77
|
+
return `${props.usage.usedPercent}%${left ? ` (${left})` : ""}`;
|
|
78
|
+
};
|
|
79
|
+
return (() => {
|
|
80
|
+
var _el$ = _$createElement("text");
|
|
81
|
+
_$setProp(_el$, "wrapMode", "none");
|
|
82
|
+
_$setProp(_el$, "flexShrink", 1);
|
|
83
|
+
_$insert(_el$, text);
|
|
84
|
+
_$effect((_$p) => _$setProp(_el$, "fg", fg(), _$p));
|
|
85
|
+
return _el$;
|
|
86
|
+
})();
|
|
87
|
+
}
|
|
88
|
+
function CopilotUsage(props) {
|
|
89
|
+
const ctx = usePlugin();
|
|
90
|
+
const [isCopilot, setIsCopilot] = createSignal(false);
|
|
91
|
+
const dims = useTerminalDimensions();
|
|
92
|
+
const detailed = () => props.showDetails() || dims().width >= DETAILED_WIDTH;
|
|
93
|
+
let generation = 0;
|
|
94
|
+
async function check(id) {
|
|
95
|
+
const current = ++generation;
|
|
96
|
+
setIsCopilot(false);
|
|
97
|
+
if (!id)
|
|
98
|
+
return;
|
|
99
|
+
try {
|
|
100
|
+
await ctx.data.session.sync(id);
|
|
101
|
+
if (current !== generation)
|
|
102
|
+
return;
|
|
103
|
+
setIsCopilot(ctx.data.session.get(id)?.model?.providerID === COPILOT_PROVIDER_ID);
|
|
104
|
+
} catch {
|
|
105
|
+
if (current === generation)
|
|
106
|
+
setIsCopilot(false);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
createEffect(() => {
|
|
110
|
+
check(props.sessionID());
|
|
111
|
+
});
|
|
112
|
+
onCleanup(ctx.data.on("session.model.selected", (event) => {
|
|
113
|
+
if (event.data.sessionID === props.sessionID())
|
|
114
|
+
check(props.sessionID());
|
|
115
|
+
}));
|
|
116
|
+
return _$createComponent(Show, {
|
|
117
|
+
get when() {
|
|
118
|
+
return isCopilot();
|
|
119
|
+
},
|
|
120
|
+
get children() {
|
|
121
|
+
var _el$2 = _$createElement("box"), _el$3 = _$createElement("text"), _el$4 = _$createTextNode(`Copilot `);
|
|
122
|
+
_$insertNode(_el$2, _el$3);
|
|
123
|
+
_$setProp(_el$2, "flexDirection", "row");
|
|
124
|
+
_$setProp(_el$2, "flexShrink", 1);
|
|
125
|
+
_$setProp(_el$2, "minWidth", 0);
|
|
126
|
+
_$insertNode(_el$3, _el$4);
|
|
127
|
+
_$setProp(_el$3, "flexShrink", 0);
|
|
128
|
+
_$insert(_el$2, _$createComponent(Show, {
|
|
129
|
+
get when() {
|
|
130
|
+
return props.usage();
|
|
131
|
+
},
|
|
132
|
+
get fallback() {
|
|
133
|
+
return (() => {
|
|
134
|
+
var _el$6 = _$createElement("text");
|
|
135
|
+
_$insertNode(_el$6, _$createTextNode(`\u2014`));
|
|
136
|
+
_$setProp(_el$6, "flexShrink", 0);
|
|
137
|
+
_$effect((_$p) => _$setProp(_el$6, "fg", ctx.theme.text.subdued, _$p));
|
|
138
|
+
return _el$6;
|
|
139
|
+
})();
|
|
140
|
+
},
|
|
141
|
+
children: (u) => _$createComponent(Segment, {
|
|
142
|
+
get usage() {
|
|
143
|
+
return u();
|
|
144
|
+
},
|
|
145
|
+
get detailed() {
|
|
146
|
+
return detailed();
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
}), null);
|
|
150
|
+
_$effect((_$p) => _$setProp(_el$3, "fg", ctx.theme.text.subdued, _$p));
|
|
151
|
+
return _el$2;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
var tui_default = Plugin.define({
|
|
156
|
+
id: "opencode-copilot-statusline.tui",
|
|
157
|
+
setup(context) {
|
|
158
|
+
const rpc = context.client.rpc(UsageRpc);
|
|
159
|
+
const [usage, setUsage] = createSignal();
|
|
160
|
+
let timer;
|
|
161
|
+
async function refresh() {
|
|
162
|
+
try {
|
|
163
|
+
const result = await rpc.get({});
|
|
164
|
+
if (result?.usage)
|
|
165
|
+
setUsage(result.usage);
|
|
166
|
+
} catch {}
|
|
167
|
+
}
|
|
168
|
+
refresh();
|
|
169
|
+
timer = setInterval(() => void refresh(), INTERVAL_MS);
|
|
170
|
+
const unregister = context.ui.slot({
|
|
171
|
+
append: "prompt.footer.status",
|
|
172
|
+
render: (input) => _$createComponent(CopilotUsage, {
|
|
173
|
+
sessionID: () => input.sessionID,
|
|
174
|
+
showDetails: () => input.showDetails,
|
|
175
|
+
usage
|
|
176
|
+
})
|
|
177
|
+
});
|
|
178
|
+
return () => {
|
|
179
|
+
if (timer)
|
|
180
|
+
clearInterval(timer);
|
|
181
|
+
unregister();
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
export {
|
|
186
|
+
tui_default as default
|
|
187
|
+
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-copilot-statusline",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenCode TUI plugin: display GitHub Copilot quota (premium requests, with a chat fallback for Copilot Free) in the footer statusline. V2-native, zero-config, no cookies.",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/lnwu/opencode-statusline-plugins.git"
|
|
9
|
+
"url": "git+https://github.com/lnwu/opencode-statusline-plugins.git",
|
|
10
|
+
"directory": "packages/opencode-copilot-statusline"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/lnwu/opencode-statusline-plugins/tree/main/packages/opencode-copilot-statusline#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/lnwu/opencode-statusline-plugins/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"opencode",
|
|
18
|
+
"opencode-plugin",
|
|
19
|
+
"github-copilot",
|
|
20
|
+
"copilot",
|
|
21
|
+
"statusline",
|
|
22
|
+
"usage",
|
|
23
|
+
"quota"
|
|
24
|
+
],
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"exports": {
|
|
29
|
+
".": "./dist/index.js",
|
|
30
|
+
"./rpc": "./dist/rpc.js",
|
|
31
|
+
"./tui": "./dist/tui.js"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "bun run scripts/build.ts",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"pretest": "bun run build",
|
|
37
|
+
"test": "bun test test/usage.test.ts test/e2e",
|
|
38
|
+
"test:pack": "bun test test/pack.test.ts",
|
|
39
|
+
"prepack": "bun run build"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@opencode/plugin": ">2.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@opencode/theme": ">2.0.0",
|
|
46
|
+
"@opentui/core": ">=0.5.10",
|
|
47
|
+
"@opentui/solid": ">=0.5.10",
|
|
48
|
+
"solid-js": ">=1.9.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@opencode/theme": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@opencode/theme": ">2.0.0",
|
|
57
|
+
"@opentui/core": "^0.5.11",
|
|
58
|
+
"@opentui/solid": "^0.5.11",
|
|
59
|
+
"bun-types": "^1.4.2",
|
|
60
|
+
"core": "*",
|
|
61
|
+
"solid-js": "^1.9.15",
|
|
62
|
+
"typescript": "^7.0.2"
|
|
9
63
|
}
|
|
10
64
|
}
|