@jeffreycao/copilot-api 1.3.4 → 1.3.5

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 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:
@@ -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-artyYmCm.js";
3
+ import { setupGitHubToken } from "./token-G21yvpKv.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-PjO-EXxs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-PjO-EXxs.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-artyYmCm.js";
3
+ import { setupGitHubToken } from "./token-G21yvpKv.js";
4
+ import { getCopilotUsage } from "./get-copilot-usage-xj7WA78o.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-C9abXGdq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-usage-C9abXGdq.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,173 @@
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
+ compactUseSmallModel: true,
48
+ useMessagesApi: true
49
+ };
50
+ let cachedConfig = null;
51
+ function ensureConfigFile() {
52
+ try {
53
+ fs.accessSync(PATHS.CONFIG_PATH, fs.constants.R_OK | fs.constants.W_OK);
54
+ } catch {
55
+ fs.mkdirSync(PATHS.APP_DIR, { recursive: true });
56
+ fs.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
57
+ try {
58
+ fs.chmodSync(PATHS.CONFIG_PATH, 384);
59
+ } catch {
60
+ return;
61
+ }
62
+ }
63
+ }
64
+ function readConfigFromDisk() {
65
+ ensureConfigFile();
66
+ try {
67
+ const raw = fs.readFileSync(PATHS.CONFIG_PATH, "utf8");
68
+ if (!raw.trim()) {
69
+ fs.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
70
+ return defaultConfig;
71
+ }
72
+ return JSON.parse(raw);
73
+ } catch (error) {
74
+ consola.error("Failed to read config file, using default config", error);
75
+ return defaultConfig;
76
+ }
77
+ }
78
+ function mergeDefaultConfig(config) {
79
+ const extraPrompts = config.extraPrompts ?? {};
80
+ const defaultExtraPrompts = defaultConfig.extraPrompts ?? {};
81
+ const modelReasoningEfforts = config.modelReasoningEfforts ?? {};
82
+ const defaultModelReasoningEfforts = defaultConfig.modelReasoningEfforts ?? {};
83
+ const missingExtraPromptModels = Object.keys(defaultExtraPrompts).filter((model) => !Object.hasOwn(extraPrompts, model));
84
+ const missingReasoningEffortModels = Object.keys(defaultModelReasoningEfforts).filter((model) => !Object.hasOwn(modelReasoningEfforts, model));
85
+ const hasExtraPromptChanges = missingExtraPromptModels.length > 0;
86
+ const hasReasoningEffortChanges = missingReasoningEffortModels.length > 0;
87
+ if (!hasExtraPromptChanges && !hasReasoningEffortChanges) return {
88
+ mergedConfig: config,
89
+ changed: false
90
+ };
91
+ return {
92
+ mergedConfig: {
93
+ ...config,
94
+ extraPrompts: {
95
+ ...defaultExtraPrompts,
96
+ ...extraPrompts
97
+ },
98
+ modelReasoningEfforts: {
99
+ ...defaultModelReasoningEfforts,
100
+ ...modelReasoningEfforts
101
+ }
102
+ },
103
+ changed: true
104
+ };
105
+ }
106
+ function mergeConfigWithDefaults() {
107
+ const config = readConfigFromDisk();
108
+ const { mergedConfig, changed } = mergeDefaultConfig(config);
109
+ if (changed) try {
110
+ fs.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(mergedConfig, null, 2)}\n`, "utf8");
111
+ } catch (writeError) {
112
+ consola.warn("Failed to write merged extraPrompts to config file", writeError);
113
+ }
114
+ cachedConfig = mergedConfig;
115
+ return mergedConfig;
116
+ }
117
+ function getConfig() {
118
+ cachedConfig ??= readConfigFromDisk();
119
+ return cachedConfig;
120
+ }
121
+ function getExtraPromptForModel(model) {
122
+ return getConfig().extraPrompts?.[model] ?? "";
123
+ }
124
+ function getSmallModel() {
125
+ return getConfig().smallModel ?? "gpt-5-mini";
126
+ }
127
+ function getResponsesApiContextManagementModels() {
128
+ return getConfig().responsesApiContextManagementModels ?? defaultConfig.responsesApiContextManagementModels ?? [];
129
+ }
130
+ function isResponsesApiContextManagementModel(model) {
131
+ return getResponsesApiContextManagementModels().includes(model);
132
+ }
133
+ function getReasoningEffortForModel(model) {
134
+ return getConfig().modelReasoningEfforts?.[model] ?? "high";
135
+ }
136
+ function shouldCompactUseSmallModel() {
137
+ return getConfig().compactUseSmallModel ?? true;
138
+ }
139
+ function normalizeProviderBaseUrl(url) {
140
+ return url.trim().replace(/\/+$/u, "");
141
+ }
142
+ function getProviderConfig(name) {
143
+ const providerName = name.trim();
144
+ if (!providerName) return null;
145
+ const provider = getConfig().providers?.[providerName];
146
+ if (!provider) return null;
147
+ if (provider.enabled === false) return null;
148
+ const type = provider.type ?? "anthropic";
149
+ if (type !== "anthropic") {
150
+ consola.warn(`Provider ${providerName} is ignored because only anthropic type is supported`);
151
+ return null;
152
+ }
153
+ const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? "");
154
+ const apiKey = (provider.apiKey ?? "").trim();
155
+ if (!baseUrl || !apiKey) {
156
+ consola.warn(`Provider ${providerName} is enabled but missing baseUrl or apiKey`);
157
+ return null;
158
+ }
159
+ return {
160
+ name: providerName,
161
+ type,
162
+ baseUrl,
163
+ apiKey,
164
+ models: provider.models
165
+ };
166
+ }
167
+ function isMessagesApiEnabled() {
168
+ return getConfig().useMessagesApi ?? true;
169
+ }
170
+
171
+ //#endregion
172
+ export { getConfig, getExtraPromptForModel, getProviderConfig, getReasoningEffortForModel, getSmallModel, isMessagesApiEnabled, isResponsesApiContextManagementModel, mergeConfigWithDefaults, shouldCompactUseSmallModel };
173
+ //# sourceMappingURL=config-D3COstcJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-D3COstcJ.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 compactUseSmallModel?: 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}\n\nexport interface ResolvedProviderConfig {\n name: string\n type: \"anthropic\"\n baseUrl: string\n apiKey: string\n models?: Record<string, ModelConfig>\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 compactUseSmallModel: 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 shouldCompactUseSmallModel(): boolean {\n const config = getConfig()\n return config.compactUseSmallModel ?? true\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 }\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-artyYmCm.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-xj7WA78o.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-copilot-usage-xj7WA78o.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"}