clisponsor 1.0.10 → 1.0.15
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 +1 -1
- package/bin/clisponsor.mjs +507 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLIsponsor Hook
|
|
2
2
|
|
|
3
|
-
Hook package codebase for the `clisponsor` installer and Codex, Claude Code, and
|
|
3
|
+
Hook package codebase for the `clisponsor` installer and Codex, Claude Code, Gemini, Antigravity, OpenCode, Pi, GitHub Copilot CLI, and Qwen Code hook templates.
|
|
4
4
|
|
|
5
5
|
This codebase owns local installation, device login, diagnostics, and hook adapters. It must not contain public website, dashboard app, API account, or ad-serving server code.
|
|
6
6
|
|
package/bin/clisponsor.mjs
CHANGED
|
@@ -16,6 +16,30 @@ const DEFAULT_BACKEND_BASE_URL = process.env.CLISPONSOR_BACKEND_BASE_URL || "htt
|
|
|
16
16
|
const HOOK_VERSION = "1.0.0";
|
|
17
17
|
const NETWORK_TIMEOUT_MS = 3000;
|
|
18
18
|
const ANTIGRAVITY_EVENTS = ["PreInvocation"];
|
|
19
|
+
const OPENCODE_NON_TUI_COMMANDS = new Set([
|
|
20
|
+
"completion",
|
|
21
|
+
"acp",
|
|
22
|
+
"mcp",
|
|
23
|
+
"run",
|
|
24
|
+
"debug",
|
|
25
|
+
"providers",
|
|
26
|
+
"auth",
|
|
27
|
+
"agent",
|
|
28
|
+
"upgrade",
|
|
29
|
+
"uninstall",
|
|
30
|
+
"serve",
|
|
31
|
+
"web",
|
|
32
|
+
"models",
|
|
33
|
+
"stats",
|
|
34
|
+
"export",
|
|
35
|
+
"import",
|
|
36
|
+
"github",
|
|
37
|
+
"pr",
|
|
38
|
+
"session",
|
|
39
|
+
"plugin",
|
|
40
|
+
"plug",
|
|
41
|
+
"db",
|
|
42
|
+
]);
|
|
19
43
|
|
|
20
44
|
function argValue(name) {
|
|
21
45
|
const prefix = `${name}=`;
|
|
@@ -130,6 +154,22 @@ function chmodExecutable(file) {
|
|
|
130
154
|
} catch {}
|
|
131
155
|
}
|
|
132
156
|
|
|
157
|
+
function xdgConfigHome() {
|
|
158
|
+
return process.env.XDG_CONFIG_HOME || path.join(HOME, ".config");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function openCodeConfigDir() {
|
|
162
|
+
return process.env.OPENCODE_CONFIG_DIR || path.join(xdgConfigHome(), "opencode");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function copilotHome() {
|
|
166
|
+
return process.env.COPILOT_HOME || path.join(HOME, ".copilot");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function qwenHome() {
|
|
170
|
+
return process.env.QWEN_HOME || path.join(HOME, ".qwen");
|
|
171
|
+
}
|
|
172
|
+
|
|
133
173
|
function commandExists(command) {
|
|
134
174
|
const paths = String(process.env.PATH || "").split(path.delimiter).filter(Boolean);
|
|
135
175
|
const extensions = process.platform === "win32" ? String(process.env.PATHEXT || ".EXE;.CMD;.BAT").split(";") : [""];
|
|
@@ -162,6 +202,10 @@ function isClisponsorCommand(value) {
|
|
|
162
202
|
(value.includes("clisponsor_claude_hook.mjs") ||
|
|
163
203
|
value.includes("clisponsor_gemini_hook.mjs") ||
|
|
164
204
|
value.includes("clisponsor_antigravity_hook.mjs") ||
|
|
205
|
+
value.includes("clisponsor_opencode_plugin") ||
|
|
206
|
+
value.includes("clisponsor_pi_extension") ||
|
|
207
|
+
value.includes("clisponsor_copilot_hook.mjs") ||
|
|
208
|
+
value.includes("clisponsor_qwen_hook.mjs") ||
|
|
165
209
|
value.includes(`${path.sep}.clisponsor${path.sep}`) ||
|
|
166
210
|
value.includes("/.clisponsor/") ||
|
|
167
211
|
value.includes("\\.clisponsor\\"))
|
|
@@ -235,6 +279,28 @@ function addGeminiCommandHook(settings, eventName, matcher, command) {
|
|
|
235
279
|
return true;
|
|
236
280
|
}
|
|
237
281
|
|
|
282
|
+
function addQwenCommandHook(settings, eventName, command) {
|
|
283
|
+
if (!isPlainObject(settings.hooks)) settings.hooks = {};
|
|
284
|
+
const current = Array.isArray(settings.hooks[eventName]) ? settings.hooks[eventName] : [];
|
|
285
|
+
const exists = current.some((entry) => isClisponsorHookEntry(entry));
|
|
286
|
+
if (exists) return false;
|
|
287
|
+
|
|
288
|
+
settings.hooks[eventName] = [
|
|
289
|
+
...current,
|
|
290
|
+
{
|
|
291
|
+
hooks: [
|
|
292
|
+
{
|
|
293
|
+
type: "command",
|
|
294
|
+
command,
|
|
295
|
+
name: "clisponsor",
|
|
296
|
+
timeout: 5000,
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
];
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
|
|
238
304
|
function removeClaudeCommandHooks(settings) {
|
|
239
305
|
if (!isPlainObject(settings.hooks)) return false;
|
|
240
306
|
let changed = false;
|
|
@@ -466,7 +532,7 @@ try {
|
|
|
466
532
|
const ad = await res.json();
|
|
467
533
|
if (outputMode === "antigravity") {
|
|
468
534
|
const payload = {};
|
|
469
|
-
if (ad.display_line) payload.injectSteps = [{
|
|
535
|
+
if (ad.display_line) payload.injectSteps = [{ userMessage: sponsoredLine(ad.display_line) }];
|
|
470
536
|
console.log(JSON.stringify(payload));
|
|
471
537
|
} else if (ad.display_line) {
|
|
472
538
|
console.log(JSON.stringify({ systemMessage: sponsoredLine(ad.display_line) }));
|
|
@@ -481,6 +547,270 @@ try {
|
|
|
481
547
|
`;
|
|
482
548
|
}
|
|
483
549
|
|
|
550
|
+
function openCodePluginSource() {
|
|
551
|
+
return `import fs from "node:fs";
|
|
552
|
+
import crypto from "node:crypto";
|
|
553
|
+
|
|
554
|
+
const CONFIG_PATH = ${JSON.stringify(CONFIG_PATH)};
|
|
555
|
+
const HOOK_VERSION = ${JSON.stringify(HOOK_VERSION)};
|
|
556
|
+
const NON_TUI_COMMANDS = new Set(${JSON.stringify([...OPENCODE_NON_TUI_COMMANDS])});
|
|
557
|
+
|
|
558
|
+
function readConfig() {
|
|
559
|
+
try {
|
|
560
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
|
|
561
|
+
} catch {
|
|
562
|
+
return {};
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function likelyTuiInvocation() {
|
|
567
|
+
const args = process.argv.slice(2).filter((arg) => !arg.startsWith("-"));
|
|
568
|
+
if (args.length === 0) return true;
|
|
569
|
+
return !NON_TUI_COMMANDS.has(args[0]);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function sponsoredLine(line) {
|
|
573
|
+
return "[Sponsored] " + line;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export const CLIsponsorOpenCodePlugin = async ({ client }) => {
|
|
577
|
+
if (!likelyTuiInvocation()) return {};
|
|
578
|
+
let startSessionRequested = false;
|
|
579
|
+
|
|
580
|
+
async function serve(event, placement, metadata = {}) {
|
|
581
|
+
if (placement === "StartSession") {
|
|
582
|
+
if (startSessionRequested) return;
|
|
583
|
+
startSessionRequested = true;
|
|
584
|
+
}
|
|
585
|
+
const cfg = readConfig();
|
|
586
|
+
const serveBaseUrl = cfg.serveBaseUrl || cfg.apiBaseUrl;
|
|
587
|
+
if (!serveBaseUrl || !cfg.userId || !cfg.deviceCode || !cfg.deviceSecret) return;
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
const res = await fetch(serveBaseUrl + "/v1/ads/serve", {
|
|
591
|
+
method: "POST",
|
|
592
|
+
headers: {
|
|
593
|
+
"content-type": "application/json",
|
|
594
|
+
"authorization": "Bearer " + cfg.deviceSecret,
|
|
595
|
+
"x-clisponsor-hook-version": HOOK_VERSION,
|
|
596
|
+
},
|
|
597
|
+
body: JSON.stringify({
|
|
598
|
+
user_id: cfg.userId,
|
|
599
|
+
device_code: cfg.deviceCode,
|
|
600
|
+
client: "OpenCode",
|
|
601
|
+
hook_event: event,
|
|
602
|
+
placement,
|
|
603
|
+
idempotency_key: crypto.randomUUID(),
|
|
604
|
+
metadata: {
|
|
605
|
+
hookVersion: HOOK_VERSION,
|
|
606
|
+
openCode: metadata,
|
|
607
|
+
},
|
|
608
|
+
}),
|
|
609
|
+
});
|
|
610
|
+
if (!res.ok) return;
|
|
611
|
+
const ad = await res.json();
|
|
612
|
+
if (!ad.display_line) return;
|
|
613
|
+
await client.tui.showToast({
|
|
614
|
+
body: {
|
|
615
|
+
title: "CLIsponsor " + placement,
|
|
616
|
+
message: sponsoredLine(ad.display_line),
|
|
617
|
+
variant: "info",
|
|
618
|
+
duration: 10000,
|
|
619
|
+
},
|
|
620
|
+
});
|
|
621
|
+
} catch {}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const startupTimer = setTimeout(() => {
|
|
625
|
+
void serve("plugin.start", "StartSession");
|
|
626
|
+
}, 2500);
|
|
627
|
+
startupTimer.unref?.();
|
|
628
|
+
|
|
629
|
+
return {
|
|
630
|
+
event: async ({ event }) => {
|
|
631
|
+
if (event.type === "session.created") {
|
|
632
|
+
await serve("session.created", "StartSession", {
|
|
633
|
+
sessionID: event.properties?.sessionID,
|
|
634
|
+
});
|
|
635
|
+
} else if (event.type === "session.idle") {
|
|
636
|
+
await serve("session.idle", "EndTurn", {
|
|
637
|
+
sessionID: event.properties?.sessionID,
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
},
|
|
641
|
+
"chat.message": async (input) => {
|
|
642
|
+
await serve("chat.message", "StartTurn", {
|
|
643
|
+
sessionID: input.sessionID,
|
|
644
|
+
agent: input.agent,
|
|
645
|
+
});
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
};
|
|
649
|
+
`;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function piExtensionSource() {
|
|
653
|
+
return `import fs from "node:fs";
|
|
654
|
+
import crypto from "node:crypto";
|
|
655
|
+
|
|
656
|
+
const CONFIG_PATH = ${JSON.stringify(CONFIG_PATH)};
|
|
657
|
+
const HOOK_VERSION = ${JSON.stringify(HOOK_VERSION)};
|
|
658
|
+
|
|
659
|
+
function readConfig() {
|
|
660
|
+
try {
|
|
661
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
|
|
662
|
+
} catch {
|
|
663
|
+
return {};
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function sponsoredLine(line) {
|
|
668
|
+
return "[Sponsored] " + line;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function canShow(ctx) {
|
|
672
|
+
return Boolean(ctx?.hasUI && ctx?.ui?.notify);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
export default function CLIsponsorPiExtension(pi) {
|
|
676
|
+
async function serve(event, placement, ctx, metadata = {}) {
|
|
677
|
+
if (!canShow(ctx)) return;
|
|
678
|
+
const cfg = readConfig();
|
|
679
|
+
const serveBaseUrl = cfg.serveBaseUrl || cfg.apiBaseUrl;
|
|
680
|
+
if (!serveBaseUrl || !cfg.userId || !cfg.deviceCode || !cfg.deviceSecret) return;
|
|
681
|
+
|
|
682
|
+
try {
|
|
683
|
+
const res = await fetch(serveBaseUrl + "/v1/ads/serve", {
|
|
684
|
+
method: "POST",
|
|
685
|
+
headers: {
|
|
686
|
+
"content-type": "application/json",
|
|
687
|
+
"authorization": "Bearer " + cfg.deviceSecret,
|
|
688
|
+
"x-clisponsor-hook-version": HOOK_VERSION,
|
|
689
|
+
},
|
|
690
|
+
body: JSON.stringify({
|
|
691
|
+
user_id: cfg.userId,
|
|
692
|
+
device_code: cfg.deviceCode,
|
|
693
|
+
client: "Pi",
|
|
694
|
+
hook_event: event,
|
|
695
|
+
placement,
|
|
696
|
+
idempotency_key: crypto.randomUUID(),
|
|
697
|
+
metadata: {
|
|
698
|
+
hookVersion: HOOK_VERSION,
|
|
699
|
+
pi: metadata,
|
|
700
|
+
},
|
|
701
|
+
}),
|
|
702
|
+
});
|
|
703
|
+
if (!res.ok) return;
|
|
704
|
+
const ad = await res.json();
|
|
705
|
+
if (!ad.display_line) return;
|
|
706
|
+
ctx.ui.notify("CLIsponsor " + placement + "\\n" + sponsoredLine(ad.display_line), "info");
|
|
707
|
+
} catch {}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
pi.on("session_start", async (event, ctx) => {
|
|
711
|
+
await serve("session_start", "StartSession", ctx, {
|
|
712
|
+
reason: event?.reason,
|
|
713
|
+
});
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
pi.on("agent_start", async (_event, ctx) => {
|
|
717
|
+
await serve("agent_start", "StartTurn", ctx);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
721
|
+
await serve("agent_end", "EndTurn", ctx);
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
`;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function copilotHookSource() {
|
|
728
|
+
return `#!/usr/bin/env node
|
|
729
|
+
import fs from "node:fs";
|
|
730
|
+
import crypto from "node:crypto";
|
|
731
|
+
|
|
732
|
+
const event = process.argv[2] || "userPromptSubmitted";
|
|
733
|
+
const CONFIG_PATH = ${JSON.stringify(CONFIG_PATH)};
|
|
734
|
+
const HOOK_VERSION = ${JSON.stringify(HOOK_VERSION)};
|
|
735
|
+
const placements = {
|
|
736
|
+
sessionStart: "StartSession",
|
|
737
|
+
userPromptSubmitted: "StartTurn",
|
|
738
|
+
agentStop: "EndTurn",
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
function sponsoredLine(line) {
|
|
742
|
+
return "[Sponsored] " + line;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function readStdin() {
|
|
746
|
+
return new Promise((resolve) => {
|
|
747
|
+
let data = "";
|
|
748
|
+
process.stdin.on("data", (chunk) => (data += chunk));
|
|
749
|
+
process.stdin.on("end", () => resolve(data));
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function readConfig() {
|
|
754
|
+
try {
|
|
755
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
|
|
756
|
+
} catch {
|
|
757
|
+
return {};
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function progress(message) {
|
|
762
|
+
console.log(JSON.stringify({ type: "progress", message }));
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const inputRaw = await readStdin();
|
|
766
|
+
let input = {};
|
|
767
|
+
try {
|
|
768
|
+
input = inputRaw.trim() ? JSON.parse(inputRaw) : {};
|
|
769
|
+
} catch {}
|
|
770
|
+
|
|
771
|
+
try {
|
|
772
|
+
const placement = placements[event];
|
|
773
|
+
if (!placement) process.exit(0);
|
|
774
|
+
const cfg = readConfig();
|
|
775
|
+
const serveBaseUrl = cfg.serveBaseUrl || cfg.apiBaseUrl;
|
|
776
|
+
if (!serveBaseUrl || !cfg.userId || !cfg.deviceCode || !cfg.deviceSecret) process.exit(0);
|
|
777
|
+
|
|
778
|
+
const res = await fetch(serveBaseUrl + "/v1/ads/serve", {
|
|
779
|
+
method: "POST",
|
|
780
|
+
headers: {
|
|
781
|
+
"content-type": "application/json",
|
|
782
|
+
"authorization": "Bearer " + cfg.deviceSecret,
|
|
783
|
+
"x-clisponsor-hook-version": HOOK_VERSION,
|
|
784
|
+
},
|
|
785
|
+
body: JSON.stringify({
|
|
786
|
+
user_id: cfg.userId,
|
|
787
|
+
device_code: cfg.deviceCode,
|
|
788
|
+
client: "GitHubCopilot",
|
|
789
|
+
hook_event: event,
|
|
790
|
+
placement,
|
|
791
|
+
idempotency_key: crypto.randomUUID(),
|
|
792
|
+
metadata: {
|
|
793
|
+
hookVersion: HOOK_VERSION,
|
|
794
|
+
copilot: {
|
|
795
|
+
sessionId: input.sessionId || input.session_id,
|
|
796
|
+
source: input.source,
|
|
797
|
+
reason: input.reason,
|
|
798
|
+
stopReason: input.stopReason || input.stop_reason,
|
|
799
|
+
},
|
|
800
|
+
},
|
|
801
|
+
}),
|
|
802
|
+
});
|
|
803
|
+
if (!res.ok) process.exit(0);
|
|
804
|
+
const ad = await res.json();
|
|
805
|
+
if (!ad.display_line) process.exit(0);
|
|
806
|
+
progress("CLIsponsor " + placement + ": " + sponsoredLine(ad.display_line));
|
|
807
|
+
console.log(JSON.stringify({}));
|
|
808
|
+
} catch {
|
|
809
|
+
process.exit(0);
|
|
810
|
+
}
|
|
811
|
+
`;
|
|
812
|
+
}
|
|
813
|
+
|
|
484
814
|
function installGemini() {
|
|
485
815
|
if (!commandExists("gemini")) {
|
|
486
816
|
console.log("Gemini CLI not found. To enable CLIsponsor for Gemini, install Gemini CLI and rerun: npx clisponsor@latest install");
|
|
@@ -521,23 +851,132 @@ function installAntigravity() {
|
|
|
521
851
|
console.log("Antigravity CLI hook installed.");
|
|
522
852
|
}
|
|
523
853
|
|
|
854
|
+
function installOpenCode() {
|
|
855
|
+
if (!commandExists("opencode")) {
|
|
856
|
+
console.log("OpenCode CLI not found. To enable CLIsponsor for OpenCode, install OpenCode CLI and rerun: npx clisponsor@latest install");
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const opencodeDir = path.join(CONFIG_DIR, "opencode");
|
|
861
|
+
fs.mkdirSync(opencodeDir, { recursive: true });
|
|
862
|
+
const stagedPlugin = path.join(opencodeDir, "clisponsor_opencode_plugin.js");
|
|
863
|
+
fs.writeFileSync(stagedPlugin, openCodePluginSource(), { mode: 0o644 });
|
|
864
|
+
|
|
865
|
+
const pluginsDir = path.join(openCodeConfigDir(), "plugins");
|
|
866
|
+
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
867
|
+
const installedPlugin = path.join(pluginsDir, "clisponsor_opencode_plugin.js");
|
|
868
|
+
fs.copyFileSync(stagedPlugin, installedPlugin);
|
|
869
|
+
console.log(`Updated ${installedPlugin}`);
|
|
870
|
+
console.log("OpenCode CLI plugin installed.");
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function installPi() {
|
|
874
|
+
if (!commandExists("pi")) {
|
|
875
|
+
console.log("Pi CLI not found. To enable CLIsponsor for Pi, install Pi Coding Agent and rerun: npx clisponsor@latest install");
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const piDir = path.join(CONFIG_DIR, "pi");
|
|
880
|
+
fs.mkdirSync(piDir, { recursive: true });
|
|
881
|
+
const stagedExtension = path.join(piDir, "clisponsor_pi_extension.ts");
|
|
882
|
+
fs.writeFileSync(stagedExtension, piExtensionSource(), { mode: 0o644 });
|
|
883
|
+
|
|
884
|
+
const extensionsDir = path.join(HOME, ".pi", "agent", "extensions");
|
|
885
|
+
fs.mkdirSync(extensionsDir, { recursive: true });
|
|
886
|
+
const installedExtension = path.join(extensionsDir, "clisponsor_pi_extension.ts");
|
|
887
|
+
fs.copyFileSync(stagedExtension, installedExtension);
|
|
888
|
+
console.log(`Updated ${installedExtension}`);
|
|
889
|
+
console.log("Pi CLI extension installed.");
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function installCopilot() {
|
|
893
|
+
if (!commandExists("copilot")) {
|
|
894
|
+
console.log("GitHub Copilot CLI not found. To enable CLIsponsor for GitHub Copilot CLI, install Copilot CLI and rerun: npx clisponsor@latest install");
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const copilotDir = path.join(CONFIG_DIR, "copilot");
|
|
899
|
+
fs.mkdirSync(copilotDir, { recursive: true });
|
|
900
|
+
const hookPath = path.join(copilotDir, "clisponsor_copilot_hook.mjs");
|
|
901
|
+
fs.writeFileSync(hookPath, copilotHookSource(), { mode: 0o755 });
|
|
902
|
+
|
|
903
|
+
const hooksPath = path.join(copilotHome(), "hooks", "clisponsor.json");
|
|
904
|
+
writeJson(hooksPath, {
|
|
905
|
+
version: 1,
|
|
906
|
+
hooks: {
|
|
907
|
+
sessionStart: [
|
|
908
|
+
{
|
|
909
|
+
type: "command",
|
|
910
|
+
command: `node ${JSON.stringify(hookPath)} sessionStart`,
|
|
911
|
+
timeoutSec: 5,
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
userPromptSubmitted: [
|
|
915
|
+
{
|
|
916
|
+
type: "command",
|
|
917
|
+
command: `node ${JSON.stringify(hookPath)} userPromptSubmitted`,
|
|
918
|
+
timeoutSec: 5,
|
|
919
|
+
},
|
|
920
|
+
],
|
|
921
|
+
agentStop: [
|
|
922
|
+
{
|
|
923
|
+
type: "command",
|
|
924
|
+
command: `node ${JSON.stringify(hookPath)} agentStop`,
|
|
925
|
+
timeoutSec: 5,
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
},
|
|
929
|
+
});
|
|
930
|
+
console.log(`Updated ${hooksPath}`);
|
|
931
|
+
console.log("GitHub Copilot CLI hook installed.");
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function installQwen() {
|
|
935
|
+
if (!commandExists("qwen")) {
|
|
936
|
+
console.log("Qwen Code CLI not found. To enable CLIsponsor for Qwen Code, install Qwen Code and rerun: npx clisponsor@latest install");
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
const qwenDir = path.join(CONFIG_DIR, "qwen");
|
|
941
|
+
fs.mkdirSync(qwenDir, { recursive: true });
|
|
942
|
+
const hookPath = path.join(qwenDir, "clisponsor_qwen_hook.mjs");
|
|
943
|
+
fs.writeFileSync(hookPath, agentHookSource("QwenCode"), { mode: 0o755 });
|
|
944
|
+
|
|
945
|
+
const settingsPath = path.join(qwenHome(), "settings.json");
|
|
946
|
+
const settings = readEditableJson(settingsPath, {});
|
|
947
|
+
addQwenCommandHook(settings, "SessionStart", `node ${JSON.stringify(hookPath)} SessionStart`);
|
|
948
|
+
addQwenCommandHook(settings, "UserPromptSubmit", `node ${JSON.stringify(hookPath)} UserPromptSubmit`);
|
|
949
|
+
addQwenCommandHook(settings, "Stop", `node ${JSON.stringify(hookPath)} Stop`);
|
|
950
|
+
writeJson(settingsPath, settings);
|
|
951
|
+
console.log(`Updated ${settingsPath}`);
|
|
952
|
+
console.log("Qwen Code CLI hook installed.");
|
|
953
|
+
}
|
|
954
|
+
|
|
524
955
|
function installAll() {
|
|
525
956
|
installCodex();
|
|
526
957
|
installClaude();
|
|
527
958
|
installGemini();
|
|
528
959
|
installAntigravity();
|
|
960
|
+
installOpenCode();
|
|
961
|
+
installPi();
|
|
962
|
+
installCopilot();
|
|
963
|
+
installQwen();
|
|
529
964
|
}
|
|
530
965
|
|
|
531
966
|
function install() {
|
|
532
967
|
const target = process.argv[3] && !process.argv[3].startsWith("--") ? process.argv[3] : "all";
|
|
533
|
-
if (!["all", "codex", "claude", "gemini", "antigravity", "agy"].includes(target)) {
|
|
534
|
-
console.error("Unknown install target. Use: codex, claude, gemini, antigravity, or all.");
|
|
968
|
+
if (!["all", "codex", "claude", "gemini", "antigravity", "agy", "opencode", "pi", "copilot", "qwen"].includes(target)) {
|
|
969
|
+
console.error("Unknown install target. Use: codex, claude, gemini, antigravity, opencode, pi, copilot, qwen, or all.");
|
|
535
970
|
process.exit(1);
|
|
536
971
|
}
|
|
537
972
|
if (target === "all") installAll();
|
|
538
973
|
else if (target === "codex") installCodex();
|
|
539
974
|
else if (target === "claude") installClaude();
|
|
540
975
|
else if (target === "gemini") installGemini();
|
|
976
|
+
else if (target === "opencode") installOpenCode();
|
|
977
|
+
else if (target === "pi") installPi();
|
|
978
|
+
else if (target === "copilot") installCopilot();
|
|
979
|
+
else if (target === "qwen") installQwen();
|
|
541
980
|
else installAntigravity();
|
|
542
981
|
}
|
|
543
982
|
|
|
@@ -591,16 +1030,69 @@ function uninstallAntigravity() {
|
|
|
591
1030
|
console.log("Removed Antigravity hook script.");
|
|
592
1031
|
}
|
|
593
1032
|
|
|
1033
|
+
function uninstallOpenCode() {
|
|
1034
|
+
const installedPlugin = path.join(openCodeConfigDir(), "plugins", "clisponsor_opencode_plugin.js");
|
|
1035
|
+
if (fs.existsSync(installedPlugin)) {
|
|
1036
|
+
fs.rmSync(installedPlugin, { force: true });
|
|
1037
|
+
console.log(`Removed ${installedPlugin}`);
|
|
1038
|
+
} else {
|
|
1039
|
+
console.log("No CLIsponsor OpenCode plugin found.");
|
|
1040
|
+
}
|
|
1041
|
+
fs.rmSync(path.join(CONFIG_DIR, "opencode"), { recursive: true, force: true });
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
function uninstallPi() {
|
|
1045
|
+
const installedExtension = path.join(HOME, ".pi", "agent", "extensions", "clisponsor_pi_extension.ts");
|
|
1046
|
+
if (fs.existsSync(installedExtension)) {
|
|
1047
|
+
fs.rmSync(installedExtension, { force: true });
|
|
1048
|
+
console.log(`Removed ${installedExtension}`);
|
|
1049
|
+
} else {
|
|
1050
|
+
console.log("No CLIsponsor Pi extension found.");
|
|
1051
|
+
}
|
|
1052
|
+
fs.rmSync(path.join(CONFIG_DIR, "pi"), { recursive: true, force: true });
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function uninstallCopilot() {
|
|
1056
|
+
const hooksPath = path.join(copilotHome(), "hooks", "clisponsor.json");
|
|
1057
|
+
if (fs.existsSync(hooksPath)) {
|
|
1058
|
+
fs.rmSync(hooksPath, { force: true });
|
|
1059
|
+
console.log(`Removed ${hooksPath}`);
|
|
1060
|
+
} else {
|
|
1061
|
+
console.log("No CLIsponsor GitHub Copilot CLI hook found.");
|
|
1062
|
+
}
|
|
1063
|
+
fs.rmSync(path.join(CONFIG_DIR, "copilot"), { recursive: true, force: true });
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
function uninstallQwen() {
|
|
1067
|
+
const settingsPath = path.join(qwenHome(), "settings.json");
|
|
1068
|
+
const settings = readEditableJson(settingsPath, {});
|
|
1069
|
+
if (removeClaudeCommandHooks(settings)) {
|
|
1070
|
+
writeJson(settingsPath, settings);
|
|
1071
|
+
console.log(`Removed CLIsponsor hooks from ${settingsPath}`);
|
|
1072
|
+
} else {
|
|
1073
|
+
console.log("No CLIsponsor Qwen Code hooks found.");
|
|
1074
|
+
}
|
|
1075
|
+
fs.rmSync(path.join(CONFIG_DIR, "qwen", "clisponsor_qwen_hook.mjs"), { force: true });
|
|
1076
|
+
try {
|
|
1077
|
+
fs.rmdirSync(path.join(CONFIG_DIR, "qwen"));
|
|
1078
|
+
} catch {}
|
|
1079
|
+
console.log("Removed Qwen Code hook script.");
|
|
1080
|
+
}
|
|
1081
|
+
|
|
594
1082
|
function uninstall() {
|
|
595
1083
|
const target = process.argv[3] && !process.argv[3].startsWith("--") ? process.argv[3] : "all";
|
|
596
|
-
if (!["all", "codex", "claude", "gemini", "antigravity", "agy"].includes(target)) {
|
|
597
|
-
console.error("Unknown uninstall target. Use: codex, claude, gemini, antigravity, or all.");
|
|
1084
|
+
if (!["all", "codex", "claude", "gemini", "antigravity", "agy", "opencode", "pi", "copilot", "qwen"].includes(target)) {
|
|
1085
|
+
console.error("Unknown uninstall target. Use: codex, claude, gemini, antigravity, opencode, pi, copilot, qwen, or all.");
|
|
598
1086
|
process.exit(1);
|
|
599
1087
|
}
|
|
600
1088
|
if (target === "all" || target === "codex") uninstallCodex();
|
|
601
1089
|
if (target === "all" || target === "claude") uninstallClaude();
|
|
602
1090
|
if (target === "all" || target === "gemini") uninstallGemini();
|
|
603
1091
|
if (target === "all" || target === "antigravity" || target === "agy") uninstallAntigravity();
|
|
1092
|
+
if (target === "all" || target === "opencode") uninstallOpenCode();
|
|
1093
|
+
if (target === "all" || target === "pi") uninstallPi();
|
|
1094
|
+
if (target === "all" || target === "copilot") uninstallCopilot();
|
|
1095
|
+
if (target === "all" || target === "qwen") uninstallQwen();
|
|
604
1096
|
if (hasFlag("--config")) {
|
|
605
1097
|
fs.rmSync(CONFIG_PATH, { force: true });
|
|
606
1098
|
console.log(`Removed ${CONFIG_PATH}`);
|
|
@@ -660,6 +1152,10 @@ async function doctor() {
|
|
|
660
1152
|
claudeHookScript: fs.existsSync(path.join(CONFIG_DIR, "claude", "clisponsor_claude_hook.mjs")),
|
|
661
1153
|
geminiHookScript: fs.existsSync(path.join(CONFIG_DIR, "gemini", "clisponsor_gemini_hook.mjs")),
|
|
662
1154
|
antigravityHookScript: fs.existsSync(path.join(CONFIG_DIR, "antigravity", "clisponsor_antigravity_hook.mjs")),
|
|
1155
|
+
opencodePlugin: fs.existsSync(path.join(openCodeConfigDir(), "plugins", "clisponsor_opencode_plugin.js")),
|
|
1156
|
+
piExtension: fs.existsSync(path.join(HOME, ".pi", "agent", "extensions", "clisponsor_pi_extension.ts")),
|
|
1157
|
+
copilotHook: fs.existsSync(path.join(copilotHome(), "hooks", "clisponsor.json")),
|
|
1158
|
+
qwenHookScript: fs.existsSync(path.join(CONFIG_DIR, "qwen", "clisponsor_qwen_hook.mjs")),
|
|
663
1159
|
},
|
|
664
1160
|
network: {},
|
|
665
1161
|
};
|
|
@@ -687,6 +1183,10 @@ async function doctor() {
|
|
|
687
1183
|
console.log(`Claude hook script: ${diagnostics.installed.claudeHookScript ? "yes" : "no"}`);
|
|
688
1184
|
console.log(`Gemini hook script: ${diagnostics.installed.geminiHookScript ? "yes" : "no"}`);
|
|
689
1185
|
console.log(`Antigravity hook script: ${diagnostics.installed.antigravityHookScript ? "yes" : "no"}`);
|
|
1186
|
+
console.log(`OpenCode plugin: ${diagnostics.installed.opencodePlugin ? "yes" : "no"}`);
|
|
1187
|
+
console.log(`Pi extension: ${diagnostics.installed.piExtension ? "yes" : "no"}`);
|
|
1188
|
+
console.log(`GitHub Copilot CLI hook: ${diagnostics.installed.copilotHook ? "yes" : "no"}`);
|
|
1189
|
+
console.log(`Qwen Code hook script: ${diagnostics.installed.qwenHookScript ? "yes" : "no"}`);
|
|
690
1190
|
if (skipNetwork) {
|
|
691
1191
|
console.log("Network: skipped");
|
|
692
1192
|
} else {
|
|
@@ -697,9 +1197,9 @@ async function doctor() {
|
|
|
697
1197
|
|
|
698
1198
|
function help() {
|
|
699
1199
|
console.log(`clisponsor commands:
|
|
700
|
-
clisponsor install [all|codex|claude|gemini|antigravity]
|
|
1200
|
+
clisponsor install [all|codex|claude|gemini|antigravity|opencode|pi|copilot|qwen]
|
|
701
1201
|
clisponsor login <email> [--label=<device-label>]
|
|
702
|
-
clisponsor uninstall [all|codex|claude|gemini|antigravity] [--config]
|
|
1202
|
+
clisponsor uninstall [all|codex|claude|gemini|antigravity|opencode|pi|copilot|qwen] [--config]
|
|
703
1203
|
clisponsor status
|
|
704
1204
|
clisponsor doctor [--json] [--skip-network]
|
|
705
1205
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clisponsor",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "CLIsponsor installer for Codex, Claude Code, and
|
|
3
|
+
"version": "1.0.15",
|
|
4
|
+
"description": "CLIsponsor installer for Codex, Claude Code, Gemini, Antigravity, OpenCode, Pi, GitHub Copilot CLI, and Qwen Code sponsored CLI placements.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20"
|