@weiyentan/opencode-plugin-awx 0.2.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/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/auth.d.ts +112 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +180 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +148 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +334 -0
- package/dist/client.js.map +1 -0
- package/dist/contracts/job-detail.d.ts +141 -0
- package/dist/contracts/job-detail.d.ts.map +1 -0
- package/dist/contracts/job-detail.js +98 -0
- package/dist/contracts/job-detail.js.map +1 -0
- package/dist/contracts/sync-project.d.ts +31 -0
- package/dist/contracts/sync-project.d.ts.map +1 -0
- package/dist/contracts/sync-project.js +30 -0
- package/dist/contracts/sync-project.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +754 -0
- package/dist/index.js.map +1 -0
- package/dist/job-status.d.ts +116 -0
- package/dist/job-status.d.ts.map +1 -0
- package/dist/job-status.js +168 -0
- package/dist/job-status.js.map +1 -0
- package/dist/launch.d.ts +76 -0
- package/dist/launch.d.ts.map +1 -0
- package/dist/launch.js +133 -0
- package/dist/launch.js.map +1 -0
- package/dist/list-projects.d.ts +63 -0
- package/dist/list-projects.d.ts.map +1 -0
- package/dist/list-projects.js +84 -0
- package/dist/list-projects.js.map +1 -0
- package/dist/list-templates.d.ts +60 -0
- package/dist/list-templates.d.ts.map +1 -0
- package/dist/list-templates.js +120 -0
- package/dist/list-templates.js.map +1 -0
- package/dist/metrics.d.ts +174 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +275 -0
- package/dist/metrics.js.map +1 -0
- package/dist/transforms.d.ts +52 -0
- package/dist/transforms.d.ts.map +1 -0
- package/dist/transforms.js +108 -0
- package/dist/transforms.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* ── Timeout budget ─────────────────────────────────────────────── */
|
|
2
|
+
/**
|
|
3
|
+
* Calculate per-page timeout budget.
|
|
4
|
+
*
|
|
5
|
+
* Formula: totalTimeout / (maxPages + 1)
|
|
6
|
+
* The +1 accounts for any overhead/rounding and ensures we never
|
|
7
|
+
* exceed the total tool timeout even if all pages are fetched.
|
|
8
|
+
*
|
|
9
|
+
* @param totalTimeout Total tool timeout in milliseconds
|
|
10
|
+
* @param maxPages Maximum number of pages to fetch
|
|
11
|
+
* @returns Per-page budget in milliseconds (rounded down)
|
|
12
|
+
*/
|
|
13
|
+
export function calcPageBudget(totalTimeout, maxPages) {
|
|
14
|
+
return Math.floor(totalTimeout / (maxPages + 1));
|
|
15
|
+
}
|
|
16
|
+
/* ── Pagination logic ───────────────────────────────────────────── */
|
|
17
|
+
/**
|
|
18
|
+
* Fetch paginated projects from the AWX API, consolidate results,
|
|
19
|
+
* and return them sorted by name.
|
|
20
|
+
*
|
|
21
|
+
* @param client The AWX HTTP client
|
|
22
|
+
* @param options Pagination and timeout options
|
|
23
|
+
* @returns Consolidated, sorted list of projects
|
|
24
|
+
*/
|
|
25
|
+
export async function listProjects(client, options) {
|
|
26
|
+
const maxPages = options?.maxPages ?? 5;
|
|
27
|
+
const pageSize = options?.pageSize ?? 50;
|
|
28
|
+
const totalTimeout = options?.timeout ?? 30_000;
|
|
29
|
+
const toolAbortSignal = options?.abortSignal;
|
|
30
|
+
const pageBudget = calcPageBudget(totalTimeout, maxPages);
|
|
31
|
+
const allProjects = [];
|
|
32
|
+
let page = 1;
|
|
33
|
+
let hasMore = true;
|
|
34
|
+
while (hasMore && page <= maxPages) {
|
|
35
|
+
// Check if tool has been aborted
|
|
36
|
+
if (toolAbortSignal?.aborted) {
|
|
37
|
+
throw toolAbortSignal.reason ?? new DOMException("Aborted", "AbortError");
|
|
38
|
+
}
|
|
39
|
+
// Create per-page timeout signal
|
|
40
|
+
const pageController = new AbortController();
|
|
41
|
+
const pageTimer = setTimeout(() => pageController.abort(new DOMException(`Page ${page} timed out after ${pageBudget}ms`, "TimeoutError")), pageBudget);
|
|
42
|
+
// Wire tool abort signal to page controller
|
|
43
|
+
const abortHandler = () => {
|
|
44
|
+
clearTimeout(pageTimer);
|
|
45
|
+
pageController.abort(toolAbortSignal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
46
|
+
};
|
|
47
|
+
if (toolAbortSignal && !toolAbortSignal.aborted) {
|
|
48
|
+
toolAbortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
// Build request path with pagination parameters
|
|
52
|
+
const path = `/api/v2/projects/?page=${page}&page_size=${pageSize}`;
|
|
53
|
+
const response = await client.request("awx-list-projects", path, { headers: { "Content-Type": "application/json" } }, pageController.signal);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(`Failed to fetch projects: ${response.status} ${response.statusText}`);
|
|
56
|
+
}
|
|
57
|
+
const data = await response.json();
|
|
58
|
+
allProjects.push(...data.results);
|
|
59
|
+
if (!data.next) {
|
|
60
|
+
hasMore = false;
|
|
61
|
+
}
|
|
62
|
+
page++;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
clearTimeout(pageTimer);
|
|
66
|
+
if (toolAbortSignal && !toolAbortSignal.aborted) {
|
|
67
|
+
toolAbortSignal.removeEventListener("abort", abortHandler);
|
|
68
|
+
}
|
|
69
|
+
pageController.abort(); // prevent any lingering signal listeners
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Sort consolidated results by name (case-insensitive alphanumeric)
|
|
73
|
+
allProjects.sort((a, b) => a.name.localeCompare(b.name));
|
|
74
|
+
const output = {
|
|
75
|
+
count: allProjects.length,
|
|
76
|
+
results: allProjects,
|
|
77
|
+
};
|
|
78
|
+
// If we exhausted the page cap but there are more pages, warn
|
|
79
|
+
if (page > maxPages && hasMore) {
|
|
80
|
+
output.warning = "More items exist. Increase max-pages or use a filter.";
|
|
81
|
+
}
|
|
82
|
+
return output;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=list-projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-projects.js","sourceRoot":"","sources":["../src/list-projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list-templates.ts — AWX job template listing with pagination consolidation
|
|
3
|
+
*
|
|
4
|
+
* Fetches job templates from the AWX /api/v2/job_templates/ endpoint,
|
|
5
|
+
* iterates through pages up to a configurable cap, sorts results by name,
|
|
6
|
+
* and enforces a per-page timeout budget.
|
|
7
|
+
*
|
|
8
|
+
* ## Timeout Budget
|
|
9
|
+
*
|
|
10
|
+
* Per-page timeout = toolTimeoutMs / (maxPages + 1).
|
|
11
|
+
* This ensures that even if every page times out sequentially, we never
|
|
12
|
+
* exceed the tool-level timeout (the +1 accounts for the tool overhead).
|
|
13
|
+
*
|
|
14
|
+
* ## Page Cap
|
|
15
|
+
*
|
|
16
|
+
* When maxPages > 0 and the cap is reached with more pages available,
|
|
17
|
+
* the output includes a warning field. The caller can detect truncation
|
|
18
|
+
* by checking for the presence of `warning`.
|
|
19
|
+
*/
|
|
20
|
+
import type { AwxClient } from "./client.js";
|
|
21
|
+
/** A simplified template result returned to the caller */
|
|
22
|
+
export interface TemplateResult {
|
|
23
|
+
id: number;
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
}
|
|
27
|
+
/** Output of the list-templates operation */
|
|
28
|
+
export interface ListTemplatesOutput {
|
|
29
|
+
count: number;
|
|
30
|
+
results: TemplateResult[];
|
|
31
|
+
warning?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Options for listTemplates */
|
|
34
|
+
export interface ListTemplatesOptions {
|
|
35
|
+
/** Number of items per page (default: 50) */
|
|
36
|
+
pageSize?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Maximum pages to fetch.
|
|
39
|
+
* 0 = no cap (fetch all pages).
|
|
40
|
+
* Default: 5 (5 pages × 50 items = 250 max).
|
|
41
|
+
*/
|
|
42
|
+
maxPages?: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* List AWX job templates with pagination consolidation.
|
|
46
|
+
*
|
|
47
|
+
* Fetches templates from `/api/v2/job_templates/`, iterating through
|
|
48
|
+
* pages up to the configured `maxPages` cap. Each page request gets a
|
|
49
|
+
* timeout budget of `toolTimeoutMs / (maxPages + 1)`.
|
|
50
|
+
*
|
|
51
|
+
* Results are sorted by name (alphanumeric, case-insensitive) before returning.
|
|
52
|
+
*
|
|
53
|
+
* @param client The AWX HTTP client
|
|
54
|
+
* @param toolTimeoutMs Tool-level timeout in ms (used for per-page budget)
|
|
55
|
+
* @param options Optional: pageSize, maxPages
|
|
56
|
+
* @param abortSignal Optional tool context abort signal for cancellation
|
|
57
|
+
* @returns Consolidated, sorted template list with count and optional warning
|
|
58
|
+
*/
|
|
59
|
+
export declare function listTemplates(client: AwxClient, toolTimeoutMs: number, options?: ListTemplatesOptions, abortSignal?: AbortSignal): Promise<ListTemplatesOutput>;
|
|
60
|
+
//# sourceMappingURL=list-templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-templates.d.ts","sourceRoot":"","sources":["../src/list-templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,gCAAgC;AAChC,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkFD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,oBAAoB,EAC9B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAiE9B"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// ── Helpers ───────────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* Combine multiple AbortSignals into one — aborts if ANY source aborts.
|
|
4
|
+
* Uses native AbortSignal.any() on Node 20+, falls back to manual wiring.
|
|
5
|
+
*/
|
|
6
|
+
function anyAbortSignal(signals) {
|
|
7
|
+
if (typeof AbortSignal.any === "function") {
|
|
8
|
+
return AbortSignal.any(signals);
|
|
9
|
+
}
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
for (const signal of signals) {
|
|
12
|
+
if (signal.aborted) {
|
|
13
|
+
controller.abort(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
14
|
+
return controller.signal;
|
|
15
|
+
}
|
|
16
|
+
signal.addEventListener("abort", () => {
|
|
17
|
+
controller.abort(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
18
|
+
}, { once: true });
|
|
19
|
+
}
|
|
20
|
+
return controller.signal;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create an AbortSignal that fires after `ms` milliseconds.
|
|
24
|
+
* Uses setTimeout + AbortController for Node 18+ compatibility.
|
|
25
|
+
*/
|
|
26
|
+
function createTimeoutSignal(ms) {
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
const timer = setTimeout(() => controller.abort(new DOMException("Page timeout.", "TimeoutError")), ms);
|
|
29
|
+
return {
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
clear: () => clearTimeout(timer),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract path + query string from a full AWX API URL.
|
|
36
|
+
* AWX pagination returns absolute URLs in `next` — we need just the path.
|
|
37
|
+
*/
|
|
38
|
+
function extractPath(fullUrl) {
|
|
39
|
+
try {
|
|
40
|
+
const url = new URL(fullUrl);
|
|
41
|
+
return url.pathname + (url.search || "");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// If it's already just a path, return as-is
|
|
45
|
+
return fullUrl;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Map a raw AWX template item to the simplified result shape */
|
|
49
|
+
function mapTemplate(item) {
|
|
50
|
+
return {
|
|
51
|
+
id: item.id,
|
|
52
|
+
name: item.name,
|
|
53
|
+
description: item.description ?? "",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// ── Core Logic ────────────────────────────────────────────────────
|
|
57
|
+
/**
|
|
58
|
+
* List AWX job templates with pagination consolidation.
|
|
59
|
+
*
|
|
60
|
+
* Fetches templates from `/api/v2/job_templates/`, iterating through
|
|
61
|
+
* pages up to the configured `maxPages` cap. Each page request gets a
|
|
62
|
+
* timeout budget of `toolTimeoutMs / (maxPages + 1)`.
|
|
63
|
+
*
|
|
64
|
+
* Results are sorted by name (alphanumeric, case-insensitive) before returning.
|
|
65
|
+
*
|
|
66
|
+
* @param client The AWX HTTP client
|
|
67
|
+
* @param toolTimeoutMs Tool-level timeout in ms (used for per-page budget)
|
|
68
|
+
* @param options Optional: pageSize, maxPages
|
|
69
|
+
* @param abortSignal Optional tool context abort signal for cancellation
|
|
70
|
+
* @returns Consolidated, sorted template list with count and optional warning
|
|
71
|
+
*/
|
|
72
|
+
export async function listTemplates(client, toolTimeoutMs, options, abortSignal) {
|
|
73
|
+
const pageSize = options?.pageSize ?? 50;
|
|
74
|
+
const maxPages = options?.maxPages ?? 5;
|
|
75
|
+
// Per-page timeout budget: divide tool timeout by (maxPages + 1).
|
|
76
|
+
// The +1 provides a safety margin for tool overhead after the last page.
|
|
77
|
+
const effectiveMaxPages = Math.max(1, maxPages);
|
|
78
|
+
const perPageBudget = Math.floor(toolTimeoutMs / (effectiveMaxPages + 1));
|
|
79
|
+
const allResults = [];
|
|
80
|
+
let nextPage = null;
|
|
81
|
+
let pagesFetched = 0;
|
|
82
|
+
do {
|
|
83
|
+
pagesFetched++;
|
|
84
|
+
// Build URL for current page
|
|
85
|
+
const path = nextPage
|
|
86
|
+
? extractPath(nextPage)
|
|
87
|
+
: `/api/v2/job_templates/?page_size=${pageSize}`;
|
|
88
|
+
// Fetch this page with per-page timeout
|
|
89
|
+
const { signal: pageSignal, clear: clearTimeout_ } = createTimeoutSignal(perPageBudget);
|
|
90
|
+
const combinedSignal = abortSignal
|
|
91
|
+
? anyAbortSignal([abortSignal, pageSignal])
|
|
92
|
+
: pageSignal;
|
|
93
|
+
try {
|
|
94
|
+
const response = await client.request("awx-list-templates", path, undefined, combinedSignal);
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new Error(`AWX API error: ${response.status} ${response.statusText}`);
|
|
97
|
+
}
|
|
98
|
+
const data = (await response.json());
|
|
99
|
+
allResults.push(...data.results.map(mapTemplate));
|
|
100
|
+
nextPage = data.next;
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
clearTimeout_();
|
|
104
|
+
}
|
|
105
|
+
// Stop when no more pages OR we've hit the cap.
|
|
106
|
+
// maxPages <= 0 means "no cap" (fetch all).
|
|
107
|
+
} while (nextPage !== null && (maxPages <= 0 || pagesFetched < maxPages));
|
|
108
|
+
// Sort consolidated results by name (alphanumeric, case-insensitive)
|
|
109
|
+
allResults.sort((a, b) => a.name.localeCompare(b.name));
|
|
110
|
+
const output = {
|
|
111
|
+
count: allResults.length,
|
|
112
|
+
results: allResults,
|
|
113
|
+
};
|
|
114
|
+
// If there are more pages but we hit the cap, emit a warning
|
|
115
|
+
if (nextPage !== null && maxPages > 0 && pagesFetched >= maxPages) {
|
|
116
|
+
output.warning = `Page cap of ${maxPages} page${maxPages !== 1 ? "s" : ""} reached. Some results may be omitted.`;
|
|
117
|
+
}
|
|
118
|
+
return output;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=list-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-templates.js","sourceRoot":"","sources":["../src/list-templates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* metrics.ts — Per-tool counters with file-backed durability
|
|
3
|
+
*
|
|
4
|
+
* Provides structured metrics for operational visibility and phase-gating:
|
|
5
|
+
* - Per-tool call count
|
|
6
|
+
* - Per-tool error count
|
|
7
|
+
* - Per-tool latency accumulation (ms)
|
|
8
|
+
* - Token expiry events (401 detection)
|
|
9
|
+
* - PowerShell fallback count (for deprecation monitoring)
|
|
10
|
+
*
|
|
11
|
+
* ## Durability Model
|
|
12
|
+
*
|
|
13
|
+
* Counters are file-backed so they survive plugin reloads. This is required
|
|
14
|
+
* for the Phase 2→3 gate which demands 14 consecutive days of zero PowerShell
|
|
15
|
+
* AWX calls.
|
|
16
|
+
*
|
|
17
|
+
* **File format:** JSON at a configurable path (default: `.metrics/metrics.json`).
|
|
18
|
+
* **Atomic writes:** Data is written to a `.tmp` file first, then renamed over
|
|
19
|
+
* the target — preventing corruption on partial writes.
|
|
20
|
+
* **Merge-on-load:** When `load()` is called, disk values are merged with
|
|
21
|
+
* in-memory counters using `Math.max()` — counters never decrease.
|
|
22
|
+
* **Missing file:** Treated as a fresh start (no error thrown).
|
|
23
|
+
*
|
|
24
|
+
* ## Integration Point
|
|
25
|
+
*
|
|
26
|
+
* Metrics hook into the client module at pipeline boundaries — not inside
|
|
27
|
+
* individual middleware. The `client.ts` request function calls `recordCall`,
|
|
28
|
+
* `recordError`, and `recordTokenExpiry` at the top level.
|
|
29
|
+
*
|
|
30
|
+
* ```typescript
|
|
31
|
+
* // In client.ts (pipeline boundary):
|
|
32
|
+
* const start = Date.now();
|
|
33
|
+
* try {
|
|
34
|
+
* const response = await fetch(...);
|
|
35
|
+
* metrics.recordCall(toolName, Date.now() - start);
|
|
36
|
+
* if (response.status === 401) metrics.recordTokenExpiry(toolName);
|
|
37
|
+
* return response;
|
|
38
|
+
* } catch (err) {
|
|
39
|
+
* metrics.recordError(toolName);
|
|
40
|
+
* throw err;
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
/** Per-tool metrics counters */
|
|
45
|
+
export interface ToolMetrics {
|
|
46
|
+
/** Total number of tool calls (successful or not) */
|
|
47
|
+
callCount: number;
|
|
48
|
+
/** Total number of errors (any error, including HTTP and network errors) */
|
|
49
|
+
errorCount: number;
|
|
50
|
+
/** Accumulated latency in milliseconds (sum of all call latencies) */
|
|
51
|
+
totalLatencyMs: number;
|
|
52
|
+
/** Number of 401 Unauthorized responses (token expiry detection) */
|
|
53
|
+
tokenExpiryEvents: number;
|
|
54
|
+
/** Number of times the plugin fell back to PowerShell for this tool */
|
|
55
|
+
psFallbackCount: number;
|
|
56
|
+
}
|
|
57
|
+
/** Create a zeroed ToolMetrics object */
|
|
58
|
+
export declare function createDefaultMetrics(): ToolMetrics;
|
|
59
|
+
/**
|
|
60
|
+
* Thread-safe (in-memory) metrics store with file-backed durability.
|
|
61
|
+
*
|
|
62
|
+
* All recording methods are synchronous and fast — they mutate an in-memory
|
|
63
|
+
* Map. Persistence is explicit (call `persist()`) so the caller controls when
|
|
64
|
+
* to flush to disk.
|
|
65
|
+
*
|
|
66
|
+
* The durability model is **additive-merge**: on `load()`, existing in-memory
|
|
67
|
+
* counters are never decreased. This prevents race conditions where an
|
|
68
|
+
* in-memory increment during a concurrent persist window would be lost.
|
|
69
|
+
*/
|
|
70
|
+
export declare class MetricsStore {
|
|
71
|
+
/** Per-tool counters, keyed by tool name */
|
|
72
|
+
private counters;
|
|
73
|
+
/** File path for persistence (default: `.metrics/metrics.json`) */
|
|
74
|
+
private persistPath;
|
|
75
|
+
/**
|
|
76
|
+
* @param persistPath - Absolute or relative path to the metrics JSON file.
|
|
77
|
+
* Defaults to `.metrics/metrics.json` relative to the current working directory.
|
|
78
|
+
*/
|
|
79
|
+
constructor(persistPath?: string);
|
|
80
|
+
/** Get or create the ToolMetrics entry for a tool name */
|
|
81
|
+
private ensure;
|
|
82
|
+
/**
|
|
83
|
+
* Record a tool call with its latency.
|
|
84
|
+
* Called by the client at the pipeline boundary after a successful (or
|
|
85
|
+
* error-handled) fetch completes.
|
|
86
|
+
*
|
|
87
|
+
* @param toolName - The registered tool name (e.g., "list-templates", "launch-job")
|
|
88
|
+
* @param latencyMs - Round-trip latency in milliseconds
|
|
89
|
+
*/
|
|
90
|
+
recordCall(toolName: string, latencyMs: number): void;
|
|
91
|
+
/**
|
|
92
|
+
* Record an error for a tool.
|
|
93
|
+
* Called by the client when a fetch request fails (any error: 4xx, 5xx,
|
|
94
|
+
* network error, timeout, abort).
|
|
95
|
+
*/
|
|
96
|
+
recordError(toolName: string): void;
|
|
97
|
+
/**
|
|
98
|
+
* Record a token expiry event (HTTP 401).
|
|
99
|
+
* Called by the client when a request returns 401 Unauthorized, indicating
|
|
100
|
+
* the bearer token has expired or been revoked.
|
|
101
|
+
*/
|
|
102
|
+
recordTokenExpiry(toolName: string): void;
|
|
103
|
+
/**
|
|
104
|
+
* Record a PowerShell fallback for a tool.
|
|
105
|
+
* Called when the plugin tool cannot complete the operation and falls back
|
|
106
|
+
* to the legacy PowerShell script. This counter is tracked per-tool for
|
|
107
|
+
* granular deprecation monitoring (Phase 2→3 gate requires zero PS calls
|
|
108
|
+
* across all tools for 14 consecutive days).
|
|
109
|
+
*/
|
|
110
|
+
recordPsFallback(toolName: string): void;
|
|
111
|
+
/**
|
|
112
|
+
* Get metrics for a specific tool.
|
|
113
|
+
* Returns a default zeroed object if the tool has never been recorded.
|
|
114
|
+
*/
|
|
115
|
+
getMetrics(toolName: string): ToolMetrics;
|
|
116
|
+
/**
|
|
117
|
+
* Get all tracked tools' metrics as a plain object.
|
|
118
|
+
* Returns deep copies — mutations do not affect the store.
|
|
119
|
+
*/
|
|
120
|
+
getAllMetrics(): Record<string, ToolMetrics>;
|
|
121
|
+
/**
|
|
122
|
+
* Persist current metrics to disk using an atomic write strategy.
|
|
123
|
+
*
|
|
124
|
+
* **Atomicity**: Data is written to a `.tmp` file first, then renamed
|
|
125
|
+
* over the target. If the process crashes during the write, the original
|
|
126
|
+
* file remains intact.
|
|
127
|
+
*
|
|
128
|
+
* **Directory creation**: The parent directory is created recursively if
|
|
129
|
+
* it doesn't exist.
|
|
130
|
+
*/
|
|
131
|
+
persist(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Load metrics from disk and merge with in-memory counters.
|
|
134
|
+
*
|
|
135
|
+
* **Merge strategy (additive)**: For each tool, each counter is set to
|
|
136
|
+
* `Math.max(inMemory, onDisk)`. This ensures:
|
|
137
|
+
* - Counters never decrease (no lost increments).
|
|
138
|
+
* - Concurrent increments during a load window are preserved.
|
|
139
|
+
*
|
|
140
|
+
* **Missing file**: Treated as a fresh start (no error). In-memory
|
|
141
|
+
* counters are left unchanged — a subsequent `persist()` will create
|
|
142
|
+
* the file.
|
|
143
|
+
*/
|
|
144
|
+
load(): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Reset all counters to zero.
|
|
147
|
+
* Useful for testing or for clearing metrics between sessions.
|
|
148
|
+
*/
|
|
149
|
+
reset(): void;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Set up periodic persistence for a MetricsStore.
|
|
153
|
+
*
|
|
154
|
+
* Starts a `setInterval` that calls `store.persist()` at the given interval.
|
|
155
|
+
* Returns a `clear()` function that stops the interval and does a final
|
|
156
|
+
* persist to ensure in-memory counters are flushed to disk.
|
|
157
|
+
*
|
|
158
|
+
* This is the integration point for the plugin lifecycle:
|
|
159
|
+
* 1. Plugin's `server()` creates a `MetricsStore` and calls `store.load()`.
|
|
160
|
+
* 2. Plugin's `server()` calls this helper to start periodic persistence.
|
|
161
|
+
* 3. Plugin's `dispose()` hook calls `clear()` to stop the interval and
|
|
162
|
+
* perform a final persist.
|
|
163
|
+
*
|
|
164
|
+
* @param store - The MetricsStore to persist periodically
|
|
165
|
+
* @param intervalMs - Interval in milliseconds (default: 30_000 = 30s)
|
|
166
|
+
* @param onError - Optional callback invoked when a persist attempt fails.
|
|
167
|
+
* Receives the error object so the caller can surface
|
|
168
|
+
* failures (e.g., via app logging) without crashing the
|
|
169
|
+
* interval.
|
|
170
|
+
*/
|
|
171
|
+
export declare function setupMetricsPersistence(store: MetricsStore, intervalMs?: number, onError?: (err: unknown) => void): {
|
|
172
|
+
clear: () => Promise<void>;
|
|
173
|
+
};
|
|
174
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAIH,gCAAgC;AAChC,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAC;CACzB;AAWD,yCAAyC;AACzC,wBAAgB,oBAAoB,IAAI,WAAW,CAQlD;AAID;;;;;;;;;;GAUG;AACH,qBAAa,YAAY;IACvB,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAuC;IAEvD,mEAAmE;IACnE,OAAO,CAAC,WAAW,CAAS;IAE5B;;;OAGG;gBACS,WAAW,CAAC,EAAE,MAAM;IAMhC,0DAA0D;IAC1D,OAAO,CAAC,MAAM;IAWd;;;;;;;OAOG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAMrD;;;;OAIG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKnC;;;;OAIG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKzC;;;;;;OAMG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAOxC;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW;IAOzC;;;OAGG;IACH,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC;IAU5C;;;;;;;;;OASG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB9B;;;;;;;;;;;OAWG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC3B;;;OAGG;IACH,KAAK,IAAI,IAAI;CAGd;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,YAAY,EACnB,UAAU,GAAE,MAAe,EAC3B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAC/B;IAAE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAwBhC"}
|