@amistio/cli 0.1.37 → 0.1.39
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 -3
- package/dist/index.js +365 -203
- 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
|
|
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
|
-
For enterprise setup, use the web Runner panel as the primary guide. It shows repository, pairing code, AI provider, local runner, and
|
|
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
|
|
|
@@ -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,18 +57,21 @@ 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
|
|
|
@@ -83,6 +89,8 @@ 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
|
|
|
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.
|
|
93
|
+
|
|
86
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.
|
|
87
95
|
|
|
88
96
|
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
|
@@ -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
|
|
@@ -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;
|
|
@@ -3142,8 +3145,8 @@ var toolSessionMutationSchema = z3.object({
|
|
|
3142
3145
|
});
|
|
3143
3146
|
function resolveApiUrl(apiUrl, urlPath) {
|
|
3144
3147
|
const base = apiUrl.endsWith("/") ? apiUrl.slice(0, -1) : apiUrl;
|
|
3145
|
-
const
|
|
3146
|
-
return new URL(`${base}${
|
|
3148
|
+
const path19 = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
|
|
3149
|
+
return new URL(`${base}${path19}`);
|
|
3147
3150
|
}
|
|
3148
3151
|
|
|
3149
3152
|
// src/orchestrator.ts
|
|
@@ -5491,12 +5494,143 @@ async function readRunnerDaemonMetadataFile(filePath) {
|
|
|
5491
5494
|
}
|
|
5492
5495
|
}
|
|
5493
5496
|
|
|
5494
|
-
// src/runner-
|
|
5495
|
-
import {
|
|
5496
|
-
import {
|
|
5497
|
-
import { mkdir as mkdir8, readFile as readFile7, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
|
|
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";
|
|
5498
5500
|
import os5 from "node:os";
|
|
5499
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
|
+
|
|
5593
|
+
// src/runner-watch-errors.ts
|
|
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.";
|
|
5595
|
+
async function handleRunnerWatchError(options) {
|
|
5596
|
+
const consoleError = options.consoleError ?? console.error;
|
|
5597
|
+
if (isForgottenRunnerApiError(options.error)) {
|
|
5598
|
+
if (options.verbose && options.detail) {
|
|
5599
|
+
consoleError(`${forgottenRunnerWatchMessage}
|
|
5600
|
+
${options.detail}`);
|
|
5601
|
+
} else {
|
|
5602
|
+
consoleError(forgottenRunnerWatchMessage);
|
|
5603
|
+
}
|
|
5604
|
+
return { status: "failed", exitCode: 1, message: forgottenRunnerWatchMessage, stopRunner: true };
|
|
5605
|
+
}
|
|
5606
|
+
const message = "Runner hit an error while watching and will keep listening.";
|
|
5607
|
+
if (options.verbose) {
|
|
5608
|
+
consoleError(`${message}
|
|
5609
|
+
${options.detail}`);
|
|
5610
|
+
} else {
|
|
5611
|
+
consoleError(`${message} Run with --verbose for details.`);
|
|
5612
|
+
}
|
|
5613
|
+
const settlements = await Promise.allSettled([
|
|
5614
|
+
options.client.sendRunnerHeartbeat(options.projectId, options.runnerId, options.repositoryLinkId, "blocked", { ...options.heartbeatMetadata, preferenceMessage: message }),
|
|
5615
|
+
options.client.recordRunnerLog(options.projectId, {
|
|
5616
|
+
runnerId: options.runnerId,
|
|
5617
|
+
repositoryLinkId: options.repositoryLinkId,
|
|
5618
|
+
status: "failed",
|
|
5619
|
+
message,
|
|
5620
|
+
error: options.detail,
|
|
5621
|
+
machineId: options.machineId
|
|
5622
|
+
})
|
|
5623
|
+
]);
|
|
5624
|
+
options.logRejectedSettlements("record watch error", settlements);
|
|
5625
|
+
return { status: "failed", exitCode: 1, message };
|
|
5626
|
+
}
|
|
5627
|
+
|
|
5628
|
+
// src/runner-service.ts
|
|
5629
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
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";
|
|
5500
5634
|
function detectRunnerServicePlatform(platform = process.platform) {
|
|
5501
5635
|
if (platform === "darwin") return "launchd";
|
|
5502
5636
|
if (platform === "linux") return "systemd";
|
|
@@ -5507,19 +5641,19 @@ function createRunnerServiceDescriptor(input) {
|
|
|
5507
5641
|
if (platform === "unsupported") {
|
|
5508
5642
|
throw new Error("Startup services are supported for user-level launchd on macOS and systemd user services on Linux.");
|
|
5509
5643
|
}
|
|
5510
|
-
const homeDir = input.homeDir ??
|
|
5644
|
+
const homeDir = input.homeDir ?? os6.homedir();
|
|
5511
5645
|
const serviceName = runnerServiceName(input);
|
|
5512
5646
|
const serviceFilePath = runnerServiceFilePath(platform, serviceName, homeDir);
|
|
5513
5647
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5514
5648
|
const command = [input.executablePath ?? process.execPath, input.scriptPath ?? process.argv[1], ...input.args];
|
|
5515
|
-
const logPath =
|
|
5649
|
+
const logPath = path10.join(input.metadataDir ?? defaultRunnerMetadataDir(), `${runnerServiceKey(input)}.service.log`);
|
|
5516
5650
|
const metadata = {
|
|
5517
5651
|
schemaVersion: 1,
|
|
5518
5652
|
accountId: input.accountId,
|
|
5519
5653
|
projectId: input.projectId,
|
|
5520
5654
|
repositoryLinkId: input.repositoryLinkId,
|
|
5521
5655
|
runnerId: input.runnerId,
|
|
5522
|
-
rootDir:
|
|
5656
|
+
rootDir: path10.resolve(input.rootDir),
|
|
5523
5657
|
apiUrl: input.apiUrl,
|
|
5524
5658
|
serviceName,
|
|
5525
5659
|
serviceFilePath,
|
|
@@ -5536,9 +5670,9 @@ function createRunnerServiceDescriptor(input) {
|
|
|
5536
5670
|
}
|
|
5537
5671
|
async function installRunnerService(input, options = {}) {
|
|
5538
5672
|
const descriptor = createRunnerServiceDescriptor(input);
|
|
5539
|
-
await
|
|
5540
|
-
await
|
|
5541
|
-
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 });
|
|
5542
5676
|
await writeRunnerServiceMetadata(descriptor.metadata, input.metadataDir);
|
|
5543
5677
|
if (options.activate !== false) {
|
|
5544
5678
|
const activation = await activateRunnerService(descriptor.metadata);
|
|
@@ -5560,7 +5694,7 @@ async function removeRunnerService(input) {
|
|
|
5560
5694
|
}
|
|
5561
5695
|
async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetadataDir()) {
|
|
5562
5696
|
try {
|
|
5563
|
-
const parsed = JSON.parse(await
|
|
5697
|
+
const parsed = JSON.parse(await readFile8(runnerServiceMetadataPath(input, metadataDir), "utf8"));
|
|
5564
5698
|
if (parsed.schemaVersion !== 1 || !parsed.serviceName || !parsed.serviceFilePath) {
|
|
5565
5699
|
return void 0;
|
|
5566
5700
|
}
|
|
@@ -5570,8 +5704,8 @@ async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetad
|
|
|
5570
5704
|
}
|
|
5571
5705
|
}
|
|
5572
5706
|
async function writeRunnerServiceMetadata(metadata, metadataDir = defaultRunnerMetadataDir()) {
|
|
5573
|
-
await
|
|
5574
|
-
await
|
|
5707
|
+
await mkdir9(metadataDir, { recursive: true });
|
|
5708
|
+
await writeFile9(runnerServiceMetadataPath(metadata, metadataDir), JSON.stringify(metadata, null, 2), { encoding: "utf8", mode: 384 });
|
|
5575
5709
|
}
|
|
5576
5710
|
async function runnerServiceRuntimeStatus(metadata) {
|
|
5577
5711
|
if (metadata.platform === "launchd") {
|
|
@@ -5654,18 +5788,18 @@ WantedBy=default.target
|
|
|
5654
5788
|
}
|
|
5655
5789
|
function runnerServiceFilePath(platform, serviceName, homeDir) {
|
|
5656
5790
|
if (platform === "launchd") {
|
|
5657
|
-
return
|
|
5791
|
+
return path10.join(homeDir, "Library", "LaunchAgents", `${serviceName}.plist`);
|
|
5658
5792
|
}
|
|
5659
|
-
return
|
|
5793
|
+
return path10.join(homeDir, ".config", "systemd", "user", `${serviceName}.service`);
|
|
5660
5794
|
}
|
|
5661
5795
|
function runnerServiceMetadataPath(input, metadataDir) {
|
|
5662
|
-
return
|
|
5796
|
+
return path10.join(metadataDir, `${runnerServiceKey(input)}.service.json`);
|
|
5663
5797
|
}
|
|
5664
5798
|
function runnerServiceName(input) {
|
|
5665
5799
|
return `com.amistio.runner.${runnerServiceKey(input).slice(0, 20)}`;
|
|
5666
5800
|
}
|
|
5667
5801
|
function runnerServiceKey(input) {
|
|
5668
|
-
return
|
|
5802
|
+
return createHash5("sha256").update(`${input.accountId}:${input.projectId}:${input.repositoryLinkId}:${input.runnerId}`).digest("hex");
|
|
5669
5803
|
}
|
|
5670
5804
|
function launchdDomain() {
|
|
5671
5805
|
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
@@ -5993,9 +6127,9 @@ function createSmokeSession({ now, status, lastActivityAt }) {
|
|
|
5993
6127
|
|
|
5994
6128
|
// src/sync.ts
|
|
5995
6129
|
import { execFile as execFile3 } from "node:child_process";
|
|
5996
|
-
import { createHash as
|
|
5997
|
-
import { mkdir as
|
|
5998
|
-
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";
|
|
5999
6133
|
import { promisify as promisify3 } from "node:util";
|
|
6000
6134
|
var execFileAsync3 = promisify3(execFile3);
|
|
6001
6135
|
var legacySyncRoots = ["architecture", "context", "decisions", "features", "memory", "plans", "prompts", "workflows"];
|
|
@@ -6091,7 +6225,7 @@ async function readLocalSyncedDocuments(rootDir) {
|
|
|
6091
6225
|
const documentFiles = await findBrainDocumentFiles(rootDir);
|
|
6092
6226
|
const documents = [];
|
|
6093
6227
|
for (const fullPath of documentFiles) {
|
|
6094
|
-
const raw = await
|
|
6228
|
+
const raw = await readFile9(fullPath, "utf8");
|
|
6095
6229
|
const repoPath = toRepoPath(rootDir, fullPath);
|
|
6096
6230
|
const parsed = parseSyncedDocument(raw, repoPath);
|
|
6097
6231
|
if (!parsed) {
|
|
@@ -6141,8 +6275,8 @@ async function materializeBrainDocuments(rootDir, documents, options = {}) {
|
|
|
6141
6275
|
result.skipped.push(document.repoPath);
|
|
6142
6276
|
continue;
|
|
6143
6277
|
}
|
|
6144
|
-
await
|
|
6145
|
-
await
|
|
6278
|
+
await mkdir10(path11.dirname(fullPath), { recursive: true });
|
|
6279
|
+
await writeFile10(fullPath, createSyncedDocumentContent(document), "utf8");
|
|
6146
6280
|
result.written.push(document.repoPath);
|
|
6147
6281
|
}
|
|
6148
6282
|
return result;
|
|
@@ -6271,7 +6405,7 @@ function parseSyncedHtml(content) {
|
|
|
6271
6405
|
}
|
|
6272
6406
|
async function readExistingSyncedDocument(fullPath) {
|
|
6273
6407
|
try {
|
|
6274
|
-
const raw = await
|
|
6408
|
+
const raw = await readFile9(fullPath, "utf8");
|
|
6275
6409
|
const parsed = parseSyncedDocument(raw, fullPath);
|
|
6276
6410
|
if (!parsed) {
|
|
6277
6411
|
return { exists: true };
|
|
@@ -6296,7 +6430,7 @@ async function readExistingSyncedDocument(fullPath) {
|
|
|
6296
6430
|
async function findBrainDocumentFiles(rootDir) {
|
|
6297
6431
|
const files = [];
|
|
6298
6432
|
for (const syncRoot of [...syncRoots, htmlSyncRoot]) {
|
|
6299
|
-
const fullRoot =
|
|
6433
|
+
const fullRoot = path11.join(rootDir, syncRoot);
|
|
6300
6434
|
if (!await exists2(fullRoot)) {
|
|
6301
6435
|
continue;
|
|
6302
6436
|
}
|
|
@@ -6306,7 +6440,7 @@ async function findBrainDocumentFiles(rootDir) {
|
|
|
6306
6440
|
}
|
|
6307
6441
|
async function walkBrainDocumentFiles(directory, files) {
|
|
6308
6442
|
for (const entry of await readdir5(directory, { withFileTypes: true })) {
|
|
6309
|
-
const fullPath =
|
|
6443
|
+
const fullPath = path11.join(directory, entry.name);
|
|
6310
6444
|
if (entry.isDirectory()) {
|
|
6311
6445
|
await walkBrainDocumentFiles(fullPath, files);
|
|
6312
6446
|
} else if (entry.isFile() && /\.(md|mdx|html?)$/i.test(entry.name)) {
|
|
@@ -6315,23 +6449,23 @@ async function walkBrainDocumentFiles(directory, files) {
|
|
|
6315
6449
|
}
|
|
6316
6450
|
}
|
|
6317
6451
|
function safeRepoPath(rootDir, repoPath) {
|
|
6318
|
-
if (
|
|
6452
|
+
if (path11.isAbsolute(repoPath)) {
|
|
6319
6453
|
throw new Error(`Refusing to use absolute repo path: ${repoPath}`);
|
|
6320
6454
|
}
|
|
6321
|
-
const normalized =
|
|
6322
|
-
if (normalized === ".." || normalized.startsWith(`..${
|
|
6455
|
+
const normalized = path11.normalize(repoPath);
|
|
6456
|
+
if (normalized === ".." || normalized.startsWith(`..${path11.sep}`)) {
|
|
6323
6457
|
throw new Error(`Refusing to use path outside the repository: ${repoPath}`);
|
|
6324
6458
|
}
|
|
6325
|
-
const root =
|
|
6326
|
-
const fullPath =
|
|
6327
|
-
if (!fullPath.startsWith(`${root}${
|
|
6459
|
+
const root = path11.resolve(rootDir);
|
|
6460
|
+
const fullPath = path11.resolve(root, normalized);
|
|
6461
|
+
if (!fullPath.startsWith(`${root}${path11.sep}`)) {
|
|
6328
6462
|
throw new Error(`Refusing to use path outside the repository: ${repoPath}`);
|
|
6329
6463
|
}
|
|
6330
6464
|
return fullPath;
|
|
6331
6465
|
}
|
|
6332
6466
|
function isControlPlanePath(repoPath) {
|
|
6333
|
-
const normalized =
|
|
6334
|
-
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}`);
|
|
6335
6469
|
}
|
|
6336
6470
|
function canonicalControlPlaneRepoPath(repoPath) {
|
|
6337
6471
|
const normalized = repoPath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
@@ -6342,16 +6476,16 @@ function canonicalControlPlaneRepoPath(repoPath) {
|
|
|
6342
6476
|
return normalized;
|
|
6343
6477
|
}
|
|
6344
6478
|
function toRepoPath(rootDir, fullPath) {
|
|
6345
|
-
return
|
|
6479
|
+
return path11.relative(rootDir, fullPath).split(path11.sep).join("/");
|
|
6346
6480
|
}
|
|
6347
6481
|
function inferTitle(content, repoPath) {
|
|
6348
6482
|
const heading = content.split("\n").find((line) => line.startsWith("# "))?.replace(/^#\s+/, "").trim();
|
|
6349
6483
|
if (heading) return heading;
|
|
6350
6484
|
const htmlHeading = content.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
|
|
6351
|
-
return htmlHeading ||
|
|
6485
|
+
return htmlHeading || path11.basename(repoPath, path11.extname(repoPath));
|
|
6352
6486
|
}
|
|
6353
6487
|
async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingDocuments, options) {
|
|
6354
|
-
const root =
|
|
6488
|
+
const root = path11.resolve(rootDir);
|
|
6355
6489
|
const maxBytes = (options.maxFileKb ?? defaultAutoSyncMaxFileKb) * 1024;
|
|
6356
6490
|
const syncedAt = options.syncedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
6357
6491
|
const existingById = new Map(existingDocuments.map((document) => [document.documentId, document]));
|
|
@@ -6376,7 +6510,7 @@ async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingD
|
|
|
6376
6510
|
skipped.push({ repoPath: normalizedRepoPath, reason: "tooLarge" });
|
|
6377
6511
|
continue;
|
|
6378
6512
|
}
|
|
6379
|
-
const content = await
|
|
6513
|
+
const content = await readFile9(fullPath, "utf8").catch(() => void 0);
|
|
6380
6514
|
if (content === void 0) {
|
|
6381
6515
|
skipped.push({ repoPath: normalizedRepoPath, reason: "unreadable" });
|
|
6382
6516
|
continue;
|
|
@@ -6441,7 +6575,7 @@ async function listAutoSyncCandidatePaths(rootDir) {
|
|
|
6441
6575
|
}
|
|
6442
6576
|
const files = [];
|
|
6443
6577
|
for (const syncRoot of [...syncRoots, htmlSyncRoot, ...legacySyncRoots]) {
|
|
6444
|
-
const fullRoot =
|
|
6578
|
+
const fullRoot = path11.join(rootDir, syncRoot);
|
|
6445
6579
|
if (await exists2(fullRoot)) {
|
|
6446
6580
|
await walkAutoSyncFiles(rootDir, fullRoot, files);
|
|
6447
6581
|
}
|
|
@@ -6450,8 +6584,8 @@ async function listAutoSyncCandidatePaths(rootDir) {
|
|
|
6450
6584
|
}
|
|
6451
6585
|
async function walkAutoSyncFiles(rootDir, directory, files) {
|
|
6452
6586
|
for (const entry of await readdir5(directory, { withFileTypes: true }).catch(() => [])) {
|
|
6453
|
-
const fullPath =
|
|
6454
|
-
const repoPath = normalizeRepoPath3(
|
|
6587
|
+
const fullPath = path11.join(directory, entry.name);
|
|
6588
|
+
const repoPath = normalizeRepoPath3(path11.relative(rootDir, fullPath));
|
|
6455
6589
|
if (entry.isDirectory()) {
|
|
6456
6590
|
if (!autoSyncExcludedDirectoryNames.has(entry.name)) {
|
|
6457
6591
|
await walkAutoSyncFiles(rootDir, fullPath, files);
|
|
@@ -6485,7 +6619,7 @@ function legacyDocumentTypeForRepoPath(repoPath) {
|
|
|
6485
6619
|
return root && root in documentTypeByRoot ? documentTypeByRoot[root] : void 0;
|
|
6486
6620
|
}
|
|
6487
6621
|
function stableExternalDocumentId(metadata, repoPath) {
|
|
6488
|
-
return `doc_external_${
|
|
6622
|
+
return `doc_external_${createHash6("sha256").update(`${metadata.amistioAccountId}:${metadata.amistioProjectId}:${metadata.repositoryLinkId}:${repoPath}`).digest("hex").slice(0, 24)}`;
|
|
6489
6623
|
}
|
|
6490
6624
|
function normalizeRepoPath3(repoPath) {
|
|
6491
6625
|
return repoPath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
@@ -6513,9 +6647,9 @@ async function exists2(filePath) {
|
|
|
6513
6647
|
}
|
|
6514
6648
|
|
|
6515
6649
|
// src/tool-session-store.ts
|
|
6516
|
-
import { mkdir as
|
|
6517
|
-
import
|
|
6518
|
-
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";
|
|
6519
6653
|
var LocalToolSessionStore = class {
|
|
6520
6654
|
constructor(filePath = defaultSessionStorePath()) {
|
|
6521
6655
|
this.filePath = filePath;
|
|
@@ -6529,12 +6663,12 @@ var LocalToolSessionStore = class {
|
|
|
6529
6663
|
async setProviderSessionId(toolSessionId, toolName, providerSessionId) {
|
|
6530
6664
|
const data = await this.read();
|
|
6531
6665
|
data[toolSessionId] = { toolName, providerSessionId, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6532
|
-
await
|
|
6533
|
-
await
|
|
6666
|
+
await mkdir11(path12.dirname(this.filePath), { recursive: true });
|
|
6667
|
+
await writeFile11(this.filePath, JSON.stringify(data, null, 2), "utf8");
|
|
6534
6668
|
}
|
|
6535
6669
|
async read() {
|
|
6536
6670
|
try {
|
|
6537
|
-
return JSON.parse(await
|
|
6671
|
+
return JSON.parse(await readFile10(this.filePath, "utf8"));
|
|
6538
6672
|
} catch {
|
|
6539
6673
|
return {};
|
|
6540
6674
|
}
|
|
@@ -6542,16 +6676,16 @@ var LocalToolSessionStore = class {
|
|
|
6542
6676
|
};
|
|
6543
6677
|
function defaultSessionStorePath() {
|
|
6544
6678
|
if (process.platform === "darwin") {
|
|
6545
|
-
return
|
|
6679
|
+
return path12.join(os7.homedir(), "Library", "Application Support", "Amistio", "tool-sessions.json");
|
|
6546
6680
|
}
|
|
6547
6681
|
if (process.platform === "win32") {
|
|
6548
|
-
return
|
|
6682
|
+
return path12.join(process.env.APPDATA ?? os7.homedir(), "Amistio", "tool-sessions.json");
|
|
6549
6683
|
}
|
|
6550
|
-
return
|
|
6684
|
+
return path12.join(process.env.XDG_STATE_HOME ?? path12.join(os7.homedir(), ".local", "state"), "amistio", "tool-sessions.json");
|
|
6551
6685
|
}
|
|
6552
6686
|
|
|
6553
6687
|
// src/work-runner.ts
|
|
6554
|
-
import
|
|
6688
|
+
import path13 from "node:path";
|
|
6555
6689
|
var generationResultStart = "AMISTIO_BRAIN_GENERATION_RESULT_START";
|
|
6556
6690
|
var generationResultEnd = "AMISTIO_BRAIN_GENERATION_RESULT_END";
|
|
6557
6691
|
var assistantAnswerStart = "AMISTIO_ASSISTANT_ANSWER_START";
|
|
@@ -8034,15 +8168,15 @@ function normalizeProjectContextRepoPath(value, options) {
|
|
|
8034
8168
|
if (!trimmed || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed) || /^file:/i.test(trimmed) || /^[A-Za-z]:($|[^\\/])/.test(trimmed)) {
|
|
8035
8169
|
throwUnsafeProjectContextPath();
|
|
8036
8170
|
}
|
|
8037
|
-
const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") ||
|
|
8171
|
+
const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") || path13.isAbsolute(trimmed) || path13.win32.isAbsolute(trimmed);
|
|
8038
8172
|
if (!absolute) {
|
|
8039
8173
|
return normalizeRelativeProjectContextPath(trimmed);
|
|
8040
8174
|
}
|
|
8041
8175
|
if (!options.repositoryRoot) {
|
|
8042
8176
|
throwUnsafeProjectContextPath();
|
|
8043
8177
|
}
|
|
8044
|
-
const useWindowsPathRules =
|
|
8045
|
-
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));
|
|
8046
8180
|
return normalizeRelativeProjectContextPath(relativePath);
|
|
8047
8181
|
}
|
|
8048
8182
|
function normalizeRelativeProjectContextPath(value) {
|
|
@@ -8093,48 +8227,16 @@ function stripJsonFence(value) {
|
|
|
8093
8227
|
return trimmed.replace(/^```(?:json)?\s*/i, "").replace(/```$/i, "").trim();
|
|
8094
8228
|
}
|
|
8095
8229
|
|
|
8096
|
-
// src/runner-status.ts
|
|
8097
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
8098
|
-
var watchStateReminderMs = 60 * 1e3;
|
|
8099
|
-
function formatWatchStartupContext(input) {
|
|
8100
|
-
return [
|
|
8101
|
-
`Runner ${input.runnerId} is watching project ${input.projectId}.`,
|
|
8102
|
-
`Repository link: ${input.repositoryLinkId}`,
|
|
8103
|
-
`API: ${input.apiUrl}`,
|
|
8104
|
-
`Polling interval: ${input.intervalSeconds}s. Press Ctrl+C to stop.`
|
|
8105
|
-
];
|
|
8106
|
-
}
|
|
8107
|
-
function formatWatchIdleLine(action, intervalSeconds) {
|
|
8108
|
-
return `${formatProjectNextAction(action)} Checking again in ${intervalSeconds}s.`;
|
|
8109
|
-
}
|
|
8110
|
-
function shouldPrintWatchState(action, previous, nowMs, reminderMs = watchStateReminderMs) {
|
|
8111
|
-
const key = watchStateKey(action);
|
|
8112
|
-
if (!previous || previous.key !== key) {
|
|
8113
|
-
return true;
|
|
8114
|
-
}
|
|
8115
|
-
if (action.kind === "workCompleted") {
|
|
8116
|
-
return false;
|
|
8117
|
-
}
|
|
8118
|
-
return nowMs - previous.printedAtMs >= reminderMs;
|
|
8119
|
-
}
|
|
8120
|
-
function watchStateKey(action) {
|
|
8121
|
-
return [action.kind, action.message, action.workItemId, action.documentId, action.runnerId].filter(Boolean).join(":");
|
|
8122
|
-
}
|
|
8123
|
-
function stableRunnerId(input) {
|
|
8124
|
-
const digest = createHash6("sha256").update(`${input.accountId}:${input.projectId}:${input.repositoryLinkId}:${input.machineId}`).digest("hex").slice(0, 20);
|
|
8125
|
-
return `runner_${digest}`;
|
|
8126
|
-
}
|
|
8127
|
-
|
|
8128
8230
|
// src/runner-resources.ts
|
|
8129
|
-
import
|
|
8231
|
+
import os8 from "node:os";
|
|
8130
8232
|
var defaultRuntime = {
|
|
8131
8233
|
nowMs: () => Date.now(),
|
|
8132
8234
|
memoryUsage: () => process.memoryUsage(),
|
|
8133
8235
|
uptime: () => process.uptime(),
|
|
8134
8236
|
cpuUsage: () => process.cpuUsage(),
|
|
8135
|
-
totalmem: () =>
|
|
8136
|
-
freemem: () =>
|
|
8137
|
-
loadavg: () =>
|
|
8237
|
+
totalmem: () => os8.totalmem(),
|
|
8238
|
+
freemem: () => os8.freemem(),
|
|
8239
|
+
loadavg: () => os8.loadavg()
|
|
8138
8240
|
};
|
|
8139
8241
|
var previousRunnerResourceSample;
|
|
8140
8242
|
function sampleCurrentRunnerResourceUsage() {
|
|
@@ -8247,8 +8349,8 @@ function roundNumber(value, digits) {
|
|
|
8247
8349
|
// src/importer.ts
|
|
8248
8350
|
import { execFile as execFile4 } from "node:child_process";
|
|
8249
8351
|
import { createHash as createHash7 } from "node:crypto";
|
|
8250
|
-
import { readdir as readdir6, readFile as
|
|
8251
|
-
import
|
|
8352
|
+
import { readdir as readdir6, readFile as readFile11, stat as stat5 } from "node:fs/promises";
|
|
8353
|
+
import path14 from "node:path";
|
|
8252
8354
|
import { promisify as promisify4 } from "node:util";
|
|
8253
8355
|
var execFileAsync4 = promisify4(execFile4);
|
|
8254
8356
|
var defaultMaxFileKb = 256;
|
|
@@ -8269,12 +8371,12 @@ var documentFolderByType = {
|
|
|
8269
8371
|
workflow: "docs/workflows"
|
|
8270
8372
|
};
|
|
8271
8373
|
async function inspectLocalRepository(rootDir, defaultBranch) {
|
|
8272
|
-
const requestedRoot =
|
|
8374
|
+
const requestedRoot = path14.resolve(rootDir);
|
|
8273
8375
|
const root = await runGit2(["-C", requestedRoot, "rev-parse", "--show-toplevel"]).catch(() => requestedRoot);
|
|
8274
8376
|
const detectedBranch = await runGit2(["-C", root, "symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => defaultBranch);
|
|
8275
8377
|
const originUrl = await runGit2(["-C", root, "remote", "get-url", "origin"]).catch(() => void 0);
|
|
8276
8378
|
const parsedCloneUrl = originUrl ? parseOptionalOriginCloneUrl(originUrl) : void 0;
|
|
8277
|
-
const repoName = (parsedCloneUrl?.repoName ??
|
|
8379
|
+
const repoName = (parsedCloneUrl?.repoName ?? path14.basename(root)) || "repository";
|
|
8278
8380
|
const fingerprintSeed = parsedCloneUrl ? `origin:${parsedCloneUrl.normalizedKey}` : `repo:${repoName}:${detectedBranch || defaultBranch}`;
|
|
8279
8381
|
return {
|
|
8280
8382
|
rootDir: root,
|
|
@@ -8286,7 +8388,7 @@ async function inspectLocalRepository(rootDir, defaultBranch) {
|
|
|
8286
8388
|
};
|
|
8287
8389
|
}
|
|
8288
8390
|
async function scanLegacyDocuments(options) {
|
|
8289
|
-
const rootDir =
|
|
8391
|
+
const rootDir = path14.resolve(options.rootDir);
|
|
8290
8392
|
const maxBytes = (options.maxFileKb ?? defaultMaxFileKb) * 1024;
|
|
8291
8393
|
const skipped = [];
|
|
8292
8394
|
const candidates = [];
|
|
@@ -8306,7 +8408,7 @@ async function scanLegacyDocuments(options) {
|
|
|
8306
8408
|
skipped.push({ repoPath, reason: "excluded" });
|
|
8307
8409
|
continue;
|
|
8308
8410
|
}
|
|
8309
|
-
const fullPath =
|
|
8411
|
+
const fullPath = path14.join(rootDir, ...repoPath.split("/"));
|
|
8310
8412
|
const fileStat = await stat5(fullPath).catch(() => void 0);
|
|
8311
8413
|
if (!fileStat?.isFile()) {
|
|
8312
8414
|
skipped.push({ repoPath, reason: "unreadable" });
|
|
@@ -8316,7 +8418,7 @@ async function scanLegacyDocuments(options) {
|
|
|
8316
8418
|
skipped.push({ repoPath, reason: "tooLarge" });
|
|
8317
8419
|
continue;
|
|
8318
8420
|
}
|
|
8319
|
-
const content = await
|
|
8421
|
+
const content = await readFile11(fullPath, "utf8").catch(() => void 0);
|
|
8320
8422
|
if (content === void 0) {
|
|
8321
8423
|
skipped.push({ repoPath, reason: "unreadable" });
|
|
8322
8424
|
continue;
|
|
@@ -8408,8 +8510,8 @@ async function listRepositoryPaths(rootDir) {
|
|
|
8408
8510
|
async function walkRepository(rootDir, directory, files) {
|
|
8409
8511
|
const entries = await readdir6(directory, { withFileTypes: true }).catch(() => []);
|
|
8410
8512
|
for (const entry of entries) {
|
|
8411
|
-
const fullPath =
|
|
8412
|
-
const repoPath = normalizeRepoPath4(
|
|
8513
|
+
const fullPath = path14.join(directory, entry.name);
|
|
8514
|
+
const repoPath = normalizeRepoPath4(path14.relative(rootDir, fullPath));
|
|
8413
8515
|
if (entry.isDirectory()) {
|
|
8414
8516
|
if (!excludedDirectoryNames.has(entry.name)) {
|
|
8415
8517
|
await walkRepository(rootDir, fullPath, files);
|
|
@@ -8477,9 +8579,9 @@ function uniqueDestinationPath(basePath, sourcePath, usedPaths) {
|
|
|
8477
8579
|
usedPaths.add(basePath);
|
|
8478
8580
|
return basePath;
|
|
8479
8581
|
}
|
|
8480
|
-
const extension =
|
|
8481
|
-
const directory =
|
|
8482
|
-
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);
|
|
8483
8585
|
const uniquePath = `${directory}/${basename}-${hashText(sourcePath, 8)}${extension}`;
|
|
8484
8586
|
usedPaths.add(uniquePath);
|
|
8485
8587
|
return uniquePath;
|
|
@@ -8552,7 +8654,7 @@ function inferTitle2(content, repoPath) {
|
|
|
8552
8654
|
if (heading) return heading;
|
|
8553
8655
|
const htmlHeading = body.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
|
|
8554
8656
|
if (htmlHeading) return htmlHeading;
|
|
8555
|
-
const basename =
|
|
8657
|
+
const basename = path14.posix.basename(repoPath, path14.posix.extname(repoPath)).replace(/[-_]+/g, " ").trim();
|
|
8556
8658
|
return titleCase(basename || "Imported Document");
|
|
8557
8659
|
}
|
|
8558
8660
|
function stripFrontmatter(content) {
|
|
@@ -8586,7 +8688,7 @@ async function runGit2(args) {
|
|
|
8586
8688
|
|
|
8587
8689
|
// src/runner-actions.ts
|
|
8588
8690
|
import { spawn as spawn4 } from "node:child_process";
|
|
8589
|
-
import
|
|
8691
|
+
import path15 from "node:path";
|
|
8590
8692
|
function buildBackgroundRunnerArgs(options) {
|
|
8591
8693
|
const args = [
|
|
8592
8694
|
"run",
|
|
@@ -8596,7 +8698,7 @@ function buildBackgroundRunnerArgs(options) {
|
|
|
8596
8698
|
"--runner-id",
|
|
8597
8699
|
options.runnerId,
|
|
8598
8700
|
"--root",
|
|
8599
|
-
|
|
8701
|
+
path15.resolve(options.root),
|
|
8600
8702
|
"--session",
|
|
8601
8703
|
options.session,
|
|
8602
8704
|
"--interval-seconds",
|
|
@@ -8714,8 +8816,8 @@ function truncateProcessOutput(value) {
|
|
|
8714
8816
|
|
|
8715
8817
|
// src/git-worktree.ts
|
|
8716
8818
|
import { execFile as execFile5 } from "node:child_process";
|
|
8717
|
-
import { copyFile, lstat, mkdir as
|
|
8718
|
-
import
|
|
8819
|
+
import { copyFile, lstat, mkdir as mkdir12, readdir as readdir7, stat as stat6 } from "node:fs/promises";
|
|
8820
|
+
import path16 from "node:path";
|
|
8719
8821
|
import { promisify as promisify5 } from "node:util";
|
|
8720
8822
|
var execFileAsync5 = promisify5(execFile5);
|
|
8721
8823
|
var exactLocalEnvironmentFiles = /* @__PURE__ */ new Set([".env", ".env.local", ".env.development", ".env.development.local", ".env.test", ".env.test.local", ".env.production", ".env.production.local"]);
|
|
@@ -8747,7 +8849,7 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
|
|
|
8747
8849
|
const preparedLocalEnvironmentFileCount2 = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
|
|
8748
8850
|
return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount2 ? { preparedLocalEnvironmentFileCount: preparedLocalEnvironmentFileCount2 } : {} };
|
|
8749
8851
|
}
|
|
8750
|
-
await
|
|
8852
|
+
await mkdir12(path16.dirname(worktreePath), { recursive: true });
|
|
8751
8853
|
const branchExists = await gitCommandSucceeds(repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${identity.branch}`]);
|
|
8752
8854
|
const worktreeArgs = branchExists ? ["worktree", "add", worktreePath, identity.branch] : ["worktree", "add", "-b", identity.branch, worktreePath, baseRevision];
|
|
8753
8855
|
await gitOutput(repoRoot, worktreeArgs).catch((error) => {
|
|
@@ -8765,9 +8867,9 @@ async function resolveExistingGitWorktreeIsolation(rootDir, workItem) {
|
|
|
8765
8867
|
return { ...identity, baseRevision, worktreePath };
|
|
8766
8868
|
}
|
|
8767
8869
|
function localWorktreePath(repoRoot, worktreeKey) {
|
|
8768
|
-
const repoName =
|
|
8870
|
+
const repoName = path16.basename(repoRoot);
|
|
8769
8871
|
const worktreeSlug = worktreeKey.split("/").filter(Boolean).pop() ?? "work";
|
|
8770
|
-
return
|
|
8872
|
+
return path16.join(path16.dirname(repoRoot), `${repoName}.worktrees`, worktreeSlug);
|
|
8771
8873
|
}
|
|
8772
8874
|
async function assertExistingWorktree(worktreePath, branch) {
|
|
8773
8875
|
await gitOutput(worktreePath, ["rev-parse", "--is-inside-work-tree"]);
|
|
@@ -8793,8 +8895,8 @@ async function prepareLocalWorktreeEnvironment(repoRoot, worktreePath) {
|
|
|
8793
8895
|
const candidates = await localEnvironmentFileCandidates(repoRoot);
|
|
8794
8896
|
let preparedCount = 0;
|
|
8795
8897
|
for (const candidate of candidates) {
|
|
8796
|
-
const sourcePath =
|
|
8797
|
-
const targetPath =
|
|
8898
|
+
const sourcePath = path16.join(repoRoot, candidate);
|
|
8899
|
+
const targetPath = path16.join(worktreePath, candidate);
|
|
8798
8900
|
if (await pathExists(targetPath)) {
|
|
8799
8901
|
continue;
|
|
8800
8902
|
}
|
|
@@ -8825,7 +8927,7 @@ async function localEnvironmentFileCandidates(repoRoot) {
|
|
|
8825
8927
|
if (!isRootFileName(name)) {
|
|
8826
8928
|
continue;
|
|
8827
8929
|
}
|
|
8828
|
-
const source = await lstat(
|
|
8930
|
+
const source = await lstat(path16.join(repoRoot, name)).catch(() => void 0);
|
|
8829
8931
|
if (source?.isFile()) {
|
|
8830
8932
|
candidates.push(name);
|
|
8831
8933
|
}
|
|
@@ -8836,7 +8938,7 @@ function isAllowedLocalEnvironmentFile(name) {
|
|
|
8836
8938
|
return exactLocalEnvironmentFiles.has(name) || localEnvironmentFilePattern.test(name);
|
|
8837
8939
|
}
|
|
8838
8940
|
function isRootFileName(name) {
|
|
8839
|
-
return name ===
|
|
8941
|
+
return name === path16.basename(name) && !name.includes("/") && !name.includes("\\");
|
|
8840
8942
|
}
|
|
8841
8943
|
async function gitOutput(cwd, args) {
|
|
8842
8944
|
const { stdout } = await execFileAsync5("git", args, { cwd, maxBuffer: 1024 * 1024 });
|
|
@@ -8869,7 +8971,7 @@ function safeFileError(error) {
|
|
|
8869
8971
|
|
|
8870
8972
|
// src/implementation-handoff.ts
|
|
8871
8973
|
import { execFile as execFile6 } from "node:child_process";
|
|
8872
|
-
import
|
|
8974
|
+
import path17 from "node:path";
|
|
8873
8975
|
import { promisify as promisify6 } from "node:util";
|
|
8874
8976
|
var execFileAsync6 = promisify6(execFile6);
|
|
8875
8977
|
async function completeImplementationHandoff(input) {
|
|
@@ -9137,7 +9239,7 @@ async function cleanupWorktree(run, input) {
|
|
|
9137
9239
|
return { status: "failed", message: "Cleanup skipped because the worktree is not clean after PR handoff." };
|
|
9138
9240
|
}
|
|
9139
9241
|
try {
|
|
9140
|
-
await gitOutput2(run, input.primaryRepoRoot ||
|
|
9242
|
+
await gitOutput2(run, input.primaryRepoRoot || path17.dirname(input.worktreePath), ["worktree", "remove", input.worktreePath]);
|
|
9141
9243
|
return { status: "completed" };
|
|
9142
9244
|
} catch (error) {
|
|
9143
9245
|
return { status: "failed", message: `Cleanup failed: ${safeErrorMessage(error)}` };
|
|
@@ -9858,7 +9960,7 @@ program.command("import").description("Pair an existing checkout and import lega
|
|
|
9858
9960
|
});
|
|
9859
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) => {
|
|
9860
9962
|
const pairingRoot = await resolvePairingRoot(options.root, { explicitRoot: command.getOptionValueSource("root") === "cli" });
|
|
9861
|
-
let repositoryLinkId = options.repositoryLink ?? `repo_${
|
|
9963
|
+
let repositoryLinkId = options.repositoryLink ?? `repo_${randomUUID3()}`;
|
|
9862
9964
|
let credential = options.token;
|
|
9863
9965
|
if (options.pairingCode) {
|
|
9864
9966
|
const pairing = await new ApiClient({
|
|
@@ -10044,7 +10146,7 @@ work.command("prompt").description("Print or write an approved work prompt witho
|
|
|
10044
10146
|
}
|
|
10045
10147
|
const prompt = await createRunnerWorkPrompt(context.client, context.metadata.amistioProjectId, workItem);
|
|
10046
10148
|
if (options.out) {
|
|
10047
|
-
await
|
|
10149
|
+
await writeFile12(options.out, prompt, "utf8");
|
|
10048
10150
|
console.log(`Wrote work prompt to ${options.out}.`);
|
|
10049
10151
|
} else {
|
|
10050
10152
|
console.log(prompt);
|
|
@@ -10058,6 +10160,34 @@ program.command("tools").description("List local AI coding tools that the Amisti
|
|
|
10058
10160
|
}
|
|
10059
10161
|
console.log("custom - pass --tool-command to use any other local runner command.");
|
|
10060
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
|
+
});
|
|
10061
10191
|
var hostHelper = program.command("host-helper").description("Inspect the optional Amistio host helper used for stronger local execution primitives");
|
|
10062
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) => {
|
|
10063
10193
|
const config = options.path?.trim() ? { command: options.path.trim() } : nativeHostHelperConfigFromEnvironment();
|
|
@@ -10126,7 +10256,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
|
|
|
10126
10256
|
...options.toolCommand ? { toolCommand: options.toolCommand } : {},
|
|
10127
10257
|
...localModelConfig,
|
|
10128
10258
|
streamOutput: options.stream,
|
|
10129
|
-
...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${
|
|
10259
|
+
...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${randomUUID3()}`, policy: sessionPolicy, decision: localSessionDecision(sessionPolicy) } }
|
|
10130
10260
|
});
|
|
10131
10261
|
if (!options.stream && result.stdout.trim()) {
|
|
10132
10262
|
console.log(result.stdout.trim());
|
|
@@ -10149,12 +10279,7 @@ program.command("run").description("Claim and run approved Amistio work locally"
|
|
|
10149
10279
|
process.exitCode = 1;
|
|
10150
10280
|
return;
|
|
10151
10281
|
}
|
|
10152
|
-
const runnerId = options.runnerId ??
|
|
10153
|
-
accountId: context.metadata.amistioAccountId,
|
|
10154
|
-
projectId: context.metadata.amistioProjectId,
|
|
10155
|
-
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10156
|
-
machineId: runnerMachineId()
|
|
10157
|
-
});
|
|
10282
|
+
const runnerId = options.runnerId ?? await resolveLocalRunnerId(runnerIdentityScope(context.metadata));
|
|
10158
10283
|
const resolvedOptions = { ...options, runnerId };
|
|
10159
10284
|
if (resolvedOptions.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
|
|
10160
10285
|
console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
|
|
@@ -10172,7 +10297,7 @@ program.command("run").description("Claim and run approved Amistio work locally"
|
|
|
10172
10297
|
projectId: context.metadata.amistioProjectId,
|
|
10173
10298
|
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10174
10299
|
runnerId,
|
|
10175
|
-
rootDir:
|
|
10300
|
+
rootDir: path18.resolve(options.root),
|
|
10176
10301
|
apiUrl: options.apiUrl,
|
|
10177
10302
|
args: buildBackgroundRunnerArgs(resolvedOptions)
|
|
10178
10303
|
});
|
|
@@ -10225,6 +10350,9 @@ program.command("run").description("Claim and run approved Amistio work locally"
|
|
|
10225
10350
|
}
|
|
10226
10351
|
}
|
|
10227
10352
|
if (result.stopRunner) {
|
|
10353
|
+
if (result.exitCode !== 0) {
|
|
10354
|
+
process.exitCode = result.exitCode;
|
|
10355
|
+
}
|
|
10228
10356
|
return;
|
|
10229
10357
|
}
|
|
10230
10358
|
if (options.maxIterations !== void 0 && iterations >= options.maxIterations) {
|
|
@@ -10240,6 +10368,38 @@ program.command("run").description("Claim and run approved Amistio work locally"
|
|
|
10240
10368
|
}
|
|
10241
10369
|
});
|
|
10242
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
|
+
});
|
|
10243
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) => {
|
|
10244
10404
|
const context = await loadPairedApiContext(options.root, options.apiUrl);
|
|
10245
10405
|
if (!context) {
|
|
@@ -10346,12 +10506,7 @@ runnerService.command("install").description("Install a user-level startup servi
|
|
|
10346
10506
|
process.exitCode = 1;
|
|
10347
10507
|
return;
|
|
10348
10508
|
}
|
|
10349
|
-
const runnerId = options.runnerId ??
|
|
10350
|
-
accountId: context.metadata.amistioAccountId,
|
|
10351
|
-
projectId: context.metadata.amistioProjectId,
|
|
10352
|
-
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10353
|
-
machineId: runnerMachineId()
|
|
10354
|
-
});
|
|
10509
|
+
const runnerId = options.runnerId ?? await resolveLocalRunnerId(runnerIdentityScope(context.metadata));
|
|
10355
10510
|
if (options.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
|
|
10356
10511
|
console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
|
|
10357
10512
|
process.exitCode = 1;
|
|
@@ -10363,7 +10518,7 @@ runnerService.command("install").description("Install a user-level startup servi
|
|
|
10363
10518
|
projectId: context.metadata.amistioProjectId,
|
|
10364
10519
|
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10365
10520
|
runnerId,
|
|
10366
|
-
rootDir:
|
|
10521
|
+
rootDir: path18.resolve(options.root),
|
|
10367
10522
|
apiUrl: options.apiUrl,
|
|
10368
10523
|
args,
|
|
10369
10524
|
platform
|
|
@@ -10389,12 +10544,7 @@ runnerService.command("status").description("Show the startup service status for
|
|
|
10389
10544
|
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
10390
10545
|
return;
|
|
10391
10546
|
}
|
|
10392
|
-
const runnerId = options.runnerId ??
|
|
10393
|
-
accountId: context.metadata.amistioAccountId,
|
|
10394
|
-
projectId: context.metadata.amistioProjectId,
|
|
10395
|
-
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10396
|
-
machineId: runnerMachineId()
|
|
10397
|
-
});
|
|
10547
|
+
const runnerId = options.runnerId ?? await resolveLocalRunnerId(runnerIdentityScope(context.metadata));
|
|
10398
10548
|
const metadata = await readRunnerServiceMetadata({ accountId: context.metadata.amistioAccountId, projectId: context.metadata.amistioProjectId, repositoryLinkId: context.metadata.repositoryLinkId, runnerId });
|
|
10399
10549
|
if (!metadata) {
|
|
10400
10550
|
console.log("No startup service metadata found for this paired repository runner.");
|
|
@@ -10413,12 +10563,7 @@ runnerService.command("remove").description("Remove the startup service for this
|
|
|
10413
10563
|
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
10414
10564
|
return;
|
|
10415
10565
|
}
|
|
10416
|
-
const runnerId = options.runnerId ??
|
|
10417
|
-
accountId: context.metadata.amistioAccountId,
|
|
10418
|
-
projectId: context.metadata.amistioProjectId,
|
|
10419
|
-
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10420
|
-
machineId: runnerMachineId()
|
|
10421
|
-
});
|
|
10566
|
+
const runnerId = options.runnerId ?? await resolveLocalRunnerId(runnerIdentityScope(context.metadata));
|
|
10422
10567
|
const removed = await removeRunnerService({ accountId: context.metadata.amistioAccountId, projectId: context.metadata.amistioProjectId, repositoryLinkId: context.metadata.repositoryLinkId, runnerId });
|
|
10423
10568
|
console.log(removed ? `Removed startup service ${removed.serviceName}.` : "No startup service metadata found for this paired repository runner.");
|
|
10424
10569
|
});
|
|
@@ -10491,26 +10636,18 @@ async function runWatchIteration({ command, context, options, runnerId }) {
|
|
|
10491
10636
|
throw error;
|
|
10492
10637
|
}
|
|
10493
10638
|
const detail = truncateLogExcerpt(errorDetail(error));
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
10497
|
-
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
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 };
|
|
10639
|
+
return await handleRunnerWatchError({
|
|
10640
|
+
client: context.client,
|
|
10641
|
+
detail,
|
|
10642
|
+
error,
|
|
10643
|
+
heartbeatMetadata: runnerHeartbeatMetadata(),
|
|
10644
|
+
logRejectedSettlements,
|
|
10645
|
+
machineId: runnerMachineId(),
|
|
10646
|
+
projectId: context.metadata.amistioProjectId,
|
|
10647
|
+
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
10648
|
+
runnerId,
|
|
10649
|
+
...options.verbose !== void 0 ? { verbose: options.verbose } : {}
|
|
10650
|
+
});
|
|
10514
10651
|
}
|
|
10515
10652
|
}
|
|
10516
10653
|
function claimLaneIds(maxConcurrentWork) {
|
|
@@ -11055,7 +11192,7 @@ async function runNextWorkItem({
|
|
|
11055
11192
|
projectId,
|
|
11056
11193
|
result.workItem.workItemId,
|
|
11057
11194
|
finalStatus,
|
|
11058
|
-
`run_${result.workItem.workItemId}_${
|
|
11195
|
+
`run_${result.workItem.workItemId}_${randomUUID3()}`,
|
|
11059
11196
|
runnerId,
|
|
11060
11197
|
{
|
|
11061
11198
|
tool: preview.toolName,
|
|
@@ -11149,7 +11286,7 @@ async function prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency,
|
|
|
11149
11286
|
const telemetry = workItemIsolationTelemetry(workItem, { ...identity, baseRevision: workItem.baseRevision ?? "unknown", worktreePath: "" });
|
|
11150
11287
|
const finalAttempt = workItem.attempt >= maxPreflightAttempts;
|
|
11151
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}`;
|
|
11152
|
-
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, {
|
|
11153
11290
|
...telemetry,
|
|
11154
11291
|
message: statusMessage,
|
|
11155
11292
|
...finalAttempt ? { blockerReason: message } : { releaseClaim: true },
|
|
@@ -11218,7 +11355,7 @@ async function recordFinalizationFailure({ apiClient, durationMs, error, isolati
|
|
|
11218
11355
|
const settlements = await Promise.allSettled([
|
|
11219
11356
|
apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig)),
|
|
11220
11357
|
markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage6(error)),
|
|
11221
|
-
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, {
|
|
11222
11359
|
...isolationTelemetry,
|
|
11223
11360
|
tool: toolName,
|
|
11224
11361
|
durationMs,
|
|
@@ -11369,7 +11506,7 @@ async function updateRunnerCommandStatus(apiClient, context, command, status, me
|
|
|
11369
11506
|
runnerId: context.runnerId,
|
|
11370
11507
|
repositoryLinkId: context.repositoryLinkId,
|
|
11371
11508
|
status,
|
|
11372
|
-
idempotencyKey: `runner_command_${command.commandId}_${status}_${
|
|
11509
|
+
idempotencyKey: `runner_command_${command.commandId}_${status}_${randomUUID3()}`,
|
|
11373
11510
|
message,
|
|
11374
11511
|
...error ? { error } : {},
|
|
11375
11512
|
...providerAuthStatus ? { providerAuthStatus } : {}
|
|
@@ -11458,7 +11595,7 @@ async function findRecoveryWorkItem(apiClient, projectId, workItemId) {
|
|
|
11458
11595
|
return workItems.find((item) => item.workItemId === workItemId);
|
|
11459
11596
|
}
|
|
11460
11597
|
async function submitRecoveredHandoff(apiClient, context, workItem, handoff, action) {
|
|
11461
|
-
await apiClient.updateWorkStatus(context.projectId, workItem.workItemId, recoveredHandoffWorkStatus(handoff), `handoff_recovery_${workItem.workItemId}_${action}_${
|
|
11598
|
+
await apiClient.updateWorkStatus(context.projectId, workItem.workItemId, recoveredHandoffWorkStatus(handoff), `handoff_recovery_${workItem.workItemId}_${action}_${randomUUID3()}`, context.runnerId, {
|
|
11462
11599
|
implementationHandoff: handoff,
|
|
11463
11600
|
...handoff.message ? { message: handoff.message } : {},
|
|
11464
11601
|
...workItem.controllingAdrId ? { controllingAdrId: workItem.controllingAdrId } : {},
|
|
@@ -11742,7 +11879,7 @@ ${toolResult.stderr}`);
|
|
|
11742
11879
|
const resultMutation = {
|
|
11743
11880
|
status: "completed",
|
|
11744
11881
|
runnerId,
|
|
11745
|
-
idempotencyKey: `generation_${workItem.workItemId}_${workItem.attempt}_${
|
|
11882
|
+
idempotencyKey: `generation_${workItem.workItemId}_${workItem.attempt}_${randomUUID3()}`,
|
|
11746
11883
|
artifacts,
|
|
11747
11884
|
tool: toolName,
|
|
11748
11885
|
durationMs,
|
|
@@ -11817,7 +11954,7 @@ ${toolResult.stderr}`);
|
|
|
11817
11954
|
const failedResult2 = await apiClient.submitBrainGenerationResult(projectId, workItem.workItemId, {
|
|
11818
11955
|
status: "failed",
|
|
11819
11956
|
runnerId,
|
|
11820
|
-
idempotencyKey: `generation_${workItem.workItemId}_${
|
|
11957
|
+
idempotencyKey: `generation_${workItem.workItemId}_${randomUUID3()}`,
|
|
11821
11958
|
tool: toolName,
|
|
11822
11959
|
durationMs,
|
|
11823
11960
|
...failedSessionTelemetry,
|
|
@@ -11867,7 +12004,7 @@ ${toolResult.stderr}`);
|
|
|
11867
12004
|
const resultMutation = {
|
|
11868
12005
|
status: "completed",
|
|
11869
12006
|
runnerId,
|
|
11870
|
-
idempotencyKey: `assistant_${workItem.workItemId}_${
|
|
12007
|
+
idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID3()}`,
|
|
11871
12008
|
answer: answerResult.answer,
|
|
11872
12009
|
sourceBoundary: answerResult.sourceBoundary,
|
|
11873
12010
|
citations: answerResult.citations,
|
|
@@ -11903,7 +12040,7 @@ ${toolResult.stderr}`);
|
|
|
11903
12040
|
const failedMutation = {
|
|
11904
12041
|
status: "failed",
|
|
11905
12042
|
runnerId,
|
|
11906
|
-
idempotencyKey: `assistant_${workItem.workItemId}_${
|
|
12043
|
+
idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID3()}`,
|
|
11907
12044
|
tool: toolName,
|
|
11908
12045
|
durationMs,
|
|
11909
12046
|
...sessionTelemetry,
|
|
@@ -11966,7 +12103,7 @@ ${toolResult.stderr}`);
|
|
|
11966
12103
|
const resultMutation = {
|
|
11967
12104
|
status: "completed",
|
|
11968
12105
|
runnerId,
|
|
11969
|
-
idempotencyKey: `impact_${workItem.workItemId}_${
|
|
12106
|
+
idempotencyKey: `impact_${workItem.workItemId}_${randomUUID3()}`,
|
|
11970
12107
|
report: {
|
|
11971
12108
|
...report,
|
|
11972
12109
|
analyzedRepoRevision: report.analyzedRepoRevision ?? metadata?.lastSyncedRevision
|
|
@@ -12003,7 +12140,7 @@ ${toolResult.stderr}`);
|
|
|
12003
12140
|
const failedMutation = {
|
|
12004
12141
|
status: "failed",
|
|
12005
12142
|
runnerId,
|
|
12006
|
-
idempotencyKey: `impact_${workItem.workItemId}_${
|
|
12143
|
+
idempotencyKey: `impact_${workItem.workItemId}_${randomUUID3()}`,
|
|
12007
12144
|
tool: toolName,
|
|
12008
12145
|
durationMs,
|
|
12009
12146
|
...sessionTelemetry,
|
|
@@ -12064,7 +12201,7 @@ ${toolResult.stderr}`);
|
|
|
12064
12201
|
const resultMutation = {
|
|
12065
12202
|
status: "completed",
|
|
12066
12203
|
runnerId,
|
|
12067
|
-
idempotencyKey: `issue_${workItem.workItemId}_${
|
|
12204
|
+
idempotencyKey: `issue_${workItem.workItemId}_${randomUUID3()}`,
|
|
12068
12205
|
diagnosis,
|
|
12069
12206
|
tool: toolName,
|
|
12070
12207
|
durationMs,
|
|
@@ -12098,7 +12235,7 @@ ${toolResult.stderr}`);
|
|
|
12098
12235
|
const failedMutation = {
|
|
12099
12236
|
status: "failed",
|
|
12100
12237
|
runnerId,
|
|
12101
|
-
idempotencyKey: `issue_${workItem.workItemId}_${
|
|
12238
|
+
idempotencyKey: `issue_${workItem.workItemId}_${randomUUID3()}`,
|
|
12102
12239
|
tool: toolName,
|
|
12103
12240
|
durationMs,
|
|
12104
12241
|
...sessionTelemetry,
|
|
@@ -12159,7 +12296,7 @@ ${toolResult.stderr}`);
|
|
|
12159
12296
|
const resultMutation = {
|
|
12160
12297
|
status: "completed",
|
|
12161
12298
|
runnerId,
|
|
12162
|
-
idempotencyKey: `security_${workItem.workItemId}_${
|
|
12299
|
+
idempotencyKey: `security_${workItem.workItemId}_${randomUUID3()}`,
|
|
12163
12300
|
result: scanResult,
|
|
12164
12301
|
tool: toolName,
|
|
12165
12302
|
durationMs,
|
|
@@ -12193,7 +12330,7 @@ ${toolResult.stderr}`);
|
|
|
12193
12330
|
const failedMutation = {
|
|
12194
12331
|
status: "failed",
|
|
12195
12332
|
runnerId,
|
|
12196
|
-
idempotencyKey: `security_${workItem.workItemId}_${
|
|
12333
|
+
idempotencyKey: `security_${workItem.workItemId}_${randomUUID3()}`,
|
|
12197
12334
|
tool: toolName,
|
|
12198
12335
|
durationMs,
|
|
12199
12336
|
...sessionTelemetry,
|
|
@@ -12254,7 +12391,7 @@ ${toolResult.stderr}`);
|
|
|
12254
12391
|
const resultMutation = {
|
|
12255
12392
|
status: "completed",
|
|
12256
12393
|
runnerId,
|
|
12257
|
-
idempotencyKey: `app_evaluation_${workItem.workItemId}_${
|
|
12394
|
+
idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID3()}`,
|
|
12258
12395
|
result: scanResult,
|
|
12259
12396
|
tool: toolName,
|
|
12260
12397
|
durationMs,
|
|
@@ -12288,7 +12425,7 @@ ${toolResult.stderr}`);
|
|
|
12288
12425
|
const failedMutation = {
|
|
12289
12426
|
status: "failed",
|
|
12290
12427
|
runnerId,
|
|
12291
|
-
idempotencyKey: `app_evaluation_${workItem.workItemId}_${
|
|
12428
|
+
idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID3()}`,
|
|
12292
12429
|
tool: toolName,
|
|
12293
12430
|
durationMs,
|
|
12294
12431
|
...sessionTelemetry,
|
|
@@ -12349,7 +12486,7 @@ ${toolResult.stderr}`);
|
|
|
12349
12486
|
const resultMutation = {
|
|
12350
12487
|
status: "completed",
|
|
12351
12488
|
runnerId,
|
|
12352
|
-
idempotencyKey: `brain_consolidation_${workItem.workItemId}_${
|
|
12489
|
+
idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID3()}`,
|
|
12353
12490
|
result: scanResult,
|
|
12354
12491
|
tool: toolName,
|
|
12355
12492
|
durationMs,
|
|
@@ -12383,7 +12520,7 @@ ${toolResult.stderr}`);
|
|
|
12383
12520
|
const failedMutation = {
|
|
12384
12521
|
status: "failed",
|
|
12385
12522
|
runnerId,
|
|
12386
|
-
idempotencyKey: `brain_consolidation_${workItem.workItemId}_${
|
|
12523
|
+
idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID3()}`,
|
|
12387
12524
|
tool: toolName,
|
|
12388
12525
|
durationMs,
|
|
12389
12526
|
...sessionTelemetry,
|
|
@@ -12445,7 +12582,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
12445
12582
|
const resultMutation = {
|
|
12446
12583
|
status: "completed",
|
|
12447
12584
|
runnerId,
|
|
12448
|
-
idempotencyKey: `project_context_${workItem.workItemId}_${
|
|
12585
|
+
idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID3()}`,
|
|
12449
12586
|
result: refreshResult,
|
|
12450
12587
|
tool: toolName,
|
|
12451
12588
|
durationMs,
|
|
@@ -12491,7 +12628,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
12491
12628
|
const failedMutation = {
|
|
12492
12629
|
status: "failed",
|
|
12493
12630
|
runnerId,
|
|
12494
|
-
idempotencyKey: `project_context_${workItem.workItemId}_${
|
|
12631
|
+
idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID3()}`,
|
|
12495
12632
|
tool: toolName,
|
|
12496
12633
|
durationMs,
|
|
12497
12634
|
...sessionTelemetry,
|
|
@@ -12552,7 +12689,7 @@ ${toolResult.stderr}`);
|
|
|
12552
12689
|
const resultMutation = {
|
|
12553
12690
|
status: "completed",
|
|
12554
12691
|
runnerId,
|
|
12555
|
-
idempotencyKey: `implementation_verification_${workItem.workItemId}_${
|
|
12692
|
+
idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID3()}`,
|
|
12556
12693
|
result: verificationResult,
|
|
12557
12694
|
tool: toolName,
|
|
12558
12695
|
durationMs,
|
|
@@ -12586,7 +12723,7 @@ ${toolResult.stderr}`);
|
|
|
12586
12723
|
const failedMutation = {
|
|
12587
12724
|
status: "failed",
|
|
12588
12725
|
runnerId,
|
|
12589
|
-
idempotencyKey: `implementation_verification_${workItem.workItemId}_${
|
|
12726
|
+
idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID3()}`,
|
|
12590
12727
|
tool: toolName,
|
|
12591
12728
|
durationMs,
|
|
12592
12729
|
...sessionTelemetry,
|
|
@@ -12647,7 +12784,7 @@ ${toolResult.stderr}`);
|
|
|
12647
12784
|
const resultMutation = {
|
|
12648
12785
|
status: "completed",
|
|
12649
12786
|
runnerId,
|
|
12650
|
-
idempotencyKey: `test_quality_${workItem.workItemId}_${
|
|
12787
|
+
idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID3()}`,
|
|
12651
12788
|
result: scanResult,
|
|
12652
12789
|
tool: toolName,
|
|
12653
12790
|
durationMs,
|
|
@@ -12681,7 +12818,7 @@ ${toolResult.stderr}`);
|
|
|
12681
12818
|
const failedMutation = {
|
|
12682
12819
|
status: "failed",
|
|
12683
12820
|
runnerId,
|
|
12684
|
-
idempotencyKey: `test_quality_${workItem.workItemId}_${
|
|
12821
|
+
idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID3()}`,
|
|
12685
12822
|
tool: toolName,
|
|
12686
12823
|
durationMs,
|
|
12687
12824
|
...sessionTelemetry,
|
|
@@ -12742,7 +12879,7 @@ ${toolResult.stderr}`);
|
|
|
12742
12879
|
const resultMutation = {
|
|
12743
12880
|
status: "completed",
|
|
12744
12881
|
runnerId,
|
|
12745
|
-
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${
|
|
12882
|
+
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID3()}`,
|
|
12746
12883
|
result: gateResult,
|
|
12747
12884
|
tool: toolName,
|
|
12748
12885
|
durationMs,
|
|
@@ -12776,7 +12913,7 @@ ${toolResult.stderr}`);
|
|
|
12776
12913
|
const failedMutation = {
|
|
12777
12914
|
status: "failed",
|
|
12778
12915
|
runnerId,
|
|
12779
|
-
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${
|
|
12916
|
+
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID3()}`,
|
|
12780
12917
|
tool: toolName,
|
|
12781
12918
|
durationMs,
|
|
12782
12919
|
...sessionTelemetry,
|
|
@@ -12921,6 +13058,14 @@ async function loadPairedApiContext(root, apiUrl) {
|
|
|
12921
13058
|
})
|
|
12922
13059
|
};
|
|
12923
13060
|
}
|
|
13061
|
+
function runnerIdentityScope(metadata) {
|
|
13062
|
+
return {
|
|
13063
|
+
accountId: metadata.amistioAccountId,
|
|
13064
|
+
projectId: metadata.amistioProjectId,
|
|
13065
|
+
repositoryLinkId: metadata.repositoryLinkId,
|
|
13066
|
+
machineId: runnerMachineId()
|
|
13067
|
+
};
|
|
13068
|
+
}
|
|
12924
13069
|
async function loadProjectNextAction(apiClient, projectId, repositoryLinkId, root) {
|
|
12925
13070
|
const [{ workItems }, { documents }, { runners }] = await Promise.all([
|
|
12926
13071
|
apiClient.listWorkItems(projectId),
|
|
@@ -13005,7 +13150,7 @@ async function prepareToolSession({
|
|
|
13005
13150
|
});
|
|
13006
13151
|
return { ...selection, toolSession: toolSession2 };
|
|
13007
13152
|
}
|
|
13008
|
-
const toolSessionId = `tool_session_${
|
|
13153
|
+
const toolSessionId = `tool_session_${randomUUID3()}`;
|
|
13009
13154
|
const { toolSession } = await apiClient.createToolSession(projectId, {
|
|
13010
13155
|
toolSessionId,
|
|
13011
13156
|
repositoryLinkId,
|
|
@@ -13112,6 +13257,23 @@ function truncateLogExcerpt(value) {
|
|
|
13112
13257
|
function formatHostHelperCapability(label, support) {
|
|
13113
13258
|
return `${label}: ${support.supported ? "supported" : "unsupported"}${support.reason ? ` (${support.reason})` : ""}`;
|
|
13114
13259
|
}
|
|
13260
|
+
function harnessProviderCheckTargets() {
|
|
13261
|
+
return [
|
|
13262
|
+
{ label: "GitHub Models Direct", request: { providerId: "github-models", providerClientId: "github-models-api", routeType: "directProvider" } },
|
|
13263
|
+
{ label: "GitHub Copilot SDK", request: { providerId: "github-copilot", providerClientId: "github-copilot-sdk", routeType: "agentClient" } }
|
|
13264
|
+
];
|
|
13265
|
+
}
|
|
13266
|
+
function formatProviderAuthStatusLines(status) {
|
|
13267
|
+
const lines = [
|
|
13268
|
+
`Target: ${status.providerId}:${status.providerClientId}:${status.routeType}`,
|
|
13269
|
+
`Checked: ${status.checkedAt}`
|
|
13270
|
+
];
|
|
13271
|
+
if (status.authMethodLabel) lines.push(`Auth: ${status.authMethodLabel}`);
|
|
13272
|
+
if (status.modelCount !== void 0) lines.push(`Models: ${status.modelCount}`);
|
|
13273
|
+
if (status.message) lines.push(`Message: ${status.message}`);
|
|
13274
|
+
if (status.errorCode) lines.push(`Code: ${status.errorCode}`);
|
|
13275
|
+
return lines;
|
|
13276
|
+
}
|
|
13115
13277
|
function collectRepeatedOption(value, previous) {
|
|
13116
13278
|
return [...previous, value];
|
|
13117
13279
|
}
|
|
@@ -13138,7 +13300,7 @@ function parseReasoningEffort(value) {
|
|
|
13138
13300
|
throw new Error(`Expected reasoning effort auto, low, medium, high, or xhigh; received ${value}.`);
|
|
13139
13301
|
}
|
|
13140
13302
|
function inferRepoName(root) {
|
|
13141
|
-
return
|
|
13303
|
+
return path18.basename(path18.resolve(root)) || "repository";
|
|
13142
13304
|
}
|
|
13143
13305
|
function createRepoFingerprint(accountId, projectId, repositoryLinkId) {
|
|
13144
13306
|
return createHash9("sha256").update(`${accountId}:${projectId}:${repositoryLinkId}`).digest("hex");
|
|
@@ -13415,7 +13577,7 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurr
|
|
|
13415
13577
|
return {
|
|
13416
13578
|
version: CLI_VERSION,
|
|
13417
13579
|
mode,
|
|
13418
|
-
hostname:
|
|
13580
|
+
hostname: os9.hostname(),
|
|
13419
13581
|
...runnerIsolationCapabilityMetadata(),
|
|
13420
13582
|
maxConcurrentWork: concurrencyMetadata.maxConcurrentWork,
|
|
13421
13583
|
activeClaimLaneIds: concurrencyMetadata.activeClaimLaneIds,
|
|
@@ -13440,7 +13602,7 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurr
|
|
|
13440
13602
|
};
|
|
13441
13603
|
}
|
|
13442
13604
|
function runnerMachineId() {
|
|
13443
|
-
return createHash9("sha256").update(`${
|
|
13605
|
+
return createHash9("sha256").update(`${os9.hostname()}:${os9.platform()}:${os9.arch()}`).digest("hex").slice(0, 20);
|
|
13444
13606
|
}
|
|
13445
13607
|
async function delay(milliseconds) {
|
|
13446
13608
|
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|