@jeffreycao/copilot-api 1.3.4 → 1.3.7
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 +27 -5
- package/dist/auth-DteTbqnM.js +46 -0
- package/dist/auth-DteTbqnM.js.map +1 -0
- package/dist/check-usage-Z8fnzZQt.js +45 -0
- package/dist/check-usage-Z8fnzZQt.js.map +1 -0
- package/dist/config-DYOE_-p1.js +170 -0
- package/dist/config-DYOE_-p1.js.map +1 -0
- package/dist/debug-Dx1S6uWG.js +82 -0
- package/dist/debug-Dx1S6uWG.js.map +1 -0
- package/dist/get-copilot-usage-CuVET98U.js +12 -0
- package/dist/get-copilot-usage-CuVET98U.js.map +1 -0
- package/dist/main.js +23 -551
- package/dist/main.js.map +1 -1
- package/dist/paths-Cla6y5eD.js +33 -0
- package/dist/paths-Cla6y5eD.js.map +1 -0
- package/dist/{server-C-LzaXzh.js → server-BBSm2bHY.js} +102 -23
- package/dist/server-BBSm2bHY.js.map +1 -0
- package/dist/start-BaeeWbdc.js +259 -0
- package/dist/start-BaeeWbdc.js.map +1 -0
- package/dist/token-8AyBHdsS.js +156 -0
- package/dist/token-8AyBHdsS.js.map +1 -0
- package/dist/{config-DqFPDXKy.js → utils-DKqD66k9.js} +6 -208
- package/dist/utils-DKqD66k9.js.map +1 -0
- package/package.json +1 -1
- package/dist/config-DqFPDXKy.js.map +0 -1
- package/dist/server-C-LzaXzh.js.map +0 -1
package/README.md
CHANGED
|
@@ -48,9 +48,9 @@ Compared with routing everything through plain Chat Completions compatibility, t
|
|
|
48
48
|
- **Token Visibility**: Option to display GitHub and Copilot tokens during authentication and refresh for debugging (`--show-token`).
|
|
49
49
|
- **Flexible Authentication**: Authenticate interactively or provide a GitHub token directly, suitable for CI/CD environments.
|
|
50
50
|
- **Support for Different Account Types**: Works with individual, business, and enterprise GitHub Copilot plans.
|
|
51
|
-
- **Opencode OAuth Support**: Use opencode GitHub Copilot authentication by setting `COPILOT_API_OAUTH_APP=opencode` environment variable.
|
|
52
|
-
- **GitHub Enterprise Support**: Connect to GHE.com by setting `COPILOT_API_ENTERPRISE_URL` environment variable (e.g., `company.ghe.com`).
|
|
53
|
-
- **Custom Data Directory**: Change the default data directory (where tokens and config are stored) by setting `COPILOT_API_HOME` environment variable.
|
|
51
|
+
- **Opencode OAuth Support**: Use opencode GitHub Copilot authentication by setting `COPILOT_API_OAUTH_APP=opencode` environment variable or using `--oauth-app=opencode` command line option.
|
|
52
|
+
- **GitHub Enterprise Support**: Connect to GHE.com by setting `COPILOT_API_ENTERPRISE_URL` environment variable (e.g., `company.ghe.com`) or using `--enterprise-url=company.ghe.com` command line option.
|
|
53
|
+
- **Custom Data Directory**: Change the default data directory (where tokens and config are stored) by setting `COPILOT_API_HOME` environment variable or using `--api-home=/path/to/dir` command line option.
|
|
54
54
|
- **Multi-Provider Anthropic Proxy Routes**: Add global provider configs and call external Anthropic-compatible APIs via `/:provider/v1/messages` and `/:provider/v1/models`.
|
|
55
55
|
|
|
56
56
|
## Better Agent Semantics
|
|
@@ -199,6 +199,16 @@ Copilot API now uses a subcommand structure with these main commands:
|
|
|
199
199
|
|
|
200
200
|
## Command Line Options
|
|
201
201
|
|
|
202
|
+
### Global Options
|
|
203
|
+
|
|
204
|
+
The following options can be used with any subcommand. When passing them before the subcommand, use the `--key=value` form:
|
|
205
|
+
|
|
206
|
+
| Option | Description | Default | Alias |
|
|
207
|
+
| ----------------- | ------------------------------------------------------ | ------- | ----- |
|
|
208
|
+
| --api-home | Path to the API home directory (sets COPILOT_API_HOME) | none | none |
|
|
209
|
+
| --oauth-app | OAuth app identifier (sets COPILOT_API_OAUTH_APP) | none | none |
|
|
210
|
+
| --enterprise-url | Enterprise URL for GitHub (sets COPILOT_API_ENTERPRISE_URL) | none | none |
|
|
211
|
+
|
|
202
212
|
### Start Command Options
|
|
203
213
|
|
|
204
214
|
The following command line options are available for the `start` command:
|
|
@@ -244,6 +254,7 @@ The following command line options are available for the `start` command:
|
|
|
244
254
|
"enabled": true,
|
|
245
255
|
"baseUrl": "your-base-url",
|
|
246
256
|
"apiKey": "sk-your-provider-key",
|
|
257
|
+
"adjustInputTokens": false,
|
|
247
258
|
"models": {
|
|
248
259
|
"kimi-k2.5": {
|
|
249
260
|
"temperature": 1,
|
|
@@ -265,7 +276,6 @@ The following command line options are available for the `start` command:
|
|
|
265
276
|
"gpt-5.4": "xhigh"
|
|
266
277
|
},
|
|
267
278
|
"useFunctionApplyPatch": true,
|
|
268
|
-
"compactUseSmallModel": true,
|
|
269
279
|
"useMessagesApi": true
|
|
270
280
|
}
|
|
271
281
|
```
|
|
@@ -275,6 +285,7 @@ The following command line options are available for the `start` command:
|
|
|
275
285
|
- `enabled` defaults to `true` if omitted.
|
|
276
286
|
- `baseUrl` should be provider API base URL without trailing `/v1/messages`.
|
|
277
287
|
- `apiKey` is used as upstream `x-api-key`.
|
|
288
|
+
- `adjustInputTokens` (optional): When `true`, the proxy will adjust the `input_tokens` in the usage response by subtracting `cache_read_input_tokens` and `cache_creation_input_tokens`.
|
|
278
289
|
- `models` (optional): Per-model configuration map. Each key is a model ID (matching the model name in requests), and the value is:
|
|
279
290
|
- `temperature` (optional): Default temperature value used when the request does not specify one.
|
|
280
291
|
- `topP` (optional): Default top_p value used when the request does not specify one.
|
|
@@ -283,7 +294,6 @@ The following command line options are available for the `start` command:
|
|
|
283
294
|
- **responsesApiContextManagementModels:** List of model IDs that should receive Responses API `context_management` compaction instructions. Use this when a model supports server-side context management and you want the proxy to keep only the latest compaction carrier on follow-up turns.
|
|
284
295
|
- **modelReasoningEfforts:** Per-model `reasoning.effort` sent to the Copilot Responses API. Allowed values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. If a model isn’t listed, `high` is used by default.
|
|
285
296
|
- **useFunctionApplyPatch:** When `true`, the server will convert any custom tool named `apply_patch` in Responses payloads into an OpenAI-style function tool (`type: "function"`) with a parameter schema so assistants can call it using function-calling semantics to edit files. Set to `false` to leave tools unchanged. Defaults to `true`.
|
|
286
|
-
- **compactUseSmallModel:** When `true`, detected "compact" requests (e.g., from Claude Code or Opencode compact mode) will automatically use the configured `smallModel` to avoid consuming premium model usage for short/background tasks. Defaults to `true`.
|
|
287
297
|
- **useMessagesApi:** When `true`, Claude-family models that support Copilot's native `/v1/messages` endpoint will use the Messages API; otherwise they fall back to `/chat/completions`. Set to `false` to disable Messages API routing and always use `/chat/completions`. Defaults to `true`.
|
|
288
298
|
|
|
289
299
|
Edit this file to customize prompts or swap in your own fast model. Restart the server (or rerun the command) after changes so the cached config is refreshed.
|
|
@@ -389,6 +399,18 @@ npx @jeffreycao/copilot-api@latest start --proxy-env
|
|
|
389
399
|
|
|
390
400
|
# Use opencode GitHub Copilot authentication
|
|
391
401
|
COPILOT_API_OAUTH_APP=opencode npx @jeffreycao/copilot-api@latest start
|
|
402
|
+
|
|
403
|
+
# Set custom API home directory via command line
|
|
404
|
+
npx @jeffreycao/copilot-api@latest --api-home=/path/to/custom/dir start
|
|
405
|
+
|
|
406
|
+
# Use GitHub Enterprise via command line
|
|
407
|
+
npx @jeffreycao/copilot-api@latest --enterprise-url=company.ghe.com start
|
|
408
|
+
|
|
409
|
+
# Use opencode OAuth via command line
|
|
410
|
+
npx @jeffreycao/copilot-api@latest --oauth-app=opencode start
|
|
411
|
+
|
|
412
|
+
# Combine multiple global options
|
|
413
|
+
npx @jeffreycao/copilot-api@latest --api-home=/custom/path --oauth-app=opencode --enterprise-url=company.ghe.com start
|
|
392
414
|
```
|
|
393
415
|
|
|
394
416
|
### Opencode OAuth Authentication
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { PATHS, ensurePaths } from "./paths-Cla6y5eD.js";
|
|
2
|
+
import { state } from "./utils-DKqD66k9.js";
|
|
3
|
+
import { setupGitHubToken } from "./token-8AyBHdsS.js";
|
|
4
|
+
import { defineCommand } from "citty";
|
|
5
|
+
import consola from "consola";
|
|
6
|
+
|
|
7
|
+
//#region src/auth.ts
|
|
8
|
+
async function runAuth(options) {
|
|
9
|
+
if (options.verbose) {
|
|
10
|
+
consola.level = 5;
|
|
11
|
+
consola.info("Verbose logging enabled");
|
|
12
|
+
}
|
|
13
|
+
state.showToken = options.showToken;
|
|
14
|
+
await ensurePaths();
|
|
15
|
+
await setupGitHubToken({ force: true });
|
|
16
|
+
consola.success("GitHub token written to", PATHS.GITHUB_TOKEN_PATH);
|
|
17
|
+
}
|
|
18
|
+
const auth = defineCommand({
|
|
19
|
+
meta: {
|
|
20
|
+
name: "auth",
|
|
21
|
+
description: "Run GitHub auth flow without running the server"
|
|
22
|
+
},
|
|
23
|
+
args: {
|
|
24
|
+
verbose: {
|
|
25
|
+
alias: "v",
|
|
26
|
+
type: "boolean",
|
|
27
|
+
default: false,
|
|
28
|
+
description: "Enable verbose logging"
|
|
29
|
+
},
|
|
30
|
+
"show-token": {
|
|
31
|
+
type: "boolean",
|
|
32
|
+
default: false,
|
|
33
|
+
description: "Show GitHub token on auth"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
run({ args }) {
|
|
37
|
+
return runAuth({
|
|
38
|
+
verbose: args.verbose,
|
|
39
|
+
showToken: args["show-token"]
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { auth };
|
|
46
|
+
//# sourceMappingURL=auth-DteTbqnM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-DteTbqnM.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { PATHS, ensurePaths } from \"./lib/paths\"\nimport { state } from \"./lib/state\"\nimport { setupGitHubToken } from \"./lib/token\"\n\ninterface RunAuthOptions {\n verbose: boolean\n showToken: boolean\n}\n\nexport async function runAuth(options: RunAuthOptions): Promise<void> {\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.showToken = options.showToken\n\n await ensurePaths()\n await setupGitHubToken({ force: true })\n consola.success(\"GitHub token written to\", PATHS.GITHUB_TOKEN_PATH)\n}\n\nexport const auth = defineCommand({\n meta: {\n name: \"auth\",\n description: \"Run GitHub auth flow without running the server\",\n },\n args: {\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub token on auth\",\n },\n },\n run({ args }) {\n return runAuth({\n verbose: args.verbose,\n showToken: args[\"show-token\"],\n })\n },\n})\n"],"mappings":";;;;;;;AAcA,eAAsB,QAAQ,SAAwC;AACpE,KAAI,QAAQ,SAAS;AACnB,UAAQ,QAAQ;AAChB,UAAQ,KAAK,0BAA0B;;AAGzC,OAAM,YAAY,QAAQ;AAE1B,OAAM,aAAa;AACnB,OAAM,iBAAiB,EAAE,OAAO,MAAM,CAAC;AACvC,SAAQ,QAAQ,2BAA2B,MAAM,kBAAkB;;AAGrE,MAAa,OAAO,cAAc;CAChC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,SAAS;GACP,OAAO;GACP,MAAM;GACN,SAAS;GACT,aAAa;GACd;EACD,cAAc;GACZ,MAAM;GACN,SAAS;GACT,aAAa;GACd;EACF;CACD,IAAI,EAAE,QAAQ;AACZ,SAAO,QAAQ;GACb,SAAS,KAAK;GACd,WAAW,KAAK;GACjB,CAAC;;CAEL,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ensurePaths } from "./paths-Cla6y5eD.js";
|
|
2
|
+
import "./utils-DKqD66k9.js";
|
|
3
|
+
import { setupGitHubToken } from "./token-8AyBHdsS.js";
|
|
4
|
+
import { getCopilotUsage } from "./get-copilot-usage-CuVET98U.js";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
import consola from "consola";
|
|
7
|
+
|
|
8
|
+
//#region src/check-usage.ts
|
|
9
|
+
const checkUsage = defineCommand({
|
|
10
|
+
meta: {
|
|
11
|
+
name: "check-usage",
|
|
12
|
+
description: "Show current GitHub Copilot usage/quota information"
|
|
13
|
+
},
|
|
14
|
+
async run() {
|
|
15
|
+
await ensurePaths();
|
|
16
|
+
await setupGitHubToken();
|
|
17
|
+
try {
|
|
18
|
+
const usage = await getCopilotUsage();
|
|
19
|
+
const premium = usage.quota_snapshots.premium_interactions;
|
|
20
|
+
const premiumTotal = premium.entitlement;
|
|
21
|
+
const premiumUsed = premiumTotal - premium.remaining;
|
|
22
|
+
const premiumPercentUsed = premiumTotal > 0 ? premiumUsed / premiumTotal * 100 : 0;
|
|
23
|
+
const premiumPercentRemaining = premium.percent_remaining;
|
|
24
|
+
function summarizeQuota(name, snap) {
|
|
25
|
+
if (!snap) return `${name}: N/A`;
|
|
26
|
+
const total = snap.entitlement;
|
|
27
|
+
const used = total - snap.remaining;
|
|
28
|
+
const percentUsed = total > 0 ? used / total * 100 : 0;
|
|
29
|
+
const percentRemaining = snap.percent_remaining;
|
|
30
|
+
return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`;
|
|
31
|
+
}
|
|
32
|
+
const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`;
|
|
33
|
+
const chatLine = summarizeQuota("Chat", usage.quota_snapshots.chat);
|
|
34
|
+
const completionsLine = summarizeQuota("Completions", usage.quota_snapshots.completions);
|
|
35
|
+
consola.box(`Copilot Usage (plan: ${usage.copilot_plan})\nQuota resets: ${usage.quota_reset_date}\n\nQuotas:\n ${premiumLine}\n ${chatLine}\n ${completionsLine}`);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
consola.error("Failed to fetch Copilot usage:", err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { checkUsage };
|
|
45
|
+
//# sourceMappingURL=check-usage-Z8fnzZQt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-usage-Z8fnzZQt.js","names":[],"sources":["../src/check-usage.ts"],"sourcesContent":["import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupGitHubToken } from \"./lib/token\"\nimport {\n getCopilotUsage,\n type QuotaDetail,\n} from \"./services/github/get-copilot-usage\"\n\nexport const checkUsage = defineCommand({\n meta: {\n name: \"check-usage\",\n description: \"Show current GitHub Copilot usage/quota information\",\n },\n async run() {\n await ensurePaths()\n await setupGitHubToken()\n try {\n const usage = await getCopilotUsage()\n const premium = usage.quota_snapshots.premium_interactions\n const premiumTotal = premium.entitlement\n const premiumUsed = premiumTotal - premium.remaining\n const premiumPercentUsed =\n premiumTotal > 0 ? (premiumUsed / premiumTotal) * 100 : 0\n const premiumPercentRemaining = premium.percent_remaining\n\n // Helper to summarize a quota snapshot\n function summarizeQuota(name: string, snap: QuotaDetail | undefined) {\n if (!snap) return `${name}: N/A`\n const total = snap.entitlement\n const used = total - snap.remaining\n const percentUsed = total > 0 ? (used / total) * 100 : 0\n const percentRemaining = snap.percent_remaining\n return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`\n }\n\n const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`\n const chatLine = summarizeQuota(\"Chat\", usage.quota_snapshots.chat)\n const completionsLine = summarizeQuota(\n \"Completions\",\n usage.quota_snapshots.completions,\n )\n\n consola.box(\n `Copilot Usage (plan: ${usage.copilot_plan})\\n`\n + `Quota resets: ${usage.quota_reset_date}\\n`\n + `\\nQuotas:\\n`\n + ` ${premiumLine}\\n`\n + ` ${chatLine}\\n`\n + ` ${completionsLine}`,\n )\n } catch (err) {\n consola.error(\"Failed to fetch Copilot usage:\", err)\n process.exit(1)\n }\n },\n})\n"],"mappings":";;;;;;;;AAUA,MAAa,aAAa,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,MAAM;AACV,QAAM,aAAa;AACnB,QAAM,kBAAkB;AACxB,MAAI;GACF,MAAM,QAAQ,MAAM,iBAAiB;GACrC,MAAM,UAAU,MAAM,gBAAgB;GACtC,MAAM,eAAe,QAAQ;GAC7B,MAAM,cAAc,eAAe,QAAQ;GAC3C,MAAM,qBACJ,eAAe,IAAK,cAAc,eAAgB,MAAM;GAC1D,MAAM,0BAA0B,QAAQ;GAGxC,SAAS,eAAe,MAAc,MAA+B;AACnE,QAAI,CAAC,KAAM,QAAO,GAAG,KAAK;IAC1B,MAAM,QAAQ,KAAK;IACnB,MAAM,OAAO,QAAQ,KAAK;IAC1B,MAAM,cAAc,QAAQ,IAAK,OAAO,QAAS,MAAM;IACvD,MAAM,mBAAmB,KAAK;AAC9B,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,MAAM,SAAS,YAAY,QAAQ,EAAE,CAAC,UAAU,iBAAiB,QAAQ,EAAE,CAAC;;GAGzG,MAAM,cAAc,YAAY,YAAY,GAAG,aAAa,SAAS,mBAAmB,QAAQ,EAAE,CAAC,UAAU,wBAAwB,QAAQ,EAAE,CAAC;GAChJ,MAAM,WAAW,eAAe,QAAQ,MAAM,gBAAgB,KAAK;GACnE,MAAM,kBAAkB,eACtB,eACA,MAAM,gBAAgB,YACvB;AAED,WAAQ,IACN,wBAAwB,MAAM,aAAa,mBACtB,MAAM,iBAAiB,iBAEnC,YAAY,MACZ,SAAS,MACT,kBACV;WACM,KAAK;AACZ,WAAQ,MAAM,kCAAkC,IAAI;AACpD,WAAQ,KAAK,EAAE;;;CAGpB,CAAC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { PATHS } from "./paths-Cla6y5eD.js";
|
|
2
|
+
import consola from "consola";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
//#region src/lib/config.ts
|
|
6
|
+
const gpt5ExplorationPrompt = `## Exploration and reading files
|
|
7
|
+
- **Think first.** Before any tool call, decide ALL files/resources you will need.
|
|
8
|
+
- **Batch everything.** If you need multiple files (even from different places), read them together.
|
|
9
|
+
- **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.
|
|
10
|
+
- **Only make sequential calls if you truly cannot know the next file without seeing a result first.**
|
|
11
|
+
- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`;
|
|
12
|
+
const gpt5CommentaryPrompt = `# Working with the user
|
|
13
|
+
|
|
14
|
+
You interact with the user through a terminal. You have 2 ways of communicating with the users:
|
|
15
|
+
- Share intermediary updates in \`commentary\` channel.
|
|
16
|
+
- After you have completed all your work, send a message to the \`final\` channel.
|
|
17
|
+
|
|
18
|
+
## Intermediary updates
|
|
19
|
+
|
|
20
|
+
- Intermediary updates go to the \`commentary\` channel.
|
|
21
|
+
- User updates are short updates while you are working, they are NOT final answers.
|
|
22
|
+
- You use 1-2 sentence user updates to communicate progress and new information to the user as you are doing work.
|
|
23
|
+
- Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements (“Done —”, “Got it”, “Great question, ”) or framing phrases.
|
|
24
|
+
- You provide user updates frequently, every 20s.
|
|
25
|
+
- Before exploring or doing substantial work, you start with a user update acknowledging the request and explaining your first step. You should include your understanding of the user request and explain what you will do. Avoid commenting on the request or using starters such as "Got it -" or "Understood -" etc.
|
|
26
|
+
- When exploring, e.g. searching, reading files, you provide user updates as you go, every 20s, explaining what context you are gathering and what you've learned. Vary your sentence structure when providing these updates to avoid sounding repetitive - in particular, don't start each sentence the same way.
|
|
27
|
+
- After you have sufficient context, and the work is substantial, you provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).
|
|
28
|
+
- Before performing file edits of any kind, you provide updates explaining what edits you are making.
|
|
29
|
+
- As you are thinking, you very frequently provide updates even if not taking any actions, informing the user of your progress. You interrupt your thinking and send multiple updates in a row if thinking for more than 100 words.
|
|
30
|
+
- Tone of your updates MUST match your personality.`;
|
|
31
|
+
const defaultConfig = {
|
|
32
|
+
auth: { apiKeys: [] },
|
|
33
|
+
providers: {},
|
|
34
|
+
extraPrompts: {
|
|
35
|
+
"gpt-5-mini": gpt5ExplorationPrompt,
|
|
36
|
+
"gpt-5.3-codex": gpt5CommentaryPrompt,
|
|
37
|
+
"gpt-5.4": gpt5CommentaryPrompt
|
|
38
|
+
},
|
|
39
|
+
smallModel: "gpt-5-mini",
|
|
40
|
+
responsesApiContextManagementModels: [],
|
|
41
|
+
modelReasoningEfforts: {
|
|
42
|
+
"gpt-5-mini": "low",
|
|
43
|
+
"gpt-5.3-codex": "xhigh",
|
|
44
|
+
"gpt-5.4": "xhigh"
|
|
45
|
+
},
|
|
46
|
+
useFunctionApplyPatch: true,
|
|
47
|
+
useMessagesApi: true
|
|
48
|
+
};
|
|
49
|
+
let cachedConfig = null;
|
|
50
|
+
function ensureConfigFile() {
|
|
51
|
+
try {
|
|
52
|
+
fs.accessSync(PATHS.CONFIG_PATH, fs.constants.R_OK | fs.constants.W_OK);
|
|
53
|
+
} catch {
|
|
54
|
+
fs.mkdirSync(PATHS.APP_DIR, { recursive: true });
|
|
55
|
+
fs.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
|
|
56
|
+
try {
|
|
57
|
+
fs.chmodSync(PATHS.CONFIG_PATH, 384);
|
|
58
|
+
} catch {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function readConfigFromDisk() {
|
|
64
|
+
ensureConfigFile();
|
|
65
|
+
try {
|
|
66
|
+
const raw = fs.readFileSync(PATHS.CONFIG_PATH, "utf8");
|
|
67
|
+
if (!raw.trim()) {
|
|
68
|
+
fs.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
|
|
69
|
+
return defaultConfig;
|
|
70
|
+
}
|
|
71
|
+
return JSON.parse(raw);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
consola.error("Failed to read config file, using default config", error);
|
|
74
|
+
return defaultConfig;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function mergeDefaultConfig(config) {
|
|
78
|
+
const extraPrompts = config.extraPrompts ?? {};
|
|
79
|
+
const defaultExtraPrompts = defaultConfig.extraPrompts ?? {};
|
|
80
|
+
const modelReasoningEfforts = config.modelReasoningEfforts ?? {};
|
|
81
|
+
const defaultModelReasoningEfforts = defaultConfig.modelReasoningEfforts ?? {};
|
|
82
|
+
const missingExtraPromptModels = Object.keys(defaultExtraPrompts).filter((model) => !Object.hasOwn(extraPrompts, model));
|
|
83
|
+
const missingReasoningEffortModels = Object.keys(defaultModelReasoningEfforts).filter((model) => !Object.hasOwn(modelReasoningEfforts, model));
|
|
84
|
+
const hasExtraPromptChanges = missingExtraPromptModels.length > 0;
|
|
85
|
+
const hasReasoningEffortChanges = missingReasoningEffortModels.length > 0;
|
|
86
|
+
if (!hasExtraPromptChanges && !hasReasoningEffortChanges) return {
|
|
87
|
+
mergedConfig: config,
|
|
88
|
+
changed: false
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
mergedConfig: {
|
|
92
|
+
...config,
|
|
93
|
+
extraPrompts: {
|
|
94
|
+
...defaultExtraPrompts,
|
|
95
|
+
...extraPrompts
|
|
96
|
+
},
|
|
97
|
+
modelReasoningEfforts: {
|
|
98
|
+
...defaultModelReasoningEfforts,
|
|
99
|
+
...modelReasoningEfforts
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
changed: true
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function mergeConfigWithDefaults() {
|
|
106
|
+
const config = readConfigFromDisk();
|
|
107
|
+
const { mergedConfig, changed } = mergeDefaultConfig(config);
|
|
108
|
+
if (changed) try {
|
|
109
|
+
fs.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(mergedConfig, null, 2)}\n`, "utf8");
|
|
110
|
+
} catch (writeError) {
|
|
111
|
+
consola.warn("Failed to write merged extraPrompts to config file", writeError);
|
|
112
|
+
}
|
|
113
|
+
cachedConfig = mergedConfig;
|
|
114
|
+
return mergedConfig;
|
|
115
|
+
}
|
|
116
|
+
function getConfig() {
|
|
117
|
+
cachedConfig ??= readConfigFromDisk();
|
|
118
|
+
return cachedConfig;
|
|
119
|
+
}
|
|
120
|
+
function getExtraPromptForModel(model) {
|
|
121
|
+
return getConfig().extraPrompts?.[model] ?? "";
|
|
122
|
+
}
|
|
123
|
+
function getSmallModel() {
|
|
124
|
+
return getConfig().smallModel ?? "gpt-5-mini";
|
|
125
|
+
}
|
|
126
|
+
function getResponsesApiContextManagementModels() {
|
|
127
|
+
return getConfig().responsesApiContextManagementModels ?? defaultConfig.responsesApiContextManagementModels ?? [];
|
|
128
|
+
}
|
|
129
|
+
function isResponsesApiContextManagementModel(model) {
|
|
130
|
+
return getResponsesApiContextManagementModels().includes(model);
|
|
131
|
+
}
|
|
132
|
+
function getReasoningEffortForModel(model) {
|
|
133
|
+
return getConfig().modelReasoningEfforts?.[model] ?? "high";
|
|
134
|
+
}
|
|
135
|
+
function normalizeProviderBaseUrl(url) {
|
|
136
|
+
return url.trim().replace(/\/+$/u, "");
|
|
137
|
+
}
|
|
138
|
+
function getProviderConfig(name) {
|
|
139
|
+
const providerName = name.trim();
|
|
140
|
+
if (!providerName) return null;
|
|
141
|
+
const provider = getConfig().providers?.[providerName];
|
|
142
|
+
if (!provider) return null;
|
|
143
|
+
if (provider.enabled === false) return null;
|
|
144
|
+
const type = provider.type ?? "anthropic";
|
|
145
|
+
if (type !== "anthropic") {
|
|
146
|
+
consola.warn(`Provider ${providerName} is ignored because only anthropic type is supported`);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? "");
|
|
150
|
+
const apiKey = (provider.apiKey ?? "").trim();
|
|
151
|
+
if (!baseUrl || !apiKey) {
|
|
152
|
+
consola.warn(`Provider ${providerName} is enabled but missing baseUrl or apiKey`);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
name: providerName,
|
|
157
|
+
type,
|
|
158
|
+
baseUrl,
|
|
159
|
+
apiKey,
|
|
160
|
+
models: provider.models,
|
|
161
|
+
adjustInputTokens: provider.adjustInputTokens
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function isMessagesApiEnabled() {
|
|
165
|
+
return getConfig().useMessagesApi ?? true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//#endregion
|
|
169
|
+
export { getConfig, getExtraPromptForModel, getProviderConfig, getReasoningEffortForModel, getSmallModel, isMessagesApiEnabled, isResponsesApiContextManagementModel, mergeConfigWithDefaults };
|
|
170
|
+
//# sourceMappingURL=config-DYOE_-p1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-DYOE_-p1.js","names":["defaultConfig: AppConfig","cachedConfig: AppConfig | null"],"sources":["../src/lib/config.ts"],"sourcesContent":["import consola from \"consola\"\nimport fs from \"node:fs\"\n\nimport { PATHS } from \"./paths\"\n\nexport interface AppConfig {\n auth?: {\n apiKeys?: Array<string>\n }\n providers?: Record<string, ProviderConfig>\n extraPrompts?: Record<string, string>\n smallModel?: string\n responsesApiContextManagementModels?: Array<string>\n modelReasoningEfforts?: Record<\n string,\n \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"\n >\n useFunctionApplyPatch?: boolean\n useMessagesApi?: boolean\n}\n\nexport interface ModelConfig {\n temperature?: number\n topP?: number\n topK?: number\n}\n\nexport interface ProviderConfig {\n type?: string\n enabled?: boolean\n baseUrl?: string\n apiKey?: string\n models?: Record<string, ModelConfig>\n adjustInputTokens?: boolean\n}\n\nexport interface ResolvedProviderConfig {\n name: string\n type: \"anthropic\"\n baseUrl: string\n apiKey: string\n models?: Record<string, ModelConfig>\n adjustInputTokens?: boolean\n}\n\nconst gpt5ExplorationPrompt = `## Exploration and reading files\n- **Think first.** Before any tool call, decide ALL files/resources you will need.\n- **Batch everything.** If you need multiple files (even from different places), read them together.\n- **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.\n- **Only make sequential calls if you truly cannot know the next file without seeing a result first.**\n- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`\n\nconst gpt5CommentaryPrompt = `# Working with the user\n\nYou interact with the user through a terminal. You have 2 ways of communicating with the users: \n- Share intermediary updates in \\`commentary\\` channel. \n- After you have completed all your work, send a message to the \\`final\\` channel. \n\n## Intermediary updates\n\n- Intermediary updates go to the \\`commentary\\` channel.\n- User updates are short updates while you are working, they are NOT final answers.\n- You use 1-2 sentence user updates to communicate progress and new information to the user as you are doing work.\n- Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements (“Done —”, “Got it”, “Great question, ”) or framing phrases.\n- You provide user updates frequently, every 20s.\n- Before exploring or doing substantial work, you start with a user update acknowledging the request and explaining your first step. You should include your understanding of the user request and explain what you will do. Avoid commenting on the request or using starters such as \"Got it -\" or \"Understood -\" etc.\n- When exploring, e.g. searching, reading files, you provide user updates as you go, every 20s, explaining what context you are gathering and what you've learned. Vary your sentence structure when providing these updates to avoid sounding repetitive - in particular, don't start each sentence the same way.\n- After you have sufficient context, and the work is substantial, you provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).\n- Before performing file edits of any kind, you provide updates explaining what edits you are making.\n- As you are thinking, you very frequently provide updates even if not taking any actions, informing the user of your progress. You interrupt your thinking and send multiple updates in a row if thinking for more than 100 words.\n- Tone of your updates MUST match your personality.`\n\nconst defaultConfig: AppConfig = {\n auth: {\n apiKeys: [],\n },\n providers: {},\n extraPrompts: {\n \"gpt-5-mini\": gpt5ExplorationPrompt,\n \"gpt-5.3-codex\": gpt5CommentaryPrompt,\n \"gpt-5.4\": gpt5CommentaryPrompt,\n },\n smallModel: \"gpt-5-mini\",\n responsesApiContextManagementModels: [],\n modelReasoningEfforts: {\n \"gpt-5-mini\": \"low\",\n \"gpt-5.3-codex\": \"xhigh\",\n \"gpt-5.4\": \"xhigh\",\n },\n useFunctionApplyPatch: true,\n useMessagesApi: true,\n}\n\nlet cachedConfig: AppConfig | null = null\n\nfunction ensureConfigFile(): void {\n try {\n fs.accessSync(PATHS.CONFIG_PATH, fs.constants.R_OK | fs.constants.W_OK)\n } catch {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n try {\n fs.chmodSync(PATHS.CONFIG_PATH, 0o600)\n } catch {\n return\n }\n }\n}\n\nfunction readConfigFromDisk(): AppConfig {\n ensureConfigFile()\n try {\n const raw = fs.readFileSync(PATHS.CONFIG_PATH, \"utf8\")\n if (!raw.trim()) {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n return defaultConfig\n }\n return JSON.parse(raw) as AppConfig\n } catch (error) {\n consola.error(\"Failed to read config file, using default config\", error)\n return defaultConfig\n }\n}\n\nfunction mergeDefaultConfig(config: AppConfig): {\n mergedConfig: AppConfig\n changed: boolean\n} {\n const extraPrompts = config.extraPrompts ?? {}\n const defaultExtraPrompts = defaultConfig.extraPrompts ?? {}\n const modelReasoningEfforts = config.modelReasoningEfforts ?? {}\n const defaultModelReasoningEfforts = defaultConfig.modelReasoningEfforts ?? {}\n\n const missingExtraPromptModels = Object.keys(defaultExtraPrompts).filter(\n (model) => !Object.hasOwn(extraPrompts, model),\n )\n\n const missingReasoningEffortModels = Object.keys(\n defaultModelReasoningEfforts,\n ).filter((model) => !Object.hasOwn(modelReasoningEfforts, model))\n\n const hasExtraPromptChanges = missingExtraPromptModels.length > 0\n const hasReasoningEffortChanges = missingReasoningEffortModels.length > 0\n\n if (!hasExtraPromptChanges && !hasReasoningEffortChanges) {\n return { mergedConfig: config, changed: false }\n }\n\n return {\n mergedConfig: {\n ...config,\n extraPrompts: {\n ...defaultExtraPrompts,\n ...extraPrompts,\n },\n modelReasoningEfforts: {\n ...defaultModelReasoningEfforts,\n ...modelReasoningEfforts,\n },\n },\n changed: true,\n }\n}\n\nexport function mergeConfigWithDefaults(): AppConfig {\n const config = readConfigFromDisk()\n const { mergedConfig, changed } = mergeDefaultConfig(config)\n\n if (changed) {\n try {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(mergedConfig, null, 2)}\\n`,\n \"utf8\",\n )\n } catch (writeError) {\n consola.warn(\n \"Failed to write merged extraPrompts to config file\",\n writeError,\n )\n }\n }\n\n cachedConfig = mergedConfig\n return mergedConfig\n}\n\nexport function getConfig(): AppConfig {\n cachedConfig ??= readConfigFromDisk()\n return cachedConfig\n}\n\nexport function getExtraPromptForModel(model: string): string {\n const config = getConfig()\n return config.extraPrompts?.[model] ?? \"\"\n}\n\nexport function getSmallModel(): string {\n const config = getConfig()\n return config.smallModel ?? \"gpt-5-mini\"\n}\n\nexport function getResponsesApiContextManagementModels(): Array<string> {\n const config = getConfig()\n return (\n config.responsesApiContextManagementModels\n ?? defaultConfig.responsesApiContextManagementModels\n ?? []\n )\n}\n\nexport function isResponsesApiContextManagementModel(model: string): boolean {\n return getResponsesApiContextManagementModels().includes(model)\n}\n\nexport function getReasoningEffortForModel(\n model: string,\n): \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" {\n const config = getConfig()\n return config.modelReasoningEfforts?.[model] ?? \"high\"\n}\n\nexport function normalizeProviderBaseUrl(url: string): string {\n return url.trim().replace(/\\/+$/u, \"\")\n}\n\nexport function getProviderConfig(name: string): ResolvedProviderConfig | null {\n const providerName = name.trim()\n if (!providerName) {\n return null\n }\n\n const config = getConfig()\n const provider = config.providers?.[providerName]\n if (!provider) {\n return null\n }\n\n if (provider.enabled === false) {\n return null\n }\n\n const type = provider.type ?? \"anthropic\"\n if (type !== \"anthropic\") {\n consola.warn(\n `Provider ${providerName} is ignored because only anthropic type is supported`,\n )\n return null\n }\n\n const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? \"\")\n const apiKey = (provider.apiKey ?? \"\").trim()\n if (!baseUrl || !apiKey) {\n consola.warn(\n `Provider ${providerName} is enabled but missing baseUrl or apiKey`,\n )\n return null\n }\n\n return {\n name: providerName,\n type,\n baseUrl,\n apiKey,\n models: provider.models,\n adjustInputTokens: provider.adjustInputTokens,\n }\n}\n\nexport function listEnabledProviders(): Array<string> {\n const config = getConfig()\n const providerNames = Object.keys(config.providers ?? {})\n return providerNames.filter((name) => getProviderConfig(name) !== null)\n}\n\nexport function isMessagesApiEnabled(): boolean {\n const config = getConfig()\n return config.useMessagesApi ?? true\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { PATHS } from "./paths-Cla6y5eD.js";
|
|
2
|
+
import { defineCommand } from "citty";
|
|
3
|
+
import consola from "consola";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
|
|
7
|
+
//#region src/debug.ts
|
|
8
|
+
async function getPackageVersion() {
|
|
9
|
+
try {
|
|
10
|
+
const packageJsonPath = new URL("../package.json", import.meta.url).pathname;
|
|
11
|
+
return JSON.parse(await fs.readFile(packageJsonPath)).version;
|
|
12
|
+
} catch {
|
|
13
|
+
return "unknown";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function getRuntimeInfo() {
|
|
17
|
+
const isBun = typeof Bun !== "undefined";
|
|
18
|
+
return {
|
|
19
|
+
name: isBun ? "bun" : "node",
|
|
20
|
+
version: isBun ? Bun.version : process.version.slice(1),
|
|
21
|
+
platform: os.platform(),
|
|
22
|
+
arch: os.arch()
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async function checkTokenExists() {
|
|
26
|
+
try {
|
|
27
|
+
if (!(await fs.stat(PATHS.GITHUB_TOKEN_PATH)).isFile()) return false;
|
|
28
|
+
return (await fs.readFile(PATHS.GITHUB_TOKEN_PATH, "utf8")).trim().length > 0;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function getDebugInfo() {
|
|
34
|
+
const [version, tokenExists] = await Promise.all([getPackageVersion(), checkTokenExists()]);
|
|
35
|
+
return {
|
|
36
|
+
version,
|
|
37
|
+
runtime: getRuntimeInfo(),
|
|
38
|
+
paths: {
|
|
39
|
+
APP_DIR: PATHS.APP_DIR,
|
|
40
|
+
GITHUB_TOKEN_PATH: PATHS.GITHUB_TOKEN_PATH
|
|
41
|
+
},
|
|
42
|
+
tokenExists
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function printDebugInfoPlain(info) {
|
|
46
|
+
consola.info(`copilot-api debug
|
|
47
|
+
|
|
48
|
+
Version: ${info.version}
|
|
49
|
+
Runtime: ${info.runtime.name} ${info.runtime.version} (${info.runtime.platform} ${info.runtime.arch})
|
|
50
|
+
|
|
51
|
+
Paths:
|
|
52
|
+
- APP_DIR: ${info.paths.APP_DIR}
|
|
53
|
+
- GITHUB_TOKEN_PATH: ${info.paths.GITHUB_TOKEN_PATH}
|
|
54
|
+
|
|
55
|
+
Token exists: ${info.tokenExists ? "Yes" : "No"}`);
|
|
56
|
+
}
|
|
57
|
+
function printDebugInfoJson(info) {
|
|
58
|
+
console.log(JSON.stringify(info, null, 2));
|
|
59
|
+
}
|
|
60
|
+
async function runDebug(options) {
|
|
61
|
+
const debugInfo = await getDebugInfo();
|
|
62
|
+
if (options.json) printDebugInfoJson(debugInfo);
|
|
63
|
+
else printDebugInfoPlain(debugInfo);
|
|
64
|
+
}
|
|
65
|
+
const debug = defineCommand({
|
|
66
|
+
meta: {
|
|
67
|
+
name: "debug",
|
|
68
|
+
description: "Print debug information about the application"
|
|
69
|
+
},
|
|
70
|
+
args: { json: {
|
|
71
|
+
type: "boolean",
|
|
72
|
+
default: false,
|
|
73
|
+
description: "Output debug information as JSON"
|
|
74
|
+
} },
|
|
75
|
+
run({ args }) {
|
|
76
|
+
return runDebug({ json: args.json });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { debug };
|
|
82
|
+
//# sourceMappingURL=debug-Dx1S6uWG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-Dx1S6uWG.js","names":[],"sources":["../src/debug.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\nimport fs from \"node:fs/promises\"\nimport os from \"node:os\"\n\nimport { PATHS } from \"./lib/paths\"\n\ninterface DebugInfo {\n version: string\n runtime: {\n name: string\n version: string\n platform: string\n arch: string\n }\n paths: {\n APP_DIR: string\n GITHUB_TOKEN_PATH: string\n }\n tokenExists: boolean\n}\n\ninterface RunDebugOptions {\n json: boolean\n}\n\nasync function getPackageVersion(): Promise<string> {\n try {\n const packageJsonPath = new URL(\"../package.json\", import.meta.url).pathname\n // @ts-expect-error https://github.com/sindresorhus/eslint-plugin-unicorn/blob/v59.0.1/docs/rules/prefer-json-parse-buffer.md\n // JSON.parse() can actually parse buffers\n const packageJson = JSON.parse(await fs.readFile(packageJsonPath)) as {\n version: string\n }\n return packageJson.version\n } catch {\n return \"unknown\"\n }\n}\n\nfunction getRuntimeInfo() {\n const isBun = typeof Bun !== \"undefined\"\n\n return {\n name: isBun ? \"bun\" : \"node\",\n version: isBun ? Bun.version : process.version.slice(1),\n platform: os.platform(),\n arch: os.arch(),\n }\n}\n\nasync function checkTokenExists(): Promise<boolean> {\n try {\n const stats = await fs.stat(PATHS.GITHUB_TOKEN_PATH)\n if (!stats.isFile()) return false\n\n const content = await fs.readFile(PATHS.GITHUB_TOKEN_PATH, \"utf8\")\n return content.trim().length > 0\n } catch {\n return false\n }\n}\n\nasync function getDebugInfo(): Promise<DebugInfo> {\n const [version, tokenExists] = await Promise.all([\n getPackageVersion(),\n checkTokenExists(),\n ])\n\n return {\n version,\n runtime: getRuntimeInfo(),\n paths: {\n APP_DIR: PATHS.APP_DIR,\n GITHUB_TOKEN_PATH: PATHS.GITHUB_TOKEN_PATH,\n },\n tokenExists,\n }\n}\n\nfunction printDebugInfoPlain(info: DebugInfo): void {\n consola.info(`copilot-api debug\n\nVersion: ${info.version}\nRuntime: ${info.runtime.name} ${info.runtime.version} (${info.runtime.platform} ${info.runtime.arch})\n\nPaths:\n- APP_DIR: ${info.paths.APP_DIR}\n- GITHUB_TOKEN_PATH: ${info.paths.GITHUB_TOKEN_PATH}\n\nToken exists: ${info.tokenExists ? \"Yes\" : \"No\"}`)\n}\n\nfunction printDebugInfoJson(info: DebugInfo): void {\n console.log(JSON.stringify(info, null, 2))\n}\n\nexport async function runDebug(options: RunDebugOptions): Promise<void> {\n const debugInfo = await getDebugInfo()\n\n if (options.json) {\n printDebugInfoJson(debugInfo)\n } else {\n printDebugInfoPlain(debugInfo)\n }\n}\n\nexport const debug = defineCommand({\n meta: {\n name: \"debug\",\n description: \"Print debug information about the application\",\n },\n args: {\n json: {\n type: \"boolean\",\n default: false,\n description: \"Output debug information as JSON\",\n },\n },\n run({ args }) {\n return runDebug({\n json: args.json,\n })\n },\n})\n"],"mappings":";;;;;;;AA4BA,eAAe,oBAAqC;AAClD,KAAI;EACF,MAAM,kBAAkB,IAAI,IAAI,mBAAmB,OAAO,KAAK,IAAI,CAAC;AAMpE,SAHoB,KAAK,MAAM,MAAM,GAAG,SAAS,gBAAgB,CAAC,CAG/C;SACb;AACN,SAAO;;;AAIX,SAAS,iBAAiB;CACxB,MAAM,QAAQ,OAAO,QAAQ;AAE7B,QAAO;EACL,MAAM,QAAQ,QAAQ;EACtB,SAAS,QAAQ,IAAI,UAAU,QAAQ,QAAQ,MAAM,EAAE;EACvD,UAAU,GAAG,UAAU;EACvB,MAAM,GAAG,MAAM;EAChB;;AAGH,eAAe,mBAAqC;AAClD,KAAI;AAEF,MAAI,EADU,MAAM,GAAG,KAAK,MAAM,kBAAkB,EACzC,QAAQ,CAAE,QAAO;AAG5B,UADgB,MAAM,GAAG,SAAS,MAAM,mBAAmB,OAAO,EACnD,MAAM,CAAC,SAAS;SACzB;AACN,SAAO;;;AAIX,eAAe,eAAmC;CAChD,MAAM,CAAC,SAAS,eAAe,MAAM,QAAQ,IAAI,CAC/C,mBAAmB,EACnB,kBAAkB,CACnB,CAAC;AAEF,QAAO;EACL;EACA,SAAS,gBAAgB;EACzB,OAAO;GACL,SAAS,MAAM;GACf,mBAAmB,MAAM;GAC1B;EACD;EACD;;AAGH,SAAS,oBAAoB,MAAuB;AAClD,SAAQ,KAAK;;WAEJ,KAAK,QAAQ;WACb,KAAK,QAAQ,KAAK,GAAG,KAAK,QAAQ,QAAQ,IAAI,KAAK,QAAQ,SAAS,GAAG,KAAK,QAAQ,KAAK;;;aAGvF,KAAK,MAAM,QAAQ;uBACT,KAAK,MAAM,kBAAkB;;gBAEpC,KAAK,cAAc,QAAQ,OAAO;;AAGlD,SAAS,mBAAmB,MAAuB;AACjD,SAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;;AAG5C,eAAsB,SAAS,SAAyC;CACtE,MAAM,YAAY,MAAM,cAAc;AAEtC,KAAI,QAAQ,KACV,oBAAmB,UAAU;KAE7B,qBAAoB,UAAU;;AAIlC,MAAa,QAAQ,cAAc;CACjC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,EACJ,MAAM;EACJ,MAAM;EACN,SAAS;EACT,aAAa;EACd,EACF;CACD,IAAI,EAAE,QAAQ;AACZ,SAAO,SAAS,EACd,MAAM,KAAK,MACZ,CAAC;;CAEL,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { HTTPError, getGitHubApiBaseUrl, githubHeaders, state } from "./utils-DKqD66k9.js";
|
|
2
|
+
|
|
3
|
+
//#region src/services/github/get-copilot-usage.ts
|
|
4
|
+
const getCopilotUsage = async () => {
|
|
5
|
+
const response = await fetch(`${getGitHubApiBaseUrl()}/copilot_internal/user`, { headers: githubHeaders(state) });
|
|
6
|
+
if (!response.ok) throw new HTTPError("Failed to get Copilot usage", response);
|
|
7
|
+
return await response.json();
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
export { getCopilotUsage };
|
|
12
|
+
//# sourceMappingURL=get-copilot-usage-CuVET98U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-copilot-usage-CuVET98U.js","names":[],"sources":["../src/services/github/get-copilot-usage.ts"],"sourcesContent":["import { getGitHubApiBaseUrl, githubHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const getCopilotUsage = async (): Promise<CopilotUsageResponse> => {\n const response = await fetch(\n `${getGitHubApiBaseUrl()}/copilot_internal/user`,\n {\n headers: githubHeaders(state),\n },\n )\n\n if (!response.ok) {\n throw new HTTPError(\"Failed to get Copilot usage\", response)\n }\n\n return (await response.json()) as CopilotUsageResponse\n}\n\nexport interface QuotaDetail {\n entitlement: number\n overage_count: number\n overage_permitted: boolean\n percent_remaining: number\n quota_id: string\n quota_remaining: number\n remaining: number\n unlimited: boolean\n}\n\ninterface QuotaSnapshots {\n chat: QuotaDetail\n completions: QuotaDetail\n premium_interactions: QuotaDetail\n}\n\ninterface CopilotUsageResponse {\n access_type_sku: string\n analytics_tracking_id: string\n assigned_date: string\n can_signup_for_limited: boolean\n chat_enabled: boolean\n copilot_plan: string\n organization_login_list: Array<unknown>\n organization_list: Array<unknown>\n quota_reset_date: string\n quota_snapshots: QuotaSnapshots\n}\n"],"mappings":";;;AAIA,MAAa,kBAAkB,YAA2C;CACxE,MAAM,WAAW,MAAM,MACrB,GAAG,qBAAqB,CAAC,yBACzB,EACE,SAAS,cAAc,MAAM,EAC9B,CACF;AAED,KAAI,CAAC,SAAS,GACZ,OAAM,IAAI,UAAU,+BAA+B,SAAS;AAG9D,QAAQ,MAAM,SAAS,MAAM"}
|