alpic 0.0.0-dev.f2f58a6 → 0.0.0-dev.f330b89

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 (178) hide show
  1. package/dist/__tests__/auth.e2e.test.d.ts +1 -0
  2. package/dist/__tests__/auth.e2e.test.js +158 -0
  3. package/dist/__tests__/auth.e2e.test.js.map +1 -0
  4. package/dist/__tests__/deploy-flags.e2e.test.d.ts +1 -0
  5. package/dist/__tests__/deploy-flags.e2e.test.js +111 -0
  6. package/dist/__tests__/deploy-flags.e2e.test.js.map +1 -0
  7. package/dist/__tests__/deploy.e2e.test.js +51 -150
  8. package/dist/__tests__/deploy.e2e.test.js.map +1 -1
  9. package/dist/__tests__/deployment-inspect.e2e.test.d.ts +1 -0
  10. package/dist/__tests__/deployment-inspect.e2e.test.js +113 -0
  11. package/dist/__tests__/deployment-inspect.e2e.test.js.map +1 -0
  12. package/dist/__tests__/deployment-list.e2e.test.d.ts +1 -0
  13. package/dist/__tests__/deployment-list.e2e.test.js +116 -0
  14. package/dist/__tests__/deployment-list.e2e.test.js.map +1 -0
  15. package/dist/__tests__/deployment-logs.e2e.test.d.ts +1 -0
  16. package/dist/__tests__/deployment-logs.e2e.test.js +255 -0
  17. package/dist/__tests__/deployment-logs.e2e.test.js.map +1 -0
  18. package/dist/__tests__/environment-variable/environment-variable-add.e2e.test.d.ts +1 -0
  19. package/dist/__tests__/environment-variable/environment-variable-add.e2e.test.js +260 -0
  20. package/dist/__tests__/environment-variable/environment-variable-add.e2e.test.js.map +1 -0
  21. package/dist/__tests__/environment-variable/environment-variable-list.e2e.test.d.ts +1 -0
  22. package/dist/__tests__/environment-variable/environment-variable-list.e2e.test.js +140 -0
  23. package/dist/__tests__/environment-variable/environment-variable-list.e2e.test.js.map +1 -0
  24. package/dist/__tests__/environment-variable/environment-variable-remove.e2e.test.d.ts +1 -0
  25. package/dist/__tests__/environment-variable/environment-variable-remove.e2e.test.js +151 -0
  26. package/dist/__tests__/environment-variable/environment-variable-remove.e2e.test.js.map +1 -0
  27. package/dist/__tests__/environment-variable/environment-variable-update.e2e.test.d.ts +1 -0
  28. package/dist/__tests__/environment-variable/environment-variable-update.e2e.test.js +343 -0
  29. package/dist/__tests__/environment-variable/environment-variable-update.e2e.test.js.map +1 -0
  30. package/dist/__tests__/environment-variable/environment-variable-validation.test.d.ts +1 -0
  31. package/dist/__tests__/environment-variable/environment-variable-validation.test.js +20 -0
  32. package/dist/__tests__/environment-variable/environment-variable-validation.test.js.map +1 -0
  33. package/dist/__tests__/fixtures/demo-project/index.js +1 -1
  34. package/dist/__tests__/fixtures/demo-project/index.js.map +1 -1
  35. package/dist/__tests__/git-flags.e2e.test.d.ts +1 -0
  36. package/dist/__tests__/git-flags.e2e.test.js +124 -0
  37. package/dist/__tests__/git-flags.e2e.test.js.map +1 -0
  38. package/dist/__tests__/git.e2e.test.d.ts +1 -0
  39. package/dist/__tests__/git.e2e.test.js +221 -0
  40. package/dist/__tests__/git.e2e.test.js.map +1 -0
  41. package/dist/__tests__/logs.e2e.test.d.ts +1 -0
  42. package/dist/__tests__/logs.e2e.test.js +227 -0
  43. package/dist/__tests__/logs.e2e.test.js.map +1 -0
  44. package/dist/__tests__/mock-server.d.ts +16 -0
  45. package/dist/__tests__/mock-server.js +440 -25
  46. package/dist/__tests__/mock-server.js.map +1 -1
  47. package/dist/__tests__/publish.e2e.test.d.ts +1 -0
  48. package/dist/__tests__/publish.e2e.test.js +505 -0
  49. package/dist/__tests__/publish.e2e.test.js.map +1 -0
  50. package/dist/__tests__/tunnel.e2e.test.d.ts +1 -0
  51. package/dist/__tests__/tunnel.e2e.test.js +64 -0
  52. package/dist/__tests__/tunnel.e2e.test.js.map +1 -0
  53. package/dist/__tests__/utils.d.ts +44 -1
  54. package/dist/__tests__/utils.js +195 -11
  55. package/dist/__tests__/utils.js.map +1 -1
  56. package/dist/api.d.ts +1 -2
  57. package/dist/api.js +6 -10
  58. package/dist/api.js.map +1 -1
  59. package/dist/commands/deploy.d.ts +7 -4
  60. package/dist/commands/deploy.js +47 -28
  61. package/dist/commands/deploy.js.map +1 -1
  62. package/dist/commands/deployment/inspect.d.ts +11 -0
  63. package/dist/commands/deployment/inspect.js +91 -0
  64. package/dist/commands/deployment/inspect.js.map +1 -0
  65. package/dist/commands/deployment/list.d.ts +11 -0
  66. package/dist/commands/deployment/list.js +97 -0
  67. package/dist/commands/deployment/list.js.map +1 -0
  68. package/dist/commands/deployment/logs.d.ts +12 -0
  69. package/dist/commands/deployment/logs.js +50 -0
  70. package/dist/commands/deployment/logs.js.map +1 -0
  71. package/dist/commands/environment-variable/add.d.ts +14 -0
  72. package/dist/commands/environment-variable/add.js +46 -0
  73. package/dist/commands/environment-variable/add.js.map +1 -0
  74. package/dist/commands/environment-variable/list.d.ts +9 -0
  75. package/dist/commands/environment-variable/list.js +44 -0
  76. package/dist/commands/environment-variable/list.js.map +1 -0
  77. package/dist/commands/environment-variable/remove.d.ts +11 -0
  78. package/dist/commands/environment-variable/remove.js +32 -0
  79. package/dist/commands/environment-variable/remove.js.map +1 -0
  80. package/dist/commands/environment-variable/update.d.ts +13 -0
  81. package/dist/commands/environment-variable/update.js +40 -0
  82. package/dist/commands/environment-variable/update.js.map +1 -0
  83. package/dist/commands/git/connect.d.ts +10 -0
  84. package/dist/commands/git/connect.js +60 -0
  85. package/dist/commands/git/connect.js.map +1 -0
  86. package/dist/commands/git/disconnect.d.ts +9 -0
  87. package/dist/commands/git/disconnect.js +45 -0
  88. package/dist/commands/git/disconnect.js.map +1 -0
  89. package/dist/commands/git.d.ts +6 -0
  90. package/dist/commands/git.js +17 -0
  91. package/dist/commands/git.js.map +1 -0
  92. package/dist/commands/login.d.ts +6 -0
  93. package/dist/commands/login.js +34 -0
  94. package/dist/commands/login.js.map +1 -0
  95. package/dist/commands/logout.d.ts +6 -0
  96. package/dist/commands/logout.js +20 -0
  97. package/dist/commands/logout.js.map +1 -0
  98. package/dist/commands/logs.d.ts +16 -0
  99. package/dist/commands/logs.js +96 -0
  100. package/dist/commands/logs.js.map +1 -0
  101. package/dist/commands/publish.d.ts +15 -0
  102. package/dist/commands/publish.js +51 -0
  103. package/dist/commands/publish.js.map +1 -0
  104. package/dist/commands/tunnel.d.ts +9 -0
  105. package/dist/commands/tunnel.js +53 -0
  106. package/dist/commands/tunnel.js.map +1 -0
  107. package/dist/commands/whoami.d.ts +6 -0
  108. package/dist/commands/whoami.js +13 -0
  109. package/dist/commands/whoami.js.map +1 -0
  110. package/dist/env.d.ts +4 -0
  111. package/dist/env.js +10 -0
  112. package/dist/env.js.map +1 -0
  113. package/dist/lib/alpic-command.d.ts +6 -0
  114. package/dist/lib/alpic-command.js +27 -0
  115. package/dist/lib/alpic-command.js.map +1 -0
  116. package/dist/lib/archive.d.ts +3 -3
  117. package/dist/lib/archive.js +11 -15
  118. package/dist/lib/archive.js.map +1 -1
  119. package/dist/lib/auth/auth.d.ts +2 -0
  120. package/dist/lib/auth/auth.js +21 -0
  121. package/dist/lib/auth/auth.js.map +1 -0
  122. package/dist/lib/auth/oauth/client.d.ts +28 -0
  123. package/dist/lib/auth/oauth/client.js +110 -0
  124. package/dist/lib/auth/oauth/client.js.map +1 -0
  125. package/dist/lib/auth/oauth/constants.d.ts +2 -0
  126. package/dist/lib/auth/oauth/constants.js +3 -0
  127. package/dist/lib/auth/oauth/constants.js.map +1 -0
  128. package/dist/lib/auth/oauth/server/assets/alpic-mountain.png +0 -0
  129. package/dist/lib/auth/oauth/server/assets/authorize.html +195 -0
  130. package/dist/lib/auth/oauth/server/assets/callback.html +88 -0
  131. package/dist/lib/auth/oauth/server/index.d.ts +8 -0
  132. package/dist/lib/auth/oauth/server/index.js +105 -0
  133. package/dist/lib/auth/oauth/server/index.js.map +1 -0
  134. package/dist/lib/auth/whoami.d.ts +1 -0
  135. package/dist/lib/auth/whoami.js +41 -0
  136. package/dist/lib/auth/whoami.js.map +1 -0
  137. package/dist/lib/base-workflow.d.ts +10 -0
  138. package/dist/lib/base-workflow.js +22 -0
  139. package/dist/lib/base-workflow.js.map +1 -0
  140. package/dist/lib/config.d.ts +2 -2
  141. package/dist/lib/config.js +7 -7
  142. package/dist/lib/config.js.map +1 -1
  143. package/dist/lib/deployment.d.ts +70 -3
  144. package/dist/lib/deployment.js +121 -10
  145. package/dist/lib/deployment.js.map +1 -1
  146. package/dist/lib/environment-variable.d.ts +41 -0
  147. package/dist/lib/environment-variable.js +311 -0
  148. package/dist/lib/environment-variable.js.map +1 -0
  149. package/dist/lib/git.d.ts +22 -0
  150. package/dist/lib/git.js +131 -0
  151. package/dist/lib/git.js.map +1 -0
  152. package/dist/lib/global-store.d.ts +28 -0
  153. package/dist/lib/global-store.js +76 -0
  154. package/dist/lib/global-store.js.map +1 -0
  155. package/dist/lib/logs.d.ts +20 -0
  156. package/dist/lib/logs.js +86 -0
  157. package/dist/lib/logs.js.map +1 -0
  158. package/dist/lib/project.d.ts +68 -61
  159. package/dist/lib/project.js +275 -250
  160. package/dist/lib/project.js.map +1 -1
  161. package/dist/lib/publish.d.ts +22 -0
  162. package/dist/lib/publish.js +188 -0
  163. package/dist/lib/publish.js.map +1 -0
  164. package/dist/lib/table.d.ts +8 -0
  165. package/dist/lib/table.js +27 -0
  166. package/dist/lib/table.js.map +1 -0
  167. package/dist/lib/telemetry.js +7 -7
  168. package/dist/lib/telemetry.js.map +1 -1
  169. package/dist/lib/utils.d.ts +4 -0
  170. package/dist/lib/utils.js +45 -0
  171. package/dist/lib/utils.js.map +1 -0
  172. package/dist/lib/utils.test.d.ts +1 -0
  173. package/dist/lib/utils.test.js +27 -0
  174. package/dist/lib/utils.test.js.map +1 -0
  175. package/package.json +35 -31
  176. package/dist/lib/global-config.d.ts +0 -9
  177. package/dist/lib/global-config.js +0 -48
  178. package/dist/lib/global-config.js.map +0 -1
