costlayers 0.8.15 → 0.8.16

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
@@ -2,29 +2,30 @@
2
2
 
3
3
  CostLayers helps coding-agent users stop paying for repeated repo context. API users can route model calls through the gateway for invoice savings. ChatGPT-login Codex users get a usage-stretch meter that shows how much repeated context was avoided.
4
4
 
5
- ## Install
5
+ ## Quick Start
6
6
 
7
- One-command setup:
7
+ Daily Codex use:
8
8
 
9
9
  ```bash
10
10
  cd your-repo
11
- npx -y costlayers@latest start --email you@example.com
11
+ npx -y costlayers@latest codex --email you@example.com
12
12
  ```
13
13
 
14
- ## Usage
14
+ If `OPENAI_API_KEY` is set, CostLayers automatically uses API invoice mode.
15
+ If no API key is set, it uses ChatGPT-login usage-stretch mode.
15
16
 
16
- Inside a repo:
17
+ Run a one-command API savings test:
17
18
 
18
19
  ```bash
19
- costlayers init
20
- costlayers scan
20
+ npx -y costlayers@latest test --email you@example.com
21
21
  ```
22
22
 
23
- Launch Codex with CostLayers context:
23
+ ## Usage
24
+
25
+ Inside a repo:
24
26
 
25
27
  ```bash
26
- cd your-repo
27
- npx -y costlayers@latest start --email you@example.com -- codex
28
+ npx -y costlayers@latest codex --email you@example.com
28
29
  ```
29
30
 
30
31
  This gives Codex `.agentspend/repo-pack.md` and `.agentspend/runtime-plan.md`
@@ -44,7 +45,7 @@ API write permission:
44
45
 
45
46
  ```bash
46
47
  export OPENAI_API_KEY=sk-proj-...
47
- npx -y costlayers@latest start --email you@example.com --codex-proxy -- codex
48
+ npx -y costlayers@latest codex --email you@example.com
48
49
  ```
49
50
 
50
51
  ChatGPT-login Codex can be metered, but it does not create per-request OpenAI
@@ -52,8 +53,9 @@ Platform invoice savings because it is not billed through your Platform API key.
52
53
 
53
54
  ## Which Mode Should I Use?
54
55
 
55
- - ChatGPT-login Codex: use `start -- codex` to reduce repeated repo context and stretch usage limits.
56
- - OpenAI Platform API billing: use `--codex-proxy` with `OPENAI_API_KEY` for invoice-backed savings.
56
+ - ChatGPT-login Codex: use `costlayers codex --email you@example.com --chatgpt` to reduce repeated repo context and stretch usage limits.
57
+ - OpenAI Platform API billing: set `OPENAI_API_KEY`, then use `costlayers codex --email you@example.com` for invoice-backed savings.
58
+ - Savings proof: set `OPENAI_API_KEY`, then run `costlayers test --email you@example.com`.
57
59
  - Other OpenAI-compatible clients: point the client at the CostLayers gateway URL and check `costlayers gateway report`.
58
60
 
59
61
  To install only the Codex profile after signup:
package/bin/agentspend.js CHANGED
@@ -9,7 +9,7 @@ const https = require("https");
9
9
  const os = require("os");
10
10
  const { spawnSync } = require("child_process");
11
11
 
12
- const VERSION = "0.8.15";
12
+ const VERSION = "0.8.16";
13
13
  const INSTALL_SPEC = "costlayers@latest";
14
14
  const DEFAULT_RUNS_PER_WEEK = 20;
15
15
  const WEEKS_PER_MONTH = 4.33;
