alpic 0.0.0-dev.ge3eb38c → 0.0.0-dev.ge422ee2

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.
Files changed (129) hide show
  1. package/README.md +4 -0
  2. package/dist/__tests__/audit.e2e.test.js +1 -1
  3. package/dist/__tests__/audit.e2e.test.js.map +1 -1
  4. package/dist/__tests__/environment-delete.e2e.test.d.ts +1 -0
  5. package/dist/__tests__/environment-delete.e2e.test.js +95 -0
  6. package/dist/__tests__/environment-delete.e2e.test.js.map +1 -0
  7. package/dist/__tests__/insights-category.e2e.test.d.ts +1 -0
  8. package/dist/__tests__/insights-category.e2e.test.js +247 -0
  9. package/dist/__tests__/insights-category.e2e.test.js.map +1 -0
  10. package/dist/__tests__/insights.e2e.test.d.ts +1 -0
  11. package/dist/__tests__/insights.e2e.test.js +193 -0
  12. package/dist/__tests__/insights.e2e.test.js.map +1 -0
  13. package/dist/__tests__/mock-api.d.ts +19 -0
  14. package/dist/__tests__/mock-api.js +297 -32
  15. package/dist/__tests__/mock-api.js.map +1 -1
  16. package/dist/__tests__/submission.e2e.test.d.ts +1 -0
  17. package/dist/__tests__/submission.e2e.test.js +173 -0
  18. package/dist/__tests__/submission.e2e.test.js.map +1 -0
  19. package/dist/__tests__/team-inspect.e2e.test.js +26 -1
  20. package/dist/__tests__/team-inspect.e2e.test.js.map +1 -1
  21. package/dist/__tests__/tunnel.e2e.test.js +67 -14
  22. package/dist/__tests__/tunnel.e2e.test.js.map +1 -1
  23. package/dist/__tests__/utils.d.ts +3 -0
  24. package/dist/__tests__/utils.js +13 -0
  25. package/dist/__tests__/utils.js.map +1 -1
  26. package/dist/commands/audit.js +3 -1
  27. package/dist/commands/audit.js.map +1 -1
  28. package/dist/commands/deploy.js +11 -11
  29. package/dist/commands/deploy.js.map +1 -1
  30. package/dist/commands/deployment/list.js +3 -3
  31. package/dist/commands/deployment/list.js.map +1 -1
  32. package/dist/commands/environment/delete.d.ts +13 -0
  33. package/dist/commands/environment/delete.js +45 -0
  34. package/dist/commands/environment/delete.js.map +1 -0
  35. package/dist/commands/environment/inspect.js +3 -3
  36. package/dist/commands/environment/inspect.js.map +1 -1
  37. package/dist/commands/environment/list.js +3 -3
  38. package/dist/commands/environment/list.js.map +1 -1
  39. package/dist/commands/insights/category/create.d.ts +17 -0
  40. package/dist/commands/insights/category/create.js +63 -0
  41. package/dist/commands/insights/category/create.js.map +1 -0
  42. package/dist/commands/insights/category/delete.d.ts +13 -0
  43. package/dist/commands/insights/category/delete.js +40 -0
  44. package/dist/commands/insights/category/delete.js.map +1 -0
  45. package/dist/commands/insights/category/link.d.ts +14 -0
  46. package/dist/commands/insights/category/link.js +36 -0
  47. package/dist/commands/insights/category/link.js.map +1 -0
  48. package/dist/commands/insights/category/list.d.ts +13 -0
  49. package/dist/commands/insights/category/list.js +46 -0
  50. package/dist/commands/insights/category/list.js.map +1 -0
  51. package/dist/commands/insights/category/unlink.d.ts +14 -0
  52. package/dist/commands/insights/category/unlink.js +36 -0
  53. package/dist/commands/insights/category/unlink.js.map +1 -0
  54. package/dist/commands/insights/category/update.d.ts +16 -0
  55. package/dist/commands/insights/category/update.js +45 -0
  56. package/dist/commands/insights/category/update.js.map +1 -0
  57. package/dist/commands/insights/feedback/list.d.ts +19 -0
  58. package/dist/commands/insights/feedback/list.js +73 -0
  59. package/dist/commands/insights/feedback/list.js.map +1 -0
  60. package/dist/commands/insights/intent/list.d.ts +22 -0
  61. package/dist/commands/insights/intent/list.js +89 -0
  62. package/dist/commands/insights/intent/list.js.map +1 -0
  63. package/dist/commands/link.js +3 -3
  64. package/dist/commands/link.js.map +1 -1
  65. package/dist/commands/logs.js +1 -9
  66. package/dist/commands/logs.js.map +1 -1
  67. package/dist/commands/project/inspect.js +3 -3
  68. package/dist/commands/project/inspect.js.map +1 -1
  69. package/dist/commands/project/list.js +3 -3
  70. package/dist/commands/project/list.js.map +1 -1
  71. package/dist/commands/submission/create.d.ts +15 -0
  72. package/dist/commands/submission/create.js +59 -0
  73. package/dist/commands/submission/create.js.map +1 -0
  74. package/dist/commands/submission/inspect.d.ts +10 -0
  75. package/dist/commands/submission/inspect.js +56 -0
  76. package/dist/commands/submission/inspect.js.map +1 -0
  77. package/dist/commands/submission/list.d.ts +13 -0
  78. package/dist/commands/submission/list.js +63 -0
  79. package/dist/commands/submission/list.js.map +1 -0
  80. package/dist/commands/submission/update.d.ts +12 -0
  81. package/dist/commands/submission/update.js +37 -0
  82. package/dist/commands/submission/update.js.map +1 -0
  83. package/dist/commands/team/inspect.js +25 -10
  84. package/dist/commands/team/inspect.js.map +1 -1
  85. package/dist/commands/team/list.js +3 -3
  86. package/dist/commands/team/list.js.map +1 -1
  87. package/dist/commands/tunnel.js +5 -5
  88. package/dist/commands/tunnel.js.map +1 -1
  89. package/dist/lib/agent-mode.d.ts +8 -1
  90. package/dist/lib/agent-mode.js +25 -26
  91. package/dist/lib/agent-mode.js.map +1 -1
  92. package/dist/lib/alpic-command.d.ts +2 -1
  93. package/dist/lib/alpic-command.js +3 -3
  94. package/dist/lib/alpic-command.js.map +1 -1
  95. package/dist/lib/audit.d.ts +1 -0
  96. package/dist/lib/audit.js +20 -13
  97. package/dist/lib/audit.js.map +1 -1
  98. package/dist/lib/base-workflow.d.ts +2 -1
  99. package/dist/lib/base-workflow.js +3 -3
  100. package/dist/lib/base-workflow.js.map +1 -1
  101. package/dist/lib/deployment.js +12 -12
  102. package/dist/lib/deployment.js.map +1 -1
  103. package/dist/lib/environment-variable.js +13 -13
  104. package/dist/lib/environment-variable.js.map +1 -1
  105. package/dist/lib/insights.d.ts +19 -0
  106. package/dist/lib/insights.js +54 -0
  107. package/dist/lib/insights.js.map +1 -0
  108. package/dist/lib/link.js +4 -4
  109. package/dist/lib/link.js.map +1 -1
  110. package/dist/lib/playground.js +25 -25
  111. package/dist/lib/playground.js.map +1 -1
  112. package/dist/lib/publish.js +3 -3
  113. package/dist/lib/publish.js.map +1 -1
  114. package/dist/lib/resolve.d.ts +7 -6
  115. package/dist/lib/resolve.js +23 -18
  116. package/dist/lib/resolve.js.map +1 -1
  117. package/dist/lib/submission.d.ts +13 -0
  118. package/dist/lib/submission.js +52 -0
  119. package/dist/lib/submission.js.map +1 -0
  120. package/dist/lib/telemetry.js +8 -5
  121. package/dist/lib/telemetry.js.map +1 -1
  122. package/dist/lib/utils.d.ts +8 -1
  123. package/dist/lib/utils.js +14 -1
  124. package/dist/lib/utils.js.map +1 -1
  125. package/dist/lib/utils.test.js +28 -5
  126. package/dist/lib/utils.test.js.map +1 -1
  127. package/dist/sentry.js +1 -0
  128. package/dist/sentry.js.map +1 -1
  129. package/package.json +20 -20
