@tailglow/cli 0.9.6 → 0.9.15

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/src/config.js CHANGED
@@ -71,6 +71,7 @@ export function readCliVersion() {
71
71
  version_read ??= fs
72
72
  .readFile(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8")
73
73
  .then((manifest) => String(JSON.parse(manifest).version));
74
+
74
75
  return version_read;
75
76
  }
76
77
 
@@ -87,7 +88,9 @@ export function readCliVersion() {
87
88
  export async function loadConfig(path = CLI_CONFIGS.CONFIG_PATH) {
88
89
  try {
89
90
  const parsed = JSON.parse(await fs.readFile(path, "utf-8"));
91
+
90
92
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
93
+
91
94
  return parsed;
92
95
  } catch {
93
96
  return {};
@@ -107,15 +110,18 @@ export async function loadConfig(path = CLI_CONFIGS.CONFIG_PATH) {
107
110
  export async function loadConfigForWrite(path = CLI_CONFIGS.CONFIG_PATH) {
108
111
  /** @type {string} */
109
112
  let contents;
113
+
110
114
  try {
111
115
  contents = await fs.readFile(path, "utf-8");
112
116
  } catch (err) {
113
117
  if (/** @type {NodeJS.ErrnoException} */ (err).code === "ENOENT") return {};
118
+
114
119
  throw new Error(`Cannot read ${path}: ${err instanceof Error ? err.message : String(err)}`);
115
120
  }
116
121
 
117
122
  /** @type {unknown} */
118
123
  let parsed;
124
+
119
125
  try {
120
126
  parsed = JSON.parse(contents);
121
127
  } catch {
@@ -148,15 +154,18 @@ export async function loadConfigForWrite(path = CLI_CONFIGS.CONFIG_PATH) {
148
154
  */
149
155
  export async function saveConfig(values, path = CLI_CONFIGS.CONFIG_PATH) {
150
156
  const directory = dirname(path);
157
+
151
158
  await fs.mkdir(directory, { recursive: true, mode: CLI_CONFIGS.DIRECTORY_MODE });
152
159
  await fs.chmod(directory, CLI_CONFIGS.DIRECTORY_MODE);
153
160
 
154
161
  const staged = `${path}.${process.pid}.${Date.now()}.tmp`;
162
+
155
163
  try {
156
164
  await fs.writeFile(staged, `${JSON.stringify(values, null, 2)}\n`, {
157
165
  mode: CLI_CONFIGS.FILE_MODE,
158
166
  flag: "wx"
159
167
  });
168
+
160
169
  await fs.chmod(staged, CLI_CONFIGS.FILE_MODE);
161
170
  await fs.rename(staged, path);
162
171
  } catch (err) {
@@ -197,9 +206,11 @@ export async function withConfigLock(
197
206
  ) {
198
207
  const lock_path = `${path}.lock`;
199
208
  const token = `${process.pid}:${randomUUID()}`;
209
+
200
210
  await fs.mkdir(dirname(path), { recursive: true, mode: CLI_CONFIGS.DIRECTORY_MODE });
201
211
 
202
212
  const started_at = Date.now();
213
+
203
214
  while (true) {
204
215
  try {
205
216
  await fs.writeFile(lock_path, token, { flag: "wx", mode: CLI_CONFIGS.FILE_MODE });
@@ -212,6 +223,7 @@ export async function withConfigLock(
212
223
  `If no tglow is running, delete ${lock_path} and try again.`
213
224
  );
214
225
  }
226
+
215
227
  await new Promise((resolve) => setTimeout(resolve, CLI_CONFIGS.LOCK_RETRY_MS));
216
228
  }
217
229
  }
@@ -233,6 +245,7 @@ export async function withConfigLock(
233
245
  export async function updateConfig(mutate, path = CLI_CONFIGS.CONFIG_PATH) {
234
246
  return withConfigLock(async () => {
235
247
  const next = mutate(await loadConfigForWrite(path));
248
+
236
249
  await saveConfig(next, path);
237
250
  return next;
238
251
  }, path);
@@ -244,6 +257,7 @@ export async function updateConfig(mutate, path = CLI_CONFIGS.CONFIG_PATH) {
244
257
  */
245
258
  export function redactSetting(key, value) {
246
259
  if (!SECRET_KEYS.has(key)) return value;
260
+
247
261
  return value.length <= 8 ? "********" : `${value.slice(0, 4)}...${value.slice(-4)}`;
248
262
  }
249
263
 
@@ -260,12 +274,17 @@ function isNonEmptyString(value) {
260
274
  */
261
275
  export function readStoredLogin(config) {
262
276
  const login = config.login;
277
+
263
278
  if (!login || typeof login !== "object" || Array.isArray(login)) return null;
279
+
264
280
  const fields = /** @type {Record<string, unknown>} */ (login);
281
+
265
282
  if (fields.type !== "session" && fields.type !== "api_key") return null;
283
+
266
284
  for (const name of ["secret", "id", "api_url", "team_id", "user_id", "email"]) {
267
285
  if (!isNonEmptyString(fields[name])) return null;
268
286
  }
287
+
269
288
  return /** @type {import("./types.d.ts").StoredLogin} */ (login);
270
289
  }
271
290
 
@@ -277,13 +296,19 @@ export function readStoredLogin(config) {
277
296
  */
278
297
  export function readPendingLogin(config) {
279
298
  const pending = config.pending_login;
299
+
280
300
  if (!pending || typeof pending !== "object" || Array.isArray(pending)) return null;
301
+
281
302
  const fields = /** @type {Record<string, unknown>} */ (pending);
303
+
282
304
  if (fields.method !== "account" && fields.method !== "api_key") return null;
305
+
283
306
  for (const name of ["id", "poll_secret", "user_code", "verification_url", "api_url"]) {
284
307
  if (!isNonEmptyString(fields[name])) return null;
285
308
  }
309
+
286
310
  if (typeof fields.started_at !== "number") return null;
311
+
287
312
  return /** @type {import("./types.d.ts").PendingLogin} */ (pending);
288
313
  }
289
314
 
@@ -304,12 +329,15 @@ export function readPendingLogin(config) {
304
329
  */
305
330
  export function resolveSetting(name, { flags = {}, env = process.env, config = {} } = {}) {
306
331
  const flag = flags[name.replaceAll("_", "-")] ?? flags[name];
332
+
307
333
  if (typeof flag === "string" && flag.length > 0) return flag;
308
334
 
309
335
  const from_env = env[`TAILGLOW_${name.toUpperCase()}`];
336
+
310
337
  if (typeof from_env === "string" && from_env.length > 0) return from_env;
311
338
 
312
339
  const stored = config[name];
340
+
313
341
  return typeof stored === "string" && stored.length > 0 ? stored : null;
314
342
  }
315
343
 
@@ -355,18 +383,22 @@ export function isSameApiUrl(first, second) {
355
383
  */
356
384
  export function resolveCredential({ flags = {}, env = process.env, config = {} } = {}) {
357
385
  const flag = flags["api-key"];
386
+
358
387
  if (isNonEmptyString(flag)) return { secret: String(flag), source: "flag", login: null };
359
388
 
360
389
  const from_env = env.TAILGLOW_API_KEY;
390
+
361
391
  if (isNonEmptyString(from_env)) return { secret: String(from_env), source: "env", login: null };
362
392
 
363
393
  const login = readStoredLogin(config);
364
394
  const api_url = resolveApiUrl({ flags, env, config });
395
+
365
396
  if (login && isSameApiUrl(login.api_url, api_url)) {
366
397
  return { secret: login.secret, source: "login", login };
367
398
  }
368
399
 
369
400
  const stored = config.api_key;
401
+
370
402
  if (isNonEmptyString(stored)) return { secret: String(stored), source: "config", login: null };
371
403
 
372
404
  if (login) {
@@ -376,6 +408,7 @@ export function resolveCredential({ flags = {}, env = process.env, config = {} }
376
408
  `${api_url}\`.`
377
409
  );
378
410
  }
411
+
379
412
  throw new Error(
380
413
  "Not signed in. Run `tglow login`, or set TAILGLOW_API_KEY to an API key.\n" +
381
414
  "Create a key in the dashboard under Team settings."
@@ -28,11 +28,13 @@ export function toDocsPath(reference) {
28
28
  /** @param {string} path */
29
29
  function findSimilarPaths(path) {
30
30
  const name = (path.split("/").pop() ?? path).toLowerCase();
31
+
31
32
  if (!name) return [];
32
33
 
33
34
  return Object.keys(DOCS_PAGES)
34
35
  .filter((candidate) => {
35
36
  const candidate_name = (candidate.split("/").pop() ?? candidate).toLowerCase();
37
+
36
38
  return candidate_name.includes(name) || name.includes(candidate_name);
37
39
  })
38
40
  .slice(0, 5);
@@ -70,12 +72,14 @@ export function runDocsCommand(words, flags) {
70
72
  const json = Boolean(flags.json);
71
73
 
72
74
  const unknown = Object.keys(flags).filter((flag) => !RESERVED_FLAGS.has(flag));
75
+
73
76
  if (unknown.length > 0) {
74
77
  return fail(
75
78
  `Unknown flag${unknown.length === 1 ? "" : "s"}: ${unknown.map((flag) => `--${flag}`).join(", ")}.`,
76
79
  json
77
80
  );
78
81
  }
82
+
79
83
  if (rest.length > 0) {
80
84
  return fail(`Unexpected argument${rest.length === 1 ? "" : "s"}: ${rest.join(", ")}.`, json);
81
85
  }
@@ -129,10 +133,12 @@ export function runDocsCommand(words, flags) {
129
133
  similar.length > 0
130
134
  ? ` Did you mean ${similar.map((candidate) => `\`${candidate}\``).join(", ")}?`
131
135
  : "";
136
+
132
137
  return fail(`No docs page "${reference}".${hint} Run \`tglow docs\` for the index.`, json);
133
138
  }
134
139
 
135
140
  const url = `${CLI_CONFIGS.DOCS_URL}/${path}.md`;
141
+
136
142
  if (json) return { output: JSON.stringify({ path, url, content }, null, 2), code: 0 };
137
143
 
138
144
  return { output: `${content.trimEnd()}\n\nLatest version online: ${url}`, code: 0 };