hunter-harness 0.2.11 → 0.2.12

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 +56 -27
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -1230,6 +1230,9 @@ var HunterHarnessApiClient = class {
1230
1230
  body
1231
1231
  });
1232
1232
  }
1233
+ async getProject(projectId, requestId) {
1234
+ return this.request("GET", "/api/v1/projects/" + encodeURIComponent(projectId), { requestId });
1235
+ }
1233
1236
  async createProposalSession(projectId, body, requestId, idempotencyKey) {
1234
1237
  return this.request("POST", "/api/v1/projects/" + encodeURIComponent(projectId) + "/proposal-sessions", { requestId, idempotencyKey, body });
1235
1238
  }
@@ -3732,6 +3735,10 @@ function assertPreviewAllowed(preview, skip) {
3732
3735
  }
3733
3736
  }
3734
3737
  var CREDENTIALS_HINT = "\u53EF\u5728\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5F55\u5165\uFF0C\u6216\u5199\u5165 .harness/credentials.local.yaml\uFF08\u52FF\u63D0\u4EA4 git\uFF09";
3738
+ var STALE_BASELINE_MESSAGE = "\u670D\u52A1\u7AEF\u5DF2\u6709\u66F4\u65B0\u7684\u63A8\u9001\uFF0C\u8BF7\u5148 git pull \u540E\u6267\u884C npx hunter-harness update \u518D\u63A8";
3739
+ function staleBaselineError(code) {
3740
+ return new PushWorkflowError(STALE_BASELINE_MESSAGE, 5, code);
3741
+ }
3735
3742
  function makePreview(baseline, files, confirmedProjectLocal, ignorePaths, deletedAt = (/* @__PURE__ */ new Date()).toISOString()) {
3736
3743
  const filteredFiles = {};
3737
3744
  for (const [path, content] of Object.entries(files)) {
@@ -3776,14 +3783,12 @@ async function pushProject(options) {
3776
3783
  const profile = parseHarnessProfile(project.project.profiles[0]);
3777
3784
  const installedPaths = profile === null ? /* @__PURE__ */ new Set() : new Set(await Promise.all(enabledHarnessAgents(project).map((agent) => managedBundleTargets(options.resourcesRoot, profile, agent))).then((targets) => targets.flatMap((target) => [...target])));
3778
3785
  let preview = makePreview(baseline, await managedFiles(root, project), options.confirmedProjectLocal ?? [], installedPaths);
3779
- const initialSkip = await resolveSensitiveScanSkip(preview, options);
3780
- if (initialSkip.cancelled === true) {
3781
- return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
3782
- }
3783
- let sensitiveScanSkip = initialSkip.skip;
3784
- let sensitiveScanSkipReason = initialSkip.reason;
3785
- assertPreviewAllowed(preview, sensitiveScanSkip);
3786
3786
  if (options.dryRun) {
3787
+ const drySkip = await resolveSensitiveScanSkip(preview, options);
3788
+ if (drySkip.cancelled === true) {
3789
+ return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
3790
+ }
3791
+ assertPreviewAllowed(preview, drySkip.skip);
3787
3792
  return { preview, proposalId: null, projectId: project.project.project_id };
3788
3793
  }
3789
3794
  const localCredentials = await readLocalCredentials(root);
@@ -3817,6 +3822,25 @@ async function pushProject(options) {
3817
3822
  if (parsedServerUrl.protocol !== "https:") {
3818
3823
  throw new PushWorkflowError("server_url must use HTTPS", 3, "SERVER_URL_INVALID");
3819
3824
  }
3825
+ const client = new HunterHarnessApiClient({
3826
+ serverUrl: parsedServerUrl.toString(),
3827
+ token,
3828
+ ...options.fetch === void 0 ? {} : { fetch: options.fetch }
3829
+ });
3830
+ const boundAtStart = project.project.project_id;
3831
+ if (boundAtStart !== null) {
3832
+ const remote = await client.getProject(boundAtStart, uuidV7());
3833
+ if (remote.latest_project_version !== baseline.complete_project_version) {
3834
+ throw staleBaselineError("PROJECT_VERSION_CONFLICT");
3835
+ }
3836
+ }
3837
+ const initialSkip = await resolveSensitiveScanSkip(preview, options);
3838
+ if (initialSkip.cancelled === true) {
3839
+ return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
3840
+ }
3841
+ let sensitiveScanSkip = initialSkip.skip;
3842
+ let sensitiveScanSkipReason = initialSkip.reason;
3843
+ assertPreviewAllowed(preview, sensitiveScanSkip);
3820
3844
  if (options.confirmProposal !== void 0 && !await options.confirmProposal(preview)) {
3821
3845
  return { preview, proposalId: null, projectId: project.project.project_id, cancelled: true };
3822
3846
  }
@@ -3831,11 +3855,6 @@ async function pushProject(options) {
3831
3855
  await atomicWriteJson(workflowPath, workflow);
3832
3856
  preview = makePreview(baseline, await managedFiles(root, project), options.confirmedProjectLocal ?? [], installedPaths, workflow.created_at);
3833
3857
  const requestId = workflow.request_id;
3834
- const client = new HunterHarnessApiClient({
3835
- serverUrl: parsedServerUrl.toString(),
3836
- token,
3837
- ...options.fetch === void 0 ? {} : { fetch: options.fetch }
3838
- });
3839
3858
  if (project.project.project_id === null) {
3840
3859
  const resolved = await client.resolveProject({
3841
3860
  schema_version: 1,
@@ -3942,8 +3961,8 @@ async function pushProject(options) {
3942
3961
  throw error;
3943
3962
  }
3944
3963
  if (error instanceof ApiError) {
3945
- if (error.code === "STALE_PUSH") {
3946
- throw new PushWorkflowError("\u670D\u52A1\u7AEF\u5DF2\u6709\u66F4\u65B0\u7684\u63A8\u9001\uFF0C\u8BF7\u5148 git pull \u540E\u6267\u884C npx hunter-harness update \u518D\u63A8", 5, "STALE_PUSH");
3964
+ if (error.code === "STALE_PUSH" || error.code === "PROJECT_VERSION_CONFLICT") {
3965
+ throw staleBaselineError(error.code);
3947
3966
  }
3948
3967
  if (error.code === "SENSITIVE_CONTENT_BLOCKED") {
3949
3968
  const details = error.details;
@@ -4314,17 +4333,20 @@ async function updateProject(options) {
4314
4333
  if (project.project.project_id === null) {
4315
4334
  throw new UpdateWorkflowError("project is not bound to a server", 3, "PROJECT_NOT_BOUND");
4316
4335
  }
4317
- const serverUrl = options.serverUrl ?? project.server.url;
4318
4336
  const tokenEnv = options.tokenEnv ?? project.server.token_env;
4319
- if (serverUrl === null || serverUrl === void 0) {
4320
- throw new UpdateWorkflowError("server_url is required", 3, "SERVER_URL_REQUIRED");
4321
- }
4322
4337
  if (!/^[A-Z_][A-Z0-9_]*$/.test(tokenEnv)) {
4323
4338
  throw new UpdateWorkflowError("token_env is invalid", 3, "TOKEN_ENV_INVALID");
4324
4339
  }
4325
- const token = options.env[tokenEnv];
4326
- if (token === void 0 || token.trim() === "") {
4327
- throw new UpdateWorkflowError("API token environment variable is unset", 8, "TOKEN_INVALID");
4340
+ const local = await readLocalCredentials(root);
4341
+ const serverUrl = options.serverUrl ?? local?.server_url ?? project.server.url;
4342
+ if (serverUrl === null || serverUrl === void 0) {
4343
+ throw new UpdateWorkflowError("server_url is required", 3, "SERVER_URL_REQUIRED");
4344
+ }
4345
+ const envToken = options.env[tokenEnv]?.trim();
4346
+ const localToken = local?.token?.trim();
4347
+ const token = envToken !== void 0 && envToken !== "" ? envToken : localToken !== void 0 && localToken !== "" ? localToken : void 0;
4348
+ if (token === void 0) {
4349
+ throw new UpdateWorkflowError(`API token \u672A\u914D\u7F6E\uFF1A\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${tokenEnv}\uFF0C\u6216\u5728 .harness/credentials.local.yaml \u914D\u7F6E token\uFF08\u53EF\u901A\u8FC7 npx hunter-harness push \u5F15\u5BFC\u5199\u5165\uFF09`, 8, "TOKEN_INVALID");
4328
4350
  }
4329
4351
  const requestId = uuidV7();
4330
4352
  const baseline = await readBaseline(root);
@@ -4569,13 +4591,15 @@ function normalizeProfile(value) {
4569
4591
  if (value === "2" || value === "java") return "java";
4570
4592
  throw new InitConfigurationError("\u914D\u7F6E\u7C7B\u578B\u5FC5\u987B\u4E3A general \u6216 java");
4571
4593
  }
4594
+ var ALL_AGENT_TOKENS = /* @__PURE__ */ new Set(["all", "5"]);
4572
4595
  function parseAgentsInput(raw) {
4573
4596
  const trimmed = raw.trim();
4574
4597
  if (trimmed === "") return ["claude-code"];
4575
- if (trimmed === "all") return [...HARNESS_AGENT_ORDER];
4598
+ if (ALL_AGENT_TOKENS.has(trimmed)) return [...HARNESS_AGENT_ORDER];
4576
4599
  const agents = [];
4577
4600
  for (const token of trimmed.split(",")) {
4578
4601
  const value = token.trim();
4602
+ if (ALL_AGENT_TOKENS.has(value)) return [...HARNESS_AGENT_ORDER];
4579
4603
  const byIndex = AGENT_BY_INDEX[value];
4580
4604
  if (byIndex !== void 0) {
4581
4605
  agents.push(byIndex);
@@ -5018,6 +5042,14 @@ var AGENT_LABELS = {
5018
5042
  cursor: "Cursor",
5019
5043
  codebuddy: "CodeBuddy"
5020
5044
  };
5045
+ function agentMenuLines(installedProfiles) {
5046
+ const lines = HARNESS_AGENT_ORDER.map((agent, index) => {
5047
+ const profile = installedProfiles?.[agent];
5048
+ const suffix = profile === void 0 ? "" : `\uFF08\u5DF2\u5B89\u88C5\uFF1A${profile}\uFF09`;
5049
+ return ` ${index + 1}. ${AGENT_LABELS[agent]}${suffix}`;
5050
+ }).join("\n");
5051
+ return lines + "\n 5. \u5168\u90E8";
5052
+ }
5021
5053
  async function configureCodeBuddyExtras(agents, surface, options, dependencies) {
5022
5054
  if (!agents.includes("codebuddy")) return;
5023
5055
  const plan = await inspectCodeBuddySetup(dependencies.cwd);
@@ -5067,7 +5099,7 @@ async function runFirstInstall(options, dependencies) {
5067
5099
  options,
5068
5100
  options.nonInteractive === true ? {} : {
5069
5101
  agents: () => dependencies.prompt(
5070
- "\u8BF7\u9009\u62E9\u76EE\u6807 Agent\uFF08\u53EF\u591A\u9009\uFF0C\u4F7F\u7528\u9017\u53F7\u5206\u9694\uFF09\n 1. Claude Code\n 2. Codex\n 3. Cursor\n 4. CodeBuddy\n\u8BF7\u8F93\u5165\u7F16\u53F7 [1]: "
5102
+ "\u8BF7\u9009\u62E9\u76EE\u6807 Agent\uFF08\u53EF\u591A\u9009\uFF0C\u4F7F\u7528\u9017\u53F7\u5206\u9694\uFF09\n" + agentMenuLines() + "\n\u8BF7\u8F93\u5165\u7F16\u53F7 [1]: "
5071
5103
  ).then((answer) => answer.trim()),
5072
5104
  profile: () => dependencies.prompt(
5073
5105
  "\u8BF7\u9009\u62E9 Harness \u7C7B\u578B\uFF1A\n1. \u901A\u7528\uFF08\u9ED8\u8BA4\uFF09\n2. Java\n\u8BF7\u8F93\u5165 1 \u6216 2 [1]: "
@@ -5165,10 +5197,7 @@ async function runExistingProject(options, dependencies, currentProfile, current
5165
5197
  `Hunter Harness \u5F53\u524D\u914D\u7F6E\uFF1A
5166
5198
  ${currentLines}
5167
5199
  \u8BF7\u9009\u62E9\u672C\u6B21\u8981\u65B0\u589E\u6216\u5237\u65B0\u7684\u5DE5\u5177\uFF08\u53EF\u591A\u9009\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u672A\u9009\u62E9\u7684\u5DE5\u5177\u4FDD\u6301\u4E0D\u53D8\uFF09\uFF1A
5168
- 1. Claude Code
5169
- 2. Codex
5170
- 3. Cursor
5171
- 4. CodeBuddy
5200
+ ` + agentMenuLines(installed.profiles) + `
5172
5201
  \u8BF7\u8F93\u5165\u7F16\u53F7 [${defaultSelection}]\uFF0C\u6216\u8F93\u5165 0 \u53D6\u6D88\uFF1A`
5173
5202
  );
5174
5203
  if (answer.trim() === "0" || /^c/i.test(answer.trim())) return 2;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hunter-harness",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "Local-first, server-governed agent harness",
5
5
  "license": "MIT",
6
6
  "type": "module",