@@ -0,0 +1,193 @@
1
+ import { beforeEach, describe, expect, it } from "vitest";
2
+ import { cliSession, createConfigFile, expectSuccessfulApiCall, registerCliE2ELifecycle } from "./utils.js";
3
+ const ENV_ID = "env-insights-test";
4
+ const PROJECT_ID = "prj-insights-test";
5
+ const TEAM_ID = "mock-team-id";
6
+ describe("alpic insights (E2E)", () => {
7
+ const context = registerCliE2ELifecycle({
8
+ projects: [
9
+ {
10
+ projectId: PROJECT_ID,
11
+ teamId: TEAM_ID,
12
+ projectName: "insights-project",
13
+ environmentId: ENV_ID,
14
+ environmentName: "production",
15
+ },
16
+ ],
17
+ });
18
+ let mockServer;
19
+ let serverUrl;
20
+ let cwd;
21
+ let binPath;
22
+ beforeEach(() => {
23
+ ({ mockServer, serverUrl, cwd, binPath } = context);
24
+ mockServer.setIntents(ENV_ID, [
25
+ {
26
+ id: "intent-1",
27
+ intentIds: ["intent-1", "intent-2"],
28
+ environmentId: ENV_ID,
29
+ message: `Schedule a meeting ${"with the product team ".repeat(6)}`,
30
+ toolName: "create-event",
31
+ createdAt: new Date("2026-08-12T10:00:00.000Z"),
32
+ occurrenceCount: 2,
33
+ clientName: "Claude Desktop",
34
+ clientId: "claude-desktop",
35
+ categories: [{ id: "category-scheduling", name: "Scheduling", color: "blue" }],
36
+ },
37
+ {
38
+ id: "intent-2",
39
+ intentIds: ["intent-3"],
40
+ environmentId: ENV_ID,
41
+ message: "Find the latest release notes",
42
+ toolName: "search",
43
+ createdAt: new Date("2026-08-12T09:00:00.000Z"),
44
+ occurrenceCount: 1,
45
+ clientName: null,
46
+ clientId: null,
47
+ categories: [],
48
+ },
49
+ ]);
50
+ mockServer.setFeedbacks(ENV_ID, [
51
+ {
52
+ id: "feedback-1",
53
+ environmentId: ENV_ID,
54
+ content: `The calendar tool timed out ${"after I selected a time ".repeat(6)}`,
55
+ source: "user",
56
+ createdAt: new Date("2026-08-12T11:00:00.000Z"),
57
+ clientName: "Claude Desktop",
58
+ clientId: "claude-desktop",
59
+ },
60
+ {
61
+ id: "feedback-2",
62
+ environmentId: ENV_ID,
63
+ content: "The calendar tool timed out again",
64
+ source: "model",
65
+ createdAt: new Date("2026-08-12T10:30:00.000Z"),
66
+ clientName: "Claude Desktop",
67
+ clientId: "claude-desktop",
68
+ },
69
+ ]);
70
+ });
71
+ const env = () => ({ ALPIC_API_BASE_URL: serverUrl, ALPIC_API_KEY: "test-api-key" });
72
+ it("lists intents using the local environment and truncates human output", async () => {
73
+ createConfigFile(cwd, PROJECT_ID, TEAM_ID, "insights-project", ENV_ID, "production");
74
+ const { exitCode, output } = await cliSession(process.execPath, [binPath, "insights", "intent", "list", "--limit", "1"], {
75
+ cwd,
76
+ env: env(),
77
+ });
78
+ expect(exitCode).toBe(0);
79
+ expect(output).toContain("Schedule a meeting");
80
+ expect(output).toContain("…");
81
+ expect(output).toContain("Found 2 intents");
82
+ expect(output).toContain("occurrences");
83
+ const call = mockServer.getLastCall("GET", /\/v1\/environments\/.*\/intents/);
84
+ expectSuccessfulApiCall(call);
85
+ const input = call.input;
86
+ expect(input.since).toBe("7d");
87
+ expect(input.until).toBeUndefined();
88
+ });
89
+ it("passes intent filters, pagination, sorting, and an explicit time range", async () => {
90
+ const { exitCode } = await cliSession(process.execPath, [
91
+ binPath,
92
+ "insights",
93
+ "intent",
94
+ "list",
95
+ "--environment-id",
96
+ ENV_ID,
97
+ "--since",
98
+ "2026-08-01T00:00:00Z",
99
+ "--until",
100
+ "2026-08-12T00:00:00Z",
101
+ "--search",
102
+ "meeting",
103
+ "--tool",
104
+ "create-event",
105
+ "--client-id",
106
+ "claude-desktop",
107
+ "--category-id",
108
+ "category-scheduling",
109
+ "--sort",
110
+ "asc",
111
+ "--limit",
112
+ "20",
113
+ "--offset",
114
+ "1",
115
+ ], { cwd, env: env() });
116
+ expect(exitCode).toBe(0);
117
+ const input = mockServer.getLastCall("GET", /\/intents/)?.input;
118
+ expect(input).toMatchObject({
119
+ since: "2026-08-01T00:00:00Z",
120
+ until: "2026-08-12T00:00:00Z",
121
+ search: "meeting",
122
+ sortDir: "asc",
123
+ limit: "20",
124
+ offset: "1",
125
+ });
126
+ expect(input["tools[0]"]).toBe("create-event");
127
+ expect(input["clientIds[0]"]).toBe("claude-desktop");
128
+ expect(input["categoryIds[0]"]).toBe("category-scheduling");
129
+ });
130
+ it("emits intent JSON and resolves an environment name", async () => {
131
+ const { exitCode, output } = await cliSession(process.execPath, [
132
+ binPath,
133
+ "insights",
134
+ "intent",
135
+ "list",
136
+ "--project-name",
137
+ "insights-project",
138
+ "--environment-name",
139
+ "production",
140
+ "--json",
141
+ ], { cwd, env: env() });
142
+ expect(exitCode).toBe(0);
143
+ const json = output.trim();
144
+ expect(json).toBeDefined();
145
+ const parsed = JSON.parse(json ?? "");
146
+ expect(parsed.intents[0].message).toContain("with the product team");
147
+ expect(parsed.intents[0].createdAt).toBe("2026-08-12T10:00:00.000Z");
148
+ expectSuccessfulApiCall(mockServer.getLastCall("GET", `/v1/environments/${ENV_ID}/intents`));
149
+ });
150
+ it("lists feedback and passes its filters", async () => {
151
+ const { exitCode, output } = await cliSession(process.execPath, [
152
+ binPath,
153
+ "insights",
154
+ "feedback",
155
+ "list",
156
+ "--environment-id",
157
+ ENV_ID,
158
+ "--search",
159
+ "calendar",
160
+ "--client-id",
161
+ "claude-desktop",
162
+ "--limit",
163
+ "1",
164
+ "--offset",
165
+ "0",
166
+ ], { cwd, env: env() });
167
+ expect(exitCode).toBe(0);
168
+ expect(output).toContain("Found 2 feedbacks");
169
+ expect(output).toContain("calendar tool timed out");
170
+ const input = mockServer.getLastCall("GET", /\/feedbacks/)?.input;
171
+ expect(input).toMatchObject({ search: "calendar", limit: "1", offset: "0" });
172
+ expect(input["clientIds[0]"]).toBe("claude-desktop");
173
+ });
174
+ it("prints empty results and rejects invalid time values before calling the API", async () => {
175
+ mockServer.setFeedbacks(ENV_ID, []);
176
+ const empty = await cliSession(process.execPath, [binPath, "insights", "feedback", "list", "--environment-id", ENV_ID], { cwd, env: env() });
177
+ expect(empty.exitCode).toBe(0);
178
+ expect(empty.output).toContain("No feedback found for this period.");
179
+ const invalid = await cliSession(process.execPath, [binPath, "insights", "feedback", "list", "--environment-id", ENV_ID, "--since", "yesterday"], { cwd, env: env() });
180
+ expect(invalid.exitCode).toBe(1);
181
+ expect(invalid.output).toContain("Invalid --since value");
182
+ expect(mockServer.getCalls("GET", /\/feedbacks/)).toHaveLength(1);
183
+ });
184
+ it("reports authentication and missing environment errors", async () => {
185
+ const unauthenticated = await cliSession(process.execPath, [binPath, "insights", "intent", "list", "--environment-id", ENV_ID, "--non-interactive"], { cwd, env: { ALPIC_API_BASE_URL: serverUrl } });
186
+ expect(unauthenticated.exitCode).toBe(1);
187
+ expect(unauthenticated.output).toContain("Not authenticated");
188
+ const missing = await cliSession(process.execPath, [binPath, "insights", "intent", "list", "--environment-id", "missing"], { cwd, env: env() });
189
+ expect(missing.exitCode).toBe(1);
190
+ expect(missing.output).toContain("Environment not found");
191
+ });
192
+ });
193
+ //# sourceMappingURL=insights.e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/insights.e2e.test.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ export type MockDeploymentLogPage = {
8
8
  }[];
