@openma/deepseek-harness-acp 0.4.4 → 0.4.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/dist/bridge.js CHANGED
@@ -1482,13 +1482,24 @@ async function apply(ctx, config = {}) {
1482
1482
  mcpCapabilities: { http: true, sse: false },
1483
1483
  ...hasPersistence ? { sessionCapabilities: { list: {} } } : {}
1484
1484
  },
1485
- authMethods: []
1485
+ authMethods: [
1486
+ // ACP Terminal Auth (see the registry's AUTHENTICATION.md):
1487
+ // the client runs `dsh-acp login` in an interactive
1488
+ // terminal; the key lands in the shared harness
1489
+ // credential store (~/.dsh/.credentials.yaml).
1490
+ {
1491
+ id: "terminal-login",
1492
+ name: "Log in with a DeepSeek API key",
1493
+ description: "Interactive terminal setup \u2014 saves the key to the harness credential store shared with the dsh Web UI",
1494
+ _meta: { "terminal-auth": { args: ["login"], env: {} } }
1495
+ }
1496
+ ]
1486
1497
  });
1487
1498
  },
1488
1499
  async authenticate(_params) {
1489
1500
  if (!await credentialPresent()) {
1490
1501
  throw authRequired(
1491
- "no DeepSeek credential found: save one in the dsh Web UI (Settings \u2192 Models) or set DEEPSEEK_API_KEY"
1502
+ "no DeepSeek credential found: run `dsh-acp login`, save one in the dsh Web UI (Settings \u2192 Models), or set DEEPSEEK_API_KEY"
1492
1503
  );
1493
1504
  }
1494
1505
  },
package/dist/index.js CHANGED
@@ -1507,13 +1507,24 @@ async function apply(ctx, config = {}) {
1507
1507
  mcpCapabilities: { http: true, sse: false },
1508
1508
  ...hasPersistence ? { sessionCapabilities: { list: {} } } : {}
1509
1509
  },
1510
- authMethods: []
1510
+ authMethods: [
1511
+ // ACP Terminal Auth (see the registry's AUTHENTICATION.md):
1512
+ // the client runs `dsh-acp login` in an interactive
1513
+ // terminal; the key lands in the shared harness
1514
+ // credential store (~/.dsh/.credentials.yaml).
1515
+ {
1516
+ id: "terminal-login",
1517
+ name: "Log in with a DeepSeek API key",
1518
+ description: "Interactive terminal setup \u2014 saves the key to the harness credential store shared with the dsh Web UI",
1519
+ _meta: { "terminal-auth": { args: ["login"], env: {} } }
1520
+ }
1521
+ ]
1511
1522
  });
1512
1523
  },
1513
1524
  async authenticate(_params) {
1514
1525
  if (!await credentialPresent()) {
1515
1526
  throw authRequired(
1516
- "no DeepSeek credential found: save one in the dsh Web UI (Settings \u2192 Models) or set DEEPSEEK_API_KEY"
1527
+ "no DeepSeek credential found: run `dsh-acp login`, save one in the dsh Web UI (Settings \u2192 Models), or set DEEPSEEK_API_KEY"
1517
1528
  );
1518
1529
  }
1519
1530
  },
@@ -2356,6 +2367,9 @@ async function bootAcpProfile(host, overrides) {
2356
2367
  overlays.push({ id: "session-telemetry-otel", disabled: true });
2357
2368
  }
2358
2369
  const bridgeRow = rows.get("acp-bridge");
