@thecorporation/corp-tools 26.3.24 → 26.3.32
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/browser.d.ts +3464 -0
- package/dist/browser.js +1518 -0
- package/dist/browser.js.map +1 -0
- package/dist/index.d.ts +157 -1
- package/dist/index.js +694 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/browser.js
ADDED
|
@@ -0,0 +1,1518 @@
|
|
|
1
|
+
// src/process-transport.ts
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
4
|
+
import { resolve, join } from "path";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
|
|
8
|
+
// src/env.ts
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
10
|
+
import { randomBytes } from "crypto";
|
|
11
|
+
var ENV_TEMPLATE = `# Corporation API server configuration
|
|
12
|
+
# Generated by: corp serve
|
|
13
|
+
|
|
14
|
+
# Required \u2014 secret for signing JWTs
|
|
15
|
+
JWT_SECRET={{JWT_SECRET}}
|
|
16
|
+
|
|
17
|
+
# Required \u2014 Fernet key for encrypting secrets at rest (base64url, 32 bytes)
|
|
18
|
+
SECRETS_MASTER_KEY={{SECRETS_MASTER_KEY}}
|
|
19
|
+
|
|
20
|
+
# Required \u2014 bearer token for internal worker-to-server auth
|
|
21
|
+
INTERNAL_WORKER_TOKEN={{INTERNAL_WORKER_TOKEN}}
|
|
22
|
+
|
|
23
|
+
# Server port (default: 8000)
|
|
24
|
+
# PORT=8000
|
|
25
|
+
|
|
26
|
+
# Data directory for git repos (default: ./data/repos)
|
|
27
|
+
# DATA_DIR=./data/repos
|
|
28
|
+
|
|
29
|
+
# Redis URL for agent job queue (optional)
|
|
30
|
+
# REDIS_URL=redis://localhost:6379/0
|
|
31
|
+
|
|
32
|
+
# LLM proxy upstream (default: https://openrouter.ai/api/v1)
|
|
33
|
+
# LLM_UPSTREAM_URL=https://openrouter.ai/api/v1
|
|
34
|
+
|
|
35
|
+
# PEM-encoded Ed25519 key for signing git commits (optional)
|
|
36
|
+
# COMMIT_SIGNING_KEY=
|
|
37
|
+
|
|
38
|
+
# Max agent queue depth (default: 1000)
|
|
39
|
+
# MAX_QUEUE_DEPTH=1000
|
|
40
|
+
`;
|
|
41
|
+
function generateFernetKey() {
|
|
42
|
+
return randomBytes(32).toString("base64url") + "=";
|
|
43
|
+
}
|
|
44
|
+
function generateSecret(length = 32) {
|
|
45
|
+
return randomBytes(length).toString("hex");
|
|
46
|
+
}
|
|
47
|
+
function loadEnvFile(path) {
|
|
48
|
+
if (!existsSync(path)) return;
|
|
49
|
+
const content = readFileSync(path, "utf-8");
|
|
50
|
+
for (const line of content.split("\n")) {
|
|
51
|
+
const trimmed = line.trim();
|
|
52
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
53
|
+
const eqIdx = trimmed.indexOf("=");
|
|
54
|
+
if (eqIdx === -1) continue;
|
|
55
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
56
|
+
const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
57
|
+
if (!process.env[key]) {
|
|
58
|
+
process.env[key] = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function ensureEnvFile(envPath) {
|
|
63
|
+
if (existsSync(envPath)) return;
|
|
64
|
+
console.log("No .env file found. Generating one with dev defaults...\n");
|
|
65
|
+
const content = ENV_TEMPLATE.replace("{{JWT_SECRET}}", generateSecret()).replace("{{SECRETS_MASTER_KEY}}", generateFernetKey()).replace("{{INTERNAL_WORKER_TOKEN}}", generateSecret());
|
|
66
|
+
writeFileSync(envPath, content, "utf-8");
|
|
67
|
+
console.log(` Created ${envPath}
|
|
68
|
+
`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/process-transport.ts
|
|
72
|
+
var require2 = createRequire(import.meta.url);
|
|
73
|
+
var cachedBinaryPath;
|
|
74
|
+
function resolveBinaryPath(processUrl) {
|
|
75
|
+
if (cachedBinaryPath !== void 0) return cachedBinaryPath;
|
|
76
|
+
const parsed = new URL(processUrl);
|
|
77
|
+
if (parsed.pathname && parsed.pathname !== "/") {
|
|
78
|
+
cachedBinaryPath = parsed.pathname;
|
|
79
|
+
return cachedBinaryPath;
|
|
80
|
+
}
|
|
81
|
+
const envBin = process.env.CORP_SERVER_BIN;
|
|
82
|
+
if (envBin && existsSync2(envBin)) {
|
|
83
|
+
cachedBinaryPath = envBin;
|
|
84
|
+
return cachedBinaryPath;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const server = require2("@thecorporation/server");
|
|
88
|
+
const pkgPath = server.getBinaryPath?.();
|
|
89
|
+
if (pkgPath) {
|
|
90
|
+
cachedBinaryPath = pkgPath;
|
|
91
|
+
return pkgPath;
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
cachedBinaryPath = resolve("services/api-rs/target/release/api-rs");
|
|
96
|
+
return cachedBinaryPath;
|
|
97
|
+
}
|
|
98
|
+
function parseStatusFromStderr(stderr) {
|
|
99
|
+
const lines = stderr.split("\n");
|
|
100
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
101
|
+
const match = lines[i].match(/^HTTP (\d+)$/);
|
|
102
|
+
if (match) return parseInt(match[1], 10);
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
var STATUS_TEXT = {
|
|
107
|
+
200: "OK",
|
|
108
|
+
201: "Created",
|
|
109
|
+
204: "No Content",
|
|
110
|
+
400: "Bad Request",
|
|
111
|
+
401: "Unauthorized",
|
|
112
|
+
403: "Forbidden",
|
|
113
|
+
404: "Not Found",
|
|
114
|
+
409: "Conflict",
|
|
115
|
+
422: "Unprocessable Entity",
|
|
116
|
+
500: "Internal Server Error"
|
|
117
|
+
};
|
|
118
|
+
function buildProcessResponse(status, body) {
|
|
119
|
+
return {
|
|
120
|
+
status,
|
|
121
|
+
ok: status >= 200 && status < 300,
|
|
122
|
+
statusText: STATUS_TEXT[status] ?? String(status),
|
|
123
|
+
headers: new Headers({ "content-type": "application/json" }),
|
|
124
|
+
json: async () => JSON.parse(body),
|
|
125
|
+
text: async () => body,
|
|
126
|
+
body: null,
|
|
127
|
+
bodyUsed: false,
|
|
128
|
+
redirected: false,
|
|
129
|
+
type: "basic",
|
|
130
|
+
url: "",
|
|
131
|
+
clone: () => buildProcessResponse(status, body),
|
|
132
|
+
arrayBuffer: async () => new TextEncoder().encode(body).buffer,
|
|
133
|
+
blob: async () => new Blob([body]),
|
|
134
|
+
formData: async () => {
|
|
135
|
+
throw new Error("not supported");
|
|
136
|
+
},
|
|
137
|
+
bytes: async () => new TextEncoder().encode(body)
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
var CORP_CONFIG_DIR = process.env.CORP_CONFIG_DIR || join(homedir(), ".corp");
|
|
141
|
+
function readJsonFileSafe(path) {
|
|
142
|
+
try {
|
|
143
|
+
if (!existsSync2(path)) return null;
|
|
144
|
+
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
145
|
+
} catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function loadServerSecretsFromAuth() {
|
|
150
|
+
const auth = readJsonFileSafe(join(CORP_CONFIG_DIR, "auth.json"));
|
|
151
|
+
if (!auth) return null;
|
|
152
|
+
const ss = auth.server_secrets;
|
|
153
|
+
if (!ss || typeof ss !== "object") return null;
|
|
154
|
+
const s = ss;
|
|
155
|
+
if (typeof s.jwt_secret === "string" && typeof s.secrets_master_key === "string" && typeof s.internal_worker_token === "string") {
|
|
156
|
+
return { jwt_secret: s.jwt_secret, secrets_master_key: s.secrets_master_key, internal_worker_token: s.internal_worker_token };
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
function loadDataDirFromConfig() {
|
|
161
|
+
const cfg = readJsonFileSafe(join(CORP_CONFIG_DIR, "config.json"));
|
|
162
|
+
if (!cfg) return void 0;
|
|
163
|
+
const dataDir = typeof cfg.data_dir === "string" ? cfg.data_dir : void 0;
|
|
164
|
+
return dataDir || void 0;
|
|
165
|
+
}
|
|
166
|
+
var envLoaded = false;
|
|
167
|
+
function ensureEnv() {
|
|
168
|
+
if (envLoaded) return;
|
|
169
|
+
const secrets = loadServerSecretsFromAuth();
|
|
170
|
+
if (secrets) {
|
|
171
|
+
if (!process.env.JWT_SECRET) process.env.JWT_SECRET = secrets.jwt_secret;
|
|
172
|
+
if (!process.env.SECRETS_MASTER_KEY) process.env.SECRETS_MASTER_KEY = secrets.secrets_master_key;
|
|
173
|
+
if (!process.env.INTERNAL_WORKER_TOKEN) process.env.INTERNAL_WORKER_TOKEN = secrets.internal_worker_token;
|
|
174
|
+
envLoaded = true;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const envPath = resolve(process.cwd(), ".env");
|
|
178
|
+
ensureEnvFile(envPath);
|
|
179
|
+
loadEnvFile(envPath);
|
|
180
|
+
envLoaded = true;
|
|
181
|
+
}
|
|
182
|
+
function processRequest(processUrl, method, pathWithQuery, headers, body, options) {
|
|
183
|
+
ensureEnv();
|
|
184
|
+
const binPath = resolveBinaryPath(processUrl);
|
|
185
|
+
const args = ["--skip-validation", "call"];
|
|
186
|
+
const dataDir = options?.dataDir ?? loadDataDirFromConfig();
|
|
187
|
+
if (dataDir) {
|
|
188
|
+
args.push("--data-dir", dataDir);
|
|
189
|
+
}
|
|
190
|
+
args.push(method, pathWithQuery);
|
|
191
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
192
|
+
args.push("-H", `${key}: ${value}`);
|
|
193
|
+
}
|
|
194
|
+
if (body) {
|
|
195
|
+
args.push("--stdin");
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
const stdout = execFileSync(binPath, args, {
|
|
199
|
+
input: body ?? void 0,
|
|
200
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
201
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
202
|
+
env: process.env
|
|
203
|
+
});
|
|
204
|
+
return buildProcessResponse(200, stdout.toString("utf-8"));
|
|
205
|
+
} catch (err) {
|
|
206
|
+
const execErr = err;
|
|
207
|
+
if (execErr.stdout === void 0 && execErr.stderr === void 0) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`No api-rs binary found. Install @thecorporation/server or set CORP_SERVER_BIN.
|
|
210
|
+
Attempted: ${binPath}
|
|
211
|
+
Error: ${execErr.message ?? String(err)}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const stderr = execErr.stderr?.toString("utf-8") ?? "";
|
|
215
|
+
const stdout = execErr.stdout?.toString("utf-8") ?? "";
|
|
216
|
+
const status = parseStatusFromStderr(stderr);
|
|
217
|
+
if (status !== null) {
|
|
218
|
+
return buildProcessResponse(status, stdout);
|
|
219
|
+
}
|
|
220
|
+
const isConfigError = stderr.includes("panic") || stderr.includes("INTERNAL_WORKER_TOKEN") || stderr.includes("JWT_SECRET") || stderr.includes("SECRETS_MASTER_KEY");
|
|
221
|
+
if (isConfigError) {
|
|
222
|
+
throw new Error("Server configuration incomplete. Run 'corp setup' to configure local mode.");
|
|
223
|
+
}
|
|
224
|
+
throw new Error(`api-rs process failed:
|
|
225
|
+
${stderr || stdout || String(err)}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/api-client.ts
|
|
230
|
+
var SessionExpiredError = class extends Error {
|
|
231
|
+
constructor(detail) {
|
|
232
|
+
super(detail || "Your API key is no longer valid. Run 'corp setup' to re-authenticate.");
|
|
233
|
+
this.name = "SessionExpiredError";
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
var MAX_ERROR_DETAIL_LEN = 500;
|
|
237
|
+
function sanitizeErrorDetail(value) {
|
|
238
|
+
const sanitized = value.replace(/[\u0000-\u001f\u007f-\u009f\u001b]/g, " ").replace(/\s+/g, " ").trim();
|
|
239
|
+
if (!sanitized) {
|
|
240
|
+
return "request failed";
|
|
241
|
+
}
|
|
242
|
+
return sanitized.length > MAX_ERROR_DETAIL_LEN ? `${sanitized.slice(0, MAX_ERROR_DETAIL_LEN)}...` : sanitized;
|
|
243
|
+
}
|
|
244
|
+
function pathSegment(value) {
|
|
245
|
+
return encodeURIComponent(String(value));
|
|
246
|
+
}
|
|
247
|
+
async function extractErrorMessage(resp) {
|
|
248
|
+
try {
|
|
249
|
+
const text = await resp.text();
|
|
250
|
+
try {
|
|
251
|
+
const json = JSON.parse(text);
|
|
252
|
+
const val = json.error || json.message || json.detail;
|
|
253
|
+
if (val == null) return sanitizeErrorDetail(text);
|
|
254
|
+
return sanitizeErrorDetail(typeof val === "string" ? val : JSON.stringify(val));
|
|
255
|
+
} catch {
|
|
256
|
+
return sanitizeErrorDetail(text);
|
|
257
|
+
}
|
|
258
|
+
} catch {
|
|
259
|
+
return sanitizeErrorDetail(resp.statusText);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
var CorpAPIClient = class {
|
|
263
|
+
apiUrl;
|
|
264
|
+
apiKey;
|
|
265
|
+
workspaceId;
|
|
266
|
+
constructor(apiUrl, apiKey, workspaceId) {
|
|
267
|
+
this.apiUrl = apiUrl.startsWith("process://") ? apiUrl : apiUrl.replace(/\/+$/, "");
|
|
268
|
+
this.apiKey = apiKey;
|
|
269
|
+
this.workspaceId = workspaceId;
|
|
270
|
+
}
|
|
271
|
+
headers() {
|
|
272
|
+
return {
|
|
273
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
274
|
+
"Content-Type": "application/json",
|
|
275
|
+
Accept: "application/json"
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
async request(method, path, body, params) {
|
|
279
|
+
let fullPath = path;
|
|
280
|
+
if (params) {
|
|
281
|
+
const qs = new URLSearchParams(params).toString();
|
|
282
|
+
if (qs) fullPath += `?${qs}`;
|
|
283
|
+
}
|
|
284
|
+
if (this.apiUrl.startsWith("process://")) {
|
|
285
|
+
const hdrs = this.headers();
|
|
286
|
+
const bodyStr = body !== void 0 ? JSON.stringify(body) : void 0;
|
|
287
|
+
return processRequest(this.apiUrl, method, fullPath, hdrs, bodyStr);
|
|
288
|
+
}
|
|
289
|
+
const url = `${this.apiUrl}${fullPath}`;
|
|
290
|
+
const opts = { method, headers: this.headers() };
|
|
291
|
+
if (body !== void 0) opts.body = JSON.stringify(body);
|
|
292
|
+
return fetch(url, opts);
|
|
293
|
+
}
|
|
294
|
+
async throwIfError(resp) {
|
|
295
|
+
if (!resp.ok) {
|
|
296
|
+
const detail = await extractErrorMessage(resp);
|
|
297
|
+
if (resp.status === 401) throw new SessionExpiredError(detail);
|
|
298
|
+
const prefix = resp.status >= 500 ? "Server error" : resp.status === 404 ? "Not found" : resp.status === 422 ? "Validation error" : `HTTP ${resp.status}`;
|
|
299
|
+
throw new Error(`${prefix}: ${detail}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async get(path, params) {
|
|
303
|
+
const resp = await this.request("GET", path, void 0, params);
|
|
304
|
+
await this.throwIfError(resp);
|
|
305
|
+
return resp.json();
|
|
306
|
+
}
|
|
307
|
+
async post(path, body) {
|
|
308
|
+
const resp = await this.request("POST", path, body);
|
|
309
|
+
await this.throwIfError(resp);
|
|
310
|
+
return resp.json();
|
|
311
|
+
}
|
|
312
|
+
async postWithParams(path, body, params) {
|
|
313
|
+
const resp = await this.request("POST", path, body, params);
|
|
314
|
+
await this.throwIfError(resp);
|
|
315
|
+
return resp.json();
|
|
316
|
+
}
|
|
317
|
+
async patch(path, body) {
|
|
318
|
+
const resp = await this.request("PATCH", path, body);
|
|
319
|
+
await this.throwIfError(resp);
|
|
320
|
+
return resp.json();
|
|
321
|
+
}
|
|
322
|
+
async del(path) {
|
|
323
|
+
const resp = await this.request("DELETE", path);
|
|
324
|
+
await this.throwIfError(resp);
|
|
325
|
+
}
|
|
326
|
+
/** Public generic GET for declarative/registry-driven commands. */
|
|
327
|
+
async fetchJSON(path, params) {
|
|
328
|
+
return this.get(path, params);
|
|
329
|
+
}
|
|
330
|
+
/** Public generic request for declarative/registry-driven write commands. */
|
|
331
|
+
async submitJSON(method, path, body) {
|
|
332
|
+
const resp = await this.request(method, path, body);
|
|
333
|
+
await this.throwIfError(resp);
|
|
334
|
+
const text = await resp.text();
|
|
335
|
+
if (!text) return {};
|
|
336
|
+
return JSON.parse(text);
|
|
337
|
+
}
|
|
338
|
+
// --- Workspace ---
|
|
339
|
+
getStatus() {
|
|
340
|
+
return this.get(`/v1/workspaces/${pathSegment(this.workspaceId)}/status`);
|
|
341
|
+
}
|
|
342
|
+
// --- Obligations ---
|
|
343
|
+
getObligations(tier) {
|
|
344
|
+
const params = {};
|
|
345
|
+
if (tier) params.tier = tier;
|
|
346
|
+
return this.get("/v1/obligations/summary", params);
|
|
347
|
+
}
|
|
348
|
+
// --- Digests ---
|
|
349
|
+
listDigests() {
|
|
350
|
+
return this.get("/v1/digests");
|
|
351
|
+
}
|
|
352
|
+
triggerDigest() {
|
|
353
|
+
return this.post("/v1/digests/trigger");
|
|
354
|
+
}
|
|
355
|
+
getDigest(key) {
|
|
356
|
+
return this.get(`/v1/digests/${pathSegment(key)}`);
|
|
357
|
+
}
|
|
358
|
+
// --- References ---
|
|
359
|
+
syncReferences(kind, items, entityId) {
|
|
360
|
+
const body = { kind, items };
|
|
361
|
+
if (entityId) body.entity_id = entityId;
|
|
362
|
+
return this.post("/v1/references/sync", body);
|
|
363
|
+
}
|
|
364
|
+
// --- Entities ---
|
|
365
|
+
listEntities() {
|
|
366
|
+
return this.get("/v1/entities");
|
|
367
|
+
}
|
|
368
|
+
// --- Contacts ---
|
|
369
|
+
listContacts(entityId) {
|
|
370
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/contacts`);
|
|
371
|
+
}
|
|
372
|
+
getContact(id, entityId) {
|
|
373
|
+
return this.get(`/v1/contacts/${pathSegment(id)}`, { entity_id: entityId });
|
|
374
|
+
}
|
|
375
|
+
getContactProfile(id, entityId) {
|
|
376
|
+
return this.get(`/v1/contacts/${pathSegment(id)}/profile`, { entity_id: entityId });
|
|
377
|
+
}
|
|
378
|
+
createContact(data) {
|
|
379
|
+
return this.post("/v1/contacts", data);
|
|
380
|
+
}
|
|
381
|
+
updateContact(id, data) {
|
|
382
|
+
return this.patch(`/v1/contacts/${pathSegment(id)}`, data);
|
|
383
|
+
}
|
|
384
|
+
getNotificationPrefs(contactId) {
|
|
385
|
+
return this.get(`/v1/contacts/${pathSegment(contactId)}/notification-prefs`);
|
|
386
|
+
}
|
|
387
|
+
updateNotificationPrefs(contactId, prefs) {
|
|
388
|
+
return this.patch(`/v1/contacts/${pathSegment(contactId)}/notification-prefs`, prefs);
|
|
389
|
+
}
|
|
390
|
+
// --- Cap Table ---
|
|
391
|
+
getCapTable(entityId) {
|
|
392
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/cap-table`);
|
|
393
|
+
}
|
|
394
|
+
async getSafeNotes(entityId) {
|
|
395
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/safe-notes`);
|
|
396
|
+
}
|
|
397
|
+
createSafeNote(data) {
|
|
398
|
+
return this.post("/v1/safe-notes", data);
|
|
399
|
+
}
|
|
400
|
+
listShareTransfers(entityId) {
|
|
401
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/share-transfers`);
|
|
402
|
+
}
|
|
403
|
+
getShareTransfers(entityId) {
|
|
404
|
+
return this.listShareTransfers(entityId);
|
|
405
|
+
}
|
|
406
|
+
getValuations(entityId) {
|
|
407
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/valuations`);
|
|
408
|
+
}
|
|
409
|
+
getCurrent409a(entityId) {
|
|
410
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/current-409a`);
|
|
411
|
+
}
|
|
412
|
+
createValuation(data) {
|
|
413
|
+
return this.post("/v1/valuations", data);
|
|
414
|
+
}
|
|
415
|
+
createInstrument(data) {
|
|
416
|
+
return this.post("/v1/equity/instruments", data);
|
|
417
|
+
}
|
|
418
|
+
submitValuationForApproval(valuationId, entityId) {
|
|
419
|
+
return this.post(`/v1/valuations/${pathSegment(valuationId)}/submit-for-approval`, { entity_id: entityId });
|
|
420
|
+
}
|
|
421
|
+
approveValuation(valuationId, entityId, resolutionId) {
|
|
422
|
+
const body = { entity_id: entityId };
|
|
423
|
+
if (resolutionId) body.resolution_id = resolutionId;
|
|
424
|
+
return this.post(`/v1/valuations/${pathSegment(valuationId)}/approve`, body);
|
|
425
|
+
}
|
|
426
|
+
transferShares(data) {
|
|
427
|
+
return this.post("/v1/equity/transfer-workflows", data);
|
|
428
|
+
}
|
|
429
|
+
calculateDistribution(data) {
|
|
430
|
+
return this.post("/v1/distributions", data);
|
|
431
|
+
}
|
|
432
|
+
// --- Equity rounds (v1) ---
|
|
433
|
+
createEquityRound(data) {
|
|
434
|
+
return this.post("/v1/equity/rounds", data);
|
|
435
|
+
}
|
|
436
|
+
applyEquityRoundTerms(roundId, data) {
|
|
437
|
+
return this.post(`/v1/equity/rounds/${pathSegment(roundId)}/apply-terms`, data);
|
|
438
|
+
}
|
|
439
|
+
boardApproveEquityRound(roundId, data) {
|
|
440
|
+
return this.post(`/v1/equity/rounds/${pathSegment(roundId)}/board-approve`, data);
|
|
441
|
+
}
|
|
442
|
+
acceptEquityRound(roundId, data) {
|
|
443
|
+
return this.post(`/v1/equity/rounds/${pathSegment(roundId)}/accept`, data);
|
|
444
|
+
}
|
|
445
|
+
previewRoundConversion(data) {
|
|
446
|
+
return this.post("/v1/equity/conversions/preview", data);
|
|
447
|
+
}
|
|
448
|
+
executeRoundConversion(data) {
|
|
449
|
+
return this.post("/v1/equity/conversions/execute", data);
|
|
450
|
+
}
|
|
451
|
+
// --- Staged equity rounds ---
|
|
452
|
+
listEquityRounds(entityId) {
|
|
453
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/equity-rounds`);
|
|
454
|
+
}
|
|
455
|
+
startEquityRound(data) {
|
|
456
|
+
return this.post("/v1/equity/rounds/staged", data);
|
|
457
|
+
}
|
|
458
|
+
addRoundSecurity(roundId, data) {
|
|
459
|
+
return this.post(`/v1/equity/rounds/${pathSegment(roundId)}/securities`, data);
|
|
460
|
+
}
|
|
461
|
+
issueRound(roundId, data) {
|
|
462
|
+
return this.post(`/v1/equity/rounds/${pathSegment(roundId)}/issue`, data);
|
|
463
|
+
}
|
|
464
|
+
// --- Intent lifecycle helpers ---
|
|
465
|
+
createExecutionIntent(data) {
|
|
466
|
+
return this.post("/v1/execution/intents", data);
|
|
467
|
+
}
|
|
468
|
+
evaluateIntent(intentId, entityId) {
|
|
469
|
+
return this.postWithParams(`/v1/intents/${pathSegment(intentId)}/evaluate`, {}, { entity_id: entityId });
|
|
470
|
+
}
|
|
471
|
+
authorizeIntent(intentId, entityId) {
|
|
472
|
+
return this.postWithParams(`/v1/intents/${pathSegment(intentId)}/authorize`, {}, { entity_id: entityId });
|
|
473
|
+
}
|
|
474
|
+
// --- Governance ---
|
|
475
|
+
listGovernanceBodies(entityId) {
|
|
476
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/governance-bodies`);
|
|
477
|
+
}
|
|
478
|
+
getGovernanceSeats(bodyId, entityId) {
|
|
479
|
+
return this.get(`/v1/governance-bodies/${pathSegment(bodyId)}/seats`, { entity_id: entityId });
|
|
480
|
+
}
|
|
481
|
+
listMeetings(bodyId, entityId) {
|
|
482
|
+
return this.get(`/v1/governance-bodies/${pathSegment(bodyId)}/meetings`, { entity_id: entityId });
|
|
483
|
+
}
|
|
484
|
+
getMeetingResolutions(meetingId, entityId) {
|
|
485
|
+
return this.get(`/v1/meetings/${pathSegment(meetingId)}/resolutions`, { entity_id: entityId });
|
|
486
|
+
}
|
|
487
|
+
scheduleMeeting(data) {
|
|
488
|
+
return this.post("/v1/meetings", data);
|
|
489
|
+
}
|
|
490
|
+
conveneMeeting(meetingId, entityId, data) {
|
|
491
|
+
return this.postWithParams(`/v1/meetings/${pathSegment(meetingId)}/convene`, data, { entity_id: entityId });
|
|
492
|
+
}
|
|
493
|
+
castVote(entityId, meetingId, itemId, data) {
|
|
494
|
+
return this.postWithParams(`/v1/meetings/${pathSegment(meetingId)}/agenda-items/${pathSegment(itemId)}/vote`, data, { entity_id: entityId });
|
|
495
|
+
}
|
|
496
|
+
sendNotice(meetingId, entityId) {
|
|
497
|
+
return this.postWithParams(`/v1/meetings/${pathSegment(meetingId)}/notice`, {}, { entity_id: entityId });
|
|
498
|
+
}
|
|
499
|
+
adjournMeeting(meetingId, entityId) {
|
|
500
|
+
return this.postWithParams(`/v1/meetings/${pathSegment(meetingId)}/adjourn`, {}, { entity_id: entityId });
|
|
501
|
+
}
|
|
502
|
+
reopenMeeting(meetingId, entityId) {
|
|
503
|
+
return this.postWithParams(`/v1/meetings/${pathSegment(meetingId)}/reopen`, {}, { entity_id: entityId });
|
|
504
|
+
}
|
|
505
|
+
cancelMeeting(meetingId, entityId) {
|
|
506
|
+
return this.postWithParams(`/v1/meetings/${pathSegment(meetingId)}/cancel`, {}, { entity_id: entityId });
|
|
507
|
+
}
|
|
508
|
+
finalizeAgendaItem(meetingId, itemId, data) {
|
|
509
|
+
return this.post(`/v1/meetings/${pathSegment(meetingId)}/agenda-items/${pathSegment(itemId)}/finalize`, data);
|
|
510
|
+
}
|
|
511
|
+
computeResolution(meetingId, itemId, entityId, data) {
|
|
512
|
+
return this.postWithParams(`/v1/meetings/${pathSegment(meetingId)}/agenda-items/${pathSegment(itemId)}/resolution`, data, { entity_id: entityId });
|
|
513
|
+
}
|
|
514
|
+
attachResolutionDocument(meetingId, resolutionId, data) {
|
|
515
|
+
return this.post(`/v1/meetings/${pathSegment(meetingId)}/resolutions/${pathSegment(resolutionId)}/attach-document`, data);
|
|
516
|
+
}
|
|
517
|
+
writtenConsent(data) {
|
|
518
|
+
return this.post("/v1/meetings/written-consent", data);
|
|
519
|
+
}
|
|
520
|
+
getGovernanceMode(entityId) {
|
|
521
|
+
return this.get("/v1/governance/mode", { entity_id: entityId });
|
|
522
|
+
}
|
|
523
|
+
setGovernanceMode(data) {
|
|
524
|
+
return this.post("/v1/governance/mode", data);
|
|
525
|
+
}
|
|
526
|
+
resignSeat(seatId, entityId) {
|
|
527
|
+
return this.post(`/v1/governance-seats/${pathSegment(seatId)}/resign`, { entity_id: entityId });
|
|
528
|
+
}
|
|
529
|
+
createGovernanceIncident(data) {
|
|
530
|
+
return this.post("/v1/governance/incidents", data);
|
|
531
|
+
}
|
|
532
|
+
listGovernanceIncidents(entityId) {
|
|
533
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/governance/incidents`);
|
|
534
|
+
}
|
|
535
|
+
resolveGovernanceIncident(incidentId, data) {
|
|
536
|
+
return this.post(`/v1/governance/incidents/${pathSegment(incidentId)}/resolve`, data);
|
|
537
|
+
}
|
|
538
|
+
getGovernanceProfile(entityId) {
|
|
539
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/governance/profile`);
|
|
540
|
+
}
|
|
541
|
+
listAgendaItems(meetingId, entityId) {
|
|
542
|
+
return this.get(`/v1/meetings/${pathSegment(meetingId)}/agenda-items`, { entity_id: entityId });
|
|
543
|
+
}
|
|
544
|
+
listVotes(meetingId, itemId, entityId) {
|
|
545
|
+
return this.get(`/v1/meetings/${pathSegment(meetingId)}/agenda-items/${pathSegment(itemId)}/votes`, { entity_id: entityId });
|
|
546
|
+
}
|
|
547
|
+
// --- Documents ---
|
|
548
|
+
getEntityDocuments(entityId) {
|
|
549
|
+
return this.get(`/v1/formations/${pathSegment(entityId)}/documents`);
|
|
550
|
+
}
|
|
551
|
+
getDocument(documentId, entityId) {
|
|
552
|
+
return this.get(`/v1/documents/${pathSegment(documentId)}`, { entity_id: entityId });
|
|
553
|
+
}
|
|
554
|
+
signDocument(documentId, entityId, data) {
|
|
555
|
+
return this.postWithParams(`/v1/documents/${pathSegment(documentId)}/sign`, data, { entity_id: entityId });
|
|
556
|
+
}
|
|
557
|
+
generateContract(data) {
|
|
558
|
+
return this.post("/v1/contracts", data);
|
|
559
|
+
}
|
|
560
|
+
getSigningLink(documentId, entityId) {
|
|
561
|
+
return this.get(`/v1/sign/${pathSegment(documentId)}`, { entity_id: entityId });
|
|
562
|
+
}
|
|
563
|
+
async validatePreviewPdf(entityId, documentId) {
|
|
564
|
+
const resp = await this.request("GET", "/v1/documents/preview/pdf/validate", void 0, { entity_id: entityId, document_id: documentId });
|
|
565
|
+
await this.throwIfError(resp);
|
|
566
|
+
return { entity_id: entityId, document_id: documentId };
|
|
567
|
+
}
|
|
568
|
+
getPreviewPdfUrl(entityId, documentId) {
|
|
569
|
+
if (this.apiUrl.startsWith("process://")) {
|
|
570
|
+
throw new Error(
|
|
571
|
+
"PDF preview is not available in local process transport mode.\n Use cloud mode (npx corp setup) or start a local HTTP server (npx corp serve) instead."
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
const qs = new URLSearchParams({ entity_id: entityId, document_id: documentId }).toString();
|
|
575
|
+
return `${this.apiUrl}/v1/documents/preview/pdf?${qs}`;
|
|
576
|
+
}
|
|
577
|
+
// --- Finance ---
|
|
578
|
+
listInvoices(entityId) {
|
|
579
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/invoices`);
|
|
580
|
+
}
|
|
581
|
+
listBankAccounts(entityId) {
|
|
582
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/bank-accounts`);
|
|
583
|
+
}
|
|
584
|
+
listPayments(entityId) {
|
|
585
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/payments`);
|
|
586
|
+
}
|
|
587
|
+
listPayrollRuns(entityId) {
|
|
588
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/payroll-runs`);
|
|
589
|
+
}
|
|
590
|
+
listDistributions(entityId) {
|
|
591
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/distributions`);
|
|
592
|
+
}
|
|
593
|
+
listReconciliations(entityId) {
|
|
594
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/reconciliations`);
|
|
595
|
+
}
|
|
596
|
+
createInvoice(data) {
|
|
597
|
+
return this.post("/v1/treasury/invoices", data);
|
|
598
|
+
}
|
|
599
|
+
runPayroll(data) {
|
|
600
|
+
return this.post("/v1/payroll/runs", data);
|
|
601
|
+
}
|
|
602
|
+
submitPayment(data) {
|
|
603
|
+
return this.post("/v1/payments", data);
|
|
604
|
+
}
|
|
605
|
+
openBankAccount(data) {
|
|
606
|
+
return this.post("/v1/treasury/bank-accounts", data);
|
|
607
|
+
}
|
|
608
|
+
activateBankAccount(bankAccountId, entityId) {
|
|
609
|
+
return this.postWithParams(`/v1/bank-accounts/${pathSegment(bankAccountId)}/activate`, {}, { entity_id: entityId });
|
|
610
|
+
}
|
|
611
|
+
classifyContractor(data) {
|
|
612
|
+
return this.post("/v1/contractors/classify", data);
|
|
613
|
+
}
|
|
614
|
+
reconcileLedger(data) {
|
|
615
|
+
return this.post("/v1/ledger/reconcile", data);
|
|
616
|
+
}
|
|
617
|
+
getFinancialStatements(entityId, params) {
|
|
618
|
+
return this.get("/v1/treasury/financial-statements", { entity_id: entityId, ...params ?? {} });
|
|
619
|
+
}
|
|
620
|
+
// --- Equity analytics ---
|
|
621
|
+
getDilutionPreview(entityId, roundId) {
|
|
622
|
+
return this.get("/v1/equity/dilution/preview", { entity_id: entityId, round_id: roundId });
|
|
623
|
+
}
|
|
624
|
+
getControlMap(entityId, rootEntityId) {
|
|
625
|
+
return this.get("/v1/equity/control-map", { entity_id: entityId, root_entity_id: rootEntityId });
|
|
626
|
+
}
|
|
627
|
+
// --- Tax ---
|
|
628
|
+
listTaxFilings(entityId) {
|
|
629
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/tax-filings`);
|
|
630
|
+
}
|
|
631
|
+
listDeadlines(entityId) {
|
|
632
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/deadlines`);
|
|
633
|
+
}
|
|
634
|
+
listContractorClassifications(entityId) {
|
|
635
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/contractor-classifications`);
|
|
636
|
+
}
|
|
637
|
+
fileTaxDocument(data) {
|
|
638
|
+
return this.post("/v1/tax/filings", data);
|
|
639
|
+
}
|
|
640
|
+
trackDeadline(data) {
|
|
641
|
+
return this.post("/v1/deadlines", data);
|
|
642
|
+
}
|
|
643
|
+
// --- Billing ---
|
|
644
|
+
getBillingStatus() {
|
|
645
|
+
return this.get("/v1/billing/status", { workspace_id: this.workspaceId });
|
|
646
|
+
}
|
|
647
|
+
getBillingPlans() {
|
|
648
|
+
return this.get("/v1/billing/plans").then((data) => {
|
|
649
|
+
if (typeof data === "object" && data !== null && "plans" in data) {
|
|
650
|
+
return data.plans;
|
|
651
|
+
}
|
|
652
|
+
return data;
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
createBillingPortal() {
|
|
656
|
+
return this.post("/v1/billing/portal", { workspace_id: this.workspaceId });
|
|
657
|
+
}
|
|
658
|
+
createBillingCheckout(planId, entityId) {
|
|
659
|
+
const body = { plan_id: planId };
|
|
660
|
+
if (entityId) body.entity_id = entityId;
|
|
661
|
+
return this.post("/v1/billing/checkout", body);
|
|
662
|
+
}
|
|
663
|
+
// --- Formations ---
|
|
664
|
+
getFormation(id) {
|
|
665
|
+
return this.get(`/v1/formations/${pathSegment(id)}`);
|
|
666
|
+
}
|
|
667
|
+
getFormationDocuments(id) {
|
|
668
|
+
return this.get(`/v1/formations/${pathSegment(id)}/documents`);
|
|
669
|
+
}
|
|
670
|
+
createFormation(data) {
|
|
671
|
+
return this.post("/v1/formations", data);
|
|
672
|
+
}
|
|
673
|
+
createFormationWithCapTable(data) {
|
|
674
|
+
return this.post("/v1/formations/with-cap-table", data);
|
|
675
|
+
}
|
|
676
|
+
createPendingEntity(data) {
|
|
677
|
+
return this.post("/v1/formations/pending", data);
|
|
678
|
+
}
|
|
679
|
+
addFounder(entityId, data) {
|
|
680
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/founders`, data);
|
|
681
|
+
}
|
|
682
|
+
finalizeFormation(entityId, data = {}) {
|
|
683
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/finalize`, data);
|
|
684
|
+
}
|
|
685
|
+
markFormationDocumentsSigned(entityId) {
|
|
686
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/mark-documents-signed`);
|
|
687
|
+
}
|
|
688
|
+
getFormationGates(entityId) {
|
|
689
|
+
return this.get(`/v1/formations/${pathSegment(entityId)}/gates`);
|
|
690
|
+
}
|
|
691
|
+
recordFilingAttestation(entityId, data) {
|
|
692
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/filing-attestation`, data);
|
|
693
|
+
}
|
|
694
|
+
addRegisteredAgentConsentEvidence(entityId, data) {
|
|
695
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/registered-agent-consent-evidence`, data);
|
|
696
|
+
}
|
|
697
|
+
submitFiling(entityId) {
|
|
698
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/submit-filing`);
|
|
699
|
+
}
|
|
700
|
+
confirmFiling(entityId, data) {
|
|
701
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/filing-confirmation`, data);
|
|
702
|
+
}
|
|
703
|
+
applyEin(entityId) {
|
|
704
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/apply-ein`);
|
|
705
|
+
}
|
|
706
|
+
confirmEin(entityId, data) {
|
|
707
|
+
return this.post(`/v1/formations/${pathSegment(entityId)}/ein-confirmation`, data);
|
|
708
|
+
}
|
|
709
|
+
// --- Human obligations ---
|
|
710
|
+
getHumanObligations() {
|
|
711
|
+
return this.get(`/v1/workspaces/${pathSegment(this.workspaceId)}/human-obligations`);
|
|
712
|
+
}
|
|
713
|
+
getSignerToken(obligationId) {
|
|
714
|
+
return this.post(`/v1/human-obligations/${pathSegment(obligationId)}/signer-token`);
|
|
715
|
+
}
|
|
716
|
+
// --- Next Steps ---
|
|
717
|
+
getEntityNextSteps(entityId) {
|
|
718
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/next-steps`);
|
|
719
|
+
}
|
|
720
|
+
getWorkspaceNextSteps() {
|
|
721
|
+
return this.get(`/v1/workspaces/${pathSegment(this.workspaceId)}/next-steps`);
|
|
722
|
+
}
|
|
723
|
+
// --- Demo ---
|
|
724
|
+
seedDemo(data) {
|
|
725
|
+
return this.post("/v1/demo/seed", data);
|
|
726
|
+
}
|
|
727
|
+
// --- Entities writes ---
|
|
728
|
+
convertEntity(entityId, data) {
|
|
729
|
+
const body = {
|
|
730
|
+
target_type: data.target_type ?? data.new_entity_type
|
|
731
|
+
};
|
|
732
|
+
const jurisdiction = data.jurisdiction ?? data.new_jurisdiction;
|
|
733
|
+
if (jurisdiction) body.jurisdiction = jurisdiction;
|
|
734
|
+
return this.post(`/v1/entities/${pathSegment(entityId)}/convert`, body);
|
|
735
|
+
}
|
|
736
|
+
dissolveEntity(entityId, data) {
|
|
737
|
+
return this.post(`/v1/entities/${pathSegment(entityId)}/dissolve`, data);
|
|
738
|
+
}
|
|
739
|
+
// --- Agents ---
|
|
740
|
+
listAgents() {
|
|
741
|
+
return this.get("/v1/agents");
|
|
742
|
+
}
|
|
743
|
+
getAgent(id) {
|
|
744
|
+
return this.get(`/v1/agents/${pathSegment(id)}/resolved`);
|
|
745
|
+
}
|
|
746
|
+
createAgent(data) {
|
|
747
|
+
return this.post("/v1/agents", data);
|
|
748
|
+
}
|
|
749
|
+
updateAgent(id, data) {
|
|
750
|
+
return this.patch(`/v1/agents/${pathSegment(id)}`, data);
|
|
751
|
+
}
|
|
752
|
+
deleteAgent(id) {
|
|
753
|
+
return this.patch(`/v1/agents/${pathSegment(id)}`, { status: "disabled" });
|
|
754
|
+
}
|
|
755
|
+
sendAgentMessage(id, message) {
|
|
756
|
+
return this.post(`/v1/agents/${pathSegment(id)}/messages`, { message });
|
|
757
|
+
}
|
|
758
|
+
addAgentSkill(id, data) {
|
|
759
|
+
return this.post(`/v1/agents/${pathSegment(id)}/skills`, data);
|
|
760
|
+
}
|
|
761
|
+
listSupportedModels() {
|
|
762
|
+
return this.get("/v1/models");
|
|
763
|
+
}
|
|
764
|
+
async getAgentExecution(agentId, executionId) {
|
|
765
|
+
return this.get(`/v1/agents/${pathSegment(agentId)}/executions/${pathSegment(executionId)}`);
|
|
766
|
+
}
|
|
767
|
+
async getAgentExecutionResult(agentId, executionId) {
|
|
768
|
+
return this.get(`/v1/agents/${pathSegment(agentId)}/executions/${pathSegment(executionId)}/result`);
|
|
769
|
+
}
|
|
770
|
+
async getAgentExecutionLogs(agentId, executionId) {
|
|
771
|
+
return this.get(`/v1/agents/${pathSegment(agentId)}/executions/${pathSegment(executionId)}/logs`);
|
|
772
|
+
}
|
|
773
|
+
async killAgentExecution(agentId, executionId) {
|
|
774
|
+
return this.post(`/v1/agents/${pathSegment(agentId)}/executions/${pathSegment(executionId)}/kill`, {});
|
|
775
|
+
}
|
|
776
|
+
// --- Governance bodies ---
|
|
777
|
+
createGovernanceBody(data) {
|
|
778
|
+
return this.post("/v1/governance-bodies", data);
|
|
779
|
+
}
|
|
780
|
+
createGovernanceSeat(bodyId, entityId, data) {
|
|
781
|
+
return this.postWithParams(`/v1/governance-bodies/${pathSegment(bodyId)}/seats`, data, { entity_id: entityId });
|
|
782
|
+
}
|
|
783
|
+
// --- Work Items ---
|
|
784
|
+
listWorkItems(entityId, params) {
|
|
785
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/work-items`, params);
|
|
786
|
+
}
|
|
787
|
+
getWorkItem(entityId, workItemId) {
|
|
788
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/work-items/${pathSegment(workItemId)}`);
|
|
789
|
+
}
|
|
790
|
+
createWorkItem(entityId, data) {
|
|
791
|
+
return this.post(`/v1/entities/${pathSegment(entityId)}/work-items`, data);
|
|
792
|
+
}
|
|
793
|
+
claimWorkItem(entityId, workItemId, data) {
|
|
794
|
+
return this.post(`/v1/entities/${pathSegment(entityId)}/work-items/${pathSegment(workItemId)}/claim`, data);
|
|
795
|
+
}
|
|
796
|
+
completeWorkItem(entityId, workItemId, data) {
|
|
797
|
+
return this.post(`/v1/entities/${pathSegment(entityId)}/work-items/${pathSegment(workItemId)}/complete`, data);
|
|
798
|
+
}
|
|
799
|
+
releaseWorkItem(entityId, workItemId) {
|
|
800
|
+
return this.post(`/v1/entities/${pathSegment(entityId)}/work-items/${pathSegment(workItemId)}/release`, {});
|
|
801
|
+
}
|
|
802
|
+
cancelWorkItem(entityId, workItemId) {
|
|
803
|
+
return this.post(`/v1/entities/${pathSegment(entityId)}/work-items/${pathSegment(workItemId)}/cancel`, {});
|
|
804
|
+
}
|
|
805
|
+
// --- API Keys ---
|
|
806
|
+
listApiKeys() {
|
|
807
|
+
return this.get("/v1/api-keys", { workspace_id: this.workspaceId });
|
|
808
|
+
}
|
|
809
|
+
async createApiKey(data) {
|
|
810
|
+
return this.post("/v1/api-keys", data);
|
|
811
|
+
}
|
|
812
|
+
async revokeApiKey(keyId) {
|
|
813
|
+
return this.del(`/v1/api-keys/${pathSegment(keyId)}`);
|
|
814
|
+
}
|
|
815
|
+
async rotateApiKey(keyId) {
|
|
816
|
+
return this.post(`/v1/api-keys/${pathSegment(keyId)}/rotate`, {});
|
|
817
|
+
}
|
|
818
|
+
// --- Obligations ---
|
|
819
|
+
assignObligation(obligationId, contactId) {
|
|
820
|
+
return this.patch(`/v1/obligations/${pathSegment(obligationId)}/assign`, { contact_id: contactId });
|
|
821
|
+
}
|
|
822
|
+
// --- Config ---
|
|
823
|
+
getConfig() {
|
|
824
|
+
return this.get("/v1/config");
|
|
825
|
+
}
|
|
826
|
+
// --- Services ---
|
|
827
|
+
listServiceCatalog() {
|
|
828
|
+
return this.get("/v1/services/catalog");
|
|
829
|
+
}
|
|
830
|
+
createServiceRequest(data) {
|
|
831
|
+
return this.post("/v1/services/requests", data);
|
|
832
|
+
}
|
|
833
|
+
getServiceRequest(id, entityId) {
|
|
834
|
+
return this.get(`/v1/services/requests/${pathSegment(id)}`, { entity_id: entityId });
|
|
835
|
+
}
|
|
836
|
+
listServiceRequests(entityId) {
|
|
837
|
+
return this.get(`/v1/entities/${pathSegment(entityId)}/service-requests`);
|
|
838
|
+
}
|
|
839
|
+
beginServiceCheckout(id, data) {
|
|
840
|
+
return this.post(`/v1/services/requests/${pathSegment(id)}/checkout`, data);
|
|
841
|
+
}
|
|
842
|
+
fulfillServiceRequest(id, data) {
|
|
843
|
+
return this.post(`/v1/services/requests/${pathSegment(id)}/fulfill`, data);
|
|
844
|
+
}
|
|
845
|
+
cancelServiceRequest(id, data) {
|
|
846
|
+
return this.post(`/v1/services/requests/${pathSegment(id)}/cancel`, data);
|
|
847
|
+
}
|
|
848
|
+
// --- Feedback ---
|
|
849
|
+
submitFeedback(message, category, email) {
|
|
850
|
+
return this.post("/v1/feedback", { message, category, email });
|
|
851
|
+
}
|
|
852
|
+
// --- Link/Claim ---
|
|
853
|
+
async createLink(externalId, provider) {
|
|
854
|
+
const resp = await this.request("POST", "/v1/workspaces/link", { external_id: externalId, provider });
|
|
855
|
+
if (!resp.ok) {
|
|
856
|
+
const detail = await extractErrorMessage(resp);
|
|
857
|
+
if (resp.status === 401) throw new SessionExpiredError(detail);
|
|
858
|
+
const prefix = resp.status >= 500 ? "Server error" : resp.status === 404 ? "Not found" : resp.status === 422 ? "Validation error" : `HTTP ${resp.status}`;
|
|
859
|
+
throw new Error(`${prefix}: ${detail}`);
|
|
860
|
+
}
|
|
861
|
+
return resp.json();
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
// src/reference-tracker.ts
|
|
866
|
+
var RESOURCE_KINDS = [
|
|
867
|
+
"entity",
|
|
868
|
+
"contact",
|
|
869
|
+
"share_transfer",
|
|
870
|
+
"invoice",
|
|
871
|
+
"bank_account",
|
|
872
|
+
"payment",
|
|
873
|
+
"payroll_run",
|
|
874
|
+
"distribution",
|
|
875
|
+
"reconciliation",
|
|
876
|
+
"tax_filing",
|
|
877
|
+
"deadline",
|
|
878
|
+
"classification",
|
|
879
|
+
"body",
|
|
880
|
+
"meeting",
|
|
881
|
+
"seat",
|
|
882
|
+
"agenda_item",
|
|
883
|
+
"resolution",
|
|
884
|
+
"document",
|
|
885
|
+
"work_item",
|
|
886
|
+
"agent",
|
|
887
|
+
"valuation",
|
|
888
|
+
"safe_note",
|
|
889
|
+
"instrument",
|
|
890
|
+
"share_class",
|
|
891
|
+
"round",
|
|
892
|
+
"service_request"
|
|
893
|
+
];
|
|
894
|
+
var VALID_RESOURCE_KINDS = new Set(RESOURCE_KINDS);
|
|
895
|
+
var MAX_REFERENCE_INPUT_LEN = 256;
|
|
896
|
+
function normalize(value) {
|
|
897
|
+
return value.trim().toLowerCase();
|
|
898
|
+
}
|
|
899
|
+
function validateReferenceInput(value, field, options = {}) {
|
|
900
|
+
const trimmed = value.trim();
|
|
901
|
+
if (!options.allowEmpty && trimmed.length === 0) {
|
|
902
|
+
throw new Error(`${field} cannot be empty.`);
|
|
903
|
+
}
|
|
904
|
+
if (trimmed.length > MAX_REFERENCE_INPUT_LEN) {
|
|
905
|
+
throw new Error(`${field} must be at most ${MAX_REFERENCE_INPUT_LEN} characters.`);
|
|
906
|
+
}
|
|
907
|
+
return trimmed;
|
|
908
|
+
}
|
|
909
|
+
function shortId(value) {
|
|
910
|
+
return String(value ?? "").slice(0, 8);
|
|
911
|
+
}
|
|
912
|
+
function slugify(value) {
|
|
913
|
+
return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
914
|
+
}
|
|
915
|
+
function parseLastReference(value) {
|
|
916
|
+
const trimmed = validateReferenceInput(value, "reference", { allowEmpty: false }).toLowerCase();
|
|
917
|
+
if (trimmed === "_" || trimmed === "@last") {
|
|
918
|
+
return { isLast: true };
|
|
919
|
+
}
|
|
920
|
+
if (trimmed.startsWith("@last:")) {
|
|
921
|
+
const kind = trimmed.slice(6);
|
|
922
|
+
if (!VALID_RESOURCE_KINDS.has(kind)) {
|
|
923
|
+
throw new Error(`Unknown reference kind: ${kind}`);
|
|
924
|
+
}
|
|
925
|
+
return { isLast: true, kind };
|
|
926
|
+
}
|
|
927
|
+
return { isLast: false };
|
|
928
|
+
}
|
|
929
|
+
function uniqueStrings(values) {
|
|
930
|
+
const out = /* @__PURE__ */ new Set();
|
|
931
|
+
for (const value of values) {
|
|
932
|
+
if (!value) continue;
|
|
933
|
+
const trimmed = value.trim();
|
|
934
|
+
if (!trimmed) continue;
|
|
935
|
+
out.add(normalize(trimmed));
|
|
936
|
+
const slug = slugify(trimmed);
|
|
937
|
+
if (slug) out.add(slug);
|
|
938
|
+
}
|
|
939
|
+
return out;
|
|
940
|
+
}
|
|
941
|
+
function kindLabel(kind) {
|
|
942
|
+
return kind.replaceAll("_", " ");
|
|
943
|
+
}
|
|
944
|
+
function extractId(record, fields) {
|
|
945
|
+
for (const field of fields) {
|
|
946
|
+
const value = record[field];
|
|
947
|
+
if (typeof value === "string" && value.trim()) {
|
|
948
|
+
return value.trim();
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
return void 0;
|
|
952
|
+
}
|
|
953
|
+
function isValidResourceKind(kind) {
|
|
954
|
+
return VALID_RESOURCE_KINDS.has(kind);
|
|
955
|
+
}
|
|
956
|
+
function describeReferenceRecord(kind, record) {
|
|
957
|
+
const specs = {
|
|
958
|
+
entity: { idFields: ["entity_id", "id"], labelFields: ["legal_name", "name"] },
|
|
959
|
+
contact: { idFields: ["contact_id", "id"], labelFields: ["name", "email"] },
|
|
960
|
+
share_transfer: {
|
|
961
|
+
idFields: ["transfer_id", "id"],
|
|
962
|
+
labelFields: ["from_holder", "to_holder", "transfer_type", "status"]
|
|
963
|
+
},
|
|
964
|
+
invoice: {
|
|
965
|
+
idFields: ["invoice_id", "id"],
|
|
966
|
+
labelFields: ["customer_name", "description", "due_date"]
|
|
967
|
+
},
|
|
968
|
+
bank_account: {
|
|
969
|
+
idFields: ["bank_account_id", "account_id", "id"],
|
|
970
|
+
labelFields: ["bank_name", "account_type"]
|
|
971
|
+
},
|
|
972
|
+
payment: {
|
|
973
|
+
idFields: ["payment_id", "id"],
|
|
974
|
+
labelFields: ["recipient", "description", "payment_method"]
|
|
975
|
+
},
|
|
976
|
+
payroll_run: {
|
|
977
|
+
idFields: ["payroll_run_id", "id"],
|
|
978
|
+
labelFields: ["pay_period_start", "pay_period_end"]
|
|
979
|
+
},
|
|
980
|
+
distribution: {
|
|
981
|
+
idFields: ["distribution_id", "id"],
|
|
982
|
+
labelFields: ["description", "distribution_type"]
|
|
983
|
+
},
|
|
984
|
+
reconciliation: {
|
|
985
|
+
idFields: ["reconciliation_id", "id"],
|
|
986
|
+
labelFields: ["as_of_date", "status"]
|
|
987
|
+
},
|
|
988
|
+
tax_filing: {
|
|
989
|
+
idFields: ["filing_id", "id"],
|
|
990
|
+
labelFields: ["document_type", "tax_year"]
|
|
991
|
+
},
|
|
992
|
+
deadline: {
|
|
993
|
+
idFields: ["deadline_id", "id"],
|
|
994
|
+
labelFields: ["deadline_type", "description", "due_date"]
|
|
995
|
+
},
|
|
996
|
+
classification: {
|
|
997
|
+
idFields: ["classification_id", "id"],
|
|
998
|
+
labelFields: ["contractor_name", "state", "risk_level"]
|
|
999
|
+
},
|
|
1000
|
+
body: { idFields: ["body_id", "id"], labelFields: ["name"] },
|
|
1001
|
+
meeting: { idFields: ["meeting_id", "id"], labelFields: ["title", "name"] },
|
|
1002
|
+
seat: {
|
|
1003
|
+
idFields: ["seat_id", "id"],
|
|
1004
|
+
labelFields: ["seat_name", "title", "holder_name", "holder", "holder_email"]
|
|
1005
|
+
},
|
|
1006
|
+
agenda_item: { idFields: ["agenda_item_id", "item_id", "id"], labelFields: ["title"] },
|
|
1007
|
+
resolution: { idFields: ["resolution_id", "id"], labelFields: ["title"] },
|
|
1008
|
+
document: { idFields: ["document_id", "id"], labelFields: ["title", "name"] },
|
|
1009
|
+
work_item: { idFields: ["work_item_id", "id"], labelFields: ["title"] },
|
|
1010
|
+
agent: { idFields: ["agent_id", "id"], labelFields: ["name"] },
|
|
1011
|
+
valuation: {
|
|
1012
|
+
idFields: ["valuation_id", "id"],
|
|
1013
|
+
labelFields: ["valuation_type", "effective_date", "date"]
|
|
1014
|
+
},
|
|
1015
|
+
safe_note: {
|
|
1016
|
+
idFields: ["safe_note_id", "safe_id", "id"],
|
|
1017
|
+
labelFields: ["investor_name", "investor", "safe_type"]
|
|
1018
|
+
},
|
|
1019
|
+
instrument: { idFields: ["instrument_id", "id"], labelFields: ["symbol", "kind", "name"] },
|
|
1020
|
+
share_class: {
|
|
1021
|
+
idFields: ["share_class_id", "id"],
|
|
1022
|
+
labelFields: ["class_code", "name", "share_class"]
|
|
1023
|
+
},
|
|
1024
|
+
round: { idFields: ["round_id", "equity_round_id", "id"], labelFields: ["name"] },
|
|
1025
|
+
service_request: {
|
|
1026
|
+
idFields: ["request_id", "service_request_id", "id"],
|
|
1027
|
+
labelFields: ["service_slug", "status"]
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
const spec = specs[kind];
|
|
1031
|
+
const id = extractId(record, spec.idFields);
|
|
1032
|
+
if (!id) {
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
const labels = spec.labelFields.map((field) => record[field]).filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
1036
|
+
const persistedHandle = typeof record.handle === "string" && record.handle.trim().length > 0 ? record.handle.trim() : void 0;
|
|
1037
|
+
let label = labels[0] ?? id;
|
|
1038
|
+
if (kind === "share_transfer") {
|
|
1039
|
+
const fromHolder = typeof record.from_holder === "string" ? record.from_holder.trim() : "";
|
|
1040
|
+
const toHolder = typeof record.to_holder === "string" ? record.to_holder.trim() : "";
|
|
1041
|
+
const transferType = typeof record.transfer_type === "string" ? record.transfer_type.trim() : "";
|
|
1042
|
+
const composite = [
|
|
1043
|
+
fromHolder && toHolder ? `${fromHolder}-to-${toHolder}` : "",
|
|
1044
|
+
transferType
|
|
1045
|
+
].filter(Boolean).join("-");
|
|
1046
|
+
if (composite) {
|
|
1047
|
+
label = composite;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return {
|
|
1051
|
+
id,
|
|
1052
|
+
label,
|
|
1053
|
+
tokens: uniqueStrings([id, persistedHandle, ...labels]),
|
|
1054
|
+
raw: record
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
function getReferenceId(kind, record) {
|
|
1058
|
+
return describeReferenceRecord(kind, record)?.id;
|
|
1059
|
+
}
|
|
1060
|
+
function getReferenceLabel(kind, record) {
|
|
1061
|
+
return describeReferenceRecord(kind, record)?.label;
|
|
1062
|
+
}
|
|
1063
|
+
function getReferenceAlias(kind, record) {
|
|
1064
|
+
if (typeof record.handle === "string" && record.handle.trim().length > 0) {
|
|
1065
|
+
return record.handle.trim();
|
|
1066
|
+
}
|
|
1067
|
+
const described = describeReferenceRecord(kind, record);
|
|
1068
|
+
if (!described) return void 0;
|
|
1069
|
+
const alias = slugify(described.label);
|
|
1070
|
+
return alias || shortId(described.id);
|
|
1071
|
+
}
|
|
1072
|
+
function matchRank(record, normalizedQuery) {
|
|
1073
|
+
if (!normalizedQuery || normalizedQuery === "*") {
|
|
1074
|
+
return 5;
|
|
1075
|
+
}
|
|
1076
|
+
if (normalize(record.id) === normalizedQuery) {
|
|
1077
|
+
return 0;
|
|
1078
|
+
}
|
|
1079
|
+
if (record.tokens.has(normalizedQuery)) {
|
|
1080
|
+
return 1;
|
|
1081
|
+
}
|
|
1082
|
+
if (normalize(record.id).startsWith(normalizedQuery)) {
|
|
1083
|
+
return 2;
|
|
1084
|
+
}
|
|
1085
|
+
if (Array.from(record.tokens).some((token) => token.startsWith(normalizedQuery))) {
|
|
1086
|
+
return 3;
|
|
1087
|
+
}
|
|
1088
|
+
return 4;
|
|
1089
|
+
}
|
|
1090
|
+
var ReferenceTracker = class {
|
|
1091
|
+
constructor(storage) {
|
|
1092
|
+
this.storage = storage;
|
|
1093
|
+
}
|
|
1094
|
+
/** Remember a reference for @last reuse. */
|
|
1095
|
+
remember(kind, id, entityId) {
|
|
1096
|
+
this.storage.setLastReference(kind, id, entityId);
|
|
1097
|
+
}
|
|
1098
|
+
/** Resolve @last / @last:kind references. */
|
|
1099
|
+
resolveLastReference(ref, kind, entityId) {
|
|
1100
|
+
const parsed = parseLastReference(ref);
|
|
1101
|
+
if (!parsed.isLast) return parsed;
|
|
1102
|
+
const lastKind = parsed.kind ?? kind;
|
|
1103
|
+
if (lastKind !== kind) {
|
|
1104
|
+
throw new Error(
|
|
1105
|
+
`@last:${lastKind} cannot be used where a ${kindLabel(kind)} reference is required.`
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
const id = this.storage.getLastReference(lastKind, entityId);
|
|
1109
|
+
return { ...parsed, id };
|
|
1110
|
+
}
|
|
1111
|
+
/** Find the single best match for a query against a list of records. */
|
|
1112
|
+
findBestMatch(kind, query, records) {
|
|
1113
|
+
const matches = this.findMatches(kind, query, records);
|
|
1114
|
+
return matches.length > 0 ? matches[0] : null;
|
|
1115
|
+
}
|
|
1116
|
+
/** Find all matching records ranked by relevance. */
|
|
1117
|
+
findMatches(kind, query, records) {
|
|
1118
|
+
const trimmedQuery = validateReferenceInput(query, "query", { allowEmpty: true });
|
|
1119
|
+
const described = records.map((record) => describeReferenceRecord(kind, record)).filter((record) => record !== null);
|
|
1120
|
+
const normalizedQuery = normalize(trimmedQuery);
|
|
1121
|
+
const matches = described.filter((record) => {
|
|
1122
|
+
if (!normalizedQuery || normalizedQuery === "*") {
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
if (normalize(record.id).startsWith(normalizedQuery)) {
|
|
1126
|
+
return true;
|
|
1127
|
+
}
|
|
1128
|
+
if (record.tokens.has(normalizedQuery)) {
|
|
1129
|
+
return true;
|
|
1130
|
+
}
|
|
1131
|
+
return Array.from(record.tokens).some((token) => token.includes(normalizedQuery));
|
|
1132
|
+
}).sort(
|
|
1133
|
+
(left, right) => matchRank(left, normalizedQuery) - matchRank(right, normalizedQuery) || left.label.localeCompare(right.label) || left.id.localeCompare(right.id)
|
|
1134
|
+
);
|
|
1135
|
+
return matches.map((record) => ({
|
|
1136
|
+
kind,
|
|
1137
|
+
id: record.id,
|
|
1138
|
+
short_id: shortId(record.id),
|
|
1139
|
+
label: record.label,
|
|
1140
|
+
alias: getReferenceAlias(kind, record.raw),
|
|
1141
|
+
raw: record.raw
|
|
1142
|
+
}));
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
// src/workflows/equity-helpers.ts
|
|
1147
|
+
function normalizedGrantType(grantType) {
|
|
1148
|
+
return grantType.trim().toLowerCase().replaceAll("-", "_").replaceAll(" ", "_");
|
|
1149
|
+
}
|
|
1150
|
+
function expectedInstrumentKinds(grantType) {
|
|
1151
|
+
switch (normalizedGrantType(grantType)) {
|
|
1152
|
+
case "common":
|
|
1153
|
+
case "common_stock":
|
|
1154
|
+
return ["common_equity"];
|
|
1155
|
+
case "preferred":
|
|
1156
|
+
case "preferred_stock":
|
|
1157
|
+
return ["preferred_equity"];
|
|
1158
|
+
case "unit":
|
|
1159
|
+
case "membership_unit":
|
|
1160
|
+
return ["membership_unit"];
|
|
1161
|
+
case "option":
|
|
1162
|
+
case "options":
|
|
1163
|
+
case "stock_option":
|
|
1164
|
+
case "iso":
|
|
1165
|
+
case "nso":
|
|
1166
|
+
return ["option_grant"];
|
|
1167
|
+
case "rsa":
|
|
1168
|
+
return ["common_equity", "preferred_equity"];
|
|
1169
|
+
default:
|
|
1170
|
+
return [];
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function grantRequiresCurrent409a(grantType, instrumentKind) {
|
|
1174
|
+
return instrumentKind?.toLowerCase() === "option_grant" || expectedInstrumentKinds(grantType).includes("option_grant");
|
|
1175
|
+
}
|
|
1176
|
+
function buildInstrumentCreationHint(grantType) {
|
|
1177
|
+
const normalized = normalizedGrantType(grantType);
|
|
1178
|
+
switch (normalized) {
|
|
1179
|
+
case "preferred":
|
|
1180
|
+
case "preferred_stock":
|
|
1181
|
+
return "Create one with: corp cap-table create-instrument --kind preferred_equity --symbol SERIES-A --authorized-units <shares>";
|
|
1182
|
+
case "option":
|
|
1183
|
+
case "options":
|
|
1184
|
+
case "stock_option":
|
|
1185
|
+
case "iso":
|
|
1186
|
+
case "nso":
|
|
1187
|
+
return "Create one with: corp cap-table create-instrument --kind option_grant --symbol OPTION-PLAN --authorized-units <shares>";
|
|
1188
|
+
case "membership_unit":
|
|
1189
|
+
case "unit":
|
|
1190
|
+
return "Create one with: corp cap-table create-instrument --kind membership_unit --symbol UNIT --authorized-units <units>";
|
|
1191
|
+
case "common":
|
|
1192
|
+
case "common_stock":
|
|
1193
|
+
return "Create one with: corp cap-table create-instrument --kind common_equity --symbol COMMON --authorized-units <shares>";
|
|
1194
|
+
default:
|
|
1195
|
+
return "Create a matching instrument first, then pass --instrument-id explicitly.";
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
function resolveInstrumentForGrant(instruments, grantType, explicitInstrumentId) {
|
|
1199
|
+
if (explicitInstrumentId) {
|
|
1200
|
+
const explicit = instruments.find(
|
|
1201
|
+
(instrument) => instrument.instrument_id === explicitInstrumentId
|
|
1202
|
+
);
|
|
1203
|
+
if (!explicit) {
|
|
1204
|
+
throw new Error(
|
|
1205
|
+
`Instrument ${explicitInstrumentId} was not found on the cap table.`
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
return explicit;
|
|
1209
|
+
}
|
|
1210
|
+
const kinds = expectedInstrumentKinds(grantType);
|
|
1211
|
+
if (kinds.length === 0) {
|
|
1212
|
+
throw new Error(
|
|
1213
|
+
`No default instrument mapping exists for grant type "${grantType}". ${buildInstrumentCreationHint(grantType)}`
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
const match = instruments.find(
|
|
1217
|
+
(instrument) => kinds.includes(String(instrument.kind).toLowerCase())
|
|
1218
|
+
);
|
|
1219
|
+
if (!match) {
|
|
1220
|
+
throw new Error(
|
|
1221
|
+
`No instrument found for grant type "${grantType}". Expected one of: ${kinds.join(", ")}. ${buildInstrumentCreationHint(grantType)}`
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
return match;
|
|
1225
|
+
}
|
|
1226
|
+
async function entityHasActiveBoard(client, entityId) {
|
|
1227
|
+
const bodies = await client.listGovernanceBodies(entityId);
|
|
1228
|
+
return bodies.some(
|
|
1229
|
+
(body) => String(body.body_type ?? "").toLowerCase() === "board_of_directors" && String(body.status ?? "active").toLowerCase() === "active"
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
async function ensureIssuancePreflight(client, entityId, grantType, instrument, meetingId, resolutionId, operationLabel) {
|
|
1233
|
+
if (!meetingId || !resolutionId) {
|
|
1234
|
+
if (await entityHasActiveBoard(client, entityId)) {
|
|
1235
|
+
const label = operationLabel ?? "this issuance";
|
|
1236
|
+
throw new Error(
|
|
1237
|
+
`Board approval is required for ${label}. Pass --meeting-id and --resolution-id from a passed board vote.
|
|
1238
|
+
Tip: Use 'corp governance quick-approve --text "RESOLVED: authorize ${label}"' for one-step approval.`
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
if (!grantRequiresCurrent409a(grantType, instrument?.kind)) {
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
try {
|
|
1246
|
+
await client.getCurrent409a(entityId);
|
|
1247
|
+
} catch (err) {
|
|
1248
|
+
const msg = String(err);
|
|
1249
|
+
if (msg.includes("404") || msg.includes("Not found") || msg.includes("not found")) {
|
|
1250
|
+
throw new Error(
|
|
1251
|
+
"Stock option issuances require a current approved 409A valuation. Create and approve one first with: corp cap-table create-valuation --type four_oh_nine_a --date YYYY-MM-DD --methodology <method>; corp cap-table submit-valuation <valuation-ref>; corp cap-table approve-valuation <valuation-ref> --resolution-id <resolution-ref>"
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
throw err;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// src/workflows/issue-equity.ts
|
|
1259
|
+
async function issueEquity(client, args) {
|
|
1260
|
+
const steps = [];
|
|
1261
|
+
try {
|
|
1262
|
+
const capTable = await client.getCapTable(args.entityId);
|
|
1263
|
+
const issuerLegalEntityId = capTable.issuer_legal_entity_id;
|
|
1264
|
+
if (!issuerLegalEntityId) {
|
|
1265
|
+
return {
|
|
1266
|
+
success: false,
|
|
1267
|
+
error: "No issuer legal entity found. Has this entity been formed with a cap table?",
|
|
1268
|
+
steps
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
const instruments = capTable.instruments ?? [];
|
|
1272
|
+
if (!instruments.length) {
|
|
1273
|
+
return {
|
|
1274
|
+
success: false,
|
|
1275
|
+
error: "No instruments found on cap table. Create one with: corp cap-table create-instrument --kind common_equity --symbol COMMON --authorized-units <shares>",
|
|
1276
|
+
steps
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
const instrument = resolveInstrumentForGrant(
|
|
1280
|
+
instruments,
|
|
1281
|
+
args.grantType,
|
|
1282
|
+
args.instrumentId
|
|
1283
|
+
);
|
|
1284
|
+
const instrumentId = instrument.instrument_id;
|
|
1285
|
+
steps.push({
|
|
1286
|
+
name: "resolve_instrument",
|
|
1287
|
+
status: "ok",
|
|
1288
|
+
data: {
|
|
1289
|
+
instrument_id: instrumentId,
|
|
1290
|
+
symbol: instrument.symbol,
|
|
1291
|
+
kind: instrument.kind
|
|
1292
|
+
},
|
|
1293
|
+
detail: `Using instrument: ${instrument.symbol} (${instrument.kind})`
|
|
1294
|
+
});
|
|
1295
|
+
await ensureIssuancePreflight(
|
|
1296
|
+
client,
|
|
1297
|
+
args.entityId,
|
|
1298
|
+
args.grantType,
|
|
1299
|
+
instrument,
|
|
1300
|
+
args.meetingId,
|
|
1301
|
+
args.resolutionId,
|
|
1302
|
+
"equity issuance"
|
|
1303
|
+
);
|
|
1304
|
+
steps.push({ name: "preflight", status: "ok" });
|
|
1305
|
+
const round = await client.startEquityRound({
|
|
1306
|
+
entity_id: args.entityId,
|
|
1307
|
+
name: `${args.grantType} grant \u2014 ${args.recipientName}`,
|
|
1308
|
+
issuer_legal_entity_id: issuerLegalEntityId
|
|
1309
|
+
});
|
|
1310
|
+
const roundId = round.round_id ?? round.equity_round_id;
|
|
1311
|
+
steps.push({
|
|
1312
|
+
name: "start_round",
|
|
1313
|
+
status: "ok",
|
|
1314
|
+
data: { round_id: roundId }
|
|
1315
|
+
});
|
|
1316
|
+
const securityData = {
|
|
1317
|
+
entity_id: args.entityId,
|
|
1318
|
+
instrument_id: instrumentId,
|
|
1319
|
+
quantity: args.shares,
|
|
1320
|
+
recipient_name: args.recipientName,
|
|
1321
|
+
grant_type: args.grantType
|
|
1322
|
+
};
|
|
1323
|
+
if (args.recipientEmail) securityData.email = args.recipientEmail;
|
|
1324
|
+
const existingHolders = capTable.holders ?? [];
|
|
1325
|
+
const matchingHolder = existingHolders.find((h) => {
|
|
1326
|
+
const nameMatch = String(h.name ?? "").toLowerCase() === args.recipientName.toLowerCase();
|
|
1327
|
+
const emailMatch = args.recipientEmail && String(h.email ?? "").toLowerCase() === args.recipientEmail.toLowerCase();
|
|
1328
|
+
return nameMatch || emailMatch;
|
|
1329
|
+
});
|
|
1330
|
+
if (matchingHolder) {
|
|
1331
|
+
const holderId = matchingHolder.holder_id ?? matchingHolder.contact_id ?? matchingHolder.id;
|
|
1332
|
+
if (holderId) securityData.holder_id = holderId;
|
|
1333
|
+
}
|
|
1334
|
+
await client.addRoundSecurity(roundId, securityData);
|
|
1335
|
+
steps.push({ name: "add_security", status: "ok" });
|
|
1336
|
+
const issuePayload = {
|
|
1337
|
+
entity_id: args.entityId
|
|
1338
|
+
};
|
|
1339
|
+
if (args.meetingId) issuePayload.meeting_id = args.meetingId;
|
|
1340
|
+
if (args.resolutionId) issuePayload.resolution_id = args.resolutionId;
|
|
1341
|
+
const result = await client.issueRound(roundId, issuePayload);
|
|
1342
|
+
steps.push({ name: "issue_round", status: "ok" });
|
|
1343
|
+
return {
|
|
1344
|
+
success: true,
|
|
1345
|
+
data: {
|
|
1346
|
+
...result,
|
|
1347
|
+
round_id: roundId,
|
|
1348
|
+
round,
|
|
1349
|
+
shares: args.shares,
|
|
1350
|
+
grant_type: args.grantType,
|
|
1351
|
+
recipient: args.recipientName
|
|
1352
|
+
},
|
|
1353
|
+
steps
|
|
1354
|
+
};
|
|
1355
|
+
} catch (err) {
|
|
1356
|
+
steps.push({
|
|
1357
|
+
name: "error",
|
|
1358
|
+
status: "failed",
|
|
1359
|
+
detail: String(err)
|
|
1360
|
+
});
|
|
1361
|
+
return {
|
|
1362
|
+
success: false,
|
|
1363
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1364
|
+
steps
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
// src/workflows/issue-safe.ts
|
|
1370
|
+
async function issueSafe(client, args) {
|
|
1371
|
+
const steps = [];
|
|
1372
|
+
const safeType = args.safeType ?? "post_money";
|
|
1373
|
+
try {
|
|
1374
|
+
await ensureIssuancePreflight(
|
|
1375
|
+
client,
|
|
1376
|
+
args.entityId,
|
|
1377
|
+
safeType,
|
|
1378
|
+
void 0,
|
|
1379
|
+
args.meetingId,
|
|
1380
|
+
args.resolutionId,
|
|
1381
|
+
"SAFE issuance"
|
|
1382
|
+
);
|
|
1383
|
+
steps.push({ name: "preflight", status: "ok" });
|
|
1384
|
+
const body = {
|
|
1385
|
+
entity_id: args.entityId,
|
|
1386
|
+
investor_name: args.investorName,
|
|
1387
|
+
principal_amount_cents: args.amountCents,
|
|
1388
|
+
valuation_cap_cents: args.valuationCapCents,
|
|
1389
|
+
safe_type: safeType
|
|
1390
|
+
};
|
|
1391
|
+
if (args.email) body.email = args.email;
|
|
1392
|
+
if (args.meetingId) body.meeting_id = args.meetingId;
|
|
1393
|
+
if (args.resolutionId) body.resolution_id = args.resolutionId;
|
|
1394
|
+
const result = await client.createSafeNote(body);
|
|
1395
|
+
steps.push({ name: "create_safe", status: "ok" });
|
|
1396
|
+
return {
|
|
1397
|
+
success: true,
|
|
1398
|
+
data: {
|
|
1399
|
+
...result,
|
|
1400
|
+
investor_name: args.investorName,
|
|
1401
|
+
amount_cents: args.amountCents,
|
|
1402
|
+
valuation_cap_cents: args.valuationCapCents
|
|
1403
|
+
},
|
|
1404
|
+
steps
|
|
1405
|
+
};
|
|
1406
|
+
} catch (err) {
|
|
1407
|
+
steps.push({
|
|
1408
|
+
name: "error",
|
|
1409
|
+
status: "failed",
|
|
1410
|
+
detail: String(err)
|
|
1411
|
+
});
|
|
1412
|
+
return {
|
|
1413
|
+
success: false,
|
|
1414
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1415
|
+
steps
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
// src/workflows/written-consent.ts
|
|
1421
|
+
async function writtenConsent(client, args) {
|
|
1422
|
+
const steps = [];
|
|
1423
|
+
try {
|
|
1424
|
+
const payload = {
|
|
1425
|
+
entity_id: args.entityId,
|
|
1426
|
+
body_id: args.bodyId,
|
|
1427
|
+
title: args.title,
|
|
1428
|
+
description: args.description
|
|
1429
|
+
};
|
|
1430
|
+
const result = await client.writtenConsent(payload);
|
|
1431
|
+
const meetingId = String(result.meeting_id ?? "");
|
|
1432
|
+
steps.push({
|
|
1433
|
+
name: "create_written_consent",
|
|
1434
|
+
status: "ok",
|
|
1435
|
+
data: { meeting_id: meetingId || void 0 }
|
|
1436
|
+
});
|
|
1437
|
+
if (meetingId) {
|
|
1438
|
+
try {
|
|
1439
|
+
const seats = await client.getGovernanceSeats(
|
|
1440
|
+
args.bodyId,
|
|
1441
|
+
args.entityId
|
|
1442
|
+
);
|
|
1443
|
+
const seatIds = seats.map(
|
|
1444
|
+
(s) => String(
|
|
1445
|
+
s.seat_id ?? s.id ?? ""
|
|
1446
|
+
)
|
|
1447
|
+
).filter((id) => id.length > 0);
|
|
1448
|
+
if (seatIds.length > 0) {
|
|
1449
|
+
await client.conveneMeeting(meetingId, args.entityId, {
|
|
1450
|
+
present_seat_ids: seatIds
|
|
1451
|
+
});
|
|
1452
|
+
steps.push({
|
|
1453
|
+
name: "auto_open",
|
|
1454
|
+
status: "ok",
|
|
1455
|
+
data: { seat_count: seatIds.length },
|
|
1456
|
+
detail: `Opened with ${seatIds.length} seat(s) present`
|
|
1457
|
+
});
|
|
1458
|
+
} else {
|
|
1459
|
+
steps.push({
|
|
1460
|
+
name: "auto_open",
|
|
1461
|
+
status: "skipped",
|
|
1462
|
+
detail: "No seats found on body"
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
} catch {
|
|
1466
|
+
steps.push({
|
|
1467
|
+
name: "auto_open",
|
|
1468
|
+
status: "skipped",
|
|
1469
|
+
detail: "Failed to auto-open meeting (non-fatal)"
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
} else {
|
|
1473
|
+
steps.push({
|
|
1474
|
+
name: "auto_open",
|
|
1475
|
+
status: "skipped",
|
|
1476
|
+
detail: "No meeting_id returned"
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
return {
|
|
1480
|
+
success: true,
|
|
1481
|
+
data: { ...result, meeting_id: meetingId || void 0 },
|
|
1482
|
+
steps
|
|
1483
|
+
};
|
|
1484
|
+
} catch (err) {
|
|
1485
|
+
steps.push({
|
|
1486
|
+
name: "error",
|
|
1487
|
+
status: "failed",
|
|
1488
|
+
detail: String(err)
|
|
1489
|
+
});
|
|
1490
|
+
return {
|
|
1491
|
+
success: false,
|
|
1492
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1493
|
+
steps
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
export {
|
|
1498
|
+
CorpAPIClient,
|
|
1499
|
+
RESOURCE_KINDS,
|
|
1500
|
+
ReferenceTracker,
|
|
1501
|
+
describeReferenceRecord,
|
|
1502
|
+
ensureIssuancePreflight,
|
|
1503
|
+
entityHasActiveBoard,
|
|
1504
|
+
expectedInstrumentKinds,
|
|
1505
|
+
getReferenceAlias,
|
|
1506
|
+
getReferenceId,
|
|
1507
|
+
getReferenceLabel,
|
|
1508
|
+
grantRequiresCurrent409a,
|
|
1509
|
+
isValidResourceKind,
|
|
1510
|
+
issueEquity,
|
|
1511
|
+
issueSafe,
|
|
1512
|
+
matchRank,
|
|
1513
|
+
normalizedGrantType,
|
|
1514
|
+
shortId,
|
|
1515
|
+
slugify,
|
|
1516
|
+
writtenConsent
|
|
1517
|
+
};
|
|
1518
|
+
//# sourceMappingURL=browser.js.map
|