alpic 0.0.0-dev.97d4256 → 0.0.0-dev.97ff947

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 (75) hide show
  1. package/dist/__tests__/deploy.e2e.test.d.ts +1 -0
  2. package/dist/__tests__/deploy.e2e.test.js +250 -0
  3. package/dist/__tests__/deploy.e2e.test.js.map +1 -0
  4. package/dist/__tests__/fixtures/demo-project/index.d.ts +1 -0
  5. package/dist/__tests__/fixtures/demo-project/index.js +4 -0
  6. package/dist/__tests__/fixtures/demo-project/index.js.map +1 -0
  7. package/dist/__tests__/git.e2e.test.d.ts +1 -0
  8. package/dist/__tests__/git.e2e.test.js +250 -0
  9. package/dist/__tests__/git.e2e.test.js.map +1 -0
  10. package/dist/__tests__/mock-server.d.ts +22 -0
  11. package/dist/__tests__/mock-server.js +367 -0
  12. package/dist/__tests__/mock-server.js.map +1 -0
  13. package/dist/__tests__/utils.d.ts +29 -0
  14. package/dist/__tests__/utils.js +89 -0
  15. package/dist/__tests__/utils.js.map +1 -0
  16. package/dist/api.d.ts +4 -0
  17. package/dist/api.js +19 -0
  18. package/dist/api.js.map +1 -0
  19. package/dist/commands/deploy.d.ts +9 -0
  20. package/dist/commands/deploy.js +85 -0
  21. package/dist/commands/deploy.js.map +1 -0
  22. package/dist/commands/git/connect.d.ts +9 -0
  23. package/dist/commands/git/connect.js +104 -0
  24. package/dist/commands/git/connect.js.map +1 -0
  25. package/dist/commands/git/disconnect.d.ts +9 -0
  26. package/dist/commands/git/disconnect.js +65 -0
  27. package/dist/commands/git/disconnect.js.map +1 -0
  28. package/dist/commands/{hello.d.ts → git.d.ts} +1 -1
  29. package/dist/commands/git.js +17 -0
  30. package/dist/commands/git.js.map +1 -0
  31. package/dist/commands/telemetry/disable.d.ts +5 -0
  32. package/dist/commands/telemetry/disable.js +14 -0
  33. package/dist/commands/telemetry/disable.js.map +1 -0
  34. package/dist/commands/telemetry/enable.d.ts +5 -0
  35. package/dist/commands/telemetry/enable.js +13 -0
  36. package/dist/commands/telemetry/enable.js.map +1 -0
  37. package/dist/commands/telemetry/status.d.ts +5 -0
  38. package/dist/commands/telemetry/status.js +19 -0
  39. package/dist/commands/telemetry/status.js.map +1 -0
  40. package/dist/lib/archive.d.ts +7 -0
  41. package/dist/lib/archive.js +55 -0
  42. package/dist/lib/archive.js.map +1 -0
  43. package/dist/lib/auth.d.ts +1 -0
  44. package/dist/lib/auth.js +10 -0
  45. package/dist/lib/auth.js.map +1 -0
  46. package/dist/lib/config.d.ts +11 -0
  47. package/dist/lib/config.js +31 -0
  48. package/dist/lib/config.js.map +1 -0
  49. package/dist/lib/deployment.d.ts +21 -0
  50. package/dist/lib/deployment.js +38 -0
  51. package/dist/lib/deployment.js.map +1 -0
  52. package/dist/lib/git.d.ts +8 -0
  53. package/dist/lib/git.js +69 -0
  54. package/dist/lib/git.js.map +1 -0
  55. package/dist/lib/global-config.d.ts +9 -0
  56. package/dist/lib/global-config.js +48 -0
  57. package/dist/lib/global-config.js.map +1 -0
  58. package/dist/lib/project.d.ts +67 -0
  59. package/dist/lib/project.js +285 -0
  60. package/dist/lib/project.js.map +1 -0
  61. package/dist/lib/telemetry.d.ts +7 -0
  62. package/dist/lib/telemetry.js +66 -0
  63. package/dist/lib/telemetry.js.map +1 -0
  64. package/dist/lib/upload.d.ts +1 -0
  65. package/dist/lib/upload.js +14 -0
  66. package/dist/lib/upload.js.map +1 -0
  67. package/dist/posthog.d.ts +3 -0
  68. package/dist/posthog.js +10 -0
  69. package/dist/posthog.js.map +1 -0
  70. package/dist/types.d.ts +7 -0
  71. package/dist/types.js +2 -0
  72. package/dist/types.js.map +1 -0
  73. package/package.json +25 -5
  74. package/dist/commands/hello.js +0 -10
  75. package/dist/commands/hello.js.map +0 -1
