ghostrail 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +12 -9
  2. package/dist/agent/claude-code.d.ts.map +1 -1
  3. package/dist/agent/claude-code.js +4 -1
  4. package/dist/agent/claude-code.js.map +1 -1
  5. package/dist/board/page.d.ts +1 -1
  6. package/dist/board/page.d.ts.map +1 -1
  7. package/dist/board/page.js +2 -1
  8. package/dist/board/page.js.map +1 -1
  9. package/dist/board/server.d.ts +1 -1
  10. package/dist/board/server.d.ts.map +1 -1
  11. package/dist/board/server.js +7 -1
  12. package/dist/board/server.js.map +1 -1
  13. package/dist/board/stats.d.ts +1 -1
  14. package/dist/board/stats.d.ts.map +1 -1
  15. package/dist/board/stats.js +2 -1
  16. package/dist/board/stats.js.map +1 -1
  17. package/dist/claude-profile/index.d.ts +1 -1
  18. package/dist/claude-profile/index.d.ts.map +1 -1
  19. package/dist/claude-profile/index.js +1 -1
  20. package/dist/claude-profile/index.js.map +1 -1
  21. package/dist/claude-profile/setup.d.ts +7 -0
  22. package/dist/claude-profile/setup.d.ts.map +1 -1
  23. package/dist/claude-profile/setup.js +7 -0
  24. package/dist/claude-profile/setup.js.map +1 -1
  25. package/dist/cli/commands.d.ts +6 -0
  26. package/dist/cli/commands.d.ts.map +1 -1
  27. package/dist/cli/commands.js +98 -2
  28. package/dist/cli/commands.js.map +1 -1
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/cli.js +3 -1
  31. package/dist/cli.js.map +1 -1
  32. package/dist/commands/help.d.ts.map +1 -1
  33. package/dist/commands/help.js +11 -0
  34. package/dist/commands/help.js.map +1 -1
  35. package/dist/config/load.d.ts.map +1 -1
  36. package/dist/config/load.js +18 -2
  37. package/dist/config/load.js.map +1 -1
  38. package/dist/config/schema.d.ts +8 -2
  39. package/dist/config/schema.d.ts.map +1 -1
  40. package/dist/config/schema.js.map +1 -1
  41. package/dist/dashboard/page.d.ts +7 -1
  42. package/dist/dashboard/page.d.ts.map +1 -1
  43. package/dist/dashboard/page.js +204 -39
  44. package/dist/dashboard/page.js.map +1 -1
  45. package/dist/dashboard/routes.d.ts +32 -0
  46. package/dist/dashboard/routes.d.ts.map +1 -1
  47. package/dist/dashboard/routes.js +45 -0
  48. package/dist/dashboard/routes.js.map +1 -1
  49. package/dist/dashboard/server.d.ts +35 -9
  50. package/dist/dashboard/server.d.ts.map +1 -1
  51. package/dist/dashboard/server.js +75 -11
  52. package/dist/dashboard/server.js.map +1 -1
  53. package/dist/doctor/checks.d.ts +87 -0
  54. package/dist/doctor/checks.d.ts.map +1 -0
  55. package/dist/doctor/checks.js +414 -0
  56. package/dist/doctor/checks.js.map +1 -0
  57. package/dist/doctor/index.d.ts +6 -0
  58. package/dist/doctor/index.d.ts.map +1 -0
  59. package/dist/doctor/index.js +5 -0
  60. package/dist/doctor/index.js.map +1 -0
  61. package/dist/doctor/probes.d.ts +42 -0
  62. package/dist/doctor/probes.d.ts.map +1 -0
  63. package/dist/doctor/probes.js +136 -0
  64. package/dist/doctor/probes.js.map +1 -0
  65. package/dist/doctor/report.d.ts +13 -0
  66. package/dist/doctor/report.d.ts.map +1 -0
  67. package/dist/doctor/report.js +32 -0
  68. package/dist/doctor/report.js.map +1 -0
  69. package/dist/doctor/run.d.ts +48 -0
  70. package/dist/doctor/run.d.ts.map +1 -0
  71. package/dist/doctor/run.js +109 -0
  72. package/dist/doctor/run.js.map +1 -0
  73. package/dist/doctor/types.d.ts +17 -0
  74. package/dist/doctor/types.d.ts.map +1 -0
  75. package/dist/doctor/types.js +6 -0
  76. package/dist/doctor/types.js.map +1 -0
  77. package/dist/loop/budget.d.ts +11 -2
  78. package/dist/loop/budget.d.ts.map +1 -1
  79. package/dist/loop/budget.js +13 -3
  80. package/dist/loop/budget.js.map +1 -1
  81. package/dist/loop/comments.d.ts +20 -0
  82. package/dist/loop/comments.d.ts.map +1 -0
  83. package/dist/loop/comments.js +35 -0
  84. package/dist/loop/comments.js.map +1 -0
  85. package/dist/loop/engine.d.ts.map +1 -1
  86. package/dist/loop/engine.js +18 -0
  87. package/dist/loop/engine.js.map +1 -1
  88. package/dist/loop/index.d.ts +1 -0
  89. package/dist/loop/index.d.ts.map +1 -1
  90. package/dist/loop/index.js +1 -0
  91. package/dist/loop/index.js.map +1 -1
  92. package/dist/source/dependencies.d.ts +81 -0
  93. package/dist/source/dependencies.d.ts.map +1 -0
  94. package/dist/source/dependencies.js +218 -0
  95. package/dist/source/dependencies.js.map +1 -0
  96. package/dist/source/index.d.ts +1 -0
  97. package/dist/source/index.d.ts.map +1 -1
  98. package/dist/source/index.js +1 -0
  99. package/dist/source/index.js.map +1 -1
  100. package/dist/source/linear.d.ts +17 -1
  101. package/dist/source/linear.d.ts.map +1 -1
  102. package/dist/source/linear.js +75 -9
  103. package/dist/source/linear.js.map +1 -1
  104. package/dist/store/events.d.ts +8 -0
  105. package/dist/store/events.d.ts.map +1 -1
  106. package/dist/store/events.js +10 -0
  107. package/dist/store/events.js.map +1 -1
  108. package/dist/store/file-store.d.ts +1 -1
  109. package/dist/store/file-store.d.ts.map +1 -1
  110. package/dist/store/file-store.js +7 -0
  111. package/dist/store/file-store.js.map +1 -1
  112. package/dist/store/index.d.ts +1 -1
  113. package/dist/store/index.d.ts.map +1 -1
  114. package/dist/store/index.js +1 -1
  115. package/dist/store/index.js.map +1 -1
  116. package/dist/store/port.d.ts +15 -0
  117. package/dist/store/port.d.ts.map +1 -1
  118. package/dist/store/port.js +17 -1
  119. package/dist/store/port.js.map +1 -1
  120. package/dist/triage/engine.d.ts.map +1 -1
  121. package/dist/triage/engine.js +2 -0
  122. package/dist/triage/engine.js.map +1 -1
  123. package/package.json +3 -1
  124. package/skill/ghostrail/SKILL.md +15 -34
  125. package/templates/code/prompts/resolve-issue.md +7 -0
  126. package/templates/code/prompts/triage.md +7 -0
  127. package/templates/content/prompts/draft.md +7 -0
