@harborclient/team-hub 0.4.0 → 0.4.2

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/cli.js CHANGED
@@ -364,6 +364,7 @@ var REQUESTS_COLLECTION = "requests";
364
364
  var AUDIT_LOG_COLLECTION = "auditLog";
365
365
  var LLM_USAGE_COLLECTION = "llmUsage";
366
366
  var LLM_USAGE_LOG_COLLECTION = "llmUsageLog";
367
+ var RUN_RESULTS_COLLECTION = "runResults";
367
368
  var WRITE_BATCH_LIMIT = 500;
368
369
 
369
370
  // src/db/systemUsers.ts
@@ -400,7 +401,7 @@ var firestoreConfigSchema = z2.object({
400
401
 
401
402
  // src/db/firestore/utils.ts
402
403
  function parseAuditEntityType(value) {
403
- if (value === "user" || value === "api_token" || value === "collection" || value === "environment" || value === "snippet" || value === "folder" || value === "request") {
404
+ if (value === "user" || value === "api_token" || value === "collection" || value === "environment" || value === "snippet" || value === "folder" || value === "request" || value === "run_result") {
404
405
  return value;
405
406
  }
406
407
  throw new Error(`Invalid audit entity type: ${value}`);
@@ -544,6 +545,19 @@ function mapFirestoreRequest(id, data) {
544
545
  updatedByUserId: data.updatedByUserId ?? null
545
546
  };
546
547
  }
548
+ function mapFirestoreRunResult(id, data) {
549
+ return {
550
+ id,
551
+ kind: data.kind,
552
+ label: data.label,
553
+ collectionName: data.collectionName,
554
+ requestName: data.requestName,
555
+ summary: data.summary,
556
+ payload: data.payload,
557
+ createdAt: data.createdAt,
558
+ createdByUserId: data.createdByUserId ?? null
559
+ };
560
+ }
547
561
  function mapFirestoreAuditLog(id, data) {
548
562
  return {
549
563
  id,
@@ -557,6 +571,53 @@ function mapFirestoreAuditLog(id, data) {
557
571
  };
558
572
  }
559
573
 
574
+ // src/db/runResultPayload.ts
575
+ function isRecord(value) {
576
+ return value != null && typeof value === "object" && !Array.isArray(value);
577
+ }
578
+ function summarizeResultRows(results) {
579
+ let passed = 0;
580
+ let failed = 0;
581
+ let skipped = 0;
582
+ for (const row of results) {
583
+ if (!isRecord(row)) {
584
+ continue;
585
+ }
586
+ const status = row.status;
587
+ if (status === "passed") {
588
+ passed += 1;
589
+ } else if (status === "failed") {
590
+ failed += 1;
591
+ } else if (status === "skipped") {
592
+ skipped += 1;
593
+ }
594
+ }
595
+ return { passed, failed, skipped };
596
+ }
597
+ function buildDefaultRunResultLabel(metadata) {
598
+ const target = metadata.requestName ?? metadata.collectionName ?? "Run";
599
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
600
+ return `${target} \u2014 ${timestamp}`;
601
+ }
602
+ function parseRunResultPayload(payload) {
603
+ const kind = payload.harborclientExport;
604
+ if (kind !== "collection-run-results" && kind !== "request-run-results") {
605
+ throw new Error("Invalid run result payload: harborclientExport is required");
606
+ }
607
+ const results = payload.results;
608
+ if (!Array.isArray(results) || results.length === 0) {
609
+ throw new Error("Invalid run result payload: results are required");
610
+ }
611
+ const collection = isRecord(payload.collection) ? payload.collection : void 0;
612
+ const request = isRecord(payload.request) ? payload.request : void 0;
613
+ return {
614
+ kind,
615
+ collectionName: typeof collection?.name === "string" ? collection.name : null,
616
+ requestName: typeof request?.name === "string" ? request.name : null,
617
+ summary: summarizeResultRows(results)
618
+ };
619
+ }
620
+
560
621
  // src/db/trimRequiredName.ts
561
622
  function trimRequiredName(name, label) {
562
623
  const trimmed = name.trim();
@@ -1822,6 +1883,78 @@ var FirestoreDatabase = class _FirestoreDatabase {
1822
1883
  }
1823
1884
  return usage;
1824
1885
  }
1886
+ /**
1887
+ * Lists run results saved by the given user, newest first.
1888
+ *
1889
+ * @param userId - Owning user identifier.
1890
+ */
1891
+ async listRunResultsForUser(userId) {
1892
+ const snapshot = await this.requireClient().collection(RUN_RESULTS_COLLECTION).where("createdByUserId", "==", userId).orderBy("createdAt", "desc").get();
1893
+ return snapshot.docs.map(
1894
+ (doc) => mapFirestoreRunResult(doc.id, doc.data())
1895
+ );
1896
+ }
1897
+ /**
1898
+ * Lists all run results for admin inspection, newest first.
1899
+ */
1900
+ async listAllRunResults() {
1901
+ const snapshot = await this.requireClient().collection(RUN_RESULTS_COLLECTION).orderBy("createdAt", "desc").get();
1902
+ return snapshot.docs.map(
1903
+ (doc) => mapFirestoreRunResult(doc.id, doc.data())
1904
+ );
1905
+ }
1906
+ /**
1907
+ * Creates a standalone run result snapshot.
1908
+ *
1909
+ * @param input - HarborClient export payload and optional label.
1910
+ * @param actingUserId - User performing the create action.
1911
+ */
1912
+ async createRunResult(input, actingUserId) {
1913
+ const metadata = parseRunResultPayload(input.payload);
1914
+ const label = input.label?.trim() || buildDefaultRunResultLabel(metadata);
1915
+ const id = randomUUID2();
1916
+ const now = /* @__PURE__ */ new Date();
1917
+ const data = {
1918
+ kind: metadata.kind,
1919
+ label,
1920
+ collectionName: metadata.collectionName,
1921
+ requestName: metadata.requestName,
1922
+ summary: metadata.summary,
1923
+ payload: input.payload,
1924
+ createdAt: now,
1925
+ createdByUserId: actingUserId
1926
+ };
1927
+ await this.requireClient().collection(RUN_RESULTS_COLLECTION).doc(id).set(data);
1928
+ await this.recordAuditEntry(actingUserId, "create", "run_result", id);
1929
+ return mapFirestoreRunResult(id, data);
1930
+ }
1931
+ /**
1932
+ * Finds a run result by stable identifier.
1933
+ *
1934
+ * @param id - Run result ID to look up.
1935
+ */
1936
+ async findRunResultById(id) {
1937
+ const snapshot = await this.requireClient().collection(RUN_RESULTS_COLLECTION).doc(id).get();
1938
+ if (!snapshot.exists) {
1939
+ return null;
1940
+ }
1941
+ return mapFirestoreRunResult(id, snapshot.data());
1942
+ }
1943
+ /**
1944
+ * Deletes a run result.
1945
+ *
1946
+ * @param id - Run result ID to delete.
1947
+ * @param actingUserId - User performing the delete action.
1948
+ */
1949
+ async deleteRunResult(id, actingUserId) {
1950
+ const docRef = this.requireClient().collection(RUN_RESULTS_COLLECTION).doc(id);
1951
+ const snapshot = await docRef.get();
1952
+ if (!snapshot.exists) {
1953
+ throw new Error("Run result not found");
1954
+ }
1955
+ await this.recordAuditEntry(actingUserId, "delete", "run_result", id);
1956
+ await docRef.delete();
1957
+ }
1825
1958
  /**
1826
1959
  * Inserts a per-request LLM usage log entry.
1827
1960
  *
@@ -1976,7 +2109,7 @@ function parseAuditAction(value) {
1976
2109
  throw new Error(`Invalid audit action: ${value}`);
1977
2110
  }
1978
2111
  function parseAuditEntityType2(value) {
1979
- if (value === "user" || value === "api_token" || value === "collection" || value === "environment" || value === "snippet" || value === "folder" || value === "request") {
2112
+ if (value === "user" || value === "api_token" || value === "collection" || value === "environment" || value === "snippet" || value === "folder" || value === "request" || value === "run_result") {
1980
2113
  return value;
1981
2114
  }
1982
2115
  throw new Error(`Invalid audit entity type: ${value}`);
@@ -2073,6 +2206,23 @@ function mapSnippetSqlRow(row) {
2073
2206
  deletionLocked: Boolean(row.deletion_locked)
2074
2207
  };
2075
2208
  }
2209
+ function mapRunResultSqlRow(row) {
2210
+ return {
2211
+ id: row.id,
2212
+ kind: row.kind,
2213
+ label: row.label,
2214
+ collectionName: row.collection_name,
2215
+ requestName: row.request_name,
2216
+ summary: {
2217
+ passed: row.summary_passed,
2218
+ failed: row.summary_failed,
2219
+ skipped: row.summary_skipped
2220
+ },
2221
+ payload: parseJson(row.payload, {}),
2222
+ createdAt: row.created_at,
2223
+ createdByUserId: row.created_by_user_id ?? null
2224
+ };
2225
+ }
2076
2226
  function mapFolderSqlRow(row) {
2077
2227
  return {
2078
2228
  id: row.id,
@@ -2349,6 +2499,22 @@ WHERE role = 'user'
2349
2499
  AND snippet_access = '[]'
2350
2500
  AND collection_access LIKE '%"*"%';
2351
2501
  `.trim();
2502
+ var RUN_RESULTS_MIGRATION_SQL = `
2503
+ CREATE TABLE IF NOT EXISTS run_results (
2504
+ id VARCHAR(36) PRIMARY KEY,
2505
+ kind VARCHAR(64) NOT NULL,
2506
+ label VARCHAR(512) NOT NULL,
2507
+ collection_name VARCHAR(512),
2508
+ request_name VARCHAR(512),
2509
+ summary_passed INT NOT NULL DEFAULT 0,
2510
+ summary_failed INT NOT NULL DEFAULT 0,
2511
+ summary_skipped INT NOT NULL DEFAULT 0,
2512
+ payload LONGTEXT NOT NULL,
2513
+ created_at DATETIME NOT NULL,
2514
+ created_by_user_id VARCHAR(36),
2515
+ INDEX run_results_created_idx (created_at)
2516
+ )
2517
+ `.trim();
2352
2518
  var MYSQL_MIGRATIONS = [
2353
2519
  USERS_MIGRATION_SQL,
2354
2520
  API_TOKENS_MIGRATION_SQL,
@@ -2374,7 +2540,8 @@ var MYSQL_MIGRATIONS = [
2374
2540
  COLLECTIONS_DELETION_LOCKED_MIGRATION_SQL,
2375
2541
  ENVIRONMENTS_DELETION_LOCKED_MIGRATION_SQL,
2376
2542
  USERS_SNIPPET_ACCESS_MIGRATION_SQL,
2377
- USERS_SNIPPET_ACCESS_BACKFILL_SQL
2543
+ USERS_SNIPPET_ACCESS_BACKFILL_SQL,
2544
+ RUN_RESULTS_MIGRATION_SQL
2378
2545
  ];
2379
2546
 
2380
2547
  // src/db/mysql/schemas.ts
@@ -2478,6 +2645,8 @@ var LLM_USAGE_SELECT_COLUMNS = `id, user_id, period, prompt_tokens, completion_t
2478
2645
  var COLLECTION_SELECT = `SELECT ${COLLECTION_SELECT_COLUMNS} FROM collections`;
2479
2646
  var ENVIRONMENT_SELECT = `SELECT ${ENVIRONMENT_SELECT_COLUMNS} FROM environments`;
2480
2647
  var SNIPPET_SELECT = `SELECT ${SNIPPET_SELECT_COLUMNS} FROM snippets`;
2648
+ var RUN_RESULT_SELECT_COLUMNS = "id, kind, label, collection_name, request_name, summary_passed, summary_failed, summary_skipped, payload, created_at, created_by_user_id";
2649
+ var RUN_RESULT_SELECT = `SELECT ${RUN_RESULT_SELECT_COLUMNS} FROM run_results`;
2481
2650
  var USER_SELECT = `SELECT ${USER_SELECT_COLUMNS} FROM users`;
2482
2651
  var API_TOKEN_SELECT = `SELECT ${API_TOKEN_SELECT_COLUMNS} FROM api_tokens`;
2483
2652
  var FOLDER_SELECT = `SELECT ${FOLDER_SELECT_COLUMNS} FROM folders`;
@@ -3883,6 +4052,93 @@ var MysqlDatabase = class _MysqlDatabase {
3883
4052
  );
3884
4053
  return rows.map(mapLlmUsageLogSqlRow);
3885
4054
  }
4055
+ /**
4056
+ * Lists run results saved by the given user, newest first.
4057
+ */
4058
+ async listRunResultsForUser(userId) {
4059
+ const rows = await this.queryRows(
4060
+ `${RUN_RESULT_SELECT} WHERE created_by_user_id = ? ORDER BY created_at DESC`,
4061
+ [userId]
4062
+ );
4063
+ return rows.map(mapRunResultSqlRow);
4064
+ }
4065
+ /**
4066
+ * Lists all run results for admin inspection, newest first.
4067
+ */
4068
+ async listAllRunResults() {
4069
+ const rows = await this.queryRows(
4070
+ `${RUN_RESULT_SELECT} ORDER BY created_at DESC`
4071
+ );
4072
+ return rows.map(mapRunResultSqlRow);
4073
+ }
4074
+ /**
4075
+ * Creates a standalone run result snapshot.
4076
+ */
4077
+ async createRunResult(input, actingUserId) {
4078
+ const metadata = parseRunResultPayload(input.payload);
4079
+ const label = input.label?.trim() || buildDefaultRunResultLabel(metadata);
4080
+ const id = randomUUID3();
4081
+ const now = /* @__PURE__ */ new Date();
4082
+ await this.executeStatement(
4083
+ `INSERT INTO run_results (
4084
+ id,
4085
+ kind,
4086
+ label,
4087
+ collection_name,
4088
+ request_name,
4089
+ summary_passed,
4090
+ summary_failed,
4091
+ summary_skipped,
4092
+ payload,
4093
+ created_at,
4094
+ created_by_user_id
4095
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4096
+ [
4097
+ id,
4098
+ metadata.kind,
4099
+ label,
4100
+ metadata.collectionName,
4101
+ metadata.requestName,
4102
+ metadata.summary.passed,
4103
+ metadata.summary.failed,
4104
+ metadata.summary.skipped,
4105
+ JSON.stringify(input.payload),
4106
+ now,
4107
+ actingUserId
4108
+ ]
4109
+ );
4110
+ const rows = await this.queryRows(
4111
+ `${RUN_RESULT_SELECT} WHERE id = ?`,
4112
+ [id]
4113
+ );
4114
+ const row = rows[0];
4115
+ if (!row) {
4116
+ throw new Error("Run result not found after insert");
4117
+ }
4118
+ await this.recordAuditEntry(actingUserId, "create", "run_result", id);
4119
+ return mapRunResultSqlRow(row);
4120
+ }
4121
+ /**
4122
+ * Finds a run result by id.
4123
+ */
4124
+ async findRunResultById(id) {
4125
+ const rows = await this.queryRows(
4126
+ `${RUN_RESULT_SELECT} WHERE id = ?`,
4127
+ [id]
4128
+ );
4129
+ const row = rows[0];
4130
+ return row ? mapRunResultSqlRow(row) : null;
4131
+ }
4132
+ /**
4133
+ * Deletes a run result by id.
4134
+ */
4135
+ async deleteRunResult(id, actingUserId) {
4136
+ const result = await this.executeStatement("DELETE FROM run_results WHERE id = ?", [id]);
4137
+ if (result.affectedRows === 0) {
4138
+ throw new Error("Run result not found");
4139
+ }
4140
+ await this.recordAuditEntry(actingUserId, "delete", "run_result", id);
4141
+ }
3886
4142
  /**
3887
4143
  * Ensures the internal system user exists and caches its identifier.
3888
4144
  */
@@ -4229,6 +4485,22 @@ WHERE role = 'user'
4229
4485
  AND snippet_access = '[]'
4230
4486
  AND collection_access LIKE '%"*"%';
4231
4487
  `.trim();
4488
+ var RUN_RESULTS_MIGRATION_SQL2 = `
4489
+ CREATE TABLE IF NOT EXISTS run_results (
4490
+ id TEXT PRIMARY KEY,
4491
+ kind TEXT NOT NULL CHECK (kind IN ('collection-run-results', 'request-run-results')),
4492
+ label TEXT NOT NULL,
4493
+ collection_name TEXT,
4494
+ request_name TEXT,
4495
+ summary_passed INT NOT NULL DEFAULT 0,
4496
+ summary_failed INT NOT NULL DEFAULT 0,
4497
+ summary_skipped INT NOT NULL DEFAULT 0,
4498
+ payload TEXT NOT NULL,
4499
+ created_at TIMESTAMPTZ NOT NULL,
4500
+ created_by_user_id TEXT REFERENCES users(id) ON DELETE SET NULL
4501
+ );
4502
+ CREATE INDEX IF NOT EXISTS run_results_created_idx ON run_results (created_at DESC);
4503
+ `.trim();
4232
4504
  var POSTGRES_MIGRATIONS = [
4233
4505
  USERS_MIGRATION_SQL2,
4234
4506
  API_TOKENS_MIGRATION_SQL2,
@@ -4254,7 +4526,8 @@ var POSTGRES_MIGRATIONS = [
4254
4526
  COLLECTIONS_DELETION_LOCKED_MIGRATION_SQL2,
4255
4527
  ENVIRONMENTS_DELETION_LOCKED_MIGRATION_SQL2,
4256
4528
  USERS_SNIPPET_ACCESS_MIGRATION_SQL2,
4257
- USERS_SNIPPET_ACCESS_BACKFILL_SQL2
4529
+ USERS_SNIPPET_ACCESS_BACKFILL_SQL2,
4530
+ RUN_RESULTS_MIGRATION_SQL2
4258
4531
  ];
4259
4532
 
4260
4533
  // src/db/postgres/schemas.ts
@@ -4279,6 +4552,8 @@ var { Pool } = pg;
4279
4552
  var COLLECTION_SELECT2 = `SELECT ${COLLECTION_SELECT_COLUMNS} FROM collections`;
4280
4553
  var ENVIRONMENT_SELECT2 = `SELECT ${ENVIRONMENT_SELECT_COLUMNS} FROM environments`;
4281
4554
  var SNIPPET_SELECT2 = `SELECT ${SNIPPET_SELECT_COLUMNS} FROM snippets`;
4555
+ var RUN_RESULT_SELECT_COLUMNS2 = "id, kind, label, collection_name, request_name, summary_passed, summary_failed, summary_skipped, payload, created_at, created_by_user_id";
4556
+ var RUN_RESULT_SELECT2 = `SELECT ${RUN_RESULT_SELECT_COLUMNS2} FROM run_results`;
4282
4557
  var USER_SELECT2 = `SELECT ${USER_SELECT_COLUMNS} FROM users`;
4283
4558
  var API_TOKEN_SELECT2 = `SELECT ${API_TOKEN_SELECT_COLUMNS} FROM api_tokens`;
4284
4559
  var FOLDER_SELECT2 = `SELECT ${FOLDER_SELECT_COLUMNS} FROM folders`;
@@ -5629,6 +5904,87 @@ var PostgresDatabase = class _PostgresDatabase {
5629
5904
  );
5630
5905
  return result.rows.map(mapLlmUsageLogSqlRow);
5631
5906
  }
5907
+ /**
5908
+ * Lists run results saved by the given user, newest first.
5909
+ */
5910
+ async listRunResultsForUser(userId) {
5911
+ const result = await this.query(
5912
+ `${RUN_RESULT_SELECT2} WHERE created_by_user_id = $1 ORDER BY created_at DESC`,
5913
+ [userId]
5914
+ );
5915
+ return result.rows.map(mapRunResultSqlRow);
5916
+ }
5917
+ /**
5918
+ * Lists all run results for admin inspection, newest first.
5919
+ */
5920
+ async listAllRunResults() {
5921
+ const result = await this.query(
5922
+ `${RUN_RESULT_SELECT2} ORDER BY created_at DESC`
5923
+ );
5924
+ return result.rows.map(mapRunResultSqlRow);
5925
+ }
5926
+ /**
5927
+ * Creates a standalone run result snapshot.
5928
+ */
5929
+ async createRunResult(input, actingUserId) {
5930
+ const metadata = parseRunResultPayload(input.payload);
5931
+ const label = input.label?.trim() || buildDefaultRunResultLabel(metadata);
5932
+ const id = randomUUID4();
5933
+ const now = /* @__PURE__ */ new Date();
5934
+ const result = await this.query(
5935
+ `INSERT INTO run_results (
5936
+ id,
5937
+ kind,
5938
+ label,
5939
+ collection_name,
5940
+ request_name,
5941
+ summary_passed,
5942
+ summary_failed,
5943
+ summary_skipped,
5944
+ payload,
5945
+ created_at,
5946
+ created_by_user_id
5947
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
5948
+ RETURNING ${RUN_RESULT_SELECT_COLUMNS2}`,
5949
+ [
5950
+ id,
5951
+ metadata.kind,
5952
+ label,
5953
+ metadata.collectionName,
5954
+ metadata.requestName,
5955
+ metadata.summary.passed,
5956
+ metadata.summary.failed,
5957
+ metadata.summary.skipped,
5958
+ JSON.stringify(input.payload),
5959
+ now,
5960
+ actingUserId
5961
+ ]
5962
+ );
5963
+ const row = result.rows[0];
5964
+ if (!row) {
5965
+ throw new Error("Run result not found after insert");
5966
+ }
5967
+ await this.recordAuditEntry(actingUserId, "create", "run_result", id);
5968
+ return mapRunResultSqlRow(row);
5969
+ }
5970
+ /**
5971
+ * Finds a run result by id.
5972
+ */
5973
+ async findRunResultById(id) {
5974
+ const result = await this.query(`${RUN_RESULT_SELECT2} WHERE id = $1`, [id]);
5975
+ const row = result.rows[0];
5976
+ return row ? mapRunResultSqlRow(row) : null;
5977
+ }
5978
+ /**
5979
+ * Deletes a run result by id.
5980
+ */
5981
+ async deleteRunResult(id, actingUserId) {
5982
+ const result = await this.query("DELETE FROM run_results WHERE id = $1", [id]);
5983
+ if ((result.rowCount ?? 0) === 0) {
5984
+ throw new Error("Run result not found");
5985
+ }
5986
+ await this.recordAuditEntry(actingUserId, "delete", "run_result", id);
5987
+ }
5632
5988
  /**
5633
5989
  * Returns the active pool or throws when connect has not been called.
5634
5990
  *
@@ -6655,6 +7011,9 @@ function canListEnvironments(user) {
6655
7011
  function canListSnippets(user) {
6656
7012
  return canUseDataApi(user) || canUseManagementApi(user);
6657
7013
  }
7014
+ function canListRunResults(user) {
7015
+ return canUseDataApi(user) || canUseManagementApi(user);
7016
+ }
6658
7017
  function hasWildcardAccess(access) {
6659
7018
  return access.includes("*");
6660
7019
  }
@@ -6691,6 +7050,9 @@ function canDeleteCollection(user, collection) {
6691
7050
  function canDeleteRequest(user, request) {
6692
7051
  return canUseDataApi(user) && canAccessCollection(user, request.collectionId) && request.createdByUserId === user.id;
6693
7052
  }
7053
+ function canDeleteRunResult(user, runResult) {
7054
+ return canUseDataApi(user) && runResult.createdByUserId === user.id;
7055
+ }
6694
7056
  function canCreateCollection(user) {
6695
7057
  return user.role === "user" && hasWildcardAccess(user.collectionAccess);
6696
7058
  }
@@ -6865,7 +7227,7 @@ function denyUnlessAllowed(reply, allowed) {
6865
7227
  }
6866
7228
 
6867
7229
  // src/server/routes/schemas/admin.ts
6868
- import { z as z8 } from "zod/v4";
7230
+ import { z as z9 } from "zod/v4";
6869
7231
 
6870
7232
  // src/server/routes/schemas/auth.ts
6871
7233
  import { z as z6 } from "zod/v4";
@@ -6932,316 +7294,381 @@ var llmUsageSummaryResponseSchema = z7.object({
6932
7294
  limit: z7.number().int().positive().nullable()
6933
7295
  });
6934
7296
 
6935
- // src/server/routes/schemas/admin.ts
6936
- var adminResourceOptionSchema = z8.object({
7297
+ // src/server/routes/schemas/entities.ts
7298
+ import { z as z8 } from "zod/v4";
7299
+ var collectionRecordSchema = z8.object({
6937
7300
  id: z8.string(),
6938
7301
  name: z8.string(),
7302
+ variables: z8.array(variableSchema),
7303
+ headers: z8.array(keyValueSchema),
7304
+ auth: authConfigSchema,
7305
+ preRequestScript: z8.string(),
7306
+ postRequestScript: z8.string(),
7307
+ createdAt: timestampSchema,
7308
+ updatedAt: timestampSchema,
7309
+ createdByUserId: z8.string().nullable(),
7310
+ updatedByUserId: z8.string().nullable(),
6939
7311
  deletionLocked: z8.boolean()
6940
7312
  });
6941
- var adminEntityConfigSchema = z8.object({
7313
+ var environmentRecordSchema = z8.object({
6942
7314
  id: z8.string(),
6943
7315
  name: z8.string(),
7316
+ variables: z8.array(variableSchema),
7317
+ createdAt: timestampSchema,
7318
+ updatedAt: timestampSchema,
7319
+ createdByUserId: z8.string().nullable(),
7320
+ updatedByUserId: z8.string().nullable(),
6944
7321
  deletionLocked: z8.boolean()
6945
7322
  });
6946
- var updateAdminCollectionBodySchema = z8.object({
6947
- deletionLocked: z8.boolean()
6948
- });
6949
- var updateAdminEnvironmentBodySchema = z8.object({
6950
- deletionLocked: z8.boolean()
6951
- });
6952
- var updateAdminSnippetBodySchema = z8.object({
7323
+ var snippetScopeSchema = z8.enum(["pre-request", "post-request", "any"]);
7324
+ var snippetRecordSchema = z8.object({
7325
+ id: z8.string(),
7326
+ name: z8.string(),
7327
+ code: z8.string(),
7328
+ scope: snippetScopeSchema,
7329
+ sortOrder: z8.number().int(),
7330
+ createdAt: timestampSchema,
7331
+ updatedAt: timestampSchema,
7332
+ createdByUserId: z8.string().nullable(),
7333
+ updatedByUserId: z8.string().nullable(),
6953
7334
  deletionLocked: z8.boolean()
6954
7335
  });
6955
- var listAdminCollectionsResponseSchema = z8.object({
6956
- collections: z8.array(adminResourceOptionSchema)
6957
- });
6958
- var listAdminEnvironmentsResponseSchema = z8.object({
6959
- environments: z8.array(adminResourceOptionSchema)
6960
- });
6961
- var listAdminSnippetsResponseSchema = z8.object({
6962
- snippets: z8.array(adminResourceOptionSchema)
7336
+ var folderRecordSchema = z8.object({
7337
+ id: z8.string(),
7338
+ collectionId: z8.string(),
7339
+ name: z8.string(),
7340
+ sortOrder: z8.number().int(),
7341
+ createdAt: timestampSchema,
7342
+ updatedAt: timestampSchema,
7343
+ createdByUserId: z8.string().nullable(),
7344
+ updatedByUserId: z8.string().nullable()
6963
7345
  });
6964
- var listAdminLlmModelsResponseSchema = listLlmModelsResponseSchema;
6965
- var hubUserRecordSchema = z8.object({
7346
+ var savedRequestRecordSchema = z8.object({
6966
7347
  id: z8.string(),
7348
+ collectionId: z8.string(),
6967
7349
  name: z8.string(),
6968
- role: userRoleSchema,
6969
- collectionAccess: z8.array(z8.string()),
6970
- environmentAccess: z8.array(z8.string()),
6971
- snippetAccess: z8.array(z8.string()),
6972
- llmAccess: z8.boolean(),
6973
- llmModels: z8.array(z8.string()),
6974
- llmMonthlyTokenLimit: z8.number().int().nonnegative().nullable(),
7350
+ method: httpMethodSchema,
7351
+ url: z8.string(),
7352
+ headers: z8.array(keyValueSchema),
7353
+ params: z8.array(keyValueSchema),
7354
+ auth: authConfigSchema,
7355
+ body: z8.string(),
7356
+ bodyType: bodyTypeSchema,
7357
+ preRequestScript: z8.string(),
7358
+ postRequestScript: z8.string(),
7359
+ comment: z8.string(),
7360
+ folderId: z8.string().nullable(),
7361
+ sortOrder: z8.number().int(),
6975
7362
  createdAt: timestampSchema,
6976
- updatedAt: timestampSchema
7363
+ updatedAt: timestampSchema,
7364
+ createdByUserId: z8.string().nullable(),
7365
+ updatedByUserId: z8.string().nullable()
6977
7366
  });
6978
- var adminUserListEntrySchema = hubUserRecordSchema.extend({
6979
- warnings: z8.array(z8.string())
7367
+ var createCollectionBodySchema = z8.object({
7368
+ name: z8.string().trim().min(1)
6980
7369
  });
6981
- var listAdminUsersResponseSchema = z8.object({
6982
- users: z8.array(adminUserListEntrySchema)
7370
+ var updateCollectionBodySchema = z8.object({
7371
+ name: z8.string().trim().min(1),
7372
+ variables: z8.array(variableSchema),
7373
+ headers: z8.array(keyValueSchema),
7374
+ preRequestScript: z8.string(),
7375
+ postRequestScript: z8.string(),
7376
+ auth: authConfigSchema
6983
7377
  });
6984
- var updateAdminUserBodySchema = z8.object({
6985
- name: z8.string().trim().min(1).optional(),
6986
- role: userRoleSchema.optional(),
6987
- collectionAccess: z8.array(z8.string()).optional(),
6988
- environmentAccess: z8.array(z8.string()).optional(),
6989
- snippetAccess: z8.array(z8.string()).optional(),
6990
- llmAccess: z8.boolean().optional(),
6991
- llmModels: z8.array(z8.string()).optional(),
6992
- llmMonthlyTokenLimit: z8.number().int().nonnegative().nullable().optional()
7378
+ var createEnvironmentBodySchema = z8.object({
7379
+ name: z8.string().trim().min(1)
6993
7380
  });
6994
- var createAdminUserBodySchema = z8.object({
7381
+ var updateEnvironmentBodySchema = z8.object({
6995
7382
  name: z8.string().trim().min(1),
6996
- role: userRoleSchema,
6997
- collectionAccess: z8.array(z8.string()).optional(),
6998
- environmentAccess: z8.array(z8.string()).optional(),
6999
- snippetAccess: z8.array(z8.string()).optional(),
7000
- llmAccess: z8.boolean().optional(),
7001
- llmModels: z8.array(z8.string()).optional(),
7002
- llmMonthlyTokenLimit: z8.number().int().nonnegative().nullable().optional()
7383
+ variables: z8.array(variableSchema)
7003
7384
  });
7004
- var hubApiTokenRecordSchema = z8.object({
7005
- id: z8.string(),
7006
- userId: z8.string(),
7007
- name: z8.string(),
7008
- tokenPrefix: z8.string(),
7009
- createdAt: timestampSchema,
7010
- lastUsedAt: timestampSchema.nullable(),
7011
- revokedAt: timestampSchema.nullable()
7385
+ var createSnippetBodySchema = z8.object({
7386
+ name: z8.string().trim().min(1),
7387
+ code: z8.string().default(""),
7388
+ scope: snippetScopeSchema.default("any")
7012
7389
  });
7013
- var createAdminUserResponseSchema = z8.object({
7014
- user: hubUserRecordSchema,
7015
- token: hubApiTokenRecordSchema,
7016
- secret: z8.string()
7390
+ var updateSnippetBodySchema = z8.object({
7391
+ name: z8.string().trim().min(1),
7392
+ code: z8.string(),
7393
+ scope: snippetScopeSchema
7017
7394
  });
7018
- var createAdminTokenBodySchema = z8.object({
7395
+ var createFolderBodySchema = z8.object({
7019
7396
  name: z8.string().trim().min(1)
7020
7397
  });
7021
- var createdApiTokenResponseSchema = z8.object({
7022
- token: hubApiTokenRecordSchema,
7023
- secret: z8.string()
7398
+ var renameFolderBodySchema = z8.object({
7399
+ name: z8.string().trim().min(1)
7400
+ });
7401
+ var reorderFoldersBodySchema = z8.object({
7402
+ orderedFolderIds: z8.array(z8.string().trim().min(1))
7403
+ });
7404
+ var saveRequestBodySchema = z8.object({
7405
+ name: z8.string().trim().min(1),
7406
+ method: httpMethodSchema,
7407
+ url: z8.string(),
7408
+ headers: z8.array(keyValueSchema),
7409
+ params: z8.array(keyValueSchema),
7410
+ auth: authConfigSchema,
7411
+ body: z8.string(),
7412
+ bodyType: bodyTypeSchema,
7413
+ preRequestScript: z8.string(),
7414
+ postRequestScript: z8.string(),
7415
+ comment: z8.string(),
7416
+ folderId: z8.string().nullable().optional()
7417
+ });
7418
+ var updateSaveRequestBodySchema = saveRequestBodySchema.extend({
7419
+ collectionId: z8.string().trim().min(1)
7024
7420
  });
7025
- var listAdminTokensResponseSchema = z8.object({
7026
- tokens: z8.array(hubApiTokenRecordSchema)
7421
+ var reorderRequestsBodySchema = z8.object({
7422
+ folderId: z8.string().nullable(),
7423
+ orderedRequestIds: z8.array(z8.string().trim().min(1))
7027
7424
  });
7028
- var reloadConfigSectionResultSchema = z8.object({
7029
- section: z8.enum(["db", "redis", "llm", "plugins", "server"]),
7030
- status: z8.enum(["reloaded", "unchanged", "failed", "restart-required"]),
7031
- error: z8.string().optional()
7425
+ var moveRequestBodySchema = z8.object({
7426
+ folderId: z8.string().nullable(),
7427
+ index: z8.number().int().min(0)
7032
7428
  });
7033
- var reloadConfigResponseSchema = z8.object({
7034
- sections: z8.array(reloadConfigSectionResultSchema),
7035
- fatalError: z8.string().optional()
7429
+ var listCollectionsResponseSchema = z8.object({
7430
+ collections: z8.array(collectionRecordSchema)
7036
7431
  });
7037
- function serializeHubUser(user) {
7432
+ var listEnvironmentsResponseSchema = z8.object({
7433
+ environments: z8.array(environmentRecordSchema)
7434
+ });
7435
+ var listSnippetsResponseSchema = z8.object({
7436
+ snippets: z8.array(snippetRecordSchema)
7437
+ });
7438
+ var listFoldersResponseSchema = z8.object({
7439
+ folders: z8.array(folderRecordSchema)
7440
+ });
7441
+ var listRequestsResponseSchema = z8.object({
7442
+ requests: z8.array(savedRequestRecordSchema)
7443
+ });
7444
+ var emptyResponseSchema = z8.null();
7445
+ function serializeCollection(record) {
7038
7446
  return {
7039
- id: user.id,
7040
- name: user.name,
7041
- role: user.role,
7042
- collectionAccess: user.collectionAccess,
7043
- environmentAccess: user.environmentAccess,
7044
- snippetAccess: user.snippetAccess,
7045
- llmAccess: user.llmAccess,
7046
- llmModels: user.llmModels,
7047
- llmMonthlyTokenLimit: user.llmMonthlyTokenLimit,
7048
- createdAt: user.createdAt.toISOString(),
7049
- updatedAt: user.updatedAt.toISOString()
7447
+ ...record,
7448
+ createdAt: record.createdAt.toISOString(),
7449
+ updatedAt: record.updatedAt.toISOString()
7050
7450
  };
7051
7451
  }
7052
- function serializeApiToken(token) {
7452
+ function serializeEnvironment(record) {
7053
7453
  return {
7054
- id: token.id,
7055
- userId: token.userId,
7056
- name: token.name,
7057
- tokenPrefix: token.tokenPrefix,
7058
- createdAt: token.createdAt.toISOString(),
7059
- lastUsedAt: token.lastUsedAt ? token.lastUsedAt.toISOString() : null,
7060
- revokedAt: token.revokedAt ? token.revokedAt.toISOString() : null
7454
+ ...record,
7455
+ createdAt: record.createdAt.toISOString(),
7456
+ updatedAt: record.updatedAt.toISOString()
7061
7457
  };
7062
7458
  }
7063
-
7064
- // src/server/routes/schemas/entities.ts
7065
- import { z as z9 } from "zod/v4";
7066
- var collectionRecordSchema = z9.object({
7067
- id: z9.string(),
7068
- name: z9.string(),
7069
- variables: z9.array(variableSchema),
7070
- headers: z9.array(keyValueSchema),
7071
- auth: authConfigSchema,
7072
- preRequestScript: z9.string(),
7073
- postRequestScript: z9.string(),
7074
- createdAt: timestampSchema,
7075
- updatedAt: timestampSchema,
7076
- createdByUserId: z9.string().nullable(),
7077
- updatedByUserId: z9.string().nullable(),
7078
- deletionLocked: z9.boolean()
7079
- });
7080
- var environmentRecordSchema = z9.object({
7081
- id: z9.string(),
7082
- name: z9.string(),
7083
- variables: z9.array(variableSchema),
7084
- createdAt: timestampSchema,
7085
- updatedAt: timestampSchema,
7086
- createdByUserId: z9.string().nullable(),
7087
- updatedByUserId: z9.string().nullable(),
7088
- deletionLocked: z9.boolean()
7459
+ function serializeSnippet(record) {
7460
+ return {
7461
+ ...record,
7462
+ createdAt: record.createdAt.toISOString(),
7463
+ updatedAt: record.updatedAt.toISOString()
7464
+ };
7465
+ }
7466
+ function serializeFolder(record) {
7467
+ return {
7468
+ ...record,
7469
+ createdAt: record.createdAt.toISOString(),
7470
+ updatedAt: record.updatedAt.toISOString()
7471
+ };
7472
+ }
7473
+ function serializeSavedRequest(record) {
7474
+ return {
7475
+ ...record,
7476
+ createdAt: record.createdAt.toISOString(),
7477
+ updatedAt: record.updatedAt.toISOString()
7478
+ };
7479
+ }
7480
+ var runResultRecordSchema = z8.object({
7481
+ id: z8.string(),
7482
+ kind: z8.enum(["collection-run-results", "request-run-results"]),
7483
+ label: z8.string(),
7484
+ collectionName: z8.string().nullable(),
7485
+ requestName: z8.string().nullable(),
7486
+ summary: z8.object({
7487
+ passed: z8.number().int().nonnegative(),
7488
+ failed: z8.number().int().nonnegative(),
7489
+ skipped: z8.number().int().nonnegative()
7490
+ }),
7491
+ createdAt: timestampSchema,
7492
+ createdByUserId: z8.string().nullable()
7493
+ });
7494
+ var runResultDetailSchema = runResultRecordSchema.extend({
7495
+ payload: z8.record(z8.string(), z8.unknown())
7496
+ });
7497
+ var createRunResultBodySchema = z8.object({
7498
+ label: z8.string().trim().min(1).optional(),
7499
+ payload: z8.record(z8.string(), z8.unknown())
7089
7500
  });
7090
- var snippetScopeSchema = z9.enum(["pre-request", "post-request", "any"]);
7091
- var snippetRecordSchema = z9.object({
7501
+ var listRunResultsResponseSchema = z8.object({
7502
+ runResults: z8.array(runResultRecordSchema)
7503
+ });
7504
+ function serializeRunResult(record) {
7505
+ return {
7506
+ id: record.id,
7507
+ kind: record.kind,
7508
+ label: record.label,
7509
+ collectionName: record.collectionName,
7510
+ requestName: record.requestName,
7511
+ summary: record.summary,
7512
+ createdAt: record.createdAt.toISOString(),
7513
+ createdByUserId: record.createdByUserId
7514
+ };
7515
+ }
7516
+ function serializeRunResultDetail(record) {
7517
+ return {
7518
+ ...serializeRunResult(record),
7519
+ payload: record.payload
7520
+ };
7521
+ }
7522
+
7523
+ // src/server/routes/schemas/admin.ts
7524
+ var adminResourceOptionSchema = z9.object({
7092
7525
  id: z9.string(),
7093
7526
  name: z9.string(),
7094
- code: z9.string(),
7095
- scope: snippetScopeSchema,
7096
- sortOrder: z9.number().int(),
7097
- createdAt: timestampSchema,
7098
- updatedAt: timestampSchema,
7099
- createdByUserId: z9.string().nullable(),
7100
- updatedByUserId: z9.string().nullable(),
7101
7527
  deletionLocked: z9.boolean()
7102
7528
  });
7103
- var folderRecordSchema = z9.object({
7529
+ var adminEntityConfigSchema = z9.object({
7104
7530
  id: z9.string(),
7105
- collectionId: z9.string(),
7106
7531
  name: z9.string(),
7107
- sortOrder: z9.number().int(),
7108
- createdAt: timestampSchema,
7109
- updatedAt: timestampSchema,
7110
- createdByUserId: z9.string().nullable(),
7111
- updatedByUserId: z9.string().nullable()
7532
+ deletionLocked: z9.boolean()
7112
7533
  });
7113
- var savedRequestRecordSchema = z9.object({
7114
- id: z9.string(),
7115
- collectionId: z9.string(),
7116
- name: z9.string(),
7117
- method: httpMethodSchema,
7118
- url: z9.string(),
7119
- headers: z9.array(keyValueSchema),
7120
- params: z9.array(keyValueSchema),
7121
- auth: authConfigSchema,
7122
- body: z9.string(),
7123
- bodyType: bodyTypeSchema,
7124
- preRequestScript: z9.string(),
7125
- postRequestScript: z9.string(),
7126
- comment: z9.string(),
7127
- folderId: z9.string().nullable(),
7128
- sortOrder: z9.number().int(),
7129
- createdAt: timestampSchema,
7130
- updatedAt: timestampSchema,
7131
- createdByUserId: z9.string().nullable(),
7132
- updatedByUserId: z9.string().nullable()
7534
+ var updateAdminCollectionBodySchema = z9.object({
7535
+ deletionLocked: z9.boolean()
7133
7536
  });
7134
- var createCollectionBodySchema = z9.object({
7135
- name: z9.string().trim().min(1)
7537
+ var updateAdminEnvironmentBodySchema = z9.object({
7538
+ deletionLocked: z9.boolean()
7136
7539
  });
7137
- var updateCollectionBodySchema = z9.object({
7138
- name: z9.string().trim().min(1),
7139
- variables: z9.array(variableSchema),
7140
- headers: z9.array(keyValueSchema),
7141
- preRequestScript: z9.string(),
7142
- postRequestScript: z9.string(),
7143
- auth: authConfigSchema
7540
+ var updateAdminSnippetBodySchema = z9.object({
7541
+ name: z9.string().trim().min(1).optional(),
7542
+ code: z9.string().optional(),
7543
+ scope: snippetScopeSchema.optional(),
7544
+ deletionLocked: z9.boolean().optional()
7545
+ }).superRefine((body, ctx) => {
7546
+ const hasContentFields = body.name !== void 0 || body.code !== void 0 || body.scope !== void 0;
7547
+ const hasFullContent = body.name !== void 0 && body.code !== void 0 && body.scope !== void 0;
7548
+ if (hasContentFields && !hasFullContent) {
7549
+ ctx.addIssue({
7550
+ code: "custom",
7551
+ message: "name, code, and scope must be provided together"
7552
+ });
7553
+ }
7554
+ if (!hasContentFields && body.deletionLocked === void 0) {
7555
+ ctx.addIssue({
7556
+ code: "custom",
7557
+ message: "Provide snippet content (name, code, scope) and/or deletionLocked"
7558
+ });
7559
+ }
7144
7560
  });
7145
- var createEnvironmentBodySchema = z9.object({
7146
- name: z9.string().trim().min(1)
7561
+ var createAdminSnippetBodySchema = createSnippetBodySchema;
7562
+ var listAdminCollectionsResponseSchema = z9.object({
7563
+ collections: z9.array(adminResourceOptionSchema)
7147
7564
  });
7148
- var updateEnvironmentBodySchema = z9.object({
7149
- name: z9.string().trim().min(1),
7150
- variables: z9.array(variableSchema)
7565
+ var listAdminEnvironmentsResponseSchema = z9.object({
7566
+ environments: z9.array(adminResourceOptionSchema)
7151
7567
  });
7152
- var createSnippetBodySchema = z9.object({
7153
- name: z9.string().trim().min(1),
7154
- code: z9.string().default(""),
7155
- scope: snippetScopeSchema.default("any")
7568
+ var listAdminSnippetsResponseSchema = z9.object({
7569
+ snippets: z9.array(snippetRecordSchema)
7156
7570
  });
7157
- var updateSnippetBodySchema = z9.object({
7158
- name: z9.string().trim().min(1),
7159
- code: z9.string(),
7160
- scope: snippetScopeSchema
7571
+ var listAdminRunResultsResponseSchema = z9.object({
7572
+ runResults: z9.array(runResultRecordSchema)
7161
7573
  });
7162
- var createFolderBodySchema = z9.object({
7163
- name: z9.string().trim().min(1)
7574
+ var adminSnippetRecordSchema = snippetRecordSchema;
7575
+ var listAdminLlmModelsResponseSchema = listLlmModelsResponseSchema;
7576
+ var hubUserRecordSchema = z9.object({
7577
+ id: z9.string(),
7578
+ name: z9.string(),
7579
+ role: userRoleSchema,
7580
+ collectionAccess: z9.array(z9.string()),
7581
+ environmentAccess: z9.array(z9.string()),
7582
+ snippetAccess: z9.array(z9.string()),
7583
+ llmAccess: z9.boolean(),
7584
+ llmModels: z9.array(z9.string()),
7585
+ llmMonthlyTokenLimit: z9.number().int().nonnegative().nullable(),
7586
+ createdAt: timestampSchema,
7587
+ updatedAt: timestampSchema
7164
7588
  });
7165
- var renameFolderBodySchema = z9.object({
7166
- name: z9.string().trim().min(1)
7589
+ var adminUserListEntrySchema = hubUserRecordSchema.extend({
7590
+ warnings: z9.array(z9.string())
7167
7591
  });
7168
- var reorderFoldersBodySchema = z9.object({
7169
- orderedFolderIds: z9.array(z9.string().trim().min(1))
7592
+ var listAdminUsersResponseSchema = z9.object({
7593
+ users: z9.array(adminUserListEntrySchema)
7170
7594
  });
7171
- var saveRequestBodySchema = z9.object({
7172
- name: z9.string().trim().min(1),
7173
- method: httpMethodSchema,
7174
- url: z9.string(),
7175
- headers: z9.array(keyValueSchema),
7176
- params: z9.array(keyValueSchema),
7177
- auth: authConfigSchema,
7178
- body: z9.string(),
7179
- bodyType: bodyTypeSchema,
7180
- preRequestScript: z9.string(),
7181
- postRequestScript: z9.string(),
7182
- comment: z9.string(),
7183
- folderId: z9.string().nullable().optional()
7595
+ var updateAdminUserBodySchema = z9.object({
7596
+ name: z9.string().trim().min(1).optional(),
7597
+ role: userRoleSchema.optional(),
7598
+ collectionAccess: z9.array(z9.string()).optional(),
7599
+ environmentAccess: z9.array(z9.string()).optional(),
7600
+ snippetAccess: z9.array(z9.string()).optional(),
7601
+ llmAccess: z9.boolean().optional(),
7602
+ llmModels: z9.array(z9.string()).optional(),
7603
+ llmMonthlyTokenLimit: z9.number().int().nonnegative().nullable().optional()
7184
7604
  });
7185
- var updateSaveRequestBodySchema = saveRequestBodySchema.extend({
7186
- collectionId: z9.string().trim().min(1)
7605
+ var createAdminUserBodySchema = z9.object({
7606
+ name: z9.string().trim().min(1),
7607
+ role: userRoleSchema,
7608
+ collectionAccess: z9.array(z9.string()).optional(),
7609
+ environmentAccess: z9.array(z9.string()).optional(),
7610
+ snippetAccess: z9.array(z9.string()).optional(),
7611
+ llmAccess: z9.boolean().optional(),
7612
+ llmModels: z9.array(z9.string()).optional(),
7613
+ llmMonthlyTokenLimit: z9.number().int().nonnegative().nullable().optional()
7187
7614
  });
7188
- var reorderRequestsBodySchema = z9.object({
7189
- folderId: z9.string().nullable(),
7190
- orderedRequestIds: z9.array(z9.string().trim().min(1))
7615
+ var hubApiTokenRecordSchema = z9.object({
7616
+ id: z9.string(),
7617
+ userId: z9.string(),
7618
+ name: z9.string(),
7619
+ tokenPrefix: z9.string(),
7620
+ createdAt: timestampSchema,
7621
+ lastUsedAt: timestampSchema.nullable(),
7622
+ revokedAt: timestampSchema.nullable()
7191
7623
  });
7192
- var moveRequestBodySchema = z9.object({
7193
- folderId: z9.string().nullable(),
7194
- index: z9.number().int().min(0)
7624
+ var createAdminUserResponseSchema = z9.object({
7625
+ user: hubUserRecordSchema,
7626
+ token: hubApiTokenRecordSchema,
7627
+ secret: z9.string()
7195
7628
  });
7196
- var listCollectionsResponseSchema = z9.object({
7197
- collections: z9.array(collectionRecordSchema)
7629
+ var createAdminTokenBodySchema = z9.object({
7630
+ name: z9.string().trim().min(1)
7198
7631
  });
7199
- var listEnvironmentsResponseSchema = z9.object({
7200
- environments: z9.array(environmentRecordSchema)
7632
+ var createdApiTokenResponseSchema = z9.object({
7633
+ token: hubApiTokenRecordSchema,
7634
+ secret: z9.string()
7201
7635
  });
7202
- var listSnippetsResponseSchema = z9.object({
7203
- snippets: z9.array(snippetRecordSchema)
7636
+ var listAdminTokensResponseSchema = z9.object({
7637
+ tokens: z9.array(hubApiTokenRecordSchema)
7204
7638
  });
7205
- var listFoldersResponseSchema = z9.object({
7206
- folders: z9.array(folderRecordSchema)
7639
+ var reloadConfigSectionResultSchema = z9.object({
7640
+ section: z9.enum(["db", "redis", "llm", "plugins", "server"]),
7641
+ status: z9.enum(["reloaded", "unchanged", "failed", "restart-required"]),
7642
+ error: z9.string().optional()
7207
7643
  });
7208
- var listRequestsResponseSchema = z9.object({
7209
- requests: z9.array(savedRequestRecordSchema)
7644
+ var reloadConfigResponseSchema = z9.object({
7645
+ sections: z9.array(reloadConfigSectionResultSchema),
7646
+ fatalError: z9.string().optional()
7210
7647
  });
7211
- var emptyResponseSchema = z9.null();
7212
- function serializeCollection(record) {
7213
- return {
7214
- ...record,
7215
- createdAt: record.createdAt.toISOString(),
7216
- updatedAt: record.updatedAt.toISOString()
7217
- };
7218
- }
7219
- function serializeEnvironment(record) {
7220
- return {
7221
- ...record,
7222
- createdAt: record.createdAt.toISOString(),
7223
- updatedAt: record.updatedAt.toISOString()
7224
- };
7225
- }
7226
- function serializeSnippet(record) {
7227
- return {
7228
- ...record,
7229
- createdAt: record.createdAt.toISOString(),
7230
- updatedAt: record.updatedAt.toISOString()
7231
- };
7232
- }
7233
- function serializeFolder(record) {
7648
+ function serializeHubUser(user) {
7234
7649
  return {
7235
- ...record,
7236
- createdAt: record.createdAt.toISOString(),
7237
- updatedAt: record.updatedAt.toISOString()
7650
+ id: user.id,
7651
+ name: user.name,
7652
+ role: user.role,
7653
+ collectionAccess: user.collectionAccess,
7654
+ environmentAccess: user.environmentAccess,
7655
+ snippetAccess: user.snippetAccess,
7656
+ llmAccess: user.llmAccess,
7657
+ llmModels: user.llmModels,
7658
+ llmMonthlyTokenLimit: user.llmMonthlyTokenLimit,
7659
+ createdAt: user.createdAt.toISOString(),
7660
+ updatedAt: user.updatedAt.toISOString()
7238
7661
  };
7239
7662
  }
7240
- function serializeSavedRequest(record) {
7663
+ function serializeApiToken(token) {
7241
7664
  return {
7242
- ...record,
7243
- createdAt: record.createdAt.toISOString(),
7244
- updatedAt: record.updatedAt.toISOString()
7665
+ id: token.id,
7666
+ userId: token.userId,
7667
+ name: token.name,
7668
+ tokenPrefix: token.tokenPrefix,
7669
+ createdAt: token.createdAt.toISOString(),
7670
+ lastUsedAt: token.lastUsedAt ? token.lastUsedAt.toISOString() : null,
7671
+ revokedAt: token.revokedAt ? token.revokedAt.toISOString() : null
7245
7672
  };
7246
7673
  }
7247
7674
 
@@ -7553,11 +7980,7 @@ async function registerAdminRoutes(app, options) {
7553
7980
  }
7554
7981
  const snippets = await db.listSnippets();
7555
7982
  return reply.send({
7556
- snippets: snippets.map((snippet) => ({
7557
- id: snippet.id,
7558
- name: snippet.name,
7559
- deletionLocked: snippet.deletionLocked
7560
- }))
7983
+ snippets: snippets.map((snippet) => serializeSnippet(snippet))
7561
7984
  });
7562
7985
  } catch (error) {
7563
7986
  if (handleDbError(reply, error)) {
@@ -7567,6 +7990,40 @@ async function registerAdminRoutes(app, options) {
7567
7990
  }
7568
7991
  }
7569
7992
  });
7993
+ routes.route({
7994
+ method: "POST",
7995
+ url: "/admin/snippets",
7996
+ schema: {
7997
+ body: createAdminSnippetBodySchema,
7998
+ response: {
7999
+ 201: adminSnippetRecordSchema,
8000
+ 403: errorResponseSchema
8001
+ }
8002
+ },
8003
+ /**
8004
+ * Creates a snippet through the management API.
8005
+ */
8006
+ handler: async (request, reply) => {
8007
+ try {
8008
+ const user = requireAuthenticatedUser(request);
8009
+ if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
8010
+ return;
8011
+ }
8012
+ const snippet = await db.createSnippet(
8013
+ request.body.name,
8014
+ request.body.code,
8015
+ request.body.scope,
8016
+ user.id
8017
+ );
8018
+ return reply.code(201).send(serializeSnippet(snippet));
8019
+ } catch (error) {
8020
+ if (handleDbError(reply, error)) {
8021
+ return;
8022
+ }
8023
+ throw error;
8024
+ }
8025
+ }
8026
+ });
7570
8027
  routes.route({
7571
8028
  method: "DELETE",
7572
8029
  url: "/admin/collections/:id",
@@ -7672,6 +8129,71 @@ async function registerAdminRoutes(app, options) {
7672
8129
  }
7673
8130
  }
7674
8131
  });
8132
+ routes.route({
8133
+ method: "GET",
8134
+ url: "/admin/run-results",
8135
+ schema: {
8136
+ response: {
8137
+ 200: listAdminRunResultsResponseSchema,
8138
+ 403: errorResponseSchema
8139
+ }
8140
+ },
8141
+ /**
8142
+ * Lists all run results for operator management.
8143
+ */
8144
+ handler: async (request, reply) => {
8145
+ try {
8146
+ const user = requireAuthenticatedUser(request);
8147
+ if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
8148
+ return;
8149
+ }
8150
+ const runResults = await db.listAllRunResults();
8151
+ return reply.send({
8152
+ runResults: runResults.map((record) => serializeRunResult(record))
8153
+ });
8154
+ } catch (error) {
8155
+ if (handleDbError(reply, error)) {
8156
+ return;
8157
+ }
8158
+ throw error;
8159
+ }
8160
+ }
8161
+ });
8162
+ routes.route({
8163
+ method: "DELETE",
8164
+ url: "/admin/run-results/:id",
8165
+ schema: {
8166
+ params: idParamSchema,
8167
+ response: {
8168
+ 204: emptyResponseSchema,
8169
+ 403: errorResponseSchema,
8170
+ 404: errorResponseSchema
8171
+ }
8172
+ },
8173
+ /**
8174
+ * Deletes a run result regardless of creator ownership.
8175
+ */
8176
+ handler: async (request, reply) => {
8177
+ try {
8178
+ const user = requireAuthenticatedUser(request);
8179
+ if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
8180
+ return;
8181
+ }
8182
+ const record = await db.findRunResultById(request.params.id);
8183
+ if (!record) {
8184
+ void reply.code(404).send({ error: "Run result not found" });
8185
+ return;
8186
+ }
8187
+ await db.deleteRunResult(request.params.id, user.id);
8188
+ return reply.code(204).send(null);
8189
+ } catch (error) {
8190
+ if (handleDbError(reply, error)) {
8191
+ return;
8192
+ }
8193
+ throw error;
8194
+ }
8195
+ }
8196
+ });
7675
8197
  routes.route({
7676
8198
  method: "PUT",
7677
8199
  url: "/admin/snippets/:id",
@@ -7679,13 +8201,13 @@ async function registerAdminRoutes(app, options) {
7679
8201
  params: idParamSchema,
7680
8202
  body: updateAdminSnippetBodySchema,
7681
8203
  response: {
7682
- 200: adminEntityConfigSchema,
8204
+ 200: adminSnippetRecordSchema,
7683
8205
  403: errorResponseSchema,
7684
8206
  404: errorResponseSchema
7685
8207
  }
7686
8208
  },
7687
8209
  /**
7688
- * Updates admin configuration for a snippet.
8210
+ * Updates snippet content and/or admin configuration.
7689
8211
  */
7690
8212
  handler: async (request, reply) => {
7691
8213
  try {
@@ -7693,16 +8215,20 @@ async function registerAdminRoutes(app, options) {
7693
8215
  if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
7694
8216
  return;
7695
8217
  }
7696
- const snippet = await db.setSnippetDeletionLocked(
7697
- request.params.id,
7698
- request.body.deletionLocked,
7699
- user.id
7700
- );
7701
- return reply.send({
7702
- id: snippet.id,
7703
- name: snippet.name,
7704
- deletionLocked: snippet.deletionLocked
7705
- });
8218
+ const existing = await db.findSnippetById(request.params.id);
8219
+ if (!existing) {
8220
+ void reply.code(404).send({ error: "Snippet not found" });
8221
+ return;
8222
+ }
8223
+ let snippet = existing;
8224
+ const { name, code, scope, deletionLocked } = request.body;
8225
+ if (name !== void 0 && code !== void 0 && scope !== void 0) {
8226
+ snippet = await db.updateSnippet(request.params.id, name, code, scope, user.id);
8227
+ }
8228
+ if (deletionLocked !== void 0) {
8229
+ snippet = await db.setSnippetDeletionLocked(request.params.id, deletionLocked, user.id);
8230
+ }
8231
+ return reply.send(serializeSnippet(snippet));
7706
8232
  } catch (error) {
7707
8233
  if (handleDbError(reply, error)) {
7708
8234
  return;
@@ -9050,6 +9576,140 @@ async function registerRequestRoutes(app, db) {
9050
9576
  });
9051
9577
  }
9052
9578
 
9579
+ // src/server/routes/runResults.ts
9580
+ async function registerRunResultRoutes(app, db) {
9581
+ const routes = app.withTypeProvider();
9582
+ routes.route({
9583
+ method: "GET",
9584
+ url: "/run-results",
9585
+ schema: {
9586
+ response: {
9587
+ 200: listRunResultsResponseSchema
9588
+ }
9589
+ },
9590
+ /**
9591
+ * Lists run results saved by the authenticated user token.
9592
+ */
9593
+ handler: async (request, reply) => {
9594
+ try {
9595
+ const user = requireAuthenticatedUser(request);
9596
+ if (denyUnlessAllowed(reply, canListRunResults(user))) {
9597
+ return;
9598
+ }
9599
+ const runResults = await db.listRunResultsForUser(user.id);
9600
+ return reply.send({
9601
+ runResults: runResults.map((record) => serializeRunResult(record))
9602
+ });
9603
+ } catch (error) {
9604
+ if (handleDbError(reply, error)) {
9605
+ return;
9606
+ }
9607
+ throw error;
9608
+ }
9609
+ }
9610
+ });
9611
+ routes.route({
9612
+ method: "POST",
9613
+ url: "/run-results",
9614
+ schema: {
9615
+ body: createRunResultBodySchema,
9616
+ response: {
9617
+ 200: runResultDetailSchema,
9618
+ 400: errorResponseSchema
9619
+ }
9620
+ },
9621
+ /**
9622
+ * Saves a standalone run result snapshot.
9623
+ */
9624
+ handler: async (request, reply) => {
9625
+ try {
9626
+ const user = requireAuthenticatedUser(request);
9627
+ if (denyUnlessAllowed(reply, canUseDataApi(user))) {
9628
+ return;
9629
+ }
9630
+ const record = await db.createRunResult(request.body, user.id);
9631
+ return reply.send(serializeRunResultDetail(record));
9632
+ } catch (error) {
9633
+ if (handleDbError(reply, error)) {
9634
+ return;
9635
+ }
9636
+ throw error;
9637
+ }
9638
+ }
9639
+ });
9640
+ routes.route({
9641
+ method: "GET",
9642
+ url: "/run-results/:id",
9643
+ schema: {
9644
+ params: idParamSchema,
9645
+ response: {
9646
+ 200: runResultDetailSchema,
9647
+ 404: errorResponseSchema
9648
+ }
9649
+ },
9650
+ /**
9651
+ * Loads a run result snapshot by id for shared deep links.
9652
+ */
9653
+ handler: async (request, reply) => {
9654
+ try {
9655
+ const user = requireAuthenticatedUser(request);
9656
+ if (denyUnlessAllowed(reply, canUseDataApi(user))) {
9657
+ return;
9658
+ }
9659
+ const record = await db.findRunResultById(request.params.id);
9660
+ if (!record) {
9661
+ void reply.code(404).send({ error: "Run result not found" });
9662
+ return;
9663
+ }
9664
+ return reply.send(serializeRunResultDetail(record));
9665
+ } catch (error) {
9666
+ if (handleDbError(reply, error)) {
9667
+ return;
9668
+ }
9669
+ throw error;
9670
+ }
9671
+ }
9672
+ });
9673
+ routes.route({
9674
+ method: "DELETE",
9675
+ url: "/run-results/:id",
9676
+ schema: {
9677
+ params: idParamSchema,
9678
+ response: {
9679
+ 204: emptyResponseSchema,
9680
+ 403: errorResponseSchema,
9681
+ 404: errorResponseSchema
9682
+ }
9683
+ },
9684
+ /**
9685
+ * Deletes a run result saved by the authenticated user when permitted.
9686
+ */
9687
+ handler: async (request, reply) => {
9688
+ try {
9689
+ const user = requireAuthenticatedUser(request);
9690
+ if (denyUnlessAllowed(reply, canUseDataApi(user))) {
9691
+ return;
9692
+ }
9693
+ const record = await db.findRunResultById(request.params.id);
9694
+ if (!record) {
9695
+ void reply.code(404).send({ error: "Run result not found" });
9696
+ return;
9697
+ }
9698
+ if (denyUnlessAllowed(reply, canDeleteRunResult(user, record))) {
9699
+ return;
9700
+ }
9701
+ await db.deleteRunResult(request.params.id, user.id);
9702
+ return reply.code(204).send(null);
9703
+ } catch (error) {
9704
+ if (handleDbError(reply, error)) {
9705
+ return;
9706
+ }
9707
+ throw error;
9708
+ }
9709
+ }
9710
+ });
9711
+ }
9712
+
9053
9713
  // src/server/llm/client.ts
9054
9714
  var OPENAI_BASE_URL = "https://api.openai.com/v1";
9055
9715
  var CLAUDE_BASE_URL = "https://api.anthropic.com/v1";
@@ -9692,6 +10352,7 @@ async function registerProtectedRoutes(app, options) {
9692
10352
  await registerSnippetRoutes(app, options.db);
9693
10353
  await registerFolderRoutes(app, options.db);
9694
10354
  await registerRequestRoutes(app, options.db);
10355
+ await registerRunResultRoutes(app, options.db);
9695
10356
  await registerLlmRoutes(app, { db: options.db, getLlm: options.getLlm });
9696
10357
  await registerPluginsRoutes(app, { getPlugins: options.getPlugins });
9697
10358
  }