@phantomcodes/cli 1.2.1 → 1.2.2

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
@@ -19,7 +19,6 @@ phantom setup --agent claude
19
19
  ```
20
20
 
21
21
  The skill covers workspace setup, funding, spending controls and model routing.
22
- [Skill docs](docs/guides/agent-skill.md)
23
22
 
24
23
  ## Let your agent set it up
25
24
 
@@ -65,7 +64,7 @@ key. Restart your agent, or open a new terminal, to use the connection.
65
64
 
66
65
  Codex, Claude Code, opencode, Continue, Goose, Aider, GitHub Copilot CLI and Cline
67
66
  support saved setup. Cursor gets instructions for its settings screen; pi gets
68
- the agent skill. [Agent-specific setup](docs/guides/agent-setup.md).
67
+ the agent skill.
69
68
 
70
69
  Preview without changing files with `phantom setup --dry-run`. The Phantom CLI keeps backups;
71
70
  undo the changes with `phantom setup --restore`.
@@ -83,7 +82,6 @@ phantom codex
83
82
  Both setup options require **Node 24+** and your coding agent installed. Connect, fund your
84
83
  workspace, then launch **Codex** (`phantom codex`), **Claude Code** (`phantom claude`),
85
84
  **pi** (`phantom pi`), or **any other compatible agent**.
86
- [Set up other agents](docs/guides/agent-setup.md).
87
85
 
88
86
  Login opens a device approval page and saves a key privately. Funding opens
89
87
  checkout; check your workspace balance after payment. Agent runs use temporary
@@ -103,8 +101,6 @@ phantom fund --amount 10 --coin usdc
103
101
 
104
102
  Credit is available after the payment is verified. Check balance anytime with `phantom balance`.
105
103
 
106
- [Funding and payment details](docs/guides/buying-credit.md).
107
-
108
104
  ## Give each agent a budget
109
105
 
110
106
  Create a key with its own cap, sharing your workspace balance:
@@ -132,7 +128,6 @@ PHANTOM_KEY_NAME=coding phantom route test --table
132
128
 
133
129
  Prefer a specific model? Select it in your agent. Policies that apply only to
134
130
  `auto` leave explicit model choices alone.
135
- [Routing](docs/guides/routing.md) covers conditions, fallback and conversation affinity.
136
131
 
137
132
  `phantom-1` picks a tier for each request: economy, standard or frontier. Your key's
138
133
  lineup picks the model in each tier, and a fallback model to try when it fails:
@@ -199,13 +194,7 @@ Exit codes: 0 success, 1 error, 2 rejected key; agent runs return the agent's ex
199
194
  <details>
200
195
  <summary><strong>Advanced tools, integrations and legacy commands</strong></summary>
201
196
 
202
- `phantom --help` shows the main workflows. `phantom --help --all` and the
203
- [interface reference](docs/reference.md) cover every command.
204
-
205
- - [Signed receipts](docs/guides/receipts.md): check which model answered with `phantom verify`.
206
- - [Funding credit](docs/guides/buying-credit.md): `phantom fund` and checking payments.
207
- - [Saved secrets](docs/guides/saved-secrets.md): macOS Keychain or private files; guest recovery uses `phantom workspace export` and `phantom workspace import`.
208
- - [Key selection](docs/guides/which-key.md) and [API address](docs/guides/base-url.md): environment overrides and shell integration.
197
+ `phantom --help` shows the main workflows. `phantom --help --all` covers every command.
209
198
 
210
199
  The older `phantom key` commands still work.
211
200
  `phantom keys list` reads the workspace; `phantom keys saved` lists local copies.
@@ -221,11 +210,9 @@ npm ci
221
210
  npm run typecheck
222
211
  npm run build
223
212
  npm run coverage
224
- npm run docs
225
- node scripts/gen-docs.mjs --check
226
213
  ```
227
214
 
228
- See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
215
+ Report a security problem privately to support@phantom.codes, not in a public issue.
229
216
 
230
217
  </details>
231
218
 
