@thecorporation/cli 26.3.10 → 26.3.13

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
@@ -15,12 +15,25 @@ npm install -g @thecorporation/cli
15
15
  ## Quick Start
16
16
 
17
17
  ```bash
18
- corp setup # interactive first-run wizard
18
+ corp setup # authenticate via magic link
19
19
  corp status # workspace summary
20
20
  corp chat # AI assistant with full tool access
21
21
  corp form --type llc --name "Acme" # form a new entity
22
22
  ```
23
23
 
24
+ ## Authentication
25
+
26
+ `corp setup` authenticates via magic link:
27
+
28
+ 1. Enter your name and email
29
+ 2. Check your email for a sign-in link from TheCorporation
30
+ 3. Copy the code from the link URL and paste it into the terminal
31
+ 4. Credentials are saved to `~/.corp/config.json`
32
+
33
+ Your workspace is shared across the CLI, [MCP server](https://www.npmjs.com/package/@thecorporation/mcp-server), and [chat](https://humans.thecorporation.ai/chat) — all keyed on your email.
34
+
35
+ For self-hosted setups (`CORP_API_URL` pointing to your own server), `corp setup` provisions a workspace directly without requiring a magic link.
36
+
24
37
  ## Commands
25
38
 
26
39
  ### Core
@@ -204,7 +217,7 @@ Inside `corp chat`, these slash commands are available:
204
217
 
205
218
  ## Configuration
206
219
 
207
- Config is stored at `~/.corp/config.json`. The `corp setup` wizard will populate it, or set values manually:
220
+ Config is stored at `~/.corp/config.json`. `corp setup` populates it via magic link auth. You can also set values manually:
208
221
 
209
222
  ```bash
210
223
  corp config set api_url https://api.thecorporation.ai
package/dist/index.js CHANGED
@@ -479,25 +479,112 @@ __export(setup_exports, {
479
479
  setupCommand: () => setupCommand
480
480
  });
481
481
  import { input, confirm } from "@inquirer/prompts";
482
+ async function requestMagicLink(apiUrl, email, tosAccepted) {
483
+ const resp = await fetch(`${apiUrl}/v1/auth/magic-link`, {
484
+ method: "POST",
485
+ headers: { "Content-Type": "application/json" },
486
+ body: JSON.stringify({ email, tos_accepted: tosAccepted })
487
+ });
488
+ if (!resp.ok) {
489
+ const data = await resp.json().catch(() => ({}));
490
+ const detail = data?.error ?? data?.message ?? resp.statusText;
491
+ throw new Error(
492
+ typeof detail === "string" ? detail : JSON.stringify(detail)
493
+ );
494
+ }
495
+ }
496
+ async function verifyMagicLinkCode(apiUrl, code) {
497
+ const resp = await fetch(`${apiUrl}/v1/auth/magic-link/verify`, {
498
+ method: "POST",
499
+ headers: { "Content-Type": "application/json" },
500
+ body: JSON.stringify({ code, client: "cli" })
501
+ });
502
+ const data = await resp.json().catch(() => ({}));
503
+ if (!resp.ok) {
504
+ const detail = data?.error ?? data?.message ?? resp.statusText;
505
+ throw new Error(
506
+ typeof detail === "string" ? detail : JSON.stringify(detail)
507
+ );
508
+ }
509
+ if (!data.api_key || !data.workspace_id) {
510
+ throw new Error("Unexpected response \u2014 missing api_key or workspace_id");
511
+ }
512
+ return {
513
+ api_key: data.api_key,
514
+ workspace_id: data.workspace_id
515
+ };
516
+ }
517
+ function isCloudApi(url) {
518
+ return url.replace(/\/+$/, "").includes("thecorporation.ai");
519
+ }
520
+ async function magicLinkAuth(apiUrl, email) {
521
+ console.log("\nSending magic link to " + email + "...");
522
+ await requestMagicLink(apiUrl, email, true);
523
+ console.log("Check your email for a sign-in link from TheCorporation.");
524
+ console.log(
525
+ "Copy the code from the URL (the ?code=... part) and paste it below.\n"
526
+ );
527
+ const code = await input({ message: "Paste your magic link code" });
528
+ const trimmed = code.trim().replace(/^.*[?&]code=/, "");
529
+ if (!trimmed) {
530
+ throw new Error("No code provided");
531
+ }
532
+ console.log("Verifying...");
533
+ return verifyMagicLinkCode(apiUrl, trimmed);
534
+ }
482
535
  async function setupCommand() {
483
536
  const cfg = loadConfig();
484
537
  console.log("Welcome to corp \u2014 corporate governance from the terminal.\n");
485
- cfg.api_url = API_URL;
538
+ const customUrl = process.env.CORP_API_URL;
539
+ if (customUrl) {
540
+ cfg.api_url = customUrl.replace(/\/+$/, "");
541
+ console.log(`Using API: ${cfg.api_url}
542
+ `);
543
+ } else {
544
+ cfg.api_url = CLOUD_API_URL;
545
+ }
486
546
  console.log("--- User Info ---");
487
547
  const user = cfg.user ?? { name: "", email: "" };
488
- user.name = await input({ message: "Your name", default: user.name || void 0 });
489
- user.email = await input({ message: "Your email", default: user.email || void 0 });
548
+ user.name = await input({
549
+ message: "Your name",
550
+ default: user.name || void 0
551
+ });
552
+ user.email = await input({
553
+ message: "Your email",
554
+ default: user.email || void 0
555
+ });
490
556
  cfg.user = user;
491
- if (!cfg.api_key || !cfg.workspace_id) {
492
- console.log("\nProvisioning workspace...");
493
- try {
494
- const result = await provisionWorkspace(cfg.api_url, `${user.name}'s workspace`);
495
- cfg.api_key = result.api_key;
496
- cfg.workspace_id = result.workspace_id;
497
- console.log(`Workspace provisioned: ${result.workspace_id}`);
498
- } catch (err) {
499
- printError(`Auto-provision failed: ${err}`);
500
- console.log("You can manually set credentials with: corp config set api_key <key>");
557
+ const needsAuth = !cfg.api_key || !cfg.workspace_id;
558
+ const cloud = isCloudApi(cfg.api_url);
559
+ if (needsAuth) {
560
+ if (cloud) {
561
+ try {
562
+ const result = await magicLinkAuth(cfg.api_url, user.email);
563
+ cfg.api_key = result.api_key;
564
+ cfg.workspace_id = result.workspace_id;
565
+ printSuccess(`Authenticated. Workspace: ${result.workspace_id}`);
566
+ } catch (err) {
567
+ printError(`Authentication failed: ${err}`);
568
+ console.log(
569
+ "You can manually set credentials with: corp config set api_key <key>"
570
+ );
571
+ }
572
+ } else {
573
+ console.log("\nProvisioning workspace...");
574
+ try {
575
+ const result = await provisionWorkspace(
576
+ cfg.api_url,
577
+ `${user.name}'s workspace`
578
+ );
579
+ cfg.api_key = result.api_key;
580
+ cfg.workspace_id = result.workspace_id;
581
+ console.log(`Workspace provisioned: ${result.workspace_id}`);
582
+ } catch (err) {
583
+ printError(`Auto-provision failed: ${err}`);
584
+ console.log(
585
+ "You can manually set credentials with: corp config set api_key <key>"
586
+ );
587
+ }
501
588
  }
502
589
  } else {
503
590
  console.log("\nVerifying existing credentials...");
@@ -514,21 +601,33 @@ async function setupCommand() {
514
601
  console.log("Credentials OK.");
515
602
  } else {
516
603
  console.log("API key is no longer valid.");
517
- const reprovision = await confirm({
518
- message: "Provision a new workspace? (This will replace your current credentials)",
519
- default: false
604
+ const reauth = await confirm({
605
+ message: cloud ? "Re-authenticate via magic link?" : "Provision a new workspace? (This will replace your current credentials)",
606
+ default: true
520
607
  });
521
- if (reprovision) {
608
+ if (reauth) {
522
609
  try {
523
- const result = await provisionWorkspace(cfg.api_url, `${user.name}'s workspace`);
524
- cfg.api_key = result.api_key;
525
- cfg.workspace_id = result.workspace_id;
526
- console.log(`Workspace provisioned: ${result.workspace_id}`);
610
+ if (cloud) {
611
+ const result = await magicLinkAuth(cfg.api_url, user.email);
612
+ cfg.api_key = result.api_key;
613
+ cfg.workspace_id = result.workspace_id;
614
+ printSuccess(`Authenticated. Workspace: ${result.workspace_id}`);
615
+ } else {
616
+ const result = await provisionWorkspace(
617
+ cfg.api_url,
618
+ `${user.name}'s workspace`
619
+ );
620
+ cfg.api_key = result.api_key;
621
+ cfg.workspace_id = result.workspace_id;
622
+ console.log(`Workspace provisioned: ${result.workspace_id}`);
623
+ }
527
624
  } catch (err) {
528
- printError(`Provisioning failed: ${err}`);
625
+ printError(`Authentication failed: ${err}`);
529
626
  }
530
627
  } else {
531
- console.log("Keeping existing credentials. You can manually update with: corp config set api_key <key>");
628
+ console.log(
629
+ "Keeping existing credentials. You can manually update with: corp config set api_key <key>"
630
+ );
532
631
  }
533
632
  }
534
633
  }
@@ -536,14 +635,14 @@ async function setupCommand() {
536
635
  console.log("\nConfig saved to ~/.corp/config.json");
537
636
  console.log("Run 'corp status' to verify your connection.");
538
637
  }
539
- var API_URL;
638
+ var CLOUD_API_URL;
540
639
  var init_setup = __esm({
541
640
  "src/commands/setup.ts"() {
542
641
  "use strict";
543
642
  init_config();
544
643
  init_api_client();
545
644
  init_output();
546
- API_URL = "https://api.thecorporation.ai";
645
+ CLOUD_API_URL = "https://api.thecorporation.ai";
547
646
  }
548
647
  });
549
648
 
@@ -1255,21 +1354,10 @@ __export(entities_exports, {
1255
1354
  entitiesShowCommand: () => entitiesShowCommand
1256
1355
  });
1257
1356
  import chalk3 from "chalk";
1258
- function wantsJsonOutput(opts) {
1259
- if (opts && typeof opts === "object") {
1260
- const json = opts.json;
1261
- if (typeof json === "boolean") return json;
1262
- const commandOpts = opts.opts;
1263
- if (typeof commandOpts === "function") {
1264
- return Boolean(commandOpts().json);
1265
- }
1266
- }
1267
- return false;
1268
- }
1269
1357
  async function entitiesCommand(opts) {
1270
1358
  const cfg = requireConfig("api_url", "api_key", "workspace_id");
1271
1359
  const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
1272
- const jsonOutput = wantsJsonOutput(opts);
1360
+ const jsonOutput = Boolean(opts.json);
1273
1361
  try {
1274
1362
  const entities = await withSpinner("Loading", () => client.listEntities(), jsonOutput);
1275
1363
  if (jsonOutput) {
@@ -1287,7 +1375,7 @@ async function entitiesCommand(opts) {
1287
1375
  async function entitiesShowCommand(entityId, opts) {
1288
1376
  const cfg = requireConfig("api_url", "api_key", "workspace_id");
1289
1377
  const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
1290
- const jsonOutput = wantsJsonOutput(opts);
1378
+ const jsonOutput = Boolean(opts.json);
1291
1379
  try {
1292
1380
  const entities = await client.listEntities();
1293
1381
  const entity = entities.find((e) => e.entity_id === entityId);
@@ -2120,7 +2208,7 @@ async function governanceConveneCommand(opts) {
2120
2208
  }
2121
2209
  async function governanceVoteCommand(meetingId, itemId, opts) {
2122
2210
  const cfg = requireConfig("api_url", "api_key", "workspace_id");
2123
- const eid = resolveEntityId(cfg, void 0);
2211
+ const eid = resolveEntityId(cfg, opts.entityId);
2124
2212
  const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
2125
2213
  try {
2126
2214
  const result = await client.castVote(eid, meetingId, itemId, {
@@ -2272,6 +2360,19 @@ __export(documents_exports, {
2272
2360
  documentsPreviewPdfCommand: () => documentsPreviewPdfCommand,
2273
2361
  documentsSigningLinkCommand: () => documentsSigningLinkCommand
2274
2362
  });
2363
+ function formatSigningLink(docId, result) {
2364
+ if (typeof result.token === "string" && result.token.length > 0) {
2365
+ return `${HUMANS_APP_ORIGIN}/sign/${docId}?token=${encodeURIComponent(result.token)}`;
2366
+ }
2367
+ if (typeof result.signing_url === "string" && result.signing_url.length > 0) {
2368
+ if (/^https?:\/\//.test(result.signing_url)) {
2369
+ return result.signing_url;
2370
+ }
2371
+ const normalizedPath = result.signing_url.startsWith("/human/sign/") ? result.signing_url.replace("/human/sign/", "/sign/") : result.signing_url;
2372
+ return `${HUMANS_APP_ORIGIN}${normalizedPath.startsWith("/") ? normalizedPath : `/${normalizedPath}`}`;
2373
+ }
2374
+ return `${HUMANS_APP_ORIGIN}/sign/${docId}`;
2375
+ }
2275
2376
  async function documentsListCommand(opts) {
2276
2377
  const cfg = requireConfig("api_url", "api_key", "workspace_id");
2277
2378
  const eid = resolveEntityId(cfg, opts.entityId);
@@ -2292,8 +2393,10 @@ async function documentsSigningLinkCommand(docId, opts) {
2292
2393
  const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
2293
2394
  try {
2294
2395
  const result = await client.getSigningLink(docId, eid);
2295
- const shareUrl = result.token ? `https://humans.thecorporation.ai/sign/${docId}?token=${result.token}` : result.signing_url ?? `https://humans.thecorporation.ai/sign/${docId}`;
2296
- printSuccess("Signing link generated.");
2396
+ const shareUrl = formatSigningLink(docId, result);
2397
+ if (process.stdout.isTTY) {
2398
+ printSuccess("Signing link generated.");
2399
+ }
2297
2400
  console.log(shareUrl);
2298
2401
  } catch (err) {
2299
2402
  printError(`Failed to get signing link: ${err}`);
@@ -2332,12 +2435,14 @@ async function documentsPreviewPdfCommand(opts) {
2332
2435
  process.exit(1);
2333
2436
  }
2334
2437
  }
2438
+ var HUMANS_APP_ORIGIN;
2335
2439
  var init_documents = __esm({
2336
2440
  "src/commands/documents.ts"() {
2337
2441
  "use strict";
2338
2442
  init_config();
2339
2443
  init_api_client();
2340
2444
  init_output();
2445
+ HUMANS_APP_ORIGIN = "https://humans.thecorporation.ai";
2341
2446
  }
2342
2447
  });
2343
2448
 
@@ -2610,6 +2715,10 @@ async function workItemsCreateCommand(opts) {
2610
2715
  const eid = resolveEntityId(cfg, opts.entityId);
2611
2716
  const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
2612
2717
  try {
2718
+ if (!opts.category) {
2719
+ printError("Missing required option: --category <category>");
2720
+ process.exit(1);
2721
+ }
2613
2722
  const data = { title: opts.title, category: opts.category };
2614
2723
  if (opts.description) data.description = opts.description;
2615
2724
  if (opts.deadline) data.deadline = opts.deadline;
@@ -3390,6 +3499,13 @@ INTERNAL_WORKER_TOKEN={{INTERNAL_WORKER_TOKEN}}
3390
3499
  // src/index.ts
3391
3500
  import { Command, Option } from "commander";
3392
3501
  import { createRequire } from "module";
3502
+
3503
+ // src/command-options.ts
3504
+ function inheritOption(localValue, parentValue) {
3505
+ return localValue ?? parentValue;
3506
+ }
3507
+
3508
+ // src/index.ts
3393
3509
  var require2 = createRequire(import.meta.url);
3394
3510
  var pkg = require2("../package.json");
3395
3511
  var program = new Command();
@@ -3440,8 +3556,12 @@ var entitiesCmd = program.command("entities").description("List entities, show d
3440
3556
  await entitiesCommand2(opts);
3441
3557
  });
3442
3558
  entitiesCmd.command("show <entity-id>").option("--json", "Output as JSON").description("Show entity detail").action(async (entityId, opts, cmd) => {
3559
+ const parent = cmd.parent.opts();
3443
3560
  const { entitiesShowCommand: entitiesShowCommand2 } = await Promise.resolve().then(() => (init_entities(), entities_exports));
3444
- await entitiesShowCommand2(entityId, { ...cmd.opts(), ...opts });
3561
+ await entitiesShowCommand2(entityId, {
3562
+ ...opts,
3563
+ json: inheritOption(opts.json, parent.json)
3564
+ });
3445
3565
  });
3446
3566
  entitiesCmd.command("convert <entity-id>").requiredOption("--to <type>", "Target entity type (llc, c_corp)").option("--jurisdiction <jurisdiction>", "New jurisdiction").description("Convert entity to a different type").action(async (entityId, opts) => {
3447
3567
  const { entitiesConvertCommand: entitiesConvertCommand2 } = await Promise.resolve().then(() => (init_entities(), entities_exports));
@@ -3458,7 +3578,11 @@ var contactsCmd = program.command("contacts").description("Contact management").
3458
3578
  contactsCmd.command("show <contact-id>").option("--json", "Output as JSON").description("Show contact detail/profile").action(async (contactId, opts, cmd) => {
3459
3579
  const parent = cmd.parent.opts();
3460
3580
  const { contactsShowCommand: contactsShowCommand2 } = await Promise.resolve().then(() => (init_contacts(), contacts_exports));
3461
- await contactsShowCommand2(contactId, { ...opts, entityId: parent.entityId });
3581
+ await contactsShowCommand2(contactId, {
3582
+ ...opts,
3583
+ entityId: parent.entityId,
3584
+ json: inheritOption(opts.json, parent.json)
3585
+ });
3462
3586
  });
3463
3587
  contactsCmd.command("add").requiredOption("--name <name>", "Contact name").requiredOption("--email <email>", "Contact email").option("--type <type>", "Contact type (individual, organization)", "individual").option("--category <category>", "Category (employee, contractor, board_member, investor, law_firm, valuation_firm, accounting_firm, officer, founder, member, other)").option("--phone <phone>", "Phone number").option("--notes <notes>", "Notes").description("Add a new contact").action(async (opts, cmd) => {
3464
3588
  const parent = cmd.parent.opts();
@@ -3609,9 +3733,13 @@ governanceCmd.command("convene").requiredOption("--body <id>", "Governance body
3609
3733
  const { governanceConveneCommand: governanceConveneCommand2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
3610
3734
  await governanceConveneCommand2({ ...opts, meetingType: opts.type, entityId: parent.entityId });
3611
3735
  });
3612
- governanceCmd.command("vote <meeting-id> <item-id>").requiredOption("--voter <id>", "Voter contact UUID").addOption(new Option("--vote <value>", "Vote (for, against, abstain, recusal)").choices(["for", "against", "abstain", "recusal"]).makeOptionMandatory()).description("Cast a vote on an agenda item").action(async (meetingId, itemId, opts) => {
3736
+ governanceCmd.command("vote <meeting-id> <item-id>").requiredOption("--voter <id>", "Voter contact UUID").addOption(new Option("--vote <value>", "Vote (for, against, abstain, recusal)").choices(["for", "against", "abstain", "recusal"]).makeOptionMandatory()).description("Cast a vote on an agenda item").action(async (meetingId, itemId, opts, cmd) => {
3737
+ const parent = cmd.parent.opts();
3613
3738
  const { governanceVoteCommand: governanceVoteCommand2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
3614
- await governanceVoteCommand2(meetingId, itemId, opts);
3739
+ await governanceVoteCommand2(meetingId, itemId, {
3740
+ ...opts,
3741
+ entityId: parent.entityId
3742
+ });
3615
3743
  });
3616
3744
  governanceCmd.command("notice <meeting-id>").description("Send meeting notice").action(async (meetingId, _opts, cmd) => {
3617
3745
  const parent = cmd.parent.opts();
@@ -3682,9 +3810,13 @@ var agentsCmd = program.command("agents").description("Agent management").option
3682
3810
  const { agentsListCommand: agentsListCommand2 } = await Promise.resolve().then(() => (init_agents(), agents_exports));
3683
3811
  await agentsListCommand2(opts);
3684
3812
  });
3685
- agentsCmd.command("show <agent-id>").option("--json", "Output as JSON").description("Show agent detail").action(async (agentId, opts) => {
3813
+ agentsCmd.command("show <agent-id>").option("--json", "Output as JSON").description("Show agent detail").action(async (agentId, opts, cmd) => {
3814
+ const parent = cmd.parent.opts();
3686
3815
  const { agentsShowCommand: agentsShowCommand2 } = await Promise.resolve().then(() => (init_agents(), agents_exports));
3687
- await agentsShowCommand2(agentId, opts);
3816
+ await agentsShowCommand2(agentId, {
3817
+ ...opts,
3818
+ json: inheritOption(opts.json, parent.json)
3819
+ });
3688
3820
  });
3689
3821
  agentsCmd.command("create").requiredOption("--name <name>", "Agent name").requiredOption("--prompt <prompt>", "System prompt").option("--model <model>", "Model").description("Create a new agent").action(async (opts) => {
3690
3822
  const { agentsCreateCommand: agentsCreateCommand2 } = await Promise.resolve().then(() => (init_agents(), agents_exports));
@@ -3717,12 +3849,20 @@ var workItemsCmd = program.command("work-items").description("Long-term work ite
3717
3849
  workItemsCmd.command("show <item-id>").option("--json", "Output as JSON").description("Show work item detail").action(async (itemId, opts, cmd) => {
3718
3850
  const parent = cmd.parent.opts();
3719
3851
  const { workItemsShowCommand: workItemsShowCommand2 } = await Promise.resolve().then(() => (init_work_items(), work_items_exports));
3720
- await workItemsShowCommand2(itemId, { ...opts, entityId: parent.entityId });
3852
+ await workItemsShowCommand2(itemId, {
3853
+ ...opts,
3854
+ entityId: parent.entityId,
3855
+ json: inheritOption(opts.json, parent.json)
3856
+ });
3721
3857
  });
3722
- workItemsCmd.command("create").requiredOption("--title <title>", "Work item title").requiredOption("--category <category>", "Work item category").option("--description <desc>", "Description").option("--deadline <date>", "Deadline (YYYY-MM-DD)").option("--asap", "Mark as ASAP priority").option("--created-by <name>", "Creator identifier").description("Create a new work item").action(async (opts, cmd) => {
3858
+ workItemsCmd.command("create").requiredOption("--title <title>", "Work item title").option("--category <category>", "Work item category").option("--description <desc>", "Description").option("--deadline <date>", "Deadline (YYYY-MM-DD)").option("--asap", "Mark as ASAP priority").option("--created-by <name>", "Creator identifier").description("Create a new work item").action(async (opts, cmd) => {
3723
3859
  const parent = cmd.parent.opts();
3724
3860
  const { workItemsCreateCommand: workItemsCreateCommand2 } = await Promise.resolve().then(() => (init_work_items(), work_items_exports));
3725
- await workItemsCreateCommand2({ ...opts, entityId: parent.entityId });
3861
+ await workItemsCreateCommand2({
3862
+ ...opts,
3863
+ category: inheritOption(opts.category, parent.category),
3864
+ entityId: parent.entityId
3865
+ });
3726
3866
  });
3727
3867
  workItemsCmd.command("claim <item-id>").requiredOption("--by <name>", "Agent or user claiming the item").option("--ttl <seconds>", "Auto-release TTL in seconds", parseInt).description("Claim a work item").action(async (itemId, opts, cmd) => {
3728
3868
  const parent = cmd.parent.opts();