tines 0.0.86 → 0.0.87

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 (2) hide show
  1. package/dist/index.js +135 -61
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3928,6 +3928,39 @@ var PLACEHOLDER_DESCRIPTIONS = {
3928
3928
  };
3929
3929
  var TEMPLATE_PLACEHOLDERS = Object.keys(PLACEHOLDER_DESCRIPTIONS).map((key) => ({ key, description: PLACEHOLDER_DESCRIPTIONS[key] }));
3930
3930
 
3931
+ // ../shared/src/paginate.ts
3932
+ var MAX_PAGE_SIZE = 100;
3933
+ var MAX_ALL_PAGES_ITEMS = 1e4;
3934
+ async function* listPages(fetchPage, opts = {}) {
3935
+ const pageSize = opts.pageSize ?? MAX_PAGE_SIZE;
3936
+ const maxItems = opts.maxItems ?? MAX_ALL_PAGES_ITEMS;
3937
+ const seen = /* @__PURE__ */ new Set();
3938
+ const seenCursors = /* @__PURE__ */ new Set();
3939
+ let cursor;
3940
+ for (; ; ) {
3941
+ const res = await fetchPage({ limit: pageSize, cursor });
3942
+ const fresh = res.items.filter((item) => !seen.has(item.id));
3943
+ for (const item of fresh) seen.add(item.id);
3944
+ if (seen.size > maxItems) {
3945
+ throw new Error(
3946
+ `list has more than ${maxItems} items \u2014 narrow it with filters, or page manually with --cursor`
3947
+ );
3948
+ }
3949
+ yield fresh;
3950
+ if (res.next_cursor === null) return;
3951
+ if (seenCursors.has(res.next_cursor)) {
3952
+ throw new Error(`pagination cursor did not advance past "${cursor}" (server bug?)`);
3953
+ }
3954
+ seenCursors.add(res.next_cursor);
3955
+ cursor = res.next_cursor;
3956
+ }
3957
+ }
3958
+ async function listAll(fetchPage, opts = {}) {
3959
+ const items = [];
3960
+ for await (const page of listPages(fetchPage, opts)) items.push(...page);
3961
+ return items;
3962
+ }
3963
+
3931
3964
  // src/format.ts
