nerdsystem 0.0.29 → 0.0.30

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/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import { findProjectRoot } from "./project.js";
25
25
  import { CliError, log } from "./util.js";
26
26
  const HELP = `nerd: start, check, and deploy a web app on nerdsystem
27
27
 
28
- nerd login [--email you@x.com] Sign in or sign up: prints a link to approve in the browser (--email sends it; --token nsk_... for scripts)
28
+ nerd login [--email you@x.com] Sign in or sign up: prints a link to approve in the browser (--email sends it; --token nsk_... for scripts; --ref <code> records the partner who sent you)
29
29
  nerd whoami Show the current user and orgs
30
30
  nerd doctor Check Node, pnpm, git, login, and the project
31
31
  nerd init <dir> [--org] [--name] [--describe "what it does"] Create an app and a project for it
@@ -100,7 +100,7 @@ export async function main(argv) {
100
100
  const parse = (options, allowPositionals = true) => parseArgs({ args: rest, options: { json: BOOL, ...options }, allowPositionals, strict: cmd !== "dev" });
101
101
  switch (cmd) {
102
102
  case "login": {
103
- const { values } = parse({ token: STR, "api-url": STR, email: STR });
103
+ const { values } = parse({ token: STR, "api-url": STR, email: STR, ref: STR });
104
104
  return login(values);
105
105
  }
106
106
  case "doctor": {
@@ -192,4 +192,4 @@ export async function init(dir, args) {
192
192
  log.ok(`Ready. Next:\n cd ${dir}\n pnpm install\n nerd dev\n\nThen open the folder in your coding agent and ask it to build the first feature; the nerdsystem skill walks it through.\nDeploys go to ${project.url}`);
193
193
  }
194
194
  // Kept in sync with packages/sdk/package.json by the release script.
195
- const SDK_VERSION = "0.0.29";
195
+ const SDK_VERSION = "0.0.30";
@@ -3,14 +3,19 @@
3
3
  * dashboard (or from the email when --email is given), and the token arrives here. `--token` still
4
4
  * works for scripts. A pending login is remembered, so running `nerd login` again after approving
5
5
  * picks it up instead of starting over. `--email` with a different address than the pending login's
6
- * starts a fresh one, so a typo does not lock the person out until the code expires.
6
+ * starts a fresh one, so a typo does not lock the person out until the code expires. `--ref` is the code
7
+ * of the partner who sent the person: it is recorded on the account this sign-in creates, and a code that
8
+ * is not one is left out with a line saying so, because nobody should fail to sign in over it.
7
9
  */
8
10
  export declare function login(args: {
9
11
  token?: string;
10
12
  "api-url"?: string;
11
13
  email?: string;
14
+ ref?: string;
12
15
  json?: boolean;
13
16
  }): Promise<void>;
17
+ /** The same shape the platform accepts (`lib/partners.ts` in the API); anything else is no code. */
18
+ export declare function partnerRef(value: string | undefined): string | null;
14
19
  export declare function whoami(opts: {
15
20
  json: boolean;
16
21
  }): Promise<void>;
@@ -9,7 +9,9 @@ const WAIT_S = 90;
9
9
  * dashboard (or from the email when --email is given), and the token arrives here. `--token` still
10
10
  * works for scripts. A pending login is remembered, so running `nerd login` again after approving
11
11
  * picks it up instead of starting over. `--email` with a different address than the pending login's
12
- * starts a fresh one, so a typo does not lock the person out until the code expires.
12
+ * starts a fresh one, so a typo does not lock the person out until the code expires. `--ref` is the code
13
+ * of the partner who sent the person: it is recorded on the account this sign-in creates, and a code that
14
+ * is not one is left out with a line saying so, because nobody should fail to sign in over it.
13
15
  */
14
16
  export async function login(args) {
15
17
  const cfg = loadConfig();
@@ -25,7 +27,10 @@ export async function login(args) {
25
27
  let pending = resumable ? saved : undefined;
26
28
  if (!pending) {
27
29
  const client = `nerd CLI on ${os.hostname()} (${os.platform()})`;
28
- const start = await anon.post("/session/device", { client, ...(email ? { email } : {}) });
30
+ const ref = partnerRef(args.ref);
31
+ if (args.ref && !ref && !args.json)
32
+ log.warn("Leaving out --ref: a partner's code is 2 to 32 lowercase letters, digits, or hyphens. Signing in works the same without it.");
33
+ const start = await anon.post("/session/device", { client, ...(email ? { email } : {}), ...(ref ? { ref } : {}) });
29
34
  pending = { apiUrl, deviceCode: start.deviceCode, userCode: start.userCode, verificationUrl: start.verificationUrl, interval: start.interval, expiresAt: Date.now() + start.expiresIn * 1000, ...(email ? { email } : {}) };
30
35
  saveConfig({ ...cfg, pendingLogin: pending });
31
36
  if (args.json)
@@ -70,6 +75,11 @@ export async function login(args) {
70
75
  else
71
76
  log.warn(msg);
72
77
  }
78
+ /** The same shape the platform accepts (`lib/partners.ts` in the API); anything else is no code. */
79
+ export function partnerRef(value) {
80
+ const v = value?.trim().toLowerCase() ?? "";
81
+ return /^[a-z0-9][a-z0-9-]{1,31}$/.test(v) ? v : null;
82
+ }
73
83
  function clearPending() {
74
84
  const { pendingLogin: _drop, ...rest } = loadConfig();
75
85
  saveConfig(rest);
@@ -1,3 +1,4 @@
1
+ import { type ReviewDue } from "../commands/session-review.js";
1
2
  import { type Gate } from "./types.js";
2
3
  /**
3
4
  * Brings the review of this app into the same list as every other check, so an agent meets it in the
@@ -8,3 +9,9 @@ import { type Gate } from "./types.js";
8
9
  * Skipped without comment when there is no sign-in, no linked project, or no network.
9
10
  */
10
11
  export declare const reviewGate: Gate;
12
+ /**
13
+ * The check's one line about the review. It says whether one is due and, when none is, why not: an agent
14
+ * that has just changed a build or config file cannot otherwise tell a review that was not needed from one
15
+ * that was forgotten.
16
+ */
17
+ export declare function reviewSaid(open: number, when: string | undefined, due: ReviewDue | null): string;
@@ -35,18 +35,31 @@ export const reviewGate = {
35
35
  return { rule_id: i.rule_id, severity: i.severity, audience: i.audience, file, line: Number(line) || undefined, risk: i.risk, fix: i.fix, verify: "nerd review report (say what you fixed), then nerd deploy", attempt: 1, fingerprint: `${i.rule_id}:${i.where}` };
36
36
  });
37
37
  const open = findings.length;
38
- let due = false;
39
- let first = false;
38
+ let due = null;
40
39
  try {
41
- ({ due, first } = reviewDue(ctx.root));
40
+ due = reviewDue(ctx.root);
42
41
  }
43
42
  catch {
44
43
  // a folder that cannot be listed has nothing to say here
45
44
  }
46
- if (due)
45
+ if (due?.due)
47
46
  findings.push(finding("session/review-due", { verify: "nerd review report" }));
48
- const when = got.reviewedAt ? localDay(got.reviewedAt) : undefined;
49
- const said = [open ? `${open} open from the review of ${when}; a second opinion that can be wrong, so read the code first` : when ? `Nothing open from the review of ${when}` : "No review yet", ...(due ? [first ? "no review has been reported from this folder: run nerd review checklist" : "code has changed since the last review: run nerd review checklist"] : [])];
50
- return result(this, verdict(findings), findings.length ? `${counts(findings)}: ${said.join("; ")}` : said.join("; "), findings, t);
47
+ const said = reviewSaid(open, got.reviewedAt ? localDay(got.reviewedAt) : undefined, due);
48
+ return result(this, verdict(findings), findings.length ? `${counts(findings)}: ${said}` : said, findings, t);
51
49
  },
52
50
  };
51
+ /**
52
+ * The check's one line about the review. It says whether one is due and, when none is, why not: an agent
53
+ * that has just changed a build or config file cannot otherwise tell a review that was not needed from one
54
+ * that was forgotten.
55
+ */
56
+ export function reviewSaid(open, when, due) {
57
+ const findings = open ? `${open} open from the review of ${when}; a second opinion that can be wrong, so read the code first` : when ? `Nothing open from the review of ${when}` : "No review yet";
58
+ if (!due)
59
+ return findings;
60
+ if (due.due)
61
+ return `${findings}; ${due.first ? "no review has been reported from this folder: run nerd review checklist" : "code has changed since the last review: run nerd review checklist"}`;
62
+ if (due.first)
63
+ return findings;
64
+ return `${findings}; no review is due: the app's code under src/ is as it was last reviewed here (build and config files, tests, styles, the README and package.json do not ask for one)`;
65
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nerdsystem",
3
- "version": "0.0.29",
3
+ "version": "0.0.30",
4
4
  "description": "nerd: start, check, and deploy a web app on nerdsystem from the terminal or a coding agent.",
5
5
  "type": "module",
6
6
  "bin": {