@amistio/cli 0.1.38 → 0.1.40
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 +11 -5
- package/dist/index.js +315 -183
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ 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 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.
|
|
12
|
+
The package install provides the `amistio` command and the optional `amistio-host-helper` executable. Repository cloning, project pairing, credential storage, sync watching, harness diagnostics, 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 harness status`, `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
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
|
|
|
@@ -38,6 +38,9 @@ Repository autopilot is disabled until the repository link option is enabled in
|
|
|
38
38
|
After pairing, confirm that at least one local AI tool is available:
|
|
39
39
|
|
|
40
40
|
```sh
|
|
41
|
+
amistio harness status
|
|
42
|
+
amistio harness providers
|
|
43
|
+
amistio harness tools
|
|
41
44
|
amistio tools
|
|
42
45
|
amistio host-helper status
|
|
43
46
|
amistio host-helper conformance
|
|
@@ -54,26 +57,29 @@ amistio run --watch --background --tool opencode
|
|
|
54
57
|
AMISTIO_HOST_HELPER_PATH=$(command -v amistio-host-helper) amistio host-helper status
|
|
55
58
|
AMISTIO_HOST_HELPER_PATH=$(command -v amistio-host-helper) amistio host-helper conformance
|
|
56
59
|
amistio runner status
|
|
60
|
+
amistio runner repair
|
|
57
61
|
amistio runner smoke-session-lifecycle
|
|
58
62
|
```
|
|
59
63
|
|
|
60
64
|
Provider-backed model preferences use sanitized catalog fields: `--provider`, `--model-id`, optional `--model-variant`, and `--reasoning-effort` (`auto`, `low`, `medium`, `high`, or `xhigh`). Opencode catalog metadata is synthesized by Amistio or derived from readable local OpenCode JSON config until opencode exposes a native catalog. Provider credentials, API keys, and local secret paths stay in the local tool configuration; they are not stored in Amistio preferences or runner heartbeats.
|
|
61
65
|
|
|
66
|
+
`amistio harness status`, `amistio harness providers`, and `amistio harness tools` are local diagnostics for the built-in Amistio harness. They report the default harness boundary, safe direct-provider readiness, and bounded Amistio-owned tool adapters without uploading provider credentials, environment values, local config paths, or raw tool errors. The web Runner panel remains the primary setup flow.
|
|
67
|
+
|
|
62
68
|
Opencode remains an optional compatibility route. When a provider/model is named for opencode, Amistio validates it against the safe provider catalog before launching the external tool instead of silently falling back. Command-mode local tool runs are bounded by `--tool-timeout-seconds`, wait for process cleanup after timeout, and return redacted/capped stdout and stderr so large external output, local execution-root paths, and secret-looking assignments are not persisted unbounded.
|
|
63
69
|
|
|
64
70
|
When `--tool copilot` uses the GitHub Copilot SDK, Amistio approves read-only permission requests by default and denies mutating, network, MCP, hook, memory, and shell requests. Set `AMISTIO_COPILOT_APPROVE_ALL=1` only on a local machine where broad Copilot SDK approval is intentional.
|
|
65
71
|
|
|
66
72
|
When `--tool codex` uses the Codex SDK, intermediate progress can be quiet until the final response. For live Codex CLI logs, run `amistio run --watch --tool codex --invocation-channel command`.
|
|
67
73
|
|
|
68
|
-
`amistio runner status` reports local background runner state, latest heartbeat, and bounded resource usage when available. Resource usage is latest-sample runner process memory/CPU plus safe aggregate system memory/load signals; it does not include source files, environment variables, command lines, process lists, credentials, or arbitrary local paths.
|
|
74
|
+
`amistio runner status` reports local background runner state, latest heartbeat, and bounded resource usage when available. `amistio runner repair` rotates the local runner ID for the paired checkout after a forgotten-runner tombstone; use `amistio runner repair --clear-credential` only when you need to delete the local credential and re-pair from the Runner panel. Resource usage is latest-sample runner process memory/CPU plus safe aggregate system memory/load signals; it does not include source files, environment variables, command lines, process lists, credentials, or arbitrary local paths.
|
|
69
75
|
|
|
70
76
|
`amistio runner smoke-session-lifecycle` runs a local no-claim smoke for the runner tool-session lifecycle. It does not contact the API, claim production work, inspect source, or mutate local runner state; it verifies that completed one-shot sessions close, active sessions are treated as in use, stale sessions are not selected for reuse, and fresh related reusable sessions can still continue.
|
|
71
77
|
|
|
72
78
|
The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only `projectContextRefresh` jobs from the workspace Context panel and create due runner-driven refreshes when no fresh approved map exists. Context refreshes inspect the paired checkout locally without modifying files and submit only bounded summaries, slices, entities, relations, safe citations, confidence, freshness, and repo-relative paths. If a submitted context refresh contains unsafe evidence, unsafe paths, or a map too large to store safely, Amistio marks the refresh failed with a safe reason instead of storing the rejected raw result. Approved maps are reused as context packs for source-aware assistant and impact-preview work. Current runners can also claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can claim manual read-only `appEvaluationScan` jobs from the workspace Evaluate panel and create at most one due hourly evaluation during normal watch/background polling when app evaluation is enabled for the repository link. Evaluation results contain bounded summaries, safe evidence, suggested actions, lifecycle proposals, and repo-relative paths only. Current runners can also claim manual read-only `securityPostureScan` jobs from the workspace Security panel and create due daily posture checks during normal watch/background polling. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only. Current runners can claim manual read-only `testQualityScan` jobs from the workspace Test panel and create one due daily Test scan per repository when Test quality is enabled. Test scans run only existing lint, typecheck, test, coverage, build, or verify commands and submit bounded command summaries, coverage summaries, safe findings, blocked reasons, warnings, and repo-relative paths. Missing tests, missing coverage, low coverage, failing checks, flaky tests, and test gaps create reviewable plan-backed findings in the app. Current runners also claim `implementationTestGate` jobs before implementation completion, PR handoff, or runner-managed push; a passing gate is required unless the web Test panel records an audited override. Blocked implementation Test gates submit structured Test findings, such as `blockedEnvironment`, with safe evidence, a suggested action, and a verification plan. Current runners can claim read-only `implementationVerification` jobs from Tasks to prove whether completed implementation work actually landed; verification submits bounded acceptance-criteria evidence, checks, gaps, outcome, and recommendation without mutating source. Source, secrets, environment variables, command lines, process lists, credentials, provider sessions, and arbitrary local paths stay local. Implementation or cleanup is queued separately only after the user approves an issue analysis, app evaluation finding, security remediation plan, or Test quality plan in the app.
|
|
73
79
|
|
|
74
|
-
Approved implementation work uses Git as the handoff boundary. During worktree preflight, the runner locally copies eligible ignored root dotenv files such as `.env.local` or `.env.test.local` from the paired checkout into the implementation worktree when the target is missing and ignored, so local tests can use the same machine configuration. Dotenv values, variable names, file contents, and local paths are not uploaded to Amistio, and copied dotenv files stay ignored so PR handoff does not commit them. After the local tool completes successfully, the runner materializes approved Markdown, MDX, and HTML project-brain artifacts for the same work scope into the isolated worktree before final Git status. It then commits all source and artifact changes, fetches and rebases from the linked remote's default branch, pushes an `amistio/work/...` branch, opens or reuses a pull request with the locally authenticated `gh` CLI, reports only safe PR and artifact-inclusion metadata to Amistio, and removes the local worktree after the PR URL is durable. Artifact-only materialization changes still create or reuse a PR; no-change completion requires no source changes and no approved artifact changes, and runner-created no-change worktrees are removed after final clean checks. Prepare the runner machine with Git commit identity, fetch/push permission to the linked remote, and `gh auth status`. If artifact materialization, commit, fetch/rebase, push, or PR creation fails, the work item is blocked with safe recovery choices; source files and patches are not uploaded to Amistio. The Work panel can queue scoped Retry handoff or Retry cleanup commands to the
|
|
80
|
+
Approved implementation work uses Git as the handoff boundary. During worktree preflight, the runner locally copies eligible ignored root dotenv files such as `.env.local` or `.env.test.local` from the paired checkout into the implementation worktree when the target is missing and ignored, so local tests can use the same machine configuration. Dotenv values, variable names, file contents, and local paths are not uploaded to Amistio, and copied dotenv files stay ignored so PR handoff does not commit them. After the local tool completes successfully, the runner materializes approved Markdown, MDX, and HTML project-brain artifacts for the same work scope into the isolated worktree before final Git status. It then commits all source and artifact changes, fetches and rebases from the linked remote's default branch, pushes an `amistio/work/...` branch, opens or reuses a pull request with the locally authenticated `gh` CLI, reports only safe PR and artifact-inclusion metadata to Amistio, and removes the local worktree after the PR URL is durable. Artifact-only materialization changes still create or reuse a PR; no-change completion requires no source changes and no approved artifact changes, and runner-created no-change worktrees are removed after final clean checks. Prepare the runner machine with Git commit identity, fetch/push permission to the linked remote, and `gh auth status`. If artifact materialization, commit, fetch/rebase, push, or PR creation fails, the work item is blocked with safe recovery choices; source files and patches are not uploaded to Amistio. The Work panel can queue scoped Retry handoff or Retry cleanup commands only to the runner that owns the preserved worktree for the same work item, branch, and worktree key. Rebase conflicts capture bounded repo-relative conflict files and try `git rebase --abort` so the implementation branch can be retried or manually reviewed without leaving an active rebase. Dirty, unmerged, or ambiguous worktrees are preserved rather than discarded.
|
|
75
81
|
|
|
76
|
-
Failed or stale work can be requeued from the web Tasks panel. Requeue creates a new linked work attempt and preserves the original terminal attempt for audit history;
|
|
82
|
+
Failed or stale work can be requeued from the web Tasks panel. Requeue creates a new linked work attempt and preserves the original terminal attempt for audit history; Requeue all sends one backend batch that recomputes safe candidates, reports already-active and skipped rows, and still uses linked attempts. Requeue is blocked while equivalent work is already active or when the paired runner does not advertise the needed work kind. Completed implementation status is separate from proof: queue `implementationVerification` from Tasks when a plan needs source-aware evidence before cleanup or implementation status decisions.
|
|
77
83
|
|
|
78
84
|
Runner setup and local-tool execution use bounded failure controls. `amistio run --watch` retries Git worktree preflight failures by releasing the claim for another attempt, then fails the work item after `--max-preflight-attempts` attempts, defaulting to 3. Active local-tool runs renew the work lease, and `--tool-timeout-seconds` caps tool execution, defaulting to 1800 seconds.
|
|
79
85
|
|
|
@@ -83,7 +89,7 @@ Watch mode prints a completed-work success once per work item, keeps fresh compl
|
|
|
83
89
|
|
|
84
90
|
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
91
|
|
|
86
|
-
If watch mode reports that the runner was forgotten by the server,
|
|
92
|
+
If watch mode reports that the runner was forgotten by the server, run `amistio runner repair` from the paired checkout, then start `amistio run --watch` again. The repair command stores a fresh local runner ID because the default ID for a machine/project/repository is stable and can remain tombstoned. Use `--clear-credential` only when the Runner panel tells you to create a fresh pairing code.
|
|
87
93
|
|
|
88
94
|
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.
|
|
89
95
|
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { createHash as createHash9, randomUUID as
|
|
5
|
-
import { writeFile as
|
|
6
|
-
import
|
|
7
|
-
import
|
|
4
|
+
import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
|
|
5
|
+
import { writeFile as writeFile12 } from "node:fs/promises";
|
|
6
|
+
import os9 from "node:os";
|
|
7
|
+
import path18 from "node:path";
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
|
|
10
10
|
// ../shared/src/schemas.ts
|
|
@@ -3145,8 +3145,8 @@ var toolSessionMutationSchema = z3.object({
|
|
|
3145
3145
|
});
|
|
3146
3146
|
function resolveApiUrl(apiUrl, urlPath) {
|
|
3147
3147
|
const base = apiUrl.endsWith("/") ? apiUrl.slice(0, -1) : apiUrl;
|
|
3148
|
-
const
|
|
3149
|
-
return new URL(`${base}${
|
|
3148
|
+
const path19 = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
|
|
3149
|
+
return new URL(`${base}${path19}`);
|
|
3150
3150
|
}
|
|
3151
3151
|
|
|
3152
3152
|
// src/orchestrator.ts
|
|
@@ -5494,6 +5494,102 @@ async function readRunnerDaemonMetadataFile(filePath) {
|
|
|
5494
5494
|
}
|
|
5495
5495
|
}
|
|
5496
5496
|
|
|
5497
|
+
// src/runner-identity-store.ts
|
|
5498
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
5499
|
+
import { chmod as chmod2, mkdir as mkdir8, readFile as readFile7, writeFile as writeFile8 } from "node:fs/promises";
|
|
5500
|
+
import os5 from "node:os";
|
|
5501
|
+
import path9 from "node:path";
|
|
5502
|
+
|
|
5503
|
+
// src/runner-status.ts
|
|
5504
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
5505
|
+
var watchStateReminderMs = 60 * 1e3;
|
|
5506
|
+
function formatWatchStartupContext(input) {
|
|
5507
|
+
return [
|
|
5508
|
+
`Runner ${input.runnerId} is watching project ${input.projectId}.`,
|
|
5509
|
+
`Repository link: ${input.repositoryLinkId}`,
|
|
5510
|
+
`API: ${input.apiUrl}`,
|
|
5511
|
+
`Polling interval: ${input.intervalSeconds}s. Press Ctrl+C to stop.`
|
|
5512
|
+
];
|
|
5513
|
+
}
|
|
5514
|
+
function formatWatchIdleLine(action, intervalSeconds) {
|
|
5515
|
+
return `${formatProjectNextAction(action)} Checking again in ${intervalSeconds}s.`;
|
|
5516
|
+
}
|
|
5517
|
+
function shouldPrintWatchState(action, previous, nowMs, reminderMs = watchStateReminderMs) {
|
|
5518
|
+
const key = watchStateKey(action);
|
|
5519
|
+
if (!previous || previous.key !== key) {
|
|
5520
|
+
return true;
|
|
5521
|
+
}
|
|
5522
|
+
if (action.kind === "workCompleted") {
|
|
5523
|
+
return false;
|
|
5524
|
+
}
|
|
5525
|
+
return nowMs - previous.printedAtMs >= reminderMs;
|
|
5526
|
+
}
|
|
5527
|
+
function watchStateKey(action) {
|
|
5528
|
+
return [action.kind, action.message, action.workItemId, action.documentId, action.runnerId].filter(Boolean).join(":");
|
|
5529
|
+
}
|
|
5530
|
+
function stableRunnerId(input) {
|
|
5531
|
+
const digest = createHash4("sha256").update(`${input.accountId}:${input.projectId}:${input.repositoryLinkId}:${input.machineId}`).digest("hex").slice(0, 20);
|
|
5532
|
+
return `runner_${digest}`;
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5535
|
+
// src/runner-identity-store.ts
|
|
5536
|
+
var LocalRunnerIdentityStore = class {
|
|
5537
|
+
constructor(filePath = path9.join(os5.homedir(), ".config", "amistio", "runner-identities.json")) {
|
|
5538
|
+
this.filePath = filePath;
|
|
5539
|
+
}
|
|
5540
|
+
filePath;
|
|
5541
|
+
async get(key) {
|
|
5542
|
+
const data = await this.read();
|
|
5543
|
+
return data.runnerIds[key];
|
|
5544
|
+
}
|
|
5545
|
+
async set(key, runnerId) {
|
|
5546
|
+
const data = await this.read();
|
|
5547
|
+
data.runnerIds[key] = runnerId;
|
|
5548
|
+
await this.write(data);
|
|
5549
|
+
}
|
|
5550
|
+
async delete(key) {
|
|
5551
|
+
const data = await this.read();
|
|
5552
|
+
if (!(key in data.runnerIds)) {
|
|
5553
|
+
return;
|
|
5554
|
+
}
|
|
5555
|
+
delete data.runnerIds[key];
|
|
5556
|
+
await this.write(data);
|
|
5557
|
+
}
|
|
5558
|
+
async read() {
|
|
5559
|
+
try {
|
|
5560
|
+
return normalizeStoredRunnerIdentityFile(JSON.parse(await readFile7(this.filePath, "utf8")));
|
|
5561
|
+
} catch {
|
|
5562
|
+
return { runnerIds: {} };
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
async write(data) {
|
|
5566
|
+
await mkdir8(path9.dirname(this.filePath), { recursive: true });
|
|
5567
|
+
await writeFile8(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf8", mode: 384 });
|
|
5568
|
+
await chmod2(this.filePath, 384);
|
|
5569
|
+
}
|
|
5570
|
+
};
|
|
5571
|
+
function runnerIdentityKey(scope) {
|
|
5572
|
+
return `${scope.accountId}:${scope.projectId}:${scope.repositoryLinkId}`;
|
|
5573
|
+
}
|
|
5574
|
+
function createFreshRunnerId() {
|
|
5575
|
+
return `runner_${randomUUID2().replace(/-/g, "").slice(0, 20)}`;
|
|
5576
|
+
}
|
|
5577
|
+
async function resolveLocalRunnerId(scope, store = new LocalRunnerIdentityStore()) {
|
|
5578
|
+
return await store.get(runnerIdentityKey(scope)) ?? stableRunnerId(scope);
|
|
5579
|
+
}
|
|
5580
|
+
function normalizeStoredRunnerIdentityFile(value) {
|
|
5581
|
+
if (!value || typeof value !== "object" || !("runnerIds" in value)) {
|
|
5582
|
+
return { runnerIds: {} };
|
|
5583
|
+
}
|
|
5584
|
+
const runnerIds = value.runnerIds;
|
|
5585
|
+
if (!runnerIds || typeof runnerIds !== "object") {
|
|
5586
|
+
return { runnerIds: {} };
|
|
5587
|
+
}
|
|
5588
|
+
return {
|
|
5589
|
+
runnerIds: Object.fromEntries(Object.entries(runnerIds).filter((entry) => typeof entry[1] === "string"))
|
|
5590
|
+
};
|
|
5591
|
+
}
|
|
5592
|
+
|
|
5497
5593
|
// src/runner-watch-errors.ts
|
|
5498
5594
|
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
5595
|
async function handleRunnerWatchError(options) {
|
|
@@ -5531,10 +5627,10 @@ ${options.detail}`);
|
|
|
5531
5627
|
|
|
5532
5628
|
// src/runner-service.ts
|
|
5533
5629
|
import { spawn as spawn3 } from "node:child_process";
|
|
5534
|
-
import { createHash as
|
|
5535
|
-
import { mkdir as
|
|
5536
|
-
import
|
|
5537
|
-
import
|
|
5630
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
5631
|
+
import { mkdir as mkdir9, readFile as readFile8, rm as rm3, writeFile as writeFile9 } from "node:fs/promises";
|
|
5632
|
+
import os6 from "node:os";
|
|
5633
|
+
import path10 from "node:path";
|
|
5538
5634
|
function detectRunnerServicePlatform(platform = process.platform) {
|
|
5539
5635
|
if (platform === "darwin") return "launchd";
|
|
5540
5636
|
if (platform === "linux") return "systemd";
|
|
@@ -5545,19 +5641,19 @@ function createRunnerServiceDescriptor(input) {
|
|
|
5545
5641
|
if (platform === "unsupported") {
|
|
5546
5642
|
throw new Error("Startup services are supported for user-level launchd on macOS and systemd user services on Linux.");
|
|
5547
5643
|
}
|
|
5548
|
-
const homeDir = input.homeDir ??
|
|
5644
|
+
const homeDir = input.homeDir ?? os6.homedir();
|
|
5549
5645
|
const serviceName = runnerServiceName(input);
|
|
5550
5646
|
const serviceFilePath = runnerServiceFilePath(platform, serviceName, homeDir);
|
|
5551
5647
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5552
5648
|
const command = [input.executablePath ?? process.execPath, input.scriptPath ?? process.argv[1], ...input.args];
|
|
5553
|
-
const logPath =
|
|
5649
|
+
const logPath = path10.join(input.metadataDir ?? defaultRunnerMetadataDir(), `${runnerServiceKey(input)}.service.log`);
|
|
5554
5650
|
const metadata = {
|
|
5555
5651
|
schemaVersion: 1,
|
|
5556
5652
|
accountId: input.accountId,
|
|
5557
5653
|
projectId: input.projectId,
|
|
5558
5654
|
repositoryLinkId: input.repositoryLinkId,
|
|
5559
5655
|
runnerId: input.runnerId,
|
|
5560
|
-
rootDir:
|
|
5656
|
+
rootDir: path10.resolve(input.rootDir),
|
|
5561
5657
|
apiUrl: input.apiUrl,
|
|
5562
5658
|
serviceName,
|
|
5563
5659
|
serviceFilePath,
|
|
@@ -5574,9 +5670,9 @@ function createRunnerServiceDescriptor(input) {
|
|
|
5574
5670
|
}
|
|
5575
5671
|
async function installRunnerService(input, options = {}) {
|
|
5576
5672
|
const descriptor = createRunnerServiceDescriptor(input);
|
|
5577
|
-
await
|
|
5578
|
-
await
|
|
5579
|
-
await
|
|
5673
|
+
await mkdir9(path10.dirname(descriptor.metadata.serviceFilePath), { recursive: true });
|
|
5674
|
+
await mkdir9(input.metadataDir ?? defaultRunnerMetadataDir(), { recursive: true });
|
|
5675
|
+
await writeFile9(descriptor.metadata.serviceFilePath, descriptor.content, { encoding: "utf8", mode: 384 });
|
|
5580
5676
|
await writeRunnerServiceMetadata(descriptor.metadata, input.metadataDir);
|
|
5581
5677
|
if (options.activate !== false) {
|
|
5582
5678
|
const activation = await activateRunnerService(descriptor.metadata);
|
|
@@ -5598,7 +5694,7 @@ async function removeRunnerService(input) {
|
|
|
5598
5694
|
}
|
|
5599
5695
|
async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetadataDir()) {
|
|
5600
5696
|
try {
|
|
5601
|
-
const parsed = JSON.parse(await
|
|
5697
|
+
const parsed = JSON.parse(await readFile8(runnerServiceMetadataPath(input, metadataDir), "utf8"));
|
|
5602
5698
|
if (parsed.schemaVersion !== 1 || !parsed.serviceName || !parsed.serviceFilePath) {
|
|
5603
5699
|
return void 0;
|
|
5604
5700
|
}
|
|
@@ -5608,8 +5704,8 @@ async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetad
|
|
|
5608
5704
|
}
|
|
5609
5705
|
}
|
|
5610
5706
|
async function writeRunnerServiceMetadata(metadata, metadataDir = defaultRunnerMetadataDir()) {
|
|
5611
|
-
await
|
|
5612
|
-
await
|
|
5707
|
+
await mkdir9(metadataDir, { recursive: true });
|
|
5708
|
+
await writeFile9(runnerServiceMetadataPath(metadata, metadataDir), JSON.stringify(metadata, null, 2), { encoding: "utf8", mode: 384 });
|
|
5613
5709
|
}
|
|
5614
5710
|
async function runnerServiceRuntimeStatus(metadata) {
|
|
5615
5711
|
if (metadata.platform === "launchd") {
|
|
@@ -5692,18 +5788,18 @@ WantedBy=default.target
|
|
|
5692
5788
|
}
|
|
5693
5789
|
function runnerServiceFilePath(platform, serviceName, homeDir) {
|
|
5694
5790
|
if (platform === "launchd") {
|
|
5695
|
-
return
|
|
5791
|
+
return path10.join(homeDir, "Library", "LaunchAgents", `${serviceName}.plist`);
|
|
5696
5792
|
}
|
|
5697
|
-
return
|
|
5793
|
+
return path10.join(homeDir, ".config", "systemd", "user", `${serviceName}.service`);
|
|
5698
5794
|
}
|
|
5699
5795
|
function runnerServiceMetadataPath(input, metadataDir) {
|
|
5700
|
-
return
|
|
5796
|
+
return path10.join(metadataDir, `${runnerServiceKey(input)}.service.json`);
|
|
5701
5797
|
}
|
|
5702
5798
|
function runnerServiceName(input) {
|
|
5703
5799
|
return `com.amistio.runner.${runnerServiceKey(input).slice(0, 20)}`;
|
|
5704
5800
|
}
|
|
5705
5801
|
function runnerServiceKey(input) {
|
|
5706
|
-
return
|
|
5802
|
+
return createHash5("sha256").update(`${input.accountId}:${input.projectId}:${input.repositoryLinkId}:${input.runnerId}`).digest("hex");
|
|
5707
5803
|
}
|
|
5708
5804
|
function launchdDomain() {
|
|
5709
5805
|
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
@@ -6031,9 +6127,9 @@ function createSmokeSession({ now, status, lastActivityAt }) {
|
|
|
6031
6127
|
|
|
6032
6128
|
// src/sync.ts
|
|
6033
6129
|
import { execFile as execFile3 } from "node:child_process";
|
|
6034
|
-
import { createHash as
|
|
6035
|
-
import { mkdir as
|
|
6036
|
-
import
|
|
6130
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
6131
|
+
import { mkdir as mkdir10, readdir as readdir5, readFile as readFile9, stat as stat4, writeFile as writeFile10 } from "node:fs/promises";
|
|
6132
|
+
import path11 from "node:path";
|
|
6037
6133
|
import { promisify as promisify3 } from "node:util";
|
|
6038
6134
|
var execFileAsync3 = promisify3(execFile3);
|
|
6039
6135
|
var legacySyncRoots = ["architecture", "context", "decisions", "features", "memory", "plans", "prompts", "workflows"];
|
|
@@ -6129,7 +6225,7 @@ async function readLocalSyncedDocuments(rootDir) {
|
|
|
6129
6225
|
const documentFiles = await findBrainDocumentFiles(rootDir);
|
|
6130
6226
|
const documents = [];
|
|
6131
6227
|
for (const fullPath of documentFiles) {
|
|
6132
|
-
const raw = await
|
|
6228
|
+
const raw = await readFile9(fullPath, "utf8");
|
|
6133
6229
|
const repoPath = toRepoPath(rootDir, fullPath);
|
|
6134
6230
|
const parsed = parseSyncedDocument(raw, repoPath);
|
|
6135
6231
|
if (!parsed) {
|
|
@@ -6179,8 +6275,8 @@ async function materializeBrainDocuments(rootDir, documents, options = {}) {
|
|
|
6179
6275
|
result.skipped.push(document.repoPath);
|
|
6180
6276
|
continue;
|
|
6181
6277
|
}
|
|
6182
|
-
await
|
|
6183
|
-
await
|
|
6278
|
+
await mkdir10(path11.dirname(fullPath), { recursive: true });
|
|
6279
|
+
await writeFile10(fullPath, createSyncedDocumentContent(document), "utf8");
|
|
6184
6280
|
result.written.push(document.repoPath);
|
|
6185
6281
|
}
|
|
6186
6282
|
return result;
|
|
@@ -6309,7 +6405,7 @@ function parseSyncedHtml(content) {
|
|
|
6309
6405
|
}
|
|
6310
6406
|
async function readExistingSyncedDocument(fullPath) {
|
|
6311
6407
|
try {
|
|
6312
|
-
const raw = await
|
|
6408
|
+
const raw = await readFile9(fullPath, "utf8");
|
|
6313
6409
|
const parsed = parseSyncedDocument(raw, fullPath);
|
|
6314
6410
|
if (!parsed) {
|
|
6315
6411
|
return { exists: true };
|
|
@@ -6334,7 +6430,7 @@ async function readExistingSyncedDocument(fullPath) {
|
|
|
6334
6430
|
async function findBrainDocumentFiles(rootDir) {
|
|
6335
6431
|
const files = [];
|
|
6336
6432
|
for (const syncRoot of [...syncRoots, htmlSyncRoot]) {
|
|
6337
|
-
const fullRoot =
|
|
6433
|
+
const fullRoot = path11.join(rootDir, syncRoot);
|
|
6338
6434
|
if (!await exists2(fullRoot)) {
|
|
6339
6435
|
continue;
|
|
6340
6436
|
}
|
|
@@ -6344,7 +6440,7 @@ async function findBrainDocumentFiles(rootDir) {
|
|
|
6344
6440
|
}
|
|
6345
6441
|
async function walkBrainDocumentFiles(directory, files) {
|
|
6346
6442
|
for (const entry of await readdir5(directory, { withFileTypes: true })) {
|
|
6347
|
-
const fullPath =
|
|
6443
|
+
const fullPath = path11.join(directory, entry.name);
|
|
6348
6444
|
if (entry.isDirectory()) {
|
|
6349
6445
|
await walkBrainDocumentFiles(fullPath, files);
|
|
6350
6446
|
} else if (entry.isFile() && /\.(md|mdx|html?)$/i.test(entry.name)) {
|
|
@@ -6353,23 +6449,23 @@ async function walkBrainDocumentFiles(directory, files) {
|
|
|
6353
6449
|
}
|
|
6354
6450
|
}
|
|
6355
6451
|
function safeRepoPath(rootDir, repoPath) {
|
|
6356
|
-
if (
|
|
6452
|
+
if (path11.isAbsolute(repoPath)) {
|
|
6357
6453
|
throw new Error(`Refusing to use absolute repo path: ${repoPath}`);
|
|
6358
6454
|
}
|
|
6359
|
-
const normalized =
|
|
6360
|
-
if (normalized === ".." || normalized.startsWith(`..${
|
|
6455
|
+
const normalized = path11.normalize(repoPath);
|
|
6456
|
+
if (normalized === ".." || normalized.startsWith(`..${path11.sep}`)) {
|
|
6361
6457
|
throw new Error(`Refusing to use path outside the repository: ${repoPath}`);
|
|
6362
6458
|
}
|
|
6363
|
-
const root =
|
|
6364
|
-
const fullPath =
|
|
6365
|
-
if (!fullPath.startsWith(`${root}${
|
|
6459
|
+
const root = path11.resolve(rootDir);
|
|
6460
|
+
const fullPath = path11.resolve(root, normalized);
|
|
6461
|
+
if (!fullPath.startsWith(`${root}${path11.sep}`)) {
|
|
6366
6462
|
throw new Error(`Refusing to use path outside the repository: ${repoPath}`);
|
|
6367
6463
|
}
|
|
6368
6464
|
return fullPath;
|
|
6369
6465
|
}
|
|
6370
6466
|
function isControlPlanePath(repoPath) {
|
|
6371
|
-
const normalized =
|
|
6372
|
-
return syncRoots.some((syncRoot) => normalized === syncRoot || normalized.startsWith(`${syncRoot}${
|
|
6467
|
+
const normalized = path11.normalize(repoPath);
|
|
6468
|
+
return syncRoots.some((syncRoot) => normalized === syncRoot || normalized.startsWith(`${syncRoot}${path11.sep}`)) || normalized === htmlSyncRoot || normalized.startsWith(`${htmlSyncRoot}${path11.sep}`);
|
|
6373
6469
|
}
|
|
6374
6470
|
function canonicalControlPlaneRepoPath(repoPath) {
|
|
6375
6471
|
const normalized = repoPath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
@@ -6380,16 +6476,16 @@ function canonicalControlPlaneRepoPath(repoPath) {
|
|
|
6380
6476
|
return normalized;
|
|
6381
6477
|
}
|
|
6382
6478
|
function toRepoPath(rootDir, fullPath) {
|
|
6383
|
-
return
|
|
6479
|
+
return path11.relative(rootDir, fullPath).split(path11.sep).join("/");
|
|
6384
6480
|
}
|
|
6385
6481
|
function inferTitle(content, repoPath) {
|
|
6386
6482
|
const heading = content.split("\n").find((line) => line.startsWith("# "))?.replace(/^#\s+/, "").trim();
|
|
6387
6483
|
if (heading) return heading;
|
|
6388
6484
|
const htmlHeading = content.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
|
|
6389
|
-
return htmlHeading ||
|
|
6485
|
+
return htmlHeading || path11.basename(repoPath, path11.extname(repoPath));
|
|
6390
6486
|
}
|
|
6391
6487
|
async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingDocuments, options) {
|
|
6392
|
-
const root =
|
|
6488
|
+
const root = path11.resolve(rootDir);
|
|
6393
6489
|
const maxBytes = (options.maxFileKb ?? defaultAutoSyncMaxFileKb) * 1024;
|
|
6394
6490
|
const syncedAt = options.syncedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
6395
6491
|
const existingById = new Map(existingDocuments.map((document) => [document.documentId, document]));
|
|
@@ -6414,7 +6510,7 @@ async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingD
|
|
|
6414
6510
|
skipped.push({ repoPath: normalizedRepoPath, reason: "tooLarge" });
|
|
6415
6511
|
continue;
|
|
6416
6512
|
}
|
|
6417
|
-
const content = await
|
|
6513
|
+
const content = await readFile9(fullPath, "utf8").catch(() => void 0);
|
|
6418
6514
|
if (content === void 0) {
|
|
6419
6515
|
skipped.push({ repoPath: normalizedRepoPath, reason: "unreadable" });
|
|
6420
6516
|
continue;
|
|
@@ -6479,7 +6575,7 @@ async function listAutoSyncCandidatePaths(rootDir) {
|
|
|
6479
6575
|
}
|
|
6480
6576
|
const files = [];
|
|
6481
6577
|
for (const syncRoot of [...syncRoots, htmlSyncRoot, ...legacySyncRoots]) {
|
|
6482
|
-
const fullRoot =
|
|
6578
|
+
const fullRoot = path11.join(rootDir, syncRoot);
|
|
6483
6579
|
if (await exists2(fullRoot)) {
|
|
6484
6580
|
await walkAutoSyncFiles(rootDir, fullRoot, files);
|
|
6485
6581
|
}
|
|
@@ -6488,8 +6584,8 @@ async function listAutoSyncCandidatePaths(rootDir) {
|
|
|
6488
6584
|
}
|
|
6489
6585
|
async function walkAutoSyncFiles(rootDir, directory, files) {
|
|
6490
6586
|
for (const entry of await readdir5(directory, { withFileTypes: true }).catch(() => [])) {
|
|
6491
|
-
const fullPath =
|
|
6492
|
-
const repoPath = normalizeRepoPath3(
|
|
6587
|
+
const fullPath = path11.join(directory, entry.name);
|
|
6588
|
+
const repoPath = normalizeRepoPath3(path11.relative(rootDir, fullPath));
|
|
6493
6589
|
if (entry.isDirectory()) {
|
|
6494
6590
|
if (!autoSyncExcludedDirectoryNames.has(entry.name)) {
|
|
6495
6591
|
await walkAutoSyncFiles(rootDir, fullPath, files);
|
|
@@ -6523,7 +6619,7 @@ function legacyDocumentTypeForRepoPath(repoPath) {
|
|
|
6523
6619
|
return root && root in documentTypeByRoot ? documentTypeByRoot[root] : void 0;
|
|
6524
6620
|
}
|
|
6525
6621
|
function stableExternalDocumentId(metadata, repoPath) {
|
|
6526
|
-
return `doc_external_${
|
|
6622
|
+
return `doc_external_${createHash6("sha256").update(`${metadata.amistioAccountId}:${metadata.amistioProjectId}:${metadata.repositoryLinkId}:${repoPath}`).digest("hex").slice(0, 24)}`;
|
|
6527
6623
|
}
|
|
6528
6624
|
function normalizeRepoPath3(repoPath) {
|
|
6529
6625
|
return repoPath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
@@ -6551,9 +6647,9 @@ async function exists2(filePath) {
|
|
|
6551
6647
|
}
|
|
6552
6648
|
|
|
6553
6649
|
// src/tool-session-store.ts
|
|
6554
|
-
import { mkdir as
|
|
6555
|
-
import
|
|
6556
|
-
import
|
|
6650
|
+
import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile11 } from "node:fs/promises";
|
|
6651
|
+
import os7 from "node:os";
|
|
6652
|
+
import path12 from "node:path";
|
|
6557
6653
|
var LocalToolSessionStore = class {
|
|
6558
6654
|
constructor(filePath = defaultSessionStorePath()) {
|
|
6559
6655
|
this.filePath = filePath;
|
|
@@ -6567,12 +6663,12 @@ var LocalToolSessionStore = class {
|
|
|
6567
6663
|
async setProviderSessionId(toolSessionId, toolName, providerSessionId) {
|
|
6568
6664
|
const data = await this.read();
|
|
6569
6665
|
data[toolSessionId] = { toolName, providerSessionId, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6570
|
-
await
|
|
6571
|
-
await
|
|
6666
|
+
await mkdir11(path12.dirname(this.filePath), { recursive: true });
|
|
6667
|
+
await writeFile11(this.filePath, JSON.stringify(data, null, 2), "utf8");
|
|
6572
6668
|
}
|
|
6573
6669
|
async read() {
|
|
6574
6670
|
try {
|
|
6575
|
-
return JSON.parse(await
|
|
6671
|
+
return JSON.parse(await readFile10(this.filePath, "utf8"));
|
|
6576
6672
|
} catch {
|
|
6577
6673
|
return {};
|
|
6578
6674
|
}
|
|
@@ -6580,16 +6676,16 @@ var LocalToolSessionStore = class {
|
|
|
6580
6676
|
};
|
|
6581
6677
|
function defaultSessionStorePath() {
|
|
6582
6678
|
if (process.platform === "darwin") {
|
|
6583
|
-
return
|
|
6679
|
+
return path12.join(os7.homedir(), "Library", "Application Support", "Amistio", "tool-sessions.json");
|
|
6584
6680
|
}
|
|
6585
6681
|
if (process.platform === "win32") {
|
|
6586
|
-
return
|
|
6682
|
+
return path12.join(process.env.APPDATA ?? os7.homedir(), "Amistio", "tool-sessions.json");
|
|
6587
6683
|
}
|
|
6588
|
-
return
|
|
6684
|
+
return path12.join(process.env.XDG_STATE_HOME ?? path12.join(os7.homedir(), ".local", "state"), "amistio", "tool-sessions.json");
|
|
6589
6685
|
}
|
|
6590
6686
|
|
|
6591
6687
|
// src/work-runner.ts
|
|
6592
|
-
import
|
|
6688
|
+
import path13 from "node:path";
|
|
6593
6689
|
var generationResultStart = "AMISTIO_BRAIN_GENERATION_RESULT_START";
|
|
6594
6690
|
var generationResultEnd = "AMISTIO_BRAIN_GENERATION_RESULT_END";
|
|
6595
6691
|
var assistantAnswerStart = "AMISTIO_ASSISTANT_ANSWER_START";
|
|
@@ -8072,15 +8168,15 @@ function normalizeProjectContextRepoPath(value, options) {
|
|
|
8072
8168
|
if (!trimmed || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed) || /^file:/i.test(trimmed) || /^[A-Za-z]:($|[^\\/])/.test(trimmed)) {
|
|
8073
8169
|
throwUnsafeProjectContextPath();
|
|
8074
8170
|
}
|
|
8075
|
-
const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") ||
|
|
8171
|
+
const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") || path13.isAbsolute(trimmed) || path13.win32.isAbsolute(trimmed);
|
|
8076
8172
|
if (!absolute) {
|
|
8077
8173
|
return normalizeRelativeProjectContextPath(trimmed);
|
|
8078
8174
|
}
|
|
8079
8175
|
if (!options.repositoryRoot) {
|
|
8080
8176
|
throwUnsafeProjectContextPath();
|
|
8081
8177
|
}
|
|
8082
|
-
const useWindowsPathRules =
|
|
8083
|
-
const relativePath = useWindowsPathRules ?
|
|
8178
|
+
const useWindowsPathRules = path13.win32.isAbsolute(trimmed) && !trimmed.startsWith("/");
|
|
8179
|
+
const relativePath = useWindowsPathRules ? path13.win32.relative(path13.win32.resolve(options.repositoryRoot), path13.win32.resolve(trimmed)) : path13.relative(path13.resolve(options.repositoryRoot), path13.resolve(trimmed));
|
|
8084
8180
|
return normalizeRelativeProjectContextPath(relativePath);
|
|
8085
8181
|
}
|
|
8086
8182
|
function normalizeRelativeProjectContextPath(value) {
|
|
@@ -8131,48 +8227,16 @@ function stripJsonFence(value) {
|
|
|
8131
8227
|
return trimmed.replace(/^```(?:json)?\s*/i, "").replace(/```$/i, "").trim();
|
|
8132
8228
|
}
|
|
8133
8229
|
|
|
8134
|
-
// src/runner-status.ts
|
|
8135
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
8136
|
-
var watchStateReminderMs = 60 * 1e3;
|
|
8137
|
-
function formatWatchStartupContext(input) {
|
|
8138
|
-
return [
|
|
8139
|
-
`Runner ${input.runnerId} is watching project ${input.projectId}.`,
|
|
8140
|
-
`Repository link: ${input.repositoryLinkId}`,
|
|
8141
|
-
`API: ${input.apiUrl}`,
|
|
8142
|
-
`Polling interval: ${input.intervalSeconds}s. Press Ctrl+C to stop.`
|
|
8143
|
-
];
|
|
8144
|
-
}
|
|
8145
|
-
function formatWatchIdleLine(action, intervalSeconds) {
|
|
8146
|
-
return `${formatProjectNextAction(action)} Checking again in ${intervalSeconds}s.`;
|
|
8147
|
-
}
|
|
8148
|
-
function shouldPrintWatchState(action, previous, nowMs, reminderMs = watchStateReminderMs) {
|
|
8149
|
-
const key = watchStateKey(action);
|
|
8150
|
-
if (!previous || previous.key !== key) {
|
|
8151
|
-
return true;
|
|
8152
|
-
}
|
|
8153
|
-
if (action.kind === "workCompleted") {
|
|
8154
|
-
return false;
|
|
8155
|
-
}
|
|
8156
|
-
return nowMs - previous.printedAtMs >= reminderMs;
|
|
8157
|
-
}
|
|
8158
|
-
function watchStateKey(action) {
|
|
8159
|
-
return [action.kind, action.message, action.workItemId, action.documentId, action.runnerId].filter(Boolean).join(":");
|
|
8160
|
-
}
|
|
8161
|
-
function stableRunnerId(input) {
|
|
8162
|
-
const digest = createHash6("sha256").update(`${input.accountId}:${input.projectId}:${input.repositoryLinkId}:${input.machineId}`).digest("hex").slice(0, 20);
|
|
8163
|
-
return `runner_${digest}`;
|
|
8164
|
-
}
|
|
8165
|
-
|
|
8166
8230
|
// src/runner-resources.ts
|
|
8167
|
-
import
|
|
8231
|
+
import os8 from "node:os";
|
|
8168
8232
|
var defaultRuntime = {
|
|
8169
8233
|
nowMs: () => Date.now(),
|
|
8170
8234
|
memoryUsage: () => process.memoryUsage(),
|
|
8171
8235
|
uptime: () => process.uptime(),
|
|
8172
8236
|
cpuUsage: () => process.cpuUsage(),
|
|
8173
|
-
totalmem: () =>
|
|
8174
|
-
freemem: () =>
|
|
8175
|
-
loadavg: () =>
|
|
8237
|
+
totalmem: () => os8.totalmem(),
|
|
8238
|
+
freemem: () => os8.freemem(),
|
|
8239
|
+
loadavg: () => os8.loadavg()
|
|
8176
8240
|
};
|
|
8177
8241
|
var previousRunnerResourceSample;
|
|
8178
8242
|
function sampleCurrentRunnerResourceUsage() {
|
|
@@ -8285,8 +8349,8 @@ function roundNumber(value, digits) {
|
|
|
8285
8349
|
// src/importer.ts
|
|
8286
8350
|
import { execFile as execFile4 } from "node:child_process";
|
|
8287
8351
|
import { createHash as createHash7 } from "node:crypto";
|
|
8288
|
-
import { readdir as readdir6, readFile as
|
|
8289
|
-
import
|
|
8352
|
+
import { readdir as readdir6, readFile as readFile11, stat as stat5 } from "node:fs/promises";
|
|
8353
|
+
import path14 from "node:path";
|
|
8290
8354
|
import { promisify as promisify4 } from "node:util";
|
|
8291
8355
|
var execFileAsync4 = promisify4(execFile4);
|
|
8292
8356
|
var defaultMaxFileKb = 256;
|
|
@@ -8307,12 +8371,12 @@ var documentFolderByType = {
|
|
|
8307
8371
|
workflow: "docs/workflows"
|
|
8308
8372
|
};
|
|
8309
8373
|
async function inspectLocalRepository(rootDir, defaultBranch) {
|
|
8310
|
-
const requestedRoot =
|
|
8374
|
+
const requestedRoot = path14.resolve(rootDir);
|
|
8311
8375
|
const root = await runGit2(["-C", requestedRoot, "rev-parse", "--show-toplevel"]).catch(() => requestedRoot);
|
|
8312
8376
|
const detectedBranch = await runGit2(["-C", root, "symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => defaultBranch);
|
|
8313
8377
|
const originUrl = await runGit2(["-C", root, "remote", "get-url", "origin"]).catch(() => void 0);
|
|
8314
8378
|
const parsedCloneUrl = originUrl ? parseOptionalOriginCloneUrl(originUrl) : void 0;
|
|
8315
|
-
const repoName = (parsedCloneUrl?.repoName ??
|
|
8379
|
+
const repoName = (parsedCloneUrl?.repoName ?? path14.basename(root)) || "repository";
|
|
8316
8380
|
const fingerprintSeed = parsedCloneUrl ? `origin:${parsedCloneUrl.normalizedKey}` : `repo:${repoName}:${detectedBranch || defaultBranch}`;
|
|
8317
8381
|
return {
|
|
8318
8382
|
rootDir: root,
|
|
@@ -8324,7 +8388,7 @@ async function inspectLocalRepository(rootDir, defaultBranch) {
|
|
|
8324
8388
|
};
|
|
8325
8389
|
}
|
|
8326
8390
|
async function scanLegacyDocuments(options) {
|
|
8327
|
-
const rootDir =
|
|
8391
|
+
const rootDir = path14.resolve(options.rootDir);
|
|
8328
8392
|
const maxBytes = (options.maxFileKb ?? defaultMaxFileKb) * 1024;
|
|
8329
8393
|
const skipped = [];
|
|
8330
8394
|
const candidates = [];
|
|
@@ -8344,7 +8408,7 @@ async function scanLegacyDocuments(options) {
|
|
|
8344
8408
|
skipped.push({ repoPath, reason: "excluded" });
|
|
8345
8409
|
continue;
|
|
8346
8410
|
}
|
|
8347
|
-
const fullPath =
|
|
8411
|
+
const fullPath = path14.join(rootDir, ...repoPath.split("/"));
|
|
8348
8412
|
const fileStat = await stat5(fullPath).catch(() => void 0);
|
|
8349
8413
|
if (!fileStat?.isFile()) {
|
|
8350
8414
|
skipped.push({ repoPath, reason: "unreadable" });
|
|
@@ -8354,7 +8418,7 @@ async function scanLegacyDocuments(options) {
|
|
|
8354
8418
|
skipped.push({ repoPath, reason: "tooLarge" });
|
|
8355
8419
|
continue;
|
|
8356
8420
|
}
|
|
8357
|
-
const content = await
|
|
8421
|
+
const content = await readFile11(fullPath, "utf8").catch(() => void 0);
|
|
8358
8422
|
if (content === void 0) {
|
|
8359
8423
|
skipped.push({ repoPath, reason: "unreadable" });
|
|
8360
8424
|
continue;
|
|
@@ -8446,8 +8510,8 @@ async function listRepositoryPaths(rootDir) {
|
|
|
8446
8510
|
async function walkRepository(rootDir, directory, files) {
|
|
8447
8511
|
const entries = await readdir6(directory, { withFileTypes: true }).catch(() => []);
|
|
8448
8512
|
for (const entry of entries) {
|
|
8449
|
-
const fullPath =
|
|
8450
|
-
const repoPath = normalizeRepoPath4(
|
|
8513
|
+
const fullPath = path14.join(directory, entry.name);
|
|
8514
|
+
const repoPath = normalizeRepoPath4(path14.relative(rootDir, fullPath));
|
|
8451
8515
|
if (entry.isDirectory()) {
|
|
8452
8516
|
if (!excludedDirectoryNames.has(entry.name)) {
|
|
8453
8517
|
await walkRepository(rootDir, fullPath, files);
|
|
@@ -8515,9 +8579,9 @@ function uniqueDestinationPath(basePath, sourcePath, usedPaths) {
|
|
|
8515
8579
|
usedPaths.add(basePath);
|
|
8516
8580
|
return basePath;
|
|
8517
8581
|
}
|
|
8518
|
-
const extension =
|
|
8519
|
-
const directory =
|
|
8520
|
-
const basename =
|
|
8582
|
+
const extension = path14.posix.extname(basePath) || ".md";
|
|
8583
|
+
const directory = path14.posix.dirname(basePath);
|
|
8584
|
+
const basename = path14.posix.basename(basePath, extension);
|
|
8521
8585
|
const uniquePath = `${directory}/${basename}-${hashText(sourcePath, 8)}${extension}`;
|
|
8522
8586
|
usedPaths.add(uniquePath);
|
|
8523
8587
|
return uniquePath;
|
|
@@ -8590,7 +8654,7 @@ function inferTitle2(content, repoPath) {
|
|
|
8590
8654
|
if (heading) return heading;
|
|
8591
8655
|
const htmlHeading = body.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
|
|
8592
8656
|
if (htmlHeading) return htmlHeading;
|
|
8593
|
-
const basename =
|
|
8657
|
+
const basename = path14.posix.basename(repoPath, path14.posix.extname(repoPath)).replace(/[-_]+/g, " ").trim();
|
|
8594
8658
|
return titleCase(basename || "Imported Document");
|
|
8595
8659
|
}
|
|
8596
8660
|
function stripFrontmatter(content) {
|
|
@@ -8624,7 +8688,7 @@ async function runGit2(args) {
|
|
|
8624
8688
|
|
|
8625
8689
|
// src/runner-actions.ts
|
|
8626
8690
|
import { spawn as spawn4 } from "node:child_process";
|
|
8627
|
-
import
|
|
8691
|
+
import path15 from "node:path";
|
|
8628
8692
|
function buildBackgroundRunnerArgs(options) {
|
|
8629
8693
|
const args = [
|
|
8630
8694
|
"run",
|
|
@@ -8634,7 +8698,7 @@ function buildBackgroundRunnerArgs(options) {
|
|
|
8634
8698
|
"--runner-id",
|
|
8635
8699
|
options.runnerId,
|
|
8636
8700
|
"--root",
|
|
8637
|
-
|
|
8701
|
+
path15.resolve(options.root),
|
|
8638
8702
|
"--session",
|
|
8639
8703
|
options.session,
|
|
8640
8704
|
"--interval-seconds",
|
|
@@ -8752,8 +8816,8 @@ function truncateProcessOutput(value) {
|
|
|
8752
8816
|
|
|
8753
8817
|
// src/git-worktree.ts
|
|
8754
8818
|
import { execFile as execFile5 } from "node:child_process";
|
|
8755
|
-
import { copyFile, lstat, mkdir as
|
|
8756
|
-
import
|
|
8819
|
+
import { copyFile, lstat, mkdir as mkdir12, readdir as readdir7, stat as stat6 } from "node:fs/promises";
|
|
8820
|
+
import path16 from "node:path";
|
|
8757
8821
|
import { promisify as promisify5 } from "node:util";
|
|
8758
8822
|
var execFileAsync5 = promisify5(execFile5);
|
|
8759
8823
|
var exactLocalEnvironmentFiles = /* @__PURE__ */ new Set([".env", ".env.local", ".env.development", ".env.development.local", ".env.test", ".env.test.local", ".env.production", ".env.production.local"]);
|
|
@@ -8785,7 +8849,7 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
|
|
|
8785
8849
|
const preparedLocalEnvironmentFileCount2 = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
|
|
8786
8850
|
return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount2 ? { preparedLocalEnvironmentFileCount: preparedLocalEnvironmentFileCount2 } : {} };
|
|
8787
8851
|
}
|
|
8788
|
-
await
|
|
8852
|
+
await mkdir12(path16.dirname(worktreePath), { recursive: true });
|
|
8789
8853
|
const branchExists = await gitCommandSucceeds(repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${identity.branch}`]);
|
|
8790
8854
|
const worktreeArgs = branchExists ? ["worktree", "add", worktreePath, identity.branch] : ["worktree", "add", "-b", identity.branch, worktreePath, baseRevision];
|
|
8791
8855
|
await gitOutput(repoRoot, worktreeArgs).catch((error) => {
|
|
@@ -8803,9 +8867,9 @@ async function resolveExistingGitWorktreeIsolation(rootDir, workItem) {
|
|
|
8803
8867
|
return { ...identity, baseRevision, worktreePath };
|
|
8804
8868
|
}
|
|
8805
8869
|
function localWorktreePath(repoRoot, worktreeKey) {
|
|
8806
|
-
const repoName =
|
|
8870
|
+
const repoName = path16.basename(repoRoot);
|
|
8807
8871
|
const worktreeSlug = worktreeKey.split("/").filter(Boolean).pop() ?? "work";
|
|
8808
|
-
return
|
|
8872
|
+
return path16.join(path16.dirname(repoRoot), `${repoName}.worktrees`, worktreeSlug);
|
|
8809
8873
|
}
|
|
8810
8874
|
async function assertExistingWorktree(worktreePath, branch) {
|
|
8811
8875
|
await gitOutput(worktreePath, ["rev-parse", "--is-inside-work-tree"]);
|
|
@@ -8831,8 +8895,8 @@ async function prepareLocalWorktreeEnvironment(repoRoot, worktreePath) {
|
|
|
8831
8895
|
const candidates = await localEnvironmentFileCandidates(repoRoot);
|
|
8832
8896
|
let preparedCount = 0;
|
|
8833
8897
|
for (const candidate of candidates) {
|
|
8834
|
-
const sourcePath =
|
|
8835
|
-
const targetPath =
|
|
8898
|
+
const sourcePath = path16.join(repoRoot, candidate);
|
|
8899
|
+
const targetPath = path16.join(worktreePath, candidate);
|
|
8836
8900
|
if (await pathExists(targetPath)) {
|
|
8837
8901
|
continue;
|
|
8838
8902
|
}
|
|
@@ -8863,7 +8927,7 @@ async function localEnvironmentFileCandidates(repoRoot) {
|
|
|
8863
8927
|
if (!isRootFileName(name)) {
|
|
8864
8928
|
continue;
|
|
8865
8929
|
}
|
|
8866
|
-
const source = await lstat(
|
|
8930
|
+
const source = await lstat(path16.join(repoRoot, name)).catch(() => void 0);
|
|
8867
8931
|
if (source?.isFile()) {
|
|
8868
8932
|
candidates.push(name);
|
|
8869
8933
|
}
|
|
@@ -8874,7 +8938,7 @@ function isAllowedLocalEnvironmentFile(name) {
|
|
|
8874
8938
|
return exactLocalEnvironmentFiles.has(name) || localEnvironmentFilePattern.test(name);
|
|
8875
8939
|
}
|
|
8876
8940
|
function isRootFileName(name) {
|
|
8877
|
-
return name ===
|
|
8941
|
+
return name === path16.basename(name) && !name.includes("/") && !name.includes("\\");
|
|
8878
8942
|
}
|
|
8879
8943
|
async function gitOutput(cwd, args) {
|
|
8880
8944
|
const { stdout } = await execFileAsync5("git", args, { cwd, maxBuffer: 1024 * 1024 });
|
|
@@ -8907,7 +8971,7 @@ function safeFileError(error) {
|
|
|
8907
8971
|
|
|
8908
8972
|
// src/implementation-handoff.ts
|
|
8909
8973
|
import { execFile as execFile6 } from "node:child_process";
|
|
8910
|
-
import
|
|
8974
|
+
import path17 from "node:path";
|
|
8911
8975
|
import { promisify as promisify6 } from "node:util";
|
|
8912
8976
|
var execFileAsync6 = promisify6(execFile6);
|
|
8913
8977
|
async function completeImplementationHandoff(input) {
|
|
@@ -9175,7 +9239,7 @@ async function cleanupWorktree(run, input) {
|
|
|
9175
9239
|
return { status: "failed", message: "Cleanup skipped because the worktree is not clean after PR handoff." };
|
|
9176
9240
|
}
|
|
9177
9241
|
try {
|
|
9178
|
-
await gitOutput2(run, input.primaryRepoRoot ||
|
|
9242
|
+
await gitOutput2(run, input.primaryRepoRoot || path17.dirname(input.worktreePath), ["worktree", "remove", input.worktreePath]);
|
|
9179
9243
|
return { status: "completed" };
|
|
9180
9244
|
} catch (error) {
|
|
9181
9245
|
return { status: "failed", message: `Cleanup failed: ${safeErrorMessage(error)}` };
|
|
@@ -9896,7 +9960,7 @@ program.command("import").description("Pair an existing checkout and import lega
|
|
|
9896
9960
|
});
|
|
9897
9961
|
program.command("pair").description("Pair this repository with an Amistio web project").requiredOption("--account <accountId>", "Amistio account ID").requiredOption("--project <projectId>", "Amistio project ID").option("--repository-link <repositoryLinkId>", "Existing repository link ID").option("--default-branch <branch>", "Default branch", "main").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--pairing-code <code>", "Short-lived pairing code from the Amistio app").option("--token <token>", "Runner/device credential to store outside the repository").option("--root <path>", "Repository root", defaultRoot).action(async (options, command) => {
|
|
9898
9962
|
const pairingRoot = await resolvePairingRoot(options.root, { explicitRoot: command.getOptionValueSource("root") === "cli" });
|
|
9899
|
-
let repositoryLinkId = options.repositoryLink ?? `repo_${
|
|
9963
|
+
let repositoryLinkId = options.repositoryLink ?? `repo_${randomUUID3()}`;
|
|
9900
9964
|
let credential = options.token;
|
|
9901
9965
|
if (options.pairingCode) {
|
|
9902
9966
|
const pairing = await new ApiClient({
|
|
@@ -10082,7 +10146,7 @@ work.command("prompt").description("Print or write an approved work prompt witho
|
|
|
10082
10146
|
}
|
|
10083
10147
|
const prompt = await createRunnerWorkPrompt(context.client, context.metadata.amistioProjectId, workItem);
|
|
10084
10148
|
if (options.out) {
|
|
10085
|
-
await
|
|
10149
|
+
await writeFile12(options.out, prompt, "utf8");
|
|
10086
10150
|
console.log(`Wrote work prompt to ${options.out}.`);
|
|
10087
10151
|
} else {
|
|
10088
10152
|
console.log(prompt);
|
|
@@ -10096,6 +10160,34 @@ program.command("tools").description("List local AI coding tools that the Amisti
|
|
|
10096
10160
|
}
|
|
10097
10161
|
console.log("custom - pass --tool-command to use any other local runner command.");
|
|
10098
10162
|
});
|
|
10163
|
+
var harness = program.command("harness").description("Inspect the built-in Amistio harness, direct providers, and bounded tool adapters");
|
|
10164
|
+
harness.command("status").description("Show the built-in harness boundary and execution policy summary").action(() => {
|
|
10165
|
+
console.log(`Harness: ${builtinAmistioHarnessAdapter.displayName} (${builtinAmistioHarnessAdapter.id})`);
|
|
10166
|
+
console.log("Default: built-in Amistio harness");
|
|
10167
|
+
console.log("Runner-owned boundary: pairing, work claiming, leases, worktree isolation, redaction, finalization, and handoff.");
|
|
10168
|
+
console.log(`Read-only work policy: ${runnerSupportedWorkKinds.filter((workKind) => harnessMutationPolicyForWorkKind(workKind) === "readOnly").join(", ")}`);
|
|
10169
|
+
console.log(`Mutating work policy: ${runnerSupportedWorkKinds.filter((workKind) => harnessMutationPolicyForWorkKind(workKind) === "mutating").join(", ")}`);
|
|
10170
|
+
console.log("Run execution: amistio run --watch");
|
|
10171
|
+
console.log("Local AI clients: amistio tools");
|
|
10172
|
+
console.log("Host execution helper: amistio host-helper status");
|
|
10173
|
+
});
|
|
10174
|
+
harness.command("providers").description("Check safe local readiness for direct and agent-client provider routes").option("--root <path>", "Repository root", defaultRoot).action(async (options) => {
|
|
10175
|
+
for (const target of harnessProviderCheckTargets()) {
|
|
10176
|
+
const result = await checkProviderAuthLink({ request: target.request, root: options.root });
|
|
10177
|
+
console.log(`${target.label}: ${result.providerAuthStatus.status}`);
|
|
10178
|
+
for (const line of formatProviderAuthStatusLines(result.providerAuthStatus)) {
|
|
10179
|
+
console.log(` ${line}`);
|
|
10180
|
+
}
|
|
10181
|
+
}
|
|
10182
|
+
});
|
|
10183
|
+
harness.command("tools").description("List bounded Amistio-owned tool adapters available to the harness").action(() => {
|
|
10184
|
+
console.log("Bounded Amistio harness tools. For optional local AI clients, run `amistio tools`.");
|
|
10185
|
+
for (const adapter of boundedToolAdapterCatalog) {
|
|
10186
|
+
const mutation = adapter.mutating ? "mutating" : "read-only";
|
|
10187
|
+
const implemented = adapter.implemented ? "yes" : "no";
|
|
10188
|
+
console.log(`${implemented} ${adapter.kind} ${adapter.id} - ${adapter.displayName}; ${mutation}; ${adapter.concurrency}; resources: ${adapter.serializedResourceKinds.join(", ")}`);
|
|
10189
|
+
}
|
|
10190
|
+
});
|
|
10099
10191
|
var hostHelper = program.command("host-helper").description("Inspect the optional Amistio host helper used for stronger local execution primitives");
|
|
10100
10192
|
hostHelper.command("status").description("Show configured host-helper protocol and capability status").option("--path <path>", "Helper executable path; defaults to AMISTIO_HOST_HELPER_PATH").action(async (options) => {
|
|
10101
10193
|
const config = options.path?.trim() ? { command: options.path.trim() } : nativeHostHelperConfigFromEnvironment();
|
|
@@ -10164,7 +10256,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
|
|
|
10164
10256
|
...options.toolCommand ? { toolCommand: options.toolCommand } : {},
|
|
10165
10257
|
...localModelConfig,
|
|
10166
10258
|
streamOutput: options.stream,
|
|
10167
|
-
...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${
|
|
10259
|
+
...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${randomUUID3()}`, policy: sessionPolicy, decision: localSessionDecision(sessionPolicy) } }
|
|
10168
10260
|
});
|
|
10169
10261
|
if (!options.stream && result.stdout.trim()) {
|
|
10170
10262
|
console.log(result.stdout.trim());
|
|
@@ -10187,12 +10279,7 @@ program.command("run").description("Claim and run approved Amistio work locally"
|
|
|
10187
10279
|
process.exitCode = 1;
|
|
10188
10280
|
return;
|
|
10189
10281
|
}
|
|
10190
|
-
const runnerId = options.runnerId ??
|
|
10191
|
-
accountId: context.metadata.amistioAccountId,
|
|
10192
|
-
projectId: context.metadata.amistioProjectId,
|
|
10193
|
-
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10194
|
-
machineId: runnerMachineId()
|
|
10195
|
-
});
|
|
10282
|
+
const runnerId = options.runnerId ?? await resolveLocalRunnerId(runnerIdentityScope(context.metadata));
|
|
10196
10283
|
const resolvedOptions = { ...options, runnerId };
|
|
10197
10284
|
if (resolvedOptions.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
|
|
10198
10285
|
console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
|
|
@@ -10210,7 +10297,7 @@ program.command("run").description("Claim and run approved Amistio work locally"
|
|
|
10210
10297
|
projectId: context.metadata.amistioProjectId,
|
|
10211
10298
|
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10212
10299
|
runnerId,
|
|
10213
|
-
rootDir:
|
|
10300
|
+
rootDir: path18.resolve(options.root),
|
|
10214
10301
|
apiUrl: options.apiUrl,
|
|
10215
10302
|
args: buildBackgroundRunnerArgs(resolvedOptions)
|
|
10216
10303
|
});
|
|
@@ -10281,6 +10368,38 @@ program.command("run").description("Claim and run approved Amistio work locally"
|
|
|
10281
10368
|
}
|
|
10282
10369
|
});
|
|
10283
10370
|
var runner = program.command("runner").description("Manage local Amistio runner processes");
|
|
10371
|
+
runner.command("repair").description("Repair local runner identity state for this paired repository").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Fresh runner ID to store instead of generating one").option("--clear-credential", "Delete the local runner credential so this checkout must be paired again").option("--dry-run", "Print repair actions without changing local config").action(async (options) => {
|
|
10372
|
+
const metadata = await readProjectLink(options.root);
|
|
10373
|
+
if (!metadata) {
|
|
10374
|
+
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
10375
|
+
return;
|
|
10376
|
+
}
|
|
10377
|
+
const scope = runnerIdentityScope(metadata);
|
|
10378
|
+
const identityKey = runnerIdentityKey(scope);
|
|
10379
|
+
const identityStore = new LocalRunnerIdentityStore();
|
|
10380
|
+
const credentialStore = new LocalCredentialStore();
|
|
10381
|
+
const stableRunner = stableRunnerId(scope);
|
|
10382
|
+
const currentRunnerId = await identityStore.get(identityKey) ?? stableRunner;
|
|
10383
|
+
const nextRunnerId = options.runnerId?.trim() || createFreshRunnerId();
|
|
10384
|
+
if (options.dryRun) {
|
|
10385
|
+
console.log(`Would rotate local runner ID from ${currentRunnerId} to ${nextRunnerId}.`);
|
|
10386
|
+
if (options.clearCredential) {
|
|
10387
|
+
console.log("Would delete the local runner credential for this paired checkout.");
|
|
10388
|
+
}
|
|
10389
|
+
return;
|
|
10390
|
+
}
|
|
10391
|
+
await identityStore.set(identityKey, nextRunnerId);
|
|
10392
|
+
if (options.clearCredential) {
|
|
10393
|
+
await credentialStore.delete(credentialKey(metadata.amistioAccountId, metadata.amistioProjectId, metadata.repositoryLinkId));
|
|
10394
|
+
}
|
|
10395
|
+
console.log(`Local runner ID repaired: ${currentRunnerId} -> ${nextRunnerId}.`);
|
|
10396
|
+
if (options.clearCredential) {
|
|
10397
|
+
console.log("Deleted the local runner credential. Create a fresh pairing code in the Runner panel, then run the copied pair/import command.");
|
|
10398
|
+
} else {
|
|
10399
|
+
console.log(`Next: amistio run${formatApiUrlFlag(options.apiUrl)} --watch`);
|
|
10400
|
+
console.log("If a startup service uses the old runner ID, reinstall it from the Runner panel command.");
|
|
10401
|
+
}
|
|
10402
|
+
});
|
|
10284
10403
|
runner.command("status").description("Show background runner status for the paired repository").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Limit status to one runner ID").action(async (options) => {
|
|
10285
10404
|
const context = await loadPairedApiContext(options.root, options.apiUrl);
|
|
10286
10405
|
if (!context) {
|
|
@@ -10387,12 +10506,7 @@ runnerService.command("install").description("Install a user-level startup servi
|
|
|
10387
10506
|
process.exitCode = 1;
|
|
10388
10507
|
return;
|
|
10389
10508
|
}
|
|
10390
|
-
const runnerId = options.runnerId ??
|
|
10391
|
-
accountId: context.metadata.amistioAccountId,
|
|
10392
|
-
projectId: context.metadata.amistioProjectId,
|
|
10393
|
-
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10394
|
-
machineId: runnerMachineId()
|
|
10395
|
-
});
|
|
10509
|
+
const runnerId = options.runnerId ?? await resolveLocalRunnerId(runnerIdentityScope(context.metadata));
|
|
10396
10510
|
if (options.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
|
|
10397
10511
|
console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
|
|
10398
10512
|
process.exitCode = 1;
|
|
@@ -10404,7 +10518,7 @@ runnerService.command("install").description("Install a user-level startup servi
|
|
|
10404
10518
|
projectId: context.metadata.amistioProjectId,
|
|
10405
10519
|
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10406
10520
|
runnerId,
|
|
10407
|
-
rootDir:
|
|
10521
|
+
rootDir: path18.resolve(options.root),
|
|
10408
10522
|
apiUrl: options.apiUrl,
|
|
10409
10523
|
args,
|
|
10410
10524
|
platform
|
|
@@ -10430,12 +10544,7 @@ runnerService.command("status").description("Show the startup service status for
|
|
|
10430
10544
|
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
10431
10545
|
return;
|
|
10432
10546
|
}
|
|
10433
|
-
const runnerId = options.runnerId ??
|
|
10434
|
-
accountId: context.metadata.amistioAccountId,
|
|
10435
|
-
projectId: context.metadata.amistioProjectId,
|
|
10436
|
-
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10437
|
-
machineId: runnerMachineId()
|
|
10438
|
-
});
|
|
10547
|
+
const runnerId = options.runnerId ?? await resolveLocalRunnerId(runnerIdentityScope(context.metadata));
|
|
10439
10548
|
const metadata = await readRunnerServiceMetadata({ accountId: context.metadata.amistioAccountId, projectId: context.metadata.amistioProjectId, repositoryLinkId: context.metadata.repositoryLinkId, runnerId });
|
|
10440
10549
|
if (!metadata) {
|
|
10441
10550
|
console.log("No startup service metadata found for this paired repository runner.");
|
|
@@ -10454,12 +10563,7 @@ runnerService.command("remove").description("Remove the startup service for this
|
|
|
10454
10563
|
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
10455
10564
|
return;
|
|
10456
10565
|
}
|
|
10457
|
-
const runnerId = options.runnerId ??
|
|
10458
|
-
accountId: context.metadata.amistioAccountId,
|
|
10459
|
-
projectId: context.metadata.amistioProjectId,
|
|
10460
|
-
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10461
|
-
machineId: runnerMachineId()
|
|
10462
|
-
});
|
|
10566
|
+
const runnerId = options.runnerId ?? await resolveLocalRunnerId(runnerIdentityScope(context.metadata));
|
|
10463
10567
|
const removed = await removeRunnerService({ accountId: context.metadata.amistioAccountId, projectId: context.metadata.amistioProjectId, repositoryLinkId: context.metadata.repositoryLinkId, runnerId });
|
|
10464
10568
|
console.log(removed ? `Removed startup service ${removed.serviceName}.` : "No startup service metadata found for this paired repository runner.");
|
|
10465
10569
|
});
|
|
@@ -11088,7 +11192,7 @@ async function runNextWorkItem({
|
|
|
11088
11192
|
projectId,
|
|
11089
11193
|
result.workItem.workItemId,
|
|
11090
11194
|
finalStatus,
|
|
11091
|
-
`run_${result.workItem.workItemId}_${
|
|
11195
|
+
`run_${result.workItem.workItemId}_${randomUUID3()}`,
|
|
11092
11196
|
runnerId,
|
|
11093
11197
|
{
|
|
11094
11198
|
tool: preview.toolName,
|
|
@@ -11182,7 +11286,7 @@ async function prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency,
|
|
|
11182
11286
|
const telemetry = workItemIsolationTelemetry(workItem, { ...identity, baseRevision: workItem.baseRevision ?? "unknown", worktreePath: "" });
|
|
11183
11287
|
const finalAttempt = workItem.attempt >= maxPreflightAttempts;
|
|
11184
11288
|
const statusMessage = finalAttempt ? `Git worktree preflight failed after ${workItem.attempt}/${maxPreflightAttempts} attempts. ${message}` : `Git worktree preflight attempt ${workItem.attempt}/${maxPreflightAttempts} failed. Requeueing for retry. ${message}`;
|
|
11185
|
-
const statusResult = await apiClient.updateWorkStatus(projectId, workItem.workItemId, finalAttempt ? "failed" : "approved", `worktree_${finalAttempt ? "failed" : "retry"}_${workItem.workItemId}_${workItem.attempt}_${
|
|
11289
|
+
const statusResult = await apiClient.updateWorkStatus(projectId, workItem.workItemId, finalAttempt ? "failed" : "approved", `worktree_${finalAttempt ? "failed" : "retry"}_${workItem.workItemId}_${workItem.attempt}_${randomUUID3()}`, runnerId, {
|
|
11186
11290
|
...telemetry,
|
|
11187
11291
|
message: statusMessage,
|
|
11188
11292
|
...finalAttempt ? { blockerReason: message } : { releaseClaim: true },
|
|
@@ -11251,7 +11355,7 @@ async function recordFinalizationFailure({ apiClient, durationMs, error, isolati
|
|
|
11251
11355
|
const settlements = await Promise.allSettled([
|
|
11252
11356
|
apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig)),
|
|
11253
11357
|
markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage6(error)),
|
|
11254
|
-
apiClient.updateWorkStatus(projectId, workItem.workItemId, "failed", `finalize_failed_${workItem.workItemId}_${workItem.attempt}_${
|
|
11358
|
+
apiClient.updateWorkStatus(projectId, workItem.workItemId, "failed", `finalize_failed_${workItem.workItemId}_${workItem.attempt}_${randomUUID3()}`, runnerId, {
|
|
11255
11359
|
...isolationTelemetry,
|
|
11256
11360
|
tool: toolName,
|
|
11257
11361
|
durationMs,
|
|
@@ -11402,7 +11506,7 @@ async function updateRunnerCommandStatus(apiClient, context, command, status, me
|
|
|
11402
11506
|
runnerId: context.runnerId,
|
|
11403
11507
|
repositoryLinkId: context.repositoryLinkId,
|
|
11404
11508
|
status,
|
|
11405
|
-
idempotencyKey: `runner_command_${command.commandId}_${status}_${
|
|
11509
|
+
idempotencyKey: `runner_command_${command.commandId}_${status}_${randomUUID3()}`,
|
|
11406
11510
|
message,
|
|
11407
11511
|
...error ? { error } : {},
|
|
11408
11512
|
...providerAuthStatus ? { providerAuthStatus } : {}
|
|
@@ -11445,6 +11549,9 @@ async function executeImplementationHandoffRecoveryCommand(apiClient, command, c
|
|
|
11445
11549
|
if (workItem.repositoryLinkId && workItem.repositoryLinkId !== context.repositoryLinkId) {
|
|
11446
11550
|
return { succeeded: false, message: "Handoff recovery command is not scoped to this repository link." };
|
|
11447
11551
|
}
|
|
11552
|
+
if (workItem.claimedByRunnerId && workItem.claimedByRunnerId !== context.runnerId) {
|
|
11553
|
+
return { succeeded: false, message: `Handoff recovery command belongs to runner ${workItem.claimedByRunnerId}. Restart that runner or use Requeue fresh attempt.` };
|
|
11554
|
+
}
|
|
11448
11555
|
if (!workItem.implementationHandoff?.recovery?.availableActions.includes(command.handoffRecoveryAction)) {
|
|
11449
11556
|
return { succeeded: false, message: "Handoff recovery action is no longer available for this work item." };
|
|
11450
11557
|
}
|
|
@@ -11491,7 +11598,7 @@ async function findRecoveryWorkItem(apiClient, projectId, workItemId) {
|
|
|
11491
11598
|
return workItems.find((item) => item.workItemId === workItemId);
|
|
11492
11599
|
}
|
|
11493
11600
|
async function submitRecoveredHandoff(apiClient, context, workItem, handoff, action) {
|
|
11494
|
-
await apiClient.updateWorkStatus(context.projectId, workItem.workItemId, recoveredHandoffWorkStatus(handoff), `handoff_recovery_${workItem.workItemId}_${action}_${
|
|
11601
|
+
await apiClient.updateWorkStatus(context.projectId, workItem.workItemId, recoveredHandoffWorkStatus(handoff), `handoff_recovery_${workItem.workItemId}_${action}_${randomUUID3()}`, context.runnerId, {
|
|
11495
11602
|
implementationHandoff: handoff,
|
|
11496
11603
|
...handoff.message ? { message: handoff.message } : {},
|
|
11497
11604
|
...workItem.controllingAdrId ? { controllingAdrId: workItem.controllingAdrId } : {},
|
|
@@ -11775,7 +11882,7 @@ ${toolResult.stderr}`);
|
|
|
11775
11882
|
const resultMutation = {
|
|
11776
11883
|
status: "completed",
|
|
11777
11884
|
runnerId,
|
|
11778
|
-
idempotencyKey: `generation_${workItem.workItemId}_${workItem.attempt}_${
|
|
11885
|
+
idempotencyKey: `generation_${workItem.workItemId}_${workItem.attempt}_${randomUUID3()}`,
|
|
11779
11886
|
artifacts,
|
|
11780
11887
|
tool: toolName,
|
|
11781
11888
|
durationMs,
|
|
@@ -11850,7 +11957,7 @@ ${toolResult.stderr}`);
|
|
|
11850
11957
|
const failedResult2 = await apiClient.submitBrainGenerationResult(projectId, workItem.workItemId, {
|
|
11851
11958
|
status: "failed",
|
|
11852
11959
|
runnerId,
|
|
11853
|
-
idempotencyKey: `generation_${workItem.workItemId}_${
|
|
11960
|
+
idempotencyKey: `generation_${workItem.workItemId}_${randomUUID3()}`,
|
|
11854
11961
|
tool: toolName,
|
|
11855
11962
|
durationMs,
|
|
11856
11963
|
...failedSessionTelemetry,
|
|
@@ -11900,7 +12007,7 @@ ${toolResult.stderr}`);
|
|
|
11900
12007
|
const resultMutation = {
|
|
11901
12008
|
status: "completed",
|
|
11902
12009
|
runnerId,
|
|
11903
|
-
idempotencyKey: `assistant_${workItem.workItemId}_${
|
|
12010
|
+
idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID3()}`,
|
|
11904
12011
|
answer: answerResult.answer,
|
|
11905
12012
|
sourceBoundary: answerResult.sourceBoundary,
|
|
11906
12013
|
citations: answerResult.citations,
|
|
@@ -11936,7 +12043,7 @@ ${toolResult.stderr}`);
|
|
|
11936
12043
|
const failedMutation = {
|
|
11937
12044
|
status: "failed",
|
|
11938
12045
|
runnerId,
|
|
11939
|
-
idempotencyKey: `assistant_${workItem.workItemId}_${
|
|
12046
|
+
idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID3()}`,
|
|
11940
12047
|
tool: toolName,
|
|
11941
12048
|
durationMs,
|
|
11942
12049
|
...sessionTelemetry,
|
|
@@ -11999,7 +12106,7 @@ ${toolResult.stderr}`);
|
|
|
11999
12106
|
const resultMutation = {
|
|
12000
12107
|
status: "completed",
|
|
12001
12108
|
runnerId,
|
|
12002
|
-
idempotencyKey: `impact_${workItem.workItemId}_${
|
|
12109
|
+
idempotencyKey: `impact_${workItem.workItemId}_${randomUUID3()}`,
|
|
12003
12110
|
report: {
|
|
12004
12111
|
...report,
|
|
12005
12112
|
analyzedRepoRevision: report.analyzedRepoRevision ?? metadata?.lastSyncedRevision
|
|
@@ -12036,7 +12143,7 @@ ${toolResult.stderr}`);
|
|
|
12036
12143
|
const failedMutation = {
|
|
12037
12144
|
status: "failed",
|
|
12038
12145
|
runnerId,
|
|
12039
|
-
idempotencyKey: `impact_${workItem.workItemId}_${
|
|
12146
|
+
idempotencyKey: `impact_${workItem.workItemId}_${randomUUID3()}`,
|
|
12040
12147
|
tool: toolName,
|
|
12041
12148
|
durationMs,
|
|
12042
12149
|
...sessionTelemetry,
|
|
@@ -12097,7 +12204,7 @@ ${toolResult.stderr}`);
|
|
|
12097
12204
|
const resultMutation = {
|
|
12098
12205
|
status: "completed",
|
|
12099
12206
|
runnerId,
|
|
12100
|
-
idempotencyKey: `issue_${workItem.workItemId}_${
|
|
12207
|
+
idempotencyKey: `issue_${workItem.workItemId}_${randomUUID3()}`,
|
|
12101
12208
|
diagnosis,
|
|
12102
12209
|
tool: toolName,
|
|
12103
12210
|
durationMs,
|
|
@@ -12131,7 +12238,7 @@ ${toolResult.stderr}`);
|
|
|
12131
12238
|
const failedMutation = {
|
|
12132
12239
|
status: "failed",
|
|
12133
12240
|
runnerId,
|
|
12134
|
-
idempotencyKey: `issue_${workItem.workItemId}_${
|
|
12241
|
+
idempotencyKey: `issue_${workItem.workItemId}_${randomUUID3()}`,
|
|
12135
12242
|
tool: toolName,
|
|
12136
12243
|
durationMs,
|
|
12137
12244
|
...sessionTelemetry,
|
|
@@ -12192,7 +12299,7 @@ ${toolResult.stderr}`);
|
|
|
12192
12299
|
const resultMutation = {
|
|
12193
12300
|
status: "completed",
|
|
12194
12301
|
runnerId,
|
|
12195
|
-
idempotencyKey: `security_${workItem.workItemId}_${
|
|
12302
|
+
idempotencyKey: `security_${workItem.workItemId}_${randomUUID3()}`,
|
|
12196
12303
|
result: scanResult,
|
|
12197
12304
|
tool: toolName,
|
|
12198
12305
|
durationMs,
|
|
@@ -12226,7 +12333,7 @@ ${toolResult.stderr}`);
|
|
|
12226
12333
|
const failedMutation = {
|
|
12227
12334
|
status: "failed",
|
|
12228
12335
|
runnerId,
|
|
12229
|
-
idempotencyKey: `security_${workItem.workItemId}_${
|
|
12336
|
+
idempotencyKey: `security_${workItem.workItemId}_${randomUUID3()}`,
|
|
12230
12337
|
tool: toolName,
|
|
12231
12338
|
durationMs,
|
|
12232
12339
|
...sessionTelemetry,
|
|
@@ -12287,7 +12394,7 @@ ${toolResult.stderr}`);
|
|
|
12287
12394
|
const resultMutation = {
|
|
12288
12395
|
status: "completed",
|
|
12289
12396
|
runnerId,
|
|
12290
|
-
idempotencyKey: `app_evaluation_${workItem.workItemId}_${
|
|
12397
|
+
idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID3()}`,
|
|
12291
12398
|
result: scanResult,
|
|
12292
12399
|
tool: toolName,
|
|
12293
12400
|
durationMs,
|
|
@@ -12321,7 +12428,7 @@ ${toolResult.stderr}`);
|
|
|
12321
12428
|
const failedMutation = {
|
|
12322
12429
|
status: "failed",
|
|
12323
12430
|
runnerId,
|
|
12324
|
-
idempotencyKey: `app_evaluation_${workItem.workItemId}_${
|
|
12431
|
+
idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID3()}`,
|
|
12325
12432
|
tool: toolName,
|
|
12326
12433
|
durationMs,
|
|
12327
12434
|
...sessionTelemetry,
|
|
@@ -12382,7 +12489,7 @@ ${toolResult.stderr}`);
|
|
|
12382
12489
|
const resultMutation = {
|
|
12383
12490
|
status: "completed",
|
|
12384
12491
|
runnerId,
|
|
12385
|
-
idempotencyKey: `brain_consolidation_${workItem.workItemId}_${
|
|
12492
|
+
idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID3()}`,
|
|
12386
12493
|
result: scanResult,
|
|
12387
12494
|
tool: toolName,
|
|
12388
12495
|
durationMs,
|
|
@@ -12416,7 +12523,7 @@ ${toolResult.stderr}`);
|
|
|
12416
12523
|
const failedMutation = {
|
|
12417
12524
|
status: "failed",
|
|
12418
12525
|
runnerId,
|
|
12419
|
-
idempotencyKey: `brain_consolidation_${workItem.workItemId}_${
|
|
12526
|
+
idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID3()}`,
|
|
12420
12527
|
tool: toolName,
|
|
12421
12528
|
durationMs,
|
|
12422
12529
|
...sessionTelemetry,
|
|
@@ -12478,7 +12585,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
12478
12585
|
const resultMutation = {
|
|
12479
12586
|
status: "completed",
|
|
12480
12587
|
runnerId,
|
|
12481
|
-
idempotencyKey: `project_context_${workItem.workItemId}_${
|
|
12588
|
+
idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID3()}`,
|
|
12482
12589
|
result: refreshResult,
|
|
12483
12590
|
tool: toolName,
|
|
12484
12591
|
durationMs,
|
|
@@ -12524,7 +12631,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
12524
12631
|
const failedMutation = {
|
|
12525
12632
|
status: "failed",
|
|
12526
12633
|
runnerId,
|
|
12527
|
-
idempotencyKey: `project_context_${workItem.workItemId}_${
|
|
12634
|
+
idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID3()}`,
|
|
12528
12635
|
tool: toolName,
|
|
12529
12636
|
durationMs,
|
|
12530
12637
|
...sessionTelemetry,
|
|
@@ -12585,7 +12692,7 @@ ${toolResult.stderr}`);
|
|
|
12585
12692
|
const resultMutation = {
|
|
12586
12693
|
status: "completed",
|
|
12587
12694
|
runnerId,
|
|
12588
|
-
idempotencyKey: `implementation_verification_${workItem.workItemId}_${
|
|
12695
|
+
idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID3()}`,
|
|
12589
12696
|
result: verificationResult,
|
|
12590
12697
|
tool: toolName,
|
|
12591
12698
|
durationMs,
|
|
@@ -12619,7 +12726,7 @@ ${toolResult.stderr}`);
|
|
|
12619
12726
|
const failedMutation = {
|
|
12620
12727
|
status: "failed",
|
|
12621
12728
|
runnerId,
|
|
12622
|
-
idempotencyKey: `implementation_verification_${workItem.workItemId}_${
|
|
12729
|
+
idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID3()}`,
|
|
12623
12730
|
tool: toolName,
|
|
12624
12731
|
durationMs,
|
|
12625
12732
|
...sessionTelemetry,
|
|
@@ -12680,7 +12787,7 @@ ${toolResult.stderr}`);
|
|
|
12680
12787
|
const resultMutation = {
|
|
12681
12788
|
status: "completed",
|
|
12682
12789
|
runnerId,
|
|
12683
|
-
idempotencyKey: `test_quality_${workItem.workItemId}_${
|
|
12790
|
+
idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID3()}`,
|
|
12684
12791
|
result: scanResult,
|
|
12685
12792
|
tool: toolName,
|
|
12686
12793
|
durationMs,
|
|
@@ -12714,7 +12821,7 @@ ${toolResult.stderr}`);
|
|
|
12714
12821
|
const failedMutation = {
|
|
12715
12822
|
status: "failed",
|
|
12716
12823
|
runnerId,
|
|
12717
|
-
idempotencyKey: `test_quality_${workItem.workItemId}_${
|
|
12824
|
+
idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID3()}`,
|
|
12718
12825
|
tool: toolName,
|
|
12719
12826
|
durationMs,
|
|
12720
12827
|
...sessionTelemetry,
|
|
@@ -12775,7 +12882,7 @@ ${toolResult.stderr}`);
|
|
|
12775
12882
|
const resultMutation = {
|
|
12776
12883
|
status: "completed",
|
|
12777
12884
|
runnerId,
|
|
12778
|
-
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${
|
|
12885
|
+
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID3()}`,
|
|
12779
12886
|
result: gateResult,
|
|
12780
12887
|
tool: toolName,
|
|
12781
12888
|
durationMs,
|
|
@@ -12809,7 +12916,7 @@ ${toolResult.stderr}`);
|
|
|
12809
12916
|
const failedMutation = {
|
|
12810
12917
|
status: "failed",
|
|
12811
12918
|
runnerId,
|
|
12812
|
-
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${
|
|
12919
|
+
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID3()}`,
|
|
12813
12920
|
tool: toolName,
|
|
12814
12921
|
durationMs,
|
|
12815
12922
|
...sessionTelemetry,
|
|
@@ -12954,6 +13061,14 @@ async function loadPairedApiContext(root, apiUrl) {
|
|
|
12954
13061
|
})
|
|
12955
13062
|
};
|
|
12956
13063
|
}
|
|
13064
|
+
function runnerIdentityScope(metadata) {
|
|
13065
|
+
return {
|
|
13066
|
+
accountId: metadata.amistioAccountId,
|
|
13067
|
+
projectId: metadata.amistioProjectId,
|
|
13068
|
+
repositoryLinkId: metadata.repositoryLinkId,
|
|
13069
|
+
machineId: runnerMachineId()
|
|
13070
|
+
};
|
|
13071
|
+
}
|
|
12957
13072
|
async function loadProjectNextAction(apiClient, projectId, repositoryLinkId, root) {
|
|
12958
13073
|
const [{ workItems }, { documents }, { runners }] = await Promise.all([
|
|
12959
13074
|
apiClient.listWorkItems(projectId),
|
|
@@ -13038,7 +13153,7 @@ async function prepareToolSession({
|
|
|
13038
13153
|
});
|
|
13039
13154
|
return { ...selection, toolSession: toolSession2 };
|
|
13040
13155
|
}
|
|
13041
|
-
const toolSessionId = `tool_session_${
|
|
13156
|
+
const toolSessionId = `tool_session_${randomUUID3()}`;
|
|
13042
13157
|
const { toolSession } = await apiClient.createToolSession(projectId, {
|
|
13043
13158
|
toolSessionId,
|
|
13044
13159
|
repositoryLinkId,
|
|
@@ -13145,6 +13260,23 @@ function truncateLogExcerpt(value) {
|
|
|
13145
13260
|
function formatHostHelperCapability(label, support) {
|
|
13146
13261
|
return `${label}: ${support.supported ? "supported" : "unsupported"}${support.reason ? ` (${support.reason})` : ""}`;
|
|
13147
13262
|
}
|
|
13263
|
+
function harnessProviderCheckTargets() {
|
|
13264
|
+
return [
|
|
13265
|
+
{ label: "GitHub Models Direct", request: { providerId: "github-models", providerClientId: "github-models-api", routeType: "directProvider" } },
|
|
13266
|
+
{ label: "GitHub Copilot SDK", request: { providerId: "github-copilot", providerClientId: "github-copilot-sdk", routeType: "agentClient" } }
|
|
13267
|
+
];
|
|
13268
|
+
}
|
|
13269
|
+
function formatProviderAuthStatusLines(status) {
|
|
13270
|
+
const lines = [
|
|
13271
|
+
`Target: ${status.providerId}:${status.providerClientId}:${status.routeType}`,
|
|
13272
|
+
`Checked: ${status.checkedAt}`
|
|
13273
|
+
];
|
|
13274
|
+
if (status.authMethodLabel) lines.push(`Auth: ${status.authMethodLabel}`);
|
|
13275
|
+
if (status.modelCount !== void 0) lines.push(`Models: ${status.modelCount}`);
|
|
13276
|
+
if (status.message) lines.push(`Message: ${status.message}`);
|
|
13277
|
+
if (status.errorCode) lines.push(`Code: ${status.errorCode}`);
|
|
13278
|
+
return lines;
|
|
13279
|
+
}
|
|
13148
13280
|
function collectRepeatedOption(value, previous) {
|
|
13149
13281
|
return [...previous, value];
|
|
13150
13282
|
}
|
|
@@ -13171,7 +13303,7 @@ function parseReasoningEffort(value) {
|
|
|
13171
13303
|
throw new Error(`Expected reasoning effort auto, low, medium, high, or xhigh; received ${value}.`);
|
|
13172
13304
|
}
|
|
13173
13305
|
function inferRepoName(root) {
|
|
13174
|
-
return
|
|
13306
|
+
return path18.basename(path18.resolve(root)) || "repository";
|
|
13175
13307
|
}
|
|
13176
13308
|
function createRepoFingerprint(accountId, projectId, repositoryLinkId) {
|
|
13177
13309
|
return createHash9("sha256").update(`${accountId}:${projectId}:${repositoryLinkId}`).digest("hex");
|
|
@@ -13448,7 +13580,7 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurr
|
|
|
13448
13580
|
return {
|
|
13449
13581
|
version: CLI_VERSION,
|
|
13450
13582
|
mode,
|
|
13451
|
-
hostname:
|
|
13583
|
+
hostname: os9.hostname(),
|
|
13452
13584
|
...runnerIsolationCapabilityMetadata(),
|
|
13453
13585
|
maxConcurrentWork: concurrencyMetadata.maxConcurrentWork,
|
|
13454
13586
|
activeClaimLaneIds: concurrencyMetadata.activeClaimLaneIds,
|
|
@@ -13473,7 +13605,7 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurr
|
|
|
13473
13605
|
};
|
|
13474
13606
|
}
|
|
13475
13607
|
function runnerMachineId() {
|
|
13476
|
-
return createHash9("sha256").update(`${
|
|
13608
|
+
return createHash9("sha256").update(`${os9.hostname()}:${os9.platform()}:${os9.arch()}`).digest("hex").slice(0, 20);
|
|
13477
13609
|
}
|
|
13478
13610
|
async function delay(milliseconds) {
|
|
13479
13611
|
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|