@revos/cli 0.1.0 → 0.1.2
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/README.md +24 -2
- package/bin/revos.js +1 -1
- package/dist/adapters/oclif/commands/auth/login.mjs +2 -2
- package/dist/adapters/oclif/commands/auth/logout.mjs +2 -2
- package/dist/adapters/oclif/commands/auth/status.mjs +2 -2
- package/dist/adapters/oclif/commands/init.d.mts +5 -1
- package/dist/adapters/oclif/commands/init.mjs +74 -13
- package/dist/adapters/oclif/commands/org/current.mjs +3 -3
- package/dist/adapters/oclif/commands/org/list.mjs +3 -3
- package/dist/adapters/oclif/commands/org/switch.mjs +3 -3
- package/dist/adapters/oclif/commands/overlays/diff.mjs +3 -3
- package/dist/adapters/oclif/commands/overlays/pull.mjs +3 -3
- package/dist/adapters/oclif/commands/overlays/push.mjs +3 -3
- package/dist/adapters/oclif/commands/overlays/status.mjs +3 -3
- package/dist/{base.command-BGM225ik.mjs → base.command-DDSLyx5v.mjs} +1 -1
- package/dist/{core-Bif-kxlo.mjs → core-EJgxP-x5.mjs} +106 -218
- package/dist/{index-C0e8MXGP.d.mts → index-DH6vy050.d.mts} +12 -9
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/templates/.devcontainer/Dockerfile +14 -0
- package/dist/templates/.devcontainer/devcontainer.json +54 -0
- package/dist/templates/.devcontainer/setup.sh +32 -0
- package/dist/templates/AGENTS.md +2 -3
- package/dist/templates/CLAUDE.md +0 -16
- package/dist/templates/README.md +23 -0
- package/dist/templates/dbt/dbt_project.yml +22 -0
- package/dist/templates/index.ts +4 -0
- package/dist/templates/skills/create-semantic-model/SKILL.md +1611 -0
- package/dist/templates/skills/explore-lakehouse/SKILL.md +131 -0
- package/package.json +10 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import * as os from "os";
|
|
4
|
-
import
|
|
4
|
+
import { createServer } from "node:http";
|
|
5
5
|
import * as crypto from "crypto";
|
|
6
6
|
import { Client, client } from "@revos/api-client";
|
|
7
7
|
import search from "@inquirer/search";
|
|
@@ -72,7 +72,6 @@ async function startOAuthServer() {
|
|
|
72
72
|
}
|
|
73
73
|
function tryStartServer(port) {
|
|
74
74
|
return new Promise((resolve, reject) => {
|
|
75
|
-
const app = express();
|
|
76
75
|
let callbackResolve;
|
|
77
76
|
let callbackReject;
|
|
78
77
|
let timeoutId;
|
|
@@ -80,28 +79,37 @@ function tryStartServer(port) {
|
|
|
80
79
|
callbackResolve = res;
|
|
81
80
|
callbackReject = rej;
|
|
82
81
|
});
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
const server = createServer((req, res) => {
|
|
83
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
84
|
+
if (url.pathname !== "/callback" || req.method !== "GET") {
|
|
85
|
+
res.writeHead(404);
|
|
86
|
+
res.end();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const code = url.searchParams.get("code") ?? void 0;
|
|
90
|
+
const state = url.searchParams.get("state") ?? void 0;
|
|
91
|
+
const error = url.searchParams.get("error") ?? void 0;
|
|
92
|
+
const errorDescription = url.searchParams.get("error_description") ?? void 0;
|
|
88
93
|
if (error) {
|
|
89
|
-
res.
|
|
94
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
95
|
+
res.end(getErrorHtml(errorDescription || error));
|
|
90
96
|
callbackReject(new Error(errorDescription || error));
|
|
91
97
|
return;
|
|
92
98
|
}
|
|
93
99
|
if (!code || !state) {
|
|
94
|
-
res.
|
|
100
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
101
|
+
res.end(getErrorHtml("Missing code or state parameter"));
|
|
95
102
|
callbackReject(/* @__PURE__ */ new Error("Missing code or state parameter"));
|
|
96
103
|
return;
|
|
97
104
|
}
|
|
98
|
-
res.
|
|
105
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
106
|
+
res.end(getSuccessHtml());
|
|
99
107
|
callbackResolve({
|
|
100
108
|
code,
|
|
101
109
|
state
|
|
102
110
|
});
|
|
103
111
|
});
|
|
104
|
-
|
|
112
|
+
server.listen(port, () => {
|
|
105
113
|
const shutdown = () => {
|
|
106
114
|
clearTimeout(timeoutId);
|
|
107
115
|
server.close();
|
|
@@ -136,6 +144,8 @@ function getSuccessHtml() {
|
|
|
136
144
|
<!DOCTYPE html>
|
|
137
145
|
<html>
|
|
138
146
|
<head>
|
|
147
|
+
<meta charset="utf-8">
|
|
148
|
+
<link rel="icon" href="https://www.revos.ai/favicons/favicon.ico">
|
|
139
149
|
<title>RevOS CLI - Authentication Successful</title>
|
|
140
150
|
<style>
|
|
141
151
|
body {
|
|
@@ -177,6 +187,8 @@ function getErrorHtml(message) {
|
|
|
177
187
|
<!DOCTYPE html>
|
|
178
188
|
<html>
|
|
179
189
|
<head>
|
|
190
|
+
<meta charset="utf-8">
|
|
191
|
+
<link rel="icon" href="https://www.revos.ai/favicons/favicon.ico">
|
|
180
192
|
<title>RevOS CLI - Authentication Failed</title>
|
|
181
193
|
<style>
|
|
182
194
|
body {
|
|
@@ -330,17 +342,26 @@ async function refreshAccessToken(refreshToken) {
|
|
|
330
342
|
//#endregion
|
|
331
343
|
//#region src/core/config.ts
|
|
332
344
|
const DEFAULT_API_URL = "https://api.revos.ai";
|
|
333
|
-
function getStoredAccessToken() {
|
|
345
|
+
async function getStoredAccessToken() {
|
|
334
346
|
const credentials = loadCredentials();
|
|
335
347
|
if (!credentials) return null;
|
|
336
|
-
if (isTokenExpired(credentials)) return
|
|
337
|
-
|
|
348
|
+
if (!isTokenExpired(credentials)) return credentials.accessToken;
|
|
349
|
+
if (!credentials.refreshToken) return null;
|
|
350
|
+
try {
|
|
351
|
+
const tokenResponse = await refreshAccessToken(credentials.refreshToken);
|
|
352
|
+
const newCredentials = tokenResponseToCredentials(tokenResponse, await getUserInfo(tokenResponse.access_token));
|
|
353
|
+
newCredentials.organizationId = credentials.organizationId;
|
|
354
|
+
saveCredentials(newCredentials);
|
|
355
|
+
return newCredentials.accessToken;
|
|
356
|
+
} catch {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
338
359
|
}
|
|
339
360
|
function getStoredOrganizationId() {
|
|
340
361
|
return loadCredentials()?.organizationId ?? void 0;
|
|
341
362
|
}
|
|
342
|
-
function getConfig() {
|
|
343
|
-
const token = process.env.REVOS_TOKEN || getStoredAccessToken();
|
|
363
|
+
async function getConfig() {
|
|
364
|
+
const token = process.env.REVOS_TOKEN || await getStoredAccessToken();
|
|
344
365
|
if (!token) throw new Error("Not authenticated. Run 'revos auth login' or set REVOS_TOKEN environment variable.");
|
|
345
366
|
return {
|
|
346
367
|
apiUrl: process.env.REVOS_API_URL || DEFAULT_API_URL,
|
|
@@ -780,15 +801,41 @@ async function selectOrganization(organizations) {
|
|
|
780
801
|
}
|
|
781
802
|
//#endregion
|
|
782
803
|
//#region src/core/services/init.service.ts
|
|
783
|
-
var InitService = class {
|
|
804
|
+
var InitService = class InitService {
|
|
805
|
+
static PROJECT_DIRS = [
|
|
806
|
+
".devcontainer",
|
|
807
|
+
".claude/skills/explore-lakehouse",
|
|
808
|
+
".claude/skills/create-semantic-model",
|
|
809
|
+
"dbt/models/bronze",
|
|
810
|
+
"dbt/models/silver",
|
|
811
|
+
"dbt/models/gold",
|
|
812
|
+
"semantic/cubes"
|
|
813
|
+
];
|
|
814
|
+
static PROJECT_FILES = [
|
|
815
|
+
".devcontainer/devcontainer.json",
|
|
816
|
+
".devcontainer/Dockerfile",
|
|
817
|
+
".devcontainer/setup.sh",
|
|
818
|
+
".gitignore",
|
|
819
|
+
"README.md",
|
|
820
|
+
"dbt/profiles.yml",
|
|
821
|
+
"dbt/dbt_project.yml",
|
|
822
|
+
"CLAUDE.md",
|
|
823
|
+
"AGENTS.md",
|
|
824
|
+
".claude/skills/explore-lakehouse/SKILL.md",
|
|
825
|
+
".claude/skills/create-semantic-model/SKILL.md",
|
|
826
|
+
"dbt/models/bronze/.gitkeep",
|
|
827
|
+
"dbt/models/silver/.gitkeep",
|
|
828
|
+
"dbt/models/gold/.gitkeep",
|
|
829
|
+
"semantic/cubes/.gitkeep"
|
|
830
|
+
];
|
|
784
831
|
constructor(templatesDir) {
|
|
785
832
|
this.templatesDir = templatesDir;
|
|
786
833
|
}
|
|
787
834
|
async run(options) {
|
|
788
|
-
const { projectName, targetDir, apiUrl, token } = options;
|
|
835
|
+
const { projectName, targetDir, apiUrl, token, organizationId } = options;
|
|
789
836
|
const projectDir = path.join(targetDir, projectName);
|
|
790
837
|
const projectSlug = projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
791
|
-
const org = await this.resolveOrganization(apiUrl, token);
|
|
838
|
+
const org = options.organization ?? await this.resolveOrganization(apiUrl, token, organizationId);
|
|
792
839
|
const gcpProjectId = await this.downloadGcpKey(apiUrl, token, org, projectSlug);
|
|
793
840
|
const resolvedOrg = {
|
|
794
841
|
...org,
|
|
@@ -798,14 +845,19 @@ var InitService = class {
|
|
|
798
845
|
return {
|
|
799
846
|
projectDir,
|
|
800
847
|
organization: resolvedOrg,
|
|
801
|
-
createdFiles: this.scaffold(projectDir, projectName, projectSlug, resolvedOrg)
|
|
848
|
+
createdFiles: this.scaffold(projectDir, projectName, projectSlug, resolvedOrg, options.allowExistingDir)
|
|
802
849
|
};
|
|
803
850
|
}
|
|
804
|
-
async resolveOrganization(apiUrl, token) {
|
|
851
|
+
async resolveOrganization(apiUrl, token, organizationId) {
|
|
805
852
|
const api = createApiClient({
|
|
806
853
|
apiUrl,
|
|
807
854
|
token
|
|
808
855
|
});
|
|
856
|
+
if (organizationId) {
|
|
857
|
+
const full = unwrap(await api.organizations.get({ id: organizationId }));
|
|
858
|
+
if (!full.bqDataset) throw new Error("Organization is missing BigQuery dataset configuration. Contact support.");
|
|
859
|
+
return full;
|
|
860
|
+
}
|
|
809
861
|
const orgs = unwrap(await api.organizations.list()) ?? [];
|
|
810
862
|
if (orgs.length === 0) throw new Error("No organizations found for this account.");
|
|
811
863
|
const selected = orgs.length === 1 ? orgs[0] : await selectOrganization(orgs);
|
|
@@ -831,41 +883,50 @@ var InitService = class {
|
|
|
831
883
|
});
|
|
832
884
|
return JSON.parse(keyJson).project_id ?? "";
|
|
833
885
|
}
|
|
834
|
-
|
|
835
|
-
|
|
886
|
+
dryRun(projectName, targetDir) {
|
|
887
|
+
return {
|
|
888
|
+
projectDir: path.join(targetDir, projectName),
|
|
889
|
+
dirs: InitService.PROJECT_DIRS,
|
|
890
|
+
files: InitService.PROJECT_FILES
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
scaffold(projectDir, projectName, projectSlug, org, allowExistingDir) {
|
|
894
|
+
if (fs.existsSync(projectDir) && !allowExistingDir) throw new Error(`Directory "${projectName}" already exists. Remove it or choose a different name.`);
|
|
836
895
|
const created = [];
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
".claude/skills",
|
|
841
|
-
"dbt/models/bronze",
|
|
842
|
-
"dbt/models/silver",
|
|
843
|
-
"dbt/models/gold",
|
|
844
|
-
"semantic/cubes",
|
|
845
|
-
"semantic/views"
|
|
846
|
-
]) fs.mkdirSync(path.join(projectDir, dir), { recursive: true });
|
|
896
|
+
const dirs = InitService.PROJECT_DIRS;
|
|
897
|
+
for (const dir of dirs) fs.mkdirSync(path.join(projectDir, dir), { recursive: true });
|
|
898
|
+
const dbtName = projectName.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
847
899
|
const files = {
|
|
848
|
-
".devcontainer/devcontainer.json": this.
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
"
|
|
900
|
+
".devcontainer/devcontainer.json": this.renderTemplate(".devcontainer/devcontainer.json", {
|
|
901
|
+
projectName,
|
|
902
|
+
projectSlug,
|
|
903
|
+
bqProjectId: org.bqProjectId || "",
|
|
904
|
+
bqDataset: org.bqDataset,
|
|
905
|
+
organizationId: org.id
|
|
906
|
+
}),
|
|
907
|
+
".devcontainer/Dockerfile": this.renderTemplate(".devcontainer/Dockerfile", {}),
|
|
908
|
+
".devcontainer/setup.sh": this.renderTemplate(".devcontainer/setup.sh", {}),
|
|
909
|
+
".gitignore": this.renderTemplate(".gitignore", {}),
|
|
910
|
+
"README.md": this.renderTemplate("README.md", {
|
|
856
911
|
projectName,
|
|
857
912
|
orgName: org.name
|
|
858
913
|
}),
|
|
914
|
+
"dbt/profiles.yml": this.renderTemplate("dbt/profiles.yml", { bqLocation: org.bqLocation ?? "europe-west3" }),
|
|
915
|
+
"dbt/dbt_project.yml": this.renderTemplate("dbt/dbt_project.yml", { dbtName }),
|
|
916
|
+
"CLAUDE.md": this.renderTemplate("CLAUDE.md", {}),
|
|
859
917
|
"AGENTS.md": this.renderTemplate("AGENTS.md", {
|
|
860
918
|
projectName,
|
|
861
919
|
orgName: org.name
|
|
862
920
|
}),
|
|
863
|
-
".claude/skills
|
|
921
|
+
".claude/skills/explore-lakehouse/SKILL.md": this.renderTemplate("skills/explore-lakehouse/SKILL.md", {
|
|
922
|
+
bqProjectId: org.bqProjectId || "",
|
|
923
|
+
bqDataset: org.bqDataset
|
|
924
|
+
}),
|
|
925
|
+
".claude/skills/create-semantic-model/SKILL.md": this.renderTemplate("skills/create-semantic-model/SKILL.md", {}),
|
|
864
926
|
"dbt/models/bronze/.gitkeep": "",
|
|
865
927
|
"dbt/models/silver/.gitkeep": "",
|
|
866
928
|
"dbt/models/gold/.gitkeep": "",
|
|
867
|
-
"semantic/cubes/.gitkeep": ""
|
|
868
|
-
"semantic/views/.gitkeep": ""
|
|
929
|
+
"semantic/cubes/.gitkeep": ""
|
|
869
930
|
};
|
|
870
931
|
for (const [rel, content] of Object.entries(files)) {
|
|
871
932
|
const full = path.join(projectDir, rel);
|
|
@@ -875,186 +936,13 @@ var InitService = class {
|
|
|
875
936
|
}
|
|
876
937
|
return created;
|
|
877
938
|
}
|
|
878
|
-
renderDbtProfiles(projectName, org) {
|
|
879
|
-
return [
|
|
880
|
-
`${projectName.replace(/[^a-zA-Z0-9_]/g, "_")}:`,
|
|
881
|
-
` target: dev`,
|
|
882
|
-
` outputs:`,
|
|
883
|
-
` dev:`,
|
|
884
|
-
` type: bigquery`,
|
|
885
|
-
` method: service-account`,
|
|
886
|
-
` project: "{{ env_var('GOOGLE_CLOUD_PROJECT') }}"`,
|
|
887
|
-
` dataset: "{{ env_var('REVOS_BQ_DATASET') }}"`,
|
|
888
|
-
` keyfile: "{{ env_var('GOOGLE_APPLICATION_CREDENTIALS') }}"`,
|
|
889
|
-
` threads: 4`,
|
|
890
|
-
` timeout_seconds: 300`,
|
|
891
|
-
` location: ${org.bqLocation ?? "europe-west3"}`,
|
|
892
|
-
``
|
|
893
|
-
].join("\n");
|
|
894
|
-
}
|
|
895
|
-
renderDbtProject(projectName) {
|
|
896
|
-
const dbtName = projectName.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
897
|
-
return [
|
|
898
|
-
`name: "${dbtName}"`,
|
|
899
|
-
`version: "1.0.0"`,
|
|
900
|
-
``,
|
|
901
|
-
`profile: "${dbtName}"`,
|
|
902
|
-
``,
|
|
903
|
-
`model-paths: ["models"]`,
|
|
904
|
-
`seed-paths: ["seeds"]`,
|
|
905
|
-
`test-paths: ["tests"]`,
|
|
906
|
-
`analysis-paths: ["analyses"]`,
|
|
907
|
-
`macro-paths: ["macros"]`,
|
|
908
|
-
``,
|
|
909
|
-
`target-path: "target"`,
|
|
910
|
-
`clean-targets: ["target", "dbt_packages"]`,
|
|
911
|
-
``,
|
|
912
|
-
`models:`,
|
|
913
|
-
` ${dbtName}:`,
|
|
914
|
-
` bronze:`,
|
|
915
|
-
` +materialized: table`,
|
|
916
|
-
` silver:`,
|
|
917
|
-
` +materialized: table`,
|
|
918
|
-
` gold:`,
|
|
919
|
-
` +materialized: table`,
|
|
920
|
-
``
|
|
921
|
-
].join("\n");
|
|
922
|
-
}
|
|
923
|
-
renderRevosEnv(org) {
|
|
924
|
-
return [
|
|
925
|
-
`export GOOGLE_CLOUD_PROJECT="${org.bqProjectId || ""}"`,
|
|
926
|
-
`export REVOS_BQ_DATASET="${org.bqDataset}"`,
|
|
927
|
-
``
|
|
928
|
-
].join("\n");
|
|
929
|
-
}
|
|
930
|
-
renderDevcontainer(projectName, projectSlug) {
|
|
931
|
-
const config = {
|
|
932
|
-
name: `${projectName} — RevOS Data Engineer`,
|
|
933
|
-
image: "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
|
|
934
|
-
features: {
|
|
935
|
-
"ghcr.io/devcontainers/features/python:1": { version: "3.11" },
|
|
936
|
-
"ghcr.io/devcontainers/features/node:1": { version: "lts" },
|
|
937
|
-
"ghcr.io/anthropics/devcontainer-features/claude-code:1": {}
|
|
938
|
-
},
|
|
939
|
-
postCreateCommand: "bash .devcontainer/setup.sh",
|
|
940
|
-
mounts: [{
|
|
941
|
-
source: `\${localEnv:HOME}/.revos/${projectSlug}-gsa-creds.json`,
|
|
942
|
-
target: "/tmp/.revos-gsa-creds.json",
|
|
943
|
-
type: "bind"
|
|
944
|
-
}],
|
|
945
|
-
customizations: { vscode: {
|
|
946
|
-
extensions: ["anthropics.claude-code", "innoverio.vscode-dbt-power-user"],
|
|
947
|
-
settings: { "python.defaultInterpreterPath": "/usr/local/python/current/bin/python" }
|
|
948
|
-
} }
|
|
949
|
-
};
|
|
950
|
-
return JSON.stringify(config, null, 2) + "\n";
|
|
951
|
-
}
|
|
952
|
-
renderSetupScript() {
|
|
953
|
-
return [
|
|
954
|
-
"#!/usr/bin/env bash",
|
|
955
|
-
"set -euo pipefail",
|
|
956
|
-
"",
|
|
957
|
-
"# Google Cloud CLI",
|
|
958
|
-
"curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \\",
|
|
959
|
-
" | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg",
|
|
960
|
-
"echo \"deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main\" \\",
|
|
961
|
-
" | sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list",
|
|
962
|
-
"sudo apt-get update -q",
|
|
963
|
-
"sudo apt-get install -y google-cloud-cli",
|
|
964
|
-
"",
|
|
965
|
-
"# dbt (cryptography<47 avoids Illegal instruction on ARM)",
|
|
966
|
-
"pip install --quiet dbt-bigquery \"cryptography<47\"",
|
|
967
|
-
"",
|
|
968
|
-
"# Copy mounted GCP service account key to vscode user's home",
|
|
969
|
-
"if [ -f /tmp/.revos-gsa-creds.json ]; then",
|
|
970
|
-
" mkdir -p \"$HOME/.revos\"",
|
|
971
|
-
" cp /tmp/.revos-gsa-creds.json \"$HOME/.revos/gsa-creds.json\"",
|
|
972
|
-
" chmod 600 \"$HOME/.revos/gsa-creds.json\"",
|
|
973
|
-
"fi",
|
|
974
|
-
"",
|
|
975
|
-
"# Source project-specific env (BQ project, dataset, location)",
|
|
976
|
-
"if [ -f \".revos/env\" ]; then",
|
|
977
|
-
" # shellcheck source=/dev/null",
|
|
978
|
-
" . \".revos/env\"",
|
|
979
|
-
" grep -v \"^#\" \".revos/env\" >> \"$HOME/.bashrc\"",
|
|
980
|
-
"fi",
|
|
981
|
-
"",
|
|
982
|
-
"# Configure gcloud with service account and project",
|
|
983
|
-
"GCP_KEY=\"$HOME/.revos/gsa-creds.json\"",
|
|
984
|
-
"if [ -f \"$GCP_KEY\" ]; then",
|
|
985
|
-
" gcloud auth activate-service-account --key-file=\"$GCP_KEY\"",
|
|
986
|
-
" echo \"export GOOGLE_APPLICATION_CREDENTIALS=\\\"$GCP_KEY\\\"\" >> \"$HOME/.bashrc\"",
|
|
987
|
-
"fi",
|
|
988
|
-
"if [ -n \"${GOOGLE_CLOUD_PROJECT:-}\" ]; then",
|
|
989
|
-
" gcloud config set project \"$GOOGLE_CLOUD_PROJECT\"",
|
|
990
|
-
"fi",
|
|
991
|
-
"",
|
|
992
|
-
"# Symlink dbt profiles so `dbt run` works without --profiles-dir",
|
|
993
|
-
"mkdir -p \"$HOME/.dbt\"",
|
|
994
|
-
"ln -sf \"$(pwd)/dbt/profiles.yml\" \"$HOME/.dbt/profiles.yml\"",
|
|
995
|
-
""
|
|
996
|
-
].join("\n");
|
|
997
|
-
}
|
|
998
|
-
renderGitignore() {
|
|
999
|
-
return [
|
|
1000
|
-
"# dbt",
|
|
1001
|
-
"dbt/target/",
|
|
1002
|
-
"dbt/logs/",
|
|
1003
|
-
"dbt/dbt_packages/",
|
|
1004
|
-
"dbt/profiles.yml",
|
|
1005
|
-
"",
|
|
1006
|
-
"# Python",
|
|
1007
|
-
"__pycache__/",
|
|
1008
|
-
"*.py[cod]",
|
|
1009
|
-
".venv/",
|
|
1010
|
-
"venv/",
|
|
1011
|
-
"",
|
|
1012
|
-
"# Node",
|
|
1013
|
-
"node_modules/",
|
|
1014
|
-
"",
|
|
1015
|
-
"# Local env",
|
|
1016
|
-
".env",
|
|
1017
|
-
".env.*",
|
|
1018
|
-
"!.env.example",
|
|
1019
|
-
""
|
|
1020
|
-
].join("\n");
|
|
1021
|
-
}
|
|
1022
939
|
renderTemplate(relativePath, vars) {
|
|
1023
940
|
const filePath = path.join(this.templatesDir, relativePath);
|
|
1024
941
|
if (!fs.existsSync(filePath)) throw new Error(`Template "${relativePath}" not found — try reinstalling the revos CLI`);
|
|
1025
942
|
let content = fs.readFileSync(filePath, "utf-8");
|
|
1026
|
-
for (const [key, value] of Object.entries(vars)) content = content.replaceAll(
|
|
943
|
+
for (const [key, value] of Object.entries(vars)) content = content.replaceAll(`<%=${key}%>`, value);
|
|
1027
944
|
return content;
|
|
1028
945
|
}
|
|
1029
|
-
renderReadme(projectName, org) {
|
|
1030
|
-
return [
|
|
1031
|
-
`# ${projectName}`,
|
|
1032
|
-
``,
|
|
1033
|
-
`RevOS data engineering project for **${org.name}**.`,
|
|
1034
|
-
``,
|
|
1035
|
-
`## Getting started`,
|
|
1036
|
-
``,
|
|
1037
|
-
`1. Open this folder in VS Code and click **Reopen in Container** — the Dev Container installs all tools automatically.`,
|
|
1038
|
-
`2. Inside the container, verify your setup:`,
|
|
1039
|
-
` \`\`\`bash`,
|
|
1040
|
-
` dbt --version`,
|
|
1041
|
-
` bq version`,
|
|
1042
|
-
` \`\`\``,
|
|
1043
|
-
``,
|
|
1044
|
-
`## Project structure`,
|
|
1045
|
-
``,
|
|
1046
|
-
`\`\`\``,
|
|
1047
|
-
`dbt/models/`,
|
|
1048
|
-
` bronze/ # raw ingested data`,
|
|
1049
|
-
` silver/ # cleaned & conformed`,
|
|
1050
|
-
` gold/ # business-ready marts`,
|
|
1051
|
-
`semantic/`,
|
|
1052
|
-
` cubes/ # Cube.dev cube definitions`,
|
|
1053
|
-
` views/ # Cube.dev view definitions`,
|
|
1054
|
-
`\`\`\``,
|
|
1055
|
-
``
|
|
1056
|
-
].join("\n");
|
|
1057
|
-
}
|
|
1058
946
|
};
|
|
1059
947
|
//#endregion
|
|
1060
948
|
export { getCredentialsPath as A, getUserInfo as C, tokenResponseToCredentials as D, setClerkEnv as E, loadCredentials as M, saveCredentials as N, startOAuthServer as O, ApiError as P, generatePKCEChallenge as S, setClerkConfig as T, isContentEqual as _, PullService as a, buildAuthorizationUrl as b, loadOverlayFile as c, loadOverlaysFromDir as d, saveOverlayToFile as f, formatError as g, findRemoteOnlyOverlays as h, StatusService as i, isTokenExpired as j, deleteCredentials as k, loadOverlays as l, unwrap as m, selectOrganization as n, PushService as o, createApiClient as p, DiffService as r, getLocalOverlayNames as s, InitService as t, loadOverlaysByNames as u, sanitizeFileName as v, refreshAccessToken as w, exchangeCodeForTokens as x, getConfig as y };
|
|
@@ -13,7 +13,7 @@ declare class ApiError extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/core/config.d.ts
|
|
16
|
-
declare function getConfig(): Config
|
|
16
|
+
declare function getConfig(): Promise<Config>;
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/core/auth/credentials-store.d.ts
|
|
19
19
|
declare function getCredentialsPath(): string;
|
|
@@ -161,6 +161,9 @@ interface InitOptions {
|
|
|
161
161
|
targetDir: string;
|
|
162
162
|
apiUrl: string;
|
|
163
163
|
token: string;
|
|
164
|
+
organizationId?: string;
|
|
165
|
+
organization?: OrganizationInfo;
|
|
166
|
+
allowExistingDir?: boolean;
|
|
164
167
|
}
|
|
165
168
|
interface InitResult {
|
|
166
169
|
projectDir: string;
|
|
@@ -169,19 +172,19 @@ interface InitResult {
|
|
|
169
172
|
}
|
|
170
173
|
declare class InitService {
|
|
171
174
|
private readonly templatesDir;
|
|
175
|
+
private static readonly PROJECT_DIRS;
|
|
176
|
+
private static readonly PROJECT_FILES;
|
|
172
177
|
constructor(templatesDir: string);
|
|
173
178
|
run(options: InitOptions): Promise<InitResult>;
|
|
174
|
-
|
|
179
|
+
resolveOrganization(apiUrl: string, token: string, organizationId?: string): Promise<OrganizationInfo>;
|
|
175
180
|
private downloadGcpKey;
|
|
181
|
+
dryRun(projectName: string, targetDir: string): {
|
|
182
|
+
projectDir: string;
|
|
183
|
+
dirs: readonly string[];
|
|
184
|
+
files: readonly string[];
|
|
185
|
+
};
|
|
176
186
|
private scaffold;
|
|
177
|
-
private renderDbtProfiles;
|
|
178
|
-
private renderDbtProject;
|
|
179
|
-
private renderRevosEnv;
|
|
180
|
-
private renderDevcontainer;
|
|
181
|
-
private renderSetupScript;
|
|
182
|
-
private renderGitignore;
|
|
183
187
|
private renderTemplate;
|
|
184
|
-
private renderReadme;
|
|
185
188
|
}
|
|
186
189
|
//#endregion
|
|
187
190
|
export { ClerkEnv as A, tokenResponseToCredentials as B, LoadedOverlay as C, loadOverlaysByNames as D, loadOverlays as E, generatePKCEChallenge as F, isTokenExpired as G, startOAuthServer as H, getUserInfo as I, getConfig as J, loadCredentials as K, refreshAccessToken as L, PKCEChallenge as M, buildAuthorizationUrl as N, loadOverlaysFromDir as O, exchangeCodeForTokens as P, setClerkConfig as R, sanitizeFileName as S, loadOverlayFile as T, deleteCredentials as U, OAuthServerResult as V, getCredentialsPath as W, ApiError as Y, createApiClient as _, DiffOptions as a, formatError as b, StatusOptions as c, PullOptions as d, PullService as f, ApiClient as g, PushService as h, DiffContext as i, ClerkOAuthConfig as j, saveOverlayToFile as k, StatusService as l, PushOptions as m, InitResult as n, DiffService as o, PushContext as p, saveCredentials as q, InitService as r, StatusContext as s, InitOptions as t, PullContext as u, unwrap as v, getLocalOverlayNames as w, isContentEqual as x, findRemoteOnlyOverlays as y, setClerkEnv as z };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as DiffEntry, c as OverlayStatusInfo, d as StatusResult, f as SyncStatus, i as DiffChange, l as PullResult, n as CubeDefinition, o as DiffResult, r as CubeOverlay, s as OverlayFile, t as Config, u as PushResult } from "./types-DZssnweO.mjs";
|
|
2
|
-
import { A as ClerkEnv, B as tokenResponseToCredentials, C as LoadedOverlay, D as loadOverlaysByNames, E as loadOverlays, F as generatePKCEChallenge, G as isTokenExpired, H as startOAuthServer, I as getUserInfo, J as getConfig, K as loadCredentials, L as refreshAccessToken, M as PKCEChallenge, N as buildAuthorizationUrl, O as loadOverlaysFromDir, P as exchangeCodeForTokens, R as setClerkConfig, S as sanitizeFileName, T as loadOverlayFile, U as deleteCredentials, V as OAuthServerResult, W as getCredentialsPath, Y as ApiError, _ as createApiClient, a as DiffOptions, b as formatError, c as StatusOptions, d as PullOptions, f as PullService, g as ApiClient, h as PushService, i as DiffContext, j as ClerkOAuthConfig, k as saveOverlayToFile, l as StatusService, m as PushOptions, n as InitResult, o as DiffService, p as PushContext, q as saveCredentials, r as InitService, s as StatusContext, t as InitOptions, u as PullContext, v as unwrap, w as getLocalOverlayNames, x as isContentEqual, y as findRemoteOnlyOverlays, z as setClerkEnv } from "./index-
|
|
2
|
+
import { A as ClerkEnv, B as tokenResponseToCredentials, C as LoadedOverlay, D as loadOverlaysByNames, E as loadOverlays, F as generatePKCEChallenge, G as isTokenExpired, H as startOAuthServer, I as getUserInfo, J as getConfig, K as loadCredentials, L as refreshAccessToken, M as PKCEChallenge, N as buildAuthorizationUrl, O as loadOverlaysFromDir, P as exchangeCodeForTokens, R as setClerkConfig, S as sanitizeFileName, T as loadOverlayFile, U as deleteCredentials, V as OAuthServerResult, W as getCredentialsPath, Y as ApiError, _ as createApiClient, a as DiffOptions, b as formatError, c as StatusOptions, d as PullOptions, f as PullService, g as ApiClient, h as PushService, i as DiffContext, j as ClerkOAuthConfig, k as saveOverlayToFile, l as StatusService, m as PushOptions, n as InitResult, o as DiffService, p as PushContext, q as saveCredentials, r as InitService, s as StatusContext, t as InitOptions, u as PullContext, v as unwrap, w as getLocalOverlayNames, x as isContentEqual, y as findRemoteOnlyOverlays, z as setClerkEnv } from "./index-DH6vy050.mjs";
|
|
3
3
|
import { a as OrgListResult, c as StoredCredentials, i as OAuthCallbackResult, l as TokenResponse, n as AuthStatusInfo, o as OrgSwitchResult, r as ClerkUserInfo, s as OrganizationInfo, t as AuthResult } from "./types-DsQtGF-c.mjs";
|
|
4
4
|
export { ApiClient, ApiError, AuthResult, AuthStatusInfo, ClerkEnv, ClerkOAuthConfig, ClerkUserInfo, Config, CubeDefinition, CubeOverlay, DiffChange, DiffContext, DiffEntry, DiffOptions, DiffResult, DiffService, InitOptions, InitResult, InitService, LoadedOverlay, OAuthCallbackResult, OAuthServerResult, OrgListResult, OrgSwitchResult, OrganizationInfo, OverlayFile, OverlayStatusInfo, PKCEChallenge, PullContext, PullOptions, PullResult, PullService, PushContext, PushOptions, PushResult, PushService, StatusContext, StatusOptions, StatusResult, StatusService, StoredCredentials, SyncStatus, TokenResponse, buildAuthorizationUrl, createApiClient, deleteCredentials, exchangeCodeForTokens, findRemoteOnlyOverlays, formatError, generatePKCEChallenge, getConfig, getCredentialsPath, getLocalOverlayNames, getUserInfo, isContentEqual, isTokenExpired, loadCredentials, loadOverlayFile, loadOverlays, loadOverlaysByNames, loadOverlaysFromDir, refreshAccessToken, sanitizeFileName, saveCredentials, saveOverlayToFile, setClerkConfig, setClerkEnv, startOAuthServer, tokenResponseToCredentials, unwrap };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as getCredentialsPath, C as getUserInfo, D as tokenResponseToCredentials, E as setClerkEnv, M as loadCredentials, N as saveCredentials, O as startOAuthServer, P as ApiError, S as generatePKCEChallenge, T as setClerkConfig, _ as isContentEqual, a as PullService, b as buildAuthorizationUrl, c as loadOverlayFile, d as loadOverlaysFromDir, f as saveOverlayToFile, g as formatError, h as findRemoteOnlyOverlays, i as StatusService, j as isTokenExpired, k as deleteCredentials, l as loadOverlays, m as unwrap, o as PushService, p as createApiClient, r as DiffService, s as getLocalOverlayNames, t as InitService, u as loadOverlaysByNames, v as sanitizeFileName, w as refreshAccessToken, x as exchangeCodeForTokens, y as getConfig } from "./core-
|
|
1
|
+
import { A as getCredentialsPath, C as getUserInfo, D as tokenResponseToCredentials, E as setClerkEnv, M as loadCredentials, N as saveCredentials, O as startOAuthServer, P as ApiError, S as generatePKCEChallenge, T as setClerkConfig, _ as isContentEqual, a as PullService, b as buildAuthorizationUrl, c as loadOverlayFile, d as loadOverlaysFromDir, f as saveOverlayToFile, g as formatError, h as findRemoteOnlyOverlays, i as StatusService, j as isTokenExpired, k as deleteCredentials, l as loadOverlays, m as unwrap, o as PushService, p as createApiClient, r as DiffService, s as getLocalOverlayNames, t as InitService, u as loadOverlaysByNames, v as sanitizeFileName, w as refreshAccessToken, x as exchangeCodeForTokens, y as getConfig } from "./core-EJgxP-x5.mjs";
|
|
2
2
|
export { ApiError, DiffService, InitService, PullService, PushService, StatusService, buildAuthorizationUrl, createApiClient, deleteCredentials, exchangeCodeForTokens, findRemoteOnlyOverlays, formatError, generatePKCEChallenge, getConfig, getCredentialsPath, getLocalOverlayNames, getUserInfo, isContentEqual, isTokenExpired, loadCredentials, loadOverlayFile, loadOverlays, loadOverlaysByNames, loadOverlaysFromDir, refreshAccessToken, sanitizeFileName, saveCredentials, saveOverlayToFile, setClerkConfig, setClerkEnv, startOAuthServer, tokenResponseToCredentials, unwrap };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM mcr.microsoft.com/devcontainers/python:3.13
|
|
2
|
+
|
|
3
|
+
# Pre-install dbt so it's available before VS Code extensions activate
|
|
4
|
+
# (cryptography<47 avoids Illegal instruction on ARM)
|
|
5
|
+
RUN pip install --quiet dbt-bigquery "cryptography<47"
|
|
6
|
+
|
|
7
|
+
# Google Cloud CLI
|
|
8
|
+
RUN curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
|
|
9
|
+
| gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg \
|
|
10
|
+
&& echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \
|
|
11
|
+
> /etc/apt/sources.list.d/google-cloud-sdk.list \
|
|
12
|
+
&& apt-get update -q \
|
|
13
|
+
&& apt-get install -y --no-install-recommends google-cloud-cli \
|
|
14
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%=projectName%> — RevOS Data Engineer",
|
|
3
|
+
"build": {
|
|
4
|
+
"dockerfile": "Dockerfile"
|
|
5
|
+
},
|
|
6
|
+
"features": {
|
|
7
|
+
"ghcr.io/devcontainers/features/node:1": {
|
|
8
|
+
"version": "lts"
|
|
9
|
+
},
|
|
10
|
+
"ghcr.io/anthropics/devcontainer-features/claude-code:1": {}
|
|
11
|
+
},
|
|
12
|
+
"otherPortsAttributes": {
|
|
13
|
+
"onAutoForward": "notify"
|
|
14
|
+
},
|
|
15
|
+
"containerEnv": {
|
|
16
|
+
"DBT_PROJECT_DIR": "${containerWorkspaceFolder}/dbt",
|
|
17
|
+
"GOOGLE_APPLICATION_CREDENTIALS": "/home/vscode/.revos/gsa-creds.json",
|
|
18
|
+
"GOOGLE_CLOUD_PROJECT": "<%=bqProjectId%>",
|
|
19
|
+
"REVOS_BQ_DATASET": "<%=bqDataset%>",
|
|
20
|
+
"REVOS_ORG_ID": "<%=organizationId%>"
|
|
21
|
+
},
|
|
22
|
+
"postCreateCommand": "bash .devcontainer/setup.sh",
|
|
23
|
+
"mounts": [
|
|
24
|
+
{
|
|
25
|
+
"source": "${localEnv:HOME}/.revos/<%=projectSlug%>-gsa-creds.json",
|
|
26
|
+
"target": "/tmp/.revos-gsa-creds.json",
|
|
27
|
+
"type": "bind"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"source": "${localEnv:HOME}/.revos/credentials.json",
|
|
31
|
+
"target": "/tmp/.revos-credentials.json",
|
|
32
|
+
"type": "bind"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"source": "claude-code-config",
|
|
36
|
+
"target": "/home/vscode/.claude",
|
|
37
|
+
"type": "volume"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"customizations": {
|
|
41
|
+
"vscode": {
|
|
42
|
+
"extensions": [
|
|
43
|
+
"anthropics.claude-code",
|
|
44
|
+
"innoverio.vscode-dbt-power-user"
|
|
45
|
+
],
|
|
46
|
+
"settings": {
|
|
47
|
+
"files.associations": {
|
|
48
|
+
"*.sql": "jinja-sql",
|
|
49
|
+
"*.yml": "yaml"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Copy mounted GCP service account key to vscode user's home
|
|
5
|
+
if [ -f /tmp/.revos-gsa-creds.json ]; then
|
|
6
|
+
mkdir -p "$HOME/.revos"
|
|
7
|
+
cp /tmp/.revos-gsa-creds.json "$HOME/.revos/gsa-creds.json"
|
|
8
|
+
chmod 600 "$HOME/.revos/gsa-creds.json"
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
# Copy mounted RevOS CLI credentials so revos commands work without re-login
|
|
12
|
+
if [ -f /tmp/.revos-credentials.json ]; then
|
|
13
|
+
mkdir -p "$HOME/.revos"
|
|
14
|
+
cp /tmp/.revos-credentials.json "$HOME/.revos/credentials.json"
|
|
15
|
+
chmod 600 "$HOME/.revos/credentials.json"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Configure gcloud with service account and project
|
|
19
|
+
GCP_KEY="$HOME/.revos/gsa-creds.json"
|
|
20
|
+
if [ -f "$GCP_KEY" ]; then
|
|
21
|
+
gcloud auth activate-service-account --key-file="$GCP_KEY"
|
|
22
|
+
fi
|
|
23
|
+
if [ -n "${GOOGLE_CLOUD_PROJECT:-}" ]; then
|
|
24
|
+
gcloud config set project "$GOOGLE_CLOUD_PROJECT"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Install RevOS CLI
|
|
28
|
+
npm install -g @revos/cli
|
|
29
|
+
|
|
30
|
+
# Symlink dbt profiles so `dbt run` works without --profiles-dir
|
|
31
|
+
mkdir -p "$HOME/.dbt"
|
|
32
|
+
ln -sf "$(pwd)/dbt/profiles.yml" "$HOME/.dbt/profiles.yml"
|
package/dist/templates/AGENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# <%=projectName%> — AI Companion Guide
|
|
2
2
|
|
|
3
|
-
This is a RevOS data engineering project for
|
|
3
|
+
This is a RevOS data engineering project for **<%=orgName%>** organization.
|
|
4
4
|
|
|
5
5
|
## Project Structure
|
|
6
6
|
|
|
@@ -8,7 +8,6 @@ This is a RevOS data engineering project for **{{orgName}}**.
|
|
|
8
8
|
- `dbt/models/silver/` — cleaned & conformed
|
|
9
9
|
- `dbt/models/gold/` — business-ready marts
|
|
10
10
|
- `semantic/cubes/` — Cube.dev cube definitions
|
|
11
|
-
- `semantic/views/` — Cube.dev view definitions
|
|
12
11
|
|
|
13
12
|
## Key Commands
|
|
14
13
|
|
package/dist/templates/CLAUDE.md
CHANGED
|
@@ -1,17 +1 @@
|
|
|
1
|
-
# {{projectName}}
|
|
2
|
-
|
|
3
|
-
RevOS data engineering project for **{{orgName}}**.
|
|
4
|
-
|
|
5
1
|
@AGENTS.md
|
|
6
|
-
|
|
7
|
-
## Project structure
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
dbt/models/
|
|
11
|
-
bronze/ # raw ingested data
|
|
12
|
-
silver/ # cleaned & conformed
|
|
13
|
-
gold/ # business-ready marts
|
|
14
|
-
semantic/
|
|
15
|
-
cubes/ # Cube.dev cube definitions
|
|
16
|
-
views/ # Cube.dev view definitions
|
|
17
|
-
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# <%=projectName%>
|
|
2
|
+
|
|
3
|
+
RevOS data engineering project for **<%=orgName%>** organization.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
1. Open this folder in VS Code and click **Reopen in Container** — the Dev Container installs all tools automatically.
|
|
8
|
+
2. Inside the container, verify your setup:
|
|
9
|
+
```bash
|
|
10
|
+
dbt --version
|
|
11
|
+
bq version
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Project structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
dbt/models/
|
|
18
|
+
bronze/ # raw ingested data
|
|
19
|
+
silver/ # cleaned & conformed
|
|
20
|
+
gold/ # business-ready marts
|
|
21
|
+
semantic/
|
|
22
|
+
cubes/ # Cube.dev cube definitions
|
|
23
|
+
```
|