@urateam/cli 0.1.39 → 0.1.40

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 (45) hide show
  1. package/dist/__tests__/init.test.d.ts +2 -0
  2. package/dist/__tests__/init.test.d.ts.map +1 -0
  3. package/dist/__tests__/init.test.js +65 -0
  4. package/dist/__tests__/init.test.js.map +1 -0
  5. package/dist/__tests__/repo.test.d.ts +2 -0
  6. package/dist/__tests__/repo.test.d.ts.map +1 -0
  7. package/dist/__tests__/repo.test.js +191 -0
  8. package/dist/__tests__/repo.test.js.map +1 -0
  9. package/dist/__tests__/start-user-level-fallback.test.d.ts +2 -0
  10. package/dist/__tests__/start-user-level-fallback.test.d.ts.map +1 -0
  11. package/dist/__tests__/start-user-level-fallback.test.js +191 -0
  12. package/dist/__tests__/start-user-level-fallback.test.js.map +1 -0
  13. package/dist/__tests__/uninstall.test.d.ts +2 -0
  14. package/dist/__tests__/uninstall.test.d.ts.map +1 -0
  15. package/dist/__tests__/uninstall.test.js +51 -0
  16. package/dist/__tests__/uninstall.test.js.map +1 -0
  17. package/dist/__tests__/user-level-config.test.d.ts +2 -0
  18. package/dist/__tests__/user-level-config.test.d.ts.map +1 -0
  19. package/dist/__tests__/user-level-config.test.js +92 -0
  20. package/dist/__tests__/user-level-config.test.js.map +1 -0
  21. package/dist/commands/init.d.ts +11 -0
  22. package/dist/commands/init.d.ts.map +1 -0
  23. package/dist/commands/init.js +27 -0
  24. package/dist/commands/init.js.map +1 -0
  25. package/dist/commands/repo.d.ts +7 -0
  26. package/dist/commands/repo.d.ts.map +1 -0
  27. package/dist/commands/repo.js +126 -0
  28. package/dist/commands/repo.js.map +1 -0
  29. package/dist/commands/start.d.ts.map +1 -1
  30. package/dist/commands/start.js +25 -0
  31. package/dist/commands/start.js.map +1 -1
  32. package/dist/commands/uninstall.d.ts +17 -0
  33. package/dist/commands/uninstall.d.ts.map +1 -0
  34. package/dist/commands/uninstall.js +38 -0
  35. package/dist/commands/uninstall.js.map +1 -0
  36. package/dist/index.js +6 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/lib/build-repo-configs.d.ts.map +1 -1
  39. package/dist/lib/build-repo-configs.js +85 -27
  40. package/dist/lib/build-repo-configs.js.map +1 -1
  41. package/dist/lib/user-level-config.d.ts +70 -0
  42. package/dist/lib/user-level-config.d.ts.map +1 -0
  43. package/dist/lib/user-level-config.js +91 -0
  44. package/dist/lib/user-level-config.js.map +1 -0
  45. package/package.json +4 -3
@@ -1,4 +1,5 @@
1
1
  import { repoPluginsFromEnv } from "./repo-plugins-from-env.js";
2
+ import { readUserLevelConfig } from "./user-level-config.js";
2
3
  function parseCsv(raw) {
3
4
  return raw.split(",").filter(Boolean);
4
5
  }