3932
3965
  function timestamp(ms) {
3933
3966
  return new Date(ms).toISOString().replace("T", " ").slice(0, 19);
@@ -4128,7 +4161,16 @@ function withCommon(cmd, { baseUrlFlag = true } = {}) {
4128
4161
  }
4129
4162
  function withList(cmd) {
4130
4163
  return withCommon(
4131
- cmd.option("--limit <n>", "maximum items to return", (v) => Number.parseInt(v, 10)).option("--cursor <cursor>", "resume from the next_cursor of a previous page")
4164
+ cmd.option(
4165
+ "--limit <n>",
4166
+ "maximum items to return (page size under --all-pages)",
4167
+ (v) => Number.parseInt(v, 10)
4168
+ ).option("--cursor <cursor>", "resume from the next_cursor of a previous page").addOption(
4169
+ new Option(
4170
+ "--all-pages",
4171
+ "fetch every page, not just the first (slower on large lists)"
4172
+ ).conflicts("cursor")
4173
+ )
4132
4174
  );
4133
4175
  }
4134
4176
  function resolveUrl(opts) {
@@ -4177,8 +4219,24 @@ function printJson(value) {
4177
4219
  function printList(res, opts, render) {
4178
4220
  if (opts.json) return printJson(res);
4179
4221
  render(res.items);
4180
- if (res.next_cursor) console.log(`
4181
- more results: rerun with --cursor ${res.next_cursor}`);
4222
+ if (res.next_cursor) {
4223
+ console.log(
4224
+ `
4225
+ more results: rerun with --all-pages, or resume with --cursor ${res.next_cursor}`
4226
+ );
4227
+ }
4228
+ }
4229
+ async function fetchList(opts, fetchPage) {
4230
+ if (opts.allPages) {
4231
+ return { items: await listAll(fetchPage, { pageSize: opts.limit }), next_cursor: null };
4232
+ }
4233
+ const res = await fetchPage({ limit: opts.limit, cursor: opts.cursor });
4234
+ if (res.next_cursor && opts.json && !opts.cursor) {
4235
+ console.error(
4236
+ "warning: more items exist beyond this page \u2014 rerun with --all-pages for all of them"
4237
+ );
4238
+ }
4239
+ return res;
4182
4240
  }
4183
4241
  function table(rows) {
4184
4242
  if (rows.length === 0) return;
@@ -4289,16 +4347,18 @@ function register(program3) {
4289
4347
  ).action(async (opts) => {
4290
4348
  const api = client(opts);
4291
4349
  const scope = await resolveScopeFlags(api, opts);
4292
- const res = await api.listContext({
4293
- kind: opts.kind,
4294
- project: scope.project_id ?? void 0,
4295
- state: scope.workflow_state_id ?? void 0,
4296
- issue: scope.issue_id ?? void 0,
4297
- q: opts.search,
4298
- exact: opts.exact ? true : void 0,
4299
- limit: opts.limit,
4300
- cursor: opts.cursor
4301
- });
4350
+ const res = await fetchList(
4351
+ opts,
4352
+ (page) => api.listContext({
4353
+ kind: opts.kind,
4354
+ project: scope.project_id ?? void 0,
4355
+ state: scope.workflow_state_id ?? void 0,
4356
+ issue: scope.issue_id ?? void 0,
4357
+ q: opts.search,
4358
+ exact: opts.exact ? true : void 0,
4359
+ ...page
4360
+ })
4361
+ );
4302
4362
  printList(res, opts, (items) => {
4303
4363
  if (items.length === 0) return console.log("no context items");
4304
4364
  table([
@@ -4413,7 +4473,7 @@ function register(program3) {
4413
4473
  context.command("init").description('Seed the global "agent-guidelines" prompt (a no-op if it already exists)')
4414
4474
  ).action(async (opts) => {
4415
4475
  const api = client(opts);
4416
- const { items } = await api.listContext({ kind: "prompt", exact: true, limit: 100 });
4476
+ const items = await listAll((page) => api.listContext({ kind: "prompt", exact: true, ...page }));
4417
4477
  const existing = items.find((i) => i.name === AGENT_GUIDELINES_NAME);
4418
4478
  if (existing) {
4419
4479
  if (opts.json) return printJson(existing);
@@ -4616,17 +4676,20 @@ function register2(program3) {
4616
4676
  issues.command("list").description("List issues across projects (hides done issues unless --all)").option("-p, --project <name>", "filter by project name or id").option("-s, --state <name>", "filter by state name or id").option("-c, --category <cat>", "filter by state category").option("-w, --workflow <id-or-name>", "filter by workflow").option("-a, --all", "include issues in done states").option("--ready", "only issues that are actionable now (not done, not a duplicate, no open blockers)").option("-q, --search <text>", "search titles and descriptions")
4617
4677
  ).action(
4618
4678
  async (opts) => {
4619
- const res = await client(opts).listIssues({
4620
- project: opts.project,
4621
- state: opts.state,
4622
- category: opts.category,
4623
- workflow: opts.workflow,
4624
- hide_done: !opts.all,
4625
- ready: opts.ready,
4626
- q: opts.search,
4627
- limit: opts.limit,
4628
- cursor: opts.cursor
4629
- });
4679
+ const api = client(opts);
4680
+ const res = await fetchList(
4681
+ opts,
4682
+ (page) => api.listIssues({
4683
+ project: opts.project,
4684
+ state: opts.state,
4685
+ category: opts.category,
4686
+ workflow: opts.workflow,
4687
+ hide_done: !opts.all,
4688
+ ready: opts.ready,
4689
+ q: opts.search,
4690
+ ...page
4691
+ })
4692
+ );
4630
4693
  printList(res, opts, (items) => {
4631
4694
  if (items.length === 0) return console.log(opts.ready ? "no ready issues" : "no issues");
4632
4695
  table([
@@ -5129,13 +5192,15 @@ files (v${artifact.current_version.version}):`);
5129
5192
  // src/commands/journal.ts
5130
5193
  var STATE_FLAG_HELP = "target this state's journal instead of your run's launch stage (options go BEFORE <ref>)";
5131
5194
  async function journalItemAt(api, scope) {
5132
- const { items } = await api.listContext({
5133
- kind: "prompt",
5134
- project: scope.project_id ?? void 0,
5135
- state: scope.workflow_state_id ?? void 0,
5136
- exact: true,
5137
- limit: 100
5138
- });
5195
+ const items = await listAll(
5196
+ (page) => api.listContext({
5197
+ kind: "prompt",
5198
+ project: scope.project_id ?? void 0,
5199
+ state: scope.workflow_state_id ?? void 0,
5200
+ exact: true,
5201
+ ...page
5202
+ })
5203
+ );
5139
5204
  return items.find((i) => i.name === JOURNAL_NAME) ?? null;
5140
5205
  }
5141
5206
  function stateScope(issue, state, workflow) {
@@ -5261,13 +5326,15 @@ function registerEvents(program3) {
5261
5326
  ).action(async (opts) => {
5262
5327
  const api = client(opts);
5263
5328
  const issueId = opts.issue ? (await resolveIssue(api, opts.issue)).id : void 0;
5264
- const res = await api.listEvents({
5265
- issue: issueId,
5266
- project: opts.project,
5267
- type: opts.type,
5268
- limit: opts.limit,
5269
- cursor: opts.cursor
5270
- });
5329
+ const res = await fetchList(
5330
+ opts,
5331
+ (page) => api.listEvents({
5332
+ issue: issueId,
5333
+ project: opts.project,
5334
+ type: opts.type,
5335
+ ...page
5336
+ })
5337
+ );
5271
5338
  printList(res, opts, (items) => {
5272
5339
  if (items.length === 0) return console.log("no events");
5273
5340
  table([
@@ -5282,7 +5349,8 @@ function registerEvents(program3) {
5282
5349
  function register4(program3) {
5283
5350
  const projects = program3.command("projects").description("Manage projects");
5284
5351
  withList(projects.command("list").description("List projects")).action(async (opts) => {
5285
- const res = await client(opts).listProjects({ limit: opts.limit, cursor: opts.cursor });
5352
+ const api = client(opts);
5353
+ const res = await fetchList(opts, (page) => api.listProjects(page));
5286
5354
  printList(res, opts, (items) => {
5287
5355
  if (items.length === 0) return console.log("no projects");
5288
5356
  table([
@@ -6484,13 +6552,15 @@ function register6(program3) {
6484
6552
  const api = client(opts);
6485
6553
  const issueId = opts.issue ? (await resolveIssue(api, opts.issue)).id : void 0;
6486
6554
  const runnerId = opts.runner ? (await resolveRunner(api, opts.runner)).id : void 0;
6487
- const res = await api.listRuns({
6488
- issue: issueId,
6489
- runner: runnerId,
6490
- active: opts.active ? true : void 0,
6491
- limit: opts.limit,
6492
- cursor: opts.cursor
6493
- });
6555
+ const res = await fetchList(
6556
+ opts,
6557
+ (page) => api.listRuns({
6558
+ issue: issueId,
6559
+ runner: runnerId,
6560
+ active: opts.active ? true : void 0,
6561
+ ...page
6562
+ })
6563
+ );
6494
6564
  printList(res, opts, (items) => {
6495
6565
  if (items.length === 0) return console.log(opts.active ? "no active runs" : "no runs");
6496
6566
  table([["ID", "ISSUE", "RUNNER", "TIER", "STATUS", "DURATION", "COST", "CREATED"], ...items.map(runRow)]);
@@ -6565,7 +6635,7 @@ import { createInterface } from "node:readline/promises";
6565
6635
  async function resolveSchedule(api, ref) {
6566
6636
  const { project, name: name2 } = parseScheduleRef(ref);
6567
6637
  const proj = await resolveProject(api, project);
6568
- const { items } = await api.listProjectSchedules(proj.id, { limit: 100 });
6638
+ const items = await listAll((page) => api.listProjectSchedules(proj.id, page));
6569
6639
  const found = items.find((s) => s.name === name2) ?? items.find((s) => s.id === name2);
6570
6640
  if (!found) {
6571
6641
  die(
@@ -6595,12 +6665,15 @@ function register7(program3) {
6595
6665
  withList(
6596
6666
  schedules.command("list").description("List scheduled tasks (hides paused schedules unless --all)").option("-p, --project <name>", "filter by project name or id").option("-a, --all", "include paused schedules")
6597
6667
  ).action(async (opts) => {
6598
- const res = await client(opts).listSchedules({
6599
- project: opts.project,
6600
- enabled: opts.all ? void 0 : true,
6601
- limit: opts.limit,
6602
- cursor: opts.cursor
6603
- });
6668
+ const api = client(opts);
6669
+ const res = await fetchList(
6670
+ opts,
6671
+ (page) => api.listSchedules({
6672
+ project: opts.project,
6673
+ enabled: opts.all ? void 0 : true,
6674
+ ...page
6675
+ })
6676
+ );
6604
6677
  printList(res, opts, (items) => {
6605
6678
  if (items.length === 0) return console.log("no schedules");
6606
6679
  table([
@@ -6729,14 +6802,14 @@ function register8(program3) {
6729
6802
  withCommon(supervisor.command("status").description("One-screen overview: kill switch, quota, utilization, runners")).action(
6730
6803
  async (opts) => {
6731
6804
  const api = client(opts);
6732
- const [settings, runnersRes, workflows, activeRuns] = await Promise.all([
6805
+ const [settings, runnersRes, workflows, activeRunItems] = await Promise.all([
6733
6806
  api.getSupervisorSettings(),
6734
6807
  api.listRunners(),
6735
- api.listWorkflows({ limit: 100 }),
6736
- api.listRuns({ active: true, limit: 100 })
6808
+ api.listWorkflows(),
6809
+ listAll((page) => api.listRuns({ active: true, ...page }))
6737
6810
  ]);
6738
6811
  if (opts.json) {
6739
- return printJson({ settings, runners: runnersRes.items, active_runs: activeRuns.items });
6812
+ return printJson({ settings, runners: runnersRes.items, active_runs: activeRunItems });
6740
6813
  }
6741
6814
  const stateNames = /* @__PURE__ */ new Map();
6742
6815
  for (const wf of workflows.items) {
@@ -6744,7 +6817,7 @@ function register8(program3) {
6744
6817
  }
6745
6818
  console.log(`automation: ${settings.enabled ? "ON" : "OFF (kill switch \u2014 nothing dispatches)"}`);
6746
6819
  console.log(quotaLabel(settings.quota, (id) => stateNames.get(id) ?? id));
6747
- console.log(`utilization: ${utilizationLabel(settings.quota, activeRuns.items, (id) => stateNames.get(id) ?? id)}`);
6820
+ console.log(`utilization: ${utilizationLabel(settings.quota, activeRunItems, (id) => stateNames.get(id) ?? id)}`);
6748
6821
  console.log(`attempt limit: ${settings.attempt_limit} strikes, then the issue parks`);
6749
6822
  if (runnersRes.items.length === 0) {
6750
6823
  console.log("runners: none");
@@ -6809,7 +6882,7 @@ function register8(program3) {
6809
6882
  quota: { type: "state_roster", default_limit: opts.default, overrides }
6810
6883
  });
6811
6884
  if (opts.json) return printJson(settings);
6812
- const workflows = await api.listWorkflows({ limit: 100 });
6885
+ const workflows = await api.listWorkflows();
6813
6886
  const stateNames = /* @__PURE__ */ new Map();
6814
6887
  for (const wf of workflows.items) {
6815
6888
  for (const s of wf.states) stateNames.set(s.id, `${wf.name}/${s.name}`);
@@ -6915,7 +6988,8 @@ function register9(program3) {
6915
6988
  const workflows = program3.command("workflows").description("Manage the workflow library");
6916
6989
  withList(workflows.command("list").description("List the workflow library")).action(
6917
6990
  async (opts) => {
6918
- const res = await client(opts).listWorkflows({ limit: opts.limit, cursor: opts.cursor });
6991
+ const api = client(opts);
6992
+ const res = await fetchList(opts, (page) => api.listWorkflows(page));
6919
6993
  printList(res, opts, (items) => {
6920
6994
  if (items.length === 0) return console.log("no workflows");
6921
6995
  table([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.86",
3
+ "version": "0.0.87",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",