codetime-cli 0.1.0 → 0.2.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 (2) hide show
  1. package/bin/codetime.mjs +30 -8
  2. package/package.json +2 -2
package/bin/codetime.mjs CHANGED
@@ -1329,9 +1329,10 @@ function msToIso(ms) {
1329
1329
  }
1330
1330
 
1331
1331
  // src/lib/constants.ts
1332
- var PACKAGE_VERSION = true ? "0.1.0" : "0.1.1";
1332
+ var PACKAGE_VERSION = true ? "0.2.0" : "0.1.1";
1333
1333
  var DEFAULT_API_URL = "https://codetime.dev";
1334
- var DEFAULT_BACKFILL_BATCH_SIZE = 500;
1334
+ var DEFAULT_BACKFILL_BATCH_SIZE = 50;
1335
+ var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
1335
1336
  var ROLLUP_BUCKET_MS = 15 * 60 * 1e3;
1336
1337
  var DEFAULT_HOOK_SYNC_MIN_INTERVAL_SECONDS = 60;
1337
1338
 
@@ -4538,7 +4539,7 @@ function createCli(ctx, registry) {
4538
4539
  cli.command("hook", "Read agent hook JSON from stdin and report a throttled event").option("--agent <name>", "Agent name").option("--project <name>", "Project name").option("--min-interval <seconds>", "Minimum seconds between similar hook reports").action((options) => hookCommand(normalizeOptions(options), ctx));
4539
4540
  cli.command("sync-local-trigger", "Trigger one background local sync with throttle and locking").option("--min-interval <seconds>", "Minimum seconds between sync triggers").action((options) => syncLocalTriggerCommand(normalizeOptions(options), ctx, registry));
4540
4541
  cli.command("sync-local-runner", "Internal background local sync runner").option("--lock-file <path>", "Lock file for the active sync").option("--state-file <path>", "State file for trigger metadata").action((options) => syncLocalRunnerCommand(normalizeOptions(options), ctx, registry));
4541
- cli.command("backfill [action]", "Inspect local history import candidates").option("--source <source>", "Backfill source").option("--since <time>", "Only include history after this time").option("--until <time>", "Only include history before this time").option("--project <name>", "Project filter").option("--source-root <path>", "Override source history root").option("--include-source-path", "Include local source paths in output").option("--import-run <id>", "Import run id for verify/resume workflows").option("--limit <count>", "Maximum session files to parse").option("--batch-size <count>", "Events per batch during import").option("--replace", "Replace conflicting records during import (default)").option("--skip-conflicts", "Skip conflicting records instead of replacing them").option("--force", "Force full re-import: clear watermark and re-process all files").action((action, options) => backfillCommand({ ...normalizeOptions(options), action }, ctx, registry));
4542
+ cli.command("backfill [action]", "Inspect local history import candidates").option("--source <source>", "Backfill source").option("--since <time>", "Only include history after this time").option("--until <time>", "Only include history before this time").option("--project <name>", "Project filter").option("--source-root <path>", "Override source history root").option("--include-source-path", "Include local source paths in output").option("--import-run <id>", "Import run id for verify/resume workflows").option("--limit <count>", "Maximum session files to parse").option("--batch-size <count>", "Max rollups per request (also bounded by --batch-bytes)").option("--batch-bytes <bytes>", "Soft byte cap for the JSON body of a single ingest POST").option("--replace", "Replace conflicting records during import (default)").option("--skip-conflicts", "Skip conflicting records instead of replacing them").option("--force", "Force full re-import: clear watermark and re-process all files").action((action, options) => backfillCommand({ ...normalizeOptions(options), action }, ctx, registry));
4542
4543
  cli.command("token [action] [value]", "Set, show, or clear the persisted API token").option("--remote <url>", "Override API base URL when setting a token").action((action, value, options) => tokenCommand(action, value, normalizeOptions(options), ctx));
4543
4544
  cli.command("machine [action]", "List or rename machines (requires login)").option("--name <name>", "New display name (used by `machine rename`)").option("--id <id>", "Machine id (defaults to current machine)").action((action, options) => machineCommand(action, normalizeOptions(options), ctx));
4544
4545
  return cli;
@@ -4556,6 +4557,7 @@ function normalizeOptions(options) {
4556
4557
  sourceRoot: "source-root",
4557
4558
  importRun: "import-run",
4558
4559
  batchSize: "batch-size",
4560
+ batchBytes: "batch-bytes",
4559
4561
  skipConflicts: "skip-conflicts"
4560
4562
  };
4561
4563
  for (const [camel, dashed] of Object.entries(aliases)) {
@@ -5001,7 +5003,9 @@ async function importBackfillPlan(plan, options, ctx, registry) {
5001
5003
  const counts = { inserted: 0, skipped: 0, conflicts: 0, failed: 0 };
5002
5004
  const rollups = buildSessionRollups(canonicalEvents);
5003
5005
  const batchSize = Math.max(1, Math.floor(numberOption(options["batch-size"]) || DEFAULT_BACKFILL_BATCH_SIZE));
5004
- const totalBatches = Math.ceil(rollups.length / batchSize);
5006
+ const batchBytes = Math.max(64 * 1024, Math.floor(numberOption(options["batch-bytes"]) || DEFAULT_BACKFILL_BATCH_BYTES));
5007
+ const batches = packRollupBatches(rollups, batchSize, batchBytes);
5008
+ const totalBatches = batches.length;
5005
5009
  let uploadBar;
5006
5010
  if (!options.json) {
5007
5011
  write(ctx.stdout, `rollup ${rollups.length} from ${canonicalEvents.length} events
@@ -5009,9 +5013,9 @@ async function importBackfillPlan(plan, options, ctx, registry) {
5009
5013
  uploadBar = new ProgressBar(ctx.stdout, `upload`.padEnd(12));
5010
5014
  uploadBar.init(totalBatches, `0/${totalBatches} batches, 0 inserted`);
5011
5015
  }
5012
- for (let index = 0; index < rollups.length; index += batchSize) {
5013
- const batch = rollups.slice(index, index + batchSize);
5014
- const batchNumber = Math.floor(index / batchSize) + 1;
5016
+ for (let i = 0; i < batches.length; i += 1) {
5017
+ const batch = batches[i];
5018
+ const batchNumber = i + 1;
5015
5019
  try {
5016
5020
  const result2 = await sendSessionRollupBatch(batch, options, ctx);
5017
5021
  counts.inserted += result2.inserted;
@@ -5019,7 +5023,7 @@ async function importBackfillPlan(plan, options, ctx, registry) {
5019
5023
  counts.conflicts += result2.conflicts;
5020
5024
  counts.failed += result2.failed;
5021
5025
  } catch (error) {
5022
- debug(ctx, `backfill rollup batch ${index + 1}-${index + batch.length} failed: ${error.message}
5026
+ debug(ctx, `backfill rollup batch ${batchNumber}/${totalBatches} (${batch.length} rollups) failed: ${error.message}
5023
5027
  `);
5024
5028
  counts.failed += batch.length;
5025
5029
  }
@@ -5111,6 +5115,24 @@ async function deleteSessionRollupsBySourceAPI(source, options, ctx) {
5111
5115
  function shouldUseIncrementalBackfill(options) {
5112
5116
  return !stringOption(options.since) && !stringOption(options.until) && !stringOption(options["source-root"]) && numberOption(options.limit) === void 0;
5113
5117
  }
5118
+ function packRollupBatches(rollups, maxCount, maxBytes) {
5119
+ const batches = [];
5120
+ let current = [];
5121
+ let currentBytes = 0;
5122
+ for (const rollup of rollups) {
5123
+ const size = JSON.stringify(rollup).length;
5124
+ const wouldExceed = current.length > 0 && (current.length >= maxCount || currentBytes + size > maxBytes);
5125
+ if (wouldExceed) {
5126
+ batches.push(current);
5127
+ current = [];
5128
+ currentBytes = 0;
5129
+ }
5130
+ current.push(rollup);
5131
+ currentBytes += size;
5132
+ }
5133
+ if (current.length > 0) batches.push(current);
5134
+ return batches;
5135
+ }
5114
5136
  function selectBackfillFilesForImport(files, watermarkTs) {
5115
5137
  if (!watermarkTs) {
5116
5138
  return files;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "codetime-cli",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "description": "codetime CLI — install AI-agent hooks (Claude Code, Codex, OpenCode, Pi) and report activity to codetime.dev.",
6
6
  "license": "MIT",
7
7
  "publishConfig": {
@@ -35,7 +35,7 @@
35
35
  "devDependencies": {
36
36
  "cac": "^7.0.0",
37
37
  "esbuild": "^0.28.0",
38
- "@codetime/shared": "0.1.0"
38
+ "@codetime/shared": "0.2.0"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -p tsconfig.json",