@@ -1,8 +1,23 @@
1
- import { OpenAPIHandler } from "@orpc/openapi/node";
2
- import { ORPCError, implement } from "@orpc/server";
3
- import express, {} from "express";
4
1
  import { randomUUID } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import * as https from "node:https";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
5
6
  import { contract } from "@alpic-ai/api";
7
+ import { OpenAPIHandler } from "@orpc/openapi/node";
8
+ import { implement, ORPCError } from "@orpc/server";
9
+ import express, {} from "express";
10
+ import { inc as semverInc } from "semver";
11
+ const __testDirname = dirname(fileURLToPath(import.meta.url));
12
+ const FIXTURE_CERT_PATH = join(__testDirname, "fixtures", "certs", "localhost-cert.pem");
13
+ const FIXTURE_KEY_PATH = join(__testDirname, "fixtures", "certs", "localhost-key.pem");
14
+ function getTlsAssets() {
15
+ return {
16
+ key: readFileSync(FIXTURE_KEY_PATH, "utf8"),
17
+ cert: readFileSync(FIXTURE_CERT_PATH, "utf8"),
18
+ certPath: FIXTURE_CERT_PATH,
19
+ };
20
+ }
6
21
  export class MockApiServer {
7
22
  server = null;
8
23
  app;
@@ -10,12 +25,17 @@ export class MockApiServer {
10
25
  mockData;
11
26
  callHistory = [];
12
27
  constructor() {
13
- this.port;
14
28
  this.app = express();
15
29
  this.mockData = {
16
30
  projects: new Map(),
17
31
  environments: new Map(),
18
32
  deployments: new Map(),
33
+ environmentVariables: new Map(),
34
+ runtimeLogs: new Map(),
35
+ runtimeLogPages: new Map(),
36
+ deploymentLogPages: new Map(),
37
+ deploymentLogCallCounts: new Map(),
38
+ publishedServers: new Map(),
19
39
  };
20
40
  }
21
41
  async start() {
@@ -23,11 +43,17 @@ export class MockApiServer {
23
43
  throw new Error("Server is already running");
24
44
  }
25
45
  this.configureServer();
46
+ const tlsAssets = getTlsAssets();
47
+ process.env.NODE_EXTRA_CA_CERTS = tlsAssets.certPath;
48
+ const httpsOptions = { key: tlsAssets.key, cert: tlsAssets.cert };
26
49
  return new Promise((resolve) => {
27
- this.server = this.app.listen(0, "127.0.0.1", () => {
50
+ this.server = https.createServer(httpsOptions, this.app).listen(0, "127.0.0.1", () => {
51
+ if (!this.server) {
52
+ throw new Error("Server is not running");
53
+ }
28
54
  const address = this.server.address();
29
55
  this.port = address.port;
30
- const url = `http://127.0.0.1:${this.port}`;
56
+ const url = `https://127.0.0.1:${this.port}`;
31
57
  resolve(url);
32
58
  });
33
59
  });
@@ -45,7 +71,99 @@ export class MockApiServer {
45
71
  res.status(200).end();
46
72
  });
47
73
  this.app.use(express.json());
48
- const router = {
74
+ this.app.get("/.well-known/oauth-protected-resource", (_req, res) => {
75
+ const base = `https://127.0.0.1:${this.port}`;
76
+ res.status(200).json({
77
+ resource: "https://api.alpic.ai",
78
+ authorization_servers: [base],
79
+ bearer_methods_supported: ["header"],
80
+ scopes_supported: ["openid", "email", "profile"],
81
+ resource_documentation: "https://docs.alpic.ai",
82
+ });
83
+ });
84
+ this.app.get("/.well-known/openid-configuration", (_req, res) => {
85
+ const base = `https://127.0.0.1:${this.port}`;
86
+ res.status(200).json({
87
+ issuer: base,
88
+ userinfo_endpoint: `${base}/oauth2/userInfo`,
89
+ authorization_endpoint: `${base}/oauth2/authorize`,
90
+ token_endpoint: `${base}/oauth2/token`,
91
+ jwks_uri: `${base}/.well-known/jwks.json`,
92
+ });
93
+ });
94
+ this.app.get("/oauth2/userInfo", (req, res) => {
95
+ const auth = req.headers.authorization;
96
+ if (!auth?.startsWith("Bearer ")) {
97
+ res.status(401).json({ error: "Missing or invalid Authorization" });
98
+ return;
99
+ }
100
+ res.status(200).json({
101
+ sub: "test-sub",
102
+ email_verified: "true",
103
+ identities: [],
104
+ name: "Test User",
105
+ email: "test@example.com",
106
+ picture: "",
107
+ username: "testuser",
108
+ });
109
+ });
110
+ const router = implement(contract).router({
111
+ tunnels: {
112
+ getTicket: {
113
+ v1: implement(contract.tunnels.getTicket.v1).handler(async () => {
114
+ return {
115
+ subdomain: "cool-mongoose-123",
116
+ ticket: "mock-ticket",
117
+ tunnelHost: "tunnel.alpic.dev",
118
+ };
119
+ }),
120
+ },
121
+ },
122
+ teams: {
123
+ list: {
124
+ v1: implement(contract.teams.list.v1).handler(async () => {
125
+ return [
126
+ {
127
+ id: "mock-team-id",
128
+ name: "Mock Team",
129
+ createdAt: new Date(),
130
+ hasStripeAccount: false,
131
+ },
132
+ ];
133
+ }),
134
+ },
135
+ },
136
+ analytics: {
137
+ get: {
138
+ v1: implement(contract.analytics.get.v1).handler(async ({ input }) => {
139
+ const project = this.mockData.projects.get(input.projectId);
140
+ if (!project) {
141
+ throw new ORPCError("NOT_FOUND", {
142
+ message: "Project not found",
143
+ });
144
+ }
145
+ const emptyTimeSeries = {
146
+ sessions_count: [],
147
+ requests_count: [],
148
+ requests_latency_mean: [],
149
+ tool_errors: [],
150
+ mcp_errors: [],
151
+ output_token_mean: [],
152
+ task_count: [],
153
+ };
154
+ return {
155
+ metadata: {
156
+ startTimestamp: input.startTimestamp,
157
+ endTimestamp: input.endTimestamp,
158
+ timeZone: input.timeZone,
159
+ startDate: new Date(input.startTimestamp),
160
+ interval: "1h",
161
+ },
162
+ timeSeries: emptyTimeSeries,
163
+ };
164
+ }),
165
+ },
166
+ },
49
167
  projects: {
50
168
  list: {
51
169
  v1: implement(contract.projects.list.v1).handler(async () => {
@@ -71,7 +189,9 @@ export class MockApiServer {
71
189
  v1: implement(contract.projects.get.v1).handler(async ({ input }) => {
72
190
  const project = this.mockData.projects.get(input.projectId);
73
191
  if (!project) {
74
- throw new ORPCError("NOT_FOUND", { message: "Project not found" });
192
+ throw new ORPCError("NOT_FOUND", {
193
+ message: "Project not found",
194
+ });
75
195
  }
76
196
  return project;
77
197
  }),
@@ -117,6 +237,7 @@ export class MockApiServer {
117
237
  id: productionEnvId,
118
238
  name: productionEnvName,
119
239
  mcpServerUrl: `https://mcp-${projectId}.alpic.ai`,
240
+ domains: [],
120
241
  latestDeployment: null,
121
242
  },
122
243
  environments: [
@@ -135,27 +256,129 @@ export class MockApiServer {
135
256
  return project;
136
257
  }),
137
258
  },
259
+ update: {
260
+ v1: implement(contract.projects.update.v1).handler(async ({ input }) => {
261
+ const project = this.mockData.projects.get(input.projectId);
262
+ if (!project) {
263
+ throw new ORPCError("NOT_FOUND", {
264
+ message: "Project not found",
265
+ });
266
+ }
267
+ if (input.sourceRepository !== undefined) {
268
+ project.sourceRepository = input.sourceRepository;
269
+ }
270
+ this.mockData.projects.set(input.projectId, project);
271
+ return project;
272
+ }),
273
+ },
274
+ delete: {
275
+ v1: implement(contract.projects.delete.v1).handler(async ({ input }) => {
276
+ const project = this.mockData.projects.get(input.projectId);
277
+ if (!project) {
278
+ throw new ORPCError("NOT_FOUND", {
279
+ message: "Project not found",
280
+ });
281
+ }
282
+ this.mockData.projects.delete(input.projectId);
283
+ return { success: true };
284
+ }),
285
+ },
138
286
  },
139
287
  environments: {
288
+ create: {
289
+ v1: implement(contract.environments.create.v1).handler(async ({ input }) => {
290
+ const project = this.mockData.projects.get(input.projectId);
291
+ if (!project) {
292
+ throw new ORPCError("NOT_FOUND", {
293
+ message: "Project not found",
294
+ });
295
+ }
296
+ const environmentId = randomUUID();
297
+ const mcpServerUrl = `https://mcp-${environmentId}.alpic.ai`;
298
+ const createdAt = new Date();
299
+ const environment = {
300
+ id: environmentId,
301
+ name: input.name,
302
+ sourceBranch: input.sourceBranch,
303
+ mcpServerUrl,
304
+ domains: [mcpServerUrl],
305
+ createdAt,
306
+ projectId: input.projectId,
307
+ };
308
+ this.mockData.environments.set(environmentId, environment);
309
+ this.mockData.projects.set(input.projectId, {
310
+ ...project,
311
+ environments: [
312
+ ...project.environments,
313
+ {
314
+ id: environmentId,
315
+ name: input.name,
316
+ sourceBranch: input.sourceBranch,
317
+ mcpServerUrl,
318
+ createdAt,
319
+ projectId: input.projectId,
320
+ latestDeployment: null,
321
+ },
322
+ ],
323
+ });
324
+ return {
325
+ id: environmentId,
326
+ name: input.name,
327
+ sourceBranch: input.sourceBranch,
328
+ createdAt,
329
+ projectId: input.projectId,
330
+ urls: [mcpServerUrl],
331
+ };
332
+ }),
333
+ },
140
334
  get: {
141
335
  v1: implement(contract.environments.get.v1).handler(async ({ input }) => {
142
336
  const environment = this.mockData.environments.get(input.environmentId);
143
337
  if (!environment) {
144
- throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
338
+ throw new ORPCError("NOT_FOUND", {
339
+ message: "Environment not found",
340
+ });
145
341
  }
146
342
  return environment;
147
343
  }),
148
344
  },
345
+ getLogs: {
346
+ v1: implement(contract.environments.getLogs.v1).handler(async ({ input }) => {
347
+ const environment = this.mockData.environments.get(input.environmentId);
348
+ if (!environment) {
349
+ throw new ORPCError("NOT_FOUND", {
350
+ message: "Environment not found",
351
+ });
352
+ }
353
+ const pagedLogs = this.mockData.runtimeLogPages.get(input.environmentId);
354
+ const pageIndex = input.nextToken === undefined ? 0 : Number.parseInt(input.nextToken, 10);
355
+ const allLogs = pagedLogs === undefined
356
+ ? (this.mockData.runtimeLogs.get(input.environmentId) ?? [])
357
+ : (pagedLogs[pageIndex] ?? []);
358
+ let logs = allLogs;
359
+ if (input.level !== undefined) {
360
+ const levels = input.level;
361
+ logs = allLogs.filter((log) => levels.includes(log.type));
362
+ }
363
+ const nextToken = pagedLogs === undefined || pageIndex + 1 >= pagedLogs.length ? null : String(pageIndex + 1);
364
+ return { logs, nextToken };
365
+ }),
366
+ },
149
367
  deploy: {
150
368
  v1: implement(contract.environments.deploy.v1).handler(async ({ input }) => {
151
369
  const environment = this.mockData.environments.get(input.environmentId);
152
370
  if (!environment) {
153
- throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
371
+ throw new ORPCError("NOT_FOUND", {
372
+ message: "Environment not found",
373
+ });
154
374
  }
155
375
  const deploymentId = randomUUID();
156
376
  const deployment = {
157
377
  id: deploymentId,
158
378
  status: "ongoing",
379
+ environmentId: environment.id,
380
+ environmentName: environment.name,
381
+ isCurrent: false,
159
382
  sourceRef: environment.sourceBranch,
160
383
  sourceCommitId: randomUUID().slice(0, 7),
161
384
  sourceCommitMessage: "Mock deployment",
@@ -163,38 +386,197 @@ export class MockApiServer {
163
386
  authorAvatarUrl: null,
164
387
  startedAt: new Date(),
165
388
  completedAt: null,
389
+ deploymentPageUrl: "https://app.alpic.ai/deployments/mock",
166
390
  };
167
391
  this.mockData.deployments.set(deploymentId, deployment);
168
392
  return deployment;
169
393
  }),
170
394
  },
171
395
  },
396
+ environmentVariables: {
397
+ list: {
398
+ v1: implement(contract.environmentVariables.list.v1).handler(async ({ input }) => {
399
+ const environment = this.mockData.environments.get(input.environmentId);
400
+ if (!environment) {
401
+ throw new ORPCError("NOT_FOUND", {
402
+ message: "Environment not found",
403
+ });
404
+ }
405
+ return this.mockData.environmentVariables.get(input.environmentId) ?? [];
406
+ }),
407
+ },
408
+ create: {
409
+ v1: implement(contract.environmentVariables.create.v1).handler(async ({ input }) => {
410
+ const environment = this.mockData.environments.get(input.environmentId);
411
+ if (!environment) {
412
+ throw new ORPCError("NOT_FOUND", {
413
+ message: "Environment not found",
414
+ });
415
+ }
416
+ const existing = this.mockData.environmentVariables.get(input.environmentId) ?? [];
417
+ const newVars = input.environmentVariables.map((variable) => ({
418
+ id: randomUUID(),
419
+ key: variable.key,
420
+ value: variable.value,
421
+ isSecret: variable.isSecret,
422
+ createdAt: new Date(),
423
+ }));
424
+ for (const newVar of newVars) {
425
+ if (existing.some((existingVar) => existingVar.key === newVar.key)) {
426
+ throw new ORPCError("BAD_REQUEST", {
427
+ message: `Environment variable "${newVar.key}" already exists`,
428
+ });
429
+ }
430
+ }
431
+ this.mockData.environmentVariables.set(input.environmentId, [...existing, ...newVars]);
432
+ return { success: true };
433
+ }),
434
+ },
435
+ update: {
436
+ v1: implement(contract.environmentVariables.update.v1).handler(async ({ input }) => {
437
+ for (const [environmentId, variables] of this.mockData.environmentVariables.entries()) {
438
+ const index = variables.findIndex((variable) => variable.id === input.environmentVariableId);
439
+ if (index !== -1) {
440
+ const updated = variables.map((variable, i) => i === index
441
+ ? {
442
+ ...variable,
443
+ key: input.key,
444
+ value: input.value ?? variable.value,
445
+ isSecret: input.isSecret,
446
+ }
447
+ : variable);
448
+ this.mockData.environmentVariables.set(environmentId, updated);
449
+ return { success: true };
450
+ }
451
+ }
452
+ throw new ORPCError("NOT_FOUND", {
453
+ message: "Environment variable not found",
454
+ });
455
+ }),
456
+ },
457
+ delete: {
458
+ v1: implement(contract.environmentVariables.delete.v1).handler(async ({ input }) => {
459
+ for (const [environmentId, variables] of this.mockData.environmentVariables.entries()) {
460
+ const index = variables.findIndex((variable) => variable.id === input.environmentVariableId);
461
+ if (index !== -1) {
462
+ this.mockData.environmentVariables.set(environmentId, variables.filter((_, i) => i !== index));
463
+ return { success: true };
464
+ }
465
+ }
466
+ throw new ORPCError("NOT_FOUND", {
467
+ message: "Environment variable not found",
468
+ });
469
+ }),
470
+ },
471
+ },
472
+ distribution: {
473
+ info: {
474
+ v1: implement(contract.distribution.info.v1).handler(async ({ input }) => {
475
+ const existing = this.mockData.publishedServers.get(`${input.projectId}:${input.domain}`);
476
+ return {
477
+ serverFields: existing ?? {
478
+ $schema: "https://registry.modelcontextprotocol.io/schemas/1.0/server.json",
479
+ name: "",
480
+ description: "",
481
+ },
482
+ };
483
+ }),
484
+ },
485
+ publish: {
486
+ v1: implement(contract.distribution.publish.v1).handler(async ({ input }) => {
487
+ const project = this.mockData.projects.get(input.projectId);
488
+ if (!project) {
489
+ throw new ORPCError("NOT_FOUND", {
490
+ message: "Project not found",
491
+ });
492
+ }
493
+ const key = `${input.projectId}:${input.domain}`;
494
+ const existing = this.mockData.publishedServers.get(key);
495
+ const version = existing?.version ? (semverInc(existing.version, "patch") ?? "0.0.1") : "0.0.1";
496
+ const serverFields = {
497
+ $schema: "https://registry.modelcontextprotocol.io/schemas/1.0/server.json",
498
+ name: input.domain,
499
+ description: input.description,
500
+ title: input.title,
501
+ version,
502
+ ...(input.websiteUrl ? { websiteUrl: input.websiteUrl } : {}),
503
+ ...(input.iconSrc ? { icons: [{ src: input.iconSrc }] } : {}),
504
+ };
505
+ if (!input.dryRun) {
506
+ this.mockData.publishedServers.set(key, serverFields);
507
+ }
508
+ return { serverFields };
509
+ }),
510
+ },
511
+ },
172
512
  deployments: {
513
+ list: {
514
+ v1: implement(contract.deployments.list.v1).handler(async ({ input }) => {
515
+ const project = this.mockData.projects.get(input.projectId);
516
+ if (!project) {
517
+ throw new ORPCError("NOT_FOUND", {
518
+ message: "Project not found",
519
+ });
520
+ }
521
+ let deployments = Array.from(this.mockData.deployments.values());
522
+ if (input.environmentId) {
523
+ deployments = deployments.filter((deployment) => deployment.environmentId === input.environmentId);
524
+ }
525
+ if (input.status?.length) {
526
+ deployments = deployments.filter((deployment) => input.status.includes(deployment.status));
527
+ }
528
+ return deployments.map((deployment) => ({
529
+ ...deployment,
530
+ isCurrent: deployment.status === "deployed",
531
+ }));
532
+ }),
533
+ },
173
534
  get: {
174
535
  v1: implement(contract.deployments.get.v1).handler(async ({ input }) => {
175
536
  const deployment = this.mockData.deployments.get(input.deploymentId);
176
537
  if (!deployment) {
177
- throw new ORPCError("NOT_FOUND", { message: "Deployment not found" });
538
+ throw new ORPCError("NOT_FOUND", {
539
+ message: "Deployment not found",
540
+ });
178
541
  }
179
- if (deployment.status === "ongoing" && deployment.startedAt) {
180
- const elapsed = Date.now() - deployment.startedAt.getTime();
181
- if (elapsed > 2000) {
182
- const updatedDeployment = {
183
- ...deployment,
184
- status: "deployed",
185
- completedAt: new Date(),
186
- };
187
- this.mockData.deployments.set(input.deploymentId, updatedDeployment);
188
- return updatedDeployment;
189
- }
542
+ if (deployment.status === "ongoing") {
543
+ const updatedDeployment = {
544
+ ...deployment,
545
+ status: "deployed",
546
+ completedAt: new Date(),
547
+ deploymentPageUrl: deployment.deploymentPageUrl ?? null,
548
+ };
549
+ this.mockData.deployments.set(input.deploymentId, updatedDeployment);
550
+ return updatedDeployment;
190
551
  }
191
552
  return deployment;
192
553
  }),
193
554
  },
555
+ getLogs: {
556
+ v1: implement(contract.deployments.getLogs.v1).handler(async ({ input }) => {
557
+ const deployment = this.mockData.deployments.get(input.deploymentId);
558
+ if (!deployment) {
559
+ throw new ORPCError("NOT_FOUND", {
560
+ message: "Deployment not found",
561
+ });
562
+ }
563
+ const pages = this.mockData.deploymentLogPages.get(input.deploymentId);
564
+ if (pages !== undefined) {
565
+ const callCount = this.mockData.deploymentLogCallCounts.get(input.deploymentId) ?? 0;
566
+ const pageIndex = Math.min(callCount, pages.length - 1);
567
+ this.mockData.deploymentLogCallCounts.set(input.deploymentId, callCount + 1);
568
+ return pages[pageIndex] ?? { logs: [], hasMoreLogs: false };
569
+ }
570
+ return {
571
+ logs: [{ timestamp: new Date(), content: "Mock log entry" }],
572
+ hasMoreLogs: false,
573
+ };
574
+ }),
575
+ },
194
576
  uploadArtifact: {
195
577
  v1: implement(contract.deployments.uploadArtifact.v1).handler(async () => {
196
578
  const token = randomUUID();
197
- const baseUrl = `http://127.0.0.1:${this.port}`;
579
+ const baseUrl = `https://127.0.0.1:${this.port}`;
198
580
  const uploadUrl = `${baseUrl}/__mock__upload/${token}`;
199
581
  return {
200
582
  uploadUrl,
@@ -204,7 +586,7 @@ export class MockApiServer {
204
586
  }),
205
587
  },
206
588
  },
207
- };
589
+ });
208
590
  const handler = new OpenAPIHandler(router);
209
591
  this.app.use(async (req, res) => {
210
592
  const apiCall = {
@@ -212,7 +594,12 @@ export class MockApiServer {
212
594
  path: req.path,
213
595
  timestamp: new Date(),
214
596
  };
215
- if (req.body) {
597
+ if (req.method === "GET") {
598
+ if (Object.keys(req.query).length > 0) {
599
+ apiCall.input = req.query;
600
+ }
601
+ }
602
+ else if (req.body) {
216
603
  apiCall.input = req.body;
217
604
  }
218
605
  const result = await handler.handle(req, res, {
@@ -266,6 +653,8 @@ export class MockApiServer {
266
653
  getLastCall(method, pathPattern) {
267
654
  for (let i = this.callHistory.length - 1; i >= 0; i--) {
268
655
  const call = this.callHistory[i];
656
+ if (!call)
657
+ continue;
269
658
  if (method && call.method !== method)
270
659
  continue;
271
660
  if (pathPattern) {
@@ -282,6 +671,32 @@ export class MockApiServer {
282
671
  }
283
672
  return undefined;
284
673
  }
674
+ addEnvironmentVariables(environmentId, variables) {
675
+ this.mockData.environmentVariables.set(environmentId, variables);
676
+ }
677
+ setRuntimeLogs(environmentId, logs) {
678
+ this.mockData.runtimeLogs.set(environmentId, logs);
679
+ this.mockData.runtimeLogPages.delete(environmentId);
680
+ }
681
+ setRuntimeLogPages(environmentId, pages) {
682
+ this.mockData.runtimeLogPages.set(environmentId, pages);
683
+ this.mockData.runtimeLogs.delete(environmentId);
684
+ }
685
+ setPublishedServer(projectId, domain, server) {
686
+ this.mockData.publishedServers.set(`${projectId}:${domain}`, server);
687
+ }
688
+ addDeployments(deployments) {
689
+ for (const deployment of deployments) {
690
+ this.mockData.deployments.set(deployment.id, {
691
+ ...deployment,
692
+ deploymentPageUrl: null,
693
+ });
694
+ }
695
+ }
696
+ setDeploymentLogPages(deploymentId, pages) {
697
+ this.mockData.deploymentLogPages.set(deploymentId, pages);
698
+ this.mockData.deploymentLogCallCounts.set(deploymentId, 0);
699
+ }
285
700
  addProject(project) {
286
701
  this.mockData.projects.set(project.id, project);
287
702
  if (project.productionEnvironment) {