@massa-ai/tools-api 1.41.0 → 1.42.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.
Files changed (2) hide show
  1. package/dist/index.js +189 -4
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -108904,6 +108904,48 @@ var init_postgres_vector_store = __esm(() => {
108904
108904
  lastIndexed: row.last_updated?.toISOString() ?? null
108905
108905
  }));
108906
108906
  }
108907
+ async getPool() {
108908
+ if (this.pool)
108909
+ return this.pool;
108910
+ const pg = await import("pg");
108911
+ const PgPool = pg.default?.Pool ?? pg.Pool;
108912
+ const poolConfig = {
108913
+ connectionString: this.config.connectionString,
108914
+ max: this.config.poolSize,
108915
+ idleTimeoutMillis: 30000,
108916
+ connectionTimeoutMillis: 5000
108917
+ };
108918
+ this.pool = new PgPool(poolConfig);
108919
+ return this.pool;
108920
+ }
108921
+ async listAllProjectsAcrossDimensions() {
108922
+ const pool = await this.getPool();
108923
+ const { rows: tables } = await pool.query(`
108924
+ SELECT tablename FROM pg_tables
108925
+ WHERE tablename = 'vector_documents'
108926
+ OR tablename ~ '^vector_documents_[0-9]+d$'
108927
+ ORDER BY tablename
108928
+ `);
108929
+ if (tables.length === 0)
108930
+ return [];
108931
+ const unionParts = tables.map((t2) => `SELECT project_id, COUNT(*)::int AS doc_count, MAX(updated_at) AS last_updated, SUM(LENGTH(content))::bigint AS total_size FROM ${t2.tablename} WHERE id NOT LIKE '_metadata:%' GROUP BY project_id`).join(" UNION ALL ");
108932
+ const { rows } = await pool.query(`
108933
+ SELECT project_id,
108934
+ SUM(doc_count)::int AS doc_count,
108935
+ MAX(last_updated) AS last_updated,
108936
+ SUM(total_size)::bigint AS total_size
108937
+ FROM (${unionParts}) AS merged
108938
+ GROUP BY project_id
108939
+ ORDER BY last_updated DESC
108940
+ `);
108941
+ return rows.map((row) => ({
108942
+ projectId: row.project_id,
108943
+ projectPath: null,
108944
+ documentCount: parseInt(row.doc_count),
108945
+ totalSize: parseInt(row.total_size ?? "0"),
108946
+ lastIndexed: row.last_updated?.toISOString() ?? null
108947
+ }));
108948
+ }
108907
108949
  async getCollection(name26) {
108908
108950
  const pool = await this.ensureInitialized();
108909
108951
  return new PostgresVectorCollection(pool, name26, this.tableName, this);
@@ -179209,7 +179251,12 @@ var identityBodySchema = t.Object({
179209
179251
  var projectRoutes = new Elysia({ prefix: "/api/v1/project" }).get("/list", async () => {
179210
179252
  try {
179211
179253
  const vectorStore = await getVectorStore();
179212
- const projects = await vectorStore.listProjects();
179254
+ let projects;
179255
+ try {
179256
+ projects = await vectorStore.listProjects();
179257
+ } catch {
179258
+ projects = await vectorStore.listAllProjectsAcrossDimensions?.() ?? [];
179259
+ }
179213
179260
  return {
179214
179261
  success: true,
179215
179262
  data: {
@@ -181605,6 +181652,26 @@ init_dist();
181605
181652
  var CONFIG_DETAIL = {
181606
181653
  tags: ["config"]
181607
181654
  };
181655
+ var SENSITIVE_FIELDS = {
181656
+ database: ["url"],
181657
+ embedding: ["apiKey"],
181658
+ llm: ["apiKey"],
181659
+ security: ["apiKey"]
181660
+ };
181661
+ function getFieldByPath(config3, section, field3) {
181662
+ const sec = config3[section];
181663
+ if (!sec || typeof sec !== "object")
181664
+ return;
181665
+ const parts = field3.split(".");
181666
+ let val = sec;
181667
+ for (const p of parts) {
181668
+ if (val && typeof val === "object")
181669
+ val = val[p];
181670
+ else
181671
+ return;
181672
+ }
181673
+ return val;
181674
+ }
181608
181675
  var configRoutes = new Elysia({ prefix: "/api/v1/config" }).get("/", ({ set: set3 }) => {
181609
181676
  const config3 = loadConfig();
181610
181677
  const masked = maskSensitive(config3);
@@ -181620,6 +181687,32 @@ var configRoutes = new Elysia({ prefix: "/api/v1/config" }).get("/", ({ set: set
181620
181687
  summary: "Get current config with sensitive fields masked",
181621
181688
  description: "Returns the current config.json with security.apiKey, llm.apiKey, embedding.apiKey, and database.url masked to '***'. Includes restartNeededSections \u2014 the subset of [database, embedding, llm, security] present in the config."
181622
181689
  }
181690
+ }).get("/reveal", ({ query, set: set3 }) => {
181691
+ const section = query.section;
181692
+ const field3 = query.field;
181693
+ if (!section || !field3) {
181694
+ set3.status = 400;
181695
+ return { success: false, error: "section and field query params are required" };
181696
+ }
181697
+ const allowed = SENSITIVE_FIELDS[section];
181698
+ if (!allowed || !allowed.includes(field3)) {
181699
+ set3.status = 400;
181700
+ return { success: false, error: `field "${section}.${field3}" is not a sensitive field` };
181701
+ }
181702
+ const config3 = loadConfig();
181703
+ const value = getFieldByPath(config3, section, field3);
181704
+ set3.status = 200;
181705
+ return { success: true, data: { section, field: field3, value: value ?? "" } };
181706
+ }, {
181707
+ query: t.Object({
181708
+ section: t.String(),
181709
+ field: t.String()
181710
+ }),
181711
+ detail: {
181712
+ ...CONFIG_DETAIL,
181713
+ summary: "Reveal a single sensitive config field (unmasked)",
181714
+ description: "Returns the unmasked value for one sensitive field (database.url, embedding.apiKey, llm.apiKey, security.apiKey). Requires API key. Only sensitive fields can be revealed."
181715
+ }
181623
181716
  }).put("/", ({ body, set: set3 }) => {
181624
181717
  const result = savePartialConfig(body);
181625
181718
  if (!result.success) {
@@ -181828,13 +181921,105 @@ function writeOverlayAtomically(overlayPath, data) {
181828
181921
  }
181829
181922
  }
181830
181923
 
181924
+ // src/routes/model-registry-stream.ts
181925
+ init_config();
181926
+ import path36 from "path";
181927
+ import { spawn as spawn3 } from "child_process";
181928
+ var GENERATE_SCRIPT2 = path36.resolve(import.meta.dirname, "../../../../scripts/generate-subagent-artifacts.ts");
181929
+ var encoder3 = new TextEncoder;
181930
+ function sseFrame(data) {
181931
+ return encoder3.encode(`data: ${JSON.stringify(data)}
181932
+
181933
+ `);
181934
+ }
181935
+ var modelRegistryStreamRoutes = new Elysia({ prefix: "/api/v1/model-registry" }).post("/regenerate-stream", () => {
181936
+ let child = null;
181937
+ let closed = false;
181938
+ const stream2 = new ReadableStream({
181939
+ start(controller2) {
181940
+ try {
181941
+ child = spawn3("bun", [GENERATE_SCRIPT2], {
181942
+ env: { ...process.env },
181943
+ stdio: ["pipe", "pipe", "pipe"]
181944
+ });
181945
+ } catch (e) {
181946
+ controller2.enqueue(sseFrame({
181947
+ type: "done",
181948
+ exitCode: null,
181949
+ error: `spawn failed: ${e.message}`
181950
+ }));
181951
+ controller2.close();
181952
+ closed = true;
181953
+ return;
181954
+ }
181955
+ const emitLine = (streamName, chunk) => {
181956
+ if (closed)
181957
+ return;
181958
+ const text3 = chunk.toString();
181959
+ const lines = text3.split(`
181960
+ `);
181961
+ for (const line of lines) {
181962
+ if (line.length === 0)
181963
+ continue;
181964
+ try {
181965
+ controller2.enqueue(sseFrame({ type: "line", stream: streamName, text: line }));
181966
+ } catch {
181967
+ closed = true;
181968
+ return;
181969
+ }
181970
+ }
181971
+ };
181972
+ child.stdout?.on("data", (chunk) => emitLine("stdout", chunk));
181973
+ child.stderr?.on("data", (chunk) => emitLine("stderr", chunk));
181974
+ child.on("error", (e) => {
181975
+ if (closed)
181976
+ return;
181977
+ closed = true;
181978
+ try {
181979
+ controller2.enqueue(sseFrame({ type: "done", exitCode: null, error: `spawn error: ${e.message}` }));
181980
+ controller2.close();
181981
+ } catch {}
181982
+ });
181983
+ child.on("close", (code) => {
181984
+ if (closed)
181985
+ return;
181986
+ closed = true;
181987
+ try {
181988
+ controller2.enqueue(sseFrame({ type: "done", exitCode: code }));
181989
+ controller2.close();
181990
+ } catch {}
181991
+ });
181992
+ },
181993
+ cancel() {
181994
+ closed = true;
181995
+ try {
181996
+ child?.kill();
181997
+ } catch {}
181998
+ }
181999
+ });
182000
+ return new Response(stream2, {
182001
+ headers: {
182002
+ "Content-Type": "text/event-stream",
182003
+ "Cache-Control": "no-cache",
182004
+ Connection: "keep-alive",
182005
+ "X-Accel-Buffering": "no"
182006
+ }
182007
+ });
182008
+ }, {
182009
+ detail: {
182010
+ tags: ["model-registry"],
182011
+ summary: "Regenerate subagent artifacts (streaming SSE)",
182012
+ description: 'Spawns `bun scripts/generate-subagent-artifacts.ts` with child_process.spawn (non-blocking). Pipes stdout/stderr line-by-line as SSE `data: {"type":"line","stream":"stdout|stderr","text":"..."}` events, then a terminal `data: {"type":"done","exitCode":<n>}` event. On spawn failure emits `done` with `exitCode:null` + `error`. The existing blocking POST /regenerate route stays for API compatibility.'
182013
+ }
182014
+ });
182015
+
181831
182016
  // src/middleware/error.ts
181832
182017
  init_dist();
181833
- var errorHandler = new Elysia({ name: "error-handler" }).onError(({ code, error: error51, set: set3, path: path36, request }) => {
182018
+ var errorHandler = new Elysia({ name: "error-handler" }).onError(({ code, error: error51, set: set3, path: path37, request }) => {
181834
182019
  logger.error("[massa-ai-api] Request failed", undefined, {
181835
182020
  ...safeErrorSummary(error51),
181836
182021
  code,
181837
- path: path36,
182022
+ path: path37,
181838
182023
  method: request.method
181839
182024
  });
181840
182025
  if (error51 instanceof SearchServiceError) {
@@ -181950,7 +182135,7 @@ var app = new Elysia({ adapter: node() }).use(cors(buildCorsOptions(config.get("
181950
182135
  },
181951
182136
  security: [{ ApiKeyAuth: [] }]
181952
182137
  }
181953
- })).use(errorHandler).use(authMiddleware).use(searchRoutes).use(memoryRoutes).use(checkpointRoutes).use(projectRoutes).use(contextRoutes).use(analyticsRoutes).use(systemRoutes).use(eventsRoutes).use(workspaceRoutes).use(fileRoutes).use(synapseRoutes).use(hookRoutes).use(bootstrapRoutes).use(handoffRoutes).use(proposalRoutes).use(executorRoutes).use(webRoutes).use(webUiRoutes).use(architectureRoutes).use(dashboardRoutes).use(profileRoutes).use(configRoutes).use(modelRegistryRoutes).get("/health", () => buildHealthResponse(getParserReadiness()));
182138
+ })).use(errorHandler).use(authMiddleware).use(searchRoutes).use(memoryRoutes).use(checkpointRoutes).use(projectRoutes).use(contextRoutes).use(analyticsRoutes).use(systemRoutes).use(eventsRoutes).use(workspaceRoutes).use(fileRoutes).use(synapseRoutes).use(hookRoutes).use(bootstrapRoutes).use(handoffRoutes).use(proposalRoutes).use(executorRoutes).use(webRoutes).use(webUiRoutes).use(architectureRoutes).use(dashboardRoutes).use(profileRoutes).use(configRoutes).use(modelRegistryRoutes).use(modelRegistryStreamRoutes).get("/health", () => buildHealthResponse(getParserReadiness()));
181954
182139
  initAuthOrExit();
181955
182140
  warnIfTrustOverrideEnabled();
181956
182141
  await listenAfterParserValidation({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massa-ai/tools-api",
3
- "version": "1.41.0",
3
+ "version": "1.42.0",
4
4
  "author": "luizgmassa",
5
5
  "description": "massa-ai REST API server - Semantic code search, memory, and context compression",
6
6
  "type": "module",
@@ -21,8 +21,8 @@
21
21
  "test": "bun scripts/run-tests-isolated.ts"
22
22
  },
23
23
  "dependencies": {
24
- "@massa-ai/core": "^1.41.0",
25
- "@massa-ai/shared": "^1.41.0",
24
+ "@massa-ai/core": "^1.42.0",
25
+ "@massa-ai/shared": "^1.42.0",
26
26
  "elysia": "^1.2.25",
27
27
  "@elysiajs/swagger": "^1.2.0",
28
28
  "@elysiajs/cors": "^1.2.0",