dsh-opencode 0.1.0 → 0.1.1

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
@@ -57,12 +57,12 @@ Refreshes run single-flight per source (Models.dev once for both products) with
57
57
 
58
58
  | Command | Effect |
59
59
  |---|---|
60
- | `/dsh-opencode` | After the key is stored securely, refreshes both catalogs and enables Zen/Go immediately; never accepts key values |
60
+ | `/dsh-opencode [<api-key>]` | Store the API key securely and enable Zen/Go immediately; with no argument, checks status and guides setup |
61
61
  | `/opencode-refresh [all\|zen\|go]` | Force a catalog refresh (joins in-flight fetches) |
62
62
  | `/opencode-status` | Freshness, per-source errors, ready/pending counts, credential presence |
63
63
  | `/opencode-models <zen\|go> [--all]` | Ready models, or every candidate with its state and reason |
64
64
 
65
- None of these commands displays key values or fragments, and none registers a model-visible tool.
65
+ None of these commands displays key values or fragments, and none registers a model-visible tool. `/dsh-opencode` accepts the key only as its single argument and writes it through the DSH credential seam (`recordInput: false` keeps it out of session logs); it never echoes the value back.
66
66
 
67
67
  ## Configuration
68
68
 
package/lib/commands.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { PRODUCT_BY_ROUTE, ROUTE_BY_PRODUCT, describeNonReadyState } from "./normalize.js";
2
+ import { assertUsableApiKey } from "@deepseek-ai/dsh-llm";
2
3
  //#region src/commands.ts
3
4
  const USAGE_REFRESH = "Usage: /opencode-refresh [all|zen|go]";
4
5
  const USAGE_MODELS = "Usage: /opencode-models <zen|go> [--all]";
5
- const USAGE_ENABLE = "Usage: /dsh-opencode — never pass the API key in the command line";
6
6
  /** Whether one date stamp renders as a short local time. */
7
7
  function renderTime(timestamp) {
8
8
  if (timestamp === void 0) return "never";
@@ -37,6 +37,10 @@ function parseProduct(raw) {
37
37
  if (raw === "zen") return "zen";
38
38
  if (raw === "go") return "go";
39
39
  }
40
+ /** Distinct credential references across the configured routes. */
41
+ function configuredRefs(services) {
42
+ return [...new Set([...services.config().providers.values()].map((provider) => provider.apiKeyEnv))];
43
+ }
40
44
  /** Build the three command definitions. */
41
45
  function commandDefinitions(ctx, services) {
42
46
  return [
@@ -130,25 +134,46 @@ function commandDefinitions(ctx, services) {
130
134
  },
131
135
  {
132
136
  name: "dsh-opencode",
133
- description: "Enable OpenCode Zen/Go immediately after the API key is stored securely",
137
+ description: "Store the OpenCode API key and enable Zen/Go immediately",
134
138
  recordInput: false,
135
139
  handler: async (invocation) => {
136
- if (invocation.rawInput.trim().length > 0) return {
137
- kind: "error",
138
- text: USAGE_ENABLE
139
- };
140
+ const raw = invocation.rawInput.trim();
141
+ let stored = false;
142
+ if (raw.length > 0) {
143
+ try {
144
+ assertUsableApiKey(raw, "opencode-live", "input");
145
+ } catch (error) {
146
+ return {
147
+ kind: "error",
148
+ text: error.message
149
+ };
150
+ }
151
+ const failures = [];
152
+ for (const ref of configuredRefs(services)) try {
153
+ await services.storeCredential(ref, raw);
154
+ } catch (error) {
155
+ failures.push(`${ref}: ${error.message}`);
156
+ }
157
+ if (failures.length > 0) return {
158
+ kind: "error",
159
+ text: ["Could not store the OpenCode API key:", ...failures].join("\n")
160
+ };
161
+ stored = true;
162
+ }
140
163
  const routes = [ROUTE_BY_PRODUCT.zen, ROUTE_BY_PRODUCT.go];
141
- if (!(await Promise.all(routes.map(async (route) => {
142
- return (await services.describeCredential(route))?.configured === true;
143
- }))).some((configured) => configured)) return {
144
- kind: "success",
145
- text: [
146
- "No OpenCode API key is configured yet.",
147
- "Store it securely through the standard credentials input (the web Models page writes it),",
148
- "then run /dsh-opencode again to refresh both catalogs and enable the routes immediately.",
149
- "This command never accepts, records, or displays key values."
150
- ].join("\n")
151
- };
164
+ if (!stored) {
165
+ if (!(await Promise.all(routes.map(async (route) => {
166
+ return (await services.describeCredential(route))?.configured === true;
167
+ }))).some((configured) => configured)) return {
168
+ kind: "success",
169
+ text: [
170
+ "No OpenCode API key is configured yet.",
171
+ "Run /dsh-opencode <your-api-key> to store it securely and enable both routes at once,",
172
+ "or store it through the standard credentials input (the web Models page writes it) and run /dsh-opencode again.",
173
+ "This command never displays key values."
174
+ ].join("\n")
175
+ };
176
+ }
152
177
  if (invocation.signal.aborted) return {
153
178
  kind: "success",
154
179
  text: "Refresh cancelled."
@@ -159,7 +184,7 @@ function commandDefinitions(ctx, services) {
159
184
  signal: invocation.signal
160
185
  });
161
186
  } catch {}
162
- const lines = ["OpenCode API key configured. Zen and Go are enabled:"];
187
+ const lines = [stored ? "OpenCode API key stored. Zen and Go are enabled:" : "OpenCode API key configured. Zen and Go are enabled:"];
163
188
  for (const route of routes) lines.push(await productStatus(ctx, services, PRODUCT_BY_ROUTE[route]));
164
189
  return {
165
190
  kind: "success",
package/lib/index.js CHANGED
@@ -154,6 +154,11 @@ function apply(ctx, config = {}) {
154
154
  const provider = currentConfig.providers.get(route);
155
155
  if (provider === void 0) return void 0;
156
156
  return describeCredential(ctx, provider.apiKeyEnv);
157
+ },
158
+ storeCredential: async (ref, value) => {
159
+ const credentials = ctx.get("credentials");
160
+ if (credentials === void 0) throw new Error("opencode-live: no credentials service is mounted; export the API key in the launch environment instead");
161
+ await credentials.set(ref, value);
157
162
  }
158
163
  });
159
164
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-opencode",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Out-of-tree DSH bundle with live dynamic OpenCode Zen / Go model catalogs",
5
5
  "private": false,
6
6
  "license": "MIT",