@thecorporation/cli 26.3.21 → 26.3.22

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/index.js CHANGED
@@ -90,9 +90,13 @@ function allowUnsafeApiUrl() {
90
90
  return process.env.CORP_UNSAFE_API_URL === "1";
91
91
  }
92
92
  function validateApiUrl(value) {
93
+ const trimmed = value.trim();
94
+ if (trimmed.startsWith("process://")) {
95
+ return trimmed;
96
+ }
93
97
  let parsed;
94
98
  try {
95
- parsed = new URL(value.trim());
99
+ parsed = new URL(trimmed);
96
100
  } catch {
97
101
  throw new Error("api_url must be a valid absolute URL");
98
102
  }
@@ -217,6 +221,7 @@ function normalizeConfig(raw) {
217
221
  cfg.workspace_id = normalizeString(raw.workspace_id) ?? cfg.workspace_id;
218
222
  cfg.hosting_mode = normalizeString(raw.hosting_mode) ?? cfg.hosting_mode;
219
223
  cfg.active_entity_id = normalizeString(raw.active_entity_id) ?? cfg.active_entity_id;
224
+ cfg.data_dir = normalizeString(raw.data_dir) ?? cfg.data_dir;
220
225
  if (isObject(raw.llm)) {
221
226
  cfg.llm.provider = normalizeString(raw.llm.provider) ?? cfg.llm.provider;
222
227
  cfg.llm.api_key = normalizeString(raw.llm.api_key) ?? cfg.llm.api_key;
@@ -263,7 +268,8 @@ function serializeConfig(cfg) {
263
268
  name: normalized.user.name,
264
269
  email: normalized.user.email
265
270
  },
266
- active_entity_id: normalized.active_entity_id
271
+ active_entity_id: normalized.active_entity_id,
272
+ ...normalized.data_dir ? { data_dir: normalized.data_dir } : {}
267
273
  };
268
274
  if (normalized.active_entity_ids && Object.keys(normalized.active_entity_ids).length > 0) {
269
275
  serialized.active_entity_ids = normalized.active_entity_ids;
@@ -283,6 +289,20 @@ function serializeAuth(cfg) {
283
289
  if (normalized.llm.api_key) {
284
290
  serialized.llm = { api_key: normalized.llm.api_key };
285
291
  }
292
+ const existingAuth = readJsonFile(AUTH_FILE);
293
+ if (isObject(existingAuth) && isObject(existingAuth.server_secrets)) {
294
+ const ss = existingAuth.server_secrets;
295
+ if (typeof ss.jwt_secret === "string" && typeof ss.secrets_master_key === "string" && typeof ss.internal_worker_token === "string") {
296
+ serialized.server_secrets = {
297
+ jwt_secret: ss.jwt_secret,
298
+ secrets_master_key: ss.secrets_master_key,
299
+ internal_worker_token: ss.internal_worker_token
300
+ };
301
+ }
302
+ }
303
+ if (cfg._server_secrets) {
304
+ serialized.server_secrets = cfg._server_secrets;
305
+ }
286
306
  return JSON.stringify(serialized, null, 2) + "\n";
287
307
  }
288
308
  function requireSupportedConfigKey(dotPath) {
@@ -311,6 +331,9 @@ function setKnownConfigValue(cfg, dotPath, value) {
311
331
  case "hosting_mode":
312
332
  cfg.hosting_mode = value.trim();
313
333
  return;
334
+ case "data_dir":
335
+ cfg.data_dir = value.trim();
336
+ return;
314
337
  case "llm.provider":
315
338
  cfg.llm.provider = value.trim();
316
339
  return;
@@ -492,7 +515,8 @@ var init_config = __esm({
492
515
  "llm.base_url",
493
516
  "user.name",
494
517
  "user.email",
495
- "active_entity_id"
518
+ "active_entity_id",
519
+ "data_dir"
496
520
  ]);
497
521
  SENSITIVE_CONFIG_KEYS = /* @__PURE__ */ new Set(["api_url", "api_key", "workspace_id"]);
498
522
  DEFAULTS = {
@@ -507,7 +531,8 @@ var init_config = __esm({
507
531
  base_url: process.env.CORP_LLM_BASE_URL || void 0
508
532
  },
509
533
  user: { name: "", email: "" },
510
- active_entity_id: ""
534
+ active_entity_id: "",
535
+ data_dir: ""
511
536
  };
512
537
  }
513
538
  });
@@ -658,7 +683,11 @@ function describeReferenceRecord(kind, record) {
658
683
  idFields: ["share_class_id", "id"],
659
684
  labelFields: ["class_code", "name", "share_class"]
660
685
  },
661
- round: { idFields: ["round_id", "equity_round_id", "id"], labelFields: ["name"] }
686
+ round: { idFields: ["round_id", "equity_round_id", "id"], labelFields: ["name"] },
687
+ service_request: {
688
+ idFields: ["request_id", "service_request_id", "id"],
689
+ labelFields: ["service_slug", "status"]
690
+ }
662
691
  };
663
692
  const spec = specs[kind];
664
693
  const id = extractId(record, spec.idFields);
@@ -726,7 +755,8 @@ var init_references = __esm({
726
755
  "safe_note",
727
756
  "instrument",
728
757
  "share_class",
729
- "round"
758
+ "round",
759
+ "service_request"
730
760
  ];
731
761
  VALID_RESOURCE_KINDS = new Set(RESOURCE_KINDS);
732
762
  MAX_REFERENCE_INPUT_LEN = 256;
@@ -755,6 +785,7 @@ var init_references = __esm({
755
785
  valuationsCache = /* @__PURE__ */ new Map();
756
786
  safeNotesCache = /* @__PURE__ */ new Map();
757
787
  roundsCache = /* @__PURE__ */ new Map();
788
+ serviceRequestsCache = /* @__PURE__ */ new Map();
758
789
  capTableCache = /* @__PURE__ */ new Map();
759
790
  agentsCache;
760
791
  constructor(client, cfg) {
@@ -869,6 +900,9 @@ var init_references = __esm({
869
900
  async resolveRound(entityId, ref) {
870
901
  return this.resolve("round", ref, { entityId });
871
902
  }
903
+ async resolveServiceRequest(entityId, ref) {
904
+ return this.resolve("service_request", ref, { entityId });
905
+ }
872
906
  async find(kind, query, scope = {}) {
873
907
  const trimmedQuery = validateReferenceInput(query, "query", { allowEmpty: true });
874
908
  const records = await this.listRecords(kind, scope);
@@ -1056,6 +1090,8 @@ var init_references = __esm({
1056
1090
  return this.listShareClasses(scope.entityId);
1057
1091
  case "round":
1058
1092
  return this.listRounds(scope.entityId);
1093
+ case "service_request":
1094
+ return this.listServiceRequestRecords(scope.entityId);
1059
1095
  }
1060
1096
  })();
1061
1097
  return this.attachStableHandles(kind, records, scope.entityId);
@@ -1320,6 +1356,14 @@ var init_references = __esm({
1320
1356
  const capTable = await this.getCapTable(entityId);
1321
1357
  return Array.isArray(capTable.share_classes) ? capTable.share_classes : [];
1322
1358
  }
1359
+ async listServiceRequestRecords(entityId) {
1360
+ if (!entityId) throw new Error("An entity context is required to resolve service requests.");
1361
+ const cached = this.serviceRequestsCache.get(entityId);
1362
+ if (cached) return cached;
1363
+ const requests = await this.client.listServiceRequests(entityId);
1364
+ this.serviceRequestsCache.set(entityId, requests);
1365
+ return requests;
1366
+ }
1323
1367
  };
1324
1368
  }
1325
1369
  });
@@ -1882,6 +1926,33 @@ function printAgentsTable(agents) {
1882
1926
  }
1883
1927
  console.log(table.toString());
1884
1928
  }
1929
+ function printServiceCatalogTable(items) {
1930
+ const table = makeTable("Service Catalog", ["Slug", "Name", "Price", "Type"]);
1931
+ for (const item of items) {
1932
+ table.push([
1933
+ s(item.slug),
1934
+ s(item.name),
1935
+ money(item.amount_cents),
1936
+ s(item.price_type)
1937
+ ]);
1938
+ }
1939
+ console.log(table.toString());
1940
+ }
1941
+ function printServiceRequestsTable(requests) {
1942
+ const table = makeTable("Service Requests", ["Ref", "Service", "Amount", "Status", "Created"]);
1943
+ for (const r of requests) {
1944
+ const status = s(r.status);
1945
+ const colored = status === "fulfilled" ? chalk.green(status) : status === "paid" ? chalk.cyan(status) : status === "checkout" ? chalk.yellow(status) : status === "failed" ? chalk.dim(status) : status;
1946
+ table.push([
1947
+ formatReferenceCell("service_request", r),
1948
+ s(r.service_slug),
1949
+ money(r.amount_cents),
1950
+ colored,
1951
+ date(r.created_at)
1952
+ ]);
1953
+ }
1954
+ console.log(table.toString());
1955
+ }
1885
1956
  function printBillingPanel(status, plans) {
1886
1957
  const plan = s(status.plan ?? status.tier) || "free";
1887
1958
  const subStatus = s(status.status) || "active";
@@ -1935,7 +2006,15 @@ var setup_exports = {};
1935
2006
  __export(setup_exports, {
1936
2007
  setupCommand: () => setupCommand
1937
2008
  });
1938
- import { input, confirm } from "@inquirer/prompts";
2009
+ import { input, select } from "@inquirer/prompts";
2010
+ import { homedir as homedir2 } from "os";
2011
+ import { join as join2 } from "path";
2012
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync } from "fs";
2013
+ import {
2014
+ generateSecret,
2015
+ generateFernetKey,
2016
+ processRequest
2017
+ } from "@thecorporation/corp-tools";
1939
2018
  async function requestMagicLink(apiUrl, email, tosAccepted) {
1940
2019
  const resp = await fetch(`${apiUrl}/v1/auth/magic-link`, {
1941
2020
  method: "POST",
@@ -1971,9 +2050,6 @@ async function verifyMagicLinkCode(apiUrl, code) {
1971
2050
  workspace_id: data.workspace_id
1972
2051
  };
1973
2052
  }
1974
- function isCloudApi(url) {
1975
- return url.replace(/\/+$/, "").includes("thecorporation.ai");
1976
- }
1977
2053
  async function magicLinkAuth(apiUrl, email) {
1978
2054
  console.log("\nSending magic link to " + email + "...");
1979
2055
  await requestMagicLink(apiUrl, email, true);
@@ -1989,22 +2065,46 @@ async function magicLinkAuth(apiUrl, email) {
1989
2065
  console.log("Verifying...");
1990
2066
  return verifyMagicLinkCode(apiUrl, trimmed);
1991
2067
  }
2068
+ function setupDataDir(dirPath) {
2069
+ if (!existsSync2(dirPath)) {
2070
+ mkdirSync2(dirPath, { recursive: true });
2071
+ return { isNew: true };
2072
+ }
2073
+ try {
2074
+ const entries = readdirSync(dirPath);
2075
+ if (entries.length > 0) {
2076
+ console.log("Found existing data, reusing.");
2077
+ return { isNew: false };
2078
+ }
2079
+ } catch {
2080
+ }
2081
+ return { isNew: true };
2082
+ }
2083
+ async function localProvision(dataDir, name) {
2084
+ const resp = processRequest(
2085
+ "process://",
2086
+ "POST",
2087
+ "/v1/workspaces/provision",
2088
+ { "Content-Type": "application/json" },
2089
+ JSON.stringify({ name }),
2090
+ { dataDir }
2091
+ );
2092
+ if (!resp.ok) {
2093
+ const detail = await resp.text();
2094
+ throw new Error(`Provision failed: HTTP ${resp.status} \u2014 ${detail}`);
2095
+ }
2096
+ const body = await resp.json();
2097
+ if (!body.api_key || !body.workspace_id) {
2098
+ throw new Error("Provision response missing api_key or workspace_id");
2099
+ }
2100
+ return {
2101
+ api_key: body.api_key,
2102
+ workspace_id: body.workspace_id
2103
+ };
2104
+ }
1992
2105
  async function setupCommand() {
1993
2106
  const cfg = loadConfig();
1994
2107
  console.log("Welcome to corp \u2014 corporate governance from the terminal.\n");
1995
- const customUrl = process.env.CORP_API_URL;
1996
- if (customUrl) {
1997
- try {
1998
- cfg.api_url = validateApiUrl(customUrl);
1999
- } catch (err) {
2000
- printError(`Invalid CORP_API_URL: ${err}`);
2001
- process.exit(1);
2002
- }
2003
- console.log(`Using API: ${cfg.api_url}
2004
- `);
2005
- } else {
2006
- cfg.api_url = CLOUD_API_URL;
2007
- }
2008
2108
  console.log("--- User Info ---");
2009
2109
  const user = cfg.user ?? { name: "", email: "" };
2010
2110
  user.name = await input({
@@ -2016,10 +2116,53 @@ async function setupCommand() {
2016
2116
  default: user.email || void 0
2017
2117
  });
2018
2118
  cfg.user = user;
2019
- const needsAuth = !cfg.api_key || !cfg.workspace_id;
2020
- const cloud = isCloudApi(cfg.api_url);
2021
- if (needsAuth) {
2022
- if (cloud) {
2119
+ console.log("\n--- Hosting Mode ---");
2120
+ const hostingMode = await select({
2121
+ message: "How would you like to run corp?",
2122
+ choices: [
2123
+ { value: "local", name: "Local (your machine)" },
2124
+ { value: "cloud", name: "TheCorporation cloud" },
2125
+ { value: "self-hosted", name: "Self-hosted server (custom URL)" }
2126
+ ],
2127
+ default: cfg.hosting_mode || "local"
2128
+ });
2129
+ cfg.hosting_mode = hostingMode;
2130
+ if (hostingMode === "local") {
2131
+ const dataDir = await input({
2132
+ message: "Data directory",
2133
+ default: cfg.data_dir || DEFAULT_DATA_DIR
2134
+ });
2135
+ cfg.data_dir = dataDir;
2136
+ cfg.api_url = "process://";
2137
+ const { isNew } = setupDataDir(dataDir);
2138
+ const serverSecrets = {
2139
+ jwt_secret: generateSecret(),
2140
+ secrets_master_key: generateFernetKey(),
2141
+ internal_worker_token: generateSecret()
2142
+ };
2143
+ process.env.JWT_SECRET = serverSecrets.jwt_secret;
2144
+ process.env.SECRETS_MASTER_KEY = serverSecrets.secrets_master_key;
2145
+ process.env.INTERNAL_WORKER_TOKEN = serverSecrets.internal_worker_token;
2146
+ cfg._server_secrets = serverSecrets;
2147
+ if (isNew || !cfg.workspace_id) {
2148
+ console.log("\nProvisioning workspace...");
2149
+ try {
2150
+ const result = await localProvision(dataDir, `${user.name}'s workspace`);
2151
+ cfg.api_key = result.api_key;
2152
+ cfg.workspace_id = result.workspace_id;
2153
+ printSuccess(`Local workspace ready: ${result.workspace_id}`);
2154
+ } catch (err) {
2155
+ printError(`Workspace provisioning failed: ${err}`);
2156
+ console.log("You can retry with 'corp setup'.");
2157
+ }
2158
+ } else {
2159
+ console.log("\nExisting workspace found.");
2160
+ }
2161
+ } else if (hostingMode === "cloud") {
2162
+ cfg.api_url = CLOUD_API_URL;
2163
+ cfg.data_dir = "";
2164
+ const needsAuth = !cfg.api_key || !cfg.workspace_id;
2165
+ if (needsAuth) {
2023
2166
  try {
2024
2167
  const result = await magicLinkAuth(cfg.api_url, user.email);
2025
2168
  cfg.api_key = result.api_key;
@@ -2032,6 +2175,22 @@ async function setupCommand() {
2032
2175
  );
2033
2176
  }
2034
2177
  } else {
2178
+ console.log("\nExisting credentials found. Run 'corp status' to verify.");
2179
+ }
2180
+ } else {
2181
+ const url = await input({
2182
+ message: "Server URL",
2183
+ default: cfg.api_url !== CLOUD_API_URL && !cfg.api_url.startsWith("process://") ? cfg.api_url : void 0
2184
+ });
2185
+ try {
2186
+ cfg.api_url = validateApiUrl(url);
2187
+ } catch (err) {
2188
+ printError(`Invalid URL: ${err}`);
2189
+ process.exit(1);
2190
+ }
2191
+ cfg.data_dir = "";
2192
+ const needsAuth = !cfg.api_key || !cfg.workspace_id;
2193
+ if (needsAuth) {
2035
2194
  console.log("\nProvisioning workspace...");
2036
2195
  try {
2037
2196
  const result = await provisionWorkspace(
@@ -2048,57 +2207,13 @@ async function setupCommand() {
2048
2207
  );
2049
2208
  }
2050
2209
  }
2051
- } else {
2052
- console.log("\nVerifying existing credentials...");
2053
- let keyValid = false;
2054
- try {
2055
- const resp = await fetch(
2056
- `${cfg.api_url.replace(/\/+$/, "")}/v1/workspaces/${cfg.workspace_id}/status`,
2057
- { headers: { Authorization: `Bearer ${cfg.api_key}` } }
2058
- );
2059
- keyValid = resp.status !== 401;
2060
- } catch {
2061
- }
2062
- if (keyValid) {
2063
- console.log("Credentials OK.");
2064
- } else {
2065
- console.log("API key is no longer valid.");
2066
- const reauth = await confirm({
2067
- message: cloud ? "Re-authenticate via magic link?" : "Provision a new workspace? (This will replace your current credentials)",
2068
- default: true
2069
- });
2070
- if (reauth) {
2071
- try {
2072
- if (cloud) {
2073
- const result = await magicLinkAuth(cfg.api_url, user.email);
2074
- cfg.api_key = result.api_key;
2075
- cfg.workspace_id = result.workspace_id;
2076
- printSuccess(`Authenticated. Workspace: ${result.workspace_id}`);
2077
- } else {
2078
- const result = await provisionWorkspace(
2079
- cfg.api_url,
2080
- `${user.name}'s workspace`
2081
- );
2082
- cfg.api_key = result.api_key;
2083
- cfg.workspace_id = result.workspace_id;
2084
- console.log(`Workspace provisioned: ${result.workspace_id}`);
2085
- }
2086
- } catch (err) {
2087
- printError(`Authentication failed: ${err}`);
2088
- }
2089
- } else {
2090
- console.log(
2091
- "Keeping existing credentials. You can manually update with: corp config set api_key <key>"
2092
- );
2093
- }
2094
- }
2095
2210
  }
2096
2211
  saveConfig(cfg);
2097
2212
  console.log("\nSettings saved to ~/.corp/config.json");
2098
2213
  console.log("Credentials saved to ~/.corp/auth.json");
2099
2214
  console.log("Run 'corp status' to verify your connection.");
2100
2215
  }
2101
- var CLOUD_API_URL;
2216
+ var CLOUD_API_URL, DEFAULT_DATA_DIR;
2102
2217
  var init_setup = __esm({
2103
2218
  "src/commands/setup.ts"() {
2104
2219
  "use strict";
@@ -2106,6 +2221,7 @@ var init_setup = __esm({
2106
2221
  init_api_client();
2107
2222
  init_output();
2108
2223
  CLOUD_API_URL = "https://api.thecorporation.ai";
2224
+ DEFAULT_DATA_DIR = join2(homedir2(), ".corp", "data");
2109
2225
  }
2110
2226
  });
2111
2227
 
@@ -3114,11 +3230,11 @@ import {
3114
3230
  isWriteTool as _isWriteTool,
3115
3231
  executeTool as _executeTool
3116
3232
  } from "@thecorporation/corp-tools";
3117
- import { join as join2 } from "path";
3118
- import { homedir as homedir2 } from "os";
3233
+ import { join as join3 } from "path";
3234
+ import { homedir as homedir3 } from "os";
3119
3235
  async function executeTool(name, args, client) {
3120
3236
  return _executeTool(name, args, client, {
3121
- dataDir: join2(homedir2(), ".corp"),
3237
+ dataDir: join3(homedir3(), ".corp"),
3122
3238
  onEntityFormed: (entityId) => {
3123
3239
  try {
3124
3240
  updateConfig((cfg) => {
@@ -6127,6 +6243,171 @@ var init_work_items = __esm({
6127
6243
  }
6128
6244
  });
6129
6245
 
6246
+ // src/commands/services.ts
6247
+ var services_exports = {};
6248
+ __export(services_exports, {
6249
+ servicesBuyCommand: () => servicesBuyCommand,
6250
+ servicesCancelCommand: () => servicesCancelCommand,
6251
+ servicesCatalogCommand: () => servicesCatalogCommand,
6252
+ servicesFulfillCommand: () => servicesFulfillCommand,
6253
+ servicesListCommand: () => servicesListCommand,
6254
+ servicesShowCommand: () => servicesShowCommand
6255
+ });
6256
+ import chalk11 from "chalk";
6257
+ async function servicesCatalogCommand(opts) {
6258
+ const cfg = requireConfig("api_url", "api_key", "workspace_id");
6259
+ const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
6260
+ try {
6261
+ const items = await client.listServiceCatalog();
6262
+ if (opts.json) {
6263
+ printJson(items);
6264
+ return;
6265
+ }
6266
+ printServiceCatalogTable(items);
6267
+ } catch (err) {
6268
+ printError(`Failed to list service catalog: ${err}`);
6269
+ process.exit(1);
6270
+ }
6271
+ }
6272
+ async function servicesBuyCommand(slug, opts) {
6273
+ const cfg = requireConfig("api_url", "api_key", "workspace_id");
6274
+ const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
6275
+ const resolver = new ReferenceResolver(client, cfg);
6276
+ try {
6277
+ const eid = await resolver.resolveEntity(opts.entityId);
6278
+ const payload = { entity_id: eid, service_slug: slug };
6279
+ if (opts.dryRun) {
6280
+ printDryRun("services.create_request", payload);
6281
+ return;
6282
+ }
6283
+ const result = await client.createServiceRequest(payload);
6284
+ await resolver.stabilizeRecord("service_request", result, eid);
6285
+ resolver.rememberFromRecord("service_request", result, eid);
6286
+ const requestId = String(result.request_id ?? result.id ?? "");
6287
+ if (requestId) {
6288
+ const checkout = await client.beginServiceCheckout(requestId, { entity_id: eid });
6289
+ if (opts.json) {
6290
+ printJson(checkout);
6291
+ return;
6292
+ }
6293
+ printSuccess(`Service request created: ${requestId}`);
6294
+ printReferenceSummary("service_request", result, { showReuseHint: true });
6295
+ if (checkout.checkout_url) {
6296
+ console.log(`
6297
+ ${chalk11.bold("Checkout URL:")} ${checkout.checkout_url}`);
6298
+ }
6299
+ console.log(chalk11.dim("\n Next steps:"));
6300
+ console.log(chalk11.dim(" Complete payment at the checkout URL above"));
6301
+ console.log(chalk11.dim(" corp services list --entity-id <id>"));
6302
+ } else {
6303
+ printWriteResult(result, "Service request created", {
6304
+ referenceKind: "service_request",
6305
+ showReuseHint: true
6306
+ });
6307
+ }
6308
+ } catch (err) {
6309
+ printError(`Failed to create service request: ${err}`);
6310
+ process.exit(1);
6311
+ }
6312
+ }
6313
+ async function servicesListCommand(opts) {
6314
+ const cfg = requireConfig("api_url", "api_key", "workspace_id");
6315
+ const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
6316
+ const resolver = new ReferenceResolver(client, cfg);
6317
+ try {
6318
+ const eid = await resolver.resolveEntity(opts.entityId);
6319
+ const requests = await client.listServiceRequests(eid);
6320
+ const stable = await resolver.stabilizeRecords("service_request", requests, eid);
6321
+ if (opts.json) {
6322
+ printJson(stable);
6323
+ return;
6324
+ }
6325
+ printServiceRequestsTable(stable);
6326
+ } catch (err) {
6327
+ printError(`Failed to list service requests: ${err}`);
6328
+ process.exit(1);
6329
+ }
6330
+ }
6331
+ async function servicesShowCommand(ref_, opts) {
6332
+ const cfg = requireConfig("api_url", "api_key", "workspace_id");
6333
+ const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
6334
+ const resolver = new ReferenceResolver(client, cfg);
6335
+ try {
6336
+ const eid = await resolver.resolveEntity(opts.entityId);
6337
+ const requestId = await resolver.resolveServiceRequest(eid, ref_);
6338
+ const result = await client.getServiceRequest(requestId, eid);
6339
+ await resolver.stabilizeRecord("service_request", result, eid);
6340
+ resolver.rememberFromRecord("service_request", result, eid);
6341
+ if (opts.json) {
6342
+ printJson(result);
6343
+ return;
6344
+ }
6345
+ printReferenceSummary("service_request", result);
6346
+ printJson(result);
6347
+ } catch (err) {
6348
+ printError(`Failed to show service request: ${err}`);
6349
+ process.exit(1);
6350
+ }
6351
+ }
6352
+ async function servicesFulfillCommand(ref_, opts) {
6353
+ const cfg = requireConfig("api_url", "api_key", "workspace_id");
6354
+ const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
6355
+ const resolver = new ReferenceResolver(client, cfg);
6356
+ try {
6357
+ const eid = await resolver.resolveEntity(opts.entityId);
6358
+ const requestId = await resolver.resolveServiceRequest(eid, ref_);
6359
+ const result = await client.fulfillServiceRequest(requestId, {
6360
+ entity_id: eid,
6361
+ note: opts.note
6362
+ });
6363
+ await resolver.stabilizeRecord("service_request", result, eid);
6364
+ resolver.rememberFromRecord("service_request", result, eid);
6365
+ if (opts.json) {
6366
+ printJson(result);
6367
+ return;
6368
+ }
6369
+ printSuccess(`Service request fulfilled: ${requestId}`);
6370
+ printReferenceSummary("service_request", result, { showReuseHint: true });
6371
+ printJson(result);
6372
+ } catch (err) {
6373
+ printError(`Failed to fulfill service request: ${err}`);
6374
+ process.exit(1);
6375
+ }
6376
+ }
6377
+ async function servicesCancelCommand(ref_, opts) {
6378
+ const cfg = requireConfig("api_url", "api_key", "workspace_id");
6379
+ const client = new CorpAPIClient(cfg.api_url, cfg.api_key, cfg.workspace_id);
6380
+ const resolver = new ReferenceResolver(client, cfg);
6381
+ try {
6382
+ const eid = await resolver.resolveEntity(opts.entityId);
6383
+ const requestId = await resolver.resolveServiceRequest(eid, ref_);
6384
+ const result = await client.cancelServiceRequest(requestId, {
6385
+ entity_id: eid
6386
+ });
6387
+ await resolver.stabilizeRecord("service_request", result, eid);
6388
+ resolver.rememberFromRecord("service_request", result, eid);
6389
+ if (opts.json) {
6390
+ printJson(result);
6391
+ return;
6392
+ }
6393
+ printSuccess(`Service request cancelled: ${requestId}`);
6394
+ printReferenceSummary("service_request", result, { showReuseHint: true });
6395
+ printJson(result);
6396
+ } catch (err) {
6397
+ printError(`Failed to cancel service request: ${err}`);
6398
+ process.exit(1);
6399
+ }
6400
+ }
6401
+ var init_services = __esm({
6402
+ "src/commands/services.ts"() {
6403
+ "use strict";
6404
+ init_config();
6405
+ init_api_client();
6406
+ init_output();
6407
+ init_references();
6408
+ }
6409
+ });
6410
+
6130
6411
  // src/commands/billing.ts
6131
6412
  var billing_exports = {};
6132
6413
  __export(billing_exports, {
@@ -6237,8 +6518,8 @@ __export(form_exports, {
6237
6518
  formCreateCommand: () => formCreateCommand,
6238
6519
  formFinalizeCommand: () => formFinalizeCommand
6239
6520
  });
6240
- import { input as input2, select, confirm as confirm2, number } from "@inquirer/prompts";
6241
- import chalk11 from "chalk";
6521
+ import { input as input2, select as select2, confirm as confirm2, number } from "@inquirer/prompts";
6522
+ import chalk12 from "chalk";
6242
6523
  import Table3 from "cli-table3";
6243
6524
  import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "fs";
6244
6525
  import { relative as relative2, resolve as resolve2 } from "path";
@@ -6248,9 +6529,9 @@ function isCorp(entityType) {
6248
6529
  }
6249
6530
  function sectionHeader(title) {
6250
6531
  console.log();
6251
- console.log(chalk11.blue("\u2500".repeat(50)));
6252
- console.log(chalk11.blue.bold(` ${title}`));
6253
- console.log(chalk11.blue("\u2500".repeat(50)));
6532
+ console.log(chalk12.blue("\u2500".repeat(50)));
6533
+ console.log(chalk12.blue.bold(` ${title}`));
6534
+ console.log(chalk12.blue("\u2500".repeat(50)));
6254
6535
  }
6255
6536
  function officerTitleLabel(title) {
6256
6537
  switch (title) {
@@ -6410,7 +6691,7 @@ async function phaseEntityDetails(opts, serverCfg, scripted) {
6410
6691
  if (scripted) {
6411
6692
  entityType = "llc";
6412
6693
  } else {
6413
- entityType = await select({
6694
+ entityType = await select2({
6414
6695
  message: "Entity type",
6415
6696
  choices: [
6416
6697
  { value: "llc", name: "LLC" },
@@ -6469,13 +6750,13 @@ async function phasePeople(opts, entityType, scripted) {
6469
6750
  }
6470
6751
  const founderCount = await number({ message: "Number of founders (1-6)", default: 1 }) ?? 1;
6471
6752
  for (let i = 0; i < founderCount; i++) {
6472
- console.log(chalk11.dim(`
6753
+ console.log(chalk12.dim(`
6473
6754
  Founder ${i + 1} of ${founderCount}:`));
6474
6755
  const name = await input2({ message: ` Name` });
6475
6756
  const email = await input2({ message: ` Email` });
6476
6757
  let role = "member";
6477
6758
  if (isCorp(entityType)) {
6478
- role = await select({
6759
+ role = await select2({
6479
6760
  message: " Role",
6480
6761
  choices: [
6481
6762
  { value: "director", name: "Director" },
@@ -6490,7 +6771,7 @@ async function phasePeople(opts, entityType, scripted) {
6490
6771
  if (isCorp(entityType)) {
6491
6772
  const wantOfficer = role === "officer" || await confirm2({ message: " Assign officer title?", default: i === 0 });
6492
6773
  if (wantOfficer) {
6493
- officerTitle = await select({
6774
+ officerTitle = await select2({
6494
6775
  message: " Officer title",
6495
6776
  choices: OfficerTitle.map((t) => ({
6496
6777
  value: t,
@@ -6515,7 +6796,7 @@ async function phaseStock(opts, entityType, founders, scripted) {
6515
6796
  const rofr = opts.rofr ?? (!scripted && isCorp(entityType) ? await confirm2({ message: "Right of first refusal?", default: true }) : isCorp(entityType));
6516
6797
  if (!scripted) {
6517
6798
  for (const f of founders) {
6518
- console.log(chalk11.dim(`
6799
+ console.log(chalk12.dim(`
6519
6800
  Equity for ${f.name}:`));
6520
6801
  if (isCorp(entityType)) {
6521
6802
  const shares = await number({ message: ` Shares to purchase`, default: 0 });
@@ -6536,7 +6817,7 @@ async function phaseStock(opts, entityType, founders, scripted) {
6536
6817
  if (wantVesting) {
6537
6818
  const totalMonths = await number({ message: " Total vesting months", default: 48 }) ?? 48;
6538
6819
  const cliffMonths = await number({ message: " Cliff months", default: 12 }) ?? 12;
6539
- const acceleration = await select({
6820
+ const acceleration = await select2({
6540
6821
  message: " Acceleration",
6541
6822
  choices: [
6542
6823
  { value: "none", name: "None" },
@@ -6561,17 +6842,17 @@ async function phaseStock(opts, entityType, founders, scripted) {
6561
6842
  }
6562
6843
  function printSummary(entityType, name, jurisdiction, fiscalYearEnd, sCorpElection, founders, transferRestrictions, rofr) {
6563
6844
  sectionHeader("Formation Summary");
6564
- console.log(` ${chalk11.bold("Entity:")} ${name}`);
6565
- console.log(` ${chalk11.bold("Type:")} ${entityType}`);
6566
- console.log(` ${chalk11.bold("Jurisdiction:")} ${jurisdiction}`);
6567
- console.log(` ${chalk11.bold("Fiscal Year End:")} ${fiscalYearEnd}`);
6845
+ console.log(` ${chalk12.bold("Entity:")} ${name}`);
6846
+ console.log(` ${chalk12.bold("Type:")} ${entityType}`);
6847
+ console.log(` ${chalk12.bold("Jurisdiction:")} ${jurisdiction}`);
6848
+ console.log(` ${chalk12.bold("Fiscal Year End:")} ${fiscalYearEnd}`);
6568
6849
  if (isCorp(entityType)) {
6569
- console.log(` ${chalk11.bold("S-Corp Election:")} ${sCorpElection ? "Yes" : "No"}`);
6570
- console.log(` ${chalk11.bold("Transfer Restrictions:")} ${transferRestrictions ? "Yes" : "No"}`);
6571
- console.log(` ${chalk11.bold("Right of First Refusal:")} ${rofr ? "Yes" : "No"}`);
6850
+ console.log(` ${chalk12.bold("S-Corp Election:")} ${sCorpElection ? "Yes" : "No"}`);
6851
+ console.log(` ${chalk12.bold("Transfer Restrictions:")} ${transferRestrictions ? "Yes" : "No"}`);
6852
+ console.log(` ${chalk12.bold("Right of First Refusal:")} ${rofr ? "Yes" : "No"}`);
6572
6853
  }
6573
6854
  const table = new Table3({
6574
- head: [chalk11.dim("Name"), chalk11.dim("Email"), chalk11.dim("Role"), chalk11.dim("Equity"), chalk11.dim("Officer")]
6855
+ head: [chalk12.dim("Name"), chalk12.dim("Email"), chalk12.dim("Role"), chalk12.dim("Equity"), chalk12.dim("Officer")]
6575
6856
  });
6576
6857
  for (const f of founders) {
6577
6858
  const equity = f.shares_purchased ? `${f.shares_purchased.toLocaleString()} shares` : f.ownership_pct ? `${f.ownership_pct}%` : "\u2014";
@@ -6598,7 +6879,7 @@ async function formCommand(opts) {
6598
6879
  printSummary(entityType, name, jurisdiction, fiscalYearEnd, sCorpElection, founders, transferRestrictions, rofr);
6599
6880
  const shouldProceed = scripted ? true : await confirm2({ message: "Proceed with formation?", default: true });
6600
6881
  if (!shouldProceed) {
6601
- console.log(chalk11.yellow("Formation cancelled."));
6882
+ console.log(chalk12.yellow("Formation cancelled."));
6602
6883
  return;
6603
6884
  }
6604
6885
  const members = founders.map((f) => {
@@ -6654,17 +6935,17 @@ async function formCommand(opts) {
6654
6935
  if (holders.length > 0) {
6655
6936
  console.log();
6656
6937
  const table = new Table3({
6657
- head: [chalk11.dim("Holder"), chalk11.dim("Shares"), chalk11.dim("Ownership %")]
6938
+ head: [chalk12.dim("Holder"), chalk12.dim("Shares"), chalk12.dim("Ownership %")]
6658
6939
  });
6659
6940
  for (const h of holders) {
6660
6941
  const pct = typeof h.ownership_pct === "number" ? `${h.ownership_pct.toFixed(1)}%` : "\u2014";
6661
6942
  table.push([String(h.name ?? "?"), String(h.shares ?? 0), pct]);
6662
6943
  }
6663
- console.log(chalk11.bold(" Cap Table:"));
6944
+ console.log(chalk12.bold(" Cap Table:"));
6664
6945
  console.log(table.toString());
6665
6946
  }
6666
6947
  if (result.next_action) {
6667
- console.log(chalk11.yellow(`
6948
+ console.log(chalk12.yellow(`
6668
6949
  Next: ${result.next_action}`));
6669
6950
  }
6670
6951
  } catch (err) {
@@ -6735,7 +7016,7 @@ async function formCreateCommand(opts) {
6735
7016
  console.log(` Type: ${result.entity_type}`);
6736
7017
  console.log(` Jurisdiction: ${result.jurisdiction}`);
6737
7018
  console.log(` Status: ${result.formation_status}`);
6738
- console.log(chalk11.yellow(`
7019
+ console.log(chalk12.yellow(`
6739
7020
  Next: corp form add-founder @last:entity --name "..." --email "..." --role member --pct 50`));
6740
7021
  } catch (err) {
6741
7022
  printError(`Failed to create pending entity: ${err}`);
@@ -6773,7 +7054,7 @@ async function formAddFounderCommand(entityId, opts) {
6773
7054
  const pct = typeof m.ownership_pct === "number" ? ` (${m.ownership_pct}%)` : "";
6774
7055
  console.log(` - ${m.name} <${m.email ?? "no email"}> [${m.role ?? "member"}]${pct}`);
6775
7056
  }
6776
- console.log(chalk11.yellow(`
7057
+ console.log(chalk12.yellow(`
6777
7058
  Next: add more founders or run: corp form finalize @last:entity`));
6778
7059
  } catch (err) {
6779
7060
  printError(`Failed to add founder: ${err}`);
@@ -6837,17 +7118,17 @@ async function formFinalizeCommand(entityId, opts) {
6837
7118
  if (holders.length > 0) {
6838
7119
  console.log();
6839
7120
  const table = new Table3({
6840
- head: [chalk11.dim("Holder"), chalk11.dim("Shares"), chalk11.dim("Ownership %")]
7121
+ head: [chalk12.dim("Holder"), chalk12.dim("Shares"), chalk12.dim("Ownership %")]
6841
7122
  });
6842
7123
  for (const h of holders) {
6843
7124
  const pct = typeof h.ownership_pct === "number" ? `${h.ownership_pct.toFixed(1)}%` : "\u2014";
6844
7125
  table.push([String(h.name ?? "?"), String(h.shares ?? 0), pct]);
6845
7126
  }
6846
- console.log(chalk11.bold(" Cap Table:"));
7127
+ console.log(chalk12.bold(" Cap Table:"));
6847
7128
  console.log(table.toString());
6848
7129
  }
6849
7130
  if (result.next_action) {
6850
- console.log(chalk11.yellow(`
7131
+ console.log(chalk12.yellow(`
6851
7132
  Next: ${result.next_action}`));
6852
7133
  }
6853
7134
  } catch (err) {
@@ -6920,7 +7201,7 @@ var api_keys_exports = {};
6920
7201
  __export(api_keys_exports, {
6921
7202
  apiKeysCommand: () => apiKeysCommand
6922
7203
  });
6923
- import chalk12 from "chalk";
7204
+ import chalk13 from "chalk";
6924
7205
  import Table4 from "cli-table3";
6925
7206
  async function apiKeysCommand(opts) {
6926
7207
  const cfg = requireConfig("api_url", "api_key", "workspace_id");
@@ -6940,9 +7221,9 @@ async function apiKeysCommand(opts) {
6940
7221
  return;
6941
7222
  }
6942
7223
  console.log(`
6943
- ${chalk12.bold("API Keys")}`);
7224
+ ${chalk13.bold("API Keys")}`);
6944
7225
  const table = new Table4({
6945
- head: [chalk12.dim("ID"), chalk12.dim("Name"), chalk12.dim("Key"), chalk12.dim("Created")]
7226
+ head: [chalk13.dim("ID"), chalk13.dim("Name"), chalk13.dim("Key"), chalk13.dim("Created")]
6946
7227
  });
6947
7228
  for (const k of keys) {
6948
7229
  table.push([
@@ -7224,7 +7505,7 @@ var feedback_exports = {};
7224
7505
  __export(feedback_exports, {
7225
7506
  feedbackCommand: () => feedbackCommand
7226
7507
  });
7227
- import chalk13 from "chalk";
7508
+ import chalk14 from "chalk";
7228
7509
  async function feedbackCommand(message, opts) {
7229
7510
  if (!message || message.trim().length === 0) {
7230
7511
  printError("Feedback message cannot be empty");
@@ -7243,7 +7524,7 @@ async function feedbackCommand(message, opts) {
7243
7524
  return;
7244
7525
  }
7245
7526
  console.log(`
7246
- ${chalk13.green("\u2713")} Feedback submitted (${chalk13.dim(result.feedback_id)})`);
7527
+ ${chalk14.green("\u2713")} Feedback submitted (${chalk14.dim(result.feedback_id)})`);
7247
7528
  } catch (err) {
7248
7529
  const detail = String(err);
7249
7530
  if (detail.includes("404")) {
@@ -7273,38 +7554,8 @@ var serve_exports = {};
7273
7554
  __export(serve_exports, {
7274
7555
  serveCommand: () => serveCommand
7275
7556
  });
7276
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
7277
7557
  import { resolve as resolve3 } from "path";
7278
- import { randomBytes } from "crypto";
7279
- function generateFernetKey() {
7280
- return randomBytes(32).toString("base64url") + "=";
7281
- }
7282
- function generateSecret(length = 32) {
7283
- return randomBytes(length).toString("hex");
7284
- }
7285
- function loadEnvFile(path) {
7286
- if (!existsSync2(path)) return;
7287
- const content = readFileSync4(path, "utf-8");
7288
- for (const line of content.split("\n")) {
7289
- const trimmed = line.trim();
7290
- if (!trimmed || trimmed.startsWith("#")) continue;
7291
- const eqIdx = trimmed.indexOf("=");
7292
- if (eqIdx === -1) continue;
7293
- const key = trimmed.slice(0, eqIdx).trim();
7294
- const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
7295
- if (!process.env[key]) {
7296
- process.env[key] = value;
7297
- }
7298
- }
7299
- }
7300
- function ensureEnvFile(envPath) {
7301
- if (existsSync2(envPath)) return;
7302
- console.log("No .env file found. Generating one with dev defaults...\n");
7303
- const content = ENV_TEMPLATE.replace("{{JWT_SECRET}}", generateSecret()).replace("{{SECRETS_MASTER_KEY}}", generateFernetKey()).replace("{{INTERNAL_WORKER_TOKEN}}", generateSecret());
7304
- writeFileSync2(envPath, content, "utf-8");
7305
- console.log(` Created ${envPath}
7306
- `);
7307
- }
7558
+ import { ensureEnvFile, loadEnvFile } from "@thecorporation/corp-tools";
7308
7559
  async function serveCommand(opts) {
7309
7560
  let server;
7310
7561
  try {
@@ -7349,40 +7600,9 @@ async function serveCommand(opts) {
7349
7600
  process.exit(code ?? 0);
7350
7601
  });
7351
7602
  }
7352
- var ENV_TEMPLATE;
7353
7603
  var init_serve = __esm({
7354
7604
  "src/commands/serve.ts"() {
7355
7605
  "use strict";
7356
- ENV_TEMPLATE = `# Corporation API server configuration
7357
- # Generated by: corp serve
7358
-
7359
- # Required \u2014 secret for signing JWTs
7360
- JWT_SECRET={{JWT_SECRET}}
7361
-
7362
- # Required \u2014 Fernet key for encrypting secrets at rest (base64url, 32 bytes)
7363
- SECRETS_MASTER_KEY={{SECRETS_MASTER_KEY}}
7364
-
7365
- # Required \u2014 bearer token for internal worker-to-server auth
7366
- INTERNAL_WORKER_TOKEN={{INTERNAL_WORKER_TOKEN}}
7367
-
7368
- # Server port (default: 8000)
7369
- # PORT=8000
7370
-
7371
- # Data directory for git repos (default: ./data/repos)
7372
- # DATA_DIR=./data/repos
7373
-
7374
- # Redis URL for agent job queue (optional)
7375
- # REDIS_URL=redis://localhost:6379/0
7376
-
7377
- # LLM proxy upstream (default: https://openrouter.ai/api/v1)
7378
- # LLM_UPSTREAM_URL=https://openrouter.ai/api/v1
7379
-
7380
- # PEM-encoded Ed25519 key for signing git commits (optional)
7381
- # COMMIT_SIGNING_KEY=
7382
-
7383
- # Max agent queue depth (default: 1000)
7384
- # MAX_QUEUE_DEPTH=1000
7385
- `;
7386
7606
  }
7387
7607
  });
7388
7608
 
@@ -8140,6 +8360,59 @@ workItemsCmd.command("cancel <item-ref>").option("--json", "Output as JSON").des
8140
8360
  json: inheritOption(opts.json, parent.json)
8141
8361
  });
8142
8362
  });
8363
+ var servicesCmd = program.command("services").description("Service catalog and fulfillment").option("--entity-id <ref>", "Entity reference (ID, short ID, @last, or unique name)").option("--json", "Output as JSON");
8364
+ servicesCmd.command("catalog").option("--json", "Output as JSON").description("List the service catalog").action(async (opts, cmd) => {
8365
+ const parent = cmd.parent.opts();
8366
+ const { servicesCatalogCommand: servicesCatalogCommand2 } = await Promise.resolve().then(() => (init_services(), services_exports));
8367
+ await servicesCatalogCommand2({
8368
+ json: inheritOption(opts.json, parent.json)
8369
+ });
8370
+ });
8371
+ servicesCmd.command("buy <slug>").option("--json", "Output as JSON").option("--dry-run", "Show the request without executing").description("Purchase a service from the catalog").action(async (slug, opts, cmd) => {
8372
+ const parent = cmd.parent.opts();
8373
+ const { servicesBuyCommand: servicesBuyCommand2 } = await Promise.resolve().then(() => (init_services(), services_exports));
8374
+ await servicesBuyCommand2(slug, {
8375
+ ...opts,
8376
+ entityId: parent.entityId,
8377
+ json: inheritOption(opts.json, parent.json)
8378
+ });
8379
+ });
8380
+ servicesCmd.command("list").option("--json", "Output as JSON").description("List service requests for an entity").action(async (opts, cmd) => {
8381
+ const parent = cmd.parent.opts();
8382
+ const { servicesListCommand: servicesListCommand2 } = await Promise.resolve().then(() => (init_services(), services_exports));
8383
+ await servicesListCommand2({
8384
+ ...opts,
8385
+ entityId: parent.entityId,
8386
+ json: inheritOption(opts.json, parent.json)
8387
+ });
8388
+ });
8389
+ servicesCmd.command("show <ref>").option("--json", "Output as JSON").description("Show service request detail").action(async (ref_, opts, cmd) => {
8390
+ const parent = cmd.parent.opts();
8391
+ const { servicesShowCommand: servicesShowCommand2 } = await Promise.resolve().then(() => (init_services(), services_exports));
8392
+ await servicesShowCommand2(ref_, {
8393
+ ...opts,
8394
+ entityId: parent.entityId,
8395
+ json: inheritOption(opts.json, parent.json)
8396
+ });
8397
+ });
8398
+ servicesCmd.command("fulfill <ref>").option("--note <note>", "Fulfillment note").option("--json", "Output as JSON").description("Mark a service request as fulfilled (operator)").action(async (ref_, opts, cmd) => {
8399
+ const parent = cmd.parent.opts();
8400
+ const { servicesFulfillCommand: servicesFulfillCommand2 } = await Promise.resolve().then(() => (init_services(), services_exports));
8401
+ await servicesFulfillCommand2(ref_, {
8402
+ ...opts,
8403
+ entityId: parent.entityId,
8404
+ json: inheritOption(opts.json, parent.json)
8405
+ });
8406
+ });
8407
+ servicesCmd.command("cancel <ref>").option("--json", "Output as JSON").description("Cancel a service request").action(async (ref_, opts, cmd) => {
8408
+ const parent = cmd.parent.opts();
8409
+ const { servicesCancelCommand: servicesCancelCommand2 } = await Promise.resolve().then(() => (init_services(), services_exports));
8410
+ await servicesCancelCommand2(ref_, {
8411
+ ...opts,
8412
+ entityId: parent.entityId,
8413
+ json: inheritOption(opts.json, parent.json)
8414
+ });
8415
+ });
8143
8416
  var billingCmd = program.command("billing").description("Billing status, plans, and subscription management").option("--json", "Output as JSON").action(async (opts) => {
8144
8417
  const { billingCommand: billingCommand2 } = await Promise.resolve().then(() => (init_billing(), billing_exports));
8145
8418
  await billingCommand2(opts);