@@ -6,10 +6,23 @@ import { resolveStateDir } from "../factory/state.js";
6
6
  import { repoStateDir } from "../global/state-dir.js";
7
7
  import { FileStore } from "../store/file-store.js";
8
8
  import { DASHBOARD_PAGE_HTML } from "./page.js";
9
- import { isCrossOriginWrite, parseDashboardRoute, parseWatchUpdate } from "./routes.js";
10
- /** The store dir inside a ghostrail. */
9
+ import { isCrossOriginWrite, parseArtifactHash, parseDashboardRoute, parseDetailQuery, parseWatchUpdate, } from "./routes.js";
11
10
  /** How many runs to show per ghostrail. Enough to see recent activity, not a log. */
12
11
  const RUNS_PER_GHOSTRAIL = 5;
12
+ /**
13
+ * One ghostrail's store: the dep when it was supplied, otherwise a file store
14
+ * over the directory `stateDir` resolved.
15
+ *
16
+ * Every read goes through here, the listing and both detail routes, so nothing
17
+ * in this server can disagree with anything else about where a ghostrail's
18
+ * history lives. That agreement is the whole point: TJ-1639 was two places
19
+ * deriving one directory and reaching different answers.
20
+ */
21
+ async function storeFor(deps, entry) {
22
+ if (deps.store !== undefined)
23
+ return deps.store(entry);
24
+ return new FileStore(await deps.stateDir(entry));
25
+ }
13
26
  function sendJson(res, status, data) {
14
27
  res.writeHead(status, { "content-type": "application/json" });
15
28
  res.end(JSON.stringify(data));
@@ -38,13 +51,7 @@ export async function collectViews(deps, entries) {
38
51
  let runs = [];
39
52
  if (health === "ok") {
40
53
  try {
41
- const rows = await new FileStore(await deps.stateDir(entry)).listRuns({});
42
- runs = rows.slice(0, RUNS_PER_GHOSTRAIL).map((row) => ({
43
- runId: row.runId,
44
- ...(row.itemId === undefined ? {} : { itemId: row.itemId }),
45
- ...(row.state === undefined ? {} : { state: row.state }),
46
- ...(row.title === undefined ? {} : { title: row.title }),
47
- }));
54
+ runs = (await (await storeFor(deps, entry)).listRuns({})).slice(0, RUNS_PER_GHOSTRAIL);
48
55
  }
49
56
  catch {
50
57
  runs = [];
@@ -55,8 +62,9 @@ export async function collectViews(deps, entries) {
55
62
  return views;
56
63
  }
57
64
  /**
58
- * The dashboard's HTTP server: one page, one listing, and one write route that
59
- * sets a ghostrail's `watch` flag and does nothing else.
65
+ * The dashboard's HTTP server: one page, one listing, two read routes for a
66
+ * single run's live PR status and its artifacts, and one write route that sets
67
+ * a ghostrail's `watch` flag and does nothing else.
60
68
  *
61
69
  * It never starts or stops anything. `start` and the service units act on the
62
70
  * flag, which keeps process control off an HTTP surface: the worst a
@@ -94,6 +102,62 @@ export function createDashboardServer(deps, options = {}) {
94
102
  if (route.kind === "ghostrails") {
95
103
  return sendJson(res, 200, { ghostrails: await collectViews(deps, entries) });
96
104
  }
105
+ if (route.kind === "prStatus" || route.kind === "artifact") {
106
+ // Both are reads. The write route states its verb and these did not,
107
+ // which left a form POST able to reach a store read: harmless on its
108
+ // own, since a cross-origin page cannot read the response either way,
109
+ // but a route that accepts verbs it does not mean is the kind of thing
110
+ // a later guard gets reasoned about wrongly.
111
+ if (req.method !== "GET")
112
+ return sendJson(res, 405, { error: "use GET" });
113
+ const key = route.kind === "prStatus" ? "run" : "hash";
114
+ const query = parseDetailQuery(url.searchParams, key);
115
+ if (query === undefined) {
116
+ return sendJson(res, 400, { error: `expected ?path=<ghostrail path>&${key}=<id>` });
117
+ }
118
+ // Same rule the write route holds to: a ghostrail must already be
119
+ // registered. Reading a store under a path supplied by the request
120
+ // would let a caller name any directory on the machine.
121
+ const entry = entries.find((e) => e.path === query.path);
122
+ if (entry === undefined) {
123
+ return sendJson(res, 404, { error: "no registered ghostrail at that path" });
124
+ }
125
+ if (route.kind === "artifact") {
126
+ // A hash that is not a digest is a 404, not a 400: the request is
127
+ // well formed, it just names something no store could hold (TJ-1641).
128
+ const hash = parseArtifactHash(url.searchParams);
129
+ if (hash === undefined)
130
+ return sendJson(res, 404, { error: "not found" });
131
+ const blob = await (await storeFor(deps, entry)).getArtifact(hash);
132
+ if (blob === undefined)
133
+ return sendJson(res, 404, { error: "not found" });
134
+ res.writeHead(200, { "content-type": "application/octet-stream" });
135
+ return res.end(blob);
136
+ }
137
+ const row = await (await storeFor(deps, entry)).getRun(query.value);
138
+ if (row === undefined)
139
+ return sendJson(res, 404, { error: "not found" });
140
+ if (row.prNumber === undefined) {
141
+ return sendJson(res, 404, { error: "no pull request for this run" });
142
+ }
143
+ if (deps.prStatus === undefined)
144
+ return sendJson(res, 200, { available: false });
145
+ try {
146
+ return sendJson(res, 200, {
147
+ available: true,
148
+ ...(await deps.prStatus(entry, row.prNumber)),
149
+ });
150
+ }
151
+ catch (error) {
152
+ // Never a 500. A route whose whole job is "tell me what is known"
153
+ // answers "not known" when the lookup fails, and the page renders
154
+ // that as a muted line rather than an alarm.
155
+ return sendJson(res, 200, {
156
+ available: false,
157
+ error: error instanceof Error ? error.message : String(error),
158
+ });
159
+ }
160
+ }
97
161
  if (route.kind === "watch") {
98
162
  if (req.method !== "POST")
99
163
  return sendJson(res, 405, { error: "use POST" });
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Pure check functions: each takes already-gathered facts and returns a
3
+ * {@link DoctorCheck} (or one per item, for the checks that repeat per prompt).
4
+ * No I/O here — see probes.ts for that — so every rule is testable without a
5
+ * container, a tracker, or the network. Rules are documented in
6
+ * specs/doctor-command.spec.md.
7
+ */
8
+ import type { ConfigError } from "../config/result.js";
9
+ import type { BackendKind } from "../config/schema.js";
10
+ import type { RunRow } from "../store/events.js";
11
+ import type { DoctorCheck } from "./types.js";
12
+ export declare function checkConfig(parsed: {
13
+ ok: true;
14
+ } | {
15
+ ok: false;
16
+ errors: readonly ConfigError[];
17
+ }): DoctorCheck;
18
+ export type PromptField = "agent.prompt" | "respond.prompt" | "triage.prompt";
19
+ export interface PromptFact {
20
+ field: PromptField;
21
+ path: string;
22
+ exists: boolean;
23
+ committed: boolean;
24
+ }
25
+ /** One check per configured prompt path: exists on disk, and is committed. */
26
+ export declare function checkPrompt(fact: PromptFact): DoctorCheck;
27
+ /**
28
+ * The `package.json` script name a gate command invokes, if it looks like a
29
+ * package-manager script invocation (`pnpm <script>`, `npm run <script>`,
30
+ * `yarn <script>`); undefined for anything else, including a package-manager
31
+ * builtin like `pnpm install`. Best-effort: a command this misses is simply not
32
+ * checked further, never reported as broken on a guess.
33
+ */
34
+ export declare function scriptNameOf(command: string): string | undefined;
35
+ /** Gate commands that name a `package.json` script the project does not have. */
36
+ export declare function unresolvedGateCommands(commands: readonly string[], scripts: Record<string, unknown> | undefined): string[];
37
+ export declare function checkGate(commands: readonly string[], scripts: Record<string, unknown> | undefined): DoctorCheck;
38
+ export interface BackendFacts {
39
+ kind: BackendKind;
40
+ image?: string;
41
+ quick: boolean;
42
+ /** undefined only when `quick` skipped the probe. */
43
+ dockerAvailable?: boolean;
44
+ /** undefined when `quick` skipped, docker is unreachable, or no custom image is configured. */
45
+ imagePresent?: boolean;
46
+ }
47
+ export declare function checkBackend(facts: BackendFacts): DoctorCheck;
48
+ export interface ClaudeProfileFacts {
49
+ present: boolean;
50
+ /** Skills requested via `--pin` at `claude-setup` time, not installed. */
51
+ missingPins: string[];
52
+ }
53
+ export declare function checkClaudeProfile(facts: ClaudeProfileFacts): DoctorCheck;
54
+ export type CredentialShape = "unset" | "blank" | "set";
55
+ /** Whether a raw env value is unset, present-but-blank, or a real value. Never returns the value. */
56
+ export declare function classifyCredential(value: string | undefined): CredentialShape;
57
+ export declare function checkClaudeAuthCredential(anthropicApiKey: string | undefined, oauthToken: string | undefined): DoctorCheck;
58
+ export declare function checkLinearCredential(linearApiKey: string | undefined): DoctorCheck;
59
+ export declare function checkGithubCredential(ghToken: string | undefined, githubToken: string | undefined): DoctorCheck;
60
+ export type ClaudeProbeResult = {
61
+ kind: "skipped";
62
+ } | {
63
+ kind: "ok";
64
+ } | {
65
+ kind: "invalid-token";
66
+ } | {
67
+ kind: "error";
68
+ message: string;
69
+ } | {
70
+ kind: "not-run";
71
+ };
72
+ export declare function checkClaudeProbe(result: ClaudeProbeResult, backendKind: BackendKind): DoctorCheck;
73
+ export type LinearProbeResult = {
74
+ kind: "skipped";
75
+ } | {
76
+ kind: "ok";
77
+ } | {
78
+ kind: "unauthorized";
79
+ } | {
80
+ kind: "error";
81
+ message: string;
82
+ } | {
83
+ kind: "not-run";
84
+ };
85
+ export declare function checkLinearProbe(result: LinearProbeResult): DoctorCheck;
86
+ export declare function checkHistory(rows: readonly RunRow[]): DoctorCheck;
87
+ //# sourceMappingURL=checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/doctor/checks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Pure check functions: each takes already-gathered facts and returns a
3
+ * {@link DoctorCheck} (or one per item, for the checks that repeat per prompt).
4
+ * No I/O here — see probes.ts for that — so every rule is testable without a
5
+ * container, a tracker, or the network. Rules are documented in
6
+ * specs/doctor-command.spec.md.
7
+ */
8
+ import { DEFAULT_CONTAINER_IMAGE } from "../backend/docker.js";
9
+ export function checkConfig(parsed) {
10
+ const id = "config";
11
+ const label = "config";
12
+ if (parsed.ok) {
13
+ return { id, label, status: "pass", detail: "ghostrail.toml parses" };
14
+ }
15
+ const detail = parsed.errors
16
+ .map((error) => `config error at ${error.path || "(root)"}: ${error.message}`)
17
+ .join("; ");
18
+ return {
19
+ id,
20
+ label,
21
+ status: "fail",
22
+ detail,
23
+ fix: `fix ${parsed.errors.length === 1 ? "the error" : "these errors"} in ghostrail.toml`,
24
+ };
25
+ }
26
+ /** One check per configured prompt path: exists on disk, and is committed. */
27
+ export function checkPrompt(fact) {
28
+ const id = `prompt:${fact.field}`;
29
+ const label = `prompt (${fact.field})`;
30
+ if (!fact.exists) {
31
+ return {
32
+ id,
33
+ label,
34
+ status: "fail",
35
+ detail: `${fact.path} does not exist`,
36
+ fix: `create ${fact.path}`,
37
+ };
38
+ }
39
+ if (!fact.committed) {
40
+ return {
41
+ id,
42
+ label,
43
+ status: "fail",
44
+ detail: `${fact.path} exists but is not committed; a fresh clone or worktree will not have it`,
45
+ fix: `git add ${fact.path} && git commit`,
46
+ };
47
+ }
48
+ return { id, label, status: "pass", detail: `${fact.path} exists and is committed` };
49
+ }
50
+ /** Package-manager subcommands that are not a `package.json` script name. */
51
+ const PACKAGE_MANAGER_BUILTINS = new Set([
52
+ "install",
53
+ "ci",
54
+ "add",
55
+ "remove",
56
+ "rm",
57
+ "dlx",
58
+ "exec",
59
+ "create",
60
+ "init",
61
+ "link",
62
+ "unlink",
63
+ "publish",
64
+ "update",
65
+ "outdated",
66
+ "list",
67
+ "ls",
68
+ "why",
69
+ "store",
70
+ "prune",
71
+ "audit",
72
+ "dedupe",
73
+ "pack",
74
+ "patch",
75
+ "rebuild",
76
+ ]);
77
+ /**
78
+ * The `package.json` script name a gate command invokes, if it looks like a
79
+ * package-manager script invocation (`pnpm <script>`, `npm run <script>`,
80
+ * `yarn <script>`); undefined for anything else, including a package-manager
81
+ * builtin like `pnpm install`. Best-effort: a command this misses is simply not
82
+ * checked further, never reported as broken on a guess.
83
+ */
84
+ export function scriptNameOf(command) {
85
+ const [head, ...rest] = command.trim().split(/\s+/);
86
+ const next = rest[0];
87
+ if (head === "pnpm" || head === "yarn") {
88
+ if (next === undefined || next.startsWith("-"))
89
+ return undefined;
90
+ if (next === "run")
91
+ return rest[1];
92
+ if (PACKAGE_MANAGER_BUILTINS.has(next))
93
+ return undefined;
94
+ return next;
95
+ }
96
+ if (head === "npm") {
97
+ if (next !== "run" && next !== "run-script")
98
+ return undefined;
99
+ return rest[1];
100
+ }
101
+ return undefined;
102
+ }
103
+ /** Gate commands that name a `package.json` script the project does not have. */
104
+ export function unresolvedGateCommands(commands, scripts) {
105
+ if (scripts === undefined)
106
+ return [];
107
+ const unresolved = [];
108
+ for (const command of commands) {
109
+ const script = scriptNameOf(command);
110
+ if (script !== undefined && !(script in scripts))
111
+ unresolved.push(command);
112
+ }
113
+ return unresolved;
114
+ }
115
+ export function checkGate(commands, scripts) {
116
+ const id = "gate";
117
+ const label = "gate";
118
+ if (commands.length === 0) {
119
+ return {
120
+ id,
121
+ label,
122
+ status: "fail",
123
+ detail: "[gate].commands is empty; nothing verifies an agent's changes",
124
+ fix: "add CI-mirroring commands to [gate].commands in ghostrail.toml",
125
+ };
126
+ }
127
+ const unresolved = unresolvedGateCommands(commands, scripts);
128
+ if (unresolved.length > 0) {
129
+ return {
130
+ id,
131
+ label,
132
+ status: "warn",
133
+ detail: `no matching package.json script for: ${unresolved.join(", ")}`,
134
+ fix: "add the missing script(s) to package.json, or fix the gate command",
135
+ };
136
+ }
137
+ return { id, label, status: "pass", detail: `${commands.length} gate command(s) configured` };
138
+ }
139
+ export function checkBackend(facts) {
140
+ const id = "backend";
141
+ const label = "backend";
142
+ if (facts.kind !== "container") {
143
+ return { id, label, status: "pass", detail: `${facts.kind} needs no image` };
144
+ }
145
+ if (facts.quick) {
146
+ return { id, label, status: "warn", detail: "skipped docker/image checks (--quick)" };
147
+ }
148
+ if (facts.dockerAvailable === false) {
149
+ return {
150
+ id,
151
+ label,
152
+ status: "fail",
153
+ detail: "docker is not reachable",
154
+ fix: "start Docker; `docker version` should succeed",
155
+ };
156
+ }
157
+ if (facts.image === undefined) {
158
+ return {
159
+ id,
160
+ label,
161
+ status: "warn",
162
+ detail: `using the default ${DEFAULT_CONTAINER_IMAGE} image, which has no node, pnpm, git, gh, or claude, so a run will provision and then fail at the gate`,
163
+ fix: "set [backend].image, or build the shipped image with `ghostrail image build`",
164
+ };
165
+ }
166
+ if (facts.imagePresent === false) {
167
+ return {
168
+ id,
169
+ label,
170
+ status: "fail",
171
+ detail: `image ${facts.image} not found locally`,
172
+ fix: `ghostrail image build --tag ${facts.image}`,
173
+ };
174
+ }
175
+ return { id, label, status: "pass", detail: `image ${facts.image} present, docker reachable` };
176
+ }
177
+ export function checkClaudeProfile(facts) {
178
+ const id = "claude-profile";
179
+ const label = "claude profile";
180
+ if (!facts.present) {
181
+ return {
182
+ id,
183
+ label,
184
+ status: "pass",
185
+ detail: "no ghostrail Claude profile in use; runs use your normal ~/.claude",
186
+ };
187
+ }
188
+ const redirectNote = "runs redirect HOME (and pass --settings) at this profile, which carries no " +
189
+ ".credentials.json by design, so only ANTHROPIC_API_KEY / CLAUDE_CODE_OAUTH_TOKEN " +
190
+ "survive that redirect; an interactive login in your real home is invisible to the agent";
191
+ if (facts.missingPins.length > 0) {
192
+ return {
193
+ id,
194
+ label,
195
+ status: "warn",
196
+ detail: `${redirectNote}. Requested via --pin but not installed: ${facts.missingPins.join(", ")}`,
197
+ fix: `ghostrail claude-setup --pin ${facts.missingPins.join(" --pin ")} --write`,
198
+ };
199
+ }
200
+ return { id, label, status: "pass", detail: redirectNote };
201
+ }
202
+ /** Whether a raw env value is unset, present-but-blank, or a real value. Never returns the value. */
203
+ export function classifyCredential(value) {
204
+ if (value === undefined)
205
+ return "unset";
206
+ return value.trim().length === 0 ? "blank" : "set";
207
+ }
208
+ export function checkClaudeAuthCredential(anthropicApiKey, oauthToken) {
209
+ const id = "credential:claude-auth";
210
+ const label = "credential (claude auth)";
211
+ const apiKey = classifyCredential(anthropicApiKey);
212
+ const oauth = classifyCredential(oauthToken);
213
+ if (apiKey === "blank") {
214
+ return {
215
+ id,
216
+ label,
217
+ status: "fail",
218
+ detail: "ANTHROPIC_API_KEY is set but empty",
219
+ fix: "unset ANTHROPIC_API_KEY or give it a real value",
220
+ };
221
+ }
222
+ if (oauth === "blank") {
223
+ return {
224
+ id,
225
+ label,
226
+ status: "fail",
227
+ detail: "CLAUDE_CODE_OAUTH_TOKEN is set but empty",
228
+ fix: "unset CLAUDE_CODE_OAUTH_TOKEN, or run `claude setup-token`",
229
+ };
230
+ }
231
+ if (apiKey === "unset" && oauth === "unset") {
232
+ return {
233
+ id,
234
+ label,
235
+ status: "fail",
236
+ detail: "neither ANTHROPIC_API_KEY nor CLAUDE_CODE_OAUTH_TOKEN is set",
237
+ fix: "run `claude setup-token`, or set ANTHROPIC_API_KEY",
238
+ };
239
+ }
240
+ if (apiKey === "set" && oauth === "set") {
241
+ return {
242
+ id,
243
+ label,
244
+ status: "warn",
245
+ detail: "ANTHROPIC_API_KEY is set alongside CLAUDE_CODE_OAUTH_TOKEN; the API key wins, so a stale key silently beats a good token",
246
+ fix: "unset ANTHROPIC_API_KEY to bill the Pro/Max subscription via the OAuth token",
247
+ };
248
+ }
249
+ return {
250
+ id,
251
+ label,
252
+ status: "pass",
253
+ detail: `using ${apiKey === "set" ? "ANTHROPIC_API_KEY" : "CLAUDE_CODE_OAUTH_TOKEN"}`,
254
+ };
255
+ }
256
+ export function checkLinearCredential(linearApiKey) {
257
+ const id = "credential:linear";
258
+ const label = "credential (linear)";
259
+ const shape = classifyCredential(linearApiKey);
260
+ if (shape === "unset") {
261
+ return {
262
+ id,
263
+ label,
264
+ status: "fail",
265
+ detail: "LINEAR_API_KEY is not set; required for every run",
266
+ fix: "ghostrail secrets-setup --write",
267
+ };
268
+ }
269
+ if (shape === "blank") {
270
+ return {
271
+ id,
272
+ label,
273
+ status: "fail",
274
+ detail: "LINEAR_API_KEY is set but empty",
275
+ fix: "unset LINEAR_API_KEY or give it a real value",
276
+ };
277
+ }
278
+ return { id, label, status: "pass", detail: "LINEAR_API_KEY is set" };
279
+ }
280
+ export function checkGithubCredential(ghToken, githubToken) {
281
+ const id = "credential:github";
282
+ const label = "credential (github)";
283
+ const gh = classifyCredential(ghToken);
284
+ const github = classifyCredential(githubToken);
285
+ if (gh === "blank") {
286
+ return {
287
+ id,
288
+ label,
289
+ status: "fail",
290
+ detail: "GH_TOKEN is set but empty",
291
+ fix: "unset GH_TOKEN or give it a real value",
292
+ };
293
+ }
294
+ if (github === "blank") {
295
+ return {
296
+ id,
297
+ label,
298
+ status: "fail",
299
+ detail: "GITHUB_TOKEN is set but empty",
300
+ fix: "unset GITHUB_TOKEN or give it a real value",
301
+ };
302
+ }
303
+ if (gh === "unset" && github === "unset") {
304
+ return {
305
+ id,
306
+ label,
307
+ status: "warn",
308
+ detail: "neither GH_TOKEN nor GITHUB_TOKEN is set; publishing a PR will fail",
309
+ fix: "ghostrail secrets-setup --write",
310
+ };
311
+ }
312
+ return {
313
+ id,
314
+ label,
315
+ status: "pass",
316
+ detail: `using ${gh === "set" ? "GH_TOKEN" : "GITHUB_TOKEN"}`,
317
+ };
318
+ }
319
+ export function checkClaudeProbe(result, backendKind) {
320
+ const id = "probe:claude-auth";
321
+ const label = "probe (claude auth)";
322
+ switch (result.kind) {
323
+ case "not-run":
324
+ return {
325
+ id,
326
+ label,
327
+ status: "warn",
328
+ detail: "not run: no claude credential is set",
329
+ };
330
+ case "skipped":
331
+ return {
332
+ id,
333
+ label,
334
+ status: "warn",
335
+ detail: "skipped (--quick); presence of a credential does not mean it authenticates",
336
+ };
337
+ case "ok":
338
+ return {
339
+ id,
340
+ label,
341
+ status: "pass",
342
+ detail: `claude -p answered inside the configured backend (${backendKind})`,
343
+ };
344
+ case "invalid-token":
345
+ return {
346
+ id,
347
+ label,
348
+ status: "fail",
349
+ detail: `claude -p failed inside the configured backend (${backendKind}): 401 Invalid bearer token`,
350
+ fix: "the token is expired or revoked; run `claude setup-token`",
351
+ };
352
+ case "error":
353
+ return {
354
+ id,
355
+ label,
356
+ status: "fail",
357
+ detail: `claude -p failed inside the configured backend (${backendKind}): ${result.message}`,
358
+ };
359
+ }
360
+ }
361
+ export function checkLinearProbe(result) {
362
+ const id = "probe:linear-auth";
363
+ const label = "probe (linear auth)";
364
+ switch (result.kind) {
365
+ case "not-run":
366
+ return { id, label, status: "warn", detail: "not run: LINEAR_API_KEY is not usable" };
367
+ case "skipped":
368
+ return { id, label, status: "warn", detail: "skipped (--quick)" };
369
+ case "ok":
370
+ return { id, label, status: "pass", detail: "LINEAR_API_KEY authenticated" };
371
+ case "unauthorized":
372
+ return {
373
+ id,
374
+ label,
375
+ status: "fail",
376
+ detail: "LINEAR_API_KEY was rejected (unauthorized)",
377
+ fix: "generate a new key at Linear Settings > Security & Access",
378
+ };
379
+ case "error":
380
+ return {
381
+ id,
382
+ label,
383
+ status: "fail",
384
+ detail: `LINEAR_API_KEY probe failed: ${result.message}`,
385
+ };
386
+ }
387
+ }
388
+ const HISTORY_SAMPLE = 5;
389
+ export function checkHistory(rows) {
390
+ const id = "history";
391
+ const label = "recent history";
392
+ if (rows.length === 0) {
393
+ return {
394
+ id,
395
+ label,
396
+ status: "pass",
397
+ detail: "no run history yet (the store is empty or absent, not evidence of health)",
398
+ };
399
+ }
400
+ const sample = rows.slice(0, HISTORY_SAMPLE);
401
+ const states = sample.map((row) => row.state ?? "unknown").join(", ");
402
+ const mostRecent = sample[0];
403
+ if (mostRecent?.state === "failed") {
404
+ const error = mostRecent.error ?? "no error was recorded for it";
405
+ return {
406
+ id,
407
+ label,
408
+ status: "warn",
409
+ detail: `recent runs: ${states}. The most recent run failed: ${error}`,
410
+ };
411
+ }
412
+ return { id, label, status: "pass", detail: `recent runs: ${states}` };
413
+ }
414
+ //# sourceMappingURL=checks.js.map