9
9
  hasMoreLogs: boolean;
10
10
  };
11
+ type MockTeamUsage = RouterOutput["teams"]["getUsage"]["v1"];
11
12
  type BeaconGetResponse = RouterOutput["beacon"]["get"]["v1"];
12
13
  type MockPlaygroundData = RouterOutput["playground"]["get"]["v1"];
13
14
  type MockTunnelTicket = {
@@ -15,6 +16,12 @@ type MockTunnelTicket = {
15
16
  ticket: string;
16
17
  tunnelHost: string;
17
18
  };
19
+ type MockIntents = RouterOutput["intents"]["list"]["v1"];
20
+ type MockFeedbacks = RouterOutput["feedbacks"]["list"]["v1"];
21
+ type MockIntentCategory = RouterOutput["intentCategories"]["create"]["v1"] & {
22
+ linkedIntentIds: Set<string>;
23
+ };
24
+ type MockSubmission = RouterOutput["submissions"]["get"]["v1"];
18
25
  export type ApiCall = {
19
26
  method: string;
20
27
  path: string;
@@ -40,6 +47,7 @@ export declare class MockApiServer {
40
47
  id: string;
41
48
  name: string;
42
49
  }[]): void;
50
+ setTeamUsage(usage: MockTeamUsage): void;
43
51
  addEnvironmentVariables(environmentId: string, variables: RouterOutput["environmentVariables"]["list"]["v1"]): void;
44
52
  setRuntimeLogs(environmentId: string, logs: MockRuntimeLog[]): void;
45
53
  setRuntimeLogPages(environmentId: string, pages: MockRuntimeLog[][]): void;
@@ -48,6 +56,17 @@ export declare class MockApiServer {
48
56
  setDeploymentLogPages(deploymentId: string, pages: MockDeploymentLogPage[]): void;
49
57
  setBeaconGetResponses(responses: BeaconGetResponse[]): void;
50
58
  setPlayground(environmentId: string, playground: MockPlaygroundData): void;
59
+ setIntents(environmentId: string, intents: MockIntents["intents"]): void;
60
+ setIntentCategories(environmentId: string, categories: Array<Omit<MockIntentCategory, "linkedIntentIds"> & {
61
+ linkedIntentIds?: string[];
62
+ }>): void;
63
+ private requireMockEnvironmentCategories;
64
+ private requireMockIntentCategory;
65
+ private requireMockIntents;
66
+ private removeCategoryFromIntents;
67
+ setFeedbacks(environmentId: string, feedbacks: MockFeedbacks["feedbacks"]): void;
68
+ addSubmission(submission: MockSubmission): void;
69
+ setSubmissionPrefill(platform: MockSubmission["platform"], formData: MockSubmission["formData"]): void;
51
70
  addProject(project: RouterOutput["projects"]["get"]["v1"]): void;
52
71
  }
53
72
  export {};
@@ -8,6 +8,31 @@ import { OpenAPIHandler } from "@orpc/openapi/node";
8
8
  import { implement, ORPCError } from "@orpc/server";
9
9
  import express, {} from "express";
10
10
  import { inc as semverInc } from "semver";
11
+ function createEmptyMockData() {
12
+ return {
13
+ tunnelTicket: null,
14
+ teams: null,
15
+ projects: new Map(),
16
+ environments: new Map(),
17
+ deployments: new Map(),
18
+ environmentVariables: new Map(),
19
+ runtimeLogs: new Map(),
20
+ runtimeLogPages: new Map(),
21
+ deploymentLogPages: new Map(),
22
+ deploymentLogCallCounts: new Map(),
23
+ publishedServers: new Map(),
24
+ beaconGetResponses: [],
25
+ beaconGetCallCount: 0,
26
+ playgrounds: new Map(),
27
+ teamUsages: new Map(),
28
+ intents: new Map(),
29
+ feedbacks: new Map(),
30
+ intentCategories: new Map(),
31
+ intentCategoryCounter: 0,
32
+ submissions: new Map(),
33
+ submissionPrefills: new Map(),
34
+ };
35
+ }
11
36
  const __testDirname = dirname(fileURLToPath(import.meta.url));
12
37
  const FIXTURE_CERT_PATH = join(__testDirname, "fixtures", "certs", "localhost-cert.pem");
13
38
  const FIXTURE_KEY_PATH = join(__testDirname, "fixtures", "certs", "localhost-key.pem");
@@ -26,22 +51,7 @@ export class MockApiServer {
26
51
  callHistory = [];
27
52
  constructor() {
28
53
  this.app = express();
29
- this.mockData = {
30
- tunnelTicket: null,
31
- teams: null,
32
- projects: new Map(),
33
- environments: new Map(),
34
- deployments: new Map(),
35
- environmentVariables: new Map(),
36
- runtimeLogs: new Map(),
37
- runtimeLogPages: new Map(),
38
- deploymentLogPages: new Map(),
39
- deploymentLogCallCounts: new Map(),
40
- publishedServers: new Map(),
41
- beaconGetResponses: [],
42
- beaconGetCallCount: 0,
43
- playgrounds: new Map(),
44
- };
54
+ this.mockData = createEmptyMockData();
45
55
  }
46
56
  async start() {
47
57
  if (this.server) {
@@ -140,6 +150,15 @@ export class MockApiServer {
140
150
  ];
141
151
  }),
142
152
  },
153
+ getUsage: {
154
+ v1: implement(contract.teams.getUsage.v1).handler(async ({ input }) => {
155
+ const usage = this.mockData.teamUsages.get(input.teamId);
156
+ if (!usage) {
157
+ throw new ORPCError("NOT_FOUND", { message: "Team usage not found" });
158
+ }
159
+ return usage;
160
+ }),
161
+ },
143
162
  },
