alpic 0.0.0-dev.f71cfeb → 0.0.0-dev.f91105e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/auth.e2e.test.d.ts +1 -0
- package/dist/__tests__/auth.e2e.test.js +147 -0
- package/dist/__tests__/auth.e2e.test.js.map +1 -0
- package/dist/__tests__/deploy.e2e.test.d.ts +1 -0
- package/dist/__tests__/deploy.e2e.test.js +220 -0
- package/dist/__tests__/deploy.e2e.test.js.map +1 -0
- package/dist/__tests__/fixtures/demo-project/index.d.ts +1 -0
- package/dist/__tests__/fixtures/demo-project/index.js +4 -0
- package/dist/__tests__/fixtures/demo-project/index.js.map +1 -0
- package/dist/__tests__/git.e2e.test.d.ts +1 -0
- package/dist/__tests__/git.e2e.test.js +225 -0
- package/dist/__tests__/git.e2e.test.js.map +1 -0
- package/dist/__tests__/mock-server.d.ts +22 -0
- package/dist/__tests__/mock-server.js +490 -0
- package/dist/__tests__/mock-server.js.map +1 -0
- package/dist/__tests__/utils.d.ts +61 -0
- package/dist/__tests__/utils.js +198 -0
- package/dist/__tests__/utils.js.map +1 -0
- package/dist/api.d.ts +3 -0
- package/dist/api.js +15 -0
- package/dist/api.js.map +1 -0
- package/dist/commands/deploy.d.ts +9 -0
- package/dist/commands/deploy.js +84 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/git/connect.d.ts +9 -0
- package/dist/commands/git/connect.js +59 -0
- package/dist/commands/git/connect.js.map +1 -0
- package/dist/commands/git/disconnect.d.ts +9 -0
- package/dist/commands/git/disconnect.js +51 -0
- package/dist/commands/git/disconnect.js.map +1 -0
- package/dist/commands/{hello.d.ts → git.d.ts} +1 -1
- package/dist/commands/git.js +17 -0
- package/dist/commands/git.js.map +1 -0
- package/dist/commands/login.d.ts +6 -0
- package/dist/commands/login.js +39 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +6 -0
- package/dist/commands/logout.js +20 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/telemetry/disable.d.ts +5 -0
- package/dist/commands/telemetry/disable.js +14 -0
- package/dist/commands/telemetry/disable.js.map +1 -0
- package/dist/commands/telemetry/enable.d.ts +5 -0
- package/dist/commands/telemetry/enable.js +13 -0
- package/dist/commands/telemetry/enable.js.map +1 -0
- package/dist/commands/telemetry/status.d.ts +5 -0
- package/dist/commands/telemetry/status.js +19 -0
- package/dist/commands/telemetry/status.js.map +1 -0
- package/dist/commands/whoami.d.ts +6 -0
- package/dist/commands/whoami.js +25 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/env.d.ts +5 -0
- package/dist/env.js +11 -0
- package/dist/env.js.map +1 -0
- package/dist/lib/alpic-command.d.ts +4 -0
- package/dist/lib/alpic-command.js +17 -0
- package/dist/lib/alpic-command.js.map +1 -0
- package/dist/lib/archive.d.ts +7 -0
- package/dist/lib/archive.js +55 -0
- package/dist/lib/archive.js.map +1 -0
- package/dist/lib/auth/auth.d.ts +2 -0
- package/dist/lib/auth/auth.js +20 -0
- package/dist/lib/auth/auth.js.map +1 -0
- package/dist/lib/auth/oauth/client.d.ts +12 -0
- package/dist/lib/auth/oauth/client.js +66 -0
- package/dist/lib/auth/oauth/client.js.map +1 -0
- package/dist/lib/auth/oauth/config.d.ts +12 -0
- package/dist/lib/auth/oauth/config.js +39 -0
- package/dist/lib/auth/oauth/config.js.map +1 -0
- package/dist/lib/auth/oauth/constants.d.ts +2 -0
- package/dist/lib/auth/oauth/constants.js +3 -0
- package/dist/lib/auth/oauth/constants.js.map +1 -0
- package/dist/lib/auth/oauth/server.d.ts +8 -0
- package/dist/lib/auth/oauth/server.js +90 -0
- package/dist/lib/auth/oauth/server.js.map +1 -0
- package/dist/lib/auth/whoami.d.ts +28 -0
- package/dist/lib/auth/whoami.js +35 -0
- package/dist/lib/auth/whoami.js.map +1 -0
- package/dist/lib/config.d.ts +11 -0
- package/dist/lib/config.js +31 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/deployment.d.ts +21 -0
- package/dist/lib/deployment.js +39 -0
- package/dist/lib/deployment.js.map +1 -0
- package/dist/lib/git.d.ts +14 -0
- package/dist/lib/git.js +105 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/global-store.d.ts +28 -0
- package/dist/lib/global-store.js +75 -0
- package/dist/lib/global-store.js.map +1 -0
- package/dist/lib/project.d.ts +67 -0
- package/dist/lib/project.js +285 -0
- package/dist/lib/project.js.map +1 -0
- package/dist/lib/telemetry.d.ts +7 -0
- package/dist/lib/telemetry.js +66 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/dist/lib/upload.d.ts +1 -0
- package/dist/lib/upload.js +14 -0
- package/dist/lib/upload.js.map +1 -0
- package/dist/posthog.d.ts +3 -0
- package/dist/posthog.js +10 -0
- package/dist/posthog.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +33 -6
- package/dist/commands/hello.js +0 -10
- package/dist/commands/hello.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { setEnabled } from "../../lib/telemetry.js";
|
|
4
|
+
export default class TelemetryEnable extends Command {
|
|
5
|
+
static description = "Enable Alpic telemetry on this machine";
|
|
6
|
+
async run() {
|
|
7
|
+
await this.parse(TelemetryEnable);
|
|
8
|
+
setEnabled(true);
|
|
9
|
+
console.log(chalk.green("✓"), "Telemetry has been", chalk.green.bold("enabled"));
|
|
10
|
+
console.log(chalk.gray("Config saved to ~/.alpic/config.json"));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=enable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enable.js","sourceRoot":"","sources":["../../../src/commands/telemetry/enable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,OAAO;IAClD,MAAM,CAAU,WAAW,GAAG,wCAAwC,CAAC;IAEvE,KAAK,CAAC,GAAG;QACP,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,oBAAoB,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC,CAAC;IAClE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { getMachineId, isEnabled } from "../../lib/telemetry.js";
|
|
4
|
+
export default class TelemetryStatus extends Command {
|
|
5
|
+
static description = "Show Alpic telemetry settings for this machine";
|
|
6
|
+
async run() {
|
|
7
|
+
await this.parse(TelemetryStatus);
|
|
8
|
+
const enabled = isEnabled();
|
|
9
|
+
console.log(chalk.bold.underline("Alpic Telemetry"));
|
|
10
|
+
console.log();
|
|
11
|
+
console.log("Status:", enabled ? chalk.green.bold("Enabled") : chalk.yellow.bold("Disabled"));
|
|
12
|
+
console.log(chalk.gray("Machine ID:"), getMachineId());
|
|
13
|
+
console.log();
|
|
14
|
+
console.log(chalk.gray("To opt out, run: alpic telemetry disable"));
|
|
15
|
+
console.log(chalk.gray("Or set: ALPIC_TELEMETRY_DISABLED=1"));
|
|
16
|
+
console.log(chalk.gray("Debug mode: ALPIC_TELEMETRY_DEBUG=1"));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/telemetry/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,OAAO;IAClD,MAAM,CAAU,WAAW,GAAG,gDAAgD,CAAC;IAE/E,KAAK,CAAC,GAAG;QACP,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAC9F,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC,CAAC;IACjE,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { AlpicCommand } from "../lib/alpic-command.js";
|
|
4
|
+
import { getWhoamiInfo } from "../lib/auth/whoami.js";
|
|
5
|
+
export class Whoami extends AlpicCommand {
|
|
6
|
+
static description = "Show the current Alpic identity (API key or logged-in user)";
|
|
7
|
+
static examples = ["<%= config.bin %> whoami"];
|
|
8
|
+
async run() {
|
|
9
|
+
await this.parse(Whoami);
|
|
10
|
+
p.intro("Reading authentication status…");
|
|
11
|
+
const info = await getWhoamiInfo();
|
|
12
|
+
if (!info) {
|
|
13
|
+
p.cancel("Not logged in. Run `alpic login` or set ALPIC_API_KEY.");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (info.method === "api_key") {
|
|
17
|
+
const msg = `Authenticated via API key — Team: ${chalk.green(info.team?.name ?? "unknown")}`;
|
|
18
|
+
p.outro(msg);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const msg = `Authenticated as ${chalk.green(info.name)} (${chalk.cyan(info.email)})`;
|
|
22
|
+
p.outro(msg);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=whoami.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,OAAO,MAAO,SAAQ,YAAY;IACtC,MAAM,CAAU,WAAW,GAAG,6DAA6D,CAAC;IAE5F,MAAM,CAAU,QAAQ,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAExD,KAAK,CAAC,GAAG;QACP,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAEzB,CAAC,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAE1C,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC;QAEnC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,CAAC,CAAC,MAAM,CAAC,wDAAwD,CAAC,CAAC;YACnE,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,qCAAqC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,SAAS,CAAC,EAAE,CAAC;YAC7F,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,GAAG,GAAG,oBAAoB,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QACrF,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACf,CAAC"}
|
package/dist/env.d.ts
ADDED
package/dist/env.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createEnv } from "@t3-oss/env-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const env = createEnv({
|
|
4
|
+
server: {
|
|
5
|
+
ALPIC_API_BASE_URL: z.string().default("https://api.alpic.ai"),
|
|
6
|
+
ALPIC_COGNITO_CLIENT_ID: z.string().default("1023dpimab488tfv4sh4bdk2r"),
|
|
7
|
+
ALPIC_FRONTEND_BASE_URL: z.string().default("https://app.alpic.ai"),
|
|
8
|
+
},
|
|
9
|
+
runtimeEnv: process.env,
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,GAAG,GAAG,SAAS,CAAC;IAC3B,MAAM,EAAE;QACN,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC;QAC9D,uBAAuB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,2BAA2B,CAAC;QACxE,uBAAuB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC;KACpE;IACD,UAAU,EAAE,OAAO,CAAC,GAAG;CACxB,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { Command } from "@oclif/core";
|
|
3
|
+
import { ORPCError } from "@orpc/client";
|
|
4
|
+
export class AlpicCommand extends Command {
|
|
5
|
+
async catch(error) {
|
|
6
|
+
if (error instanceof ORPCError) {
|
|
7
|
+
p.cancel(`An error occurred while connecting to Alpic: ${error.message}`);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (error instanceof Error) {
|
|
11
|
+
p.cancel(error.message);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
p.cancel(String(error));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=alpic-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alpic-command.js","sourceRoot":"","sources":["../../src/lib/alpic-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,OAAgB,YAAa,SAAQ,OAAO;IACvC,KAAK,CAAC,KAAK,CAAC,KAAc;QACjC,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;YAC/B,CAAC,CAAC,MAAM,CAAC,gDAAgD,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC1E,OAAO;QACT,CAAC;QACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QACD,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function ensureGitAvailable(): void;
|
|
2
|
+
export declare function getGitFiles(deployDir: string): string[];
|
|
3
|
+
export declare function getFilesToPack(deployDir: string): string[];
|
|
4
|
+
export declare function createTarArchive(files: string[], deployDir: string): Promise<{
|
|
5
|
+
tmpDir: string;
|
|
6
|
+
archivePath: string;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { create as tarCreate } from "tar";
|
|
6
|
+
const GIT_FILES_MAX_BUFFER = 10 * 1024 * 1024;
|
|
7
|
+
function isGitRepository(dir) {
|
|
8
|
+
return existsSync(join(resolve(dir), ".git"));
|
|
9
|
+
}
|
|
10
|
+
export function ensureGitAvailable() {
|
|
11
|
+
try {
|
|
12
|
+
execSync("git --version", { stdio: "ignore" });
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new Error("Git is required to deploy. Please install git and ensure it is available in your PATH.");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function getGitFiles(deployDir) {
|
|
19
|
+
const dir = resolve(deployDir);
|
|
20
|
+
const output = execSync("git ls-files -z --cached --others --exclude-standard -- .", {
|
|
21
|
+
cwd: dir,
|
|
22
|
+
encoding: "utf8",
|
|
23
|
+
maxBuffer: GIT_FILES_MAX_BUFFER,
|
|
24
|
+
});
|
|
25
|
+
const files = output.split("\0").filter(Boolean);
|
|
26
|
+
if (files.length === 0) {
|
|
27
|
+
throw new Error("No tracked or untracked files found. Ensure you are in a git repository with files to deploy.");
|
|
28
|
+
}
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
export function getFilesToPack(deployDir) {
|
|
32
|
+
const dir = resolve(deployDir);
|
|
33
|
+
if (isGitRepository(dir)) {
|
|
34
|
+
return getGitFiles(deployDir);
|
|
35
|
+
}
|
|
36
|
+
ensureGitAvailable();
|
|
37
|
+
execSync("git init", { cwd: dir });
|
|
38
|
+
try {
|
|
39
|
+
return getGitFiles(deployDir);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
rmSync(join(dir, ".git"), { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function createTarArchive(files, deployDir) {
|
|
46
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "alpic-deploy-"));
|
|
47
|
+
const archivePath = join(tmpDir, "source.tar.gz");
|
|
48
|
+
await tarCreate({
|
|
49
|
+
gzip: true,
|
|
50
|
+
file: archivePath,
|
|
51
|
+
cwd: deployDir,
|
|
52
|
+
}, files);
|
|
53
|
+
return { tmpDir, archivePath };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/lib/archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,KAAK,CAAC;AAE1C,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,SAAS,eAAe,CAAC,GAAW;IAClC,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC;QACH,QAAQ,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;IAC5G,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,QAAQ,CAAC,2DAA2D,EAAE;QACnF,GAAG,EAAE,GAAG;QACR,QAAQ,EAAE,MAAM;QAChB,SAAS,EAAE,oBAAoB;KAChC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,+FAA+F,CAAC,CAAC;IACnH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/B,IAAI,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;IACD,kBAAkB,EAAE,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAe,EAAE,SAAiB;IACvE,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAClD,MAAM,SAAS,CACb;QACE,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,WAAW;QACjB,GAAG,EAAE,SAAS;KACf,EACD,KAAK,CACN,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getValidAccessToken } from "./oauth/client.js";
|
|
2
|
+
export async function getApiToken() {
|
|
3
|
+
return process.env.ALPIC_API_KEY ?? getValidAccessToken();
|
|
4
|
+
}
|
|
5
|
+
export async function isAuthenticated() {
|
|
6
|
+
const isAuthenticatedViaApiKey = hasApiKey();
|
|
7
|
+
if (isAuthenticatedViaApiKey)
|
|
8
|
+
return true;
|
|
9
|
+
const isAuthenticatedViaOAuth = await hasValidAccessToken();
|
|
10
|
+
if (isAuthenticatedViaOAuth)
|
|
11
|
+
return true;
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
function hasApiKey() {
|
|
15
|
+
return process.env.ALPIC_API_KEY !== undefined;
|
|
16
|
+
}
|
|
17
|
+
async function hasValidAccessToken() {
|
|
18
|
+
return (await getValidAccessToken()) !== null;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/lib/auth/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,mBAAmB,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,wBAAwB,GAAG,SAAS,EAAE,CAAC;IAC7C,IAAI,wBAAwB;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,uBAAuB,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAC5D,IAAI,uBAAuB;QAAE,OAAO,IAAI,CAAC;IAEzC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,SAAS,CAAC;AACjD,CAAC;AAED,KAAK,UAAU,mBAAmB;IAChC,OAAO,CAAC,MAAM,mBAAmB,EAAE,CAAC,KAAK,IAAI,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as openid from "openid-client";
|
|
2
|
+
import { type Credentials } from "../../global-store.js";
|
|
3
|
+
export declare function fetchUserInfo(credentials: Credentials): Promise<openid.UserInfoResponse>;
|
|
4
|
+
export declare function getValidAccessToken(): Promise<Credentials | null>;
|
|
5
|
+
export declare function getExpiresAt(expires_in: number): number;
|
|
6
|
+
export declare function prepareOAuthConfig(): Promise<{
|
|
7
|
+
authorizeUrl: URL;
|
|
8
|
+
state: string;
|
|
9
|
+
nonce: string;
|
|
10
|
+
codeVerifier: string;
|
|
11
|
+
config: openid.Configuration;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as openid from "openid-client";
|
|
2
|
+
import { globalStore } from "../../global-store.js";
|
|
3
|
+
import { getIssuer, getOAuthConfig } from "./config.js";
|
|
4
|
+
import { LOOPBACK_HOST, LOOPBACK_PORT } from "./constants.js";
|
|
5
|
+
const SCOPES = ["openid", "email", "profile"];
|
|
6
|
+
export async function fetchUserInfo(credentials) {
|
|
7
|
+
const issuer = await getIssuer();
|
|
8
|
+
const config = await getOAuthConfig(issuer);
|
|
9
|
+
return openid.fetchUserInfo(config, credentials.access_token, credentials.sub);
|
|
10
|
+
}
|
|
11
|
+
export async function getValidAccessToken() {
|
|
12
|
+
const stored = globalStore.getCredentials();
|
|
13
|
+
if (!stored) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
if (isAccessTokenExpired(stored)) {
|
|
17
|
+
try {
|
|
18
|
+
return await refreshAccessToken(stored);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return stored;
|
|
25
|
+
}
|
|
26
|
+
async function refreshAccessToken(credentials) {
|
|
27
|
+
if (!credentials.refresh_token) {
|
|
28
|
+
throw new Error("No refresh token available");
|
|
29
|
+
}
|
|
30
|
+
const issuer = await getIssuer();
|
|
31
|
+
const config = await getOAuthConfig(issuer);
|
|
32
|
+
const response = await openid.refreshTokenGrant(config, credentials.refresh_token);
|
|
33
|
+
const refreshed = {
|
|
34
|
+
access_token: response.access_token,
|
|
35
|
+
refresh_token: response.refresh_token ?? credentials.refresh_token,
|
|
36
|
+
expires_at: getExpiresAt(response.expires_in ?? 0),
|
|
37
|
+
sub: credentials.sub,
|
|
38
|
+
};
|
|
39
|
+
globalStore.saveCredentials(refreshed);
|
|
40
|
+
return refreshed;
|
|
41
|
+
}
|
|
42
|
+
export function getExpiresAt(expires_in) {
|
|
43
|
+
return expires_in !== undefined ? Math.floor(Date.now() / 1000) + expires_in : Date.now() / 1000 + 3600;
|
|
44
|
+
}
|
|
45
|
+
function isAccessTokenExpired(credentials) {
|
|
46
|
+
const EXPIRATION_WINDOW_IN_SECONDS = 300;
|
|
47
|
+
return Date.now() / 1000 + EXPIRATION_WINDOW_IN_SECONDS >= credentials.expires_at;
|
|
48
|
+
}
|
|
49
|
+
export async function prepareOAuthConfig() {
|
|
50
|
+
const issuer = await getIssuer();
|
|
51
|
+
const config = await getOAuthConfig(issuer);
|
|
52
|
+
const codeVerifier = openid.randomPKCECodeVerifier();
|
|
53
|
+
const codeChallenge = await openid.calculatePKCECodeChallenge(codeVerifier);
|
|
54
|
+
const state = openid.randomState();
|
|
55
|
+
const nonce = openid.randomNonce();
|
|
56
|
+
const authorizeUrl = openid.buildAuthorizationUrl(config, {
|
|
57
|
+
redirect_uri: `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}/callback`,
|
|
58
|
+
scope: SCOPES.join(" "),
|
|
59
|
+
code_challenge: codeChallenge,
|
|
60
|
+
code_challenge_method: "S256",
|
|
61
|
+
state,
|
|
62
|
+
nonce,
|
|
63
|
+
});
|
|
64
|
+
return { authorizeUrl, state, nonce, codeVerifier, config };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../../src/lib/auth/oauth/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as openid from "openid-client";
|
|
2
|
+
export declare function getOAuthConfig(issuer: string): Promise<openid.Configuration>;
|
|
3
|
+
export declare function getIssuer(): Promise<string>;
|
|
4
|
+
interface OAuthProtectedResourceConfig {
|
|
5
|
+
resource: string;
|
|
6
|
+
authorization_servers: string[];
|
|
7
|
+
bearer_methods_supported: string[];
|
|
8
|
+
scopes_supported: string[];
|
|
9
|
+
resource_documentation: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function getOAuthProtectedResourceConfig(): Promise<OAuthProtectedResourceConfig>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as openid from "openid-client";
|
|
2
|
+
import { env } from "../../../env.js";
|
|
3
|
+
let cachedConfig;
|
|
4
|
+
export async function getOAuthConfig(issuer) {
|
|
5
|
+
if (cachedConfig) {
|
|
6
|
+
return cachedConfig;
|
|
7
|
+
}
|
|
8
|
+
const issuerUrl = new URL(issuer);
|
|
9
|
+
try {
|
|
10
|
+
cachedConfig = await openid.discovery(issuerUrl, env.ALPIC_COGNITO_CLIENT_ID);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new Error("Failed to discover OAuth config");
|
|
14
|
+
}
|
|
15
|
+
return cachedConfig;
|
|
16
|
+
}
|
|
17
|
+
export async function getIssuer() {
|
|
18
|
+
const config = await getOAuthProtectedResourceConfig();
|
|
19
|
+
const issuer = config.authorization_servers[0];
|
|
20
|
+
if (!issuer) {
|
|
21
|
+
throw new Error("No authorization server in OAuth protected resource config");
|
|
22
|
+
}
|
|
23
|
+
return issuer;
|
|
24
|
+
}
|
|
25
|
+
let cached;
|
|
26
|
+
export async function getOAuthProtectedResourceConfig() {
|
|
27
|
+
if (cached) {
|
|
28
|
+
return cached;
|
|
29
|
+
}
|
|
30
|
+
const baseUrl = env.ALPIC_API_BASE_URL;
|
|
31
|
+
const response = await fetch(`${baseUrl}/.well-known/oauth-protected-resource`);
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`Failed to load service config from ${baseUrl} (${response.status} ${response.statusText})`);
|
|
34
|
+
}
|
|
35
|
+
const data = (await response.json());
|
|
36
|
+
cached = data;
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../src/lib/auth/oauth/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAEtC,IAAI,YAAkC,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc;IACjD,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAElC,IAAI,CAAC;QACH,YAAY,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,MAAM,GAAG,MAAM,+BAA+B,EAAE,CAAC;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAUD,IAAI,MAAoC,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,+BAA+B;IACnD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,kBAAkB,CAAC;IACvC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,uCAAuC,CAAC,CAAC;IAEhF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,sCAAsC,OAAO,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,GAAG,CAAC,CAAC;IAC/G,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAiC,CAAC;IACrE,MAAM,GAAG,IAAI,CAAC;IACd,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../src/lib/auth/oauth/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC;AACzC,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as openid from "openid-client";
|
|
2
|
+
import type { Credentials } from "../../global-store.js";
|
|
3
|
+
export declare const listenToOAuthCallback: ({ state, nonce, codeVerifier, config, }: {
|
|
4
|
+
state: string;
|
|
5
|
+
nonce: string;
|
|
6
|
+
codeVerifier: string;
|
|
7
|
+
config: openid.Configuration;
|
|
8
|
+
}) => Promise<Credentials>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import * as openid from "openid-client";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { getExpiresAt } from "./client.js";
|
|
5
|
+
import { LOOPBACK_HOST, LOOPBACK_PORT } from "./constants.js";
|
|
6
|
+
const createCallbackServer = ({ state, nonce, codeVerifier, config, onSuccess, onError, }) => createServer(async (req, res) => {
|
|
7
|
+
const url = req.url ?? "/";
|
|
8
|
+
if (!url.startsWith("/callback")) {
|
|
9
|
+
p.log.message(`Ignoring request to ${url}`);
|
|
10
|
+
res.writeHead(404).end();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const callbackUrl = new URL(url, `http://${req.headers.host}`);
|
|
14
|
+
try {
|
|
15
|
+
const tokens = await openid.authorizationCodeGrant(config, callbackUrl, {
|
|
16
|
+
pkceCodeVerifier: codeVerifier,
|
|
17
|
+
expectedState: state,
|
|
18
|
+
expectedNonce: nonce,
|
|
19
|
+
});
|
|
20
|
+
const sub = tokens.claims()?.sub;
|
|
21
|
+
if (!sub || !tokens.refresh_token) {
|
|
22
|
+
throw new Error("ID token did not contain sub");
|
|
23
|
+
}
|
|
24
|
+
const stored = {
|
|
25
|
+
access_token: tokens.access_token,
|
|
26
|
+
refresh_token: tokens.refresh_token,
|
|
27
|
+
expires_at: getExpiresAt(tokens.expires_in ?? 0),
|
|
28
|
+
sub,
|
|
29
|
+
};
|
|
30
|
+
p.log.success("✅ Authentication successful");
|
|
31
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
32
|
+
res.end(html("Logged in", "You can close this window.", true));
|
|
33
|
+
onSuccess(stored);
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
p.log.error(e instanceof Error ? e.message : String(e));
|
|
37
|
+
res.writeHead(500, { "Content-Type": "text/html" });
|
|
38
|
+
res.end(html("Login failed", "Token exchange error. Try again.", false));
|
|
39
|
+
onError(e instanceof Error ? e : new Error(String(e)));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
export const listenToOAuthCallback = ({ state, nonce, codeVerifier, config, }) => {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const callbackServer = createCallbackServer({
|
|
45
|
+
state,
|
|
46
|
+
nonce,
|
|
47
|
+
codeVerifier,
|
|
48
|
+
config,
|
|
49
|
+
onSuccess: (storedToken) => {
|
|
50
|
+
callbackServer.close();
|
|
51
|
+
resolve(storedToken);
|
|
52
|
+
},
|
|
53
|
+
onError: (err) => {
|
|
54
|
+
callbackServer.close();
|
|
55
|
+
reject(err);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
callbackServer.listen(LOOPBACK_PORT, LOOPBACK_HOST);
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
function html(title, body, success) {
|
|
62
|
+
const bg = success ? "#ecfdf5" : "#fef2f2";
|
|
63
|
+
const border = success ? "#10b981" : "#ef4444";
|
|
64
|
+
const color = success ? "#065f46" : "#991b1b";
|
|
65
|
+
const logoSvg = `<svg width="120" height="24" viewBox="0 0 213 42" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18.139 41.4181H1.58758C0.877981 41.4181 0.302734 40.8443 0.302734 40.1347V16.4957C0.302734 15.3182 1.73285 14.7794 2.48612 15.6845C7.8891 22.1765 15.4029 32.6359 19.2489 39.5848C19.7114 40.4205 19.0941 41.4181 18.139 41.4181Z" fill="#0A0E1C"/><path d="M34.9934 41.4181H23.8233C23.3366 41.4181 22.8936 41.144 22.6686 40.7125C18.5795 32.8689 8.1012 17.5927 0.638233 9.39174C0.420845 9.15285 0.302734 8.84629 0.302734 8.5233V1.58783C0.302734 0.878225 0.876486 0.302979 1.58609 0.302979H6.87182C7.19145 0.302979 7.48677 0.406983 7.71702 0.628672C14.4657 7.1264 32.6261 34.0831 35.8013 40.2463C36.1263 40.8771 35.703 41.4181 34.9934 41.4181Z" fill="#0A0E1C"/><path d="M41.4179 22.479C41.4177 26.1486 41.4178 34.7435 41.4178 40.0835C41.4178 41.3801 39.7105 41.8673 39.0235 40.7677C32.9779 31.091 20.3757 11.3903 13.1298 2.51663C12.4214 1.64905 13.0384 0.302979 14.1585 0.302979H25.3523C25.7594 0.302979 26.1363 0.487723 26.3781 0.815243C28.4729 3.65265 38.6422 17.4716 41.2512 21.8598C41.3654 22.052 41.4179 22.2554 41.4179 22.479Z" fill="#0A0E1C"/><path d="M40.133 0.302979H31.6871C30.6461 0.302979 30.0373 1.47597 30.6363 2.32725L39.0823 14.3294C39.8043 15.3554 41.4179 14.8446 41.4179 13.59V1.58783C41.4179 0.878225 40.8426 0.302979 40.133 0.302979Z" fill="#0A0E1C"/><path d="M69.4252 40.871H61.9757L75.539 0.850139H84.1548L97.737 40.871H90.2875L87.0895 31.0026H72.6044L69.4252 40.871ZM74.4856 25.1792H85.2083L79.9974 9.05754H79.6964L74.4856 25.1792Z" fill="#0A0E1C"/><path d="M102.282 40.871V0.850139H109.261V34.7936H126.229V40.871H102.282Z" fill="#0A0E1C"/><path d="M131.757 40.871V0.850139H146.204C155.065 0.850139 159.786 6.45853 159.786 14.0992C159.786 21.7985 155.008 27.3483 146.11 27.3483H138.736V40.871H131.757ZM138.736 21.3882H145.17C150.362 21.3882 152.676 18.3593 152.676 14.0992C152.676 9.83919 150.362 6.90798 145.132 6.90798H138.736V21.3882Z" fill="#0A0E1C"/><path d="M172.298 0.850139V40.871H165.319V0.850139H172.298Z" fill="#0A0E1C"/><path d="M212.303 14.3533H205.267C204.458 9.56561 200.79 6.81027 196.106 6.81027C189.804 6.81027 185.364 11.8324 185.364 20.8606C185.364 30.045 189.842 34.9108 196.087 34.9108C200.696 34.9108 204.383 32.2727 205.267 27.5828L212.303 27.6219C211.231 35.2626 205.154 41.4181 195.993 41.4181C185.835 41.4181 178.348 33.7774 178.348 20.8606C178.348 7.92413 185.929 0.302979 195.993 0.302979C204.534 0.302979 211.118 5.46191 212.303 14.3533Z" fill="#0A0E1C"/></svg>`;
|
|
66
|
+
return `<!DOCTYPE html>
|
|
67
|
+
<html>
|
|
68
|
+
<head>
|
|
69
|
+
<meta charset="utf-8">
|
|
70
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
71
|
+
<title>${title}</title>
|
|
72
|
+
<style>
|
|
73
|
+
* { box-sizing: border-box; }
|
|
74
|
+
body { margin: 0; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2rem; font-family: system-ui, -apple-system, sans-serif; background: #f8fafc; }
|
|
75
|
+
.logo { flex-shrink: 0; }
|
|
76
|
+
.alert { max-width: 360px; padding: 1.25rem 1.5rem; border-radius: 8px; border-left: 4px solid ${border}; background: ${bg}; color: ${color}; }
|
|
77
|
+
.alert h1 { margin: 0 0 0.5rem; font-size: 1.125rem; font-weight: 600; }
|
|
78
|
+
.alert p { margin: 0; font-size: 0.9375rem; line-height: 1.5; opacity: 0.95; }
|
|
79
|
+
</style>
|
|
80
|
+
</head>
|
|
81
|
+
<body>
|
|
82
|
+
<div class="logo" aria-hidden="true">${logoSvg}</div>
|
|
83
|
+
<div class="alert">
|
|
84
|
+
<h1>${title}</h1>
|
|
85
|
+
<p>${body}</p>
|
|
86
|
+
</div>
|
|
87
|
+
</body>
|
|
88
|
+
</html>`;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../../src/lib/auth/oauth/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type WhoamiInfo = {
|
|
2
|
+
method: "api_key";
|
|
3
|
+
team: {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
} | {
|
|
8
|
+
method: "oauth";
|
|
9
|
+
email: string;
|
|
10
|
+
name: string;
|
|
11
|
+
} | null;
|
|
12
|
+
export declare function getWhoamiInfo(): Promise<{
|
|
13
|
+
method: string;
|
|
14
|
+
team: {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
createdAt: Date;
|
|
18
|
+
hasStripeAccount: boolean;
|
|
19
|
+
hasActiveSubscription: boolean;
|
|
20
|
+
} | undefined;
|
|
21
|
+
email?: undefined;
|
|
22
|
+
name?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
method: string;
|
|
25
|
+
email: string;
|
|
26
|
+
name: string;
|
|
27
|
+
team?: undefined;
|
|
28
|
+
} | null>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { api } from "../../api.js";
|
|
2
|
+
import { fetchUserInfo, getValidAccessToken } from "./oauth/client.js";
|
|
3
|
+
export async function getWhoamiInfo() {
|
|
4
|
+
if (process.env.ALPIC_API_KEY !== undefined) {
|
|
5
|
+
const team = await getApiKeyTeam();
|
|
6
|
+
return {
|
|
7
|
+
method: "api_key",
|
|
8
|
+
team,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const token = await getValidAccessToken();
|
|
12
|
+
if (!token)
|
|
13
|
+
return null;
|
|
14
|
+
const userInfo = await fetchOAuthUserInfo(token);
|
|
15
|
+
if (!userInfo)
|
|
16
|
+
return null;
|
|
17
|
+
return {
|
|
18
|
+
method: "oauth",
|
|
19
|
+
email: userInfo.email,
|
|
20
|
+
name: userInfo.name,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async function fetchOAuthUserInfo(credentials) {
|
|
24
|
+
try {
|
|
25
|
+
return (await fetchUserInfo(credentials));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function getApiKeyTeam() {
|
|
32
|
+
const teams = await api.teams.list.v1();
|
|
33
|
+
return teams[0];
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=whoami.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../../src/lib/auth/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAOvE,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC;QACnC,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,IAAI;SACL,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACjD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,OAAO;QACL,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;KACpB,CAAC;AACJ,CAAC;AAmBD,KAAK,UAAU,kBAAkB,CAAC,WAAwB;IACxD,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,aAAa,CAAC,WAAW,CAAC,CAA6B,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa;IAC1B,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;IACxC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProjectConfig } from "../types.js";
|
|
2
|
+
export declare const config: {
|
|
3
|
+
load: (deployDir: string) => {
|
|
4
|
+
projectId: string;
|
|
5
|
+
teamId: string;
|
|
6
|
+
projectName: string;
|
|
7
|
+
environmentId: string | undefined;
|
|
8
|
+
environmentName: string | undefined;
|
|
9
|
+
} | null;
|
|
10
|
+
save: (config: ProjectConfig, deployDir: string) => void;
|
|
11
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const getProjectJsonPath = (deployDir) => join(deployDir, ".alpic", "project.json");
|
|
4
|
+
export const config = {
|
|
5
|
+
load: (deployDir) => {
|
|
6
|
+
const path = getProjectJsonPath(deployDir);
|
|
7
|
+
if (!existsSync(path)) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
let raw;
|
|
11
|
+
try {
|
|
12
|
+
raw = JSON.parse(readFileSync(path, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
throw new Error(`Failed to load project config: ${error.message}`);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
projectId: raw.projectId,
|
|
19
|
+
teamId: raw.teamId,
|
|
20
|
+
projectName: raw.projectName ?? raw.projectId,
|
|
21
|
+
environmentId: raw.environmentId,
|
|
22
|
+
environmentName: raw.environmentName,
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
save: (config, deployDir) => {
|
|
26
|
+
const path = getProjectJsonPath(deployDir);
|
|
27
|
+
mkdirSync(join(deployDir, ".alpic"), { recursive: true });
|
|
28
|
+
writeFileSync(path, JSON.stringify(config, null, 2));
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;AAE5F,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,SAAiB,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,GAAmE,CAAC;QACxE,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAG1C,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,kCAAmC,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QAChF,CAAC;QACD,OAAO;YACL,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,SAAS;YAC7C,aAAa,EAAE,GAAG,CAAC,aAAa;YAChC,eAAe,EAAE,GAAG,CAAC,eAAe;SACrC,CAAC;IACJ,CAAC;IACD,IAAI,EAAE,CAAC,MAAqB,EAAE,SAAiB,EAAE,EAAE;QACjD,MAAM,IAAI,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAC3C,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;CACF,CAAC"}
|