@whittlelabs/sifter 0.3.2 → 0.4.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 (3) hide show
  1. package/bin.js +58 -28
  2. package/bin.js.map +2 -2
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -3024,8 +3024,11 @@ var require_apply = __commonJS({
3024
3024
  "../../packages/shuttle/dist/branding/apply.js"(exports2) {
3025
3025
  "use strict";
3026
3026
  Object.defineProperty(exports2, "__esModule", { value: true });
3027
+ exports2.DEFAULT_PROFILE = void 0;
3027
3028
  exports2.expandHome = expandHome;
3029
+ exports2.resolveProfile = resolveProfile;
3028
3030
  exports2.resolvePaths = resolvePaths;
3031
+ exports2.runConfigSearchPaths = runConfigSearchPaths;
3029
3032
  exports2.renderTemplate = renderTemplate;
3030
3033
  var os_1 = require("os");
3031
3034
  var path_1 = require("path");
@@ -3036,16 +3039,33 @@ var require_apply = __commonJS({
3036
3039
  return (0, path_1.join)((0, os_1.homedir)(), p.slice(2));
3037
3040
  return p;
3038
3041
  }
3039
- function resolvePaths(brand) {
3040
- const configDir = expandHome(brand.paths.configDir);
3042
+ exports2.DEFAULT_PROFILE = "default";
3043
+ function resolveProfile(brand, explicit) {
3044
+ const envKey = `${brand.product.id.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_PROFILE`;
3045
+ const candidate = explicit ?? process.env[envKey] ?? exports2.DEFAULT_PROFILE;
3046
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(candidate)) {
3047
+ throw new Error(`Invalid profile "${candidate}": use lowercase letters, digits, and hyphens (e.g. "stage", "local").`);
3048
+ }
3049
+ return candidate;
3050
+ }
3051
+ function resolvePaths(brand, profile = exports2.DEFAULT_PROFILE) {
3052
+ const base = expandHome(brand.paths.configDir);
3053
+ const configDir = profile === exports2.DEFAULT_PROFILE ? base : (0, path_1.join)(base, profile);
3041
3054
  const auditFile = brand.audit?.path ? expandHome(brand.audit.path) : (0, path_1.join)(configDir, "audit.jsonl");
3042
3055
  return {
3056
+ profile,
3043
3057
  configDir,
3044
3058
  configFile: (0, path_1.join)(configDir, "config.json"),
3045
3059
  auditFile,
3046
3060
  spendStateFile: (0, path_1.join)(configDir, "spend-state.json")
3047
3061
  };
3048
3062
  }
3063
+ function runConfigSearchPaths(brand, profile) {
3064
+ if (profile === exports2.DEFAULT_PROFILE) {
3065
+ return brand.paths.configSearchPaths ? [...brand.paths.configSearchPaths] : [];
3066
+ }
3067
+ return [(0, path_1.join)(resolvePaths(brand, profile).configDir, `${brand.product.id}.yaml`)];
3068
+ }
3049
3069
  function renderTemplate(template, vars) {
3050
3070
  return template.replace(/\{(\w+)\}/g, (_match, key) => Object.prototype.hasOwnProperty.call(vars, key) ? vars[key] : `{${key}}`);
3051
3071
  }
@@ -3514,8 +3534,9 @@ var require_init = __commonJS({
3514
3534
  var store_1 = require_store();
3515
3535
  var spend_store_1 = require_spend_store();
3516
3536
  function buildInitCommand(brand) {
3517
- return new commander_1.Command("init").description(`Pair this ${brand.product.title} with Keep and seed local state`).option("--pairing-code <code>", "Pairing code from the product UI").option("--keep-url <url>", "Override the Keep API URL from BrandConfig").option("--jobs-url <url>", "Override the Jobs API URL from BrandConfig").option("--executor <type>", `Executor to write into ${brand.product.id}.yaml (e.g. claude-code). Must be in the brand's executor allowlist.`).option("--poll-interval <ms>", "Run-loop poll interval in milliseconds").option("--daily-cap <tokens>", "Daily token cap; overrides the brand default").option("--monthly-cap <tokens>", "Monthly token cap; overrides the brand default").option("--cwd <path>", "Working directory for executors that run a local subprocess (e.g. claude-code)").action(async (options) => {
3537
+ return new commander_1.Command("init").description(`Pair this ${brand.product.title} with Keep and seed local state`).option("--pairing-code <code>", "Pairing code from the product UI").option("--keep-url <url>", "Override the Keep API URL from BrandConfig").option("--jobs-url <url>", "Override the Jobs API URL from BrandConfig").option("--executor <type>", `Executor to write into ${brand.product.id}.yaml (e.g. claude-code). Must be in the brand's executor allowlist.`).option("--poll-interval <ms>", "Run-loop poll interval in milliseconds").option("--daily-cap <tokens>", "Daily token cap; overrides the brand default").option("--monthly-cap <tokens>", "Monthly token cap; overrides the brand default").option("--cwd <path>", "Working directory for executors that run a local subprocess (e.g. claude-code)").option("-p, --profile <name>", 'Config profile to pair into (isolates this pairing under its own dir; default "default")').action(async (options) => {
3518
3538
  try {
3539
+ const profile = (0, apply_1.resolveProfile)(brand, options.profile);
3519
3540
  const setupOptions = parseSetupOptions(brand, options);
3520
3541
  const effectiveBrand = options.keepUrl || options.jobsUrl ? {
3521
3542
  ...brand,
@@ -3525,7 +3546,7 @@ var require_init = __commonJS({
3525
3546
  ...options.jobsUrl ? { jobsApiUrl: options.jobsUrl } : {}
3526
3547
  }
3527
3548
  } : brand;
3528
- const paths = (0, apply_1.resolvePaths)(effectiveBrand);
3549
+ const paths = (0, apply_1.resolvePaths)(effectiveBrand, profile);
3529
3550
  await fsp.mkdir(paths.configDir, { recursive: true });
3530
3551
  for (const step of effectiveBrand.setupSteps ?? []) {
3531
3552
  if (step.phase === "pre-pair") {
@@ -3561,12 +3582,14 @@ var require_init = __commonJS({
3561
3582
  });
3562
3583
  }
3563
3584
  }
3585
+ const profileFlag = profile === apply_1.DEFAULT_PROFILE ? "" : ` --profile ${profile}`;
3564
3586
  console.log("");
3587
+ console.log(`Profile: ${profile}`);
3565
3588
  console.log(`Config: ${paths.configFile}`);
3566
3589
  console.log(`Audit: ${paths.auditFile}`);
3567
3590
  console.log(`Spend: ${paths.spendStateFile}`);
3568
3591
  console.log("");
3569
- console.log(`Run: ${effectiveBrand.product.cliBinary} run`);
3592
+ console.log(`Run: ${effectiveBrand.product.cliBinary} run${profileFlag}`);
3570
3593
  process.exit(0);
3571
3594
  } catch (err) {
3572
3595
  const message = err instanceof Error ? err.message : String(err);
@@ -17808,11 +17831,13 @@ var require_shuttle = __commonJS({
17808
17831
  var Shuttle = class {
17809
17832
  brand;
17810
17833
  config;
17834
+ profile;
17811
17835
  subscription = null;
17812
17836
  spendTracker = null;
17813
17837
  constructor(options) {
17814
17838
  this.brand = options.brand;
17815
17839
  this.config = options.config;
17840
+ this.profile = options.profile ?? apply_1.DEFAULT_PROFILE;
17816
17841
  }
17817
17842
  /** Exposed so the CLI's `caps set` can reload after a write. */
17818
17843
  get spendTrackerHandle() {
@@ -17825,7 +17850,7 @@ var require_shuttle = __commonJS({
17825
17850
  }));
17826
17851
  const logger = (0, logger_1.getLogger)();
17827
17852
  logger.info(`${this.brand.product.title} starting...`);
17828
- const paths = (0, apply_1.resolvePaths)(this.brand);
17853
+ const paths = (0, apply_1.resolvePaths)(this.brand, this.profile);
17829
17854
  const enforceMinVersion = (response) => {
17830
17855
  (0, version_check_1.checkResponseMinVersion)({
17831
17856
  response,
@@ -17876,6 +17901,7 @@ var require_shuttle = __commonJS({
17876
17901
  });
17877
17902
  await this.subscription.start();
17878
17903
  logger.info(`${this.brand.product.title} running`, {
17904
+ profile: this.profile,
17879
17905
  pools: pools.length,
17880
17906
  executors: executors.map((e) => e.capability.id),
17881
17907
  maxConcurrency: this.config.concurrency.maxJobs,
@@ -17970,16 +17996,18 @@ var require_run = __commonJS({
17970
17996
  var commander_1 = require_commander();
17971
17997
  var loader_1 = require_loader();
17972
17998
  var shuttle_1 = require_shuttle();
17999
+ var apply_1 = require_apply();
17973
18000
  var shutdown_1 = require_shutdown();
17974
18001
  function buildRunCommand(brand) {
17975
- return new commander_1.Command("run").description(`Start the ${brand.product.title} subscribe loop`).option("--config <path>", "Path to shuttle.yaml config file").action(async (options) => {
18002
+ return new commander_1.Command("run").description(`Start the ${brand.product.title} subscribe loop`).option("--config <path>", "Path to shuttle.yaml config file").option("-p, --profile <name>", 'Config profile to run (isolates pairing/config; default "default")').action(async (options) => {
17976
18003
  try {
18004
+ const profile = (0, apply_1.resolveProfile)(brand, options.profile);
17977
18005
  const config = (0, loader_1.loadConfig)({
17978
18006
  explicitPath: options.config,
17979
- searchPaths: brand.paths.configSearchPaths,
18007
+ searchPaths: (0, apply_1.runConfigSearchPaths)(brand, profile),
17980
18008
  productId: brand.product.id
17981
18009
  });
17982
- const shuttle = new shuttle_1.Shuttle({ brand, config });
18010
+ const shuttle = new shuttle_1.Shuttle({ brand, config, profile });
17983
18011
  await shuttle.start();
17984
18012
  (0, shutdown_1.setupShutdownHandler)(() => shuttle.stop());
17985
18013
  } catch (err) {
@@ -18000,12 +18028,14 @@ var require_validate = __commonJS({
18000
18028
  exports2.buildValidateCommand = buildValidateCommand;
18001
18029
  var commander_1 = require_commander();
18002
18030
  var loader_1 = require_loader();
18031
+ var apply_1 = require_apply();
18003
18032
  function buildValidateCommand(brand) {
18004
- return new commander_1.Command("validate").description("Validate the local shuttle.yaml without running").option("--config <path>", "Path to shuttle.yaml config file").action((options) => {
18033
+ return new commander_1.Command("validate").description("Validate the local shuttle.yaml without running").option("--config <path>", "Path to shuttle.yaml config file").option("-p, --profile <name>", 'Config profile to validate (default "default")').action((options) => {
18005
18034
  try {
18035
+ const profile = (0, apply_1.resolveProfile)(brand, options.profile);
18006
18036
  (0, loader_1.loadConfig)({
18007
18037
  explicitPath: options.config,
18008
- searchPaths: brand.paths.configSearchPaths,
18038
+ searchPaths: (0, apply_1.runConfigSearchPaths)(brand, profile),
18009
18039
  productId: brand.product.id
18010
18040
  });
18011
18041
  console.log("Configuration is valid");
@@ -18031,24 +18061,24 @@ var require_audit = __commonJS({
18031
18061
  var audit_log_store_1 = require_audit_log_store();
18032
18062
  function buildAuditCommand(brand) {
18033
18063
  const audit = new commander_1.Command("audit").description(`Inspect the local ${brand.product.title} audit log`);
18034
- audit.command("dump").description("Print all audit records").option("--since <iso8601>", "Only records on or after this timestamp").option("--until <iso8601>", "Only records on or before this timestamp").option("--job <jobId>", "Filter to a single job id").option("--format <format>", "jsonl | table", "jsonl").action((opts) => {
18035
- const records = filter(loadAll(brand), opts);
18064
+ audit.command("dump").description("Print all audit records").option("--since <iso8601>", "Only records on or after this timestamp").option("--until <iso8601>", "Only records on or before this timestamp").option("--job <jobId>", "Filter to a single job id").option("--format <format>", "jsonl | table", "jsonl").option("-p, --profile <name>", 'Config profile whose audit log to read (default "default")').action((opts) => {
18065
+ const records = filter(loadAll(brand, opts.profile), opts);
18036
18066
  render(records, opts.format ?? "jsonl");
18037
18067
  });
18038
- audit.command("tail").description("Print the most recent records").option("-n <count>", "Number of records to print", "20").option("--format <format>", "jsonl | table", "jsonl").action((opts) => {
18039
- const all = loadAll(brand);
18068
+ audit.command("tail").description("Print the most recent records").option("-n <count>", "Number of records to print", "20").option("--format <format>", "jsonl | table", "jsonl").option("-p, --profile <name>", 'Config profile whose audit log to read (default "default")').action((opts) => {
18069
+ const all = loadAll(brand, opts.profile);
18040
18070
  const count = Number.parseInt(opts.n ?? "20", 10) || 20;
18041
18071
  const records = all.slice(Math.max(0, all.length - count));
18042
18072
  render(records, opts.format ?? "jsonl");
18043
18073
  });
18044
- audit.command("since <iso8601>").description("Print records on or after a timestamp").option("--format <format>", "jsonl | table", "jsonl").action((iso, opts) => {
18045
- const records = filter(loadAll(brand), { since: iso });
18074
+ audit.command("since <iso8601>").description("Print records on or after a timestamp").option("--format <format>", "jsonl | table", "jsonl").option("-p, --profile <name>", 'Config profile whose audit log to read (default "default")').action((iso, opts) => {
18075
+ const records = filter(loadAll(brand, opts.profile), { since: iso });
18046
18076
  render(records, opts.format ?? "jsonl");
18047
18077
  });
18048
18078
  return audit;
18049
18079
  }
18050
- function loadAll(brand) {
18051
- const paths = (0, apply_1.resolvePaths)(brand);
18080
+ function loadAll(brand, profile) {
18081
+ const paths = (0, apply_1.resolvePaths)(brand, (0, apply_1.resolveProfile)(brand, profile));
18052
18082
  return new audit_log_store_1.AuditLogStore(paths.auditFile).readAll();
18053
18083
  }
18054
18084
  function filter(records, opts) {
@@ -18098,8 +18128,8 @@ var require_caps = __commonJS({
18098
18128
  var spend_tracker_1 = require_spend_tracker();
18099
18129
  function buildCapsCommand(brand) {
18100
18130
  const caps = new commander_1.Command("caps").description(`Show or update spend caps for ${brand.product.title}`);
18101
- caps.command("show", { isDefault: true }).description("Show current caps and per-period usage").action(() => {
18102
- const paths = (0, apply_1.resolvePaths)(brand);
18131
+ caps.command("show", { isDefault: true }).description("Show current caps and per-period usage").option("-p, --profile <name>", 'Config profile whose caps/usage to show (default "default")').action((opts) => {
18132
+ const paths = (0, apply_1.resolvePaths)(brand, (0, apply_1.resolveProfile)(brand, opts.profile));
18103
18133
  const tracker = (0, spend_tracker_1.createSpendTrackerObserver)(brand, paths.spendStateFile);
18104
18134
  const current = tracker.currentCaps();
18105
18135
  const usage = tracker.usage();
@@ -18117,8 +18147,8 @@ var require_caps = __commonJS({
18117
18147
  console.log(` daily tokens: ${usage.daily.totalTokens} (in=${usage.daily.inputTokens} out=${usage.daily.outputTokens})`);
18118
18148
  console.log(` monthly tokens: ${usage.monthly.totalTokens} (in=${usage.monthly.inputTokens} out=${usage.monthly.outputTokens})`);
18119
18149
  });
18120
- caps.command("set").description("Update caps (writes immediately; live run reloads in-process)").option("--daily-tokens <n>", "Daily token cap").option("--monthly-tokens <n>", "Monthly token cap").option("--daily-usd <n>", "Daily USD cap").option("--monthly-usd <n>", "Monthly USD cap").action(async (opts) => {
18121
- const paths = (0, apply_1.resolvePaths)(brand);
18150
+ caps.command("set").description("Update caps (writes immediately; live run reloads in-process)").option("--daily-tokens <n>", "Daily token cap").option("--monthly-tokens <n>", "Monthly token cap").option("--daily-usd <n>", "Daily USD cap").option("--monthly-usd <n>", "Monthly USD cap").option("-p, --profile <name>", 'Config profile whose caps to update (default "default")').action(async (opts) => {
18151
+ const paths = (0, apply_1.resolvePaths)(brand, (0, apply_1.resolveProfile)(brand, opts.profile));
18122
18152
  const store = new spend_store_1.SpendStore(paths.spendStateFile);
18123
18153
  const before = store.read({
18124
18154
  dailyTokens: brand.defaultCaps.dailyTokens,
@@ -18214,9 +18244,9 @@ var require_list = __commonJS({
18214
18244
  var store_1 = require_store();
18215
18245
  var pairings_1 = require_pairings();
18216
18246
  function buildListCommand(brand) {
18217
- return new commander_1.Command("list").description(`List paired ${brand.product.title} machines`).action(async () => {
18247
+ return new commander_1.Command("list").description(`List paired ${brand.product.title} machines`).option("-p, --profile <name>", 'Config profile whose pairing credentials to use (default "default")').action(async (options) => {
18218
18248
  try {
18219
- const paths = (0, apply_1.resolvePaths)(brand);
18249
+ const paths = (0, apply_1.resolvePaths)(brand, (0, apply_1.resolveProfile)(brand, options.profile));
18220
18250
  const store = new store_1.PairingConfigStore(paths.configFile);
18221
18251
  const pairing = store.read();
18222
18252
  const tokens = new keep_1.ServiceTokenManager({
@@ -18259,9 +18289,9 @@ var require_revoke = __commonJS({
18259
18289
  var store_1 = require_store();
18260
18290
  var pairings_1 = require_pairings();
18261
18291
  function buildRevokeCommand(brand) {
18262
- return new commander_1.Command("revoke").description(`Revoke a paired ${brand.product.title}`).argument("<nameOrId>", "Machine name or service-identity id to revoke").action(async (nameOrId) => {
18292
+ return new commander_1.Command("revoke").description(`Revoke a paired ${brand.product.title}`).argument("<nameOrId>", "Machine name or service-identity id to revoke").option("-p, --profile <name>", 'Config profile whose pairing credentials to use (default "default")').action(async (nameOrId, options) => {
18263
18293
  try {
18264
- const paths = (0, apply_1.resolvePaths)(brand);
18294
+ const paths = (0, apply_1.resolvePaths)(brand, (0, apply_1.resolveProfile)(brand, options.profile));
18265
18295
  const store = new store_1.PairingConfigStore(paths.configFile);
18266
18296
  const pairing = store.read();
18267
18297
  const tokens = new keep_1.ServiceTokenManager({
@@ -18454,7 +18484,7 @@ var import_shuttle = __toESM(require_dist4());
18454
18484
  // package.json
18455
18485
  var package_default = {
18456
18486
  name: "@whittlelabs/sifter",
18457
- version: "0.3.2",
18487
+ version: "0.4.0",
18458
18488
  description: "Whittle Sifter: paired AI reviewer for Whittle Sift attention pools.",
18459
18489
  bin: {
18460
18490
  "whittle-sifter": "./dist/bin.js"