2370
+ if (overrides?.serve === false && bridgeRow !== void 0) {
2371
+ overlays.push({ id: "acp-bridge", disabled: true });
2372
+ }
2359
2373
  if (overrides !== void 0 && bridgeRow !== void 0) {
2360
2374
  const patch = {};
2361
2375
  if (overrides.provider !== void 0) patch["provider"] = overrides.provider;
@@ -2392,9 +2406,93 @@ async function bootAcpProfile(host, overrides) {
2392
2406
  return ctx;
2393
2407
  }
2394
2408
 
2409
+ // src/login.ts
2410
+ import { createRequire as createRequire3 } from "node:module";
2411
+ import { join as join3 } from "node:path";
2412
+ import { pathToFileURL as pathToFileURL3 } from "node:url";
2413
+ async function readSecret(promptText) {
2414
+ const stdin = process.stdin;
2415
+ if (!stdin.isTTY) {
2416
+ const chunks = [];
2417
+ for await (const chunk of stdin) {
2418
+ chunks.push(Buffer.from(chunk));
2419
+ const text = Buffer.concat(chunks).toString("utf8");
2420
+ const newline = text.indexOf("\n");
2421
+ if (newline >= 0) return text.slice(0, newline).trim();
2422
+ }
2423
+ return Buffer.concat(chunks).toString("utf8").trim();
2424
+ }
2425
+ process.stderr.write(promptText);
2426
+ return await new Promise((resolve2) => {
2427
+ stdin.setRawMode(true);
2428
+ stdin.resume();
2429
+ stdin.setEncoding("utf8");
2430
+ let buffer = "";
2431
+ const onData = (chunk) => {
2432
+ for (const char of chunk) {
2433
+ if (char === "\n" || char === "\r" || char === "") {
2434
+ stdin.setRawMode(false);
2435
+ stdin.pause();
2436
+ stdin.off("data", onData);
2437
+ process.stderr.write("\n");
2438
+ resolve2(buffer.trim());
2439
+ return;
2440
+ }
2441
+ if (char === "") {
2442
+ stdin.setRawMode(false);
2443
+ process.stderr.write("^C\n");
2444
+ process.exit(130);
2445
+ }
2446
+ if (char === "\x7F" || char === "\b") {
2447
+ buffer = buffer.slice(0, -1);
2448
+ continue;
2449
+ }
2450
+ buffer += char;
2451
+ }
2452
+ };
2453
+ stdin.on("data", onData);
2454
+ });
2455
+ }
2456
+ async function runLogin(ctx, hostBase, argv) {
2457
+ const req = createRequire3(join3(hostBase, "noop.js"));
2458
+ let credentialRef2;
2459
+ try {
2460
+ const mod = await import(pathToFileURL3(req.resolve("@deepseek-ai/dsh-credentials")).href);
2461
+ credentialRef2 = mod.credentialRef;
2462
+ } catch {
2463
+ credentialRef2 = void 0;
2464
+ }
2465
+ const store = ctx.get("credentials");
2466
+ if (store === void 0 || credentialRef2 === void 0) {
2467
+ process.stderr.write(
2468
+ "This composition has no writable credential store; export DEEPSEEK_API_KEY instead.\n"
2469
+ );
2470
+ return 1;
2471
+ }
2472
+ const ref = credentialRef2("DEEPSEEK_API_KEY");
2473
+ const before = await store.describe(ref);
2474
+ if (before.configured && before.source !== void 0) {
2475
+ process.stderr.write(`A key is already configured (source: ${before.source}).
2476
+ `);
2477
+ }
2478
+ const key = argv[0] ?? await readSecret("DeepSeek API key (input hidden): ");
2479
+ if (key.length === 0) {
2480
+ process.stderr.write("No key entered; nothing saved.\n");
2481
+ return 1;
2482
+ }
2483
+ await store.set(ref, key);
2484
+ const masked = key.length <= 8 ? "\u2026" : `${key.slice(0, 4)}\u2026${key.slice(-4)}`;
2485
+ process.stderr.write(
2486
+ `Saved ${masked} to the harness credential store (~/.dsh/.credentials.yaml, mode 600).
2487
+ The dsh Web UI and every dsh surface share this credential.
2488
+ `
2489
+ );
2490
+ return 0;
2491
+ }
2492
+
2395
2493
  // src/settings.ts
2396
2494
  import { homedir } from "node:os";
2397
- import { join as join3 } from "node:path";
2495
+ import { join as join4 } from "node:path";
2398
2496
  var DEFAULT_PERSONA = `You are a coding assistant powered by the {{model}} model, working inside an editor via the Agent Client Protocol. Your working directory is {{cwd}}. Your bash tool runs under a file sandbox \u2014 a \`[sandbox: file access denied \u2026]\` result is policy, not a command bug.
2399
2497
 
2400
2498
  Verify your work by running the code or tests. Keep answers brief and factual.`;
@@ -2487,7 +2585,7 @@ function resolveSettings(argv) {
2487
2585
  models,
2488
2586
  maxTokens,
2489
2587
  permissionMode,
2490
- sessionRoot: stringFlag(parsed, "session-root") ?? envString("DSH_SESSION_ROOT") ?? join3(homedir(), ".dsh-acp", "sessions"),
2588
+ sessionRoot: stringFlag(parsed, "session-root") ?? envString("DSH_SESSION_ROOT") ?? join4(homedir(), ".dsh-acp", "sessions"),
2491
2589
  persona: stringFlag(parsed, "persona") ?? envString("DSH_SYSTEM_PROMPT") ?? DEFAULT_PERSONA,
2492
2590
  thinking: !parsed.flags.has("no-thinking"),
2493
2591
  reasoningEffort: reasoningRaw,
@@ -2495,6 +2593,8 @@ function resolveSettings(argv) {
2495
2593
  };
2496
2594
  }
2497
2595
  var HELP_TEXT = `Usage: dsh-acp [options]
2596
+ dsh-acp login [api-key] Save a DeepSeek API key (interactive when omitted)
2597
+ dsh-acp update Self-update via npm
2498
2598
 
2499
2599
  Agent Client Protocol (ACP) stdio server for DeepSeek Harness. Speak ACP on
2500
2600
  stdin/stdout from an ACP client such as Zed; diagnostics go to stderr.
@@ -2538,6 +2638,32 @@ async function main() {
2538
2638
  console.log(HELP_TEXT.trimEnd());
2539
2639
  return;
2540
2640
  }
2641
+ if (argv[0] === "login") {
2642
+ let booted;
2643
+ let hostBase;
2644
+ try {
2645
+ const host = resolveHost(void 0);
2646
+ hostBase = host.base;
2647
+ booted = await bootAcpProfile(host, { serve: false });
2648
+ } catch (error) {
2649
+ if (error instanceof HarnessNotFoundError) {
2650
+ logError(error.message);
2651
+ process.exitCode = 1;
2652
+ return;
2653
+ }
2654
+ throw error;
2655
+ }
2656
+ try {
2657
+ process.exitCode = await runLogin(
2658
+ booted,
2659
+ hostBase,
2660
+ argv.slice(1)
2661
+ );
2662
+ } finally {
2663
+ await booted.fiber.dispose().catch(() => void 0);
2664
+ }
2665
+ return;
2666
+ }
2541
2667
  if (argv[0] === "update") {
2542
2668
  logInfo(`updating ${NAME} (npm install -g ${NAME}@latest)\u2026`);
2543
2669
  const result = spawnSync("npm", ["install", "-g", `${NAME}@latest`], { stdio: ["ignore", 2, 2] });
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.4",
6
+ "version": "0.4.5",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/openma-ai/deepseek-harness-acp.git"