@hasna/loops 0.4.10 → 0.4.12

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/mcp/index.js CHANGED
@@ -1323,6 +1323,7 @@ function rowToWorkflowWorkItem(row) {
1323
1323
  subjectRef: row.subject_ref,
1324
1324
  projectKey: row.project_key ?? undefined,
1325
1325
  projectGroup: row.project_group ?? undefined,
1326
+ routeScope: row.route_scope ?? undefined,
1326
1327
  priority: row.priority,
1327
1328
  status: row.status,
1328
1329
  attempts: row.attempts,
@@ -1603,6 +1604,13 @@ class Store {
1603
1604
  this.addColumnIfMissing("loop_runs", "claim_token", "TEXT");
1604
1605
  this.db.exec("CREATE INDEX IF NOT EXISTS idx_runs_claim_token ON loop_runs(claim_token) WHERE claim_token IS NOT NULL");
1605
1606
  }
1607
+ },
1608
+ {
1609
+ id: "0008_work_item_route_scope",
1610
+ apply: () => {
1611
+ this.addColumnIfMissing("workflow_work_items", "route_scope", "TEXT");
1612
+ this.db.exec("CREATE INDEX IF NOT EXISTS idx_workflow_work_items_scope ON workflow_work_items(route_scope, status)");
1613
+ }
1606
1614
  }
1607
1615
  ];
1608
1616
  }
@@ -1750,6 +1758,7 @@ class Store {
1750
1758
  subject_ref TEXT NOT NULL,
1751
1759
  project_key TEXT,
1752
1760
  project_group TEXT,
1761
+ route_scope TEXT,
1753
1762
  priority INTEGER NOT NULL,
1754
1763
  status TEXT NOT NULL,
1755
1764
  attempts INTEGER NOT NULL,
@@ -1767,6 +1776,13 @@ class Store {
1767
1776
  CREATE INDEX IF NOT EXISTS idx_workflow_work_items_project ON workflow_work_items(project_key, status);
1768
1777
  CREATE INDEX IF NOT EXISTS idx_workflow_work_items_group ON workflow_work_items(project_group, status);
1769
1778
  CREATE INDEX IF NOT EXISTS idx_workflow_work_items_invocation ON workflow_work_items(invocation_id);
1779
+ -- idx_workflow_work_items_scope (route_scope, status) is created ONLY by
1780
+ -- migration 0008_work_item_route_scope, never here: this baseline DDL
1781
+ -- re-runs on EVERY open (0001 is not skip-guarded), and on a pre-0008
1782
+ -- database the CREATE TABLE above is a no-op, so an index on route_scope
1783
+ -- here would execute before the column exists and crash the open
1784
+ -- ("no such column: route_scope"). New columns may be folded into the
1785
+ -- CREATE TABLE (fresh-db only); their indexes must live in the migration.
1770
1786
 
1771
1787
  CREATE TABLE IF NOT EXISTS workflow_step_runs (
1772
1788
  id TEXT PRIMARY KEY,
@@ -2558,10 +2574,10 @@ class Store {
2558
2574
  const id = genId();
2559
2575
  const status = input.status ?? "queued";
2560
2576
  this.db.query(`INSERT INTO workflow_work_items (id, route_key, idempotency_key, invocation_id, source_type, source_ref,
2561
- subject_ref, project_key, project_group, priority, status, attempts, next_attempt_at, lease_expires_at,
2577
+ subject_ref, project_key, project_group, route_scope, priority, status, attempts, next_attempt_at, lease_expires_at,
2562
2578
  workflow_id, loop_id, workflow_run_id, last_reason, created_at, updated_at)
2563
2579
  VALUES ($id, $routeKey, $idempotencyKey, $invocationId, $sourceType, $sourceRef, $subjectRef,
2564
- $projectKey, $projectGroup, $priority, $status, 0, $nextAttemptAt, NULL, NULL, NULL, NULL,
2580
+ $projectKey, $projectGroup, $routeScope, $priority, $status, 0, $nextAttemptAt, NULL, NULL, NULL, NULL,
2565
2581
  $lastReason, $created, $updated)
2566
2582
  ON CONFLICT(route_key, idempotency_key) DO UPDATE SET
2567
2583
  invocation_id=excluded.invocation_id,
@@ -2570,6 +2586,7 @@ class Store {
2570
2586
  subject_ref=excluded.subject_ref,
2571
2587
  project_key=excluded.project_key,
2572
2588
  project_group=excluded.project_group,
2589
+ route_scope=excluded.route_scope,
2573
2590
  priority=excluded.priority,
2574
2591
  status=CASE
2575
2592
  WHEN workflow_work_items.status IN ('succeeded', 'admitted', 'running', 'failed', 'dead_letter', 'cancelled')
@@ -2611,6 +2628,7 @@ class Store {
2611
2628
  $subjectRef: input.subjectRef,
2612
2629
  $projectKey: input.projectKey ?? null,
2613
2630
  $projectGroup: input.projectGroup ?? null,
2631
+ $routeScope: input.routeScope ?? null,
2614
2632
  $priority: input.priority ?? 0,
2615
2633
  $status: status,
2616
2634
  $nextAttemptAt: input.nextAttemptAt ?? null,
@@ -2648,11 +2666,21 @@ class Store {
2648
2666
  countActiveWorkflowWorkItems(args = {}) {
2649
2667
  const active = ["admitted", "running"];
2650
2668
  const placeholders = active.map(() => "?").join(",");
2651
- const global = this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders})`).get(...active)?.count ?? 0;
2669
+ const routeScope = args.routeScope?.trim() || undefined;
2670
+ const global = routeScope ? this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders}) AND route_scope = ?`).get(...active, routeScope)?.count ?? 0 : this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders})`).get(...active)?.count ?? 0;
2652
2671
  const project = args.projectKey ? this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders}) AND project_key = ?`).get(...active, args.projectKey)?.count ?? 0 : 0;
2653
2672
  const projectGroup = args.projectGroup ? this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders}) AND project_group = ?`).get(...active, args.projectGroup)?.count ?? 0 : undefined;
