heyspex 0.2.4 → 0.2.6

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 (48) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/bin/hs.mjs +281 -30
  3. package/dev-factory/lib/dispatch-guard.mjs +75 -0
  4. package/dev-factory/lib/runner-path.mjs +51 -0
  5. package/dev-factory/spexcrawl/spexcrawl-cli.mjs +734 -0
  6. package/dist/cli/hs-connect.mjs +20 -1
  7. package/dist/cli/hs-defer.mjs +3802 -3744
  8. package/dist/cli/hs-events.mjs +1296 -0
  9. package/dist/cli/hs-export-events.mjs +1252 -0
  10. package/dist/cli/hs-handoff.mjs +2576 -0
  11. package/dist/cli/hs-index-rebuild.mjs +209 -36
  12. package/dist/cli/hs-index-reconcile.mjs +212 -39
  13. package/dist/cli/hs-init.mjs +20 -1
  14. package/dist/cli/hs-install.mjs +20 -1
  15. package/dist/cli/hs-join.mjs +20 -1
  16. package/dist/cli/hs-next-launch.mjs +2698 -0
  17. package/dist/cli/hs-pin.mjs +1336 -0
  18. package/dist/cli/hs-prereq.mjs +1380 -0
  19. package/dist/cli/hs-revalidate.mjs +3257 -0
  20. package/dist/cli/hs-spawn.mjs +12377 -0
  21. package/dist/cli/hs-test.mjs +1046 -0
  22. package/dist/cli/hs-trash.mjs +362 -0
  23. package/dist/cli/hs-trust.mjs +197 -0
  24. package/dist/cli/hs-vault-pull.mjs +214 -41
  25. package/dist/cli/hs-vault-push.mjs +212 -39
  26. package/dist/cli-templates/harness-commands/claude/hs-log.md +1 -1
  27. package/dist/cli-templates/harness-commands/claude/hs-res.md +71 -63
  28. package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -23
  29. package/dist/cli-templates/harness-commands/cursor/hs-log.md +1 -1
  30. package/dist/cli-templates/harness-commands/cursor/hs-res.md +66 -58
  31. package/dist/cli-templates/harness-commands/cursor/hs-review.md +18 -18
  32. package/docs/copilot-plugin-install.md +2 -2
  33. package/package.json +9 -5
  34. package/plugins/heyspex/.claude-plugin/plugin.json +1 -1
  35. package/plugins/heyspex/.cursor-plugin/plugin.json +1 -1
  36. package/scripts/claude-hooks/wip-checkpoint.mjs +17 -2
  37. package/scripts/claude-statusline/statusline-command.mjs +287 -27
  38. package/scripts/hs-companion.mjs +8 -0
  39. package/scripts/install-claude-code-hooks.mjs +104 -3
  40. package/scripts/install-copilot-hooks.mjs +21 -1
  41. package/scripts/install-cursor-hooks.mjs +4 -3
  42. package/scripts/lib/capture-execution-guard.mjs +221 -0
  43. package/scripts/lib/customer-kit.mjs +48 -0
  44. package/scripts/lib/hs-companion-settings.mjs +25 -0
  45. package/scripts/record-grounding.mjs +38 -0
  46. package/vendor/heyspex-mcp/http-transport.mjs +1 -1
  47. package/vendor/heyspex-mcp/test-helpers.mjs +37 -23
  48. package/bin/postinstall.mjs +0 -88
