@hasna/brains 0.0.18 → 0.0.20

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.
@@ -1 +1 @@
1
- {"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/collections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCzC,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2DlE"}
1
+ {"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/collections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCzC,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6DlE"}
@@ -1,3 +1,5 @@
1
1
  import type { Command } from "commander";
2
2
  export declare function registerDataCommands(program: Command): void;
3
+ declare function getMergeOutputDirectory(outputPath: string): string;
4
+ export { getMergeOutputDirectory };
3
5
  //# sourceMappingURL=data.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmM3D"}
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmM3D;AAED,iBAAS,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
@@ -1,3 +1,20 @@
1
1
  import type { Command } from "commander";
2
+ import { fineTunedModels } from "../../db/index.js";
3
+ type ModelRow = typeof fineTunedModels.$inferSelect;
4
+ type Provider = ModelRow["provider"];
5
+ type ModelStatus = ModelRow["status"];
6
+ interface ListModelsOptions {
7
+ json?: boolean;
8
+ provider?: string;
9
+ status?: string;
10
+ limit?: string;
11
+ }
12
+ declare function parseListLimit(rawLimit: string | undefined): number | undefined;
13
+ declare function parseListFilters(opts: ListModelsOptions): {
14
+ provider?: Provider;
15
+ status?: ModelStatus;
16
+ limit?: number;
17
+ };
2
18
  export declare function registerModelsCommands(program: Command): void;
19
+ export { parseListFilters, parseListLimit };
3
20
  //# sourceMappingURL=models.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQzC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwO7D"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,EAAS,eAAe,EAAgB,MAAM,mBAAmB,CAAC;AAKzE,KAAK,QAAQ,GAAG,OAAO,eAAe,CAAC,YAAY,CAAC;AACpD,KAAK,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;AACrC,KAAK,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAUtC,UAAU,iBAAiB;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,iBAAS,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAOxE;AAED,iBAAS,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG;IAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAiBhH;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmQ7D;AAED,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC"}
package/dist/cli/index.js CHANGED
@@ -14976,7 +14976,7 @@ function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelect
14976
14976
  }
14977
14977
 
14978
14978
  // src/cli/index.ts
14979
- import { readFileSync as readFileSync7 } from "fs";
14979
+ import { readFileSync as readFileSync8 } from "fs";
14980
14980
  import { join as join14 } from "path";
14981
14981
 
14982
14982
  // node_modules/drizzle-orm/bun-sqlite/driver.js
@@ -23207,12 +23207,58 @@ class ThinkerLabsProvider {
23207
23207
  }
23208
23208
 
23209
23209
  // src/cli/commands/models.ts
