@phantomcodes/cli 1.2.2 → 1.2.3

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.
@@ -138,6 +138,7 @@ export const COMMANDS = [
138
138
  [--yes] don't ask
139
139
  [--no-browser] print the sign-in link instead of opening it
140
140
  [--dry-run] show the changes, write nothing
141
+ [--json] print JSON in a terminal too (a pipe always gets JSON)
141
142
  [--model <id>] the model each agent starts on (default ${SETUP_MODEL})
142
143
  setup --agent <name> install the skill for one agent, without asking:
143
144
  ${orList(SETUP_NAMES)}
package/dist/cli/run.mjs CHANGED
@@ -12,7 +12,8 @@ import { LEGACY_SETUP_HINT, legacySetupFound } from "../setup/legacy.mjs";
12
12
  * fails. Errors go to `io.stderr` as JSON. It never calls `process.exit`.
13
13
  * @param argv The arguments after `phantom`.
14
14
  * @param env The environment to read settings from.
15
- * @param io Where output goes, and whether stderr is a terminal (for progress lines).
15
+ * @param io Where output goes, whether stderr is a terminal (for progress lines),
16
+ * and whether stdout is one (setup prints a summary there instead of JSON).
16
17
  * @param node The Node version it runs on; one too old to reject forged
17
18
  * eMudhra certificates gets a warning on stderr first.
18
19
  */
@@ -20,6 +21,7 @@ export async function run(argv = process.argv.slice(2), env = process.env, io =
20
21
  stdout: (s) => process.stdout.write(s),
21
22
  stderr: (s) => process.stderr.write(s),
22
23
  isTTY: Boolean(process.stderr.isTTY),
24
+ stdoutIsTTY: Boolean(process.stdout.isTTY),
23
25
  }, node = process.versions.node) {
24
26
  const unsafe = nodeTrustWarning(node);
25
27
  if (unsafe)
@@ -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.2';
2
+ export const VERSION = '1.2.3';
@@ -43,6 +43,10 @@ export function tableSetup(d) {
43
43
  lines.push(n);
44
44
  if (r.applied === false)
45
45
  lines.push('Nothing was changed.');
46
+ // Agents that read the key from the shell only see it in a terminal opened after setup.
47
+ else if (!r.dry_run && (r.files ?? []).some((f) => f.agent === 'shell')) {
48
+ lines.push('', 'Open a new terminal before you start your agents, so they can read your Phantom AI key.');
49
+ }
46
50
  return lines.join('\n');
47
51
  }
48
52
  /** `--table` output for `setup --restore`. */
@@ -56,7 +60,9 @@ export function tableRestore(d) {
56
60
  }
57
61
  /** `phantom setup`: plans, previews, asks, applies or restores agent setup. Plain setup with no key yet signs in first when it runs in a terminal. Run by `run()`. */
58
62
  export async function setupCommand(input) {
59
- const { env, io, flags, table } = input;
63
+ const { env, io, flags } = input;
64
+ // A person at a terminal gets the short summary; a pipe, or --json, gets JSON.
65
+ const table = input.table || (Boolean(io.stdoutIsTTY) && !flags['json']);
60
66
  const agent = flags['agent'];
61
67
  if (agent === true)
62
68
  die(`--agent requires ${SETUP_NAMES.join(', ')} or all`);
@@ -80,7 +86,7 @@ export async function setupCommand(input) {
80
86
  // With no key yet, sign in first, as phantom claude does, so the agents
81
87
  // get one in this run. Its words go to stderr; stdout stays setup's result.
82
88
  if (guided && !dryRun && io.isTTY && !resolveApiKey(env)) {
83
- await workspaceLogin({ ...input, io: { ...io, stdout: io.stderr }, flags: flags['no-browser'] ? { 'no-browser': true } : {} });
89
+ await workspaceLogin({ ...input, io: { ...io, stdout: io.stderr }, flags: flags['no-browser'] ? { 'no-browser': true } : {} }, { inSetup: true });
84
90
  }
85
91
  const plan = planSetup(env, {
86
92
  agent,
@@ -36,8 +36,12 @@ export async function workspaceGet(input, path) {
36
36
  const saved = credentials(input);
37
37
  return wire(saved.base_url, 'GET', '/workspaces/' + encodeURIComponent(saved.workspace_id) + path, undefined, saved.management_credential);
38
38
  }
39
- /** Device authorization for accounts or guests, saving keys locally without copying a secret through the browser. */
40
- export async function workspaceLogin(input) {
39
+ /**
40
+ * Device authorization for accounts or guests, saving keys locally without copying a secret through the browser.
41
+ * With `inSetup`, `phantom setup` carries on once it is signed in, so it says that instead of printing the
42
+ * saved key's details and the next commands.
43
+ */
44
+ export async function workspaceLogin(input, opts = {}) {
41
45
  const { env, io, flags } = input;
42
46
  if (flags.save === true)
43
47
  die('--save requires a key name.');
@@ -49,7 +53,9 @@ export async function workspaceLogin(input) {
49
53
  const start = await wire(url, 'POST', '/auth/device', { client_name: `phantom on ${hostname()}` });
50
54
  if (new URL(start.verification_uri_complete).origin !== new URL(url).origin)
51
55
  die('The approval URL belongs to another site.', 'device_origin');
52
- io.stderr(`Open ${start.verification_uri_complete}\nConfirm code ${start.user_code}\n`);
56
+ io.stderr(`Sign in at ${start.verification_uri_complete}\n` +
57
+ `Check that the page shows code ${start.user_code}, then approve.\n` +
58
+ 'Waiting for you to approve...\n');
53
59
  if (!flags['no-browser'])
54
60
  openUrl(start.verification_uri_complete);
55
61
  let interval = Math.max(1, start.interval) * 1000;
@@ -82,6 +88,10 @@ export async function workspaceLogin(input) {
82
88
  io.stderr('phantom could not save the connection. Sign in or restore your workspace to revoke the device key and reconnect.\n');
83
89
  throw error;
84
90
  }
91
+ if (opts.inSetup) {
92
+ io.stderr('Signed in. Setting up your agents next.\n\n');
93
+ return 0;
94
+ }
85
95
  io.stdout(JSON.stringify({ saved_as: name, key_name: token.key_name, workspace_id: token.workspace_id, storage: keychainOn(env) ? 'macOS Keychain' : 'private files' }) + '\n');
86
96
  io.stderr('Ready. Run phantom claude, phantom codex or phantom pi. Guests can export recovery with phantom workspace export --file <path>.\n');
87
97
  return 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phantomcodes/cli",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "AI inference for your coding agents. Route models, control spending, and fund your workspace.",
5
5
  "keywords": [
6
6
  "phantom",