nexus-agentd 0.2.5 → 0.3.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 +359 -250
- package/dist/a2a/runtime.d.ts +7 -1
- package/dist/a2a/runtime.js +290 -17
- package/dist/acp/runtime.d.ts +5 -1
- package/dist/acp/runtime.js +88 -39
- package/dist/artifact-store.d.ts +162 -0
- package/dist/artifact-store.js +1081 -0
- package/dist/cli.js +13 -14
- package/dist/completion-contract.js +1 -1
- package/dist/config.js +45 -5
- package/dist/control-plane-validation.d.ts +54 -0
- package/dist/control-plane-validation.js +152 -0
- package/dist/control-plane.d.ts +9 -32
- package/dist/control-plane.js +52 -158
- package/dist/drivers/index.js +12 -0
- package/dist/drivers/pi.js +1 -1
- package/dist/drivers/stdio.d.ts +4 -1
- package/dist/drivers/stdio.js +135 -19
- package/dist/events.d.ts +9 -0
- package/dist/events.js +17 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +33 -6
- package/dist/process-tree.js +15 -6
- package/dist/run-store.d.ts +57 -7
- package/dist/run-store.js +188 -23
- package/dist/server/auth-policy.d.ts +27 -0
- package/dist/server/auth-policy.js +143 -0
- package/dist/server/connection.d.ts +8 -0
- package/dist/server/connection.js +67 -0
- package/dist/server/diagnostics.d.ts +40 -0
- package/dist/server/diagnostics.js +130 -0
- package/dist/server/metrics.d.ts +51 -0
- package/dist/server/metrics.js +56 -0
- package/dist/server/quota.d.ts +59 -0
- package/dist/server/quota.js +176 -0
- package/dist/server/validation.d.ts +29 -0
- package/dist/server/validation.js +221 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +682 -418
- package/dist/session/admin-runs.d.ts +73 -0
- package/dist/session/admin-runs.js +297 -0
- package/dist/session/admission.d.ts +32 -0
- package/dist/session/admission.js +62 -0
- package/dist/session/readiness.d.ts +33 -0
- package/dist/session/readiness.js +174 -0
- package/dist/session-contract.d.ts +1 -0
- package/dist/session.d.ts +64 -7
- package/dist/session.js +321 -159
- package/dist/sse-writer.d.ts +7 -0
- package/dist/sse-writer.js +60 -0
- package/dist/types.d.ts +44 -2
- package/dist/types.js +1 -0
- package/dist/webui/app/sources.js +18 -10
- package/dist/webui/index.js +11 -11
- package/dist/webui/markup.js +69 -66
- package/dist/webui/styles.js +791 -616
- package/dist/workspace.d.ts +30 -0
- package/dist/workspace.js +67 -2
- package/examples/client.mjs +102 -0
- package/nexus-agentd.example.json +21 -2
- package/package.json +6 -3
package/dist/acp/runtime.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { mkdir,
|
|
2
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { Readable, Writable } from 'node:stream';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
6
|
import * as acp from '@agentclientprotocol/sdk';
|
|
7
7
|
import { terminateProcessTree } from '../process-tree.js';
|
|
8
8
|
import { TURN_COMPLETION_CONTRACT } from '../completion-contract.js';
|
|
9
|
-
|
|
9
|
+
import { readContainedFile } from '../workspace.js';
|
|
10
|
+
// Matches MAX_PUBLISHED_FILE_BYTES in session.ts; larger files are encoded only to be dropped by the base64 budget.
|
|
11
|
+
const MAX_MEDIA_MARKER_FILE_BYTES = 12 * 1024 * 1024;
|
|
12
|
+
// The agent controls how many markers it emits, so bound the reads per turn.
|
|
13
|
+
const MAX_MEDIA_MARKER_ARTIFACTS = 8;
|
|
10
14
|
export class AcpProcessRuntime {
|
|
11
15
|
driver;
|
|
12
16
|
sink;
|
|
17
|
+
allowedRoots;
|
|
18
|
+
startupTimeoutMs;
|
|
13
19
|
process;
|
|
14
20
|
connection;
|
|
15
21
|
pending;
|
|
@@ -22,9 +28,11 @@ export class AcpProcessRuntime {
|
|
|
22
28
|
inputDirectory;
|
|
23
29
|
workspace;
|
|
24
30
|
promptOutput = '';
|
|
25
|
-
constructor(driver, sink, maxStderrChunkChars = 16 * 1024, promptTimeoutMs = 30 * 60 * 1000) {
|
|
31
|
+
constructor(driver, sink, maxStderrChunkChars = 16 * 1024, promptTimeoutMs = 30 * 60 * 1000, allowedRoots = [], startupTimeoutMs = 30_000) {
|
|
26
32
|
this.driver = driver;
|
|
27
33
|
this.sink = sink;
|
|
34
|
+
this.allowedRoots = allowedRoots;
|
|
35
|
+
this.startupTimeoutMs = startupTimeoutMs;
|
|
28
36
|
const limit = Number.isFinite(maxStderrChunkChars)
|
|
29
37
|
? Math.floor(maxStderrChunkChars)
|
|
30
38
|
: 16 * 1024;
|
|
@@ -32,6 +40,21 @@ export class AcpProcessRuntime {
|
|
|
32
40
|
this.promptTimeoutMs = Math.max(10_000, Math.trunc(promptTimeoutMs));
|
|
33
41
|
}
|
|
34
42
|
async start(workspace) {
|
|
43
|
+
if (this.disposed)
|
|
44
|
+
throw new Error('ACP runtime is disposed');
|
|
45
|
+
if (this.process)
|
|
46
|
+
throw new Error('ACP runtime is already started');
|
|
47
|
+
try {
|
|
48
|
+
await withTimeout(this.connect(workspace), this.startupTimeoutMs, 'ACP startup timed out');
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (this.sink.state !== 'canceled')
|
|
52
|
+
this.onProcessFailure(error);
|
|
53
|
+
await this.dispose();
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async connect(workspace) {
|
|
35
58
|
if (this.process)
|
|
36
59
|
throw new Error('ACP runtime is already started');
|
|
37
60
|
this.workspace = workspace;
|
|
@@ -69,6 +92,8 @@ export class AcpProcessRuntime {
|
|
|
69
92
|
version: '0.2.2'
|
|
70
93
|
}
|
|
71
94
|
});
|
|
95
|
+
if (this.disposed)
|
|
96
|
+
throw new Error('ACP runtime is disposed');
|
|
72
97
|
if (initialize.protocolVersion !== acp.PROTOCOL_VERSION) {
|
|
73
98
|
this.sink.emit('terminal_output', {
|
|
74
99
|
stream: 'system',
|
|
@@ -80,10 +105,14 @@ export class AcpProcessRuntime {
|
|
|
80
105
|
cwd: workspace,
|
|
81
106
|
mcpServers: []
|
|
82
107
|
});
|
|
108
|
+
if (this.disposed)
|
|
109
|
+
throw new Error('ACP runtime is disposed');
|
|
83
110
|
this.sink.setAcpSessionId(String(session.sessionId));
|
|
84
111
|
this.sink.setState('created');
|
|
85
112
|
}
|
|
86
113
|
async prompt(message, attachments = []) {
|
|
114
|
+
if (this.disposed)
|
|
115
|
+
throw new Error('ACP runtime is disposed');
|
|
87
116
|
if (!this.connection)
|
|
88
117
|
throw new Error('ACP runtime is not connected');
|
|
89
118
|
if (this.prompting)
|
|
@@ -157,11 +186,19 @@ export class AcpProcessRuntime {
|
|
|
157
186
|
this.finishPermission({ outcome: { outcome: 'cancelled' } });
|
|
158
187
|
return;
|
|
159
188
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
189
|
+
// Protocol IDs are opaque: never interpret a numeric ID as an index or
|
|
190
|
+
// fold its case into a different (possibly permanent) permission.
|
|
191
|
+
if (response.optionId !== undefined) {
|
|
192
|
+
const option = options.find((item) => item.id === response.optionId);
|
|
193
|
+
if (!option)
|
|
194
|
+
throw new Error('Permission optionId does not match an offered option');
|
|
195
|
+
this.finishPermission({ outcome: { outcome: 'selected', optionId: option.id } });
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (response.action === 'accept' && !response.optionId) {
|
|
199
|
+
const option = options.find((item) => item.kind === 'allow_once');
|
|
163
200
|
if (!option) {
|
|
164
|
-
throw new Error('Permission request
|
|
201
|
+
throw new Error('Permission request requires an explicit optionId; no allow_once option is available');
|
|
165
202
|
}
|
|
166
203
|
this.finishPermission({
|
|
167
204
|
outcome: { outcome: 'selected', optionId: option.id }
|
|
@@ -199,11 +236,12 @@ export class AcpProcessRuntime {
|
|
|
199
236
|
if (this.pendingInput) {
|
|
200
237
|
this.finishInput('cancel');
|
|
201
238
|
}
|
|
202
|
-
|
|
239
|
+
this.sink.setState('canceled');
|
|
240
|
+
if (this.connection) {
|
|
203
241
|
try {
|
|
204
|
-
await this.connection.agent.notify(acp.methods.agent.session.cancel, {
|
|
242
|
+
await withTimeout(this.connection.agent.notify(acp.methods.agent.session.cancel, {
|
|
205
243
|
sessionId: this.requireSessionId()
|
|
206
|
-
});
|
|
244
|
+
}), 2_000, 'ACP cancellation notification timed out');
|
|
207
245
|
}
|
|
208
246
|
catch (error) {
|
|
209
247
|
this.sink.emit('terminal_output', {
|
|
@@ -229,8 +267,16 @@ export class AcpProcessRuntime {
|
|
|
229
267
|
this.pendingInput.resolve({ action: 'cancel' });
|
|
230
268
|
this.pendingInput = undefined;
|
|
231
269
|
}
|
|
232
|
-
this.connection
|
|
270
|
+
const connection = this.connection;
|
|
233
271
|
this.connection = undefined;
|
|
272
|
+
try {
|
|
273
|
+
connection?.close();
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
this.sink.emit('terminal_output', {
|
|
277
|
+
stream: 'system', text: `ACP connection close failed: ${error instanceof Error ? error.message : String(error)}`
|
|
278
|
+
});
|
|
279
|
+
}
|
|
234
280
|
const child = this.process;
|
|
235
281
|
this.process = undefined;
|
|
236
282
|
if (child) {
|
|
@@ -243,6 +289,10 @@ export class AcpProcessRuntime {
|
|
|
243
289
|
this.inputDirectory = undefined;
|
|
244
290
|
}
|
|
245
291
|
}
|
|
292
|
+
isAvailable() {
|
|
293
|
+
return !this.disposed && Boolean(this.connection) &&
|
|
294
|
+
(!this.process || (this.process.exitCode === null && this.process.signalCode === null));
|
|
295
|
+
}
|
|
246
296
|
requestPermission(params) {
|
|
247
297
|
if (this.driver.permissionPolicy === 'deny') {
|
|
248
298
|
const reject = params.options.find((option) => option.kind.startsWith('reject'));
|
|
@@ -256,8 +306,7 @@ export class AcpProcessRuntime {
|
|
|
256
306
|
: { outcome: { outcome: 'cancelled' } };
|
|
257
307
|
}
|
|
258
308
|
if (this.driver.permissionPolicy === 'allow') {
|
|
259
|
-
const allow = params.options.find((option) => option.kind.toLowerCase() === 'allow_once')
|
|
260
|
-
params.options.find((option) => option.kind.toLowerCase().startsWith('allow'));
|
|
309
|
+
const allow = params.options.find((option) => option.kind.toLowerCase() === 'allow_once');
|
|
261
310
|
return allow
|
|
262
311
|
? {
|
|
263
312
|
outcome: {
|
|
@@ -428,13 +477,23 @@ export class AcpProcessRuntime {
|
|
|
428
477
|
if (!markers.length)
|
|
429
478
|
return;
|
|
430
479
|
this.sink.replaceOutput(withoutMediaMarkers(this.promptOutput));
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
480
|
+
const roots = this.allowedRoots.length
|
|
481
|
+
? this.allowedRoots
|
|
482
|
+
: this.workspace
|
|
483
|
+
? [this.workspace]
|
|
484
|
+
: [];
|
|
485
|
+
const unique = Array.from(new Set(markers));
|
|
486
|
+
const accepted = unique.slice(0, MAX_MEDIA_MARKER_ARTIFACTS);
|
|
487
|
+
const ignored = unique.length - accepted.length;
|
|
488
|
+
if (ignored > 0) {
|
|
489
|
+
this.sink.emit('terminal_output', {
|
|
490
|
+
stream: 'system',
|
|
491
|
+
text: `Ignored ${ignored} MEDIA marker(s); at most ${MAX_MEDIA_MARKER_ARTIFACTS} artifacts can be attached per turn.`
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
for (const marker of accepted) {
|
|
436
495
|
try {
|
|
437
|
-
const artifact = await artifactFromMediaMarker(marker, `media:${randomUUID()}`,
|
|
496
|
+
const artifact = await artifactFromMediaMarker(marker, `media:${randomUUID()}`, roots);
|
|
438
497
|
this.sink.addArtifact(artifact);
|
|
439
498
|
this.sink.emit('artifact', artifact);
|
|
440
499
|
}
|
|
@@ -679,7 +738,7 @@ function withoutMediaMarkers(output) {
|
|
|
679
738
|
.join('\n')
|
|
680
739
|
.trim();
|
|
681
740
|
}
|
|
682
|
-
async function artifactFromMediaMarker(marker, id,
|
|
741
|
+
async function artifactFromMediaMarker(marker, id, roots) {
|
|
683
742
|
if (/^https?:\/\//i.test(marker)) {
|
|
684
743
|
const filename = artifactFilename(marker, undefined, 'resource', undefined);
|
|
685
744
|
return {
|
|
@@ -690,29 +749,19 @@ async function artifactFromMediaMarker(marker, id, workspace) {
|
|
|
690
749
|
metadata: { source: 'acp_media_marker' }
|
|
691
750
|
};
|
|
692
751
|
}
|
|
693
|
-
const localPath = marker.startsWith('file:')
|
|
694
|
-
|
|
695
|
-
:
|
|
696
|
-
|
|
697
|
-
? localPath
|
|
698
|
-
: path.resolve(workspace || process.cwd(), localPath));
|
|
699
|
-
const details = await stat(resolved);
|
|
700
|
-
if (!details.isFile()) {
|
|
701
|
-
throw new Error('declared path is not a regular file');
|
|
702
|
-
}
|
|
703
|
-
if (details.size > MAX_MEDIA_MARKER_FILE_BYTES) {
|
|
704
|
-
throw new Error(`declared file exceeds the ${MAX_MEDIA_MARKER_FILE_BYTES} byte limit`);
|
|
705
|
-
}
|
|
706
|
-
const filename = path.basename(resolved);
|
|
752
|
+
const localPath = marker.startsWith('file:') ? fileURLToPath(marker) : marker;
|
|
753
|
+
const file = await readContainedFile(roots, localPath, {
|
|
754
|
+
maxBytes: MAX_MEDIA_MARKER_FILE_BYTES
|
|
755
|
+
});
|
|
707
756
|
return {
|
|
708
757
|
id,
|
|
709
|
-
name: filename,
|
|
710
|
-
filename,
|
|
711
|
-
mediaType: mediaTypeForPath(filename),
|
|
712
|
-
bytesBase64:
|
|
758
|
+
name: file.filename,
|
|
759
|
+
filename: file.filename,
|
|
760
|
+
mediaType: mediaTypeForPath(file.filename),
|
|
761
|
+
bytesBase64: file.bytes.toString('base64'),
|
|
713
762
|
metadata: {
|
|
714
763
|
source: 'acp_media_marker',
|
|
715
|
-
size:
|
|
764
|
+
size: file.size
|
|
716
765
|
}
|
|
717
766
|
};
|
|
718
767
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { AgentdArtifact, AgentdRunArtifactView } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The value is persisted with run metadata so callers can distinguish a
|
|
4
|
+
* downloadable artifact from one for which the gateway only retained
|
|
5
|
+
* protocol metadata.
|
|
6
|
+
*/
|
|
7
|
+
export type ArtifactStorageStatus = 'pending' | 'available' | 'metadata_only' | 'expired' | 'evicted' | 'failed';
|
|
8
|
+
export interface StoredArtifactView extends AgentdRunArtifactView {
|
|
9
|
+
id: string;
|
|
10
|
+
storageStatus: ArtifactStorageStatus;
|
|
11
|
+
downloadable: boolean;
|
|
12
|
+
size?: number;
|
|
13
|
+
sha256?: string;
|
|
14
|
+
createdAt: number;
|
|
15
|
+
storedAt?: number;
|
|
16
|
+
expiresAt?: number;
|
|
17
|
+
storageError?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ArtifactReadResult {
|
|
20
|
+
bytes: Buffer;
|
|
21
|
+
filename: string;
|
|
22
|
+
mediaType?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ArtifactStoreMetrics {
|
|
25
|
+
totalPayloadBytes: number;
|
|
26
|
+
/** Short alias useful to metrics consumers. */
|
|
27
|
+
payloadBytes: number;
|
|
28
|
+
queuedBytes: number;
|
|
29
|
+
pendingWrites: number;
|
|
30
|
+
retainedPayloads: number;
|
|
31
|
+
retainedRuns: number;
|
|
32
|
+
lastWriteMs: number;
|
|
33
|
+
writeFailures: number;
|
|
34
|
+
maxTotalBytes: number;
|
|
35
|
+
maxQueuedBytes: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A small, deliberately structural IO seam. Tests can inject failures or
|
|
39
|
+
* latency without replacing the filesystem globally. Production callers can
|
|
40
|
+
* leave this unset and the node fs/promises implementation is used.
|
|
41
|
+
*/
|
|
42
|
+
export interface ArtifactStoreIo {
|
|
43
|
+
mkdir?: (...args: any[]) => Promise<any>;
|
|
44
|
+
readFile?: (...args: any[]) => Promise<any>;
|
|
45
|
+
writeFile?: (...args: any[]) => Promise<any>;
|
|
46
|
+
rename?: (...args: any[]) => Promise<any>;
|
|
47
|
+
chmod?: (...args: any[]) => Promise<any>;
|
|
48
|
+
readdir?: (...args: any[]) => Promise<any>;
|
|
49
|
+
lstat?: (...args: any[]) => Promise<any>;
|
|
50
|
+
unlink?: (...args: any[]) => Promise<any>;
|
|
51
|
+
rm?: (...args: any[]) => Promise<any>;
|
|
52
|
+
open?: (...args: any[]) => Promise<any>;
|
|
53
|
+
}
|
|
54
|
+
export interface ArtifactStoreOptions {
|
|
55
|
+
/** Directory owned by this store. Defaults to `<run-file>.artifacts`. */
|
|
56
|
+
rootDir?: string;
|
|
57
|
+
/** Alias accepted for configuration adapters. */
|
|
58
|
+
artifactRootDir?: string;
|
|
59
|
+
/** Maximum payload size for one artifact. Defaults to 12 MiB. */
|
|
60
|
+
maxArtifactBytes?: number;
|
|
61
|
+
/** Maximum number of artifact metadata entries retained per run. */
|
|
62
|
+
maxArtifactsPerRun?: number;
|
|
63
|
+
/** Maximum aggregate payload bytes owned by this store. */
|
|
64
|
+
maxTotalBytes?: number;
|
|
65
|
+
/** Alias accepted for history/disk budget configuration. */
|
|
66
|
+
maxDiskBytes?: number;
|
|
67
|
+
/** Maximum bytes awaiting asynchronous persistence. */
|
|
68
|
+
maxQueuedBytes?: number;
|
|
69
|
+
/** Payload retention duration. Zero expires payloads immediately. */
|
|
70
|
+
retentionDays?: number;
|
|
71
|
+
/** Clock seam used by retention and deterministic tests. */
|
|
72
|
+
now?: () => number;
|
|
73
|
+
/** Optional IO seam used by tests. */
|
|
74
|
+
io?: ArtifactStoreIo;
|
|
75
|
+
/** Called whenever the public metadata view changes. */
|
|
76
|
+
onViewsChanged?: (runId: string, views: StoredArtifactView[]) => void;
|
|
77
|
+
/** Active runs are protected from retention and disk-budget eviction. */
|
|
78
|
+
isRunActive?: (runId: string) => boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Durable payload store for run artifacts.
|
|
82
|
+
*
|
|
83
|
+
* The store keeps an index in memory and a small manifest per run. Payloads
|
|
84
|
+
* are written into a hashed run directory with hashed file names; incoming
|
|
85
|
+
* artifact ids, URLs, and filenames are never used as filesystem paths.
|
|
86
|
+
*/
|
|
87
|
+
export declare class ArtifactStore {
|
|
88
|
+
readonly rootDir: string;
|
|
89
|
+
readonly maxArtifactBytes: number;
|
|
90
|
+
readonly maxArtifactsPerRun: number;
|
|
91
|
+
readonly maxTotalBytes: number;
|
|
92
|
+
readonly maxQueuedBytes: number;
|
|
93
|
+
readonly retentionDays: number;
|
|
94
|
+
private readonly now;
|
|
95
|
+
private readonly io;
|
|
96
|
+
private readonly onViewsChanged?;
|
|
97
|
+
private readonly isRunActive;
|
|
98
|
+
private readonly entries;
|
|
99
|
+
private readonly runIds;
|
|
100
|
+
private readonly pending;
|
|
101
|
+
private readonly inFlight;
|
|
102
|
+
private readonly pendingDeletes;
|
|
103
|
+
private queuedBytes;
|
|
104
|
+
private totalBytes;
|
|
105
|
+
private activeDrain?;
|
|
106
|
+
private initialized;
|
|
107
|
+
private lastError?;
|
|
108
|
+
private lastWriteMs;
|
|
109
|
+
private writeFailures;
|
|
110
|
+
constructor(rootDir: string, options?: ArtifactStoreOptions);
|
|
111
|
+
init(): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Reconcile metadata loaded from the legacy run JSON file with manifests
|
|
114
|
+
* found on disk. A run JSON file remains the source of truth for metadata;
|
|
115
|
+
* manifests only make payload bytes durable.
|
|
116
|
+
*/
|
|
117
|
+
restoreRun(runId: string, views: AgentdRunArtifactView[]): StoredArtifactView[];
|
|
118
|
+
/**
|
|
119
|
+
* Record protocol artifacts synchronously. Payload conversion and queue
|
|
120
|
+
* insertion happen in memory; disk IO is scheduled in the background.
|
|
121
|
+
*/
|
|
122
|
+
recordArtifacts(runId: string, artifacts: AgentdArtifact[]): StoredArtifactView[];
|
|
123
|
+
/** Merge display metadata from a RunStore update without dropping storage state. */
|
|
124
|
+
mergeViews(runId: string, views: AgentdRunArtifactView[], notify?: boolean): StoredArtifactView[];
|
|
125
|
+
views(runId: string): StoredArtifactView[];
|
|
126
|
+
readArtifact(runId: string, artifactId: string): Promise<ArtifactReadResult | undefined>;
|
|
127
|
+
/** Queue removal of one run's own directory. Active runs are protected. */
|
|
128
|
+
removeRun(runId: string): void;
|
|
129
|
+
flush(): Promise<void>;
|
|
130
|
+
get queuedByteCount(): number;
|
|
131
|
+
get storedByteCount(): number;
|
|
132
|
+
stats(): ArtifactStoreMetrics;
|
|
133
|
+
private runDrain;
|
|
134
|
+
private scheduleDrain;
|
|
135
|
+
private drainLoop;
|
|
136
|
+
private writePending;
|
|
137
|
+
private ensureBudget;
|
|
138
|
+
private evict;
|
|
139
|
+
private prune;
|
|
140
|
+
private expire;
|
|
141
|
+
private deleteRunDirectory;
|
|
142
|
+
private writeManifest;
|
|
143
|
+
private loadManifests;
|
|
144
|
+
private cleanupTemps;
|
|
145
|
+
private ensureDirectory;
|
|
146
|
+
private realDirectory;
|
|
147
|
+
private realPath;
|
|
148
|
+
private readRegularFile;
|
|
149
|
+
private safeUnlink;
|
|
150
|
+
private runDirectory;
|
|
151
|
+
private manifestPath;
|
|
152
|
+
private payloadPath;
|
|
153
|
+
private viewsForRun;
|
|
154
|
+
private addPending;
|
|
155
|
+
private removePending;
|
|
156
|
+
private requeue;
|
|
157
|
+
private emit;
|
|
158
|
+
private assertInitialized;
|
|
159
|
+
}
|
|
160
|
+
export declare class ArtifactBudgetError extends Error {
|
|
161
|
+
}
|
|
162
|
+
export declare function artifactStorePathForRunStore(runStorePath: string): string;
|