@jeffreyjyz/opencode-command-code 0.2.1 → 0.2.3

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/dist/index.js CHANGED
@@ -548,6 +548,8 @@ async function loadModels(key) {
548
548
  import { Plugin } from "@opencode/plugin";
549
549
  var PLUGIN_ID = "command-code";
550
550
  var PROVIDER_BASE = "https://api.commandcode.ai/provider/v1";
551
+ var INTEGRATION_ID = "command-code";
552
+ var INTEGRATION_NAME = "Command Code";
551
553
  var LANES = [
552
554
  { id: "command-code-anthropic", name: "Command Code (Anthropic)", pkg: "@opencode/ai/providers/anthropic" },
553
555
  {
@@ -586,16 +588,52 @@ function staticSeedModels() {
586
588
  }
587
589
  return seed;
588
590
  }
591
+ async function credentialKey(ctx) {
592
+ try {
593
+ const connection = await ctx.integration.connection.active(INTEGRATION_ID);
594
+ if (connection) {
595
+ const credential = await ctx.integration.connection.resolve(connection);
596
+ if (credential?.type === "key")
597
+ return credential.key;
598
+ if (credential?.type === "oauth")
599
+ return credential.access;
600
+ }
601
+ } catch {}
602
+ try {
603
+ return await resolveKey();
604
+ } catch {
605
+ return;
606
+ }
607
+ }
589
608
  var commandCodeV2 = Plugin.define({
590
609
  id: PLUGIN_ID,
591
610
  async setup(ctx) {
592
- let key;
611
+ let localKey;
612
+ try {
613
+ localKey = await resolveKey();
614
+ } catch {}
615
+ let hasConnection = false;
593
616
  try {
594
- key = await resolveKey();
617
+ hasConnection = await ctx.integration.connection.active(INTEGRATION_ID) !== undefined;
595
618
  } catch {}
596
- if (!key) {
597
- console.warn("[command-code] no API key found \u2014 providers registered disabled. `cmd login` (~/.commandcode/auth.json), CMD_API_KEY, or providers.command-code-*.settings.apiKey in opencode.jsonc, then restart.");
619
+ if (!localKey && !hasConnection) {
620
+ console.warn(`[command-code] no API key found. Run /connect and choose "${INTEGRATION_NAME}", or \`cmd login\` (~/.commandcode/auth.json), or set CMD_API_KEY.`);
598
621
  }
622
+ await ctx.integration.transform((editor) => {
623
+ editor.update(INTEGRATION_ID, (integration) => {
624
+ if (integration.name === integration.id) {
625
+ integration.name = INTEGRATION_NAME;
626
+ }
627
+ });
628
+ editor.method.update({
629
+ integrationID: INTEGRATION_ID,
630
+ method: { type: "env", names: ["CMD_API_KEY"] }
631
+ });
632
+ editor.method.update({
633
+ integrationID: INTEGRATION_ID,
634
+ method: { type: "key", label: "Command Code API key" }
635
+ });
636
+ });
599
637
  const seed = staticSeedModels();
600
638
  await ctx.provider.transform((editor) => {
601
639
  for (const lane of LANES) {
@@ -604,10 +642,12 @@ var commandCodeV2 = Plugin.define({
604
642
  provider.name = lane.name;
605
643
  if (provider.package === "")
606
644
  provider.package = lane.pkg;
607
- provider.activation = key ? "enabled" : "disabled";
645
+ provider.activation = hasConnection || localKey ? "enabled" : "auto";
608
646
  provider.settings = { ...provider.settings, baseURL: PROVIDER_BASE };
609
- if (key && provider.settings.apiKey === undefined)
610
- provider.settings.apiKey = key;
647
+ if (!hasConnection && localKey && provider.settings.apiKey === undefined) {
648
+ provider.settings.apiKey = localKey;
649
+ }
650
+ provider.integrationID ??= INTEGRATION_ID;
611
651
  });
612
652
  editor.models.set(lane.id, seed[lane.id]);
613
653
  }
@@ -628,17 +668,23 @@ var commandCodeV2 = Plugin.define({
628
668
  },
629
669
  async execute(input) {
630
670
  const arg = typeof input?.arg === "string" ? input.arg : "";
671
+ const key = await credentialKey(ctx);
631
672
  return {
632
673
  content: await runCmduse(arg, { env: key ? { CMD_API_KEY: key } : undefined })
633
674
  };
634
675
  }
635
676
  });
636
677
  });
637
- if (!key)
638
- return;
639
678
  const controller = new AbortController;
640
- (async () => {
679
+ let refreshing = false;
680
+ const refresh = async () => {
681
+ if (refreshing || controller.signal.aborted)
682
+ return;
683
+ refreshing = true;
641
684
  try {
685
+ const key = await credentialKey(ctx);
686
+ if (!key)
687
+ return;
642
688
  const split = await loadModels(key);
643
689
  if (controller.signal.aborted)
644
690
  return;
@@ -647,10 +693,17 @@ var commandCodeV2 = Plugin.define({
647
693
  editor.models.set("command-code-openai", split.open.map((m) => toV2Model(m, LANES[1])));
648
694
  });
649
695
  } catch (e) {
650
- console.warn("[command-code] live model list unavailable, keeping static seed:", e);
696
+ console.warn("[command-code] live model list unavailable, keeping previous list:", e);
697
+ } finally {
698
+ refreshing = false;
651
699
  }
652
- })();
653
- return () => controller.abort();
700
+ };
701
+ refresh();
702
+ const timer = setInterval(() => void refresh(), 30 * 60 * 1000);
703
+ return () => {
704
+ controller.abort();
705
+ clearInterval(timer);
706
+ };
654
707
  }
655
708
  });
656
709
 
package/dist/v2.d.ts CHANGED
@@ -2,6 +2,9 @@ import { Plugin } from "@opencode/plugin";
2
2
  import { type CmdModel } from "./models";
3
3
  export declare const PLUGIN_ID = "command-code";
4
4
  export declare const PROVIDER_BASE = "https://api.commandcode.ai/provider/v1";
5
+ /** Shared integration backing both lanes' credentials (/connect + env). */
6
+ export declare const INTEGRATION_ID = "command-code";
7
+ export declare const INTEGRATION_NAME = "Command Code";
5
8
  type Lane = {
6
9
  id: "command-code-anthropic" | "command-code-openai";
7
10
  name: string;
@@ -20,4 +23,18 @@ export declare function toV2Model(m: CmdModel, lane: Lane): unknown;
20
23
  * Replaced by the gated live list once the background fetch lands; also the
21
24
  * fallback shown when the live fetch fails. */
22
25
  export declare function staticSeedModels(): Record<Lane["id"], unknown[]>;
26
+ /** The slice of the plugin context the credential resolver needs. */
27
+ type CredentialContext = {
28
+ integration: {
29
+ connection: {
30
+ active(integrationID: string): Promise<unknown>;
31
+ resolve(connection: unknown): Promise<unknown>;
32
+ };
33
+ };
34
+ };
35
+ /** Credential for spawning cmduse, host-first: a key stored by /connect lives
36
+ * only in opencode's credential store, so ask the connection before falling
37
+ * back to CMD_API_KEY / ~/.commandcode/auth.json. Resolved per call — a
38
+ * /connect mid-session must be picked up. */
39
+ export declare function credentialKey(ctx: CredentialContext): Promise<string | undefined>;
23
40
  export declare const commandCodeV2: Plugin.Plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffreyjyz/opencode-command-code",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Command Code provider + usage dashboard for opencode: two providers (Anthropic wire for Claude, OpenAI wire for everything else) against api.commandcode.ai, live model list, plan gating, /cmd-usage. Requires the cmduse CLI for usage rendering.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",