hunter-harness 0.2.7 → 0.2.8

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.
Files changed (2) hide show
  1. package/dist/bin.js +63 -9
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -4,6 +4,7 @@
4
4
  import { realpathSync } from "node:fs";
5
5
  import { pathToFileURL } from "node:url";
6
6
  import { createInterface } from "node:readline/promises";
7
+ import { Writable } from "node:stream";
7
8
  import { Command, CommanderError } from "commander";
8
9
 
9
10
  // ../contracts/dist/ai-config.js
@@ -3379,6 +3380,17 @@ async function ensureCredentialsGitignore(projectRoot) {
3379
3380
  }
3380
3381
  }
3381
3382
  const existing = new Set(content.split("\n").map((line) => line.trim()));
3383
+ const harnessDirectoryIgnored = [
3384
+ ".harness",
3385
+ ".harness/",
3386
+ "/.harness",
3387
+ "/.harness/",
3388
+ ".harness/**",
3389
+ "/.harness/**"
3390
+ ].some((line) => existing.has(line));
3391
+ if (harnessDirectoryIgnored) {
3392
+ return;
3393
+ }
3382
3394
  const missing = CREDENTIALS_GITIGNORE_LINES.filter((line) => !existing.has(line));
3383
3395
  if (missing.length === 0) {
3384
3396
  return;
@@ -3392,11 +3404,21 @@ function resolvePushAuth(input) {
3392
3404
  const localToken = input.local?.token?.trim();
3393
3405
  const token = envToken && envToken.length > 0 ? envToken : localToken && localToken.length > 0 ? localToken : void 0;
3394
3406
  const serverUrl = input.serverUrlFlag ?? input.local?.server_url ?? input.projectUrl ?? void 0;
3407
+ const missing = [];
3395
3408
  if (serverUrl === void 0 || serverUrl === null || serverUrl.trim() === "") {
3396
- return { code: "SERVER_URL_REQUIRED" };
3409
+ missing.push("url");
3397
3410
  }
3398
3411
  if (token === void 0) {
3399
- return { code: "TOKEN_INVALID" };
3412
+ missing.push("token");
3413
+ }
3414
+ if (missing.length > 0) {
3415
+ return {
3416
+ code: missing.includes("url") ? "SERVER_URL_REQUIRED" : "TOKEN_INVALID",
3417
+ missing
3418
+ };
3419
+ }
3420
+ if (serverUrl === void 0 || token === void 0) {
3421
+ throw new Error("push auth resolution invariant failed");
3400
3422
  }
3401
3423
  return { serverUrl, token };
3402
3424
  }
@@ -3734,9 +3756,6 @@ async function pushProject(options) {
3734
3756
  if (options.dryRun) {
3735
3757
  return { preview, proposalId: null, projectId: project.project.project_id };
3736
3758
  }
3737
- if (options.confirmProposal !== void 0 && !await options.confirmProposal(preview)) {
3738
- return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
3739
- }
3740
3759
  const localCredentials = await readLocalCredentials(root);
3741
3760
  const auth = resolvePushAuth({
3742
3761
  ...options.serverUrl === void 0 ? {} : { serverUrlFlag: options.serverUrl },
@@ -3748,10 +3767,10 @@ async function pushProject(options) {
3748
3767
  });
3749
3768
  if ("code" in auth) {
3750
3769
  if (auth.code === "SERVER_URL_REQUIRED") {
3751
- throw new PushWorkflowError("server_url is required; use --server-url, project.yaml server.url, or " + CREDENTIALS_HINT, 3, "SERVER_URL_REQUIRED");
3770
+ throw new PushWorkflowError("server_url is required; use --server-url, project.yaml server.url, or " + CREDENTIALS_HINT, 3, "SERVER_URL_REQUIRED", { missing_credentials: auth.missing });
3752
3771
  }
3753
3772
  const tokenEnv2 = options.tokenEnv ?? project.server.token_env;
3754
- throw new PushWorkflowError("API token is unset; set " + tokenEnv2 + " or " + CREDENTIALS_HINT, 8, "TOKEN_INVALID");
3773
+ throw new PushWorkflowError("API token is unset; set " + tokenEnv2 + " or " + CREDENTIALS_HINT, 8, "TOKEN_INVALID", { missing_credentials: auth.missing });
3755
3774
  }
3756
3775
  const serverUrl = auth.serverUrl;
3757
3776
  const token = auth.token;
@@ -3768,6 +3787,9 @@ async function pushProject(options) {
3768
3787
  if (parsedServerUrl.protocol !== "https:") {
3769
3788
  throw new PushWorkflowError("server_url must use HTTPS", 3, "SERVER_URL_INVALID");
3770
3789
  }
3790
+ if (options.confirmProposal !== void 0 && !await options.confirmProposal(preview)) {
3791
+ return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
3792
+ }
3771
3793
  const workflowPath = join10(root, ".harness", "state", "local", "push-workflow.json");
3772
3794
  const priorWorkflow = await readOptionalJson(workflowPath);
3773
3795
  const provisionalRequestId = priorWorkflow?.local_project_key === project.project.local_project_key ? priorWorkflow.request_id : uuidV7();
@@ -5089,7 +5111,9 @@ function errorPayload(error) {
5089
5111
  }
5090
5112
  async function promptForCredentials(dependencies, missing) {
5091
5113
  const serverUrl = missing === "token" ? void 0 : (await dependencies.prompt("\u670D\u52A1\u7AEF URL (https://...): ")).trim();
5092
- const token = missing === "url" ? void 0 : (await dependencies.prompt("API Token: ")).trim();
5114
+ const token = missing === "url" ? void 0 : (await (dependencies.promptSecret ?? dependencies.prompt)(
5115
+ "API Token\uFF08\u8F93\u5165\u5C06\u9690\u85CF\uFF09: "
5116
+ )).trim();
5093
5117
  if ((missing === "url" || missing === "both") && (serverUrl === void 0 || serverUrl === "")) {
5094
5118
  return null;
5095
5119
  }
@@ -5198,7 +5222,8 @@ async function runPush(options, dependencies) {
5198
5222
  return 0;
5199
5223
  } catch (error) {
5200
5224
  if (attempt === 0 && options.nonInteractive !== true && options.dryRun !== true && error instanceof PushWorkflowError && (error.code === "SERVER_URL_REQUIRED" || error.code === "TOKEN_INVALID")) {
5201
- const missing = error.code === "SERVER_URL_REQUIRED" ? "url" : error.code === "TOKEN_INVALID" ? "token" : "both";
5225
+ const missingCredentials = error.details?.missing_credentials;
5226
+ const missing = missingCredentials?.includes("url") === true && missingCredentials.includes("token") ? "both" : missingCredentials?.includes("url") === true || error.code === "SERVER_URL_REQUIRED" ? "url" : "token";
5202
5227
  dependencies.stderr(
5203
5228
  error.message + "\n\u53EF\u5728\u4E0B\u65B9\u5F55\u5165\u5E76\u5199\u5165 .harness/credentials.local.yaml\u3002\n"
5204
5229
  );
@@ -5668,6 +5693,33 @@ async function readWorkflowFamilyManifest(resourcesRoot) {
5668
5693
  }
5669
5694
 
5670
5695
  // src/bin.ts
5696
+ async function promptSecret(question, input = process.stdin, output = process.stdout) {
5697
+ if (input.isTTY !== true || output.isTTY !== true) {
5698
+ const terminal2 = createInterface({ input, output });
5699
+ try {
5700
+ return await terminal2.question(question);
5701
+ } finally {
5702
+ terminal2.close();
5703
+ }
5704
+ }
5705
+ output.write(question);
5706
+ const mutedOutput = new Writable({
5707
+ write(_chunk, _encoding, callback) {
5708
+ callback();
5709
+ }
5710
+ });
5711
+ const terminal = createInterface({
5712
+ input,
5713
+ output: mutedOutput,
5714
+ terminal: true
5715
+ });
5716
+ try {
5717
+ return await terminal.question("");
5718
+ } finally {
5719
+ terminal.close();
5720
+ output.write("\n");
5721
+ }
5722
+ }
5671
5723
  function defaultDependencies(overrides) {
5672
5724
  return {
5673
5725
  cwd: overrides.cwd ?? process.cwd(),
@@ -5682,6 +5734,7 @@ function defaultDependencies(overrides) {
5682
5734
  terminal.close();
5683
5735
  }
5684
5736
  }),
5737
+ promptSecret: overrides.promptSecret ?? overrides.prompt ?? promptSecret,
5685
5738
  fetch: overrides.fetch ?? globalThis.fetch,
5686
5739
  env: overrides.env ?? process.env
5687
5740
  };
@@ -5765,5 +5818,6 @@ if (isDirectCliEntrypoint()) {
5765
5818
  }
5766
5819
  export {
5767
5820
  isDirectCliEntrypoint,
5821
+ promptSecret,
5768
5822
  runCli
5769
5823
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hunter-harness",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Local-first, server-governed agent harness",
5
5
  "license": "MIT",
6
6
  "type": "module",