@gethmy/mcp 2.2.1 → 2.2.3

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/http.js CHANGED
@@ -26,6 +26,7 @@ var __export = (target, all) => {
26
26
  set: (newValue) => all[name] = () => newValue
27
27
  });
28
28
  };
29
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
29
30
  var __require = import.meta.require;
30
31
 
31
32
  // src/http.ts
@@ -1637,7 +1638,7 @@ var cors = (options) => {
1637
1638
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
1638
1639
  import { homedir } from "os";
1639
1640
  import { join } from "path";
1640
- var DEFAULT_API_URL = "https://gethmy.com/api";
1641
+ var DEFAULT_API_URL = "https://app.gethmy.com/api";
1641
1642
  var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
1642
1643
  function getConfigDir() {
1643
1644
  return join(homedir(), ".harmony-mcp");
@@ -1730,7 +1731,7 @@ function hasLocalConfig(cwd) {
1730
1731
  function getApiKey() {
1731
1732
  const config = loadConfig();
1732
1733
  if (!config.apiKey) {
1733
- throw new Error(`Not configured. Run "harmony-mcp configure" to set your API key.
1734
+ throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
1734
1735
  ` + "You can generate an API key at https://gethmy.com \u2192 Settings \u2192 API Keys.");
1735
1736
  }
1736
1737
  return config.apiKey;
@@ -1823,6 +1824,7 @@ var app = new Hono2;
1823
1824
  app.use("/*", cors({
1824
1825
  origin: [
1825
1826
  "https://gethmy.com",
1827
+ "https://app.gethmy.com",
1826
1828
  "http://localhost:8080",
1827
1829
  "http://localhost:3000"
1828
1830
  ],
@@ -1841,7 +1843,7 @@ async function forwardToApi(method, path, authHeader, apiKey, body) {
1841
1843
  if (apiKey) {
1842
1844
  headers["X-API-Key"] = apiKey;
1843
1845
  } else if (authHeader) {
1844
- headers["Authorization"] = authHeader;
1846
+ headers.Authorization = authHeader;
1845
1847
  } else {
1846
1848
  throw new Error("Unauthorized: Missing API key or Authorization header");
1847
1849
  }
@@ -1899,7 +1901,7 @@ async function handleRequest(c, method, path) {
1899
1901
  try {
1900
1902
  const authHeader = c.req.header("Authorization");
1901
1903
  const apiKey = c.req.header("X-API-Key");
1902
- let body = undefined;
1904
+ let body;
1903
1905
  if (["POST", "PATCH", "PUT"].includes(method)) {
1904
1906
  try {
1905
1907
  body = await c.json();
package/dist/index.js CHANGED
@@ -29,19 +29,19 @@ var __export = (target, all) => {
29
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
30
30
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
31
31
 
32
- // ../memory/src/schema.ts
32
+ // ../memory/dist/schema.js
33
33
  var init_schema = () => {};
34
34
 
35
- // ../memory/src/constraints.ts
35
+ // ../memory/dist/constraints.js
36
36
  var init_constraints = __esm(() => {
37
37
  init_schema();
38
38
  });
39
- // ../memory/src/client.ts
39
+ // ../memory/dist/client.js
40
40
  var init_client = __esm(() => {
41
41
  init_constraints();
42
42
  });
43
43
 
44
- // ../memory/src/graph-walk.ts
44
+ // ../memory/dist/graph-walk.js
45
45
  async function discoverRelatedContext(client, startIds, maxDepth = 2, maxEntities = 20, minConfidence = 0.5) {
46
46
  const visited = new Set;
47
47
  const collectedEntities = [];
@@ -122,7 +122,7 @@ async function discoverRelatedContext(client, startIds, maxDepth = 2, maxEntitie
122
122
  };
123
123
  }
124
124
 
125
- // ../memory/src/lifecycle.ts
125
+ // ../memory/dist/lifecycle.js
126
126
  function computeDecayScore(tier, lastAccessedAt, accessCount) {
127
127
  const halfLife = DECAY_HALF_LIVES[tier];
128
128
  const now = Date.now();
@@ -207,7 +207,7 @@ var init_lifecycle = __esm(() => {
207
207
  };
208
208
  });
209
209
 
210
- // ../memory/src/sync-storage.ts
210
+ // ../memory/dist/sync-storage.js
211
211
  function parseSyncMarkdown(markdown) {
212
212
  const trimmed = markdown.trim();
213
213
  let frontmatter = {
@@ -323,16 +323,9 @@ function parseYamlArray(value) {
323
323
  return match[1].split(",").map((s) => s.trim()).filter(Boolean);
324
324
  }
325
325
 
326
- // ../memory/src/sync.ts
326
+ // ../memory/dist/sync.js
327
327
  import { createHash } from "node:crypto";
328
- import {
329
- existsSync,
330
- mkdirSync,
331
- readdirSync,
332
- readFileSync,
333
- rmSync,
334
- writeFileSync
335
- } from "node:fs";
328
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
336
329
  import { join, relative, sep } from "node:path";
337
330
  function computeFileHash(content) {
338
331
  return `sha256:${createHash("sha256").update(content).digest("hex")}`;
@@ -597,8 +590,8 @@ async function syncFull(client, config, workspaceId, projectId) {
597
590
  }
598
591
  var init_sync = () => {};
599
592
 
600
- // ../memory/src/index.ts
601
- var init_src = __esm(() => {
593
+ // ../memory/dist/index.js
594
+ var init_dist = __esm(() => {
602
595
  init_client();
603
596
  init_constraints();
604
597
  init_lifecycle();
@@ -7562,7 +7555,7 @@ async function recordContextFeedback(client2, cardId, sessionStatus, progressPer
7562
7555
  }
7563
7556
  var DEFAULT_TOKEN_BUDGET = 4000, MAX_TOKENS_PER_ENTITY = 500, MIN_RELEVANCE_THRESHOLD = 0.1, TIER_WEIGHTS, PROCEDURE_BUDGET_FRACTION = 0.15, TIER_BUDGET_ALLOCATION, MIN_REFERENCE_SLOTS = 3, manifestCache, MAX_CACHE_SIZE = 50, sessionAssemblyMap, MAX_SESSION_MAP_SIZE = 100;
7564
7557
  var init_context_assembly = __esm(() => {
7565
- init_src();
7558
+ init_dist();
7566
7559
  TIER_WEIGHTS = {
7567
7560
  reference: 1,
7568
7561
  episode: 0.7,
@@ -8017,7 +8010,7 @@ var init_prompt_builder = __esm(() => {
8017
8010
  });
8018
8011
 
8019
8012
  // src/server.ts
8020
- init_src();
8013
+ init_dist();
8021
8014
 
8022
8015
  // ../../node_modules/zod/v4/core/index.js
8023
8016
  var exports_core2 = {};
@@ -25789,7 +25782,7 @@ function deriveClusterTitle(cluster, type) {
25789
25782
  init_context_assembly();
25790
25783
 
25791
25784
  // src/lifecycle-maintenance.ts
25792
- init_src();
25785
+ init_dist();
25793
25786
  async function runLifecycleMaintenance(client3, workspaceId, projectId) {
25794
25787
  const result = {
25795
25788
  archived: 0,
@@ -25858,6 +25851,47 @@ async function runLifecycleMaintenance(client3, workspaceId, projectId) {
25858
25851
  return result;
25859
25852
  }
25860
25853
 
25854
+ // src/onboard.ts
25855
+ async function onboardNewUser(params) {
25856
+ const {
25857
+ email: email3,
25858
+ password,
25859
+ fullName,
25860
+ workspaceName = `${fullName}'s Workspace`,
25861
+ projectName = "My First Board",
25862
+ template = "kanban",
25863
+ keyName = "mcp-agent",
25864
+ apiUrl = getApiUrl()
25865
+ } = params;
25866
+ const signupResult = await signupUser(apiUrl, {
25867
+ email: email3,
25868
+ password,
25869
+ full_name: fullName
25870
+ });
25871
+ const token = signupResult.session.access_token;
25872
+ const workspaceResult = await requestWithBearer(apiUrl, token, "POST", "/workspaces", {
25873
+ name: workspaceName
25874
+ });
25875
+ const projectResult = await requestWithBearer(apiUrl, token, "POST", "/projects", {
25876
+ workspaceId: workspaceResult.workspace.id,
25877
+ name: projectName,
25878
+ template
25879
+ });
25880
+ const keyResult = await requestWithBearer(apiUrl, token, "POST", "/api-keys", {
25881
+ name: keyName
25882
+ });
25883
+ return {
25884
+ user: signupResult.user,
25885
+ workspace: workspaceResult.workspace,
25886
+ project: projectResult.project,
25887
+ columns: projectResult.columns,
25888
+ apiKey: {
25889
+ rawKey: keyResult.rawKey,
25890
+ prefix: keyResult.apiKey.prefix
25891
+ }
25892
+ };
25893
+ }
25894
+
25861
25895
  // src/server.ts
25862
25896
  var memorySessions = new Map;
25863
25897
  function initMemorySession(cardId, agentIdentifier, agentName) {
@@ -25992,18 +26026,22 @@ var TOOLS = {
25992
26026
  }
25993
26027
  },
25994
26028
  harmony_move_card: {
25995
- description: "Move a card to a different column or position",
26029
+ description: "Move a card to a different column or position. Provide either columnId (UUID) or columnName (e.g. 'Review', 'Done').",
25996
26030
  inputSchema: {
25997
26031
  type: "object",
25998
26032
  properties: {
25999
26033
  cardId: { type: "string", description: "Card ID to move" },
26000
- columnId: { type: "string", description: "Target column ID" },
26034
+ columnId: { type: "string", description: "Target column ID (UUID)" },
26035
+ columnName: {
26036
+ type: "string",
26037
+ description: "Target column name (e.g. 'To Do', 'In Progress', 'Review', 'Done'). Used if columnId is not provided."
26038
+ },
26001
26039
  position: {
26002
26040
  type: "number",
26003
26041
  description: "Position in column (0-indexed)"
26004
26042
  }
26005
26043
  },
26006
- required: ["cardId", "columnId"]
26044
+ required: ["cardId"]
26007
26045
  }
26008
26046
  },
26009
26047
  harmony_archive_card: {
@@ -26134,14 +26172,21 @@ var TOOLS = {
26134
26172
  }
26135
26173
  },
26136
26174
  harmony_add_label_to_card: {
26137
- description: "Add a label to a card",
26175
+ description: "Add a label to a card. Provide labelId directly, or labelName to look up (or auto-create) the label by name.",
26138
26176
  inputSchema: {
26139
26177
  type: "object",
26140
26178
  properties: {
26141
26179
  cardId: { type: "string" },
26142
- labelId: { type: "string" }
26180
+ labelId: {
26181
+ type: "string",
26182
+ description: "Label ID (optional if labelName provided)"
26183
+ },
26184
+ labelName: {
26185
+ type: "string",
26186
+ description: "Label name — will look up or create if not found"
26187
+ }
26143
26188
  },
26144
- required: ["cardId", "labelId"]
26189
+ required: ["cardId"]
26145
26190
  }
26146
26191
  },
26147
26192
  harmony_remove_label_from_card: {
@@ -27458,6 +27503,17 @@ function registerHandlers(server, deps) {
27458
27503
  throw new Error(`Unknown resource: ${uri}`);
27459
27504
  });
27460
27505
  }
27506
+ async function resolveColumnByName(client3, projectId, columnName) {
27507
+ const board = await client3.getBoard(projectId, { summary: true });
27508
+ const columns = board.columns;
27509
+ const lower = columnName.toLowerCase();
27510
+ const col = columns.find((c) => c.name.toLowerCase() === lower) || columns.find((c) => c.name.toLowerCase().includes(lower));
27511
+ if (!col) {
27512
+ const available = columns.map((c) => c.name).join(", ");
27513
+ throw new Error(`Column "${columnName}" not found. Available columns: ${available}`);
27514
+ }
27515
+ return col;
27516
+ }
27461
27517
  async function handleToolCall(name, args, deps) {
27462
27518
  const unauthenticatedTools = ["harmony_signup", "harmony_onboard"];
27463
27519
  if (!unauthenticatedTools.includes(name) && !deps.isConfigured()) {
@@ -27507,16 +27563,29 @@ async function handleToolCall(name, args, deps) {
27507
27563
  }
27508
27564
  case "harmony_move_card": {
27509
27565
  const cardId = exports_external.string().uuid().parse(args.cardId);
27510
- const columnId = exports_external.string().uuid().parse(args.columnId);
27511
27566
  const position = args.position !== undefined ? exports_external.number().int().min(0).parse(args.position) : undefined;
27567
+ let columnId;
27568
+ let resolvedProjectId;
27569
+ if (args.columnId) {
27570
+ columnId = exports_external.string().uuid().parse(args.columnId);
27571
+ } else if (args.columnName) {
27572
+ const columnName = exports_external.string().parse(args.columnName);
27573
+ const { card: cardForProject } = await client3.getCard(cardId);
27574
+ resolvedProjectId = cardForProject?.project_id;
27575
+ if (!resolvedProjectId)
27576
+ throw new Error("Card has no project");
27577
+ const col = await resolveColumnByName(client3, resolvedProjectId, columnName);
27578
+ columnId = col.id;
27579
+ } else {
27580
+ throw new Error("Either columnId or columnName is required");
27581
+ }
27512
27582
  const result = await client3.moveCard(cardId, columnId, position);
27513
27583
  let sessionEnded = false;
27514
27584
  try {
27515
27585
  const { card } = result;
27516
- if (card?.project_id) {
27517
- const board = await client3.getBoard(card.project_id, {
27518
- summary: true
27519
- });
27586
+ const projectId = card?.project_id || resolvedProjectId;
27587
+ if (projectId) {
27588
+ const board = await client3.getBoard(projectId, { summary: true });
27520
27589
  const columns = board.columns;
27521
27590
  const destCol = columns.find((c) => c.id === columnId);
27522
27591
  const colName = destCol?.name?.toLowerCase() || "";
@@ -27603,7 +27672,32 @@ async function handleToolCall(name, args, deps) {
27603
27672
  }
27604
27673
  case "harmony_add_label_to_card": {
27605
27674
  const cardId = exports_external.string().uuid().parse(args.cardId);
27606
- const labelId = exports_external.string().uuid().parse(args.labelId);
27675
+ let labelId = args.labelId ? exports_external.string().uuid().parse(args.labelId) : undefined;
27676
+ const labelName = args.labelName ? exports_external.string().min(1).max(100).parse(args.labelName) : undefined;
27677
+ if (!labelId && labelName) {
27678
+ const { card } = await client3.getCard(cardId);
27679
+ const projectId = card.project_id;
27680
+ if (!projectId) {
27681
+ throw new Error("Cannot resolve label by name: card has no project_id");
27682
+ }
27683
+ if (projectId) {
27684
+ const board = await client3.getBoard(projectId, { summary: true });
27685
+ const labels = board.labels;
27686
+ const existing = labels.find((l) => l.name.toLowerCase() === labelName.toLowerCase());
27687
+ if (existing) {
27688
+ labelId = existing.id;
27689
+ } else {
27690
+ const created = await client3.createLabel(projectId, {
27691
+ name: labelName,
27692
+ color: "#57b8a5"
27693
+ });
27694
+ labelId = created.label.id;
27695
+ }
27696
+ }
27697
+ }
27698
+ if (!labelId) {
27699
+ throw new Error("Either labelId or labelName must be provided");
27700
+ }
27607
27701
  await client3.addLabelToCard(cardId, labelId);
27608
27702
  return { success: true };
27609
27703
  }
@@ -27733,10 +27827,17 @@ async function handleToolCall(name, args, deps) {
27733
27827
  }
27734
27828
  if (addLabels?.length) {
27735
27829
  for (const labelName of addLabels) {
27736
- const label = labels.find((l) => l.name.toLowerCase() === labelName.toLowerCase());
27830
+ let label = labels.find((l) => l.name.toLowerCase() === labelName.toLowerCase());
27831
+ if (!label && projectId) {
27832
+ const created = await client3.createLabel(projectId, {
27833
+ name: labelName,
27834
+ color: "#57b8a5"
27835
+ });
27836
+ label = created.label;
27837
+ }
27737
27838
  if (label) {
27738
27839
  await client3.addLabelToCard(cardId, label.id);
27739
- labelsAdded.push(label.name);
27840
+ labelsAdded.push(label.name ?? labelName);
27740
27841
  }
27741
27842
  }
27742
27843
  }
@@ -27856,27 +27957,21 @@ async function handleToolCall(name, args, deps) {
27856
27957
  const endProgressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
27857
27958
  await flushMemoryActions(client3, cardId);
27858
27959
  cleanupMemorySession(cardId);
27859
- const result = await client3.endAgentSession(cardId, {
27860
- status: sessionStatus,
27861
- progressPercent: endProgressPercent
27862
- });
27960
+ let result = { session: null };
27961
+ let sessionEndError = null;
27962
+ try {
27963
+ result = await client3.endAgentSession(cardId, {
27964
+ status: sessionStatus,
27965
+ progressPercent: endProgressPercent
27966
+ });
27967
+ } catch (err) {
27968
+ sessionEndError = err instanceof Error ? err.message : "Failed to end session";
27969
+ }
27863
27970
  untrack(cardId);
27864
27971
  let movedTo = null;
27865
- const _learningsExtracted = 0;
27866
- let _cardTitle = "";
27867
- let _cardLabels = [];
27868
- let _cardDescription = "";
27869
- let _cardSubtasks = [];
27870
27972
  try {
27871
27973
  const { card } = await client3.getCard(cardId);
27872
27974
  const typedCard = card;
27873
- _cardTitle = typedCard.title || "";
27874
- _cardLabels = (typedCard.labels || []).map((l) => l.name);
27875
- _cardDescription = typedCard.description || "";
27876
- _cardSubtasks = (typedCard.subtasks || []).map((s) => ({
27877
- title: s.title,
27878
- done: s.done
27879
- }));
27880
27975
  const projectId = typedCard.project_id;
27881
27976
  if (sessionStatus === "completed" && typedCard.labels?.length) {
27882
27977
  const agentLabel = typedCard.labels.find((l) => l.name.toLowerCase() === "agent");
@@ -27885,21 +27980,16 @@ async function handleToolCall(name, args, deps) {
27885
27980
  }
27886
27981
  }
27887
27982
  if (moveToColumn && projectId) {
27888
- const board = await client3.getBoard(projectId, {
27889
- summary: true
27890
- });
27891
- const columns = board.columns;
27892
- const col = columns.find((c) => c.name.toLowerCase().includes(moveToColumn.toLowerCase()));
27893
- if (col) {
27894
- await client3.moveCard(cardId, col.id);
27895
- movedTo = col.name;
27896
- }
27983
+ const col = await resolveColumnByName(client3, projectId, moveToColumn);
27984
+ await client3.moveCard(cardId, col.id);
27985
+ movedTo = col.name;
27897
27986
  }
27898
27987
  } catch {}
27899
27988
  const sessionObj = result.session;
27900
27989
  const pipelineResult = await runEndSessionPipeline(client3, deps, cardId, sessionStatus, endProgressPercent, sessionObj);
27901
27990
  return {
27902
27991
  success: true,
27992
+ ...sessionEndError && { sessionEndError },
27903
27993
  movedTo,
27904
27994
  learningsExtracted: pipelineResult.learningsExtracted,
27905
27995
  feedbackAdjusted: pipelineResult.feedbackAdjusted,
@@ -28641,38 +28731,27 @@ async function handleToolCall(name, args, deps) {
28641
28731
  const projectName = args.projectName || "My First Project";
28642
28732
  const template = args.template || "kanban";
28643
28733
  const keyName = args.keyName || "mcp-agent";
28644
- const apiUrl = deps.getApiUrl();
28645
- const signupResult = await signupUser(apiUrl, {
28734
+ const result = await onboardNewUser({
28646
28735
  email: email3,
28647
28736
  password,
28648
- full_name: fullName
28649
- });
28650
- const token = signupResult.session.access_token;
28651
- const workspaceResult = await requestWithBearer(apiUrl, token, "POST", "/workspaces", {
28652
- name: workspaceName
28737
+ fullName,
28738
+ workspaceName,
28739
+ projectName,
28740
+ template,
28741
+ keyName,
28742
+ apiUrl: deps.getApiUrl()
28653
28743
  });
28654
- const projectResult = await requestWithBearer(apiUrl, token, "POST", "/projects", {
28655
- workspaceId: workspaceResult.workspace.id,
28656
- name: projectName,
28657
- template
28658
- });
28659
- const keyResult = await requestWithBearer(apiUrl, token, "POST", "/api-keys", {
28660
- name: keyName
28661
- });
28662
- deps.saveConfig({ apiKey: keyResult.rawKey });
28663
- deps.setActiveWorkspace(workspaceResult.workspace.id);
28664
- deps.setActiveProject(projectResult.project.id);
28744
+ deps.saveConfig({ apiKey: result.apiKey.rawKey });
28745
+ deps.setActiveWorkspace(result.workspace.id);
28746
+ deps.setActiveProject(result.project.id);
28665
28747
  deps.resetClient();
28666
28748
  return {
28667
28749
  success: true,
28668
- user: signupResult.user,
28669
- workspace: workspaceResult.workspace,
28670
- project: projectResult.project,
28671
- columns: projectResult.columns,
28672
- apiKey: {
28673
- rawKey: keyResult.rawKey,
28674
- prefix: keyResult.apiKey.prefix
28675
- },
28750
+ user: result.user,
28751
+ workspace: result.workspace,
28752
+ project: result.project,
28753
+ columns: result.columns,
28754
+ apiKey: result.apiKey,
28676
28755
  message: `Onboarding complete! Account created for ${email3}. Workspace "${workspaceName}" and project "${projectName}" are ready. API key saved to config.`
28677
28756
  };
28678
28757
  }
@@ -6,7 +6,7 @@
6
6
  * after 10 minutes of inactivity or when a different card is worked on.
7
7
  *
8
8
  * Agent identity is resolved from the MCP client's `initialize` handshake
9
- * (clientInfo.name), so "Claude Code", "Cursor", "Windsurf", etc. are
9
+ * (clientInfo.name), so "Claude Code", "Cursor", "Codex", etc. are
10
10
  * detected automatically — no hardcoded fallback needed.
11
11
  */
12
12
  /** Well-known MCP client names → human-friendly display names */
package/dist/lib/cli.js CHANGED
@@ -15,6 +15,11 @@ program
15
15
  .command("serve")
16
16
  .description("Start the MCP server (stdio transport)")
17
17
  .action(async () => {
18
+ if (!isConfigured()) {
19
+ console.error("No API key configured.");
20
+ console.error("Run: npx @gethmy/mcp setup");
21
+ process.exit(1);
22
+ }
18
23
  await refreshSkills();
19
24
  const server = new HarmonyMCPServer();
20
25
  await server.run();
@@ -109,6 +114,8 @@ program
109
114
  .option("-p, --project <id>", "Set project context")
110
115
  .option("--skip-context", "Skip workspace/project selection")
111
116
  .option("--skip-docs", "Skip project docs scaffold/verification")
117
+ .option("--new", "Create a new account (skip the choice prompt)")
118
+ .option("-n, --name <name>", "Full name (for account creation)")
112
119
  .action(async (options) => {
113
120
  await runSetup({
114
121
  force: options.force,
@@ -124,6 +131,8 @@ program
124
131
  projectId: options.project,
125
132
  skipContext: options.skipContext,
126
133
  skipDocs: options.skipDocs,
134
+ newAccount: options.new,
135
+ name: options.name,
127
136
  });
128
137
  });
129
138
  program.parse();
@@ -0,0 +1,36 @@
1
+ import { requestWithBearer, signupUser } from "./api-client.js";
2
+ import { getApiUrl } from "./config.js";
3
+ export async function onboardNewUser(params) {
4
+ const { email, password, fullName, workspaceName = `${fullName}'s Workspace`, projectName = "My First Board", template = "kanban", keyName = "mcp-agent", apiUrl = getApiUrl(), } = params;
5
+ // 1. Signup
6
+ const signupResult = await signupUser(apiUrl, {
7
+ email,
8
+ password,
9
+ full_name: fullName,
10
+ });
11
+ const token = signupResult.session.access_token;
12
+ // 2. Create workspace
13
+ const workspaceResult = await requestWithBearer(apiUrl, token, "POST", "/workspaces", {
14
+ name: workspaceName,
15
+ });
16
+ // 3. Create project
17
+ const projectResult = await requestWithBearer(apiUrl, token, "POST", "/projects", {
18
+ workspaceId: workspaceResult.workspace.id,
19
+ name: projectName,
20
+ template,
21
+ });
22
+ // 4. Generate API key
23
+ const keyResult = await requestWithBearer(apiUrl, token, "POST", "/api-keys", {
24
+ name: keyName,
25
+ });
26
+ return {
27
+ user: signupResult.user,
28
+ workspace: workspaceResult.workspace,
29
+ project: projectResult.project,
30
+ columns: projectResult.columns,
31
+ apiKey: {
32
+ rawKey: keyResult.rawKey,
33
+ prefix: keyResult.apiKey.prefix,
34
+ },
35
+ };
36
+ }