2654
2673
  return { global, project, ...projectGroup !== undefined ? { projectGroup } : {} };
2655
2674
  }
2675
+ countRunningWorkflowStepsByAuthProfile() {
2676
+ const rows = this.db.query("SELECT account_profile, COUNT(*) AS count FROM workflow_step_runs WHERE status = 'running' AND account_profile IS NOT NULL GROUP BY account_profile").all();
2677
+ const counts = {};
2678
+ for (const row of rows) {
2679
+ if (row.account_profile)
2680
+ counts[row.account_profile] = row.count;
2681
+ }
2682
+ return counts;
2683
+ }
2656
2684
  requeueWorkflowWorkItem(id, patch = {}) {
2657
2685
  const current = this.getWorkflowWorkItem(id);
2658
2686
  if (!current)
@@ -3088,6 +3116,9 @@ class Store {
3088
3116
  }
3089
3117
  input.workflow.steps.forEach((step, sequence) => {
3090
3118
  const account = step.account ?? step.target.account;
3119
+ const agentTarget = step.target.type === "agent" ? step.target : undefined;
3120
+ const resolvedProfile = account?.profile ?? agentTarget?.authProfile ?? null;
3121
+ const resolvedTool = account?.tool ?? (agentTarget?.authProfile ? agentTarget.provider : null);
3091
3122
  this.db.query(`INSERT INTO workflow_step_runs (id, workflow_run_id, step_id, sequence, status, started_at, finished_at,
3092
3123
  exit_code, pid, duration_ms, stdout, stderr, error, account_profile, account_tool, created_at, updated_at)
3093
3124
  VALUES ($id, $workflowRunId, $stepId, $sequence, 'pending', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
@@ -3096,8 +3127,8 @@ class Store {
3096
3127
  $workflowRunId: runId,
3097
3128
  $stepId: step.id,
3098
3129
  $sequence: sequence,
3099
- $accountProfile: account?.profile ?? null,
3100
- $accountTool: account?.tool ?? null,
3130
+ $accountProfile: resolvedProfile,
3131
+ $accountTool: resolvedTool,
3101
3132
  $created: now,
3102
3133
  $updated: now
3103
3134
  });
@@ -7021,6 +7052,9 @@ async function executeLoopTarget(store, loop, run, opts = {}) {
7021
7052
  }
7022
7053
 
7023
7054
  // src/lib/scheduler.ts
7055
+ function loopLane(loop) {
7056
+ return loop.target.type === "command" ? "command" : "agent";
7057
+ }
7024
7058
  function manualRunScheduledFor(loop, now = new Date) {
7025
7059
  if (loop.archivedAt)
7026
7060
  return now.toISOString();
@@ -7413,14 +7447,23 @@ function claimDueRuns(deps) {
7413
7447
  const maxClaims = Math.max(0, deps.maxClaims ?? Number.POSITIVE_INFINITY);
7414
7448
  if (maxClaims === 0)
7415
7449
  return { claims, claimed, completed: [], skipped, recovered, expired };
7450
+ const laneLimits = deps.laneLimits;
7451
+ const laneClaims = { command: 0, agent: 0 };
7452
+ const laneCap = (lane) => laneLimits === undefined ? Number.POSITIVE_INFINITY : Math.max(0, laneLimits[lane] ?? Number.POSITIVE_INFINITY);
7453
+ const laneFull = (lane) => laneClaims[lane] >= laneCap(lane);
7416
7454
  for (const loop of deps.store.dueLoops(now)) {
7417
7455
  if (claims.length >= maxClaims)
7418
7456
  break;
7457
+ const lane = loopLane(loop);
7458
+ if (laneFull(lane))
7459
+ continue;
7419
7460
  const plan = dueSlots(loop, now);
7420
7461
  let loopSkips = 0;
7421
7462
  for (const slot of plan.slots) {
7422
7463
  if (claims.length >= maxClaims)
7423
7464
  break;
7465
+ if (laneFull(lane))
7466
+ break;
7424
7467
  if (loopSkips >= MAX_SKIPS_PER_LOOP_PER_TICK)
7425
7468
  break;
7426
7469
  const run = claimSlot(deps, loop, slot);
@@ -7429,6 +7472,7 @@ function claimDueRuns(deps) {
7429
7472
  if ("loop" in run) {
7430
7473
  claims.push(run);
7431
7474
  claimed.push(run.run);
7475
+ laneClaims[lane] += 1;
7432
7476
  } else if (run.status === "skipped") {
7433
7477
  skipped.push(run);
7434
7478
  loopSkips += 1;
@@ -7468,7 +7512,7 @@ async function tick(deps) {
7468
7512
  // package.json
7469
7513
  var package_default = {
7470
7514
  name: "@hasna/loops",
7471
- version: "0.4.10",
7515
+ version: "0.4.12",
7472
7516
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
7473
7517
  type: "module",
7474
7518
  main: "dist/index.js",
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@hasna/loops",
6
- version: "0.4.10",
6
+ version: "0.4.12",
7
7
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
8
8
  type: "module",
9
9
  main: "dist/index.js",
package/dist/sdk/index.js CHANGED
@@ -1321,6 +1321,7 @@ function rowToWorkflowWorkItem(row) {
1321
1321
  subjectRef: row.subject_ref,
1322
1322
  projectKey: row.project_key ?? undefined,
1323
1323
  projectGroup: row.project_group ?? undefined,
1324
+ routeScope: row.route_scope ?? undefined,
1324
1325
  priority: row.priority,
1325
1326
  status: row.status,
1326
1327
  attempts: row.attempts,
@@ -1601,6 +1602,13 @@ class Store {
1601
1602
  this.addColumnIfMissing("loop_runs", "claim_token", "TEXT");
1602
1603
  this.db.exec("CREATE INDEX IF NOT EXISTS idx_runs_claim_token ON loop_runs(claim_token) WHERE claim_token IS NOT NULL");
1603
1604
  }
1605
+ },
1606
+ {
1607
+ id: "0008_work_item_route_scope",
1608
+ apply: () => {
1609
+ this.addColumnIfMissing("workflow_work_items", "route_scope", "TEXT");
1610
+ this.db.exec("CREATE INDEX IF NOT EXISTS idx_workflow_work_items_scope ON workflow_work_items(route_scope, status)");
1611
+ }
1604
1612
  }
1605
1613
  ];
1606
1614
  }
@@ -1748,6 +1756,7 @@ class Store {
1748
1756
  subject_ref TEXT NOT NULL,
1749
1757
  project_key TEXT,
1750
1758
  project_group TEXT,
1759
+ route_scope TEXT,
1751
1760
  priority INTEGER NOT NULL,
1752
1761
  status TEXT NOT NULL,
1753
1762
  attempts INTEGER NOT NULL,
@@ -1765,6 +1774,13 @@ class Store {
1765
1774
  CREATE INDEX IF NOT EXISTS idx_workflow_work_items_project ON workflow_work_items(project_key, status);
1766
1775
  CREATE INDEX IF NOT EXISTS idx_workflow_work_items_group ON workflow_work_items(project_group, status);
1767
1776
  CREATE INDEX IF NOT EXISTS idx_workflow_work_items_invocation ON workflow_work_items(invocation_id);
1777
+ -- idx_workflow_work_items_scope (route_scope, status) is created ONLY by
1778
+ -- migration 0008_work_item_route_scope, never here: this baseline DDL
1779
+ -- re-runs on EVERY open (0001 is not skip-guarded), and on a pre-0008
1780
+ -- database the CREATE TABLE above is a no-op, so an index on route_scope
1781
+ -- here would execute before the column exists and crash the open
1782
+ -- ("no such column: route_scope"). New columns may be folded into the
1783
+ -- CREATE TABLE (fresh-db only); their indexes must live in the migration.
1768
1784
 
1769
1785
  CREATE TABLE IF NOT EXISTS workflow_step_runs (
1770
1786
  id TEXT PRIMARY KEY,
@@ -2556,10 +2572,10 @@ class Store {
2556
2572
  const id = genId();
2557
2573
  const status = input.status ?? "queued";
2558
2574
  this.db.query(`INSERT INTO workflow_work_items (id, route_key, idempotency_key, invocation_id, source_type, source_ref,
2559
- subject_ref, project_key, project_group, priority, status, attempts, next_attempt_at, lease_expires_at,
2575
+ subject_ref, project_key, project_group, route_scope, priority, status, attempts, next_attempt_at, lease_expires_at,
2560
2576
  workflow_id, loop_id, workflow_run_id, last_reason, created_at, updated_at)
2561
2577
  VALUES ($id, $routeKey, $idempotencyKey, $invocationId, $sourceType, $sourceRef, $subjectRef,
2562
- $projectKey, $projectGroup, $priority, $status, 0, $nextAttemptAt, NULL, NULL, NULL, NULL,
2578
+ $projectKey, $projectGroup, $routeScope, $priority, $status, 0, $nextAttemptAt, NULL, NULL, NULL, NULL,
2563
2579
  $lastReason, $created, $updated)
2564
2580
  ON CONFLICT(route_key, idempotency_key) DO UPDATE SET
2565
2581
  invocation_id=excluded.invocation_id,
@@ -2568,6 +2584,7 @@ class Store {
2568
2584
  subject_ref=excluded.subject_ref,
2569
2585
  project_key=excluded.project_key,
2570
2586
  project_group=excluded.project_group,
2587
+ route_scope=excluded.route_scope,
2571
2588
  priority=excluded.priority,
2572
2589
  status=CASE
2573
2590
  WHEN workflow_work_items.status IN ('succeeded', 'admitted', 'running', 'failed', 'dead_letter', 'cancelled')
@@ -2609,6 +2626,7 @@ class Store {
2609
2626
  $subjectRef: input.subjectRef,
2610
2627
  $projectKey: input.projectKey ?? null,
2611
2628
  $projectGroup: input.projectGroup ?? null,
2629
+ $routeScope: input.routeScope ?? null,
2612
2630
  $priority: input.priority ?? 0,
2613
2631
  $status: status,
2614
2632
  $nextAttemptAt: input.nextAttemptAt ?? null,
@@ -2646,11 +2664,21 @@ class Store {
2646
2664
  countActiveWorkflowWorkItems(args = {}) {
2647
2665
  const active = ["admitted", "running"];
2648
2666
  const placeholders = active.map(() => "?").join(",");
2649
- const global = this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders})`).get(...active)?.count ?? 0;
2667
+ const routeScope = args.routeScope?.trim() || undefined;
2668
+ const global = routeScope ? this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders}) AND route_scope = ?`).get(...active, routeScope)?.count ?? 0 : this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders})`).get(...active)?.count ?? 0;
2650
2669
  const project = args.projectKey ? this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders}) AND project_key = ?`).get(...active, args.projectKey)?.count ?? 0 : 0;
2651
2670
  const projectGroup = args.projectGroup ? this.db.query(`SELECT COUNT(*) AS count FROM workflow_work_items WHERE status IN (${placeholders}) AND project_group = ?`).get(...active, args.projectGroup)?.count ?? 0 : undefined;
2652
2671
  return { global, project, ...projectGroup !== undefined ? { projectGroup } : {} };
2653
2672
  }
2673
+ countRunningWorkflowStepsByAuthProfile() {
2674
+ const rows = this.db.query("SELECT account_profile, COUNT(*) AS count FROM workflow_step_runs WHERE status = 'running' AND account_profile IS NOT NULL GROUP BY account_profile").all();
2675
+ const counts = {};
2676
+ for (const row of rows) {
2677
+ if (row.account_profile)
2678
+ counts[row.account_profile] = row.count;
2679
+ }
2680
+ return counts;
2681
+ }
2654
2682
  requeueWorkflowWorkItem(id, patch = {}) {
2655
2683
  const current = this.getWorkflowWorkItem(id);
2656
2684
  if (!current)
@@ -3086,6 +3114,9 @@ class Store {
3086
3114
  }
3087
3115
  input.workflow.steps.forEach((step, sequence) => {
3088
3116
  const account = step.account ?? step.target.account;
3117
+ const agentTarget = step.target.type === "agent" ? step.target : undefined;
3118
+ const resolvedProfile = account?.profile ?? agentTarget?.authProfile ?? null;
3119
+ const resolvedTool = account?.tool ?? (agentTarget?.authProfile ? agentTarget.provider : null);
3089
3120
  this.db.query(`INSERT INTO workflow_step_runs (id, workflow_run_id, step_id, sequence, status, started_at, finished_at,
3090
3121
  exit_code, pid, duration_ms, stdout, stderr, error, account_profile, account_tool, created_at, updated_at)
3091
3122
  VALUES ($id, $workflowRunId, $stepId, $sequence, 'pending', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
@@ -3094,8 +3125,8 @@ class Store {
3094
3125
  $workflowRunId: runId,
3095
3126
  $stepId: step.id,
3096
3127
  $sequence: sequence,
3097
- $accountProfile: account?.profile ?? null,
3098
- $accountTool: account?.tool ?? null,
3128
+ $accountProfile: resolvedProfile,
3129
+ $accountTool: resolvedTool,
3099
3130
  $created: now,
3100
3131
  $updated: now
3101
3132
  });
@@ -4235,7 +4266,7 @@ class Store {
4235
4266
  // package.json
4236
4267
  var package_default = {
4237
4268
  name: "@hasna/loops",
4238
- version: "0.4.10",
4269
+ version: "0.4.12",
4239
4270
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4240
4271
  type: "module",
4241
4272
  main: "dist/index.js",
@@ -7792,6 +7823,9 @@ async function executeLoopTarget(store, loop, run, opts = {}) {
7792
7823
  }
7793
7824
 
7794
7825
  // src/lib/scheduler.ts
7826
+ function loopLane(loop) {
7827
+ return loop.target.type === "command" ? "command" : "agent";
7828
+ }
7795
7829
  function manualRunScheduledFor(loop, now = new Date) {
7796
7830
  if (loop.archivedAt)
7797
7831
  return now.toISOString();
@@ -8184,14 +8218,23 @@ function claimDueRuns(deps) {
8184
8218
  const maxClaims = Math.max(0, deps.maxClaims ?? Number.POSITIVE_INFINITY);
8185
8219
  if (maxClaims === 0)
8186
8220
  return { claims, claimed, completed: [], skipped, recovered, expired };
8221
+ const laneLimits = deps.laneLimits;
8222
+ const laneClaims = { command: 0, agent: 0 };
8223
+ const laneCap = (lane) => laneLimits === undefined ? Number.POSITIVE_INFINITY : Math.max(0, laneLimits[lane] ?? Number.POSITIVE_INFINITY);
8224
+ const laneFull = (lane) => laneClaims[lane] >= laneCap(lane);
8187
8225
  for (const loop of deps.store.dueLoops(now)) {
8188
8226
  if (claims.length >= maxClaims)
8189
8227
  break;
8228
+ const lane = loopLane(loop);
8229
+ if (laneFull(lane))
8230
+ continue;
8190
8231
  const plan = dueSlots(loop, now);
8191
8232
  let loopSkips = 0;
8192
8233
  for (const slot of plan.slots) {
8193
8234
  if (claims.length >= maxClaims)
8194
8235
  break;
8236
+ if (laneFull(lane))
8237
+ break;
8195
8238
  if (loopSkips >= MAX_SKIPS_PER_LOOP_PER_TICK)
8196
8239
  break;
8197
8240
  const run = claimSlot(deps, loop, slot);
@@ -8200,6 +8243,7 @@ function claimDueRuns(deps) {
8200
8243
  if ("loop" in run) {
8201
8244
  claims.push(run);
8202
8245
  claimed.push(run.run);
8246
+ laneClaims[lane] += 1;
8203
8247
  } else if (run.status === "skipped") {
8204
8248
  skipped.push(run);
8205
8249
  loopSkips += 1;
package/dist/types.d.ts CHANGED
@@ -104,6 +104,9 @@ export interface AgentRoutingSpec {
104
104
  eventId?: string;
105
105
  eventType?: string;
106
106
  eventSource?: string;
107
+ /** Lifecycle role of this agent step (triage/planner/worker/verifier). Used
108
+ * for per-account attribution and least-loaded auth-profile pool selection. */
109
+ role?: "triage" | "planner" | "worker" | "verifier";
107
110
  }
108
111
  export interface AgentPromptSource {
109
112
  type: "file";
@@ -209,6 +212,12 @@ export interface WorkflowWorkItem {
209
212
  subjectRef: string;
210
213
  projectKey?: string;
211
214
  projectGroup?: string;
215
+ /**
216
+ * The drain/route identity (loop name) that admitted this item. Used to scope
217
+ * the `--max-active` global admission count to a single route instead of the
218
+ * whole store. Undefined for items created before route-scope support.
219
+ */
220
+ routeScope?: string;
212
221
  priority: number;
213
222
  status: WorkflowWorkItemStatus;
214
223
  attempts: number;
@@ -230,6 +239,7 @@ export interface UpsertWorkflowWorkItemInput {
230
239
  subjectRef: string;
231
240
  projectKey?: string;
232
241
  projectGroup?: string;
242
+ routeScope?: string;
233
243
  priority?: number;
234
244
  status?: Extract<WorkflowWorkItemStatus, "queued" | "deferred">;
235
245
  nextAttemptAt?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/loops",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
4
4
  "description": "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",