@@ -0,0 +1,367 @@
1
+ import { OpenAPIHandler } from "@orpc/openapi/node";
2
+ import { ORPCError, implement } from "@orpc/server";
3
+ import express, {} from "express";
4
+ import { randomUUID } from "node:crypto";
5
+ import { contract } from "@alpic-ai/api";
6
+ export class MockApiServer {
7
+ server = null;
8
+ app;
9
+ port = null;
10
+ mockData;
11
+ callHistory = [];
12
+ constructor() {
13
+ this.app = express();
14
+ this.mockData = {
15
+ projects: new Map(),
16
+ environments: new Map(),
17
+ deployments: new Map(),
18
+ };
19
+ }
20
+ async start() {
21
+ if (this.server) {
22
+ throw new Error("Server is already running");
23
+ }
24
+ this.configureServer();
25
+ return new Promise((resolve) => {
26
+ this.server = this.app.listen(0, "127.0.0.1", () => {
27
+ const address = this.server.address();
28
+ this.port = address.port;
29
+ const url = `http://127.0.0.1:${this.port}`;
30
+ resolve(url);
31
+ });
32
+ });
33
+ }
34
+ configureServer() {
35
+ this.app.put("/__mock__upload/:token", express.raw({ type: "*/*", limit: "50mb" }), async (req, res) => {
36
+ const apiCall = {
37
+ method: req.method,
38
+ path: req.path,
39
+ timestamp: new Date(),
40
+ input: { token: req.params.token, size: req.body.length },
41
+ responseStatus: 200,
42
+ };
43
+ this.callHistory.push(apiCall);
44
+ res.status(200).end();
45
+ });
46
+ this.app.use(express.json());
47
+ const router = implement(contract).router({
48
+ projects: {
49
+ list: {
50
+ v1: implement(contract.projects.list.v1).handler(async () => {
51
+ return Array.from(this.mockData.projects.values()).map((project) => ({
52
+ id: project.id,
53
+ name: project.name,
54
+ teamId: project.teamId,
55
+ environments: project.environments,
56
+ productionEnvironment: project.productionEnvironment,
57
+ sourceRepository: project.sourceRepository,
58
+ runtime: project.runtime,
59
+ transport: project.transport,
60
+ rootDirectory: project.rootDirectory,
61
+ buildCommand: project.buildCommand,
62
+ buildOutputDir: project.buildOutputDir,
63
+ installCommand: project.installCommand,
64
+ startCommand: project.startCommand,
65
+ createdAt: project.createdAt,
66
+ }));
67
+ }),
68
+ },
69
+ get: {
70
+ v1: implement(contract.projects.get.v1).handler(async ({ input }) => {
71
+ const project = this.mockData.projects.get(input.projectId);
72
+ if (!project) {
73
+ throw new ORPCError("NOT_FOUND", { message: "Project not found" });
74
+ }
75
+ return project;
76
+ }),
77
+ },
78
+ create: {
79
+ v1: implement(contract.projects.create.v1).handler(async ({ input }) => {
80
+ const projectId = randomUUID();
81
+ const teamId = "mock-team-id";
82
+ const productionEnvId = randomUUID();
83
+ const productionEnvName = "production";
84
+ const project = {
85
+ id: projectId,
86
+ name: input.name,
87
+ teamId,
88
+ environments: [],
89
+ productionEnvironment: {
90
+ id: productionEnvId,
91
+ name: productionEnvName,
92
+ },
93
+ sourceRepository: input.sourceRepository ?? null,
94
+ runtime: input.runtime,
95
+ transport: input.transport ?? null,
96
+ rootDirectory: input.rootDirectory ?? null,
97
+ buildCommand: input.settings?.buildCommand ?? null,
98
+ buildOutputDir: input.settings?.buildOutputDir ?? null,
99
+ installCommand: input.settings?.installCommand ?? null,
100
+ startCommand: input.settings?.startCommand ?? null,
101
+ createdAt: new Date(),
102
+ };
103
+ const environment = {
104
+ id: productionEnvId,
105
+ name: productionEnvName,
106
+ sourceBranch: null,
107
+ mcpServerUrl: `https://mcp-${projectId}.alpic.ai`,
108
+ domains: [`https://mcp-${projectId}.alpic.ai`],
109
+ createdAt: new Date(),
110
+ projectId,
111
+ };
112
+ this.mockData.environments.set(productionEnvId, environment);
113
+ const fullProject = {
114
+ ...project,
115
+ productionEnvironment: {
116
+ id: productionEnvId,
117
+ name: productionEnvName,
118
+ mcpServerUrl: `https://mcp-${projectId}.alpic.ai`,
119
+ latestDeployment: null,
120
+ },
121
+ environments: [
122
+ {
123
+ id: productionEnvId,
124
+ name: productionEnvName,
125
+ sourceBranch: null,
126
+ mcpServerUrl: `https://mcp-${projectId}.alpic.ai`,
127
+ createdAt: new Date(),
128
+ projectId,
129
+ latestDeployment: null,
130
+ },
131
+ ],
132
+ };
133
+ this.mockData.projects.set(projectId, fullProject);
134
+ return project;
135
+ }),
136
+ },
137
+ update: {
138
+ v1: implement(contract.projects.update.v1).handler(async ({ input }) => {
139
+ const project = this.mockData.projects.get(input.projectId);
140
+ if (!project) {
141
+ throw new ORPCError("NOT_FOUND", { message: "Project not found" });
142
+ }
143
+ let nextProject = project;
144
+ if (input.sourceRepository !== undefined) {
145
+ nextProject.sourceRepository = input.sourceRepository;
146
+ }
147
+ this.mockData.projects.set(input.projectId, nextProject);
148
+ return nextProject;
149
+ }),
150
+ },
151
+ },
152
+ environments: {
153
+ create: {
154
+ v1: implement(contract.environments.create.v1).handler(async ({ input }) => {
155
+ const project = this.mockData.projects.get(input.projectId);
156
+ if (!project) {
157
+ throw new ORPCError("NOT_FOUND", { message: "Project not found" });
158
+ }
159
+ const environmentId = randomUUID();
160
+ const mcpServerUrl = `https://mcp-${environmentId}.alpic.ai`;
161
+ const createdAt = new Date();
162
+ const environment = {
163
+ id: environmentId,
164
+ name: input.name,
165
+ sourceBranch: input.sourceBranch,
166
+ mcpServerUrl,
167
+ domains: [mcpServerUrl],
168
+ createdAt,
169
+ projectId: input.projectId,
170
+ };
171
+ this.mockData.environments.set(environmentId, environment);
172
+ this.mockData.projects.set(input.projectId, {
173
+ ...project,
174
+ environments: [
175
+ ...project.environments,
176
+ {
177
+ id: environmentId,
178
+ name: input.name,
179
+ sourceBranch: input.sourceBranch,
180
+ mcpServerUrl,
181
+ createdAt,
182
+ projectId: input.projectId,
183
+ latestDeployment: null,
184
+ },
185
+ ],
186
+ });
187
+ return {
188
+ id: environmentId,
189
+ name: input.name,
190
+ sourceBranch: input.sourceBranch,
191
+ createdAt,
192
+ projectId: input.projectId,
193
+ urls: [mcpServerUrl],
194
+ };
195
+ }),
196
+ },
197
+ get: {
198
+ v1: implement(contract.environments.get.v1).handler(async ({ input }) => {
199
+ const environment = this.mockData.environments.get(input.environmentId);
200
+ if (!environment) {
201
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
202
+ }
203
+ return environment;
204
+ }),
205
+ },
206
+ deploy: {
207
+ v1: implement(contract.environments.deploy.v1).handler(async ({ input }) => {
208
+ const environment = this.mockData.environments.get(input.environmentId);
209
+ if (!environment) {
210
+ throw new ORPCError("NOT_FOUND", { message: "Environment not found" });
211
+ }
212
+ const deploymentId = randomUUID();
213
+ const deployment = {
214
+ id: deploymentId,
215
+ status: "ongoing",
216
+ sourceRef: environment.sourceBranch,
217
+ sourceCommitId: randomUUID().slice(0, 7),
218
+ sourceCommitMessage: "Mock deployment",
219
+ authorUsername: "test-user",
220
+ authorAvatarUrl: null,
221
+ startedAt: new Date(),
222
+ completedAt: null,
223
+ };
224
+ this.mockData.deployments.set(deploymentId, deployment);
225
+ return deployment;
226
+ }),
227
+ },
228
+ },
229
+ deployments: {
230
+ get: {
231
+ v1: implement(contract.deployments.get.v1).handler(async ({ input }) => {
232
+ const deployment = this.mockData.deployments.get(input.deploymentId);
233
+ if (!deployment) {
234
+ throw new ORPCError("NOT_FOUND", { message: "Deployment not found" });
235
+ }
236
+ if (deployment.status === "ongoing") {
237
+ const updatedDeployment = {
238
+ ...deployment,
239
+ status: "deployed",
240
+ completedAt: new Date(),
241
+ };
242
+ this.mockData.deployments.set(input.deploymentId, updatedDeployment);
243
+ return updatedDeployment;
244
+ }
245
+ return deployment;
246
+ }),
247
+ },
248
+ uploadArtifact: {
249
+ v1: implement(contract.deployments.uploadArtifact.v1).handler(async () => {
250
+ const token = randomUUID();
251
+ const baseUrl = `http://127.0.0.1:${this.port}`;
252
+ const uploadUrl = `${baseUrl}/__mock__upload/${token}`;
253
+ return {
254
+ uploadUrl,
255
+ token,
256
+ expiresAt: new Date(Date.now() + 3600000),
257
+ };
258
+ }),
259
+ },
260
+ },
261
+ });
262
+ const handler = new OpenAPIHandler(router);
263
+ this.app.use(async (req, res) => {
264
+ const apiCall = {
265
+ method: req.method,
266
+ path: req.path,
267
+ timestamp: new Date(),
268
+ };
269
+ if (req.body) {
270
+ apiCall.input = req.body;
271
+ }
272
+ const result = await handler.handle(req, res, {
273
+ context: {},
274
+ });
275
+ if (!result.matched) {
276
+ apiCall.responseStatus = 404;
277
+ this.callHistory.push(apiCall);
278
+ res.status(404).json({ error: "Not found" });
279
+ return;
280
+ }
281
+ apiCall.responseStatus = res.statusCode || 200;
282
+ this.callHistory.push(apiCall);
283
+ });
284
+ }
285
+ async stop() {
286
+ return new Promise((resolve, reject) => {
287
+ if (!this.server) {
288
+ resolve();
289
+ return;
290
+ }
291
+ this.server.close((error) => {
292
+ if (error) {
293
+ console.error("[MOCK] Error stopping server:", error);
294
+ reject(error);
295
+ }
296
+ else {
297
+ this.server = null;
298
+ resolve();
299
+ }
300
+ });
301
+ });
302
+ }
303
+ getCalls(method, pathPattern) {
304
+ return this.callHistory.filter((call) => {
305
+ if (method && call.method !== method)
306
+ return false;
307
+ if (pathPattern) {
308
+ if (typeof pathPattern === "string") {
309
+ if (!call.path.includes(pathPattern))
310
+ return false;
311
+ }
312
+ else {
313
+ if (!pathPattern.test(call.path))
314
+ return false;
315
+ }
316
+ }
317
+ return true;
318
+ });
319
+ }
320
+ getLastCall(method, pathPattern) {
321
+ for (let i = this.callHistory.length - 1; i >= 0; i--) {
322
+ const call = this.callHistory[i];
323
+ if (method && call.method !== method)
324
+ continue;
325
+ if (pathPattern) {
326
+ if (typeof pathPattern === "string") {
327
+ if (!call.path.includes(pathPattern))
328
+ continue;
329
+ }
330
+ else {
331
+ if (!pathPattern.test(call.path))
332
+ continue;
333
+ }
334
+ }
335
+ return call;
336
+ }
337
+ return undefined;
338
+ }
339
+ addProject(project) {
340
+ this.mockData.projects.set(project.id, project);
341
+ if (project.productionEnvironment) {
342
+ const env = {
343
+ id: project.productionEnvironment.id,
344
+ name: project.productionEnvironment.name,
345
+ sourceBranch: null,
346
+ mcpServerUrl: project.productionEnvironment.mcpServerUrl,
347
+ domains: [project.productionEnvironment.mcpServerUrl],
348
+ createdAt: new Date(),
349
+ projectId: project.id,
350
+ };
351
+ this.mockData.environments.set(env.id, env);
352
+ }
353
+ for (const env of project.environments) {
354
+ const fullEnv = {
355
+ id: env.id,
356
+ name: env.name,
357
+ sourceBranch: env.sourceBranch,
358
+ mcpServerUrl: env.mcpServerUrl,
359
+ domains: [env.mcpServerUrl],
360
+ createdAt: env.createdAt,
361
+ projectId: project.id,
362
+ };
363
+ this.mockData.environments.set(env.id, fullEnv);
364
+ }
365
+ }
366
+ }
367
+ //# sourceMappingURL=mock-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-server.js","sourceRoot":"","sources":["../../src/__tests__/mock-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ type Expectable = string | RegExp;
2
+ export type CliSessionOptions = {
3
+ cwd?: string;
4
+ env?: Record<string, string | undefined>;
5
+ cols?: number;
6
+ rows?: number;
7
+ timeoutMs?: number;
8
+ };
9
+ export type CliController = {
10
+ /** Wait until stdout/stderr contains a string or matches a regex */
11
+ expect: (pattern: Expectable, opts?: {
12
+ timeoutMs?: number;
13
+ }) => Promise<void>;
14
+ /** Send a line (adds newline) */
15
+ send: (line: string) => Promise<void>;
16
+ /** Send raw data (no newline) */
17
+ write: (data: string) => void;
18
+ /** Current accumulated output */
19
+ output: () => string;
20
+ /** Clear accumulated output (handy between phases) */
21
+ clear: () => void;
22
+ /** End session (SIGTERM) */
23
+ stop: () => void;
24
+ };
25
+ export declare function cliSession(cmd: string, args: string[], options: CliSessionOptions, fn: (cli: CliController) => Promise<void>): Promise<{
26
+ exitCode: number | null;
27
+ output: string;
28
+ }>;
29
+ export {};
@@ -0,0 +1,89 @@
1
+ import pty from "node-pty";
2
+ export async function cliSession(cmd, args, options, fn) {
3
+ const { cwd, env, cols = 100, rows = 30, timeoutMs = 8000 } = options;
4
+ const mergedEnv = {
5
+ ...process.env,
6
+ NO_COLOR: "1",
7
+ // Resolve @alpic-ai/api to TypeScript source via the "development" export condition,
8
+ // so e2e tests run without building the api package first.
9
+ NODE_OPTIONS: [process.env.NODE_OPTIONS, "--conditions=development"].filter(Boolean).join(" "),
10
+ ...Object.fromEntries(Object.entries(env ?? {}).filter(([, v]) => v !== undefined)),
11
+ };
12
+ for (const [k, v] of Object.entries(mergedEnv)) {
13
+ if (typeof v !== "string") {
14
+ console.error("[pty] bad env entry:", k, v, typeof v);
15
+ throw new Error(`env value for ${k} is not a string`);
16
+ }
17
+ }
18
+ const term = pty.spawn(cmd, args, {
19
+ cwd,
20
+ env: mergedEnv,
21
+ name: "xterm-256color",
22
+ cols,
23
+ rows,
24
+ });
25
+ let buf = "";
26
+ const append = (data) => {
27
+ buf += data;
28
+ };
29
+ term.onData(append);
30
+ let exitCode = null;
31
+ const exitPromise = new Promise((resolve) => {
32
+ term.onExit((e) => {
33
+ exitCode = e.exitCode ?? null;
34
+ resolve();
35
+ });
36
+ });
37
+ const controller = {
38
+ expect: (pattern, opts) => waitFor(() => matches(buf, pattern), opts?.timeoutMs ?? timeoutMs, () => buf, pattern),
39
+ send: async (line) => {
40
+ term.write(line + "\r");
41
+ },
42
+ write: (data) => term.write(data),
43
+ output: () => buf,
44
+ clear: () => {
45
+ buf = "";
46
+ },
47
+ stop: () => {
48
+ try {
49
+ term.kill();
50
+ }
51
+ catch {
52
+ // ignore
53
+ }
54
+ },
55
+ };
56
+ try {
57
+ await fn(controller);
58
+ }
59
+ finally {
60
+ controller.stop();
61
+ await Promise.race([exitPromise, sleep(200)]);
62
+ }
63
+ return { exitCode, output: buf };
64
+ }
65
+ function matches(text, pattern) {
66
+ if (typeof pattern === "string")
67
+ return text.includes(pattern);
68
+ return pattern.test(text);
69
+ }
70
+ async function waitFor(predicate, timeoutMs, getOutput, pattern) {
71
+ const start = Date.now();
72
+ while (true) {
73
+ if (predicate())
74
+ return;
75
+ if (Date.now() - start > timeoutMs) {
76
+ const out = getOutput();
77
+ throw new Error(`Timed out after ${timeoutMs}ms waiting for: ${patternToString(pattern)}\n\n` +
78
+ `--- current output ---\n${out}\n--- end output ---\n`);
79
+ }
80
+ await sleep(20);
81
+ }
82
+ }
83
+ function patternToString(p) {
84
+ return typeof p === "string" ? JSON.stringify(p) : `/${p.source}/${p.flags}`;
85
+ }
86
+ function sleep(ms) {
87
+ return new Promise((r) => setTimeout(r, ms));
88
+ }
89
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/__tests__/utils.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AA2B3B,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,IAAc,EACd,OAA0B,EAC1B,EAAyC;IAEzC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,IAAI,GAAG,EAAE,EAAE,SAAS,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAEtE,MAAM,SAAS,GAA2B;QACxC,GAAG,OAAO,CAAC,GAAG;QACd,QAAQ,EAAE,GAAG;QACb,qFAAqF;QACrF,2DAA2D;QAC3D,YAAY,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,0BAA0B,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAC9F,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAA4B,CAAC;KAC/G,CAAC;IAEF,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/C,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;QAChC,GAAG;QACH,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,gBAAgB;QACtB,IAAI;QACJ,IAAI;KACL,CAAC,CAAC;IAEH,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE;QAC9B,GAAG,IAAI,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEpB,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAChD,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YAChB,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC;YAC9B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,UAAU,GAAkB;QAChC,MAAM,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CACxB,OAAO,CACL,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,EAC3B,IAAI,EAAE,SAAS,IAAI,SAAS,EAC5B,GAAG,EAAE,CAAC,GAAG,EACT,OAAO,CACR;QACH,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACnB,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QACjC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG;QACjB,KAAK,EAAE,GAAG,EAAE;YACV,GAAG,GAAG,EAAE,CAAC;QACX,CAAC;QACD,IAAI,EAAE,GAAG,EAAE;YACT,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;KACF,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,UAAU,CAAC,CAAC;IACvB,CAAC;YAAS,CAAC;QACT,UAAU,CAAC,IAAI,EAAE,CAAC;QAClB,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,OAAmB;IAChD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/D,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,SAAwB,EACxB,SAAiB,EACjB,SAAuB,EACvB,OAAmB;IAEnB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEzB,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,SAAS,EAAE;YAAE,OAAO;QACxB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,SAAS,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,mBAAmB,SAAS,mBAAmB,eAAe,CAAC,OAAO,CAAC,MAAM;gBAC3E,2BAA2B,GAAG,wBAAwB,CACzD,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,CAAa;IACpC,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;AAC/E,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC"}
package/dist/api.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { ContractRouterClient } from "@orpc/contract";
2
+ import { contract } from "@alpic-ai/api";
3
+ export declare function getFrontendBaseUrl(): string;
4
+ export declare const api: ContractRouterClient<typeof contract>;
package/dist/api.js ADDED
@@ -0,0 +1,19 @@
1
+ import { createORPCClient } from "@orpc/client";
2
+ import { ResponseValidationPlugin } from "@orpc/contract/plugins";
3
+ import { OpenAPILink } from "@orpc/openapi-client/fetch";
4
+ import { contract } from "@alpic-ai/api";
5
+ function getApiBaseUrl() {
6
+ return process.env.ALPIC_API_BASE_URL ?? "https://api.alpic.ai";
7
+ }
8
+ export function getFrontendBaseUrl() {
9
+ return process.env.ALPIC_FRONTEND_BASE_URL ?? "https://app.alpic.ai";
10
+ }
11
+ const link = new OpenAPILink(contract, {
12
+ url: getApiBaseUrl(),
13
+ headers: {
14
+ Authorization: `Bearer ${process.env.ALPIC_API_KEY}`,
15
+ },
16
+ plugins: [new ResponseValidationPlugin(contract)],
17
+ });
18
+ export const api = createORPCClient(link);
19
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,SAAS,aAAa;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,sBAAsB,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,sBAAsB,CAAC;AACvE,CAAC;AAED,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,QAAQ,EAAE;IACrC,GAAG,EAAE,aAAa,EAAE;IACpB,OAAO,EAAE;QACP,aAAa,EAAE,UAAU,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE;KACrD;IACD,OAAO,EAAE,CAAC,IAAI,wBAAwB,CAAC,QAAQ,CAAC,CAAC;CAClD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,GAAG,GAA0C,gBAAgB,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Command } from "@oclif/core";
2
+ export declare class Deploy extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ directory: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,85 @@
1
+ import * as p from "@clack/prompts";
2
+ import { Args, Command } from "@oclif/core";
3
+ import chalk from "chalk";
4
+ import { readFileSync, rmSync } from "node:fs";
5
+ import { api } from "../api.js";
6
+ import { createTarArchive, getFilesToPack } from "../lib/archive.js";
7
+ import { ensureApiKey } from "../lib/auth.js";
8
+ import { deployAndWait, formatElapsed } from "../lib/deployment.js";
9
+ import { resolveDeployDir, resolveProjectForDeploy } from "../lib/project.js";
10
+ import { uploadToPresignedUrl } from "../lib/upload.js";
11
+ export class Deploy extends Command {
12
+ static description = "Deploy a project to Alpic";
13
+ static examples = ["<%= config.bin %> deploy", "<%= config.bin %> deploy ./my-app"];
14
+ static args = {
15
+ directory: Args.string({
16
+ description: "Directory to deploy (default: current directory)",
17
+ required: false,
18
+ }),
19
+ };
20
+ async run() {
21
+ const { args } = await this.parse(Deploy);
22
+ p.intro("Deploying to Alpic");
23
+ if (!ensureApiKey()) {
24
+ this.exit(1);
25
+ return;
26
+ }
27
+ const deployDir = resolveDeployDir(args.directory);
28
+ const config = await resolveProjectForDeploy(deployDir);
29
+ if (!config) {
30
+ p.cancel("Deploy cancelled");
31
+ this.exit(1);
32
+ return;
33
+ }
34
+ const spinner = p.spinner();
35
+ let tmpDir;
36
+ try {
37
+ spinner.start("Collecting source files...");
38
+ const files = getFilesToPack(deployDir);
39
+ spinner.stop(`Collected ${files.length} file${files.length === 1 ? "" : "s"}`);
40
+ const result = await createTarArchive(files, deployDir);
41
+ tmpDir = result.tmpDir;
42
+ const archivePath = result.archivePath;
43
+ const { uploadUrl, token } = await api.deployments.uploadArtifact.v1();
44
+ spinner.start("Uploading source...");
45
+ const buffer = readFileSync(archivePath);
46
+ await uploadToPresignedUrl(uploadUrl, buffer);
47
+ spinner.stop("Upload complete");
48
+ spinner.start("Triggering deployment...");
49
+ const deployStartedAt = Date.now();
50
+ const initialDeployment = await api.environments.deploy.v1({ environmentId: config.environmentId, token });
51
+ spinner.stop("Deployment started");
52
+ const { deployment, elapsedMs } = await deployAndWait({
53
+ initial: initialDeployment,
54
+ startedAt: deployStartedAt,
55
+ teamId: config.teamId,
56
+ projectId: config.projectId,
57
+ });
58
+ const elapsedStr = formatElapsed(elapsedMs);
59
+ if (deployment.status !== "deployed") {
60
+ throw new Error("Deployment failed");
61
+ }
62
+ const environment = await api.environments.get.v1({ environmentId: config.environmentId });
63
+ const urls = environment.mcpServerUrl ? [environment.mcpServerUrl] : environment.domains;
64
+ p.box([...urls.map((url) => chalk.bold(`🔗 ${url}`)), "", `Completed in ${elapsedStr}`].join("\n"), "Deployment summary:", {
65
+ contentAlign: "center",
66
+ titleAlign: "center",
67
+ width: "auto",
68
+ rounded: true,
69
+ contentPadding: 3,
70
+ });
71
+ }
72
+ catch (error) {
73
+ spinner.stop();
74
+ const message = error instanceof Error ? error.message : String(error);
75
+ p.cancel(`Error: ${message}`);
76
+ this.exit(1);
77
+ }
78
+ finally {
79
+ if (tmpDir) {
80
+ rmSync(tmpDir, { recursive: true, force: true });
81
+ }
82
+ }
83
+ }
84
+ }
85
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class GitConnect extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ directory: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ };
8
+ run(): Promise<void>;
9
+ }