23210
+ var VALID_STATUSES = new Set([
23211
+ "pending",
23212
+ "running",
23213
+ "succeeded",
23214
+ "failed",
23215
+ "cancelled"
23216
+ ]);
23217
+ function parseListLimit(rawLimit) {
23218
+ if (!rawLimit)
23219
+ return;
23220
+ const parsed = Number(rawLimit);
23221
+ if (!Number.isInteger(parsed) || parsed <= 0) {
23222
+ throw new Error(`Invalid --limit value: ${rawLimit}. Use a positive integer.`);
23223
+ }
23224
+ return parsed;
23225
+ }
23226
+ function parseListFilters(opts) {
23227
+ const providerRaw = opts.provider?.trim();
23228
+ const statusRaw = opts.status?.trim();
23229
+ if (providerRaw && providerRaw !== "openai" && providerRaw !== "thinker-labs") {
23230
+ throw new Error(`Invalid --provider value: ${providerRaw}. Use openai or thinker-labs.`);
23231
+ }
23232
+ if (statusRaw && !VALID_STATUSES.has(statusRaw)) {
23233
+ throw new Error(`Invalid --status value: ${statusRaw}. Use one of: ${Array.from(VALID_STATUSES).join(", ")}.`);
23234
+ }
23235
+ const limit2 = parseListLimit(opts.limit);
23236
+ const provider = providerRaw;
23237
+ const status = statusRaw;
23238
+ return { provider, status, limit: limit2 };
23239
+ }
23210
23240
  function registerModelsCommands(program2) {
23211
23241
  const modelsCmd = program2.command("models").description("Manage tracked fine-tuned models");
23212
- modelsCmd.command("list").description("List all tracked fine-tuned models").option("--json", "Output as JSON").action(async (opts) => {
23242
+ const ensureModelExists = async (id) => {
23243
+ const db = getDb();
23244
+ const [model] = await db.select({ id: fineTunedModels.id }).from(fineTunedModels).where(eq(fineTunedModels.id, id));
23245
+ if (!model) {
23246
+ printError(`Model not found: ${id}`);
23247
+ process.exit(1);
23248
+ }
23249
+ return db;
23250
+ };
23251
+ modelsCmd.command("list").description("List all tracked fine-tuned models").option("--provider <provider>", "Filter by provider (openai|thinker-labs)").option("--status <status>", "Filter by status (pending|running|succeeded|failed|cancelled)").option("--limit <n>", "Maximum number of results").option("--json", "Output as JSON").action(async (opts) => {
23213
23252
  try {
23214
23253
  const db = getDb();
23215
- const models = await db.select().from(fineTunedModels);
23254
+ const filters = parseListFilters(opts);
23255
+ const whereClause = filters.provider && filters.status ? and(eq(fineTunedModels.provider, filters.provider), eq(fineTunedModels.status, filters.status)) : filters.provider ? eq(fineTunedModels.provider, filters.provider) : filters.status ? eq(fineTunedModels.status, filters.status) : undefined;
23256
+ const query = db.select().from(fineTunedModels).$dynamic();
23257
+ if (whereClause)
23258
+ query.where(whereClause);
23259
+ if (filters.limit)
23260
+ query.limit(filters.limit);
23261
+ const models = await query;
23216
23262
  if (opts.json) {
23217
23263
  printJson(models);
23218
23264
  return;
@@ -23272,7 +23318,7 @@ function registerModelsCommands(program2) {
23272
23318
  });
23273
23319
  modelsCmd.command("rename <id> <displayName>").description("Set the display name of a model").action(async (id, displayName) => {
23274
23320
  try {
23275
- const db = getDb();
23321
+ const db = await ensureModelExists(id);
23276
23322
  await db.update(fineTunedModels).set({ displayName, updatedAt: Date.now() }).where(eq(fineTunedModels.id, id));
23277
23323
  printSuccess(`Display name set to "${displayName}"`);
23278
23324
  } catch (err) {
@@ -23282,9 +23328,9 @@ function registerModelsCommands(program2) {
23282
23328
  });
23283
23329
  modelsCmd.command("describe <id> <description>").description("Set the description of a model").action(async (id, description) => {
23284
23330
  try {
23285
- const db = getDb();
23331
+ const db = await ensureModelExists(id);
23286
23332
  await db.update(fineTunedModels).set({ description, updatedAt: Date.now() }).where(eq(fineTunedModels.id, id));
23287
- printSuccess(`Description updated.`);
23333
+ printSuccess("Description updated.");
23288
23334
  } catch (err) {
23289
23335
  printError(err instanceof Error ? err.message : String(err));
23290
23336
  process.exit(1);
@@ -23328,7 +23374,7 @@ function registerModelsCommands(program2) {
23328
23374
  });
23329
23375
  modelsCmd.command("collection <id> <collectionName>").description("Set the collection of a model").action(async (id, collectionName) => {
23330
23376
  try {
23331
- const db = getDb();
23377
+ const db = await ensureModelExists(id);
23332
23378
  await db.update(fineTunedModels).set({ collection: collectionName, updatedAt: Date.now() }).where(eq(fineTunedModels.id, id));
23333
23379
  printSuccess(`Collection set to "${collectionName}"`);
23334
23380
  } catch (err) {
@@ -23371,7 +23417,7 @@ function registerModelsCommands(program2) {
23371
23417
  status: result.status,
23372
23418
  startedAt: now
23373
23419
  });
23374
- printSuccess(`Model imported successfully.`);
23420
+ printSuccess("Model imported successfully.");
23375
23421
  console.log();
23376
23422
  console.log(` Local ID: ${modelId}`);
23377
23423
  console.log(` Job ID: ${jobId}`);
@@ -23584,7 +23630,7 @@ function registerFinetuneCommands(program2) {
23584
23630
  // src/cli/commands/data.ts
23585
23631
  import { randomUUID as randomUUID3 } from "crypto";
23586
23632
  import { readFileSync as readFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "fs";
23587
- import { join as join13 } from "path";
23633
+ import { dirname as dirname3, join as join13 } from "path";
23588
23634
  import { homedir as homedir12 } from "os";
23589
23635
  var DEFAULT_DATASETS_DIR = join13(homedir12(), ".hasna", "brains", "datasets");
23590
23636
  function registerDataCommands(program2) {
@@ -23696,7 +23742,7 @@ function registerDataCommands(program2) {
23696
23742
  process.exit(1);
23697
23743
  }
23698
23744
  }
23699
- mkdirSync6(join13(opts.output, "..").replace(/\/\.\.$/, "") || DEFAULT_DATASETS_DIR, { recursive: true });
23745
+ mkdirSync6(getMergeOutputDirectory(opts.output), { recursive: true });
23700
23746
  const allExamples = [];
23701
23747
  for (const f of files) {
23702
23748
  const lines = readFileSync6(f, "utf8").split(`
@@ -23762,6 +23808,11 @@ function registerDataCommands(program2) {
23762
23808
  }
23763
23809
  });
23764
23810
  }
23811
+ function getMergeOutputDirectory(outputPath) {
23812
+ if (!outputPath)
23813
+ return DEFAULT_DATASETS_DIR;
23814
+ return dirname3(outputPath);
23815
+ }
23765
23816
 
23766
23817
  // src/cli/commands/collections.ts
23767
23818
  async function listCollections(json = false) {
@@ -23777,7 +23828,7 @@ async function listCollections(json = false) {
23777
23828
  return;
23778
23829
  }
23779
23830
  if (rows.length === 0) {
23780
- printInfo("No collections found. Set a collection with 'brains models set-collection'.");
23831
+ printInfo("No collections found. Set a collection with 'brains models collection <model-id> <name>'.");
23781
23832
  return;
23782
23833
  }
23783
23834
  printTable(["Collection", "Model Count", "Models"], rows.map((r) => [r.collection ?? "(none)", String(r.count), r.names ?? ""]));
@@ -23794,14 +23845,22 @@ function registerCollectionsCommands(program2) {
23794
23845
  collectionsCmd.command("list").description("List all collections with model counts").option("--json", "Output as JSON").action(async (opts) => {
23795
23846
  await listCollections(opts.json);
23796
23847
  });
23797
- collectionsCmd.command("show <name>").description("List all models in a collection").action(async (name) => {
23848
+ collectionsCmd.command("show <name>").description("List all models in a collection").option("--json", "Output as JSON").action(async (name, opts) => {
23798
23849
  try {
23799
23850
  const db = getDb();
23800
23851
  const models = await db.select().from(fineTunedModels).where(eq(fineTunedModels.collection, name));
23801
23852
  if (models.length === 0) {
23853
+ if (opts.json) {
23854
+ printJson([]);
23855
+ return;
23856
+ }
23802
23857
  printInfo(`No models found in collection '${name}'.`);
23803
23858
  return;
23804
23859
  }
23860
+ if (opts.json) {
23861
+ printJson(models);
23862
+ return;
23863
+ }
23805
23864
  printTable(["ID", "Name", "Provider", "Status", "Base Model"], models.map((m) => [m.id, m.name, m.provider, printStatus(m.status), m.baseModel]));
23806
23865
  } catch (err) {
23807
23866
  printError(err instanceof Error ? err.message : String(err));
@@ -23991,9 +24050,36 @@ function registerCloudCommands2(program2) {
23991
24050
  });
23992
24051
  }
23993
24052
 
24053
+ // src/lib/package-metadata.ts
24054
+ import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
24055
+ import { dirname as dirname4, resolve as resolve2 } from "path";
24056
+ import { fileURLToPath } from "url";
24057
+ var DEFAULT_VERSION = "0.0.0";
24058
+ var cachedVersion;
24059
+ function getPackageJsonPath() {
24060
+ return resolve2(dirname4(fileURLToPath(import.meta.url)), "../../package.json");
24061
+ }
24062
+ function getPackageVersion() {
24063
+ if (cachedVersion) {
24064
+ return cachedVersion;
24065
+ }
24066
+ const packageJsonPath = getPackageJsonPath();
24067
+ if (!existsSync12(packageJsonPath)) {
24068
+ cachedVersion = DEFAULT_VERSION;
24069
+ return cachedVersion;
24070
+ }
24071
+ try {
24072
+ const packageJson = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
24073
+ cachedVersion = typeof packageJson.version === "string" ? packageJson.version : DEFAULT_VERSION;
24074
+ } catch {
24075
+ cachedVersion = DEFAULT_VERSION;
24076
+ }
24077
+ return cachedVersion;
24078
+ }
24079
+
23994
24080
  // src/cli/index.ts
23995
24081
  var program2 = new Command;
23996
- program2.name("brains").description("Fine-tuned model tracker and trainer").version("0.0.1");
24082
+ program2.name("brains").description("Fine-tuned model tracker and trainer").version(getPackageVersion());
23997
24083
  registerModelsCommands(program2);
23998
24084
  registerFinetuneCommands(program2);
23999
24085
  registerDataCommands(program2);
@@ -24072,7 +24158,7 @@ var feedbackCmd = program2.command("feedback").description("Feedback commands");
24072
24158
  feedbackCmd.command("send <message>").description("Send feedback about brains").option("--email <email>", "Contact email").action(async (message, opts) => {
24073
24159
  const { sendFeedback: sendFeedback2 } = await Promise.resolve().then(() => (init_dist(), exports_dist));
24074
24160
  const rawDb = getRawDb();
24075
- const pkg = JSON.parse(readFileSync7(join14(import.meta.dir, "../../package.json"), "utf8"));
24161
+ const pkg = JSON.parse(readFileSync8(join14(import.meta.dir, "../../package.json"), "utf8"));
24076
24162
  const result = await sendFeedback2({ service: "brains", message, email: opts.email, version: pkg.version }, rawDb);
24077
24163
  rawDb.close();
24078
24164
  if (result.sent) {
@@ -1,3 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  export declare function createServerFetchHandler(): (req: Request) => Response | Promise<Response>;
3
+ export declare function resolveServerPort(argv: string[], envPort: string | undefined): {
4
+ port?: number;
5
+ showHelp: boolean;
6
+ error?: string;
7
+ };
3
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AAwBA,wBAAgB,wBAAwB,KAChB,KAAK,OAAO,KAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8ClE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AAwBA,wBAAgB,wBAAwB,KAChB,KAAK,OAAO,KAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8ClE;AAyHD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAyBnI"}
@@ -14187,11 +14187,50 @@ async function handleGather(req) {
14187
14187
  }
14188
14188
  return json({ datasets: saved, total_examples: saved.reduce((s, d) => s + d.count, 0) });
14189
14189
  }
14190
- var port = Number(process.env["PORT"] ?? 7020);
14190
+ var USAGE = `Usage: brains-serve [options]
14191
+
14192
+ Options:
14193
+ -p, --port <number> Port to bind (default: PORT env or 7020)
14194
+ -h, --help Show this help message`;
14195
+ function resolveServerPort(argv, envPort) {
14196
+ let port = Number(envPort ?? 7020);
14197
+ for (let i = 0;i < argv.length; i += 1) {
14198
+ const arg = argv[i];
14199
+ if (!arg)
14200
+ continue;
14201
+ if (arg === "-h" || arg === "--help") {
14202
+ return { showHelp: true };
14203
+ }
14204
+ if (arg === "-p" || arg === "--port") {
14205
+ const value = argv[i + 1];
14206
+ if (!value) {
14207
+ return { showHelp: false, error: "Missing value for --port" };
14208
+ }
14209
+ port = Number(value);
14210
+ i += 1;
14211
+ continue;
14212
+ }
14213
+ }
14214
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
14215
+ return { showHelp: false, error: `Invalid port: ${String(port)}` };
14216
+ }
14217
+ return { showHelp: false, port };
14218
+ }
14191
14219
  if (import.meta.main) {
14192
- console.log(`brains server starting on port ${port}`);
14193
- Bun.serve({ port, fetch: createServerFetchHandler() });
14220
+ const resolved = resolveServerPort(process.argv.slice(2), process.env["PORT"]);
14221
+ if (resolved.showHelp) {
14222
+ console.log(USAGE);
14223
+ process.exit(0);
14224
+ }
14225
+ if (resolved.error || !resolved.port) {
14226
+ console.error(resolved.error ?? "Failed to resolve server port");
14227
+ console.error(USAGE);
14228
+ process.exit(1);
14229
+ }
14230
+ console.log(`brains server starting on port ${resolved.port}`);
14231
+ Bun.serve({ port: resolved.port, fetch: createServerFetchHandler() });
14194
14232
  }
14195
14233
  export {
14234
+ resolveServerPort,
14196
14235
  createServerFetchHandler
14197
14236
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/brains",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "description": "Fine-tuned model tracker and trainer — wraps OpenAI + Thinker Labs, gathers training data from todos/mementos/conversations/sessions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",