@shipd-ai/cli 0.1.2 → 0.1.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.
package/README.md CHANGED
@@ -22,9 +22,10 @@ shipd quests # which quests this CLI can talk to
22
22
  ```
23
23
 
24
24
  `login` opens the token page in your browser; paste the token back into the
25
- terminal. It is stored at `~/.shipd/credentials.json` (mode 0600). The token is
26
- personal: never commit it, never pass it on a command line. `shipd logout`
27
- deletes it.
25
+ terminal. `--no-browser` prints the page URL instead of opening it. The token is
26
+ stored at `~/.shipd/credentials.json` (mode 0600). It is personal: never commit
27
+ or share it. Typing `--token` by hand leaves it in your shell history, so prefer
28
+ the prompt there. `shipd logout` deletes it.
28
29
 
29
30
  ## Three input modes
30
31
 
@@ -3,9 +3,11 @@ export declare const login: import("citty").CommandDef<{
3
3
  readonly type: "string";
4
4
  readonly description: "Paste the token directly instead of prompting";
5
5
  };
6
- readonly "no-browser": {
6
+ readonly browser: {
7
7
  readonly type: "boolean";
8
- readonly description: "Print the login URL instead of opening it";
8
+ readonly default: true;
9
+ readonly description: "Open the login page in a browser";
10
+ readonly negativeDescription: "Print the login URL instead of opening a browser";
9
11
  };
10
12
  readonly quest: {
11
13
  readonly type: "string";
@@ -18,7 +18,18 @@ function openBrowser(url) {
18
18
  function prompt(question) {
19
19
  const rl = createInterface({ input: process.stdin, output: process.stdout });
20
20
  return new Promise((resolve) => {
21
+ let answered = false;
22
+ // stdin can end before an answer (a non-interactive shell with nothing
23
+ // piped in). Resolve empty so login fails with a usage error; otherwise
24
+ // the process drains and exits 0 with nothing saved.
25
+ rl.on("close", () => {
26
+ if (answered)
27
+ return;
28
+ process.stdout.write("\n");
29
+ resolve("");
30
+ });
21
31
  rl.question(question, (answer) => {
32
+ answered = true;
22
33
  rl.close();
23
34
  resolve(answer.trim());
24
35
  });
@@ -35,23 +46,31 @@ export const login = defineCommand({
35
46
  args: {
36
47
  ...questArg,
37
48
  token: { type: "string", description: "Paste the token directly instead of prompting" },
38
- "no-browser": { type: "boolean", description: "Print the login URL instead of opening it" },
49
+ // Named `browser` with a true default because citty parses `--no-browser`
50
+ // as browser=false; an arg literally named "no-browser" is never set.
51
+ browser: {
52
+ type: "boolean",
53
+ default: true,
54
+ description: "Open the login page in a browser",
55
+ negativeDescription: "Print the login URL instead of opening a browser",
56
+ },
39
57
  },
40
58
  run: async ({ args }) => {
41
59
  const authUrl = args.quest ? questAuthUrl(args.quest) : platformAuthUrl();
42
60
  let token = args.token?.trim();
43
61
  if (!token) {
44
- if (args["no-browser"]) {
45
- console.log(`\n Open this page and copy the token:\n ${authUrl}\n`);
46
- }
47
- else {
62
+ if (args.browser) {
48
63
  console.log(`\n Opening browser...\n ${authUrl}\n`);
49
64
  openBrowser(authUrl);
50
65
  }
66
+ else {
67
+ console.log(`\n Open this page and copy the token:\n ${authUrl}\n`);
68
+ }
51
69
  token = await prompt(" Paste your token: ");
52
70
  }
53
- if (!token)
54
- throw new UsageError("No token provided.");
71
+ if (!token) {
72
+ throw new UsageError("No token provided. Pass it with --token, or paste it at the prompt in an interactive terminal.");
73
+ }
55
74
  const identity = saveCredentials(token);
56
75
  const expiresAt = new Date(identity.exp * 1000).toLocaleDateString();
57
76
  console.log(`\n Logged in as ${identity.name || identity.email || identity.sub}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipd-ai/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Shipd admin CLI — run quest checks on Harbor tasks or existing tasks",
5
5
  "type": "module",
6
6
  "homepage": "https://shipd.ai",