@riocrypto/common-server 1.0.2935 → 1.0.2936

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.
@@ -0,0 +1,26 @@
1
+ export interface LogEntry {
2
+ logName?: string;
3
+ timestamp?: string;
4
+ severity?: string;
5
+ insertId?: string;
6
+ resource?: {
7
+ type?: string;
8
+ labels?: Record<string, string>;
9
+ };
10
+ labels?: Record<string, string>;
11
+ textPayload?: string;
12
+ jsonPayload?: unknown;
13
+ protoPayload?: unknown;
14
+ }
15
+ export interface ListEntriesRequest {
16
+ resourceNames: string[];
17
+ filter: string;
18
+ pageSize: number;
19
+ orderBy?: "timestamp asc" | "timestamp desc";
20
+ }
21
+ export declare const canObtainAccessToken: () => Promise<boolean>;
22
+ export declare class GcpRequestError extends Error {
23
+ status: number;
24
+ constructor(status: number, message: string);
25
+ }
26
+ export declare const listLogEntries: (request: ListEntriesRequest, timeoutMs: number) => Promise<LogEntry[]>;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.listLogEntries = exports.GcpRequestError = exports.canObtainAccessToken = void 0;
16
+ const axios_1 = __importDefault(require("axios"));
17
+ const google_auth_library_1 = require("google-auth-library");
18
+ // Minimal Cloud Logging REST client. Only entries:list is implemented on
19
+ // purpose. Auth comes from Application Default Credentials (Workload
20
+ // Identity in the cluster, GOOGLE_APPLICATION_CREDENTIALS locally) and the
21
+ // token is requested with the read-only logging scope, so even a bug in this
22
+ // file cannot turn into a write.
23
+ const LOGGING_READ_SCOPE = "https://www.googleapis.com/auth/logging.read";
24
+ const ENTRIES_LIST_URL = "https://logging.googleapis.com/v2/entries:list";
25
+ let auth = null;
26
+ const getAuth = () => {
27
+ if (!auth) {
28
+ auth = new google_auth_library_1.GoogleAuth({ scopes: [LOGGING_READ_SCOPE] });
29
+ }
30
+ return auth;
31
+ };
32
+ // Used locally to decide whether to register the tool at all. In the cluster
33
+ // Workload Identity always yields a token; on a laptop it depends on whether
34
+ // the engineer has Application Default Credentials.
35
+ const canObtainAccessToken = () => __awaiter(void 0, void 0, void 0, function* () {
36
+ try {
37
+ const client = yield getAuth().getClient();
38
+ const { token } = yield client.getAccessToken();
39
+ return Boolean(token);
40
+ }
41
+ catch (_a) {
42
+ return false;
43
+ }
44
+ });
45
+ exports.canObtainAccessToken = canObtainAccessToken;
46
+ // Cloud Logging reads are quota limited per minute. Scripts paging through
47
+ // time windows fire many requests back to back, so requests are serialised
48
+ // with a minimum spacing and 429s are retried with backoff. One process, one
49
+ // queue; the tool timeout still bounds the total wait.
50
+ const MIN_SPACING_MS = 1100;
51
+ const MAX_ATTEMPTS = 4;
52
+ let lastRequestAt = 0;
53
+ let queue = Promise.resolve();
54
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
55
+ const throttled = (fn) => {
56
+ const run = queue.then(() => __awaiter(void 0, void 0, void 0, function* () {
57
+ const wait = lastRequestAt + MIN_SPACING_MS - Date.now();
58
+ if (wait > 0)
59
+ yield sleep(wait);
60
+ lastRequestAt = Date.now();
61
+ return fn();
62
+ }));
63
+ queue = run.then(() => undefined, () => undefined);
64
+ return run;
65
+ };
66
+ const gcpErrorMessage = (error) => {
67
+ var _a, _b, _c;
68
+ const message = (_c = (_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message;
69
+ return typeof message === "string" ? message.slice(0, 400) : undefined;
70
+ };
71
+ class GcpRequestError extends Error {
72
+ constructor(status, message) {
73
+ super(message);
74
+ this.status = status;
75
+ }
76
+ }
77
+ exports.GcpRequestError = GcpRequestError;
78
+ const listLogEntries = (request, timeoutMs) => __awaiter(void 0, void 0, void 0, function* () {
79
+ var _b, _c, _d, _e;
80
+ const client = yield getAuth().getClient();
81
+ const { token } = yield client.getAccessToken();
82
+ if (!token) {
83
+ throw new Error("Could not obtain a GCP access token");
84
+ }
85
+ const body = {
86
+ resourceNames: request.resourceNames,
87
+ filter: request.filter,
88
+ pageSize: request.pageSize,
89
+ orderBy: request.orderBy || "timestamp desc",
90
+ };
91
+ const config = {
92
+ headers: {
93
+ Authorization: `Bearer ${token}`,
94
+ "Content-Type": "application/json",
95
+ },
96
+ timeout: timeoutMs,
97
+ maxContentLength: 8 * 1024 * 1024,
98
+ maxRedirects: 0,
99
+ };
100
+ for (let attempt = 1;; attempt++) {
101
+ try {
102
+ const response = yield throttled(() => axios_1.default.post(ENTRIES_LIST_URL, body, config));
103
+ return Array.isArray((_b = response.data) === null || _b === void 0 ? void 0 : _b.entries) ? response.data.entries : [];
104
+ }
105
+ catch (error) {
106
+ const status = (_c = error === null || error === void 0 ? void 0 : error.response) === null || _c === void 0 ? void 0 : _c.status;
107
+ if (status === 429 && attempt < MAX_ATTEMPTS) {
108
+ // Quota windows are a minute long; short retries just burn attempts.
109
+ const retryAfter = Number((_e = (_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.headers) === null || _e === void 0 ? void 0 : _e["retry-after"]);
110
+ const backoff = Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1000 : 5000 * 2 ** (attempt - 1);
111
+ yield sleep(Math.min(backoff, 25000));
112
+ continue;
113
+ }
114
+ if (status === 429) {
115
+ throw new GcpRequestError(429, "Cloud Logging read quota exceeded. Wait a few seconds and use fewer, wider queries (one 2 hour query instead of eight 15 minute ones).");
116
+ }
117
+ if (status === 400) {
118
+ throw new GcpRequestError(400, `Cloud Logging rejected the filter: ${gcpErrorMessage(error) || "invalid request"}`);
119
+ }
120
+ if (status !== undefined) {
121
+ throw new GcpRequestError(status, `Cloud Logging returned ${status}: ${gcpErrorMessage(error) || (error === null || error === void 0 ? void 0 : error.message) || "request failed"}`);
122
+ }
123
+ throw error;
124
+ }
125
+ }
126
+ });
127
+ exports.listLogEntries = listLogEntries;
@@ -0,0 +1,33 @@
1
+ import { GitHubAppAuth } from "./github-app-auth";
2
+ export interface GitResult {
3
+ stdout: string;
4
+ truncated: boolean;
5
+ }
6
+ export interface GitRepoCacheOptions {
7
+ org: string;
8
+ cacheDir: string;
9
+ auth: GitHubAppAuth;
10
+ historyDays: number;
11
+ refreshIntervalMs: number;
12
+ gitTimeoutMs: number;
13
+ cloneTimeoutMs: number;
14
+ }
15
+ export declare const isValidRepoName: (name: string) => boolean;
16
+ export declare const isValidRef: (ref: string) => boolean;
17
+ export declare class GitRepoCache {
18
+ private readonly options;
19
+ private repoLocks;
20
+ private lastRefresh;
21
+ constructor(options: GitRepoCacheOptions);
22
+ private repoUrl;
23
+ private repoDir;
24
+ private runGit;
25
+ private withRepoLock;
26
+ private isCloned;
27
+ private clone;
28
+ private refreshIfStale;
29
+ private refExists;
30
+ private fetchRef;
31
+ ensureRepoRef(repo: string, requestedRef?: string): Promise<string>;
32
+ gitInRepo(repo: string, args: string[], maxBuffer?: number): Promise<GitResult>;
33
+ }
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.GitRepoCache = exports.isValidRef = exports.isValidRepoName = void 0;
13
+ const child_process_1 = require("child_process");
14
+ const fs_1 = require("fs");
15
+ const path_1 = require("path");
16
+ const tool_1 = require("./tool");
17
+ // Bare, shallow, per-repo clones under a writable directory. Repos are
18
+ // cloned the first time a tool asks for them and refreshed when older than
19
+ // refreshIntervalMs. Everything is read through git plumbing against refs
20
+ // (git grep / git show / git log), so there is never a working tree and
21
+ // never a checkout to keep in sync.
22
+ //
23
+ // Git is a subprocess fed by model input. Every argument is validated by
24
+ // the caller, passed as an array (never a shell string), and user-supplied
25
+ // values always follow "--" or "-e" so they cannot be read as options.
26
+ const REPO_NAME = /^[a-z0-9][a-z0-9-]{0,49}$/;
27
+ const VERSION_TAG = /^v?\d+\.\d+\.\d+(?:[-+.][A-Za-z0-9.-]{1,40})?$/;
28
+ const SHORT_SHA = /^[0-9a-f]{7,40}$/;
29
+ const DEFAULT_REF = "HEAD";
30
+ const isValidRepoName = (name) => REPO_NAME.test(name);
31
+ exports.isValidRepoName = isValidRepoName;
32
+ const isValidRef = (ref) => ref === DEFAULT_REF || VERSION_TAG.test(ref) || SHORT_SHA.test(ref);
33
+ exports.isValidRef = isValidRef;
34
+ const basicHeaderValue = (token) => Buffer.from(`x-access-token:${token}`).toString("base64");
35
+ // Removes both the raw token and the base64 basic auth header, which is what
36
+ // execFile's error.message carries when it echoes the argv.
37
+ const scrub = (text, token) => token
38
+ ? text
39
+ .split(token)
40
+ .join("[REDACTED_GITHUB_TOKEN]")
41
+ .split(basicHeaderValue(token))
42
+ .join("[REDACTED_GITHUB_TOKEN]")
43
+ : text;
44
+ class GitRepoCache {
45
+ constructor(options) {
46
+ this.options = options;
47
+ this.repoLocks = new Map();
48
+ this.lastRefresh = new Map();
49
+ }
50
+ repoUrl(repo) {
51
+ return `https://github.com/${this.options.org}/${repo}.git`;
52
+ }
53
+ repoDir(repo) {
54
+ return (0, path_1.join)(this.options.cacheDir, `${repo}.git`);
55
+ }
56
+ runGit(args, cwd, timeoutMs, token, maxBuffer = 4 * 1024 * 1024) {
57
+ return new Promise((resolve, reject) => {
58
+ // The token travels as a per-invocation header, never into .git/config.
59
+ const authArgs = token
60
+ ? ["-c", `http.extraheader=AUTHORIZATION: basic ${basicHeaderValue(token)}`]
61
+ : [];
62
+ const fullArgs = [
63
+ "-c",
64
+ "credential.helper=",
65
+ "-c",
66
+ "core.askPass=",
67
+ "-c",
68
+ "protocol.allow=never",
69
+ "-c",
70
+ "protocol.https.allow=always",
71
+ ...authArgs,
72
+ ...args,
73
+ ];
74
+ (0, child_process_1.execFile)("git", fullArgs, {
75
+ cwd,
76
+ timeout: timeoutMs,
77
+ maxBuffer,
78
+ env: {
79
+ PATH: process.env.PATH || "/usr/bin:/bin",
80
+ HOME: this.options.cacheDir,
81
+ GIT_TERMINAL_PROMPT: "0",
82
+ GIT_CONFIG_NOSYSTEM: "1",
83
+ GIT_CONFIG_GLOBAL: "/dev/null",
84
+ LC_ALL: "C",
85
+ },
86
+ }, (error, stdout, stderr) => {
87
+ if (error) {
88
+ // git grep exits 1 on "no matches", which is not an error here.
89
+ if (error.code === 1 && args.includes("grep")) {
90
+ return resolve({ stdout: "", truncated: false });
91
+ }
92
+ if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
93
+ return resolve({ stdout: String(stdout || ""), truncated: true });
94
+ }
95
+ const detail = scrub(String(stderr || error.message || "git failed"), token)
96
+ .split("\n")
97
+ .filter((l) => l.trim().length > 0)
98
+ .slice(0, 3)
99
+ .join(" ");
100
+ if (error.killed || error.signal === "SIGTERM") {
101
+ return reject(new tool_1.ToolInputError(`git timed out after ${timeoutMs / 1000}s`));
102
+ }
103
+ return reject(new Error(detail));
104
+ }
105
+ resolve({ stdout: String(stdout || ""), truncated: false });
106
+ });
107
+ });
108
+ }
109
+ withRepoLock(repo, fn) {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ const previous = this.repoLocks.get(repo) || Promise.resolve();
112
+ const next = previous.catch(() => undefined).then(fn);
113
+ this.repoLocks.set(repo, next);
114
+ try {
115
+ return yield next;
116
+ }
117
+ finally {
118
+ if (this.repoLocks.get(repo) === next)
119
+ this.repoLocks.delete(repo);
120
+ }
121
+ });
122
+ }
123
+ isCloned(repo) {
124
+ return (0, fs_1.existsSync)((0, path_1.join)(this.repoDir(repo), "HEAD"));
125
+ }
126
+ clone(repo) {
127
+ return __awaiter(this, void 0, void 0, function* () {
128
+ (0, fs_1.mkdirSync)(this.options.cacheDir, { recursive: true });
129
+ const token = yield this.options.auth.getInstallationToken();
130
+ try {
131
+ yield this.runGit([
132
+ "clone",
133
+ "--bare",
134
+ "--single-branch",
135
+ "--no-tags",
136
+ "--depth",
137
+ String(this.options.historyDays * 10),
138
+ this.repoUrl(repo),
139
+ this.repoDir(repo),
140
+ ], undefined, this.options.cloneTimeoutMs, token);
141
+ }
142
+ catch (error) {
143
+ const message = String((error === null || error === void 0 ? void 0 : error.message) || "");
144
+ if (/not found|Repository not found|403|404/i.test(message)) {
145
+ throw new tool_1.ToolInputError(`repository "${repo}" was not found or the GitHub App is not installed on it`);
146
+ }
147
+ throw error;
148
+ }
149
+ this.lastRefresh.set(repo, Date.now());
150
+ });
151
+ }
152
+ refreshIfStale(repo) {
153
+ return __awaiter(this, void 0, void 0, function* () {
154
+ const last = this.lastRefresh.get(repo) || 0;
155
+ if (Date.now() - last < this.options.refreshIntervalMs)
156
+ return;
157
+ // A bare --single-branch clone has no fetch refspec, so name the branch
158
+ // HEAD points at explicitly or the fetch would only update FETCH_HEAD.
159
+ const head = yield this.runGit(["symbolic-ref", "--quiet", "HEAD"], this.repoDir(repo), this.options.gitTimeoutMs);
160
+ const branch = head.stdout.trim();
161
+ if (!/^refs\/heads\/[A-Za-z0-9._/-]+$/.test(branch)) {
162
+ throw new Error(`unexpected HEAD in ${repo}`);
163
+ }
164
+ const token = yield this.options.auth.getInstallationToken();
165
+ yield this.runGit([
166
+ "fetch",
167
+ "--no-tags",
168
+ "--depth",
169
+ String(this.options.historyDays * 10),
170
+ "origin",
171
+ `+${branch}:${branch}`,
172
+ ], this.repoDir(repo), this.options.gitTimeoutMs, token);
173
+ this.lastRefresh.set(repo, Date.now());
174
+ });
175
+ }
176
+ refExists(repo, ref) {
177
+ return __awaiter(this, void 0, void 0, function* () {
178
+ try {
179
+ yield this.runGit(["rev-parse", "--verify", "--quiet", "--end-of-options", `${ref}^{commit}`], this.repoDir(repo), this.options.gitTimeoutMs);
180
+ return true;
181
+ }
182
+ catch (_a) {
183
+ return false;
184
+ }
185
+ });
186
+ }
187
+ fetchRef(repo, refspec) {
188
+ return __awaiter(this, void 0, void 0, function* () {
189
+ const token = yield this.options.auth.getInstallationToken();
190
+ try {
191
+ yield this.runGit(["fetch", "--depth", "1", "origin", refspec], this.repoDir(repo), this.options.gitTimeoutMs, token);
192
+ return true;
193
+ }
194
+ catch (_a) {
195
+ return false;
196
+ }
197
+ });
198
+ }
199
+ // Makes sure the repo is cloned and the requested ref is present locally.
200
+ // Returns the fully qualified ref to pass to git plumbing.
201
+ ensureRepoRef(repo, requestedRef) {
202
+ return __awaiter(this, void 0, void 0, function* () {
203
+ if (!(0, exports.isValidRepoName)(repo)) {
204
+ throw new tool_1.ToolInputError(`"${repo}" is not a valid repository name`);
205
+ }
206
+ const ref = requestedRef || DEFAULT_REF;
207
+ if (!(0, exports.isValidRef)(ref)) {
208
+ throw new tool_1.ToolInputError(`"ref" must be HEAD, a version tag like v1.2.3, or a commit sha`);
209
+ }
210
+ return this.withRepoLock(repo, () => __awaiter(this, void 0, void 0, function* () {
211
+ if (!this.isCloned(repo)) {
212
+ yield this.clone(repo);
213
+ }
214
+ if (ref === DEFAULT_REF) {
215
+ yield this.refreshIfStale(repo);
216
+ return DEFAULT_REF;
217
+ }
218
+ if (VERSION_TAG.test(ref)) {
219
+ const candidates = ref.startsWith("v") ? [ref, ref.slice(1)] : [ref, `v${ref}`];
220
+ for (const candidate of candidates) {
221
+ const qualified = `refs/tags/${candidate}`;
222
+ if (yield this.refExists(repo, qualified))
223
+ return qualified;
224
+ if ((yield this.fetchRef(repo, `+refs/tags/${candidate}:refs/tags/${candidate}`)) &&
225
+ (yield this.refExists(repo, qualified))) {
226
+ return qualified;
227
+ }
228
+ }
229
+ throw new tool_1.ToolInputError(`tag "${ref}" does not exist in ${repo}. Use HEAD for the default branch.`);
230
+ }
231
+ if (yield this.refExists(repo, ref))
232
+ return ref;
233
+ if ((yield this.fetchRef(repo, ref)) && (yield this.refExists(repo, ref)))
234
+ return ref;
235
+ throw new tool_1.ToolInputError(`commit "${ref}" does not exist in ${repo}`);
236
+ }));
237
+ });
238
+ }
239
+ // Read-only plumbing. Callers validate every argument before reaching here.
240
+ gitInRepo(repo, args, maxBuffer) {
241
+ return this.runGit(args, this.repoDir(repo), this.options.gitTimeoutMs, undefined, maxBuffer);
242
+ }
243
+ }
244
+ exports.GitRepoCache = GitRepoCache;
@@ -0,0 +1,15 @@
1
+ export interface GitHubAppAuthOptions {
2
+ appId: string;
3
+ installationId: string;
4
+ privateKey: string;
5
+ }
6
+ export declare const buildAppJwt: (appId: string, privateKey: string, nowSeconds: number) => string;
7
+ export declare class GitHubAppAuth {
8
+ private readonly options;
9
+ private cached;
10
+ private inFlight;
11
+ constructor(options: GitHubAppAuthOptions);
12
+ private mint;
13
+ getInstallationToken(): Promise<string>;
14
+ reset(): void;
15
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.GitHubAppAuth = exports.buildAppJwt = void 0;
16
+ const axios_1 = __importDefault(require("axios"));
17
+ const crypto_1 = require("crypto");
18
+ // GitHub App authentication. The App holds only Contents:read and
19
+ // Metadata:read. Its private key is the one long-lived secret; it never
20
+ // leaves the process. Installation tokens minted from it live for an hour
21
+ // and are the only thing git ever sees.
22
+ const GITHUB_API = "https://api.github.com";
23
+ const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;
24
+ const base64Url = (input) => Buffer.from(input)
25
+ .toString("base64")
26
+ .replace(/=/g, "")
27
+ .replace(/\+/g, "-")
28
+ .replace(/\//g, "_");
29
+ const buildAppJwt = (appId, privateKey, nowSeconds) => {
30
+ const header = base64Url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
31
+ // iat backdated 60s to tolerate clock drift, exp is GitHub's 10 minute max.
32
+ const payload = base64Url(JSON.stringify({ iat: nowSeconds - 60, exp: nowSeconds + 9 * 60, iss: appId }));
33
+ const signer = (0, crypto_1.createSign)("RSA-SHA256");
34
+ signer.update(`${header}.${payload}`);
35
+ const signature = base64Url(signer.sign(privateKey));
36
+ return `${header}.${payload}.${signature}`;
37
+ };
38
+ exports.buildAppJwt = buildAppJwt;
39
+ class GitHubAppAuth {
40
+ constructor(options) {
41
+ this.options = options;
42
+ this.cached = null;
43
+ this.inFlight = null;
44
+ }
45
+ mint() {
46
+ var _a;
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ const jwt = (0, exports.buildAppJwt)(this.options.appId, this.options.privateKey, Math.floor(Date.now() / 1000));
49
+ const response = yield axios_1.default.post(`${GITHUB_API}/app/installations/${encodeURIComponent(this.options.installationId)}/access_tokens`,
50
+ // Ask for the minimum even if the App were ever granted more.
51
+ { permissions: { contents: "read", metadata: "read" } }, {
52
+ headers: {
53
+ Authorization: `Bearer ${jwt}`,
54
+ Accept: "application/vnd.github+json",
55
+ "X-GitHub-Api-Version": "2022-11-28",
56
+ },
57
+ timeout: 15000,
58
+ maxRedirects: 0,
59
+ });
60
+ const token = (_a = response.data) === null || _a === void 0 ? void 0 : _a.token;
61
+ if (!token) {
62
+ throw new Error("GitHub did not return an installation token");
63
+ }
64
+ const expiresAt = response.data.expires_at
65
+ ? Date.parse(response.data.expires_at)
66
+ : Date.now() + 60 * 60 * 1000;
67
+ this.cached = { token, expiresAt };
68
+ return token;
69
+ });
70
+ }
71
+ getInstallationToken() {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ if (this.cached && this.cached.expiresAt - Date.now() > TOKEN_REFRESH_MARGIN_MS) {
74
+ return this.cached.token;
75
+ }
76
+ if (!this.inFlight) {
77
+ this.inFlight = this.mint().then((token) => {
78
+ this.inFlight = null;
79
+ return token;
80
+ }, (error) => {
81
+ this.inFlight = null;
82
+ throw error;
83
+ });
84
+ }
85
+ return this.inFlight;
86
+ });
87
+ }
88
+ reset() {
89
+ this.cached = null;
90
+ this.inFlight = null;
91
+ }
92
+ }
93
+ exports.GitHubAppAuth = GitHubAppAuth;
@@ -0,0 +1 @@
1
+ export declare const hasInjectionMarker: (text: string) => boolean;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasInjectionMarker = void 0;
4
+ // Instruction-like text inside tool output. Kept narrow on purpose: a false
5
+ // positive can lock an admin out or reject a whole sweep report, a miss is
6
+ // just an alert not sent.
7
+ const INJECTION_MARKERS = [
8
+ /ignore\s+(all\s+|the\s+|any\s+)?(previous|prior|above|earlier)\s+(instructions|rules|prompts?)/i,
9
+ /disregard\s+(your|the|all)\s+(system|previous|prior)\s+(prompt|instructions|rules)/i,
10
+ /(reveal|print|output|show|leak|dump)\s+(me\s+)?(the|your)\s+(system\s+prompt|api\s+key|secret|token|credentials?|private\s+key)/i,
11
+ /you\s+are\s+now\s+(in\s+)?(developer|debug|admin|unrestricted|dan)\s+mode/i,
12
+ /<\/?(system|assistant|tool_result_untrusted_data|past_cases_untrusted_data|memory_untrusted_data)>/i,
13
+ /\bBEGIN\s+SYSTEM\s+PROMPT\b/i,
14
+ ];
15
+ const hasInjectionMarker = (text) => INJECTION_MARKERS.some((re) => re.test(text));
16
+ exports.hasInjectionMarker = hasInjectionMarker;
@@ -0,0 +1,45 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ export interface PodSummary {
4
+ name: string;
5
+ phase: string;
6
+ ready: string;
7
+ restarts: number;
8
+ ageMinutes: number;
9
+ containers: string[];
10
+ node?: string;
11
+ lastState?: string;
12
+ }
13
+ export interface DeploymentSummary {
14
+ name: string;
15
+ desired: number;
16
+ ready: number;
17
+ available: number;
18
+ updated: number;
19
+ images: string[];
20
+ conditions: string[];
21
+ }
22
+ export interface EventSummary {
23
+ lastSeen: string;
24
+ type: string;
25
+ reason: string;
26
+ object: string;
27
+ count: number;
28
+ message: string;
29
+ }
30
+ export declare class K8sClient {
31
+ private http;
32
+ constructor(baseURL: string, ca: Buffer | undefined, timeoutMs: number);
33
+ static fromInCluster(timeoutMs: number): K8sClient | null;
34
+ private headers;
35
+ private get;
36
+ listPods(namespace: string): Promise<PodSummary[]>;
37
+ getPodLog(namespace: string, pod: string, options: {
38
+ container?: string;
39
+ tailLines: number;
40
+ sinceSeconds: number;
41
+ previous: boolean;
42
+ }): Promise<string>;
43
+ listDeployments(namespace: string): Promise<DeploymentSummary[]>;
44
+ listEvents(namespace: string, involvedObjectName?: string): Promise<EventSummary[]>;
45
+ }