@volter/supercode-teams 0.2.140 → 0.2.141
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/package.json +1 -1
- package/server/harness-routes.mjs +2 -0
- package/server/storage-report.mjs +31 -0
- package/worker/index.mjs +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.141",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// idempotency check, and last among a team's routes. Each hook returns true when it
|
|
8
8
|
// answered the request.
|
|
9
9
|
import { bearer, hashToken, canRead, canManage, isAdmin } from '@volter/teams/server/auth';
|
|
10
|
+
import { storageReport } from './storage-report.mjs';
|
|
10
11
|
import { validateListQuery, validateSearchQuery, validateReadQuery, validateCaptureBegin, validateCapturePart, canonicalJson, carryDigest } from '../contracts/index.mjs';
|
|
11
12
|
import { normalizeSearch, textFromContent, cursorHash, cursorToken } from './catalog.mjs';
|
|
12
13
|
import { captureDigest, captureMessages, handleOtlpRoute, sha256, validateArtifactName } from './ingest.mjs';
|
|
@@ -77,6 +78,7 @@ async function beforeIdempotency({ api, req, res, auth, url, path, sub, requestI
|
|
|
77
78
|
|
|
78
79
|
async function team({ api, req, res, auth, url, path, sub, requestId }) {
|
|
79
80
|
let m;
|
|
81
|
+
if(req.method==='GET'&&sub==='/storage/uploads'){await storageReport({api,res,auth,url,requestId});return true;}
|
|
80
82
|
if(req.method==='GET'&&sub==='/sessions'){await listSessions(api,res,auth,url,requestId,false);return true;}
|
|
81
83
|
if(req.method==='GET'&&sub==='/sessions/search'){await listSessions(api,res,auth,url,requestId,true);return true;}
|
|
82
84
|
if(req.method==='GET'&&sub==='/sessions/events'){await handleCatalogEvents({api,req,res,auth,url,requestId,qobj,canReadMerged,readerProjection,fail});return true;}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Administrative, read-only retention inventory. No artifact bytes, native
|
|
2
|
+
// paths, prompts or credentials leave this door; cursors never write storage.
|
|
3
|
+
import { isAdmin } from '@volter/teams/server/auth';
|
|
4
|
+
import { fail, result, send } from '@volter/teams/server/http';
|
|
5
|
+
|
|
6
|
+
export async function storageReport({ api, res, auth, url, requestId }) {
|
|
7
|
+
if (!isAdmin(auth)) fail(403, 'forbidden', 'storage inventory requires a team administrator');
|
|
8
|
+
for (const key of url.searchParams.keys()) if (key !== 'after') fail(400, 'invalid_request', `unknown query field ${key}`);
|
|
9
|
+
const after = url.searchParams.get('after') ?? '';
|
|
10
|
+
if (after && !/^upload_[a-zA-Z0-9_-]{1,100}$/.test(after)) fail(400, 'invalid_request', 'invalid upload cursor');
|
|
11
|
+
const rows = await api.store.query(`WITH page AS MATERIALIZED (
|
|
12
|
+
SELECT * FROM uploads WHERE team_id=? AND id>? ORDER BY id LIMIT 100
|
|
13
|
+
), provenance AS MATERIALIZED (
|
|
14
|
+
SELECT c.team_id,json_extract(a.value,'$.upload_id') upload_id,
|
|
15
|
+
c.native_id,c.harness,MIN(c.created_at) captured_at
|
|
16
|
+
FROM captures c,json_each(c.commit_json,'$.artifacts') a
|
|
17
|
+
WHERE c.team_id=? AND json_extract(a.value,'$.upload_id') IN (SELECT id FROM page)
|
|
18
|
+
GROUP BY c.team_id,json_extract(a.value,'$.upload_id')
|
|
19
|
+
) SELECT u.id,u.source_id,s.machine_id,m.name machine,u.created_at,u.committed,
|
|
20
|
+
p.native_id,p.harness,p.captured_at,
|
|
21
|
+
json_extract(u.manifest_json,'$.role') role,json_extract(u.manifest_json,'$.media_type') media_type,
|
|
22
|
+
json_extract(u.manifest_json,'$.sha256') sha256,
|
|
23
|
+
(SELECT COALESCE(SUM(length(bytes)),0) FROM artifact_chunks c WHERE c.team_id=u.team_id AND c.upload_id=u.id) bytes,
|
|
24
|
+
EXISTS (SELECT 1 FROM session_artifacts a WHERE a.team_id=u.team_id AND a.upload_id=u.id) version_referenced,
|
|
25
|
+
EXISTS (SELECT 1 FROM continuations c WHERE c.team_id=u.team_id AND instr(c.value_json,u.id)>0) continuation_referenced,
|
|
26
|
+
EXISTS (SELECT 1 FROM captures c WHERE c.team_id=u.team_id AND c.state='staging' AND instr(c.begin_json,u.id)>0) staging_referenced
|
|
27
|
+
FROM page u JOIN sources s ON s.team_id=u.team_id AND s.id=u.source_id
|
|
28
|
+
LEFT JOIN machines m ON m.team_id=s.team_id AND m.id=s.machine_id
|
|
29
|
+
LEFT JOIN provenance p ON p.team_id=u.team_id AND p.upload_id=u.id ORDER BY u.id`, [auth.team_id, after, auth.team_id]);
|
|
30
|
+
send(res, 200, result(api, auth, { items: rows, next: rows.length === 100 ? rows.at(-1).id : null }), requestId);
|
|
31
|
+
}
|
package/worker/index.mjs
CHANGED
|
@@ -114,7 +114,7 @@ export class TeamsServer extends DurableObject {
|
|
|
114
114
|
this.ready = ctx.blockConcurrencyWhile(async () => {
|
|
115
115
|
// Read before migrations/maintenance: allocation failures must not hide
|
|
116
116
|
// the table responsible, including hosts with a lower storage limit.
|
|
117
|
-
|
|
117
|
+
console.log(`storage: database_bytes=${ctx.storage.sql.databaseSize}`);
|
|
118
118
|
const db = durableSql(ctx.storage);
|
|
119
119
|
migrate({ exec: db.exec, tx: db.tx, version: () => ctx.storage.kv.get('schema_version') ?? 0, setVersion: (n) => ctx.storage.kv.put('schema_version', n) });
|
|
120
120
|
this.api = createTeamsApi({ publicOrigin: normalizePublicOrigin(env.PUBLIC_ORIGIN), identityIssuer: env.VOLTER_ISSUER || null, identityOrganizations: identityOrganizationsFromEnv(env), delegatedIssuer: env.DELEGATED_ISSUER || null });
|
|
@@ -131,7 +131,7 @@ export class TeamsServer extends DurableObject {
|
|
|
131
131
|
this.handler = createHttpHandler(this.api);
|
|
132
132
|
// Retention must also run after a restart: a full object may have failed to re-arm its alarm.
|
|
133
133
|
if (this.api.serverId) await maintain(this.api).catch(error => console.error(`maintain startup: ${error.message}`));
|
|
134
|
-
if (!(await ctx.storage.getAlarm())) await ctx.storage.setAlarm(Date.now() + MAINTAIN_MS);
|
|
134
|
+
if (!(await ctx.storage.getAlarm())) await ctx.storage.setAlarm(Date.now() + MAINTAIN_MS).catch(error => console.error(`maintain alarm scheduling failed: ${error.message}`));
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
137
|
|