costlayers 0.8.13 → 0.8.15

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/README.md CHANGED
@@ -8,7 +8,7 @@ One-command setup:
8
8
 
9
9
  ```bash
10
10
  cd your-repo
11
- npx -y https://costlayers.com/costlayers-0.8.13.tgz start --email you@example.com
11
+ npx -y costlayers@latest start --email you@example.com
12
12
  ```
13
13
 
14
14
  ## Usage
@@ -24,7 +24,7 @@ Launch Codex with CostLayers context:
24
24
 
25
25
  ```bash
26
26
  cd your-repo
27
- npx -y https://costlayers.com/costlayers-0.8.13.tgz start --email you@example.com -- codex
27
+ npx -y costlayers@latest start --email you@example.com -- codex
28
28
  ```
29
29
 
30
30
  This gives Codex `.agentspend/repo-pack.md` and `.agentspend/runtime-plan.md`
@@ -44,7 +44,7 @@ API write permission:
44
44
 
45
45
  ```bash
46
46
  export OPENAI_API_KEY=sk-proj-...
47
- npx -y https://costlayers.com/costlayers-0.8.13.tgz start --email you@example.com --codex-proxy -- codex
47
+ npx -y costlayers@latest start --email you@example.com --codex-proxy -- codex
48
48
  ```
49
49
 
50
50
  ChatGPT-login Codex can be metered, but it does not create per-request OpenAI
@@ -59,7 +59,7 @@ Platform invoice savings because it is not billed through your Platform API key.
59
59
  To install only the Codex profile after signup:
60
60
 
61
61
  ```bash
62
- npx -y https://costlayers.com/costlayers-0.8.13.tgz codex-profile
62
+ npx -y costlayers@latest codex-profile
63
63
  codex --profile costlayers
64
64
  ```
65
65
 
package/bin/agentspend.js CHANGED
@@ -9,8 +9,8 @@ const https = require("https");
9
9
  const os = require("os");
10
10
  const { spawnSync } = require("child_process");
11
11
 
12
- const VERSION = "0.8.13";
13
- const INSTALL_SPEC = "https://costlayers.com/costlayers-0.8.13.tgz";
12
+ const VERSION = "0.8.15";
13
+ const INSTALL_SPEC = "costlayers@latest";
14
14
  const DEFAULT_RUNS_PER_WEEK = 20;
15
15
  const WEEKS_PER_MONTH = 4.33;
16
16
  const DEFAULT_EXCLUDES = new Set([
@@ -145,6 +145,10 @@ function readJsonIfExists(file) {
145
145
  }
146
146
  }
147
147
 
148
+ function normalizedEmail(value) {
149
+ return String(value || "").trim().toLowerCase();
150
+ }
151
+
148
152
  function estimateTokens(text) {
149
153
  return Math.ceil(String(text || "").length / 4);
150
154
  }
@@ -639,16 +643,49 @@ async function signupConnection(repo, args) {
639
643
  engine_url: result.engine_url || engineUrl,
640
644
  api_key: result.api_key,
641
645
  gateway_url: result.gateway_url,
646
+ email: normalizedEmail(payload.email),
647
+ label: payload.label,
642
648
  connected_utc: new Date().toISOString()
643
649
  };
644
650
  fs.writeFileSync(path.join(outDir, "connection.json"), JSON.stringify(connection, null, 2) + "\n", "utf8");
645
651
  return connection;
646
652
  }
647
653
 
654
+ function saveConnection(repo, connection) {
655
+ const outDir = path.join(repo, ".agentspend");
656
+ ensureDir(outDir);
657
+ fs.writeFileSync(path.join(outDir, "connection.json"), JSON.stringify(connection, null, 2) + "\n", "utf8");
658
+ }
659
+
648
660
  async function ensureConnection(repo, args) {
649
661
  const outDir = path.join(repo, ".agentspend");
650
662
  const saved = readJsonIfExists(path.join(outDir, "connection.json"));
651
- if (saved && saved.engine_url && saved.api_key) return saved;
663
+ const requestedEmail = normalizedEmail(args.email);
664
+ if (saved && saved.engine_url && saved.api_key) {
665
+ if (!requestedEmail) return saved;
666
+ const savedEmail = normalizedEmail(saved.email);
667
+ if (savedEmail === requestedEmail) return saved;
668
+ if (savedEmail && savedEmail !== requestedEmail) {
669
+ process.stdout.write(`CostLayers email changed from ${savedEmail} to ${requestedEmail}; creating a new repo key.\n`);
670
+ return signupConnection(repo, args);
671
+ }
672
+ try {
673
+ const status = await postJson(`${String(saved.engine_url).replace(/\/+$/, "")}/v1/me`, {}, saved.api_key);
674
+ const remoteEmail = normalizedEmail(status.email);
675
+ if (remoteEmail && remoteEmail !== requestedEmail) {
676
+ process.stdout.write(`CostLayers saved key belongs to ${remoteEmail}; creating a new repo key for ${requestedEmail}.\n`);
677
+ return signupConnection(repo, args);
678
+ }
679
+ if (remoteEmail === requestedEmail) {
680
+ const updated = { ...saved, email: remoteEmail };
681
+ saveConnection(repo, updated);
682
+ return updated;
683
+ }
684
+ } catch (err) {
685
+ process.stdout.write(`CostLayers could not verify the saved key email (${err.message}); creating a new repo key for ${requestedEmail}.\n`);
686
+ return signupConnection(repo, args);
687
+ }
688
+ }
652
689
  return signupConnection(repo, args);
653
690
  }
654
691
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "costlayers",
3
- "version": "0.8.13",
3
+ "version": "0.8.15",
4
4
  "description": "CostLayers cost control for AI coding agents. Build compact repo context packs, gateway reports, and savings dashboards.",
5
5
  "bin": {
6
6
  "agentspend": "bin/agentspend.js",