@hasna/brains 0.0.19 → 0.0.21

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
@@ -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));
@@ -23993,12 +24052,12 @@ function registerCloudCommands2(program2) {
23993
24052
 
23994
24053
  // src/lib/package-metadata.ts
23995
24054
  import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
23996
- import { dirname as dirname3, resolve as resolve2 } from "path";
24055
+ import { dirname as dirname4, resolve as resolve2 } from "path";
23997
24056
  import { fileURLToPath } from "url";
23998
24057
  var DEFAULT_VERSION = "0.0.0";
23999
24058
  var cachedVersion;
24000
24059
  function getPackageJsonPath() {
24001
- return resolve2(dirname3(fileURLToPath(import.meta.url)), "../../package.json");
24060
+ return resolve2(dirname4(fileURLToPath(import.meta.url)), "../../package.json");
24002
24061
  }
24003
24062
  function getPackageVersion() {
24004
24063
  if (cachedVersion) {
@@ -24018,6 +24077,17 @@ function getPackageVersion() {
24018
24077
  return cachedVersion;
24019
24078
  }
24020
24079
 
24080
+ // src/cli/remove.ts
24081
+ function parseRemoveType(rawType) {
24082
+ if (!rawType)
24083
+ return;
24084
+ const normalized = rawType.trim().toLowerCase();
24085
+ if (normalized === "model" || normalized === "job") {
24086
+ return normalized;
24087
+ }
24088
+ throw new Error(`Invalid --type value: ${rawType}. Use model or job.`);
24089
+ }
24090
+
24021
24091
  // src/cli/index.ts
24022
24092
  var program2 = new Command;
24023
24093
  program2.name("brains").description("Fine-tuned model tracker and trainer").version(getPackageVersion());
@@ -24025,34 +24095,63 @@ registerModelsCommands(program2);
24025
24095
  registerFinetuneCommands(program2);
24026
24096
  registerDataCommands(program2);
24027
24097
  registerCollectionsCommands(program2);
24028
- program2.command("remove <id>").alias("rm").alias("uninstall").description("Remove a fine-tuned model or training job by ID").option("--type <type>", "Type: model | job (default: auto-detect)").action(async (id, opts) => {
24098
+ program2.command("remove <id>").alias("rm").alias("uninstall").description("Remove a fine-tuned model or training job by ID").option("--type <type>", "Type: model | job (default: auto-detect)").option("--json", "Output as JSON").action(async (id, opts) => {
24029
24099
  const db = getDb();
24030
24100
  try {
24031
- const type = opts.type?.toLowerCase();
24032
- if (type === "job" || !type && !type) {
24101
+ const type = parseRemoveType(opts.type);
24102
+ if (!type || type === "job") {
24033
24103
  const job = db.select().from(trainingJobs).where(eq(trainingJobs.id, id)).get();
24034
- if (job || type === "job") {
24035
- if (!job) {
24036
- printError(`Job not found: ${id}`);
24037
- process.exit(1);
24038
- }
24104
+ if (job) {
24039
24105
  db.delete(trainingJobs).where(eq(trainingJobs.id, id)).run();
24106
+ if (opts.json) {
24107
+ printJson({ deleted: "job", id });
24108
+ return;
24109
+ }
24040
24110
  printSuccess(`Training job ${id} removed`);
24041
24111
  return;
24042
24112
  }
24113
+ if (type === "job") {
24114
+ if (opts.json) {
24115
+ printJson({ error: `Job not found: ${id}` });
24116
+ } else {
24117
+ printError(`Job not found: ${id}`);
24118
+ }
24119
+ process.exit(1);
24120
+ }
24043
24121
  }
24044
- if (type === "model" || !type) {
24122
+ if (!type || type === "model") {
24045
24123
  const model = db.select().from(fineTunedModels).where(eq(fineTunedModels.id, id)).get();
24046
24124
  if (model) {
24047
24125
  db.delete(fineTunedModels).where(eq(fineTunedModels.id, id)).run();
24126
+ if (opts.json) {
24127
+ printJson({ deleted: "model", id });
24128
+ return;
24129
+ }
24048
24130
  printSuccess(`Model ${id} removed`);
24049
24131
  return;
24050
24132
  }
24133
+ if (type === "model") {
24134
+ if (opts.json) {
24135
+ printJson({ error: `Model not found: ${id}` });
24136
+ } else {
24137
+ printError(`Model not found: ${id}`);
24138
+ }
24139
+ process.exit(1);
24140
+ }
24141
+ }
24142
+ const message = `Not found: ${id}. Use --type model|job to target a specific entity.`;
24143
+ if (opts.json) {
24144
+ printJson({ error: message });
24145
+ } else {
24146
+ printError(message);
24051
24147
  }
24052
- printError(`Not found: ${id}. Use --type model|job`);
24053
24148
  process.exit(1);
24054
24149
  } catch (err) {
24055
- printError(err instanceof Error ? err.message : String(err));
24150
+ if (opts.json) {
24151
+ printJson({ error: err instanceof Error ? err.message : String(err) });
24152
+ } else {
24153
+ printError(err instanceof Error ? err.message : String(err));
24154
+ }
24056
24155
  process.exit(1);
24057
24156
  }
24058
24157
  });
@@ -0,0 +1,3 @@
1
+ export type RemoveEntityType = "model" | "job";
2
+ export declare function parseRemoveType(rawType?: string): RemoveEntityType | undefined;
3
+ //# sourceMappingURL=remove.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../src/cli/remove.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,CAAC;AAE/C,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAO9E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/brains",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
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",