@@ -1298,10 +1298,10 @@ var require_stringify = __commonJS({
1298
1298
  data = Object.assign({}, file.data, data);
1299
1299
  const open = opts.delimiters[0];
1300
1300
  const close = opts.delimiters[1];
1301
- const matter4 = engine.stringify(data, options2).trim();
1301
+ const matter5 = engine.stringify(data, options2).trim();
1302
1302
  let buf = "";
1303
- if (matter4 !== "{}") {
1304
- buf = newline(open) + newline(matter4) + newline(close);
1303
+ if (matter5 !== "{}") {
1304
+ buf = newline(open) + newline(matter5) + newline(close);
1305
1305
  }
1306
1306
  if (typeof file.excerpt === "string" && file.excerpt !== "") {
1307
1307
  if (str2.indexOf(file.excerpt.trim()) === -1) {
@@ -1407,19 +1407,19 @@ var require_gray_matter = __commonJS({
1407
1407
  var toFile = require_to_file();
1408
1408
  var parse2 = require_parse();
1409
1409
  var utils = require_utils();
1410
- function matter4(input, options2) {
1410
+ function matter5(input, options2) {
1411
1411
  if (input === "") {
1412
1412
  return { data: {}, content: input, excerpt: "", orig: input };
1413
1413
  }
1414
1414
  let file = toFile(input);
1415
- const cached = matter4.cache[file.content];
1415
+ const cached = matter5.cache[file.content];
1416
1416
  if (!options2) {
1417
1417
  if (cached) {
1418
1418
  file = Object.assign({}, cached);
1419
1419
  file.orig = cached.orig;
1420
1420
  return file;
1421
1421
  }
1422
- matter4.cache[file.content] = file;
1422
+ matter5.cache[file.content] = file;
1423
1423
  }
1424
1424
  return parseMatter(file, options2);
1425
1425
  }
@@ -1441,7 +1441,7 @@ var require_gray_matter = __commonJS({
1441
1441
  }
1442
1442
  str2 = str2.slice(openLen);
1443
1443
  const len = str2.length;
1444
- const language = matter4.language(str2, opts);
1444
+ const language = matter5.language(str2, opts);
1445
1445
  if (language.name) {
1446
1446
  file.language = language.name;
1447
1447
  str2 = str2.slice(language.raw.length);
@@ -1476,24 +1476,24 @@ var require_gray_matter = __commonJS({
1476
1476
  }
1477
1477
  return file;
1478
1478
  }
1479
- matter4.engines = engines2;
1480
- matter4.stringify = function(file, data, options2) {
1481
- if (typeof file === "string") file = matter4(file, options2);
1479
+ matter5.engines = engines2;
1480
+ matter5.stringify = function(file, data, options2) {
1481
+ if (typeof file === "string") file = matter5(file, options2);
1482
1482
  return stringify(file, data, options2);
1483
1483
  };
1484
- matter4.read = function(filepath, options2) {
1484
+ matter5.read = function(filepath, options2) {
1485
1485
  const str2 = fs11.readFileSync(filepath, "utf8");
1486
- const file = matter4(str2, options2);
1486
+ const file = matter5(str2, options2);
1487
1487
  file.path = filepath;
1488
1488
  return file;
1489
1489
  };
1490
- matter4.test = function(str2, options2) {
1490
+ matter5.test = function(str2, options2) {
1491
1491
  return utils.startsWith(str2, defaults(options2).delimiters[0]);
1492
1492
  };
1493
- matter4.language = function(str2, options2) {
1493
+ matter5.language = function(str2, options2) {
1494
1494
  const opts = defaults(options2);
1495
1495
  const open = opts.delimiters[0];
1496
- if (matter4.test(str2)) {
1496
+ if (matter5.test(str2)) {
1497
1497
  str2 = str2.slice(open.length);
1498
1498
  }
1499
1499
  const language = str2.slice(0, str2.search(/\r?\n/));
@@ -1502,11 +1502,11 @@ var require_gray_matter = __commonJS({
1502
1502
  name: language ? language.trim() : ""
1503
1503
  };
1504
1504
  };
1505
- matter4.cache = {};
1506
- matter4.clearCache = function() {
1507
- matter4.cache = {};
1505
+ matter5.cache = {};
1506
+ matter5.clearCache = function() {
1507
+ matter5.cache = {};
1508
1508
  };
1509
- module2.exports = matter4;
1509
+ module2.exports = matter5;
1510
1510
  }
1511
1511
  });
1512
1512
 
@@ -4473,11 +4473,12 @@ function getPostgresAdminDb() {
4473
4473
  function inertClient() {
4474
4474
  return false;
4475
4475
  }
4476
- var isNeonPool;
4476
+ var isNeonPool, registerVaultScopedDbAccessor;
4477
4477
  var init_postgres_client_cli_shim = __esm({
4478
4478
  "dist/cli/._postgres-client.cli-shim.mjs"() {
4479
4479
  "use strict";
4480
4480
  isNeonPool = inertClient;
4481
+ registerVaultScopedDbAccessor = inertClient;
4481
4482
  }
4482
4483
  });
4483
4484
 
@@ -4720,6 +4721,141 @@ var init_clients = __esm({
4720
4721
  }
4721
4722
  });
4722
4723
 
4724
+ // spex-console/src/lib/rate-limits.ts
4725
+ var RATE_LIMIT_BUDGETS;
4726
+ var init_rate_limits = __esm({
4727
+ "spex-console/src/lib/rate-limits.ts"() {
4728
+ "use strict";
4729
+ RATE_LIMIT_BUDGETS = {
4730
+ mcp_tool_calls: { daily: 500, hourly: 125 },
4731
+ spec_writes: { daily: 100, hourly: 25 },
4732
+ capture_sessions: { daily: 20, hourly: 5 },
4733
+ // Paul's Advocate free-text chat ([[HS-4YYB-XEBI-TASK-paul-s-advocate-answers-real-questions]]):
4734
+ // a public YC reviewer can otherwise run up LLM spend on an unauthenticated-feeling
4735
+ // widget. 30/hour matches the builder brief's "e.g. 30 messages/hour" starting point;
4736
+ // daily is 4x hourly (looser than the 25%-of-daily pattern above — a reviewer chatting
4737
+ // steadily through a demo session should not get cut off mid-conversation).
4738
+ advocate_chat: { daily: 120, hourly: 30 }
4739
+ };
4740
+ }
4741
+ });
4742
+
4743
+ // spex-console/src/lib/plan-tier.ts
4744
+ function scaledBudgets(multiplier) {
4745
+ const out = {};
4746
+ for (const [bucket, budget] of Object.entries(RATE_LIMIT_BUDGETS)) {
4747
+ out[bucket] = {
4748
+ daily: Math.round(budget.daily * multiplier),
4749
+ hourly: Math.round(budget.hourly * multiplier)
4750
+ };
4751
+ }
4752
+ return Object.freeze(out);
4753
+ }
4754
+ var PLAN_TIER_RATE_LIMIT_MULTIPLIER, PLAN_TIER_LIMITS;
4755
+ var init_plan_tier = __esm({
4756
+ "spex-console/src/lib/plan-tier.ts"() {
4757
+ "use strict";
4758
+ init_sql_orm_inert_cli_shim();
4759
+ init_postgres_client_cli_shim();
4760
+ init_postgres();
4761
+ init_rate_limits();
4762
+ PLAN_TIER_RATE_LIMIT_MULTIPLIER = {
4763
+ free: 1,
4764
+ pro: 4,
4765
+ enterprise: 20
4766
+ };
4767
+ PLAN_TIER_LIMITS = {
4768
+ free: {
4769
+ statement_timeout_ms: 5e3,
4770
+ read_work_mem: "4MB",
4771
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.free)
4772
+ },
4773
+ pro: {
4774
+ statement_timeout_ms: 15e3,
4775
+ read_work_mem: "16MB",
4776
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.pro)
4777
+ },
4778
+ enterprise: {
4779
+ statement_timeout_ms: 3e4,
4780
+ read_work_mem: "64MB",
4781
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.enterprise)
4782
+ }
4783
+ };
4784
+ }
4785
+ });
4786
+
4787
+ // spex-console/src/lib/db/clients/vault-router.ts
4788
+ import { existsSync as existsSync8, readFileSync as readFileSync13 } from "node:fs";
4789
+ import { homedir as homedir10 } from "node:os";
4790
+ import { join as join11 } from "node:path";
4791
+ var CREDENTIALS_PATH, ROUTER_KEY, globalRouter, router;
4792
+ var init_vault_router = __esm({
4793
+ "spex-console/src/lib/db/clients/vault-router.ts"() {
4794
+ "use strict";
4795
+ init_sql_orm_inert_cli_shim();
4796
+ init_sql_orm_inert_cli_shim();
4797
+ init_postgres();
4798
+ init_postgres_client_cli_shim();
4799
+ CREDENTIALS_PATH = join11(homedir10(), ".heyspex", "credentials.yaml");
4800
+ ROUTER_KEY = Symbol.for("heyspex.vaultRouter");
4801
+ globalRouter = globalThis;
4802
+ if (!globalRouter[ROUTER_KEY]) {
4803
+ globalRouter[ROUTER_KEY] = {
4804
+ dedicated: /* @__PURE__ */ new Map(),
4805
+ refCache: /* @__PURE__ */ new Map(),
4806
+ warnedSchemaMissing: false
4807
+ };
4808
+ }
4809
+ router = globalRouter[ROUTER_KEY];
4810
+ }
4811
+ });
4812
+
4813
+ // spex-console/src/lib/db/clients/vault-scope.ts
4814
+ import { AsyncLocalStorage } from "node:async_hooks";
4815
+ function warnScopeClosedOnce(ctx) {
4816
+ if (ctx.warnedClosed) return;
4817
+ ctx.warnedClosed = true;
4818
+ console.warn(
4819
+ `[vault-scope] db use after vault scope closed; falling back to the pool connection (unscoped) (vaultId: ${ctx.vaultId})`
4820
+ );
4821
+ }
4822
+ function handleScopeClosedAccess(ctx) {
4823
+ if (ctx.onStaleUse === "throw") {
4824
+ throw new VaultScopeClosedError(ctx.vaultId);
4825
+ }
4826
+ warnScopeClosedOnce(ctx);
4827
+ }
4828
+ var VaultScopeClosedError, vaultScopeStorage;
4829
+ var init_vault_scope = __esm({
4830
+ "spex-console/src/lib/db/clients/vault-scope.ts"() {
4831
+ "use strict";
4832
+ init_sql_orm_inert_cli_shim();
4833
+ init_plan_tier();
4834
+ init_postgres_client_cli_shim();
4835
+ init_vault_router();
4836
+ VaultScopeClosedError = class extends Error {
4837
+ vaultId;
4838
+ constructor(vaultId) {
4839
+ super(
4840
+ `Vault scope for "${vaultId}" is closed; this path opted into onStaleUseAfterClose: "throw", so a db handle used after the scope's transaction settled fails loudly instead of silently falling back to the unscoped pool connection.`
4841
+ );
4842
+ this.name = "VaultScopeClosedError";
4843
+ this.vaultId = vaultId;
4844
+ }
4845
+ };
4846
+ vaultScopeStorage = new AsyncLocalStorage();
4847
+ registerVaultScopedDbAccessor(() => {
4848
+ const ctx = vaultScopeStorage.getStore();
4849
+ if (!ctx) return null;
4850
+ if (ctx.closed) {
4851
+ handleScopeClosedAccess(ctx);
4852
+ return null;
4853
+ }
4854
+ return ctx.proxy;
4855
+ });
4856
+ }
4857
+ });
4858
+
4723
4859
  // spex-console/src/lib/scheduler/types.ts
4724
4860
  var init_types2 = __esm({
4725
4861
  "spex-console/src/lib/scheduler/types.ts"() {
@@ -5247,10 +5383,45 @@ var init_config = __esm({
5247
5383
  }
5248
5384
  });
5249
5385
 
5250
- // spex-console/src/lib/vault-frontmatter.ts
5386
+ // spex-console/src/lib/safe-yaml.ts
5251
5387
  import yaml2 from "js-yaml";
5388
+ function dropUndefinedDeep(value) {
5389
+ if (Array.isArray(value)) {
5390
+ return value.filter((item) => item !== void 0).map((item) => dropUndefinedDeep(item));
5391
+ }
5392
+ if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
5393
+ const out = {};
5394
+ for (const [k, v] of Object.entries(value)) {
5395
+ if (v === void 0) continue;
5396
+ out[k] = dropUndefinedDeep(v);
5397
+ }
5398
+ return out;
5399
+ }
5400
+ return value;
5401
+ }
5402
+ function safeYamlDump(value, options2 = {}) {
5403
+ const { schema: _ignoredSchema, ...rest } = options2;
5404
+ const data = dropUndefinedDeep(value);
5405
+ if (grayMatterYamlStringify) return grayMatterYamlStringify(data, rest);
5406
+ return yaml2.dump(data, { ...rest, schema: SAFE_YAML_SCHEMA });
5407
+ }
5408
+ var import_gray_matter2, SAFE_YAML_SCHEMA, grayMatterYamlStringify, YAML_FAILSAFE_SCHEMA;
5409
+ var init_safe_yaml = __esm({
5410
+ "spex-console/src/lib/safe-yaml.ts"() {
5411
+ "use strict";
5412
+ import_gray_matter2 = __toESM(require_gray_matter(), 1);
5413
+ SAFE_YAML_SCHEMA = yaml2.DEFAULT_SAFE_SCHEMA ?? yaml2.DEFAULT_SCHEMA;
5414
+ grayMatterYamlStringify = (() => {
5415
+ const engine = import_gray_matter2.default.engines?.yaml;
5416
+ return typeof engine?.stringify === "function" ? engine.stringify : void 0;
5417
+ })();
5418
+ YAML_FAILSAFE_SCHEMA = yaml2.FAILSAFE_SCHEMA;
5419
+ }
5420
+ });
5421
+
5422
+ // spex-console/src/lib/vault-frontmatter.ts
5252
5423
  function normalizeFrontmatterForYaml(frontmatter) {
5253
- const processed = { ...frontmatter };
5424
+ const processed = dropUndefinedDeep(frontmatter);
5254
5425
  for (const [key, value] of Object.entries(processed)) {
5255
5426
  if (value instanceof Date) {
5256
5427
  processed[key] = value.toISOString();
@@ -5259,17 +5430,18 @@ function normalizeFrontmatterForYaml(frontmatter) {
5259
5430
  return processed;
5260
5431
  }
5261
5432
  function stringifySpecMarkdown(body, frontmatter) {
5262
- const fm = yaml2.dump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
5433
+ const fm = safeYamlDump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
5263
5434
  return `---
5264
5435
  ${fm}
5265
5436
  ---
5266
5437
  ${body}`;
5267
5438
  }
5268
- var import_gray_matter2, YAML_STRINGIFY_OPTIONS;
5439
+ var import_gray_matter3, YAML_STRINGIFY_OPTIONS;
5269
5440
  var init_vault_frontmatter = __esm({
5270
5441
  "spex-console/src/lib/vault-frontmatter.ts"() {
5271
5442
  "use strict";
5272
- import_gray_matter2 = __toESM(require_gray_matter(), 1);
5443
+ import_gray_matter3 = __toESM(require_gray_matter(), 1);
5444
+ init_safe_yaml();
5273
5445
  YAML_STRINGIFY_OPTIONS = {
5274
5446
  quotingType: '"',
5275
5447
  skipInvalid: true,
@@ -6192,12 +6364,12 @@ var init_linkage_validation = __esm({
6192
6364
  });
6193
6365
 
6194
6366
  // spex-console/src/lib/perf-timing.ts
6195
- import { AsyncLocalStorage } from "node:async_hooks";
6367
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
6196
6368
  var storage;
6197
6369
  var init_perf_timing = __esm({
6198
6370
  "spex-console/src/lib/perf-timing.ts"() {
6199
6371
  "use strict";
6200
- storage = new AsyncLocalStorage();
6372
+ storage = new AsyncLocalStorage2();
6201
6373
  }
6202
6374
  });
6203
6375
 
@@ -6219,12 +6391,12 @@ var init_planning_flow_model = __esm({
6219
6391
  });
6220
6392
 
6221
6393
  // spex-console/src/lib/request-memo.ts
6222
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
6394
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
6223
6395
  var storage2;
6224
6396
  var init_request_memo = __esm({
6225
6397
  "spex-console/src/lib/request-memo.ts"() {
6226
6398
  "use strict";
6227
- storage2 = new AsyncLocalStorage2();
6399
+ storage2 = new AsyncLocalStorage3();
6228
6400
  }
6229
6401
  });
6230
6402
 
@@ -6472,15 +6644,14 @@ var init_search_body_hit = __esm({
6472
6644
 
6473
6645
  // spex-console/src/lib/vault.ts
6474
6646
  import { spawn } from "node:child_process";
6475
- import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
6647
+ import { readdirSync as readdirSync2, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
6476
6648
  import { promises as fs7 } from "node:fs";
6477
6649
  import path14 from "node:path";
6478
- import yaml3 from "js-yaml";
6479
- var import_gray_matter3, VAULT_CACHE_TTL_WATCHED_MS, VAULT_CACHE_KEY, vaultCacheStore, VAULT_CACHE_DELTA_LISTENERS_KEY, vaultCacheDeltaListeners, SEARCH_TEXT_KEY, searchTextStore, VAULT_SNAPSHOT_KEY, vaultSnapshotStore;
6650
+ var import_gray_matter4, VAULT_CACHE_TTL_WATCHED_MS, VAULT_CACHE_KEY, vaultCacheStore, VAULT_CACHE_DELTA_LISTENERS_KEY, vaultCacheDeltaListeners, SEARCH_TEXT_KEY, searchTextStore, VAULT_SNAPSHOT_KEY, vaultSnapshotStore;
6480
6651
  var init_vault = __esm({
6481
6652
  "spex-console/src/lib/vault.ts"() {
6482
6653
  "use strict";
6483
- import_gray_matter3 = __toESM(require_gray_matter(), 1);
6654
+ import_gray_matter4 = __toESM(require_gray_matter(), 1);
6484
6655
  init_diagnose_skeleton();
6485
6656
  init_auth();
6486
6657
  init_capture_vault();
@@ -6491,6 +6662,7 @@ var init_vault = __esm({
6491
6662
  init_skips();
6492
6663
  init_clients();
6493
6664
  init_postgres_client_cli_shim();
6665
+ init_vault_scope();
6494
6666
  init_helpers();
6495
6667
  init_index_write_service();
6496
6668
  init_spec_read_service();
@@ -6507,6 +6679,7 @@ var init_vault = __esm({
6507
6679
  init_planning_flow_model();
6508
6680
  init_request_memo();
6509
6681
  init_review_mode();
6682
+ init_safe_yaml();
6510
6683
  init_active_milestone();
6511
6684
  init_area();
6512
6685
  init_claim_eligibility();
@@ -6592,7 +6765,7 @@ var init_vault = __esm({
6592
6765
 
6593
6766
  // spex-console/bin/hs-index-rebuild.ts
6594
6767
  import { execSync } from "node:child_process";
6595
- import { appendFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync15 } from "node:fs";
6768
+ import { appendFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync16 } from "node:fs";
6596
6769
  import { promises as fs10 } from "node:fs";
6597
6770
  import path15 from "node:path";
6598
6771
  import process4 from "node:process";
@@ -6985,7 +7158,7 @@ async function runReindex(input) {
6985
7158
  init_validator();
6986
7159
 
6987
7160
  // spex-console/src/lib/vault-sync/pull-io.ts
6988
- import { promises as fs9, readFileSync as readFileSync14 } from "node:fs";
7161
+ import { promises as fs9, readFileSync as readFileSync15 } from "node:fs";
6989
7162
  import { EOL } from "node:os";
6990
7163
  import nodePath2 from "node:path";
6991
7164
 
@@ -7154,7 +7327,7 @@ function appendJournal(vaultRoot2, message) {
7154
7327
  }
7155
7328
  function readDiscovery(p) {
7156
7329
  try {
7157
- return readFileSync15(p, "utf-8");
7330
+ return readFileSync16(p, "utf-8");
7158
7331
  } catch {
7159
7332
  return null;
7160
7333
  }