getmyenv 0.9.6 → 0.9.8

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
@@ -35,7 +35,7 @@ Prompts and status lines go to stderr, so `export --stdout > .env` stays clean.
35
35
  Commit `.getmyenv/project.json`. `token.json` and `claim.json` stay local and are gitignored.
36
36
 
37
37
  <!-- cli:help -->
38
- Output of `npx getmyenv --help` (0.9.6):
38
+ Output of `npx getmyenv --help` (0.9.8):
39
39
 
40
40
  ```text
41
41
  Usage: getmyenv [options] [command]
@@ -13,4 +13,6 @@ export declare function createGuestProject(opts?: GuestOpts): Promise<{
13
13
  projectName: string;
14
14
  guestExpiresAt: string;
15
15
  }>;
16
+ /** The steps printed after guest, in plain text. Runtime first, export only as an alternative. */
17
+ export declare function guestNextLines(): string[];
16
18
  export declare function guestCommand(opts?: GuestOpts): Promise<void>;
@@ -2,6 +2,7 @@ import { randomBytes } from "node:crypto";
2
2
  import path from "node:path";
3
3
  import pc from "picocolors";
4
4
  import { createUserKeys, wipe } from "@getmyenv/crypto";
5
+ import { FACTS } from "@getmyenv/shared";
5
6
  import { ensureGitignoreHasEnv, readProjectMeta, writeClaimFile, writeProjectMeta, } from "../lib/fs.js";
6
7
  import { readFolderToken, writeFolderToken } from "../lib/credentials.js";
7
8
  import { ApiClient, resolveApiUrl } from "../lib/api.js";
@@ -56,17 +57,38 @@ export async function createGuestProject(opts = {}) {
56
57
  console.error(` ${pc.dim("Guest passphrase:")} ${passphrase}\n`);
57
58
  }
58
59
  else {
59
- console.error(pc.dim("claim.json holds the claim code and guest passphrase. It is the only copy. Keep it private.\n"));
60
+ console.error(pc.dim(`claim.json holds the claim code and guest passphrase. It is the only copy. Keep it private. ${FACTS.claimLoss}\n`));
60
61
  }
61
62
  return created;
62
63
  }
64
+ /** The steps printed after guest, in plain text. Runtime first, export only as an alternative. */
65
+ export function guestNextLines() {
66
+ return [
67
+ "Next:",
68
+ " npx getmyenv import # from .env",
69
+ " npx getmyenv set API_KEY # or one at a time",
70
+ " npx getmyenv run -- npm start # your start command, no .env file on disk",
71
+ " npx getmyenv claim # keep it, with an account",
72
+ "",
73
+ "Only if a tool needs a .env file:",
74
+ " npx getmyenv export # writes a plaintext .env",
75
+ "",
76
+ `For AI agents: ${FACTS.agentRunOrExport}`,
77
+ ];
78
+ }
79
+ function colorNextLine(line) {
80
+ const hash = line.indexOf("# ");
81
+ if (line.startsWith(" npx ") && hash > 0) {
82
+ return pc.cyan(line.slice(0, hash)) + pc.dim(line.slice(hash));
83
+ }
84
+ if (line.startsWith("For AI agents:"))
85
+ return pc.dim(line);
86
+ return line;
87
+ }
63
88
  export async function guestCommand(opts = {}) {
64
89
  console.error(pc.bold("\ngetmyenv guest\n"));
65
90
  await createGuestProject(opts);
66
- console.error("Next:");
67
- console.error(pc.cyan(" npx getmyenv import ") + pc.dim("# from .env"));
68
- console.error(pc.cyan(" npx getmyenv set API_KEY ") + pc.dim("# or one at a time"));
69
- console.error(pc.cyan(" npx getmyenv run -- npm start ") + pc.dim("# your start command"));
70
- console.error(pc.cyan(" npx getmyenv claim ") + pc.dim("# keep it, with an account"));
91
+ for (const line of guestNextLines())
92
+ console.error(colorNextLine(line));
71
93
  console.error("");
72
94
  }
package/dist/lib/api.d.ts CHANGED
@@ -5,6 +5,7 @@ import type { NewMachinePolicy } from "@getmyenv/shared";
5
5
  /** From package.json, which ships with the package: src/lib and dist/lib are both two levels down. */
6
6
  export declare const CLI_VERSION: string;
7
7
  export { isDefaultApiUrl, parseSafeUrl } from "./api-url.js";
8
+ export declare const API_TIMEOUT_MS = 30000;
8
9
  export type Protection = "open" | "locked";
9
10
  export type ValueStatus = "set" | "expiring_soon" | "expired" | "missing";
10
11
  export type ContextInfo = {
package/dist/lib/api.js CHANGED
@@ -7,6 +7,7 @@ import { pickApiUrl } from "./api-url.js";
7
7
  /** From package.json, which ships with the package: src/lib and dist/lib are both two levels down. */
8
8
  export const CLI_VERSION = createRequire(import.meta.url)("../../package.json").version;
9
9
  export { isDefaultApiUrl, parseSafeUrl } from "./api-url.js";
10
+ export const API_TIMEOUT_MS = 30_000;
10
11
  export class ApiError extends Error {
11
12
  status;
12
13
  constructor(message, status) {
@@ -49,10 +50,11 @@ export class ApiClient {
49
50
  body: body === undefined ? undefined : JSON.stringify(body),
50
51
  // The token must never follow a redirect to another host.
51
52
  redirect: "error",
53
+ signal: AbortSignal.timeout(API_TIMEOUT_MS),
52
54
  });
53
55
  }
54
- catch {
55
- throw new ApiError(`Could not reach ${this.baseUrl}`, 0);
56
+ catch (err) {
57
+ throw networkError(err, `Could not reach ${this.baseUrl}`);
56
58
  }
57
59
  if (!res.ok) {
58
60
  let serverMessage;
@@ -70,8 +72,19 @@ export class ApiClient {
70
72
  }
71
73
  if (res.status === 204)
72
74
  return undefined;
73
- return (await res.json());
75
+ try {
76
+ return (await res.json());
77
+ }
78
+ catch (err) {
79
+ throw networkError(err, `The connection to ${this.baseUrl} closed before the answer arrived. Try again in a moment.`);
80
+ }
81
+ }
82
+ }
83
+ function networkError(err, fallback) {
84
+ if (err instanceof Error && err.name === "TimeoutError") {
85
+ return new ApiError(`The getmyenv server did not answer within ${API_TIMEOUT_MS / 1000} seconds. Try again in a moment.`, 0);
74
86
  }
87
+ return new ApiError(fallback, 0);
75
88
  }
76
89
  /**
77
90
  * Env / folder token only, no prompt. A server token is `auth.privateKey`:
@@ -35,8 +35,8 @@ export async function fetchWhoami(client) {
35
35
  }
36
36
  if (err instanceof ApiError && err.status === 401) {
37
37
  throw new Error(process.env.GETMYENV_TOKEN?.trim()
38
- ? `${err.message}\nGETMYENV_TOKEN no longer works. Create a new token in Project settings, Tokens.`
39
- : `${err.message}\nThis folder's token no longer works. Run: npx getmyenv unlink, then npx getmyenv`);
38
+ ? "GETMYENV_TOKEN no longer works. It was revoked or expired. Create a new token in Project settings, Tokens."
39
+ : "This folder's token no longer works. It was revoked or expired. Run: npx getmyenv unlink, then npx getmyenv");
40
40
  }
41
41
  throw err;
42
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getmyenv",
3
- "version": "0.9.6",
3
+ "version": "0.9.8",
4
4
  "description": "CLI for getmyenv, an encrypted store for environment variables and secrets. We store ciphertext only.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@getmyenv/crypto": "0.4.1",
37
- "@getmyenv/shared": "0.8.2",
37
+ "@getmyenv/shared": "0.8.4",
38
38
  "@napi-rs/keyring": "^2.1.0",
39
39
  "commander": "^15.0.0",
40
40
  "dotenv": "^16.4.7",