@@ -26,7 +26,7 @@ export const AGENTS = {
26
26
  const vars = { ANTHROPIC_BASE_URL: proxy, CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: '1', ENABLE_TOOL_SEARCH: 'true' };
27
27
  const { ANTHROPIC_AUTH_TOKEN: _auth, ...rest } = env;
28
28
  return {
29
- args: ['--settings', JSON.stringify({ apiKeyHelper: 'printenv PHANTOM_AGENT_TOKEN', env: vars }), ...args],
29
+ args: ['--settings', JSON.stringify({ apiKeyHelper: 'node -p "process.env.PHANTOM_AGENT_TOKEN || \'\'"', env: vars }), ...args],
30
30
  env: { ...rest, ANTHROPIC_BASE_URL: proxy, ANTHROPIC_API_KEY: token, PHANTOM_AGENT_TOKEN: token },
31
31
  };
32
32
  },
package/dist/cli/help.mjs CHANGED
@@ -1,9 +1,8 @@
1
1
  import { DEFAULT_BASE_URL } from "../core/request.mjs";
2
2
  import { COMMANDS } from "./commands.mjs";
3
3
  /**
4
- * Every environment variable phantom reads. The Environment sections of HELP and
5
- * docs/reference.md are built from this list, and `npm run docs` fails if the
6
- * code reads a variable that is not here.
4
+ * Every environment variable phantom reads. The Environment section of HELP is
5
+ * built from this list.
7
6
  */
