@sleep2agi/commhub-server 0.9.0-preview.27 → 0.9.0-preview.29
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/bin/commhub.ts +4 -0
- package/package.json +4 -4
- package/src/ack-create-request-transport.test.ts +159 -0
- package/src/ack-create-request.test.ts +5 -5
- package/src/admin-networks-http.test.ts +84 -0
- package/src/api-host-supervisors-fallback.test.ts +1 -6
- package/src/auth.ts +88 -14
- package/src/dashboard-slash-routing-http.test.ts +103 -0
- package/src/db-adapter-guard.test.ts +85 -0
- package/src/db-adapter.ts +64 -23
- package/src/db.ts +279 -32
- package/src/ext-token-shared.test.ts +60 -0
- package/src/ext-validation-regression.test.ts +10 -17
- package/src/external-schedule-edits-http.test.ts +316 -0
- package/src/external-schedule-edits.ts +314 -0
- package/src/file-download-authz-dev-open.test.ts +1 -1
- package/src/file-download-authz.test.ts +21 -1
- package/src/file-download-date-bucket.test.ts +1 -1
- package/src/file-network-scope-dev-open.test.ts +1 -1
- package/src/file-network-scope.test.ts +1 -1
- package/src/health-redaction.test.ts +12 -0
- package/src/index.ts +6 -2
- package/src/network-name-http.test.ts +71 -0
- package/src/network-scope.ts +20 -0
- package/src/node-attrs.test.ts +1 -1
- package/src/peer-reply-atomic.test.ts +455 -0
- package/src/peer-reply-capability.test.ts +70 -0
- package/src/require-explicit-test-db.ts +9 -0
- package/src/rest-explicit-columns-http.test.ts +149 -0
- package/src/rest-identity.test.ts +120 -0
- package/src/rest-identity.ts +93 -0
- package/src/rest-projections.ts +67 -0
- package/src/scheduled-run-terminal.test.ts +268 -0
- package/src/scheduled-tasks-http.test.ts +460 -0
- package/src/scheduled-tasks.ts +538 -0
- package/src/send-reply-agent-warning.test.ts +38 -38
- package/src/server.ts +315 -60
- package/src/shared/external-schedule-contract.ts +60 -0
- package/src/skillhub-http.test.ts +88 -0
- package/src/task-auth-origin-http.test.ts +87 -0
- package/src/task-auth-origin.test.ts +24 -0
- package/src/task-auth-origin.ts +7 -0
- package/src/task-consumption.test.ts +329 -0
- package/src/task-diagnostic-http.test.ts +145 -0
- package/src/task-diagnostic.test.ts +82 -0
- package/src/task-diagnostic.ts +89 -0
- package/src/task-idempotency-mcp.test.ts +137 -0
- package/src/task-idempotency.test.ts +60 -0
- package/src/task-idempotency.ts +75 -0
- package/src/task-lifecycle-watcher.test.ts +160 -0
- package/src/task-lifecycle-watcher.ts +49 -0
- package/src/task-network-resolution-http.test.ts +137 -0
- package/src/tasks-pagination-http.test.ts +137 -0
- package/src/tools.ts +635 -107
- package/src/uploads-http.test.ts +1 -3
- package/src/uploads.ts +14 -3
package/src/uploads-http.test.ts
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
// this file already exercises the same code path). The hub binds to
|
|
8
8
|
// an ephemeral port so multiple test runs don't collide.
|
|
9
9
|
import { describe, expect, test, beforeAll, afterAll } from "bun:test";
|
|
10
|
+
import "./require-explicit-test-db.js";
|
|
10
11
|
import { mkdtempSync, rmSync, existsSync } from "fs";
|
|
11
12
|
import { join } from "path";
|
|
12
13
|
import { tmpdir } from "os";
|
|
13
14
|
import { register, login } from "./auth.js";
|
|
14
15
|
|
|
15
|
-
const SERVER_DB = mkdtempSync(join(tmpdir(), "anet-upload-http-db-")) + "/commhub.db";
|
|
16
16
|
const UPLOADS_DIR = mkdtempSync(join(tmpdir(), "anet-upload-http-fs-"));
|
|
17
17
|
// #438 corrective: private bootServer({ port: 0 }) instance — the OS
|
|
18
18
|
// assigns the port and BASE is derived from the ACTUAL bound port, so
|
|
@@ -28,7 +28,6 @@ beforeAll(async () => {
|
|
|
28
28
|
// Bootstrap a real admin + token on the temp DB so the server can
|
|
29
29
|
// resolve it via requireAuth (we don't want to short-circuit auth —
|
|
30
30
|
// we want to validate the production code path).
|
|
31
|
-
process.env.COMMHUB_DB = SERVER_DB;
|
|
32
31
|
process.env.COMMHUB_UPLOADS_DIR = UPLOADS_DIR;
|
|
33
32
|
process.env.HOST = "127.0.0.1";
|
|
34
33
|
|
|
@@ -64,7 +63,6 @@ beforeAll(async () => {
|
|
|
64
63
|
afterAll(() => {
|
|
65
64
|
try { server?.stop?.(true); } catch {}
|
|
66
65
|
try { rmSync(UPLOADS_DIR, { recursive: true, force: true }); } catch {}
|
|
67
|
-
try { rmSync(SERVER_DB, { recursive: true, force: true }); } catch {}
|
|
68
66
|
});
|
|
69
67
|
|
|
70
68
|
function authHeaders(): Record<string, string> {
|
package/src/uploads.ts
CHANGED
|
@@ -53,6 +53,17 @@ export const UPLOAD_RATE_MAX_ENTRIES = 50_000;
|
|
|
53
53
|
// traversal.
|
|
54
54
|
export const FILE_ID_REGEX = /^[A-Za-z0-9_-]{8,64}$/;
|
|
55
55
|
|
|
56
|
+
// Stored extension tokens have one invariant at every path boundary:
|
|
57
|
+
// either no extension, or a leading dot followed by 1-16 ASCII
|
|
58
|
+
// alphanumeric characters. Keep this separate from sanitizeExt(), which
|
|
59
|
+
// parses a client filename and therefore deliberately has different
|
|
60
|
+
// capture/anchoring semantics.
|
|
61
|
+
const EXT_TOKEN_REGEX = /^\.[A-Za-z0-9]{1,16}$/;
|
|
62
|
+
|
|
63
|
+
function isValidExtToken(ext: unknown): ext is string {
|
|
64
|
+
return typeof ext === "string" && (ext === "" || EXT_TOKEN_REGEX.test(ext));
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
export type GeneratedFile = {
|
|
57
68
|
fileId: string;
|
|
58
69
|
ext: string;
|
|
@@ -138,7 +149,7 @@ export function isValidCalendarBucket(bucket: unknown): bucket is string {
|
|
|
138
149
|
*/
|
|
139
150
|
export function buildStoragePath(fileId: string, ext: string, opts: { uploadsRoot?: string; now?: Date } = {}): GeneratedFile {
|
|
140
151
|
if (!FILE_ID_REGEX.test(fileId)) throw new Error(`file_id "${fileId}" is invalid`);
|
|
141
|
-
if (
|
|
152
|
+
if (!isValidExtToken(ext)) throw new Error(`ext "${ext}" is invalid`);
|
|
142
153
|
const dateBucket = getDateBucket(opts.now);
|
|
143
154
|
const root = opts.uploadsRoot ?? getUploadsRoot();
|
|
144
155
|
const relativePath = join(dateBucket, fileId + ext);
|
|
@@ -162,7 +173,7 @@ export function buildStoragePath(fileId: string, ext: string, opts: { uploadsRoo
|
|
|
162
173
|
*/
|
|
163
174
|
export function pathForExistingBlob(dateBucket: string, fileId: string, ext: string, opts: { uploadsRoot?: string } = {}): GeneratedFile {
|
|
164
175
|
if (!FILE_ID_REGEX.test(fileId)) throw new Error(`file_id "${fileId}" is invalid`);
|
|
165
|
-
if (
|
|
176
|
+
if (!isValidExtToken(ext)) throw new Error(`ext "${ext}" is invalid`);
|
|
166
177
|
if (typeof dateBucket !== "string" || !DATE_BUCKET_REGEX.test(dateBucket)) {
|
|
167
178
|
throw new Error(`date_bucket "${dateBucket}" is invalid`);
|
|
168
179
|
}
|
|
@@ -216,7 +227,7 @@ export function validateIndexEntry(entry: unknown): entry is UploadIndexEntry {
|
|
|
216
227
|
return (
|
|
217
228
|
typeof e.file_id === "string" && FILE_ID_REGEX.test(e.file_id) &&
|
|
218
229
|
typeof e.date_bucket === "string" && /^\d{4}-\d{2}-\d{2}$/.test(e.date_bucket) &&
|
|
219
|
-
|
|
230
|
+
isValidExtToken(e.ext) &&
|
|
220
231
|
typeof e.size === "number" && e.size >= 0 && e.size <= MAX_UPLOAD_BYTES
|
|
221
232
|
);
|
|
222
233
|
}
|