144
163
  analytics: {
145
164
  get: {
@@ -172,6 +191,129 @@ export class MockApiServer {
172
191
  }),
173
192
  },
174
193
  },
194
+ intents: {
195
+ list: {
196
+ v1: implement(contract.intents.list.v1).handler(async ({ input }) => {
197
+ if (!this.mockData.environments.has(input.environmentId)) {
198
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
199
+ }
200
+ let intents = this.mockData.intents.get(input.environmentId) ?? [];
201
+ if (input.search) {
202
+ const search = input.search.toLowerCase();
203
+ intents = intents.filter((intent) => intent.message.toLowerCase().includes(search) ||
204
+ intent.toolName.toLowerCase().includes(search) ||
205
+ intent.categories.some(({ name }) => name.toLowerCase().includes(search)));
206
+ }
207
+ if (input.tools) {
208
+ intents = intents.filter(({ toolName }) => input.tools?.includes(toolName));
209
+ }
210
+ if (input.clientIds) {
211
+ intents = intents.filter(({ clientId }) => clientId !== null && input.clientIds?.includes(clientId));
212
+ }
213
+ if (input.categoryIds) {
214
+ intents = intents.filter(({ categories }) => categories.some(({ id }) => input.categoryIds?.includes(id)));
215
+ }
216
+ intents = [...intents].sort((left, right) => input.sortDir === "asc"
217
+ ? left.createdAt.getTime() - right.createdAt.getTime()
218
+ : right.createdAt.getTime() - left.createdAt.getTime());
219
+ return {
220
+ intents: intents.slice(input.offset, input.offset + input.limit),
221
+ totalCount: intents.length,
222
+ };
223
+ }),
224
+ },
225
+ },
226
+ intentCategories: {
227
+ list: {
228
+ v1: implement(contract.intentCategories.list.v1).handler(async ({ input }) => {
229
+ const categories = this.requireMockEnvironmentCategories(input.environmentId);
230
+ return categories.map(({ linkedIntentIds, ...category }) => ({
231
+ ...category,
232
+ intentCount: linkedIntentIds.size,
233
+ }));
234
+ }),
235
+ },
236
+ create: {
237
+ v1: implement(contract.intentCategories.create.v1).handler(async ({ input }) => {
238
+ const categories = this.requireMockEnvironmentCategories(input.environmentId);
239
+ this.requireMockIntents(input.environmentId, input.intentIds ?? [], "BAD_REQUEST");
240
+ const { linkedIntentIds, ...category } = {
241
+ id: `cat-mock-${++this.mockData.intentCategoryCounter}`,
242
+ createdAt: new Date("2026-08-13T00:00:00.000Z"),
243
+ environmentId: input.environmentId,
244
+ name: input.name,
245
+ color: input.color ?? "gray",
246
+ linkedIntentIds: new Set(input.intentIds ?? []),
247
+ };
248
+ categories.push({ ...category, linkedIntentIds });
249
+ return category;
250
+ }),
251
+ },
252
+ update: {
253
+ v1: implement(contract.intentCategories.update.v1).handler(async ({ input }) => {
254
+ const category = this.requireMockIntentCategory(input.environmentId, input.categoryId);
255
+ category.name = input.name ?? category.name;
256
+ category.color = input.color ?? category.color;
257
+ const { linkedIntentIds, ...updated } = category;
258
+ return updated;
259
+ }),
260
+ },
261
+ delete: {
262
+ v1: implement(contract.intentCategories.delete.v1).handler(async ({ input }) => {
263
+ const category = this.requireMockIntentCategory(input.environmentId, input.categoryId);
264
+ this.mockData.intentCategories.set(input.environmentId, (this.mockData.intentCategories.get(input.environmentId) ?? []).filter(({ id }) => id !== category.id));
265
+ this.removeCategoryFromIntents(input.environmentId, category.id, null);
266
+ return { success: true };
267
+ }),
268
+ },
269
+ link: {
270
+ v1: implement(contract.intentCategories.link.v1).handler(async ({ input }) => {
271
+ const category = this.requireMockIntentCategory(input.environmentId, input.categoryId);
272
+ this.requireMockIntents(input.environmentId, input.intentIds, "NOT_FOUND");
273
+ for (const intentId of input.intentIds) {
274
+ category.linkedIntentIds.add(intentId);
275
+ }
276
+ for (const intent of this.mockData.intents.get(input.environmentId) ?? []) {
277
+ const isLinked = intent.intentIds.some((intentId) => category.linkedIntentIds.has(intentId));
278
+ if (isLinked && !intent.categories.some(({ id }) => id === category.id)) {
279
+ intent.categories.push({ id: category.id, name: category.name, color: category.color });
280
+ }
281
+ }
282
+ return { success: true };
283
+ }),
284
+ },
285
+ unlink: {
286
+ v1: implement(contract.intentCategories.unlink.v1).handler(async ({ input }) => {
287
+ const category = this.requireMockIntentCategory(input.environmentId, input.categoryId);
288
+ for (const intentId of input.intentIds) {
289
+ category.linkedIntentIds.delete(intentId);
290
+ }
291
+ this.removeCategoryFromIntents(input.environmentId, category.id, category.linkedIntentIds);
292
+ return { success: true };
293
+ }),
294
+ },
295
+ },
296
+ feedbacks: {
297
+ list: {
298
+ v1: implement(contract.feedbacks.list.v1).handler(async ({ input }) => {
299
+ if (!this.mockData.environments.has(input.environmentId)) {
300
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
301
+ }
302
+ let feedbacks = this.mockData.feedbacks.get(input.environmentId) ?? [];
303
+ if (input.search) {
304
+ const search = input.search.toLowerCase();
305
+ feedbacks = feedbacks.filter((feedback) => feedback.content.toLowerCase().includes(search) || feedback.source.toLowerCase().includes(search));
306
+ }
307
+ if (input.clientIds) {
308
+ feedbacks = feedbacks.filter(({ clientId }) => clientId !== null && input.clientIds?.includes(clientId));
309
+ }
310
+ return {
311
+ feedbacks: feedbacks.slice(input.offset, input.offset + input.limit),
312
+ totalCount: feedbacks.length,
313
+ };
314
+ }),
315
+ },
316
+ },
175
317
  projects: {
176
318
  list: {
177
319
  v1: implement(contract.projects.list.v1).handler(async ({ input }) => {
@@ -355,6 +497,30 @@ export class MockApiServer {
355
497
  return environment;
356
498
  }),
357
499
  },
500
+ delete: {
501
+ v1: implement(contract.environments.delete.v1).handler(async ({ input }) => {
502
+ const environment = this.mockData.environments.get(input.environmentId);
503
+ if (!environment) {
504
+ throw new ORPCError("NOT_FOUND", {
505
+ message: "Environment not found",
506
+ });
507
+ }
508
+ const project = this.mockData.projects.get(environment.projectId);
509
+ if (project?.productionEnvironment?.id === input.environmentId) {
510
+ throw new ORPCError("BAD_REQUEST", {
511
+ message: "Cannot delete the production environment of the project",
512
+ });
513
+ }
514
+ this.mockData.environments.delete(input.environmentId);
515
+ if (project) {
516
+ this.mockData.projects.set(environment.projectId, {
517
+ ...project,
518
+ environments: project.environments.filter((env) => env.id !== input.environmentId),
519
+ });
520
+ }
521
+ return { success: true };
522
+ }),
523
+ },
358
524
  getLogs: {
359
525
  v1: implement(contract.environments.getLogs.v1).handler(async ({ input }) => {
360
526
  const environment = this.mockData.environments.get(input.environmentId);
@@ -640,6 +806,73 @@ export class MockApiServer {
640
806
  }),
641
807
  },
642
808
  },
809
+ submissions: {
810
+ create: {
811
+ v1: implement(contract.submissions.create.v1).handler(async ({ input }) => {
812
+ const environment = this.mockData.environments.get(input.environmentId);
813
+ if (!environment) {
814
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
815
+ }
816
+ const formData = this.mockData.submissionPrefills.get(input.platform) ?? {};
817
+ const now = new Date();
818
+ const submission = {
819
+ id: `sub-${randomUUID()}`,
820
+ environmentId: input.environmentId,
821
+ platform: input.platform,
822
+ formData,
823
+ prefillSnapshot: formData,
824
+ auditId: "audit-1",
825
+ urlVerification: null,
826
+ createdAt: now,
827
+ updatedAt: now,
828
+ };
829
+ this.mockData.submissions.set(submission.id, submission);
830
+ return submission;
831
+ }),
832
+ },
833
+ list: {
834
+ v1: implement(contract.submissions.list.v1).handler(async ({ input }) => {
835
+ const project = this.mockData.projects.get(input.projectId);
836
+ if (!project) {
837
+ throw new ORPCError("NOT_FOUND", { message: "Project not found" });
838
+ }
839
+ const environmentIds = new Set(project.environments.map((environment) => environment.id));
840
+ let submissions = [...this.mockData.submissions.values()].filter((submission) => environmentIds.has(submission.environmentId));
841
+ if (input.environmentId) {
842
+ submissions = submissions.filter((submission) => submission.environmentId === input.environmentId);
843
+ }
844
+ if (input.platform) {
845
+ submissions = submissions.filter((submission) => submission.platform === input.platform);
846
+ }
847
+ return submissions;
848
+ }),
849
+ },
850
+ get: {
851
+ v1: implement(contract.submissions.get.v1).handler(async ({ input }) => {
852
+ const submission = this.mockData.submissions.get(input.submissionId);
853
+ if (!submission) {
854
+ throw new ORPCError("NOT_FOUND", { message: "Submission not found" });
855
+ }
856
+ return submission;
857
+ }),
858
+ },
859
+ update: {
860
+ v1: implement(contract.submissions.update.v1).handler(async ({ input }) => {
861
+ const submission = this.mockData.submissions.get(input.submissionId);
862
+ if (!submission) {
863
+ throw new ORPCError("NOT_FOUND", { message: "Submission not found" });
864
+ }
865
+ if (submission.platform !== input.platform) {
866
+ throw new ORPCError("BAD_REQUEST", {
867
+ message: `Platform mismatch: submission ${submission.id} is ${submission.platform}, payload claims ${input.platform}`,
868
+ });
869
+ }
870
+ const updated = { ...submission, formData: input.formData, updatedAt: new Date() };
871
+ this.mockData.submissions.set(submission.id, updated);
872
+ return updated;
873
+ }),
874
+ },
875
+ },
643
876
  deployments: {
644
877
  list: {
645
878
  v1: implement(contract.deployments.list.v1).handler(async ({ input }) => {
@@ -748,22 +981,7 @@ export class MockApiServer {
748
981
  }
749
982
  reset() {
750
983
  this.callHistory = [];
751
- this.mockData = {
752
- tunnelTicket: null,
753
- teams: null,
754
- projects: new Map(),
755
- environments: new Map(),
756
- deployments: new Map(),
757
- environmentVariables: new Map(),
758
- runtimeLogs: new Map(),
759
- runtimeLogPages: new Map(),
760
- deploymentLogPages: new Map(),
761
- deploymentLogCallCounts: new Map(),
762
- publishedServers: new Map(),
763
- beaconGetResponses: [],
764
- beaconGetCallCount: 0,
765
- playgrounds: new Map(),
766
- };
984
+ this.mockData = createEmptyMockData();
767
985
  }
768
986
  async stop() {
769
987
  return new Promise((resolve, reject) => {
@@ -834,6 +1052,9 @@ export class MockApiServer {
834
1052
  hasStripeAccount: false,
835
1053
  }));
836
1054
  }
1055
+ setTeamUsage(usage) {
1056
+ this.mockData.teamUsages.set(usage.teamId, usage);
1057
+ }
837
1058
  addEnvironmentVariables(environmentId, variables) {
838
1059
  this.mockData.environmentVariables.set(environmentId, variables);
839
1060
  }
@@ -867,6 +1088,50 @@ export class MockApiServer {
867
1088
  setPlayground(environmentId, playground) {
868
1089
  this.mockData.playgrounds.set(environmentId, playground);
869
1090
  }
1091
+ setIntents(environmentId, intents) {
1092
+ this.mockData.intents.set(environmentId, intents);
1093
+ }
1094
+ setIntentCategories(environmentId, categories) {
1095
+ this.mockData.intentCategories.set(environmentId, categories.map((category) => ({ ...category, linkedIntentIds: new Set(category.linkedIntentIds ?? []) })));
1096
+ }
1097
+ requireMockEnvironmentCategories(environmentId) {
1098
+ if (!this.mockData.environments.has(environmentId)) {
1099
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
1100
+ }
1101
+ const categories = this.mockData.intentCategories.get(environmentId) ?? [];
1102
+ this.mockData.intentCategories.set(environmentId, categories);
1103
+ return categories;
1104
+ }
1105
+ requireMockIntentCategory(environmentId, categoryId) {
1106
+ const category = this.requireMockEnvironmentCategories(environmentId).find(({ id }) => id === categoryId);
1107
+ if (!category) {
1108
+ throw new ORPCError("NOT_FOUND", { message: "Intent category not found" });
1109
+ }
1110
+ return category;
1111
+ }
1112
+ requireMockIntents(environmentId, intentIds, errorCode) {
1113
+ const knownIntentIds = new Set((this.mockData.intents.get(environmentId) ?? []).flatMap((intent) => intent.intentIds));
1114
+ if (intentIds.some((intentId) => !knownIntentIds.has(intentId))) {
1115
+ throw new ORPCError(errorCode, { message: "One or more intents not found" });
1116
+ }
1117
+ }
1118
+ removeCategoryFromIntents(environmentId, categoryId, remainingIntentIds) {
1119
+ for (const intent of this.mockData.intents.get(environmentId) ?? []) {
1120
+ const stillLinked = remainingIntentIds !== null && intent.intentIds.some((intentId) => remainingIntentIds.has(intentId));
1121
+ if (!stillLinked) {
1122
+ intent.categories = intent.categories.filter(({ id }) => id !== categoryId);
1123
+ }
1124
+ }
1125
+ }
1126
+ setFeedbacks(environmentId, feedbacks) {
1127
+ this.mockData.feedbacks.set(environmentId, feedbacks);
1128
+ }
1129
+ addSubmission(submission) {
1130
+ this.mockData.submissions.set(submission.id, submission);
1131
+ }
1132
+ setSubmissionPrefill(platform, formData) {
1133
+ this.mockData.submissionPrefills.set(platform, formData);
1134
+ }
870
1135
  addProject(project) {
871
1136
  this.mockData.projects.set(project.id, project);
872
1137
  if (project.productionEnvironment) {