8
7
  export const ENV_VARS = [
9
8
  { name: 'PHANTOM_API_KEY', about: 'your Phantom AI API key (or save one with login)', secret: true },
package/dist/cli/run.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { VERSION } from "../core/version.mjs";
2
+ import { nodeTrustWarning } from "../core/node-trust.mjs";
2
3
  import { die } from "../core/errors.mjs";
3
4
  import { handleError, parseFlags } from "../core/helpers.mjs";
4
5
  import { resolveApiKey } from "../keys/saved.mjs";
@@ -12,12 +13,17 @@ import { LEGACY_SETUP_HINT, legacySetupFound } from "../setup/legacy.mjs";
12
13
  * @param argv The arguments after `phantom`.
13
14
  * @param env The environment to read settings from.
14
15
  * @param io Where output goes, and whether stderr is a terminal (for progress lines).
16
+ * @param node The Node version it runs on; one too old to reject forged
17
+ * eMudhra certificates gets a warning on stderr first.
15
18
  */
16
19
  export async function run(argv = process.argv.slice(2), env = process.env, io = {
17
20
  stdout: (s) => process.stdout.write(s),
18
21
  stderr: (s) => process.stderr.write(s),
19
22
  isTTY: Boolean(process.stderr.isTTY),
20
- }) {
23
+ }, node = process.versions.node) {
24
+ const unsafe = nodeTrustWarning(node);
25
+ if (unsafe)
26
+ io.stderr(unsafe);
21
27
  if (argv[0] === '--version' || argv[0] === '-v') {
22
28
  io.stdout(VERSION + '\n');
23
29
  return 0;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Node before 24.22.1 still trusts certificates chained to the eMudhra CA
3
+ * keys found guessable in October 2026, so on a network someone controls, a
4
+ * forged phantom.codes could read the key this CLI sends. 24.22.1 rejects
5
+ * those chains ("tls: reject chains containing blocked public keys").
6
+ */
7
+ /** The first Node 24 that rejects the compromised chains. */
8
+ const FIXED = [24, 22, 1];
9
+ /**
10
+ * A warning for stderr when `version` (`process.versions.node`) is a Node 24
11
+ * older than 24.22.1, or null. Other majors get none: the CLI needs 24 or newer,
12
+ * and the newer lines carry the fix.
13
+ */
14
+ export function nodeTrustWarning(version) {
15
+ const [major, minor, patch] = version.split('.').map(Number);
16
+ if (major !== FIXED[0])
17
+ return null;
18
+ if (minor > FIXED[1] || (minor === FIXED[1] && patch >= FIXED[2]))
19
+ return null;
20
+ return (`phantom: Node ${version} still trusts certificates from eMudhra's compromised CA keys, ` +
21
+ `so on an untrusted network a forged phantom.codes could read your key. ` +
22
+ `Update to Node ${FIXED.join('.')} or newer (nvm install 24), or reinstall with ` +
23
+ `curl -fsSL https://phantom.codes/install | sh, which brings its own.\n`);
24
+ }
@@ -1,2 +1,2 @@
1
1
  /** This release of phantom. `npm version` keeps it equal to the version in package.json. */
2
- export const VERSION = '1.2.1';
2
+ export const VERSION = '1.2.2';
@@ -114,6 +114,13 @@ export function applySetup(env, plan) {
114
114
  from = `${f.file}.phantom.bak`;
115
115
  writeFileSync(from, f.before, { mode: 0o600 });
116
116
  }
117
+ if (f.before !== null && (f.agent === 'claude' || f.file.endsWith('settings.json'))) {
118
+ const standardBak = `${f.file}.bak`;
119
+ if (!existsSync(standardBak)) {
120
+ const content = from && existsSync(from) ? readFileSync(from, 'utf-8') : f.before;
121
+ writeFileSync(standardBak, content, { mode: 0o600 });
122
+ }
123
+ }
117
124
  if (from)
118
125
  backup = from;
119
126
  if (f.after === null && from === null)
@@ -7,7 +7,7 @@ import { stateDir } from "../core/state.mjs";
7
7
  import { writeSecret, readSecret, removeSecret, keychainOn } from "../core/secrets.mjs";
8
8
  import { keyCommand } from "../keys/command.mjs";
9
9
  import { die } from "../core/errors.mjs";
10
- import { openUrl } from "../core/helpers.mjs";
10
+ import { openUrl, flagNum } from "../core/helpers.mjs";
11
11
  import { keyPath, namedKeyPath, saveApiKey, saveNamedKey } from "../keys/saved.mjs";
12
12
  const credentialPath = (env) => path.join(stateDir(env), 'workspace');
13
13
  const base = (input) => httpsOnly(input.baseUrl || input.env.PHANTOM_BASE_URL || DEFAULT_BASE_URL).replace(/\/$/, '');
@@ -184,7 +184,12 @@ export async function workspaceCommand(input) {
184
184
  die('--pay has been removed. Use --card or --coin.');
185
185
  if (!flags.card && !flags.coin)
186
186
  die('fund requires --card or --coin.');
187
- const result = await wire(saved.base_url, 'POST', endpoint + '/fund', { method: flags.card ? 'card' : 'crypto', amount_usd: Number(flags.amount), coin: flags.coin ?? 'usdc' }, saved.management_credential);
187
+ const amount = flagNum(flags, 'amount');
188
+ if (amount === undefined)
189
+ die('fund requires --amount <USD>.');
190
+ if (amount <= 0)
191
+ die('--amount must be greater than 0.');
192
+ const result = await wire(saved.base_url, 'POST', endpoint + '/fund', { method: flags.card ? 'card' : 'crypto', amount_usd: amount, coin: flags.coin ?? 'usdc' }, saved.management_credential);
188
193
  io.stdout(JSON.stringify(result) + '\n');
189
194
  if (result.checkout_url && !flags['no-browser'])
190
195
  openUrl(result.checkout_url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phantomcodes/cli",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "AI inference for your coding agents. Route models, control spending, and fund your workspace.",
5
5
  "keywords": [
6
6
  "phantom",
@@ -50,7 +50,6 @@
50
50
  "build": "tsc -p .",
51
51
  "coverage": "vitest run --coverage",
52
52
  "dev": "node src/phantom.mts",
53
- "docs": "node scripts/gen-docs.mjs",
54
53
  "prepare": "npm run build",
55
54
  "test": "vitest run",
56
55
  "typecheck": "tsc -p . --noEmit",
@@ -69,7 +68,6 @@
69
68
  "devDependencies": {
70
69
  "@types/node": "^24.19.1",
71
70
  "@vitest/coverage-v8": "^5.0.3",
72
- "typedoc": "^0.28.20",
73
71
  "typescript": "^6.0.3",
74
72
  "vitest": "^5.0.2"
75
73
  },