@oss-autopilot/core 3.2.0 → 3.4.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.
- package/README.md +1 -1
- package/dist/cli-registry.js +39 -3
- package/dist/cli.bundle.cjs +103 -75
- package/dist/cli.js +17 -3
- package/dist/commands/check-integration.js +8 -8
- package/dist/commands/comments.js +3 -0
- package/dist/commands/config.js +14 -7
- package/dist/commands/daily-render.js +10 -5
- package/dist/commands/daily.d.ts +3 -9
- package/dist/commands/daily.js +12 -21
- package/dist/commands/dashboard-data.js +1 -1
- package/dist/commands/dashboard-lifecycle.js +1 -1
- package/dist/commands/dashboard-process.js +4 -4
- package/dist/commands/dashboard-server.js +26 -7
- package/dist/commands/dashboard.js +2 -2
- package/dist/commands/detect-formatters.js +3 -3
- package/dist/commands/doctor.js +5 -5
- package/dist/commands/guidelines.d.ts +10 -0
- package/dist/commands/guidelines.js +25 -6
- package/dist/commands/list-move-tier.js +5 -5
- package/dist/commands/local-repos.js +9 -9
- package/dist/commands/parse-list.js +10 -10
- package/dist/commands/scout-bridge.js +2 -2
- package/dist/commands/setup.js +24 -13
- package/dist/commands/skip-add.js +6 -3
- package/dist/commands/skip-file-parser.js +3 -3
- package/dist/commands/startup.js +11 -8
- package/dist/commands/state-cmd.js +1 -1
- package/dist/commands/status.js +7 -0
- package/dist/commands/validation.js +12 -3
- package/dist/commands/vet-list.js +12 -8
- package/dist/commands/vet.js +1 -2
- package/dist/core/__fixtures__/prompt-injection-payloads.d.ts +22 -0
- package/dist/core/__fixtures__/prompt-injection-payloads.js +109 -0
- package/dist/core/anti-llm-policy.js +5 -5
- package/dist/core/auth.js +12 -8
- package/dist/core/daily-logic.d.ts +13 -1
- package/dist/core/daily-logic.js +31 -4
- package/dist/core/dates.js +3 -3
- package/dist/core/errors.d.ts +29 -0
- package/dist/core/errors.js +63 -0
- package/dist/core/formatter-detection.js +9 -9
- package/dist/core/gist-state-store.d.ts +42 -3
- package/dist/core/gist-state-store.js +89 -19
- package/dist/core/guidelines-store.js +2 -2
- package/dist/core/http-cache.js +16 -7
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +6 -1
- package/dist/core/issue-conversation.js +3 -1
- package/dist/core/paths.js +4 -4
- package/dist/core/placeholder-usernames.d.ts +1 -0
- package/dist/core/placeholder-usernames.js +27 -0
- package/dist/core/pr-comments-fetcher.d.ts +14 -6
- package/dist/core/pr-comments-fetcher.js +8 -14
- package/dist/core/pr-monitor.d.ts +0 -2
- package/dist/core/pr-monitor.js +2 -25
- package/dist/core/pr-template.js +1 -1
- package/dist/core/state-persistence.d.ts +2 -2
- package/dist/core/state-persistence.js +15 -12
- package/dist/core/state-schema.js +8 -4
- package/dist/core/state.d.ts +27 -0
- package/dist/core/state.js +71 -14
- package/dist/core/untrusted-content.d.ts +48 -0
- package/dist/core/untrusted-content.js +106 -0
- package/dist/core/urls.js +2 -2
- package/dist/formatters/json.d.ts +53 -3
- package/dist/formatters/json.js +49 -14
- package/package.json +3 -3
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrap GitHub-sourced text (PR titles, PR bodies, issue bodies, review
|
|
3
|
+
* comments) in a fenced delimiter so the LLM treats it as data, not
|
|
4
|
+
* instructions (#1192).
|
|
5
|
+
*
|
|
6
|
+
* The contract this module pins, exercised by `untrusted-content.test.ts`
|
|
7
|
+
* and the prompt-injection corpus:
|
|
8
|
+
*
|
|
9
|
+
* 1. Output starts with the open tag and ends with the close tag.
|
|
10
|
+
* 2. The close-tag literal NEVER appears inside the wrapped body — any
|
|
11
|
+
* occurrence in the input is escaped to a sentinel so an attacker
|
|
12
|
+
* cannot close the fence early and inject instructions after it.
|
|
13
|
+
* 3. `extractFromFence(wrapUntrustedContent(x, label))` returns `x`
|
|
14
|
+
* unchanged for any input — the wrapping is lossless.
|
|
15
|
+
*
|
|
16
|
+
* Consumers should pair this with the agent-side guidance in
|
|
17
|
+
* `workflows/reference.md` ("Prompt Injection Awareness"), which tells
|
|
18
|
+
* the LLM to ignore instructions inside `<github-content>` blocks.
|
|
19
|
+
*
|
|
20
|
+
* Non-goals:
|
|
21
|
+
* - This is NOT a content filter. We do not detect or strip prompt-
|
|
22
|
+
* injection payloads; the human-in-the-loop gate on `post`/`claim`
|
|
23
|
+
* remains the primary control.
|
|
24
|
+
*/
|
|
25
|
+
export declare const UNTRUSTED_OPEN_TAG_NAME = "github-content";
|
|
26
|
+
export declare const UNTRUSTED_CLOSE_TAG = "</github-content>";
|
|
27
|
+
export interface UntrustedContentMeta {
|
|
28
|
+
/** GitHub login of the content's author (e.g. PR comment author). */
|
|
29
|
+
author?: string;
|
|
30
|
+
/** GitHub author_association (OWNER, MEMBER, CONTRIBUTOR, NONE, ...). */
|
|
31
|
+
association?: string;
|
|
32
|
+
/** Free-form provenance label (e.g. "pr-body", "review-comment"). */
|
|
33
|
+
source?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Wrap `text` in a `<github-content>` fence labeled with `label` and the
|
|
37
|
+
* optional metadata. Any occurrence of the close-tag literal inside `text`
|
|
38
|
+
* is replaced with a zero-width-joined sentinel that round-trips losslessly
|
|
39
|
+
* via {@link extractFromFence}.
|
|
40
|
+
*/
|
|
41
|
+
export declare function wrapUntrustedContent(text: string, label: string, meta?: UntrustedContentMeta): string;
|
|
42
|
+
/**
|
|
43
|
+
* Reverse of {@link wrapUntrustedContent}. Extracts the original body text
|
|
44
|
+
* (un-escaping any sentinel-encoded close tags). Throws if the input is not
|
|
45
|
+
* a single well-formed fence — callers shouldn't be parsing arbitrary
|
|
46
|
+
* markdown with this; it's for tests + symmetric reasoning only.
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractFromFence(fenced: string): string;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrap GitHub-sourced text (PR titles, PR bodies, issue bodies, review
|
|
3
|
+
* comments) in a fenced delimiter so the LLM treats it as data, not
|
|
4
|
+
* instructions (#1192).
|
|
5
|
+
*
|
|
6
|
+
* The contract this module pins, exercised by `untrusted-content.test.ts`
|
|
7
|
+
* and the prompt-injection corpus:
|
|
8
|
+
*
|
|
9
|
+
* 1. Output starts with the open tag and ends with the close tag.
|
|
10
|
+
* 2. The close-tag literal NEVER appears inside the wrapped body — any
|
|
11
|
+
* occurrence in the input is escaped to a sentinel so an attacker
|
|
12
|
+
* cannot close the fence early and inject instructions after it.
|
|
13
|
+
* 3. `extractFromFence(wrapUntrustedContent(x, label))` returns `x`
|
|
14
|
+
* unchanged for any input — the wrapping is lossless.
|
|
15
|
+
*
|
|
16
|
+
* Consumers should pair this with the agent-side guidance in
|
|
17
|
+
* `workflows/reference.md` ("Prompt Injection Awareness"), which tells
|
|
18
|
+
* the LLM to ignore instructions inside `<github-content>` blocks.
|
|
19
|
+
*
|
|
20
|
+
* Non-goals:
|
|
21
|
+
* - This is NOT a content filter. We do not detect or strip prompt-
|
|
22
|
+
* injection payloads; the human-in-the-loop gate on `post`/`claim`
|
|
23
|
+
* remains the primary control.
|
|
24
|
+
*/
|
|
25
|
+
export const UNTRUSTED_OPEN_TAG_NAME = 'github-content';
|
|
26
|
+
export const UNTRUSTED_CLOSE_TAG = `</${UNTRUSTED_OPEN_TAG_NAME}>`;
|
|
27
|
+
/**
|
|
28
|
+
* Sentinels used to neutralize any literal open- or close-tag substring
|
|
29
|
+
* inside the wrapped body. We use HTML entity escapes (`<` / `>`) so
|
|
30
|
+
* the result is pure ASCII, lints cleanly, and round-trips losslessly. The
|
|
31
|
+
* `&` itself is escaped first so an input containing the literal text
|
|
32
|
+
* `</github-content>` survives the wrap/unwrap round-trip without
|
|
33
|
+
* collision.
|
|
34
|
+
*/
|
|
35
|
+
const AMP_ESCAPE = '&';
|
|
36
|
+
const CLOSE_TAG_ESCAPE = `</${UNTRUSTED_OPEN_TAG_NAME}>`;
|
|
37
|
+
const OPEN_TAG_PATTERN = new RegExp(`<${UNTRUSTED_OPEN_TAG_NAME}\\b`, 'g');
|
|
38
|
+
const OPEN_TAG_ESCAPE = `<${UNTRUSTED_OPEN_TAG_NAME}`;
|
|
39
|
+
function escapeAttr(value) {
|
|
40
|
+
// Newlines/carriage returns can't break out of a quoted attribute (no `"`)
|
|
41
|
+
// but they visually fragment the open tag in the prompt text the LLM
|
|
42
|
+
// reads, so encode them as numeric entities for defense-in-depth.
|
|
43
|
+
return value
|
|
44
|
+
.replace(/&/g, '&')
|
|
45
|
+
.replace(/"/g, '"')
|
|
46
|
+
.replace(/</g, '<')
|
|
47
|
+
.replace(/>/g, '>')
|
|
48
|
+
.replace(/\n/g, ' ')
|
|
49
|
+
.replace(/\r/g, ' ');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Wrap `text` in a `<github-content>` fence labeled with `label` and the
|
|
53
|
+
* optional metadata. Any occurrence of the close-tag literal inside `text`
|
|
54
|
+
* is replaced with a zero-width-joined sentinel that round-trips losslessly
|
|
55
|
+
* via {@link extractFromFence}.
|
|
56
|
+
*/
|
|
57
|
+
export function wrapUntrustedContent(text, label, meta = {}) {
|
|
58
|
+
// Escape order matters for round-trip correctness:
|
|
59
|
+
// 1. `&` first — so any pre-existing entity in the input gets a literal
|
|
60
|
+
// `&` that survives the unwrap pass below.
|
|
61
|
+
// 2. Close-tag literals — replaced with the entity-escaped form.
|
|
62
|
+
// 3. Open-tag literals (matched only at boundaries via OPEN_TAG_PATTERN
|
|
63
|
+
// so we don't accidentally rewrite the close-tag's `</github-content`).
|
|
64
|
+
const escapedBody = text
|
|
65
|
+
.split('&')
|
|
66
|
+
.join(AMP_ESCAPE)
|
|
67
|
+
.split(UNTRUSTED_CLOSE_TAG)
|
|
68
|
+
.join(CLOSE_TAG_ESCAPE)
|
|
69
|
+
.replace(OPEN_TAG_PATTERN, OPEN_TAG_ESCAPE);
|
|
70
|
+
const attrs = [`label="${escapeAttr(label)}"`];
|
|
71
|
+
if (meta.author !== undefined)
|
|
72
|
+
attrs.push(`author="${escapeAttr(meta.author)}"`);
|
|
73
|
+
if (meta.association !== undefined)
|
|
74
|
+
attrs.push(`association="${escapeAttr(meta.association)}"`);
|
|
75
|
+
if (meta.source !== undefined)
|
|
76
|
+
attrs.push(`source="${escapeAttr(meta.source)}"`);
|
|
77
|
+
return `<${UNTRUSTED_OPEN_TAG_NAME} ${attrs.join(' ')}>${escapedBody}${UNTRUSTED_CLOSE_TAG}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reverse of {@link wrapUntrustedContent}. Extracts the original body text
|
|
81
|
+
* (un-escaping any sentinel-encoded close tags). Throws if the input is not
|
|
82
|
+
* a single well-formed fence — callers shouldn't be parsing arbitrary
|
|
83
|
+
* markdown with this; it's for tests + symmetric reasoning only.
|
|
84
|
+
*/
|
|
85
|
+
export function extractFromFence(fenced) {
|
|
86
|
+
const openMatch = fenced.match(new RegExp(`^<${UNTRUSTED_OPEN_TAG_NAME}\\b[^>]*>`));
|
|
87
|
+
if (!openMatch) {
|
|
88
|
+
throw new Error('extractFromFence: input does not start with a <github-content> open tag');
|
|
89
|
+
}
|
|
90
|
+
if (!fenced.endsWith(UNTRUSTED_CLOSE_TAG)) {
|
|
91
|
+
throw new Error('extractFromFence: input does not end with </github-content>');
|
|
92
|
+
}
|
|
93
|
+
const inner = fenced.slice(openMatch[0].length, fenced.length - UNTRUSTED_CLOSE_TAG.length);
|
|
94
|
+
if (inner.includes(UNTRUSTED_CLOSE_TAG)) {
|
|
95
|
+
throw new Error('extractFromFence: nested </github-content> found in body — fence escaping is broken');
|
|
96
|
+
}
|
|
97
|
+
// Reverse the escapes in opposite order: tag entities first (so an
|
|
98
|
+
// already-`&`-prefixed entity survives), then unescape `&`.
|
|
99
|
+
return inner
|
|
100
|
+
.split(CLOSE_TAG_ESCAPE)
|
|
101
|
+
.join(UNTRUSTED_CLOSE_TAG)
|
|
102
|
+
.split(OPEN_TAG_ESCAPE)
|
|
103
|
+
.join(`<${UNTRUSTED_OPEN_TAG_NAME}`)
|
|
104
|
+
.split(AMP_ESCAPE)
|
|
105
|
+
.join('&');
|
|
106
|
+
}
|
package/dist/core/urls.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* owner/repo characters. Extracted from utils.ts under #1116.
|
|
6
6
|
*/
|
|
7
7
|
// Validation patterns for GitHub owner and repo names
|
|
8
|
-
const OWNER_PATTERN = /^[
|
|
9
|
-
const REPO_PATTERN = /^[
|
|
8
|
+
const OWNER_PATTERN = /^[\w-]+$/;
|
|
9
|
+
const REPO_PATTERN = /^[\w.-]+$/;
|
|
10
10
|
function isValidOwnerRepo(owner, repo) {
|
|
11
11
|
return OWNER_PATTERN.test(owner) && REPO_PATTERN.test(repo);
|
|
12
12
|
}
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* Provides structured output that can be consumed by scripts and plugins
|
|
4
4
|
*/
|
|
5
5
|
import { z, type ZodType } from 'zod';
|
|
6
|
-
import type { FetchedPR, DailyDigest, AgentState, RepoGroup, CommentedIssue, ShelvedPRRef } from '../core/types.js';
|
|
6
|
+
import type { FetchedPR, DailyDigest, AgentState, RepoGroup, CommentedIssue, ShelvedPRRef, SearchPriority, CapacityAssessment, ActionableIssue, ActionableIssueType, CompactActionableIssue, ActionMenuItem, ActionMenu } from '../core/types.js';
|
|
7
7
|
import type { ContributionStats } from '../core/stats.js';
|
|
8
8
|
import type { PRCheckFailure } from '../core/pr-monitor.js';
|
|
9
|
-
import type { SearchPriority, CapacityAssessment, ActionableIssue, ActionableIssueType, CompactActionableIssue, ActionMenuItem, ActionMenu } from '../core/types.js';
|
|
10
9
|
import type { CIFormatterDiagnosis, FormatterDetectionResult } from '../core/formatter-detection.js';
|
|
11
10
|
export type { CapacityAssessment, ActionableIssue, ActionableIssueType, CompactActionableIssue, ActionMenuItem, ActionMenu, };
|
|
12
11
|
export type ErrorCode = 'AUTH_REQUIRED' | 'RATE_LIMITED' | 'VALIDATION' | 'CONFIGURATION' | 'NETWORK' | 'NOT_FOUND' | 'STATE_CORRUPTED' | 'CONCURRENCY' | 'UNKNOWN';
|
|
@@ -51,18 +50,37 @@ export interface CompactRepoGroup {
|
|
|
51
50
|
* See `DailyWarning` and issue #1042 for the rationale — keeping this a
|
|
52
51
|
* fixed union so downstream consumers can switch on it without drift.
|
|
53
52
|
*/
|
|
54
|
-
export type DailyWarningPhase = 'fetch' | 'repo-scores' | 'analytics' | 'scout-sync' | 'partition' | 'dismiss-filter' | 'gist-checkpoint';
|
|
53
|
+
export type DailyWarningPhase = 'fetch' | 'repo-scores' | 'analytics' | 'scout-sync' | 'partition' | 'dismiss-filter' | 'gist-checkpoint' | 'gist-staleness';
|
|
55
54
|
/**
|
|
56
55
|
* A single non-fatal failure surfaced from the `daily` pipeline. Unlike
|
|
57
56
|
* `PRCheckFailure` (which is scoped to per-PR fetch errors), this covers
|
|
58
57
|
* ancillary fetches that previously demoted to a log-only `warn()` — repo
|
|
59
58
|
* metadata, monthly analytics, scout sync, Gist checkpoint, etc.
|
|
59
|
+
*
|
|
60
|
+
* `timestamp` and `details` are optional structured extensions added in
|
|
61
|
+
* #1193 so staleness warnings can carry `lastSuccessfulRefresh` /
|
|
62
|
+
* `detectedAt` without bespoke schemas. Existing producers don't need to
|
|
63
|
+
* supply them.
|
|
60
64
|
*/
|
|
61
65
|
export interface DailyWarning {
|
|
62
66
|
phase: DailyWarningPhase;
|
|
63
67
|
operation: string;
|
|
64
68
|
message: string;
|
|
69
|
+
timestamp?: string;
|
|
70
|
+
details?: Record<string, unknown>;
|
|
65
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Build a warning entry from a {@link StalenessInfo} marker (#1193).
|
|
74
|
+
* Co-located with `DailyWarning` so every command (`daily`, `status`,
|
|
75
|
+
* `comments`) builds the same shape from the same source.
|
|
76
|
+
*/
|
|
77
|
+
export interface StalenessLike {
|
|
78
|
+
source: string;
|
|
79
|
+
reason: string;
|
|
80
|
+
lastSuccessfulRefresh: string | null;
|
|
81
|
+
detectedAt: string;
|
|
82
|
+
}
|
|
83
|
+
export declare function buildStalenessWarning(info: StalenessLike): DailyWarning;
|
|
66
84
|
export interface DailyOutput {
|
|
67
85
|
digest: DailyDigestCompact;
|
|
68
86
|
capacity: CapacityAssessment;
|
|
@@ -139,6 +157,22 @@ export declare const StatusOutputSchema: z.ZodObject<{
|
|
|
139
157
|
lastRunAt: z.ZodString;
|
|
140
158
|
offline: z.ZodOptional<z.ZodBoolean>;
|
|
141
159
|
lastUpdated: z.ZodOptional<z.ZodString>;
|
|
160
|
+
warnings: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
161
|
+
phase: z.ZodEnum<{
|
|
162
|
+
fetch: "fetch";
|
|
163
|
+
"repo-scores": "repo-scores";
|
|
164
|
+
analytics: "analytics";
|
|
165
|
+
"scout-sync": "scout-sync";
|
|
166
|
+
partition: "partition";
|
|
167
|
+
"dismiss-filter": "dismiss-filter";
|
|
168
|
+
"gist-checkpoint": "gist-checkpoint";
|
|
169
|
+
"gist-staleness": "gist-staleness";
|
|
170
|
+
}>;
|
|
171
|
+
operation: z.ZodString;
|
|
172
|
+
message: z.ZodString;
|
|
173
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
174
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
175
|
+
}, z.core.$strip>>>;
|
|
142
176
|
}, z.core.$strip>;
|
|
143
177
|
export type StatusOutput = z.infer<typeof StatusOutputSchema>;
|
|
144
178
|
export declare const DailyOutputSchema: z.ZodObject<{
|
|
@@ -234,9 +268,12 @@ export declare const DailyOutputSchema: z.ZodObject<{
|
|
|
234
268
|
partition: "partition";
|
|
235
269
|
"dismiss-filter": "dismiss-filter";
|
|
236
270
|
"gist-checkpoint": "gist-checkpoint";
|
|
271
|
+
"gist-staleness": "gist-staleness";
|
|
237
272
|
}>;
|
|
238
273
|
operation: z.ZodString;
|
|
239
274
|
message: z.ZodString;
|
|
275
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
276
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
240
277
|
}, z.core.$strip>>;
|
|
241
278
|
}, z.core.$strip>;
|
|
242
279
|
export declare const CompactDailyOutputSchema: z.ZodObject<{
|
|
@@ -327,9 +364,12 @@ export declare const CompactDailyOutputSchema: z.ZodObject<{
|
|
|
327
364
|
partition: "partition";
|
|
328
365
|
"dismiss-filter": "dismiss-filter";
|
|
329
366
|
"gist-checkpoint": "gist-checkpoint";
|
|
367
|
+
"gist-staleness": "gist-staleness";
|
|
330
368
|
}>;
|
|
331
369
|
operation: z.ZodString;
|
|
332
370
|
message: z.ZodString;
|
|
371
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
372
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
333
373
|
}, z.core.$strip>>;
|
|
334
374
|
}, z.core.$strip>;
|
|
335
375
|
export declare const SearchOutputSchema: z.ZodObject<{
|
|
@@ -426,6 +466,14 @@ export declare const InitOutputSchema: z.ZodObject<{
|
|
|
426
466
|
username: z.ZodString;
|
|
427
467
|
message: z.ZodString;
|
|
428
468
|
}, z.core.$strip>;
|
|
469
|
+
export declare const ManifestOutputSchema: z.ZodObject<{
|
|
470
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
471
|
+
cliVersion: z.ZodString;
|
|
472
|
+
commands: z.ZodArray<z.ZodObject<{
|
|
473
|
+
name: z.ZodString;
|
|
474
|
+
localOnly: z.ZodBoolean;
|
|
475
|
+
}, z.core.$strip>>;
|
|
476
|
+
}, z.core.$strip>;
|
|
429
477
|
export declare const CheckSetupOutputSchema: z.ZodObject<{
|
|
430
478
|
setupComplete: z.ZodBoolean;
|
|
431
479
|
username: z.ZodString;
|
|
@@ -791,6 +839,8 @@ export interface CommentsOutput {
|
|
|
791
839
|
inlineCommentCount: number;
|
|
792
840
|
discussionCommentCount: number;
|
|
793
841
|
};
|
|
842
|
+
/** Non-fatal warnings (e.g. stale-cache fallback, #1193). Always present; empty on clean runs. */
|
|
843
|
+
warnings?: DailyWarning[];
|
|
794
844
|
}
|
|
795
845
|
/** Output of the post command */
|
|
796
846
|
export interface PostOutput {
|
package/dist/formatters/json.js
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
* Provides structured output that can be consumed by scripts and plugins
|
|
4
4
|
*/
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
+
export function buildStalenessWarning(info) {
|
|
7
|
+
return {
|
|
8
|
+
phase: 'gist-staleness',
|
|
9
|
+
operation: 'state refresh',
|
|
10
|
+
message: `Operating on ${info.source} state — Gist refresh failed: ${info.reason}`,
|
|
11
|
+
timestamp: info.detectedAt,
|
|
12
|
+
details: {
|
|
13
|
+
source: info.source,
|
|
14
|
+
lastSuccessfulRefresh: info.lastSuccessfulRefresh,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
6
18
|
/**
|
|
7
19
|
* Strip a full DailyOutput down to the compact subset (#763).
|
|
8
20
|
* Omits summary, repoGroups, and full failures array. Retains a failureCount
|
|
@@ -61,6 +73,27 @@ export function compactRepoGroups(groups) {
|
|
|
61
73
|
prUrls: group.prs.map((pr) => pr.url),
|
|
62
74
|
}));
|
|
63
75
|
}
|
|
76
|
+
// DailyWarning schema lives here (rather than further down with the rest of
|
|
77
|
+
// the daily schemas) so StatusOutputSchema below can reference it directly
|
|
78
|
+
// without `z.lazy()`. The runtime shape is shared across daily / status /
|
|
79
|
+
// comments warnings — see `DailyWarning` interface above.
|
|
80
|
+
const DailyWarningPhaseSchema = z.enum([
|
|
81
|
+
'fetch',
|
|
82
|
+
'repo-scores',
|
|
83
|
+
'analytics',
|
|
84
|
+
'scout-sync',
|
|
85
|
+
'partition',
|
|
86
|
+
'dismiss-filter',
|
|
87
|
+
'gist-checkpoint',
|
|
88
|
+
'gist-staleness',
|
|
89
|
+
]);
|
|
90
|
+
const DailyWarningSchema = z.object({
|
|
91
|
+
phase: DailyWarningPhaseSchema,
|
|
92
|
+
operation: z.string(),
|
|
93
|
+
message: z.string(),
|
|
94
|
+
timestamp: z.string().optional(),
|
|
95
|
+
details: z.record(z.string(), z.unknown()).optional(),
|
|
96
|
+
});
|
|
64
97
|
export const StatusOutputSchema = z.object({
|
|
65
98
|
stats: z.object({
|
|
66
99
|
mergedPRs: z.number().int().nonnegative(),
|
|
@@ -73,6 +106,7 @@ export const StatusOutputSchema = z.object({
|
|
|
73
106
|
lastRunAt: z.string(),
|
|
74
107
|
offline: z.boolean().optional(),
|
|
75
108
|
lastUpdated: z.string().optional(),
|
|
109
|
+
warnings: z.array(DailyWarningSchema).optional(),
|
|
76
110
|
});
|
|
77
111
|
// ── Daily output schemas (#1146) ─────────────────────────────────────
|
|
78
112
|
//
|
|
@@ -162,20 +196,8 @@ const CompactRepoGroupSchema = z.object({
|
|
|
162
196
|
repo: z.string(),
|
|
163
197
|
prUrls: z.array(z.string()),
|
|
164
198
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
'repo-scores',
|
|
168
|
-
'analytics',
|
|
169
|
-
'scout-sync',
|
|
170
|
-
'partition',
|
|
171
|
-
'dismiss-filter',
|
|
172
|
-
'gist-checkpoint',
|
|
173
|
-
]);
|
|
174
|
-
const DailyWarningSchema = z.object({
|
|
175
|
-
phase: DailyWarningPhaseSchema,
|
|
176
|
-
operation: z.string(),
|
|
177
|
-
message: z.string(),
|
|
178
|
-
});
|
|
199
|
+
// DailyWarning schemas were hoisted above StatusOutputSchema (#1193) so the
|
|
200
|
+
// status output can reference them without `z.lazy()`.
|
|
179
201
|
export const DailyOutputSchema = z.object({
|
|
180
202
|
digest: DailyDigestCompactSchema,
|
|
181
203
|
capacity: CapacityAssessmentSchema,
|
|
@@ -278,6 +300,19 @@ export const InitOutputSchema = z.object({
|
|
|
278
300
|
username: z.string(),
|
|
279
301
|
message: z.string(),
|
|
280
302
|
});
|
|
303
|
+
// ── #1190: plugin → CLI contract ─────────────────────────────────────
|
|
304
|
+
//
|
|
305
|
+
// Pinned shape lets the plugin's session-start hook verify that the bundled
|
|
306
|
+
// CLI exposes the subcommands the markdown layer expects. Bumping
|
|
307
|
+
// `schemaVersion` is a breaking change to that contract.
|
|
308
|
+
export const ManifestOutputSchema = z.object({
|
|
309
|
+
schemaVersion: z.literal(1),
|
|
310
|
+
cliVersion: z.string().regex(/^\d+\.\d+\.\d+/),
|
|
311
|
+
commands: z.array(z.object({
|
|
312
|
+
name: z.string(),
|
|
313
|
+
localOnly: z.boolean(),
|
|
314
|
+
})),
|
|
315
|
+
});
|
|
281
316
|
export const CheckSetupOutputSchema = z.object({
|
|
282
317
|
setupComplete: z.boolean(),
|
|
283
318
|
username: z.string(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oss-autopilot/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "CLI and core library for managing open source contributions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@octokit/plugin-throttling": "^11.0.3",
|
|
56
56
|
"@octokit/rest": "^22.0.1",
|
|
57
|
-
"@oss-scout/core": "^0.
|
|
57
|
+
"@oss-scout/core": "^0.8.0",
|
|
58
58
|
"commander": "^14.0.3",
|
|
59
|
-
"zod": "^4.3
|
|
59
|
+
"zod": "^4.4.3"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/node": "^25.6.0",
|