@@ -24,32 +25,69 @@ function parseCsv(raw) {
24
25
  */
25
26
  export function buildRepoConfigsFromEnv() {
26
27
  const repoConfigs = {};
27
- if (!process.env.REPO_TEAM_ID || !process.env.REPO_URL)
28
+ if (process.env.REPO_TEAM_ID && process.env.REPO_URL) {
29
+ const repoEntry = {
30
+ url: process.env.REPO_URL,
31
+ defaultBranch: process.env.REPO_DEFAULT_BRANCH ?? "main",
32
+ testCommand: process.env.REPO_TEST_CMD ?? "pnpm test",
33
+ buildCommand: process.env.REPO_BUILD_CMD ?? "pnpm build",
34
+ };
35
+ if (process.env.GITHUB_WEBHOOK_SECRET) {
36
+ repoEntry.githubFeedback = {
37
+ autoTrigger: process.env.GITHUB_FEEDBACK_AUTO_TRIGGER !== "false",
38
+ triggerKeyword: process.env.GITHUB_FEEDBACK_TRIGGER_KEYWORD,
39
+ allowedReviewers: process.env.GITHUB_FEEDBACK_ALLOWED_REVIEWERS
40
+ ? parseCsv(process.env.GITHUB_FEEDBACK_ALLOWED_REVIEWERS)
41
+ : undefined,
42
+ botLogins: process.env.GITHUB_FEEDBACK_BOT_LOGINS
43
+ ? parseCsv(process.env.GITHUB_FEEDBACK_BOT_LOGINS)
44
+ : undefined,
45
+ };
46
+ }
47
+ const pluginCfg = repoPluginsFromEnv();
48
+ if (pluginCfg)
49
+ repoEntry.plugins = pluginCfg;
50
+ repoConfigs[process.env.REPO_TEAM_ID] = repoEntry;
51
+ return repoConfigs;
52
+ }
53
+ // User-level fallback: when no REPO_* env vars produced anything, try
54
+ // ~/.urateam/config.json (or $URATEAM_HOME/config.json). This is what
55
+ // makes the `ura init` + `ura repo add` path usable end-to-end without
56
+ // operators having to hand-craft env vars.
57
+ //
58
+ // Read failures (malformed JSON, schema-validation error) bubble up —
59
+ // the operator should see the error explicitly rather than silently get
60
+ // an unconfigured daemon.
61
+ const userConfig = readUserLevelConfig();
62
+ if (!userConfig || userConfig.repos.length === 0)
28
63
  return repoConfigs;
29
- const repoEntry = {
30
- url: process.env.REPO_URL,
31
- defaultBranch: process.env.REPO_DEFAULT_BRANCH ?? "main",
32
- testCommand: process.env.REPO_TEST_CMD ?? "pnpm test",
33
- buildCommand: process.env.REPO_BUILD_CMD ?? "pnpm build",
34
- };
35
- if (process.env.GITHUB_WEBHOOK_SECRET) {
36
- repoEntry.githubFeedback = {
37
- autoTrigger: process.env.GITHUB_FEEDBACK_AUTO_TRIGGER !== "false",
38
- triggerKeyword: process.env.GITHUB_FEEDBACK_TRIGGER_KEYWORD,
39
- allowedReviewers: process.env.GITHUB_FEEDBACK_ALLOWED_REVIEWERS
40
- ? parseCsv(process.env.GITHUB_FEEDBACK_ALLOWED_REVIEWERS)
41
- : undefined,
42
- botLogins: process.env.GITHUB_FEEDBACK_BOT_LOGINS
43
- ? parseCsv(process.env.GITHUB_FEEDBACK_BOT_LOGINS)
44
- : undefined,
64
+ for (const repo of userConfig.repos) {
65
+ const entry = {
66
+ url: repo.url,
67
+ defaultBranch: repo.defaultBranch,
68
+ testCommand: repo.testCommand,
69
+ buildCommand: repo.buildCommand,
70
+ ...(repo.labelPattern && { labelPattern: repo.labelPattern }),
45
71
  };
72
+ // Key by teamId when present (matches the existing env-var schema);
73
+ // fall back to a slug derived from the URL so config.json entries
74
+ // without a teamId still get a stable key.
75
+ const key = repo.teamId ?? deriveKeyFromUrl(repo.url);
76
+ repoConfigs[key] = entry;
46
77
  }
47
- const pluginCfg = repoPluginsFromEnv();
48
- if (pluginCfg)
49
- repoEntry.plugins = pluginCfg;
50
- repoConfigs[process.env.REPO_TEAM_ID] = repoEntry;
51
78
  return repoConfigs;
52
79
  }
80
+ /**
81
+ * Derive a synthetic Record key from a repo URL when the user-level config
82
+ * entry omits `teamId`. Mirrors the slug logic in `commands/repo.ts` but
83
+ * lives here too so this file doesn't import from a sibling that pulls in
84
+ * commander.
85
+ */
86
+ function deriveKeyFromUrl(url) {
87
+ const stripped = url.replace(/\.git$/, "");
88
+ const last = stripped.split(/[/:]/).filter(Boolean).pop() ?? "repo";
89
+ return last.replace(/[^A-Za-z0-9._-]/g, "-");
90
+ }
53
91
  /**
54
92
  * Fail-fast guard: exits 1 with a clear operator-actionable error if
55
93
  * `repoConfigs` is empty.
@@ -62,12 +100,32 @@ export function buildRepoConfigsFromEnv() {
62
100
  export function requireRepoConfigs(repoConfigs, command) {
63
101
  if (Object.keys(repoConfigs).length > 0)
64
102
  return;
65
- console.error("Error: no repoConfigs could be built from environment variables.\n" +
66
- "Set REPO_TEAM_ID and REPO_URL in .urateam/.env and restart.\n" +
67
- "Example:\n" +
68
- " REPO_TEAM_ID=<your Linear team UUID — usually the same as LINEAR_TEAM_ID>\n" +
69
- ` REPO_URL=https://github.com/org/repo\n` +
70
- `\n(command: ${command})\n`);
103
+ // Branch the error message based on whether `ura init` has been run.
104
+ // Users on the user-level install path who forgot `ura repo add` need
105
+ // very different advice than project-level operators who forgot the
106
+ // env vars.
107
+ const userConfig = readUserLevelConfig();
108
+ if (userConfig) {
109
+ // `ura init` has run; they just need to add a repo.
110
+ console.error(`Error: no repos configured in ${process.env.URATEAM_HOME ?? "~/.urateam"}/config.json.\n` +
111
+ "Run 'ura repo add <url> [--team <linear-team-id>]' to register a repo.\n" +
112
+ "Example:\n" +
113
+ " ura repo add https://github.com/org/repo.git --team team-uuid\n" +
114
+ `\n(command: ${command})\n`);
115
+ }
116
+ else {
117
+ console.error("Error: no repoConfigs could be built.\n" +
118
+ "Two install paths exist:\n\n" +
119
+ " Project-level (sidecar, env-var-driven):\n" +
120
+ " Set REPO_TEAM_ID and REPO_URL in .urateam/.env and restart.\n" +
121
+ " Example:\n" +
122
+ " REPO_TEAM_ID=<your Linear team UUID — usually the same as LINEAR_TEAM_ID>\n" +
123
+ " REPO_URL=https://github.com/org/repo\n\n" +
124
+ " User-level (~/.urateam config-file-driven):\n" +
125
+ " Run 'ura init' then 'ura repo add <url> --team <id>'.\n" +
126
+ " See deploy/USER_LEVEL_INSTALL.md.\n" +
127
+ `\n(command: ${command})\n`);
128
+ }
71
129
  process.exit(1);
72
130
  }
73
131
  //# sourceMappingURL=build-repo-configs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-repo-configs.js","sourceRoot":"","sources":["../../src/lib/build-repo-configs.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,WAAW,GAA+B,EAAE,CAAC;IACnD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ;QAAE,OAAO,WAAW,CAAC;IAE3E,MAAM,SAAS,GAAe;QAC5B,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ;QACzB,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,MAAM;QACxD,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,WAAW;QACrD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,YAAY;KACzD,CAAC;IAEF,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC;QACtC,SAAS,CAAC,cAAc,GAAG;YACzB,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,KAAK,OAAO;YACjE,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,+BAA+B;YAC3D,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,iCAAiC;gBAC7D,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC;gBACzD,CAAC,CAAC,SAAS;YACb,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B;gBAC/C,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;gBAClD,CAAC,CAAC,SAAS;SACd,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;IACvC,IAAI,SAAS;QAAE,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC;IAE7C,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC;IAClD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,WAAoC,EACpC,OAAgC;IAEhC,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO;IAChD,OAAO,CAAC,KAAK,CACX,oEAAoE;QAClE,+DAA+D;QAC/D,YAAY;QACZ,+EAA+E;QAC/E,0CAA0C;QAC1C,eAAe,OAAO,KAAK,CAC9B,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"build-repo-configs.js","sourceRoot":"","sources":["../../src/lib/build-repo-configs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * User-level install — config schema, on-disk locations, read/write helpers.
3
+ *
4
+ * The user-level path stores everything under `~/.urateam/` (or
5
+ * `$URATEAM_HOME` when set). Layout:
6
+ *
7
+ * ~/.urateam/
8
+ * ├── config.json # this module's schema
9
+ * ├── .env # secrets (ANTHROPIC_API_KEY, etc.)
10
+ * ├── data/ # SQLite DB lives here
11
+ * └── repos/ # cloned repos managed by `ura repo add`
12
+ *
13
+ * The schema intentionally mirrors the existing `RepoConfig` shape (`url`,
14
+ * `defaultBranch`, `testCommand`, `buildCommand`, optional `labelPattern`)
15
+ * plus a `path` field that records WHERE the clone lives — that's the
16
+ * field the daemon needs to actually find the worktree.
17
+ *
18
+ * `URATEAM_HOME` env-var override exists for two reasons:
19
+ * 1. Tests (hermetic temp dir per test)
20
+ * 2. Operators who want to run multiple isolated user-level installs on
21
+ * one machine (e.g., one per Linear workspace) without touching
22
+ * `~/.urateam/`.
23
+ */
24
+ import { z } from "zod";
25
+ export declare const UserLevelRepoSchema: z.ZodObject<{
26
+ url: z.ZodString;
27
+ path: z.ZodString;
28
+ defaultBranch: z.ZodString;
29
+ testCommand: z.ZodDefault<z.ZodString>;
30
+ buildCommand: z.ZodDefault<z.ZodString>;
31
+ teamId: z.ZodOptional<z.ZodString>;
32
+ labelPattern: z.ZodOptional<z.ZodString>;
33
+ }, z.core.$strip>;
34
+ export type UserLevelRepo = z.infer<typeof UserLevelRepoSchema>;
35
+ export declare const UserLevelConfigSchema: z.ZodObject<{
36
+ version: z.ZodLiteral<1>;
37
+ repos: z.ZodDefault<z.ZodArray<z.ZodObject<{
38
+ url: z.ZodString;
39
+ path: z.ZodString;
40
+ defaultBranch: z.ZodString;
41
+ testCommand: z.ZodDefault<z.ZodString>;
42
+ buildCommand: z.ZodDefault<z.ZodString>;
43
+ teamId: z.ZodOptional<z.ZodString>;
44
+ labelPattern: z.ZodOptional<z.ZodString>;
45
+ }, z.core.$strip>>>;
46
+ }, z.core.$strip>;
47
+ export type UserLevelConfig = z.infer<typeof UserLevelConfigSchema>;
48
+ /**
49
+ * Resolve the user-level state directory. Returns `$URATEAM_HOME` when set,
50
+ * otherwise `~/.urateam`.
51
+ */
52
+ export declare function resolveUserLevelHome(): string;
53
+ export declare function userLevelConfigPath(): string;
54
+ export declare function userLevelReposDir(): string;
55
+ export declare function userLevelDataDir(): string;
56
+ /**
57
+ * Read and validate the user-level config. Returns `null` when the file
58
+ * doesn't exist (so callers can distinguish "not initialized" from "empty
59
+ * config"). Throws on schema-validation failure — operators should see the
60
+ * Zod error explicitly rather than silently get an unconfigured daemon.
61
+ */
62
+ export declare function readUserLevelConfig(): UserLevelConfig | null;
63
+ /**
64
+ * Write the user-level config, creating `~/.urateam/` if missing.
65
+ * Idempotent: callers are expected to read-modify-write to preserve any
66
+ * fields the writer doesn't know about (the schema is forward-compatible
67
+ * within version 1).
68
+ */
69
+ export declare function writeUserLevelConfig(config: UserLevelConfig): void;
70
+ //# sourceMappingURL=user-level-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-level-config.d.ts","sourceRoot":"","sources":["../../src/lib/user-level-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,mBAAmB;;;;;;;;iBAgB9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,qBAAqB;;;;;;;;;;;iBAKhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,eAAe,GAAG,IAAI,CAK5D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAIlE"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * User-level install — config schema, on-disk locations, read/write helpers.
3
+ *
4
+ * The user-level path stores everything under `~/.urateam/` (or
5
+ * `$URATEAM_HOME` when set). Layout:
6
+ *
7
+ * ~/.urateam/
8
+ * ├── config.json # this module's schema
9
+ * ├── .env # secrets (ANTHROPIC_API_KEY, etc.)
10
+ * ├── data/ # SQLite DB lives here
11
+ * └── repos/ # cloned repos managed by `ura repo add`
12
+ *
13
+ * The schema intentionally mirrors the existing `RepoConfig` shape (`url`,
14
+ * `defaultBranch`, `testCommand`, `buildCommand`, optional `labelPattern`)
15
+ * plus a `path` field that records WHERE the clone lives — that's the
16
+ * field the daemon needs to actually find the worktree.
17
+ *
18
+ * `URATEAM_HOME` env-var override exists for two reasons:
19
+ * 1. Tests (hermetic temp dir per test)
20
+ * 2. Operators who want to run multiple isolated user-level installs on
21
+ * one machine (e.g., one per Linear workspace) without touching
22
+ * `~/.urateam/`.
23
+ */
24
+ import { z } from "zod";
25
+ import { homedir } from "node:os";
26
+ import { join } from "node:path";
27
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
28
+ export const UserLevelRepoSchema = z.object({
29
+ /** Source URL (https / git@ — anything `git clone` accepts). */
30
+ url: z.string().min(1),
31
+ /** Absolute filesystem path where the clone lives. */
32
+ path: z.string().min(1),
33
+ /** Default branch — the daemon uses this for diff/rebase targets. */
34
+ defaultBranch: z.string().min(1),
35
+ /** Test command invoked in the worktree. */
36
+ testCommand: z.string().default("pnpm test"),
37
+ /** Build command invoked in the worktree. */
38
+ buildCommand: z.string().default("pnpm build"),
39
+ /** Linear team ID (optional — required when promote routes by team). */
40
+ teamId: z.string().optional(),
41
+ /** BEC-177: label-based routing. When set, this repo handles tickets
42
+ * whose pipeline label matches the pattern. */
43
+ labelPattern: z.string().optional(),
44
+ });
45
+ export const UserLevelConfigSchema = z.object({
46
+ /** Schema version — bumped if/when the file format changes. */
47
+ version: z.literal(1),
48
+ /** Configured repos. `ura repo add` appends; `ura repo remove` filters. */
49
+ repos: z.array(UserLevelRepoSchema).default([]),
50
+ });
51
+ /**
52
+ * Resolve the user-level state directory. Returns `$URATEAM_HOME` when set,
53
+ * otherwise `~/.urateam`.
54
+ */
55
+ export function resolveUserLevelHome() {
56
+ return process.env.URATEAM_HOME ?? join(homedir(), ".urateam");
57
+ }
58
+ export function userLevelConfigPath() {
59
+ return join(resolveUserLevelHome(), "config.json");
60
+ }
61
+ export function userLevelReposDir() {
62
+ return join(resolveUserLevelHome(), "repos");
63
+ }
64
+ export function userLevelDataDir() {
65
+ return join(resolveUserLevelHome(), "data");
66
+ }
67
+ /**
68
+ * Read and validate the user-level config. Returns `null` when the file
69
+ * doesn't exist (so callers can distinguish "not initialized" from "empty
70
+ * config"). Throws on schema-validation failure — operators should see the
71
+ * Zod error explicitly rather than silently get an unconfigured daemon.
72
+ */
73
+ export function readUserLevelConfig() {
74
+ const path = userLevelConfigPath();
75
+ if (!existsSync(path))
76
+ return null;
77
+ const raw = JSON.parse(readFileSync(path, "utf8"));
78
+ return UserLevelConfigSchema.parse(raw);
79
+ }
80
+ /**
81
+ * Write the user-level config, creating `~/.urateam/` if missing.
82
+ * Idempotent: callers are expected to read-modify-write to preserve any
83
+ * fields the writer doesn't know about (the schema is forward-compatible
84
+ * within version 1).
85
+ */
86
+ export function writeUserLevelConfig(config) {
87
+ const home = resolveUserLevelHome();
88
+ mkdirSync(home, { recursive: true });
89
+ writeFileSync(userLevelConfigPath(), JSON.stringify(config, null, 2) + "\n");
90
+ }
91
+ //# sourceMappingURL=user-level-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-level-config.js","sourceRoot":"","sources":["../../src/lib/user-level-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE7E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,gEAAgE;IAChE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACtB,sDAAsD;IACtD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,qEAAqE;IACrE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,4CAA4C;IAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;IAC5C,6CAA6C;IAC7C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;IAC9C,wEAAwE;IACxE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B;oDACgD;IAChD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,+DAA+D;IAC/D,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,2EAA2E;IAC3E,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAChD,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,aAAa,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;IACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,OAAO,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAuB;IAC1D,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,aAAa,CAAC,mBAAmB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/E,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urateam/cli",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "license": "BUSL-1.1",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,8 +22,9 @@
22
22
  "better-sqlite3": "^11.8.0",
23
23
  "commander": "^13.1.0",
24
24
  "postgres": "^3.4.0",
25
- "@urateam/core": "0.1.37",
26
- "@urateam/dashboard": "0.1.37"
25
+ "zod": "^4.3.6",
26
+ "@urateam/core": "0.1.38",
27
+ "@urateam/dashboard": "0.1.38"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@types/better-sqlite3": "^7.6.0",