@@ -50,6 +50,8 @@ function usage(exitCode = 0) {
50
50
  CostLayers ${VERSION}
51
51
 
52
52
  Usage:
53
+ costlayers codex [--email <email>] [--chatgpt|--api] [-- <codex args>]
54
+ costlayers test [--email <email>] [--prompt <text>]
53
55
  costlayers init [--repo <path>]
54
56
  costlayers scan [--repo <path>] [--price-per-1m <usd>] [--tasks <n>] [--runs-per-week <n>]
55
57
  costlayers start [--email <email>] [--provider-url <url>] [--mode measure|reduce] [--runs-per-week <n>] [--codex-proxy] [-- <agent command>]
@@ -64,6 +66,8 @@ Usage:
64
66
  costlayers doctor
65
67
 
66
68
  Commands:
69
+ codex Start Codex with CostLayers. Uses API invoice mode automatically when OPENAI_API_KEY is set.
70
+ test Run a safe read-only Codex task and print the CostLayers savings report.
67
71
  init Create .agentspend config and agent instructions.
68
72
  scan Build repo context pack and savings report.
69
73
  start One-command setup, signup, gateway start, and optional agent run.
@@ -123,7 +127,7 @@ function guardRepoRoot(repo, args) {
123
127
  "",
124
128
  "Run it inside a project folder instead:",
125
129
  " cd path/to/your-repo",
126
- ` npx -y ${INSTALL_SPEC} start --email you@example.com`,
130
+ ` npx -y ${INSTALL_SPEC} codex --email you@example.com`,
127
131
  "",
128
132
  "Or pass --repo path/to/your-repo from anywhere.",
129
133
  "If you really intend to scan your whole home directory, add --allow-home.",
@@ -538,6 +542,28 @@ function codexProxyApiKeyEnv(args = {}) {
538
542
  return String(args["api-key-env"] || "OPENAI_API_KEY");
539
543
  }
540
544
 
545
+ function apiInvoiceModeRequested(args = {}) {
546
+ return Boolean(args.api || args.invoice || args["api-mode"] || codexProxyEnabled(args));
547
+ }
548
+
549
+ function chatgptModeRequested(args = {}) {
550
+ return Boolean(args.chatgpt || args["no-api"]);
551
+ }
552
+
553
+ function codexArgsAfterDash(argv) {
554
+ const dash = argv.indexOf("--");
555
+ return dash >= 0 ? argv.slice(dash + 1) : [];
556
+ }
557
+
558
+ function withAutoCodexMode(args = {}, options = {}) {
559
+ const next = { ...args };
560
+ const keyEnv = codexProxyApiKeyEnv(next);
561
+ const hasApiKey = Boolean(process.env[keyEnv]);
562
+ const wantsInvoice = apiInvoiceModeRequested(next) || (options.preferInvoice && hasApiKey);
563
+ if (!chatgptModeRequested(next) && wantsInvoice) next["codex-proxy"] = true;
564
+ return next;
565
+ }
566
+
541
567
  function assertCodexProxyApiKey(args = {}) {
542
568
  if (!codexProxyEnabled(args)) return;
543
569
  const keyEnv = codexProxyApiKeyEnv(args);
@@ -547,7 +573,7 @@ function assertCodexProxyApiKey(args = {}) {
547
573
  "",
548
574
  "Set an OpenAI Platform API key with Responses API write permission, then rerun:",
549
575
  ` export ${keyEnv}=sk-proj-...`,
550
- ` npx -y ${INSTALL_SPEC} start --email you@example.com --codex-proxy -- codex`,
576
+ ` npx -y ${INSTALL_SPEC} codex --email you@example.com --api`,
551
577
  "",
552
578
  "ChatGPT-login Codex can be metered, but it cannot produce provider invoice savings because there is no per-request Platform invoice to reduce.",
553
579
  ""
@@ -845,7 +871,9 @@ async function runAgent(repo, args, argv, options = {}) {
845
871
  stdio: "inherit",
846
872
  shell: process.platform === "win32"
847
873
  });
848
- process.exit(typeof result.status === "number" ? result.status : 1);
874
+ const status = typeof result.status === "number" ? result.status : 1;
875
+ if (options.returnStatus) return status;
876
+ process.exit(status);
849
877
  }
850
878
 
851
879
  async function gateway(repo, args) {
@@ -918,7 +946,51 @@ async function dashboard(repo, args) {
918
946
  process.stdout.write(`claim: API invoice savings only count when provider API traffic is routed through CostLayers. ChatGPT-login Codex shows usage stretch, not invoice reduction.\n`);
919
947
  }
920
948
 
921
- async function start(repo, args, argv) {
949
+ async function codexShortcut(repo, args, argv) {
950
+ const codexTail = codexArgsAfterDash(argv);
951
+ const command = codexTail.length > 0 ? codexTail : ["codex"];
952
+ const commandToRun = isCodexCommand(command) ? command : ["codex", ...command];
953
+ const nextArgs = withAutoCodexMode(args, { preferInvoice: true });
954
+ if (codexProxyEnabled(nextArgs)) {
955
+ process.stdout.write(`CostLayers Codex: API invoice mode enabled from ${codexProxyApiKeyEnv(nextArgs)}.\n`);
956
+ } else {
957
+ process.stdout.write(`CostLayers Codex: ChatGPT usage-stretch mode. Set ${codexProxyApiKeyEnv(nextArgs)} or pass --api for invoice savings.\n`);
958
+ }
959
+ return start(repo, nextArgs, ["start", "--", ...commandToRun]);
960
+ }
961
+
962
+ async function savingsTest(repo, args) {
963
+ const nextArgs = withAutoCodexMode(args, { preferInvoice: true });
964
+ if (!codexProxyEnabled(nextArgs)) {
965
+ const keyEnv = codexProxyApiKeyEnv(nextArgs);
966
+ process.stderr.write([
967
+ `CostLayers test needs ${keyEnv} for real API invoice savings.`,
968
+ "",
969
+ "Set your OpenAI Platform API key, then rerun:",
970
+ ` source ~/.config/costlayers/env`,
971
+ ` npx -y ${INSTALL_SPEC} test --email you@example.com`,
972
+ "",
973
+ `For ChatGPT-login metering without invoice savings, run:`,
974
+ ` npx -y ${INSTALL_SPEC} codex --email you@example.com --chatgpt`,
975
+ ""
976
+ ].join("\n"));
977
+ process.exit(2);
978
+ }
979
+ const prompt = typeof args.prompt === "string"
980
+ ? args.prompt
981
+ : "Analyze this repository. Find the main entry points, data flow, and the 5 files most worth reading. Do not edit files.";
982
+ process.stdout.write("CostLayers savings test: running one safe read-only Codex task.\n");
983
+ const status = await start(repo, nextArgs, ["start", "--", "codex", "exec", "--sandbox", "read-only", prompt], { returnStatus: true });
984
+ process.stdout.write("\nCostLayers savings test report\n");
985
+ try {
986
+ await gateway(repo, { ...nextArgs, _: ["gateway", "report"] });
987
+ } catch (err) {
988
+ process.stderr.write(`Could not fetch savings report: ${err.message}\n`);
989
+ }
990
+ process.exit(status);
991
+ }
992
+
993
+ async function start(repo, args, argv, options = {}) {
922
994
  const dash = argv.indexOf("--");
923
995
  const command = dash >= 0 ? argv.slice(dash + 1) : [];
924
996
  const codexTelemetryRun = command.length > 0 && isCodexCommand(command) && !codexProxyEnabled(args);
@@ -972,12 +1044,12 @@ async function start(repo, args, argv) {
972
1044
  process.stdout.write(`CostLayers Codex profile: ${profilePath}\n`);
973
1045
  process.stdout.write(`Codex profile mode: ${codexProxyEnabled(args) ? "API invoice mode" : "ChatGPT usage-stretch mode; native Codex model path preserved"}\n`);
974
1046
  if (command.length > 0) {
975
- return runAgent(repo, args, argv, { skipSetup: true, precomputed });
1047
+ return runAgent(repo, args, argv, { skipSetup: true, precomputed, returnStatus: options.returnStatus });
976
1048
  }
977
1049
  process.stdout.write(`\nNext options:\n`);
978
1050
  process.stdout.write(` Use gateway URL in your model client: ${gatewayBaseUrl}\n`);
979
- process.stdout.write(` Run Codex metered: npx -y ${INSTALL_SPEC} start --email you@example.com -- codex\n`);
980
- process.stdout.write(` Reduce an OpenAI API invoice: export OPENAI_API_KEY=sk-proj-... && npx -y ${INSTALL_SPEC} start --email you@example.com --codex-proxy -- codex\n`);
1051
+ process.stdout.write(` Run Codex: npx -y ${INSTALL_SPEC} codex --email you@example.com\n`);
1052
+ process.stdout.write(` Prove API savings: npx -y ${INSTALL_SPEC} test --email you@example.com\n`);
981
1053
  process.stdout.write(` Or run Codex directly: codex --profile costlayers\n`);
982
1054
  process.stdout.write(` View report: npx -y ${INSTALL_SPEC} gateway report\n`);
983
1055
  process.stdout.write(` Dashboard: npx -y ${INSTALL_SPEC} dashboard\n`);
@@ -995,8 +1067,10 @@ function main() {
995
1067
  const cmd = args._[0];
996
1068
  if (!cmd || args.help || args.h) usage(0);
997
1069
  const repo = path.resolve(String(args.repo || process.cwd()));
998
- if (["init", "scan", "start", "run", "codex-profile"].includes(cmd)) guardRepoRoot(repo, args);
1070
+ if (["init", "scan", "start", "run", "codex-profile", "codex", "test"].includes(cmd)) guardRepoRoot(repo, args);
999
1071
  if (cmd === "doctor") return doctor();
1072
+ if (cmd === "codex") return codexShortcut(repo, args, rawArgv);
1073
+ if (cmd === "test") return savingsTest(repo, args);
1000
1074
  if (cmd === "init") return init(repo);
1001
1075
  if (cmd === "scan") return scan(repo, args);
1002
1076
  if (cmd === "start") return start(repo, args, rawArgv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "costlayers",
3
- "version": "0.8.15",
3
+ "version": "0.8.16",
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",