@amistio/cli 0.1.37 → 0.1.38

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 (3) hide show
  1. package/README.md +4 -2
  2. package/dist/index.js +53 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,9 +9,9 @@ npm install -g @amistio/cli
9
9
  amistio --help
10
10
  ```
11
11
 
12
- The package install provides the `amistio` command and the optional `amistio-host-helper` executable. Repository cloning, project pairing, credential storage, sync watching, helper activation, and runner execution happen only when the user explicitly runs commands such as `amistio bootstrap`, `amistio import`, `amistio pair`, `amistio sync watch`, `amistio host-helper status`, or `amistio run --watch`. When the app copies a personal project into an organization, the CLI command syntax stays the same; create the org-scoped code and run `amistio import <code>` from the intended local checkout. Import scans repo-local project-brain docs plus recognized repo-local IDE, harness, and local AI tool memory or instruction files such as `AGENTS.md`, `.github/copilot-instructions.md`, `.cursor/rules/*.mdc`, `.windsurfrules`, and `memories/*.md`; durable Amistio memory belongs in `docs/memory/`, and global plugin or harness memory outside the repository is not scanned by default.
12
+ The package install provides the `amistio` command and the optional `amistio-host-helper` executable. Repository cloning, project pairing, credential storage, sync watching, helper activation, and runner execution happen only when the user explicitly runs commands such as `amistio bootstrap`, `amistio import`, `amistio pair`, `amistio sync watch`, `amistio host-helper status`, or `amistio run --watch`. When the app copies a personal project into an organization, the CLI command syntax stays the same; create the org-scoped code and run `amistio import <code>` from the intended local checkout. Import scans repo-local project-brain docs plus recognized repo-local IDE and local AI tool memory or instruction files such as `AGENTS.md`, `.github/copilot-instructions.md`, `.cursor/rules/*.mdc`, `.windsurfrules`, and `memories/*.md`; durable Amistio memory belongs in `docs/memory/`, and global plugin memory outside the repository is not scanned by default.
13
13
 
14
- For enterprise setup, use the web Runner panel as the primary guide. It shows repository, pairing code, AI provider, local runner, and approved-work readiness with plain next actions; advanced CLI logs remain secondary diagnostics. The panel also shows trust/privacy boundaries, cost forecast and budget posture, runner health, blockers, verification health, and runner-version distribution from safe runner metadata.
14
+ For enterprise setup, use the web Runner panel as the primary guide. It shows repository, pairing code, GitHub access, AI provider, local runner, and verification readiness with plain next actions; advanced CLI logs remain secondary diagnostics. The panel also shows trust/privacy boundaries, cost forecast and budget posture, runner health, blockers, verification health, and runner-version distribution from safe runner metadata.
15
15
 
16
16
  Optional host helpers are configured outside the SaaS with `AMISTIO_HOST_HELPER_PATH`. The official npm package ships `amistio-host-helper` for Level 1 supervised process execution diagnostics; enable it with `AMISTIO_HOST_HELPER_PATH=$(command -v amistio-host-helper)` on reviewed runner machines and confirm `amistio host-helper status` plus `amistio host-helper conformance`. PTY and sandbox requests use a helper only when its versioned handshake advertises support; otherwise the CLI returns deterministic unsupported results instead of silently downgrading. The npm helper does not advertise PTY or sandbox support. Helper request envelopes include allowlisted environment values, explicit sandbox policy metadata, and bounded normalized output, and must never include provider tokens, OAuth material, runner API tokens, local auth files, or arbitrary SaaS-originated shell text.
17
17
 
@@ -83,6 +83,8 @@ Watch mode prints a completed-work success once per work item, keeps fresh compl
83
83
 
84
84
  Known validation failures such as `unsafe_context_path` are printed with attention-needed next steps. For project-context refresh path-safety failures, deploy the latest web/API fix, update and restart the runner when applicable, retry the refresh, and capture only bounded non-secret output if it repeats.
85
85
 
86
+ If watch mode reports that the runner was forgotten by the server, stop the foreground process and start or pair a new runner from the Runner panel. Forgotten runner IDs are intentionally blocked from heartbeats and work claims, so the CLI exits instead of retrying with the tombstoned identity.
87
+
86
88
  App-evaluation result finalization rejections print safe validation paths and preserve the local finalization evidence without exposing raw source or secrets. If a structured app-evaluation result is rejected, update and restart the runner, confirm the web/API deployment is current, and retry the evaluation before acting on cleanup or implementation recommendations.
87
89
 
88
90
  When brain generation or plan revision output is parsed but the Amistio API is temporarily unavailable during finalization, the runner keeps a safe pending result envelope in user-level Amistio config and replays it before claiming more work. The envelope uses a stable idempotency key and does not store raw tool stdout, provider sessions, credentials, or arbitrary local paths.
package/dist/index.js CHANGED
@@ -2730,6 +2730,9 @@ var AmistioApiError = class extends Error {
2730
2730
  statusText;
2731
2731
  detail;
2732
2732
  };
2733
+ function isForgottenRunnerApiError(error) {
2734
+ return error instanceof AmistioApiError && error.status === 403 && error.detail.includes("This runner was forgotten");
2735
+ }
2733
2736
  function isRetryableApiError(error) {
2734
2737
  if (error instanceof AmistioApiError) {
2735
2738
  return error.status === 408 || error.status === 429 || error.status >= 500;
@@ -5491,6 +5494,41 @@ async function readRunnerDaemonMetadataFile(filePath) {
5491
5494
  }
5492
5495
  }
5493
5496
 
5497
+ // src/runner-watch-errors.ts
5498
+ var forgottenRunnerWatchMessage = "This runner was forgotten by the server. Start or pair a new runner from the Runner panel; this runner ID cannot heartbeat or claim work anymore.";
5499
+ async function handleRunnerWatchError(options) {
5500
+ const consoleError = options.consoleError ?? console.error;
5501
+ if (isForgottenRunnerApiError(options.error)) {
5502
+ if (options.verbose && options.detail) {
5503
+ consoleError(`${forgottenRunnerWatchMessage}
5504
+ ${options.detail}`);
5505
+ } else {
5506
+ consoleError(forgottenRunnerWatchMessage);
5507
+ }
5508
+ return { status: "failed", exitCode: 1, message: forgottenRunnerWatchMessage, stopRunner: true };
5509
+ }
5510
+ const message = "Runner hit an error while watching and will keep listening.";
5511
+ if (options.verbose) {
5512
+ consoleError(`${message}
5513
+ ${options.detail}`);
5514
+ } else {
5515
+ consoleError(`${message} Run with --verbose for details.`);
5516
+ }
5517
+ const settlements = await Promise.allSettled([
5518
+ options.client.sendRunnerHeartbeat(options.projectId, options.runnerId, options.repositoryLinkId, "blocked", { ...options.heartbeatMetadata, preferenceMessage: message }),
5519
+ options.client.recordRunnerLog(options.projectId, {
5520
+ runnerId: options.runnerId,
5521
+ repositoryLinkId: options.repositoryLinkId,
5522
+ status: "failed",
5523
+ message,
5524
+ error: options.detail,
5525
+ machineId: options.machineId
5526
+ })
5527
+ ]);
5528
+ options.logRejectedSettlements("record watch error", settlements);
5529
+ return { status: "failed", exitCode: 1, message };
5530
+ }
5531
+
5494
5532
  // src/runner-service.ts
5495
5533
  import { spawn as spawn3 } from "node:child_process";
5496
5534
  import { createHash as createHash4 } from "node:crypto";
@@ -10225,6 +10263,9 @@ program.command("run").description("Claim and run approved Amistio work locally"
10225
10263
  }
10226
10264
  }
10227
10265
  if (result.stopRunner) {
10266
+ if (result.exitCode !== 0) {
10267
+ process.exitCode = result.exitCode;
10268
+ }
10228
10269
  return;
10229
10270
  }
10230
10271
  if (options.maxIterations !== void 0 && iterations >= options.maxIterations) {
@@ -10491,26 +10532,18 @@ async function runWatchIteration({ command, context, options, runnerId }) {
10491
10532
  throw error;
10492
10533
  }
10493
10534
  const detail = truncateLogExcerpt(errorDetail(error));
10494
- const message = "Runner hit an error while watching and will keep listening.";
10495
- if (options.verbose) {
10496
- console.error(`${message}
10497
- ${detail}`);
10498
- } else {
10499
- console.error(`${message} Run with --verbose for details.`);
10500
- }
10501
- const settlements = await Promise.allSettled([
10502
- context.client.sendRunnerHeartbeat(context.metadata.amistioProjectId, runnerId, context.metadata.repositoryLinkId, "blocked", { ...runnerHeartbeatMetadata(), preferenceMessage: message }),
10503
- context.client.recordRunnerLog(context.metadata.amistioProjectId, {
10504
- runnerId,
10505
- repositoryLinkId: context.metadata.repositoryLinkId,
10506
- status: "failed",
10507
- message,
10508
- error: detail,
10509
- machineId: runnerMachineId()
10510
- })
10511
- ]);
10512
- logRejectedSettlements("record watch error", settlements);
10513
- return { status: "failed", exitCode: 1, message };
10535
+ return await handleRunnerWatchError({
10536
+ client: context.client,
10537
+ detail,
10538
+ error,
10539
+ heartbeatMetadata: runnerHeartbeatMetadata(),
10540
+ logRejectedSettlements,
10541
+ machineId: runnerMachineId(),
10542
+ projectId: context.metadata.amistioProjectId,
10543
+ repositoryLinkId: context.metadata.repositoryLinkId,
10544
+ runnerId,
10545
+ ...options.verbose !== void 0 ? { verbose: options.verbose } : {}
10546
+ });
10514
10547
  }
10515
10548
  }
10516
10549
  function claimLaneIds(maxConcurrentWork) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amistio/cli",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",