agent-shim 8.32.0 → 8.33.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.
package/dist/index.cjs CHANGED
@@ -368,6 +368,7 @@ var categories_default_default = {
368
368
  // src/config/schema.ts
369
369
  var import_node_path2 = __toESM(require("node:path"), 1);
370
370
  var import_zod = require("zod");
371
+ var import_trilean = require("trilean");
371
372
 
372
373
  // src/versionDiscovery.ts
373
374
  var import_node_os = __toESM(require("node:os"), 1);
@@ -498,13 +499,14 @@ var CategoryMapSchema = import_zod.z.strictObject({
498
499
  }).transform((input) => expandAllCategoryKey(input));
499
500
  var DURATION_RE = /^(?:0|[1-9][0-9]*)(?:ms|s|m|h|d|w)$/;
500
501
  var DurationSchema = import_zod.z.string().regex(DURATION_RE);
501
- var WhenSchema = import_zod.z.strictObject({
502
+ var WhenConditionObjectSchema = import_zod.z.strictObject({
502
503
  newerThan: DurationSchema.optional(),
503
504
  olderThan: DurationSchema.optional(),
504
505
  maxSizeBytes: import_zod.z.int().positive().optional(),
505
506
  branch: import_zod.z.string().min(1).optional(),
506
507
  env: import_zod.z.record(import_zod.z.string().min(1), import_zod.z.string()).optional()
507
508
  });
509
+ var WhenSchema = import_zod.z.union([WhenConditionObjectSchema, import_trilean.PredicateNodeSchema]);
508
510
  var EntryValueSchema = import_zod.z.union([
509
511
  import_zod.z.boolean(),
510
512
  import_zod.z.strictObject({ value: import_zod.z.boolean(), when: WhenSchema })
@@ -969,6 +971,7 @@ function classifyEntries(names, options) {
969
971
 
970
972
  // src/resolve/conditions.ts
971
973
  var import_picomatch2 = __toESM(require("picomatch"), 1);
974
+ var import_trilean2 = require("trilean");
972
975
  var MILLISECONDS_PER_UNIT = Object.freeze({
973
976
  ms: 1,
974
977
  s: 1e3,
@@ -999,52 +1002,81 @@ function matchBranch(pattern, branch, detached = false) {
999
1002
  }
1000
1003
  return (0, import_picomatch2.default)(pattern, { dot: true, nocase: false })(branch);
1001
1004
  }
1005
+ var evaluator = (0, import_trilean2.createSyncEvaluator)({});
1006
+ function resolversOf(context) {
1007
+ const values = {
1008
+ now: context.nowMs,
1009
+ ...context.fact?.latestMtimeMs === void 0 ? {} : { "entry.latestMtimeMs": context.fact.latestMtimeMs },
1010
+ ...context.fact?.totalSizeBytes === void 0 ? {} : { "entry.totalSizeBytes": context.fact.totalSizeBytes },
1011
+ ...context.branch === void 0 ? {} : { "repo.branch": context.branch },
1012
+ "repo.detached": context.branchDetached ?? false
1013
+ };
1014
+ return {
1015
+ resolveValue: (key) => {
1016
+ const wrap = (value2) => ({ found: true, value: typeof value2 === "number" ? { kind: "number", value: value2 } : typeof value2 === "string" ? { kind: "text", value: value2 } : { kind: "boolean", value: value2 } });
1017
+ if (typeof key !== "string") {
1018
+ return { found: false };
1019
+ }
1020
+ if (key.startsWith("env.")) {
1021
+ const value2 = context.env[key.slice("env.".length)];
1022
+ return value2 === void 0 ? { found: false } : wrap(value2);
1023
+ }
1024
+ const value = values[key];
1025
+ return value === void 0 ? { found: false } : wrap(value);
1026
+ },
1027
+ // This embedding provides the reference namespace only: a tree that asks for a lookup or a collection finds neither, which the evaluator carries as indeterminate rather than letting the tree pretend an answer.
1028
+ resolveLookup: () => ({ found: false }),
1029
+ resolveCollection: () => []
1030
+ };
1031
+ }
1032
+ function fieldPredicate(field, when, context) {
1033
+ switch (field) {
1034
+ case "newerThan": {
1035
+ const window = parseDuration(when.newerThan ?? "");
1036
+ return { kind: "compare", op: "gte", left: { kind: "reference", key: "entry.latestMtimeMs" }, right: { kind: "numberLiteral", value: context.nowMs - window } };
1037
+ }
1038
+ case "olderThan": {
1039
+ const window = parseDuration(when.olderThan ?? "");
1040
+ return { kind: "compare", op: "lt", left: { kind: "reference", key: "entry.latestMtimeMs" }, right: { kind: "numberLiteral", value: context.nowMs - window } };
1041
+ }
1042
+ case "maxSizeBytes":
1043
+ return { kind: "compare", op: "lte", left: { kind: "reference", key: "entry.totalSizeBytes" }, right: { kind: "numberLiteral", value: when.maxSizeBytes ?? 0 } };
1044
+ case "branch":
1045
+ return { kind: "allOf", operands: [{ kind: "textCompare", op: "portableMatches", left: { kind: "reference", key: "repo.branch" }, right: { kind: "textLiteral", value: when.branch ?? "" } }, { kind: "compare", op: "eq", left: { kind: "reference", key: "repo.detached" }, right: { kind: "booleanLiteral", value: false } }] };
1046
+ case "env": {
1047
+ const entries = Object.entries(when.env ?? {});
1048
+ return { kind: "allOf", operands: entries.map(([name, expected]) => ({ kind: "textCompare", op: "equals", left: { kind: "reference", key: `env.${name}` }, right: { kind: "textLiteral", value: expected } })) };
1049
+ }
1050
+ }
1051
+ return { kind: "allOf", operands: [] };
1052
+ }
1002
1053
  function evaluateWhen(when, context) {
1003
1054
  if (when === void 0) {
1004
- return { passed: true, checked: [], failed: [] };
1055
+ return { status: "definite", passed: true, checked: [], failed: [] };
1056
+ }
1057
+ if ("kind" in when) {
1058
+ const result = evaluator.evaluatePredicate(when, void 0, resolversOf(context));
1059
+ return result.status === "indeterminate" ? { status: "indeterminate", reason: result.reason.message, checked: ["predicate"] } : { status: "definite", passed: result.value, checked: ["predicate"], failed: result.value ? [] : ["predicate"] };
1005
1060
  }
1006
1061
  const checked = [];
1007
1062
  const failed = [];
1008
- if (when.newerThan !== void 0) {
1009
- checked.push("newerThan");
1010
- const window = parseDuration(when.newerThan);
1011
- const latest = context.fact?.latestMtimeMs;
1012
- if (latest === void 0 || context.nowMs - latest > window) {
1013
- failed.push("newerThan");
1014
- }
1015
- }
1016
- if (when.olderThan !== void 0) {
1017
- checked.push("olderThan");
1018
- const window = parseDuration(when.olderThan);
1019
- const latest = context.fact?.latestMtimeMs;
1020
- if (latest === void 0 || context.nowMs - latest <= window) {
1021
- failed.push("olderThan");
1022
- }
1023
- }
1024
- if (when.maxSizeBytes !== void 0) {
1025
- checked.push("maxSizeBytes");
1026
- const total = context.fact?.totalSizeBytes;
1027
- if (total === void 0 || total > when.maxSizeBytes) {
1028
- failed.push("maxSizeBytes");
1063
+ for (const field of ["newerThan", "olderThan", "maxSizeBytes", "branch", "env"]) {
1064
+ if (when[field] === void 0) {
1065
+ continue;
1029
1066
  }
1030
- }
1031
- if (when.branch !== void 0) {
1032
- checked.push("branch");
1033
- if (!matchBranch(when.branch, context.branch, context.branchDetached ?? false)) {
1034
- failed.push("branch");
1067
+ checked.push(field);
1068
+ const result = evaluator.evaluatePredicate(fieldPredicate(field, when, context), void 0, resolversOf(context));
1069
+ if (result.status === "indeterminate") {
1070
+ return { status: "indeterminate", reason: result.reason.message, checked };
1035
1071
  }
1036
- }
1037
- if (when.env !== void 0) {
1038
- checked.push("env");
1039
- const mismatch = Object.entries(when.env).some(([name, expected]) => context.env[name] !== expected);
1040
- if (mismatch) {
1041
- failed.push("env");
1072
+ if (!result.value) {
1073
+ failed.push(field);
1042
1074
  }
1043
1075
  }
1044
- return { passed: failed.length === 0, checked, failed };
1076
+ return { status: "definite", passed: failed.length === 0, checked, failed };
1045
1077
  }
1046
1078
  function isVacuousWhen(when) {
1047
- return when !== void 0 && Object.keys(when).length === 0;
1079
+ return when !== void 0 && !("kind" in when) && Object.keys(when).length === 0;
1048
1080
  }
1049
1081
 
1050
1082
  // src/resolve/match.ts
@@ -1415,10 +1447,10 @@ function selectRule(relPath, flattened, context) {
1415
1447
  ...context.branchDetached === void 0 ? {} : { branchDetached: context.branchDetached },
1416
1448
  env: context.env
1417
1449
  });
1418
- if (evaluation.passed) {
1450
+ if (evaluation.status === "definite" && evaluation.passed) {
1419
1451
  return { rule, eliminated };
1420
1452
  }
1421
- eliminated.push({ rule, failed: evaluation.failed });
1453
+ eliminated.push({ rule, failed: evaluation.status === "definite" ? evaluation.failed : [`${evaluation.checked.join(", ")} could not be decided: ${evaluation.reason}`] });
1422
1454
  }
1423
1455
  return { eliminated };
1424
1456
  }
@@ -1652,7 +1684,10 @@ function planFarm(params) {
1652
1684
  var ENTER_EVERYTHING = () => true;
1653
1685
  function readsSubtreeAggregates(rule) {
1654
1686
  const when = rule.when;
1655
- return when !== void 0 && (when.newerThan !== void 0 || when.olderThan !== void 0 || when.maxSizeBytes !== void 0);
1687
+ if (when === void 0) {
1688
+ return false;
1689
+ }
1690
+ return "kind" in when || when.newerThan !== void 0 || when.olderThan !== void 0 || when.maxSizeBytes !== void 0;
1656
1691
  }
1657
1692
  function hasUnboundedDepth(pattern) {
1658
1693
  return pattern.includes("**") || /[{(]/.test(pattern);
@@ -5355,7 +5390,7 @@ var import_undici = require("undici");
5355
5390
  // package.json
5356
5391
  var package_default = {
5357
5392
  name: "agent-shim",
5358
- version: "8.32.0",
5393
+ version: "8.33.0",
5359
5394
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
5360
5395
  license: "MIT",
5361
5396
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -5411,6 +5446,7 @@ var package_default = {
5411
5446
  commander: "^15.0.0",
5412
5447
  cosmiconfig: "^10.0.1",
5413
5448
  picomatch: "^4.0.7",
5449
+ trilean: "1.7.1",
5414
5450
  undici: "7.29.1",
5415
5451
  zod: "^4.6.1"
5416
5452
  },
package/dist/index.mjs CHANGED
@@ -56,6 +56,7 @@ var categories_default_default = {
56
56
  // src/config/schema.ts
57
57
  import path2 from "node:path";
58
58
  import { z } from "zod";
59
+ import { PredicateNodeSchema } from "trilean";
59
60
 
60
61
  // src/versionDiscovery.ts
61
62
  import os from "node:os";
@@ -186,13 +187,14 @@ var CategoryMapSchema = z.strictObject({
186
187
  }).transform((input) => expandAllCategoryKey(input));
187
188
  var DURATION_RE = /^(?:0|[1-9][0-9]*)(?:ms|s|m|h|d|w)$/;
188
189
  var DurationSchema = z.string().regex(DURATION_RE);
189
- var WhenSchema = z.strictObject({
190
+ var WhenConditionObjectSchema = z.strictObject({
190
191
  newerThan: DurationSchema.optional(),
191
192
  olderThan: DurationSchema.optional(),
192
193
  maxSizeBytes: z.int().positive().optional(),
193
194
  branch: z.string().min(1).optional(),
194
195
  env: z.record(z.string().min(1), z.string()).optional()
195
196
  });
197
+ var WhenSchema = z.union([WhenConditionObjectSchema, PredicateNodeSchema]);
196
198
  var EntryValueSchema = z.union([
197
199
  z.boolean(),
198
200
  z.strictObject({ value: z.boolean(), when: WhenSchema })
@@ -657,6 +659,7 @@ function classifyEntries(names, options) {
657
659
 
658
660
  // src/resolve/conditions.ts
659
661
  import picomatch2 from "picomatch";
662
+ import { createSyncEvaluator } from "trilean";
660
663
  var MILLISECONDS_PER_UNIT = Object.freeze({
661
664
  ms: 1,
662
665
  s: 1e3,
@@ -687,52 +690,81 @@ function matchBranch(pattern, branch, detached = false) {
687
690
  }
688
691
  return picomatch2(pattern, { dot: true, nocase: false })(branch);
689
692
  }
693
+ var evaluator = createSyncEvaluator({});
694
+ function resolversOf(context) {
695
+ const values = {
696
+ now: context.nowMs,
697
+ ...context.fact?.latestMtimeMs === void 0 ? {} : { "entry.latestMtimeMs": context.fact.latestMtimeMs },
698
+ ...context.fact?.totalSizeBytes === void 0 ? {} : { "entry.totalSizeBytes": context.fact.totalSizeBytes },
699
+ ...context.branch === void 0 ? {} : { "repo.branch": context.branch },
700
+ "repo.detached": context.branchDetached ?? false
701
+ };
702
+ return {
703
+ resolveValue: (key) => {
704
+ const wrap = (value2) => ({ found: true, value: typeof value2 === "number" ? { kind: "number", value: value2 } : typeof value2 === "string" ? { kind: "text", value: value2 } : { kind: "boolean", value: value2 } });
705
+ if (typeof key !== "string") {
706
+ return { found: false };
707
+ }
708
+ if (key.startsWith("env.")) {
709
+ const value2 = context.env[key.slice("env.".length)];
710
+ return value2 === void 0 ? { found: false } : wrap(value2);
711
+ }
712
+ const value = values[key];
713
+ return value === void 0 ? { found: false } : wrap(value);
714
+ },
715
+ // This embedding provides the reference namespace only: a tree that asks for a lookup or a collection finds neither, which the evaluator carries as indeterminate rather than letting the tree pretend an answer.
716
+ resolveLookup: () => ({ found: false }),
717
+ resolveCollection: () => []
718
+ };
719
+ }
720
+ function fieldPredicate(field, when, context) {
721
+ switch (field) {
722
+ case "newerThan": {
723
+ const window = parseDuration(when.newerThan ?? "");
724
+ return { kind: "compare", op: "gte", left: { kind: "reference", key: "entry.latestMtimeMs" }, right: { kind: "numberLiteral", value: context.nowMs - window } };
725
+ }
726
+ case "olderThan": {
727
+ const window = parseDuration(when.olderThan ?? "");
728
+ return { kind: "compare", op: "lt", left: { kind: "reference", key: "entry.latestMtimeMs" }, right: { kind: "numberLiteral", value: context.nowMs - window } };
729
+ }
730
+ case "maxSizeBytes":
731
+ return { kind: "compare", op: "lte", left: { kind: "reference", key: "entry.totalSizeBytes" }, right: { kind: "numberLiteral", value: when.maxSizeBytes ?? 0 } };
732
+ case "branch":
733
+ return { kind: "allOf", operands: [{ kind: "textCompare", op: "portableMatches", left: { kind: "reference", key: "repo.branch" }, right: { kind: "textLiteral", value: when.branch ?? "" } }, { kind: "compare", op: "eq", left: { kind: "reference", key: "repo.detached" }, right: { kind: "booleanLiteral", value: false } }] };
734
+ case "env": {
735
+ const entries = Object.entries(when.env ?? {});
736
+ return { kind: "allOf", operands: entries.map(([name, expected]) => ({ kind: "textCompare", op: "equals", left: { kind: "reference", key: `env.${name}` }, right: { kind: "textLiteral", value: expected } })) };
737
+ }
738
+ }
739
+ return { kind: "allOf", operands: [] };
740
+ }
690
741
  function evaluateWhen(when, context) {
691
742
  if (when === void 0) {
692
- return { passed: true, checked: [], failed: [] };
743
+ return { status: "definite", passed: true, checked: [], failed: [] };
744
+ }
745
+ if ("kind" in when) {
746
+ const result = evaluator.evaluatePredicate(when, void 0, resolversOf(context));
747
+ return result.status === "indeterminate" ? { status: "indeterminate", reason: result.reason.message, checked: ["predicate"] } : { status: "definite", passed: result.value, checked: ["predicate"], failed: result.value ? [] : ["predicate"] };
693
748
  }
694
749
  const checked = [];
695
750
  const failed = [];
696
- if (when.newerThan !== void 0) {
697
- checked.push("newerThan");
698
- const window = parseDuration(when.newerThan);
699
- const latest = context.fact?.latestMtimeMs;
700
- if (latest === void 0 || context.nowMs - latest > window) {
701
- failed.push("newerThan");
702
- }
703
- }
704
- if (when.olderThan !== void 0) {
705
- checked.push("olderThan");
706
- const window = parseDuration(when.olderThan);
707
- const latest = context.fact?.latestMtimeMs;
708
- if (latest === void 0 || context.nowMs - latest <= window) {
709
- failed.push("olderThan");
710
- }
711
- }
712
- if (when.maxSizeBytes !== void 0) {
713
- checked.push("maxSizeBytes");
714
- const total = context.fact?.totalSizeBytes;
715
- if (total === void 0 || total > when.maxSizeBytes) {
716
- failed.push("maxSizeBytes");
751
+ for (const field of ["newerThan", "olderThan", "maxSizeBytes", "branch", "env"]) {
752
+ if (when[field] === void 0) {
753
+ continue;
717
754
  }
718
- }
719
- if (when.branch !== void 0) {
720
- checked.push("branch");
721
- if (!matchBranch(when.branch, context.branch, context.branchDetached ?? false)) {
722
- failed.push("branch");
755
+ checked.push(field);
756
+ const result = evaluator.evaluatePredicate(fieldPredicate(field, when, context), void 0, resolversOf(context));
757
+ if (result.status === "indeterminate") {
758
+ return { status: "indeterminate", reason: result.reason.message, checked };
723
759
  }
724
- }
725
- if (when.env !== void 0) {
726
- checked.push("env");
727
- const mismatch = Object.entries(when.env).some(([name, expected]) => context.env[name] !== expected);
728
- if (mismatch) {
729
- failed.push("env");
760
+ if (!result.value) {
761
+ failed.push(field);
730
762
  }
731
763
  }
732
- return { passed: failed.length === 0, checked, failed };
764
+ return { status: "definite", passed: failed.length === 0, checked, failed };
733
765
  }
734
766
  function isVacuousWhen(when) {
735
- return when !== void 0 && Object.keys(when).length === 0;
767
+ return when !== void 0 && !("kind" in when) && Object.keys(when).length === 0;
736
768
  }
737
769
 
738
770
  // src/resolve/match.ts
@@ -1103,10 +1135,10 @@ function selectRule(relPath, flattened, context) {
1103
1135
  ...context.branchDetached === void 0 ? {} : { branchDetached: context.branchDetached },
1104
1136
  env: context.env
1105
1137
  });
1106
- if (evaluation.passed) {
1138
+ if (evaluation.status === "definite" && evaluation.passed) {
1107
1139
  return { rule, eliminated };
1108
1140
  }
1109
- eliminated.push({ rule, failed: evaluation.failed });
1141
+ eliminated.push({ rule, failed: evaluation.status === "definite" ? evaluation.failed : [`${evaluation.checked.join(", ")} could not be decided: ${evaluation.reason}`] });
1110
1142
  }
1111
1143
  return { eliminated };
1112
1144
  }
@@ -1340,7 +1372,10 @@ function planFarm(params) {
1340
1372
  var ENTER_EVERYTHING = () => true;
1341
1373
  function readsSubtreeAggregates(rule) {
1342
1374
  const when = rule.when;
1343
- return when !== void 0 && (when.newerThan !== void 0 || when.olderThan !== void 0 || when.maxSizeBytes !== void 0);
1375
+ if (when === void 0) {
1376
+ return false;
1377
+ }
1378
+ return "kind" in when || when.newerThan !== void 0 || when.olderThan !== void 0 || when.maxSizeBytes !== void 0;
1344
1379
  }
1345
1380
  function hasUnboundedDepth(pattern) {
1346
1381
  return pattern.includes("**") || /[{(]/.test(pattern);
@@ -5043,7 +5078,7 @@ import { Agent as Agent3, fetch as undiciFetch } from "undici";
5043
5078
  // package.json
5044
5079
  var package_default = {
5045
5080
  name: "agent-shim",
5046
- version: "8.32.0",
5081
+ version: "8.33.0",
5047
5082
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
5048
5083
  license: "MIT",
5049
5084
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -5099,6 +5134,7 @@ var package_default = {
5099
5134
  commander: "^15.0.0",
5100
5135
  cosmiconfig: "^10.0.1",
5101
5136
  picomatch: "^4.0.7",
5137
+ trilean: "1.7.1",
5102
5138
  undici: "7.29.1",
5103
5139
  zod: "^4.6.1"
5104
5140
  },