@corenel/harness 0.17.0 → 0.18.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/dist/prompts/library/defaults.generated.js +9 -9
- package/dist/prompts/library/defaults.generated.js.map +1 -1
- package/dist/sidecar/host.d.ts +40 -5
- package/dist/sidecar/host.d.ts.map +1 -1
- package/dist/sidecar/host.js +132 -2
- package/dist/sidecar/host.js.map +1 -1
- package/dist/sidecar/memberGate.d.ts.map +1 -1
- package/dist/sidecar/memberGate.js +5 -0
- package/dist/sidecar/memberGate.js.map +1 -1
- package/dist/sidecar/server.d.ts +38 -2
- package/dist/sidecar/server.d.ts.map +1 -1
- package/dist/sidecar/server.js +193 -2
- package/dist/sidecar/server.js.map +1 -1
- package/dist/tools/urlGuard.d.ts +11 -2
- package/dist/tools/urlGuard.d.ts.map +1 -1
- package/dist/tools/urlGuard.js +70 -15
- package/dist/tools/urlGuard.js.map +1 -1
- package/dist/workflows/results/resultFile.d.ts +23 -0
- package/dist/workflows/results/resultFile.d.ts.map +1 -0
- package/dist/workflows/results/resultFile.js +70 -0
- package/dist/workflows/results/resultFile.js.map +1 -0
- package/dist/workflows/results/resultStore.d.ts +37 -0
- package/dist/workflows/results/resultStore.d.ts.map +1 -0
- package/dist/workflows/results/resultStore.js +191 -0
- package/dist/workflows/results/resultStore.js.map +1 -0
- package/package.json +2 -2
package/dist/tools/urlGuard.d.ts
CHANGED
|
@@ -5,12 +5,21 @@ export type OutboundResolver = (host: string) => Promise<string[]>;
|
|
|
5
5
|
/** Install (or clear, with null) the resolver safeFetch uses to check that a
|
|
6
6
|
* name does not resolve to an internal address. */
|
|
7
7
|
export declare function setOutboundResolver(fn: OutboundResolver | null): void;
|
|
8
|
+
/** The guard refused a request: the URL, a redirect hop, or an address a
|
|
9
|
+
* name resolves to is not one a tool may call. A caller tells a refusal from
|
|
10
|
+
* a network failure by this type, never by its message. A name that does not
|
|
11
|
+
* resolve and a redirect loop are plain Errors: nothing was refused. */
|
|
12
|
+
export declare class OutboundRefusedError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
8
15
|
/** Validate a tool-supplied URL. Returns null when allowed, else a human-readable
|
|
9
16
|
* reason it is refused (http(s) only; no internal/loopback/metadata hosts). */
|
|
10
17
|
export declare function outboundUrlError(raw: string): string | null;
|
|
11
18
|
/** fetch() with SSRF protection: the URL and every redirect hop are validated by
|
|
12
19
|
* outboundUrlError, following redirects MANUALLY so a 3xx can't escape to an
|
|
13
|
-
* internal host. Throws
|
|
14
|
-
* cross-origin redirect is opaque (unreadable) and is refused
|
|
20
|
+
* internal host. Throws OutboundRefusedError on a blocked URL/hop/address. In
|
|
21
|
+
* the browser a cross-origin redirect is opaque (unreadable) and is refused
|
|
22
|
+
* rather than followed. A redirect off the original origin never carries the
|
|
23
|
+
* caller's credentials or body on (see nextHopInit). */
|
|
15
24
|
export declare function safeFetch(url: string, init?: RequestInit): Promise<Response>;
|
|
16
25
|
//# sourceMappingURL=urlGuard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urlGuard.d.ts","sourceRoot":"","sources":["../../tools/urlGuard.ts"],"names":[],"mappings":"AA6CA;;wCAEwC;AACxC,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAGnE;mDACmD;AACnD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAErE;
|
|
1
|
+
{"version":3,"file":"urlGuard.d.ts","sourceRoot":"","sources":["../../tools/urlGuard.ts"],"names":[],"mappings":"AA6CA;;wCAEwC;AACxC,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAGnE;mDACmD;AACnD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAErE;AAID;;;wEAGwE;AACxE,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAcD;+EAC+E;AAC/E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU3D;AAyBD;;;;;wDAKwD;AACxD,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0BtF"}
|
package/dist/tools/urlGuard.js
CHANGED
|
@@ -59,18 +59,30 @@ export function setOutboundResolver(fn) {
|
|
|
59
59
|
resolver = fn;
|
|
60
60
|
}
|
|
61
61
|
const IP_LITERAL = /^[\d.]+$|:/;
|
|
62
|
-
/**
|
|
63
|
-
|
|
62
|
+
/** The guard refused a request: the URL, a redirect hop, or an address a
|
|
63
|
+
* name resolves to is not one a tool may call. A caller tells a refusal from
|
|
64
|
+
* a network failure by this type, never by its message. A name that does not
|
|
65
|
+
* resolve and a redirect loop are plain Errors: nothing was refused. */
|
|
66
|
+
export class OutboundRefusedError extends Error {
|
|
67
|
+
constructor(message) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = 'OutboundRefusedError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Check every address a name resolves to. Throws OutboundRefusedError for an
|
|
73
|
+
* internal address, a plain Error for a name that does not resolve. */
|
|
74
|
+
async function assertResolvedHostAllowed(host) {
|
|
64
75
|
if (!resolver)
|
|
65
|
-
return
|
|
76
|
+
return;
|
|
66
77
|
const bare = host.replace(/^\[/, '').replace(/\]$/, '');
|
|
67
78
|
if (IP_LITERAL.test(bare))
|
|
68
|
-
return
|
|
79
|
+
return; // a literal was already judged lexically
|
|
69
80
|
const addrs = await resolver(bare);
|
|
70
81
|
if (addrs.length === 0)
|
|
71
|
-
|
|
82
|
+
throw new Error(`could not resolve "${host}"`);
|
|
72
83
|
const bad = addrs.find((a) => isBlockedHost(a));
|
|
73
|
-
|
|
84
|
+
if (bad)
|
|
85
|
+
throw new OutboundRefusedError(`refusing to call an internal/loopback host: "${host}" resolves to ${bad}`);
|
|
74
86
|
}
|
|
75
87
|
/** Validate a tool-supplied URL. Returns null when allowed, else a human-readable
|
|
76
88
|
* reason it is refused (http(s) only; no internal/loopback/metadata hosts). */
|
|
@@ -89,24 +101,67 @@ export function outboundUrlError(raw) {
|
|
|
89
101
|
return null;
|
|
90
102
|
}
|
|
91
103
|
const MAX_REDIRECTS = 5;
|
|
104
|
+
/** Headers that carry a credential: never sent on to another origin. */
|
|
105
|
+
const CREDENTIAL_HEADERS = ['authorization', 'proxy-authorization', 'cookie', 'x-api-key'];
|
|
106
|
+
/** Headers that describe a request body: dropped with the body. */
|
|
107
|
+
const BODY_HEADERS = ['content-type', 'content-length', 'content-encoding', 'content-language', 'content-location'];
|
|
108
|
+
/** The next hop's request after a redirect, by fetch's own rules plus one of
|
|
109
|
+
* ours: a 303 (and a 301 or 302 for a POST) becomes a GET without a body,
|
|
110
|
+
* and once the chain leaves the original origin the credential headers and
|
|
111
|
+
* the body are dropped for every later hop. */
|
|
112
|
+
function nextHopInit(init, status, leftOrigin) {
|
|
113
|
+
const method = (init.method ?? 'GET').toUpperCase();
|
|
114
|
+
const toGet = (status === 303 && method !== 'GET' && method !== 'HEAD') || ((status === 301 || status === 302) && method === 'POST');
|
|
115
|
+
const dropBody = toGet || leftOrigin;
|
|
116
|
+
const headers = new Headers(init.headers);
|
|
117
|
+
if (leftOrigin)
|
|
118
|
+
for (const h of CREDENTIAL_HEADERS)
|
|
119
|
+
headers.delete(h);
|
|
120
|
+
if (dropBody)
|
|
121
|
+
for (const h of BODY_HEADERS)
|
|
122
|
+
headers.delete(h);
|
|
123
|
+
const next = { ...init, headers, ...(toGet ? { method: 'GET' } : {}) };
|
|
124
|
+
if (dropBody)
|
|
125
|
+
delete next.body;
|
|
126
|
+
return next;
|
|
127
|
+
}
|
|
92
128
|
/** fetch() with SSRF protection: the URL and every redirect hop are validated by
|
|
93
129
|
* outboundUrlError, following redirects MANUALLY so a 3xx can't escape to an
|
|
94
|
-
* internal host. Throws
|
|
95
|
-
* cross-origin redirect is opaque (unreadable) and is refused
|
|
130
|
+
* internal host. Throws OutboundRefusedError on a blocked URL/hop/address. In
|
|
131
|
+
* the browser a cross-origin redirect is opaque (unreadable) and is refused
|
|
132
|
+
* rather than followed. A redirect off the original origin never carries the
|
|
133
|
+
* caller's credentials or body on (see nextHopInit). */
|
|
96
134
|
export async function safeFetch(url, init = {}) {
|
|
97
135
|
let current = url;
|
|
136
|
+
let hopInit = init;
|
|
137
|
+
let leftOrigin = false;
|
|
138
|
+
let origin = null;
|
|
98
139
|
for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
|
|
99
140
|
const bad = outboundUrlError(current);
|
|
100
141
|
if (bad)
|
|
101
|
-
throw new
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const res = await fetch(current, { ...init, redirect: 'manual' });
|
|
142
|
+
throw new OutboundRefusedError(bad);
|
|
143
|
+
origin ?? (origin = new URL(current).origin);
|
|
144
|
+
await assertResolvedHostAllowed(new URL(current).hostname);
|
|
145
|
+
const res = await fetch(current, { ...hopInit, redirect: 'manual' });
|
|
106
146
|
if (res.type === 'opaqueredirect')
|
|
107
|
-
throw new
|
|
147
|
+
throw new OutboundRefusedError('refusing to follow an opaque cross-origin redirect');
|
|
108
148
|
if (res.status >= 300 && res.status < 400 && res.headers.has('location')) {
|
|
109
|
-
|
|
149
|
+
const next = new URL(res.headers.get('location'), current).toString();
|
|
150
|
+
try {
|
|
151
|
+
await res.body?.cancel();
|
|
152
|
+
}
|
|
153
|
+
catch { /* nothing left to release */ }
|
|
154
|
+
// outboundUrlError judges an unparseable next hop at the top of the loop.
|
|
155
|
+
let nextOrigin = null;
|
|
156
|
+
try {
|
|
157
|
+
nextOrigin = new URL(next).origin;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
nextOrigin = null;
|
|
161
|
+
}
|
|
162
|
+
leftOrigin = leftOrigin || nextOrigin !== origin;
|
|
163
|
+
hopInit = nextHopInit(hopInit, res.status, leftOrigin);
|
|
164
|
+
current = next;
|
|
110
165
|
continue;
|
|
111
166
|
}
|
|
112
167
|
return res;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urlGuard.js","sourceRoot":"","sources":["../../tools/urlGuard.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"urlGuard.js","sourceRoot":"","sources":["../../tools/urlGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;0EAE0E;AAC1E,SAAS,aAAa,CAAC,IAAY;IACjC,8DAA8D;IAC9D,qEAAqE;IACrE,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACvF,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,IAAI,CAAC,KAAK,WAAW,IAAI,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/D,IAAI,CAAC,KAAK,0BAA0B,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACrG,kFAAkF;IAClF,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,iBAAiB,IAAI,CAAC,KAAK,iBAAiB;QAAE,OAAO,IAAI,CAAC;IACjG,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9E,4EAA4E;IAC5E,6DAA6D;IAC7D,MAAM,SAAS,GAAG,yDAAyD,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpF,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtC,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtC,OAAO,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,EAAE,GAAG,8CAA8C,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,yBAAyB,EAAE,EAAE,CAAC,CAAC,CAAC;IACzG,IAAI,EAAE;QAAE,OAAO,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACtD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAW;IACrC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,iCAAiC;IACpF,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,gDAAgD;IACzF,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC,CAAC,UAAU;IAC5D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,UAAU;IACnD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,wBAAwB;IAC3E,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,mCAAmC;IAC9D,OAAO,KAAK,CAAC;AACf,CAAC;AAMD,IAAI,QAAQ,GAA4B,IAAI,CAAC;AAE7C;mDACmD;AACnD,MAAM,UAAU,mBAAmB,CAAC,EAA2B;IAC7D,QAAQ,GAAG,EAAE,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC;;;wEAGwE;AACxE,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED;uEACuE;AACvE,KAAK,UAAU,yBAAyB,CAAC,IAAY;IACnD,IAAI,CAAC,QAAQ;QAAE,OAAO;IACtB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,yCAAyC;IAC5E,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,GAAG,CAAC,CAAC;IACvE,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,IAAI,GAAG;QAAE,MAAM,IAAI,oBAAoB,CAAC,gDAAgD,IAAI,iBAAiB,GAAG,EAAE,CAAC,CAAC;AACtH,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAM,CAAC;IACX,IAAI,CAAC;QACH,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,iBAAiB,GAAG,GAAG,CAAC;IACjC,CAAC;IACD,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,yBAAyB,GAAG,GAAG,CAAC;IAC9F,IAAI,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;QAAE,OAAO,gDAAgD,CAAC,CAAC,QAAQ,GAAG,CAAC;IACpG,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB,wEAAwE;AACxE,MAAM,kBAAkB,GAAG,CAAC,eAAe,EAAE,qBAAqB,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAC3F,mEAAmE;AACnE,MAAM,YAAY,GAAG,CAAC,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;AAEpH;;;+CAG+C;AAC/C,SAAS,WAAW,CAAC,IAAiB,EAAE,MAAc,EAAE,UAAmB;IACzE,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACpD,MAAM,KAAK,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC,IAAI,MAAM,KAAK,MAAM,CAAC,CAAC;IACrI,MAAM,QAAQ,GAAG,KAAK,IAAI,UAAU,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,UAAU;QAAE,KAAK,MAAM,CAAC,IAAI,kBAAkB;YAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtE,IAAI,QAAQ;QAAE,KAAK,MAAM,CAAC,IAAI,YAAY;YAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAgB,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACpF,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IAC/B,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;wDAKwD;AACxD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,OAAoB,EAAE;IACjE,IAAI,OAAO,GAAG,GAAG,CAAC;IAClB,IAAI,OAAO,GAAgB,IAAI,CAAC;IAChC,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,GAAG;YAAE,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC7C,MAAM,KAAN,MAAM,GAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,EAAC;QACnC,MAAM,yBAAyB,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QACrE,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB;YAAE,MAAM,IAAI,oBAAoB,CAAC,oDAAoD,CAAC,CAAC;QACxH,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACzE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;YAChF,IAAI,CAAC;gBAAC,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,6BAA6B,CAAC,CAAC;YACzE,0EAA0E;YAC1E,IAAI,UAAU,GAAkB,IAAI,CAAC;YACrC,IAAI,CAAC;gBAAC,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,UAAU,GAAG,IAAI,CAAC;YAAC,CAAC;YACvE,UAAU,GAAG,UAAU,IAAI,UAAU,KAAK,MAAM,CAAC;YACjD,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YACvD,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,yBAAyB,aAAa,GAAG,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const RESULTS_DIR = "results";
|
|
2
|
+
export type ResultSource = 'job' | 'deploy' | 'canvas';
|
|
3
|
+
export interface ResultMeta {
|
|
4
|
+
workflow: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
/** ISO 8601 with the local offset. */
|
|
7
|
+
ran: string;
|
|
8
|
+
source: ResultSource;
|
|
9
|
+
/** The edit values the run used (what the form held). */
|
|
10
|
+
inputs: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
/** One safe folder name: no separators or reserved characters, no leading or
|
|
13
|
+
* trailing dots or spaces (so never "." or ".."), at most 80 characters. */
|
|
14
|
+
export declare function cleanName(name: string, slug: string): string;
|
|
15
|
+
export declare function stamp(d: Date): string;
|
|
16
|
+
export declare function isoWithOffset(d: Date): string;
|
|
17
|
+
export declare function resultPath(workflowName: string, slug: string, ranAt: Date): string;
|
|
18
|
+
export declare function formatResult(meta: ResultMeta, output: string): string;
|
|
19
|
+
export declare function parseResult(text: string): {
|
|
20
|
+
meta: ResultMeta | null;
|
|
21
|
+
output: string;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=resultFile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resultFile.d.ts","sourceRoot":"","sources":["../../../workflows/results/resultFile.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,WAAW,YAAY,CAAC;AACrC,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACvD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,YAAY,CAAC;IACrB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAMD;4EAC4E;AAC5E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAErC;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAK7C;AAED,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,MAAM,CAElF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAErE;AAsBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAWrF"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* The result file: one finished workflow run's output, as markdown with a YAML
|
|
2
|
+
* front matter header that says which workflow, when, from where, and with
|
|
3
|
+
* which inputs (so "Reuse inputs" can put them back). The body is the run's
|
|
4
|
+
* final output, unchanged. parseResult never throws: a hand-made or hand-edited
|
|
5
|
+
* file still opens, as all output and no meta. */
|
|
6
|
+
import { parse, stringify } from 'yaml';
|
|
7
|
+
export const RESULTS_DIR = 'results';
|
|
8
|
+
// Characters no folder name may hold on any backend, plus control characters.
|
|
9
|
+
const BAD = /[/\\:*?"<>|\u0000-\u001f]/g;
|
|
10
|
+
const pad = (n, w = 2) => String(n).padStart(w, '0');
|
|
11
|
+
/** One safe folder name: no separators or reserved characters, no leading or
|
|
12
|
+
* trailing dots or spaces (so never "." or ".."), at most 80 characters. */
|
|
13
|
+
export function cleanName(name, slug) {
|
|
14
|
+
const clean = (s) => s.replace(BAD, '').replace(/\s+/g, ' ').replace(/^[\s.]+|[\s.]+$/g, '').slice(0, 80).replace(/[\s.]+$/g, '');
|
|
15
|
+
return clean(name) || clean(slug) || 'Workflow';
|
|
16
|
+
}
|
|
17
|
+
export function stamp(d) {
|
|
18
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}${pad(d.getMinutes())}`;
|
|
19
|
+
}
|
|
20
|
+
export function isoWithOffset(d) {
|
|
21
|
+
const off = -d.getTimezoneOffset();
|
|
22
|
+
const sign = off >= 0 ? '+' : '-';
|
|
23
|
+
const a = Math.abs(off);
|
|
24
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${sign}${pad(Math.floor(a / 60))}:${pad(a % 60)}`;
|
|
25
|
+
}
|
|
26
|
+
export function resultPath(workflowName, slug, ranAt) {
|
|
27
|
+
return `${RESULTS_DIR}/${cleanName(workflowName, slug)}/${stamp(ranAt)}.md`;
|
|
28
|
+
}
|
|
29
|
+
export function formatResult(meta, output) {
|
|
30
|
+
return `---\n${stringify(meta).trimEnd()}\n---\n\n${output}\n`;
|
|
31
|
+
}
|
|
32
|
+
const SOURCES = ['job', 'deploy', 'canvas'];
|
|
33
|
+
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
34
|
+
function toMeta(raw) {
|
|
35
|
+
if (!isRecord(raw))
|
|
36
|
+
return null;
|
|
37
|
+
const { workflow, slug, source, inputs } = raw;
|
|
38
|
+
const ran = raw.ran instanceof Date ? raw.ran.toISOString() : raw.ran;
|
|
39
|
+
if (typeof workflow !== 'string' || typeof slug !== 'string' || typeof ran !== 'string')
|
|
40
|
+
return null;
|
|
41
|
+
return {
|
|
42
|
+
workflow, slug, ran,
|
|
43
|
+
source: SOURCES.includes(source) ? source : 'job',
|
|
44
|
+
inputs: isRecord(inputs) ? inputs : {},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Front matter: a first line of exactly ---, then YAML up to the next line of
|
|
48
|
+
// exactly ---. YAML block scalars indent their lines, so a --- inside an input
|
|
49
|
+
// value can never close it.
|
|
50
|
+
const FRONT = /^---\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/;
|
|
51
|
+
export function parseResult(text) {
|
|
52
|
+
const m = FRONT.exec(text);
|
|
53
|
+
if (!m)
|
|
54
|
+
return { meta: null, output: text.replace(/\s+$/, '') };
|
|
55
|
+
let raw;
|
|
56
|
+
try {
|
|
57
|
+
raw = parse(m[1]);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return { meta: null, output: text.replace(/\s+$/, '') };
|
|
61
|
+
}
|
|
62
|
+
const meta = toMeta(raw);
|
|
63
|
+
// Not result meta (a note that opens with a --- rule, a hand-edited header):
|
|
64
|
+
// the whole file is the output, so nothing the person wrote is hidden.
|
|
65
|
+
if (!meta)
|
|
66
|
+
return { meta: null, output: text.replace(/\s+$/, '') };
|
|
67
|
+
const body = text.slice(m[0].length).replace(/^\r?\n/, '').replace(/\s+$/, '');
|
|
68
|
+
return { meta, output: body };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=resultFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resultFile.js","sourceRoot":"","sources":["../../../workflows/results/resultFile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { FileService } from '@corenel/protocol';
|
|
2
|
+
import { type ResultMeta } from './resultFile';
|
|
3
|
+
export type SaveOutcome = {
|
|
4
|
+
path: string;
|
|
5
|
+
} | {
|
|
6
|
+
error: string;
|
|
7
|
+
};
|
|
8
|
+
export interface ResultEntry {
|
|
9
|
+
path: string;
|
|
10
|
+
meta: ResultMeta | null;
|
|
11
|
+
output: string;
|
|
12
|
+
ranAt: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function isResultPath(path: string): boolean;
|
|
15
|
+
export declare function saveResult(files: FileService | null, meta: ResultMeta, output: string, ranAt?: Date): Promise<SaveOutcome>;
|
|
16
|
+
export type PlaceOutcome = {
|
|
17
|
+
path: string;
|
|
18
|
+
same: boolean;
|
|
19
|
+
} | {
|
|
20
|
+
error: string;
|
|
21
|
+
};
|
|
22
|
+
/** Put an already-formatted result `text` at `path` (results/**.md) or the
|
|
23
|
+
* next free ` (n)` name, never overwriting anything: a name holding exactly
|
|
24
|
+
* `text` already is the same result (an earlier copy) and is reported as
|
|
25
|
+
* `same` with nothing written. Claims names in the SAME in-tab reservation as
|
|
26
|
+
* saveResult, so a save and a placement finishing together never pick one
|
|
27
|
+
* name, and writes with `create`, which refuses an existing file, so a file
|
|
28
|
+
* another tab wrote between the check and the write is not replaced either.
|
|
29
|
+
* Never throws. */
|
|
30
|
+
export declare function placeResultText(files: FileService, path: string, text: string): Promise<PlaceOutcome>;
|
|
31
|
+
export declare function listResults(files: FileService | null, opts?: {
|
|
32
|
+
slug?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
}): Promise<ResultEntry[]>;
|
|
35
|
+
export declare function readResult(files: FileService | null, path: string): Promise<ResultEntry | null>;
|
|
36
|
+
export declare function deleteResult(files: FileService, path: string): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=resultStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resultStore.d.ts","sourceRoot":"","sources":["../../../workflows/results/resultStore.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAY,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAsD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAEnG,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/D,MAAM,WAAW,WAAW;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AASrG,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGlD;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,IAAyB,GAAG,OAAO,CAAC,WAAW,CAAC,CAgCpJ;AAKD,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E;;;;;;;mBAOmB;AACnB,wBAAsB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmC3G;AA2BD,wBAAsB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,EAAE,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAiBjI;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAGrG;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlF"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/* Results as files: save, list, read, delete under results/ in the workspace.
|
|
2
|
+
* Every path in or out is checked to be results/**.md, so a crafted link can
|
|
3
|
+
* never read or delete anything else. Saving never throws: a run's outcome must
|
|
4
|
+
* not depend on whether its result could be written. */
|
|
5
|
+
import { isProtectedPath, protectedPathReason } from '@corenel/protocol';
|
|
6
|
+
import { RESULTS_DIR, formatResult, parseResult, resultPath } from './resultFile.js';
|
|
7
|
+
/** At most this many files are read for one listing. */
|
|
8
|
+
const MAX_SCAN = 500;
|
|
9
|
+
function normalize(path) {
|
|
10
|
+
return path.replace(/\\/g, '/').replace(/^\/+/, '');
|
|
11
|
+
}
|
|
12
|
+
export function isResultPath(path) {
|
|
13
|
+
const n = normalize(path);
|
|
14
|
+
return n.startsWith(`${RESULTS_DIR}/`) && n.endsWith('.md') && !n.split('/').some((seg) => seg === '..' || seg === '.' || seg === '');
|
|
15
|
+
}
|
|
16
|
+
export async function saveResult(files, meta, output, ranAt = new Date(meta.ran)) {
|
|
17
|
+
if (!files)
|
|
18
|
+
return { error: 'no workspace is open' };
|
|
19
|
+
if (!files.writable)
|
|
20
|
+
return { error: 'the workspace is read-only' };
|
|
21
|
+
const when = Number.isNaN(ranAt.getTime()) ? new Date() : ranAt;
|
|
22
|
+
const base = resultPath(meta.workflow, meta.slug, when);
|
|
23
|
+
// cleanName already makes one safe segment; this keeps the write honest if
|
|
24
|
+
// that ever changes: nothing outside results/**.md is ever written.
|
|
25
|
+
if (!isResultPath(base))
|
|
26
|
+
return { error: `not a results path: ${base}` };
|
|
27
|
+
if (isProtectedPath(base))
|
|
28
|
+
return { error: protectedPathReason(base) };
|
|
29
|
+
let path = null;
|
|
30
|
+
// The name this save holds in `reserved`; released in finally, even on a throw.
|
|
31
|
+
let claimed = null;
|
|
32
|
+
try {
|
|
33
|
+
// stat-then-write is not atomic, so each candidate is CLAIMED in this tab
|
|
34
|
+
// before the await that checks the disk: two saves finishing together (a
|
|
35
|
+
// deploy and a job) can never both pick the same free name.
|
|
36
|
+
for (let n = 1; path === null; n++) {
|
|
37
|
+
if (n > 99)
|
|
38
|
+
return { error: 'too many results in one minute' };
|
|
39
|
+
const candidate = n === 1 ? base : base.replace(/\.md$/, ` (${n}).md`);
|
|
40
|
+
if (reserved.has(candidate))
|
|
41
|
+
continue;
|
|
42
|
+
reserved.add(candidate);
|
|
43
|
+
claimed = candidate;
|
|
44
|
+
if (await files.stat(candidate)) {
|
|
45
|
+
reserved.delete(candidate);
|
|
46
|
+
claimed = null;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
path = candidate;
|
|
50
|
+
}
|
|
51
|
+
await files.write(path, formatResult(meta, output));
|
|
52
|
+
return { path };
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
return { error: String(e?.message || e) };
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
if (claimed !== null)
|
|
59
|
+
reserved.delete(claimed);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Paths a save in this tab has claimed but not finished writing. */
|
|
63
|
+
const reserved = new Set();
|
|
64
|
+
/** Put an already-formatted result `text` at `path` (results/**.md) or the
|
|
65
|
+
* next free ` (n)` name, never overwriting anything: a name holding exactly
|
|
66
|
+
* `text` already is the same result (an earlier copy) and is reported as
|
|
67
|
+
* `same` with nothing written. Claims names in the SAME in-tab reservation as
|
|
68
|
+
* saveResult, so a save and a placement finishing together never pick one
|
|
69
|
+
* name, and writes with `create`, which refuses an existing file, so a file
|
|
70
|
+
* another tab wrote between the check and the write is not replaced either.
|
|
71
|
+
* Never throws. */
|
|
72
|
+
export async function placeResultText(files, path, text) {
|
|
73
|
+
if (!files.writable)
|
|
74
|
+
return { error: 'the workspace is read-only' };
|
|
75
|
+
const base = normalize(path);
|
|
76
|
+
if (!isResultPath(base))
|
|
77
|
+
return { error: `not a results path: ${base}` };
|
|
78
|
+
const sameAt = async (candidate) => {
|
|
79
|
+
try {
|
|
80
|
+
return (await files.read(candidate)) === text;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
for (let n = 1; n <= 99; n++) {
|
|
87
|
+
const candidate = n === 1 ? base : base.replace(/\.md$/, ` (${n}).md`);
|
|
88
|
+
if (isProtectedPath(candidate))
|
|
89
|
+
return { error: protectedPathReason(candidate) };
|
|
90
|
+
if (reserved.has(candidate))
|
|
91
|
+
continue;
|
|
92
|
+
reserved.add(candidate);
|
|
93
|
+
try {
|
|
94
|
+
if (await files.stat(candidate)) {
|
|
95
|
+
if (await sameAt(candidate))
|
|
96
|
+
return { path: candidate, same: true };
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
await files.create(candidate, text);
|
|
101
|
+
return { path: candidate, same: false };
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
// Taken since the check (another tab): treat it as any existing name.
|
|
105
|
+
// Anything else is a real failure and nothing was written.
|
|
106
|
+
let now;
|
|
107
|
+
try {
|
|
108
|
+
now = await files.stat(candidate);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
now = null;
|
|
112
|
+
}
|
|
113
|
+
if (!now)
|
|
114
|
+
return { error: String(e?.message || e) };
|
|
115
|
+
if (await sameAt(candidate))
|
|
116
|
+
return { path: candidate, same: true };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
return { error: String(e?.message || e) };
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
reserved.delete(candidate);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { error: 'too many results with that name' };
|
|
127
|
+
}
|
|
128
|
+
function flatten(nodes, out) {
|
|
129
|
+
for (const n of nodes) {
|
|
130
|
+
if (n.kind === 'file')
|
|
131
|
+
out.push(n);
|
|
132
|
+
else if (n.children)
|
|
133
|
+
flatten(n.children, out);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// "2026-10-10 0930 (2).md" -> "202610100930002", for newest-first sorting.
|
|
137
|
+
const STAMP = /(\d{4})-(\d{2})-(\d{2}) (\d{2})(\d{2})(?: \((\d+)\))?\.md$/;
|
|
138
|
+
function sortKey(n) {
|
|
139
|
+
const m = STAMP.exec(n.name);
|
|
140
|
+
if (m)
|
|
141
|
+
return `${m[1]}${m[2]}${m[3]}${m[4]}${m[5]}${(m[6] ?? '1').padStart(3, '0')}`;
|
|
142
|
+
return n.mtime ? `${new Date(n.mtime).toISOString().replace(/\D/g, '').slice(0, 12)}000` : '';
|
|
143
|
+
}
|
|
144
|
+
async function readEntry(files, path, mtime) {
|
|
145
|
+
try {
|
|
146
|
+
const { meta, output } = parseResult(await files.read(path));
|
|
147
|
+
const parsed = meta ? Date.parse(meta.ran) : NaN;
|
|
148
|
+
return { path, meta, output, ranAt: Number.isNaN(parsed) ? (mtime ?? 0) : parsed };
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export async function listResults(files, opts = {}) {
|
|
155
|
+
if (!files)
|
|
156
|
+
return [];
|
|
157
|
+
let nodes;
|
|
158
|
+
try {
|
|
159
|
+
nodes = await files.list(RESULTS_DIR);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
const all = [];
|
|
165
|
+
flatten(nodes, all);
|
|
166
|
+
const md = all.filter((n) => isResultPath(n.path)).sort((a, b) => sortKey(b).localeCompare(sortKey(a)));
|
|
167
|
+
const limit = opts.limit ?? 50;
|
|
168
|
+
const out = [];
|
|
169
|
+
for (const n of md.slice(0, MAX_SCAN)) {
|
|
170
|
+
if (out.length >= limit)
|
|
171
|
+
break;
|
|
172
|
+
const e = await readEntry(files, n.path, n.mtime);
|
|
173
|
+
if (!e)
|
|
174
|
+
continue;
|
|
175
|
+
if (opts.slug && e.meta?.slug !== opts.slug)
|
|
176
|
+
continue;
|
|
177
|
+
out.push(e);
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
export async function readResult(files, path) {
|
|
182
|
+
if (!files || !isResultPath(path))
|
|
183
|
+
return null;
|
|
184
|
+
return readEntry(files, normalize(path));
|
|
185
|
+
}
|
|
186
|
+
export async function deleteResult(files, path) {
|
|
187
|
+
if (!isResultPath(path))
|
|
188
|
+
throw new Error(`not a result: ${path}`);
|
|
189
|
+
await files.remove(normalize(path));
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=resultStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resultStore.js","sourceRoot":"","sources":["../../../workflows/results/resultStore.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corenel/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"license": "Elastic-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Corenel — host-agnostic agent harness (kernel). The app resolves it via the `corenel` path alias (bypasses these exports entirely); vitest aliases it to source too. Everything else — node consumers like @corenel/cli, @corenel/term, @corenel/sidecar — resolves it through this workspace package via the exports map below, which points at dist/ unconditionally (not just at publish) so in-repo and published resolution are identical: a bundle that treats @corenel/harness as external (see packages/term/build.mjs) must see the SAME module instance node sees when it later resolves this package at runtime, and plain node cannot execute the raw .ts sources at all. Run `pnpm build` here (or let a dependent's prebuild do it) before relying on this package outside the app/vitest. NOTE: @xyflow/react and dagre are NOT dependencies here at all — this package has no .tsx files and imports neither. `react` IS a real (small) runtime dependency: `todos.ts` uses `useSyncExternalStore`. It is declared below as an OPTIONAL peer, not a hard dependency, so node-only consumers (cli/term/sidecar) aren't forced to install react; node consumers hoist it today only because something else in the tree happens to pull it in. Do not add @xyflow/react or dagre as dependencies/peers without testing a packed install end to end — an earlier attempt at that (before this package had any .tsx files) failed npm's peer resolution with ERESOLVE.",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"openai": "^4.77.0",
|
|
35
35
|
"yaml": "^2.8.3",
|
|
36
|
-
"@corenel/protocol": "0.
|
|
36
|
+
"@corenel/protocol": "0.16.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"react": "^18.0.0 || ^19.0.0"
|