create-ragen-app 0.2.0 → 0.4.0
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 +90 -7
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +218 -1
- package/dist/cli.js.map +1 -1
- package/dist/config-file.d.ts +14 -0
- package/dist/config-file.d.ts.map +1 -0
- package/dist/config-file.js +21 -0
- package/dist/config-file.js.map +1 -0
- package/dist/config-template.d.ts +30 -0
- package/dist/config-template.d.ts.map +1 -0
- package/dist/config-template.js +126 -0
- package/dist/config-template.js.map +1 -0
- package/dist/encryption-provider.d.ts +35 -0
- package/dist/encryption-provider.d.ts.map +1 -0
- package/dist/encryption-provider.js +63 -0
- package/dist/encryption-provider.js.map +1 -0
- package/dist/llm-provider.d.ts +17 -0
- package/dist/llm-provider.d.ts.map +1 -1
- package/dist/llm-provider.js +31 -1
- package/dist/llm-provider.js.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +22 -4
- package/dist/manifest.js.map +1 -1
- package/dist/manual-setup.d.ts.map +1 -1
- package/dist/manual-setup.js +85 -16
- package/dist/manual-setup.js.map +1 -1
- package/dist/node-version.d.ts +54 -0
- package/dist/node-version.d.ts.map +1 -0
- package/dist/node-version.js +81 -0
- package/dist/node-version.js.map +1 -0
- package/dist/route-table.d.ts +31 -0
- package/dist/route-table.d.ts.map +1 -0
- package/dist/route-table.js +53 -0
- package/dist/route-table.js.map +1 -0
- package/dist/storage-provider.d.ts +49 -0
- package/dist/storage-provider.d.ts.map +1 -0
- package/dist/storage-provider.js +84 -0
- package/dist/storage-provider.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this Node can be used to create a Ragen installation.
|
|
3
|
+
*
|
|
4
|
+
* The distinction that matters: **the installation** needs Node 24, not this
|
|
5
|
+
* CLI. The CLI itself runs happily on older majors — it was published and
|
|
6
|
+
* verified on Node 22 by accident — so refusing on the grounds that "this
|
|
7
|
+
* tool requires Node 24" would be both untrue and unhelpfully broad.
|
|
8
|
+
*
|
|
9
|
+
* What genuinely breaks is the first-run setup the wizard performs on the
|
|
10
|
+
* caller's behalf: `npm install` across the monorepo, `prisma generate`,
|
|
11
|
+
* `migrate deploy` and the seed all run under whatever Node invoked the
|
|
12
|
+
* wizard. Getting a half-built `node_modules` out of that is the expensive
|
|
13
|
+
* failure, because it surfaces much later and nowhere near its cause. npm's
|
|
14
|
+
* own `EBADENGINE` warning does fire, but as one line inside a wall of
|
|
15
|
+
* install output, and it does not say what will break.
|
|
16
|
+
*
|
|
17
|
+
* So the check is gated on what the wizard is about to *do*:
|
|
18
|
+
*
|
|
19
|
+
* - running the setup steps → refuse, before anything is cloned
|
|
20
|
+
* - `--skip-install` → proceed, because nothing runs here; warn instead,
|
|
21
|
+
* since the scaffolded app still needs Node 24 to start
|
|
22
|
+
*
|
|
23
|
+
* `--yes` deliberately does not bypass it. That flag means "accept the
|
|
24
|
+
* defaults", not "ignore a requirement".
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Kept in step with this package's `engines.node` by
|
|
28
|
+
* `src/__tests__/node-version.test.ts`, and with the repository's own Node
|
|
29
|
+
* version documented in AGENTS.md ("Node.js 24.x (Active LTS)").
|
|
30
|
+
*/
|
|
31
|
+
export declare const REQUIRED_NODE_MAJOR = 24;
|
|
32
|
+
export type NodeVersionVerdict = {
|
|
33
|
+
kind: 'ok';
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'warn';
|
|
36
|
+
message: string;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'refuse';
|
|
39
|
+
message: string;
|
|
40
|
+
};
|
|
41
|
+
export interface NodeVersionCheck {
|
|
42
|
+
/** As `process.version` gives it, e.g. `v22.22.3`. */
|
|
43
|
+
version: string;
|
|
44
|
+
/** False when `--skip-install` means the wizard runs nothing itself. */
|
|
45
|
+
willRunSetup: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Returns undefined for anything that is not a recognisable Node version, so
|
|
49
|
+
* an unexpected format cannot be read as "too old" and block an install that
|
|
50
|
+
* would have worked.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseNodeMajor(version: string): number | undefined;
|
|
53
|
+
export declare function checkNodeVersion({ version, willRunSetup, }: NodeVersionCheck): NodeVersionVerdict;
|
|
54
|
+
//# sourceMappingURL=node-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-version.d.ts","sourceRoot":"","sources":["../src/node-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GACd;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMlE;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,YAAY,GACb,EAAE,gBAAgB,GAAG,kBAAkB,CAkCvC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Whether this Node can be used to create a Ragen installation.
|
|
4
|
+
*
|
|
5
|
+
* The distinction that matters: **the installation** needs Node 24, not this
|
|
6
|
+
* CLI. The CLI itself runs happily on older majors — it was published and
|
|
7
|
+
* verified on Node 22 by accident — so refusing on the grounds that "this
|
|
8
|
+
* tool requires Node 24" would be both untrue and unhelpfully broad.
|
|
9
|
+
*
|
|
10
|
+
* What genuinely breaks is the first-run setup the wizard performs on the
|
|
11
|
+
* caller's behalf: `npm install` across the monorepo, `prisma generate`,
|
|
12
|
+
* `migrate deploy` and the seed all run under whatever Node invoked the
|
|
13
|
+
* wizard. Getting a half-built `node_modules` out of that is the expensive
|
|
14
|
+
* failure, because it surfaces much later and nowhere near its cause. npm's
|
|
15
|
+
* own `EBADENGINE` warning does fire, but as one line inside a wall of
|
|
16
|
+
* install output, and it does not say what will break.
|
|
17
|
+
*
|
|
18
|
+
* So the check is gated on what the wizard is about to *do*:
|
|
19
|
+
*
|
|
20
|
+
* - running the setup steps → refuse, before anything is cloned
|
|
21
|
+
* - `--skip-install` → proceed, because nothing runs here; warn instead,
|
|
22
|
+
* since the scaffolded app still needs Node 24 to start
|
|
23
|
+
*
|
|
24
|
+
* `--yes` deliberately does not bypass it. That flag means "accept the
|
|
25
|
+
* defaults", not "ignore a requirement".
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.REQUIRED_NODE_MAJOR = void 0;
|
|
29
|
+
exports.parseNodeMajor = parseNodeMajor;
|
|
30
|
+
exports.checkNodeVersion = checkNodeVersion;
|
|
31
|
+
/**
|
|
32
|
+
* Kept in step with this package's `engines.node` by
|
|
33
|
+
* `src/__tests__/node-version.test.ts`, and with the repository's own Node
|
|
34
|
+
* version documented in AGENTS.md ("Node.js 24.x (Active LTS)").
|
|
35
|
+
*/
|
|
36
|
+
exports.REQUIRED_NODE_MAJOR = 24;
|
|
37
|
+
/**
|
|
38
|
+
* Returns undefined for anything that is not a recognisable Node version, so
|
|
39
|
+
* an unexpected format cannot be read as "too old" and block an install that
|
|
40
|
+
* would have worked.
|
|
41
|
+
*/
|
|
42
|
+
function parseNodeMajor(version) {
|
|
43
|
+
const match = /^v?(\d+)\./.exec(version.trim());
|
|
44
|
+
if (!match) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return Number(match[1]);
|
|
48
|
+
}
|
|
49
|
+
function checkNodeVersion({ version, willRunSetup, }) {
|
|
50
|
+
const major = parseNodeMajor(version);
|
|
51
|
+
if (major === undefined || major >= exports.REQUIRED_NODE_MAJOR) {
|
|
52
|
+
return { kind: 'ok' };
|
|
53
|
+
}
|
|
54
|
+
if (!willRunSetup) {
|
|
55
|
+
return {
|
|
56
|
+
kind: 'warn',
|
|
57
|
+
message: [
|
|
58
|
+
`You are on Node ${major}, and a Ragen installation needs Node ${exports.REQUIRED_NODE_MAJOR}.`,
|
|
59
|
+
'Nothing is being installed here, so the files will be written anyway —',
|
|
60
|
+
'but switch before running npm install or the app in the new directory.',
|
|
61
|
+
].join(' '),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
kind: 'refuse',
|
|
66
|
+
message: [
|
|
67
|
+
`A Ragen installation needs Node ${exports.REQUIRED_NODE_MAJOR}, and this is Node ${major}.`,
|
|
68
|
+
'',
|
|
69
|
+
'Stopping before anything is written: the setup this runs for you —',
|
|
70
|
+
'npm install, prisma generate, migrate and seed — would run under this',
|
|
71
|
+
'Node and leave a tree that fails later, somewhere that does not point',
|
|
72
|
+
'back here.',
|
|
73
|
+
'',
|
|
74
|
+
` nvm use ${exports.REQUIRED_NODE_MAJOR} # or fnm, volta, asdf, …`,
|
|
75
|
+
'',
|
|
76
|
+
'Or pass --skip-install to write the files now and run the setup',
|
|
77
|
+
'yourself on a supported Node.',
|
|
78
|
+
].join('\n'),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=node-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-version.js","sourceRoot":"","sources":["../src/node-version.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AA0BH,wCAMC;AAED,4CAqCC;AArED;;;;GAIG;AACU,QAAA,mBAAmB,GAAG,EAAE,CAAC;AActC;;;;GAIG;AACH,SAAgB,cAAc,CAAC,OAAe;IAC5C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED,SAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,YAAY,GACK;IACjB,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,IAAI,2BAAmB,EAAE,CAAC;QACxD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE;gBACP,mBAAmB,KAAK,yCAAyC,2BAAmB,GAAG;gBACvF,wEAAwE;gBACxE,wEAAwE;aACzE,CAAC,IAAI,CAAC,GAAG,CAAC;SACZ,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE;YACP,mCAAmC,2BAAmB,sBAAsB,KAAK,GAAG;YACpF,EAAE;YACF,oEAAoE;YACpE,uEAAuE;YACvE,uEAAuE;YACvE,YAAY;YACZ,EAAE;YACF,aAAa,2BAAmB,6BAA6B;YAC7D,EAAE;YACF,iEAAiE;YACjE,+BAA+B;SAChC,CAAC,IAAI,CAAC,IAAI,CAAC;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Where every app finds the table: `defaultRouteTablePath()` walks up to it. */
|
|
2
|
+
export declare const ROUTE_TABLE_PATH = "infra/llm-gateway/routes.yaml";
|
|
3
|
+
export interface RouteTableEntry {
|
|
4
|
+
/** The id the application asks for — `DEFAULT_MODEL`, `EMBEDDINGS_MODEL`. */
|
|
5
|
+
modelName: string;
|
|
6
|
+
/** A `@ragenai/llm-gateway` provider id. */
|
|
7
|
+
provider: string;
|
|
8
|
+
/** The upstream's own name for the model, which may differ from the id. */
|
|
9
|
+
model: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The route table for a scaffolded installation.
|
|
13
|
+
*
|
|
14
|
+
* Written **over** the table shipped in the repository rather than beside it,
|
|
15
|
+
* which is the same thing this wizard already does to
|
|
16
|
+
* `infra/litellm/config.yaml`. Two reasons, and the second is the one that
|
|
17
|
+
* decides it:
|
|
18
|
+
*
|
|
19
|
+
* - The shipped table names Azure, Bedrock, Vertex and Scaleway. A new
|
|
20
|
+
* installation has credentials for none of them, so every entry in it is a
|
|
21
|
+
* route to a 401.
|
|
22
|
+
* - `LLM_ROUTES_PATH` would be the alternative, and a *relative* one is a
|
|
23
|
+
* trap: `routeTableFromEnv` joins it to `process.cwd()`, which is a
|
|
24
|
+
* different directory for `apps/web`, `apps/api` and `apps/worker`. An
|
|
25
|
+
* absolute path avoids that and then breaks the moment the directory is
|
|
26
|
+
* moved. Writing the file where the default lookup already walks to it
|
|
27
|
+
* needs no variable at all.
|
|
28
|
+
*/
|
|
29
|
+
export declare function renderRouteTable(entries: readonly RouteTableEntry[]): string;
|
|
30
|
+
export declare function writeRouteTable(targetDir: string, entries: readonly RouteTableEntry[]): void;
|
|
31
|
+
//# sourceMappingURL=route-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-table.d.ts","sourceRoot":"","sources":["../src/route-table.ts"],"names":[],"mappings":"AAGA,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,kCAAkC,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAyB5E;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,IAAI,CAEN"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ROUTE_TABLE_PATH = void 0;
|
|
4
|
+
exports.renderRouteTable = renderRouteTable;
|
|
5
|
+
exports.writeRouteTable = writeRouteTable;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
/** Where every app finds the table: `defaultRouteTablePath()` walks up to it. */
|
|
9
|
+
exports.ROUTE_TABLE_PATH = 'infra/llm-gateway/routes.yaml';
|
|
10
|
+
/**
|
|
11
|
+
* The route table for a scaffolded installation.
|
|
12
|
+
*
|
|
13
|
+
* Written **over** the table shipped in the repository rather than beside it,
|
|
14
|
+
* which is the same thing this wizard already does to
|
|
15
|
+
* `infra/litellm/config.yaml`. Two reasons, and the second is the one that
|
|
16
|
+
* decides it:
|
|
17
|
+
*
|
|
18
|
+
* - The shipped table names Azure, Bedrock, Vertex and Scaleway. A new
|
|
19
|
+
* installation has credentials for none of them, so every entry in it is a
|
|
20
|
+
* route to a 401.
|
|
21
|
+
* - `LLM_ROUTES_PATH` would be the alternative, and a *relative* one is a
|
|
22
|
+
* trap: `routeTableFromEnv` joins it to `process.cwd()`, which is a
|
|
23
|
+
* different directory for `apps/web`, `apps/api` and `apps/worker`. An
|
|
24
|
+
* absolute path avoids that and then breaks the moment the directory is
|
|
25
|
+
* moved. Writing the file where the default lookup already walks to it
|
|
26
|
+
* needs no variable at all.
|
|
27
|
+
*/
|
|
28
|
+
function renderRouteTable(entries) {
|
|
29
|
+
const routes = entries
|
|
30
|
+
.map(({ modelName, provider, model }) => ` ${modelName}:\n provider: ${provider}\n model: ${model}\n`)
|
|
31
|
+
.join('');
|
|
32
|
+
return `# Written by create-ragen-app for this installation.
|
|
33
|
+
#
|
|
34
|
+
# Which upstream serves each model id the application asks for. Ragen calls
|
|
35
|
+
# these providers itself — LLM_GATEWAY=native — with no proxy in the path.
|
|
36
|
+
#
|
|
37
|
+
# This replaced the table shipped in the repository, which routes to Azure,
|
|
38
|
+
# Bedrock, Vertex and Scaleway. Those entries are not wrong, they are simply
|
|
39
|
+
# not yours: nothing here has credentials for them.
|
|
40
|
+
#
|
|
41
|
+
# To serve another model, add an entry. The key is the id the application asks
|
|
42
|
+
# for (DEFAULT_MODEL, EMBEDDINGS_MODEL, REPHRASE_MODEL); \`model\` is the
|
|
43
|
+
# upstream's own name for it, which is not always the same string. Credentials
|
|
44
|
+
# stay in the environment and never appear here.
|
|
45
|
+
version: 1
|
|
46
|
+
|
|
47
|
+
routes:
|
|
48
|
+
${routes}`;
|
|
49
|
+
}
|
|
50
|
+
function writeRouteTable(targetDir, entries) {
|
|
51
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(targetDir, exports.ROUTE_TABLE_PATH), renderRouteTable(entries));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=route-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-table.js","sourceRoot":"","sources":["../src/route-table.ts"],"names":[],"mappings":";;;AAiCA,4CAyBC;AAED,0CAKC;AAjED,qCAAwC;AACxC,yCAAiC;AAEjC,iFAAiF;AACpE,QAAA,gBAAgB,GAAG,+BAA+B,CAAC;AAWhE;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,gBAAgB,CAAC,OAAmC;IAClE,MAAM,MAAM,GAAG,OAAO;SACnB,GAAG,CACF,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,CACjC,KAAK,SAAS,oBAAoB,QAAQ,gBAAgB,KAAK,IAAI,CACtE;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;;;;;;;;;;;;;;;EAgBP,MAAM,EAAE,CAAC;AACX,CAAC;AAED,SAAgB,eAAe,CAC7B,SAAiB,EACjB,OAAmC;IAEnC,IAAA,uBAAa,EAAC,IAAA,gBAAI,EAAC,SAAS,EAAE,wBAAgB,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where uploaded documents live.
|
|
3
|
+
*
|
|
4
|
+
* The wizard skipped this until now and sent people to a manual guide, which
|
|
5
|
+
* is the one question where getting it wrong is expensive: `STORAGE_PROVIDER=s3`
|
|
6
|
+
* with a missing credential does not fail at boot in every app, it fails at
|
|
7
|
+
* the first upload, as a storage error three layers from the cause.
|
|
8
|
+
*
|
|
9
|
+
* The variable names and the config field names below mirror `STORAGE_SEAM` in
|
|
10
|
+
* `@ragenai/env`. They are copied rather than imported because this package is
|
|
11
|
+
* published to npm and `@ragenai/env` is private, so a dependency on it would
|
|
12
|
+
* make `npm create ragen-app` unresolvable. The copy is held to the original
|
|
13
|
+
* by `tests/architecture/create-ragen-app-knows-the-provider-seams.test.ts` —
|
|
14
|
+
* the same arrangement `manifest.ts` already has with the `.env.example`
|
|
15
|
+
* files.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* One field of a written config, and the variable it resolves from.
|
|
19
|
+
*
|
|
20
|
+
* `required` decides how the installer renders it: a required field is typed
|
|
21
|
+
* `string`, so it needs a fallback to compile, while an optional one takes
|
|
22
|
+
* `process.env.X` as it comes. Carrying it here rather than inferring from the
|
|
23
|
+
* name keeps the renderer from having a fourth opinion about which variables a
|
|
24
|
+
* provider needs.
|
|
25
|
+
*/
|
|
26
|
+
export interface ConfigField {
|
|
27
|
+
field: string;
|
|
28
|
+
envVar: string;
|
|
29
|
+
required: boolean;
|
|
30
|
+
}
|
|
31
|
+
export type StorageChoice = 'local' | 's3';
|
|
32
|
+
export interface S3Answers {
|
|
33
|
+
bucket: string;
|
|
34
|
+
region: string;
|
|
35
|
+
accessKeyId: string;
|
|
36
|
+
secretAccessKey: string;
|
|
37
|
+
/** Blank selects AWS. Set for R2, Scaleway, MinIO or Ceph. */
|
|
38
|
+
endpoint: string;
|
|
39
|
+
}
|
|
40
|
+
export interface StorageSelection {
|
|
41
|
+
provider: StorageChoice;
|
|
42
|
+
/** Written to the root `.env.local` — credentials never go in the config. */
|
|
43
|
+
envUpdates: Record<string, string>;
|
|
44
|
+
/** The config fields, already named as the config names them. */
|
|
45
|
+
configFields: ConfigField[];
|
|
46
|
+
}
|
|
47
|
+
export declare const STORAGE_LABELS: Record<StorageChoice, string>;
|
|
48
|
+
export declare function resolveStorageSelection(provider: StorageChoice, answers?: S3Answers): StorageSelection;
|
|
49
|
+
//# sourceMappingURL=storage-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-provider.d.ts","sourceRoot":"","sources":["../src/storage-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,IAAI,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,aAAa,CAAC;IACxB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,iEAAiE;IACjE,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAGxD,CAAC;AAkBF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,SAAS,GAClB,gBAAgB,CAmDlB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Where uploaded documents live.
|
|
4
|
+
*
|
|
5
|
+
* The wizard skipped this until now and sent people to a manual guide, which
|
|
6
|
+
* is the one question where getting it wrong is expensive: `STORAGE_PROVIDER=s3`
|
|
7
|
+
* with a missing credential does not fail at boot in every app, it fails at
|
|
8
|
+
* the first upload, as a storage error three layers from the cause.
|
|
9
|
+
*
|
|
10
|
+
* The variable names and the config field names below mirror `STORAGE_SEAM` in
|
|
11
|
+
* `@ragenai/env`. They are copied rather than imported because this package is
|
|
12
|
+
* published to npm and `@ragenai/env` is private, so a dependency on it would
|
|
13
|
+
* make `npm create ragen-app` unresolvable. The copy is held to the original
|
|
14
|
+
* by `tests/architecture/create-ragen-app-knows-the-provider-seams.test.ts` —
|
|
15
|
+
* the same arrangement `manifest.ts` already has with the `.env.example`
|
|
16
|
+
* files.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.STORAGE_LABELS = void 0;
|
|
20
|
+
exports.resolveStorageSelection = resolveStorageSelection;
|
|
21
|
+
exports.STORAGE_LABELS = {
|
|
22
|
+
local: 'Local filesystem (default — no cloud account needed)',
|
|
23
|
+
s3: 'S3-compatible (AWS, Cloudflare R2, Scaleway, MinIO, Ceph)',
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A path-style endpoint is what Scaleway (dotted bucket names) and MinIO/Ceph
|
|
27
|
+
* need. Guessing it from the endpoint rather than asking keeps the wizard to
|
|
28
|
+
* one question per thing a person actually knows about their own storage.
|
|
29
|
+
*/
|
|
30
|
+
function needsPathStyle(endpoint) {
|
|
31
|
+
const host = endpoint.trim().toLowerCase();
|
|
32
|
+
if (!host) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return (!host.includes('amazonaws.com') &&
|
|
36
|
+
!host.includes('r2.cloudflarestorage.com'));
|
|
37
|
+
}
|
|
38
|
+
function resolveStorageSelection(provider, answers) {
|
|
39
|
+
if (provider === 'local' || !answers) {
|
|
40
|
+
return {
|
|
41
|
+
provider: 'local',
|
|
42
|
+
envUpdates: { STORAGE_PROVIDER: 'local' },
|
|
43
|
+
configFields: [
|
|
44
|
+
{ field: 'path', envVar: 'STORAGE_LOCAL_PATH', required: false },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const endpoint = answers.endpoint.trim();
|
|
49
|
+
const envUpdates = {
|
|
50
|
+
STORAGE_PROVIDER: 's3',
|
|
51
|
+
S3_BUCKET_NAME: answers.bucket.trim(),
|
|
52
|
+
S3_REGION: answers.region.trim(),
|
|
53
|
+
S3_ACCESS_KEY_ID: answers.accessKeyId.trim(),
|
|
54
|
+
S3_SECRET_ACCESS_KEY: answers.secretAccessKey.trim(),
|
|
55
|
+
};
|
|
56
|
+
const configFields = [
|
|
57
|
+
{ field: 'bucketName', envVar: 'S3_BUCKET_NAME', required: true },
|
|
58
|
+
{ field: 'region', envVar: 'S3_REGION', required: true },
|
|
59
|
+
{ field: 'accessKeyId', envVar: 'S3_ACCESS_KEY_ID', required: true },
|
|
60
|
+
{
|
|
61
|
+
field: 'secretAccessKey',
|
|
62
|
+
envVar: 'S3_SECRET_ACCESS_KEY',
|
|
63
|
+
required: true,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
if (endpoint) {
|
|
67
|
+
envUpdates.S3_ENDPOINT_URL = endpoint;
|
|
68
|
+
configFields.push({
|
|
69
|
+
field: 'endpoint',
|
|
70
|
+
envVar: 'S3_ENDPOINT_URL',
|
|
71
|
+
required: false,
|
|
72
|
+
});
|
|
73
|
+
if (needsPathStyle(endpoint)) {
|
|
74
|
+
envUpdates.S3_FORCE_PATH_STYLE = 'true';
|
|
75
|
+
configFields.push({
|
|
76
|
+
field: 'forcePathStyle',
|
|
77
|
+
envVar: 'S3_FORCE_PATH_STYLE',
|
|
78
|
+
required: false,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { provider: 's3', envUpdates, configFields };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=storage-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-provider.js","sourceRoot":"","sources":["../src/storage-provider.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAyDH,0DAsDC;AA3EY,QAAA,cAAc,GAAkC;IAC3D,KAAK,EAAE,sDAAsD;IAC7D,EAAE,EAAE,2DAA2D;CAChE,CAAC;AAEF;;;;GAIG;AACH,SAAS,cAAc,CAAC,QAAgB;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,CACL,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;QAC/B,CAAC,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAC3C,CAAC;AACJ,CAAC;AAED,SAAgB,uBAAuB,CACrC,QAAuB,EACvB,OAAmB;IAEnB,IAAI,QAAQ,KAAK,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QACrC,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,UAAU,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE;YACzC,YAAY,EAAE;gBACZ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,EAAE,KAAK,EAAE;aACjE;SACF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEzC,MAAM,UAAU,GAA2B;QACzC,gBAAgB,EAAE,IAAI;QACtB,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE;QACrC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE;QAChC,gBAAgB,EAAE,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE;QAC5C,oBAAoB,EAAE,OAAO,CAAC,eAAe,CAAC,IAAI,EAAE;KACrD,CAAC;IAEF,MAAM,YAAY,GAAkB;QAClC,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE;QACjE,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE;QACxD,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,QAAQ,EAAE,IAAI,EAAE;QACpE;YACE,KAAK,EAAE,iBAAiB;YACxB,MAAM,EAAE,sBAAsB;YAC9B,QAAQ,EAAE,IAAI;SACf;KACF,CAAC;IAEF,IAAI,QAAQ,EAAE,CAAC;QACb,UAAU,CAAC,eAAe,GAAG,QAAQ,CAAC;QACtC,YAAY,CAAC,IAAI,CAAC;YAChB,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,iBAAiB;YACzB,QAAQ,EAAE,KAAK;SAChB,CAAC,CAAC;QAEH,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,UAAU,CAAC,mBAAmB,GAAG,MAAM,CAAC;YACxC,YAAY,CAAC,IAAI,CAAC;gBAChB,KAAK,EAAE,gBAAgB;gBACvB,MAAM,EAAE,qBAAqB;gBAC7B,QAAQ,EAAE,KAAK;aAChB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AACtD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ragen-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Scaffold a self-hosted Ragen AI installation with one command",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "tsc -p tsconfig.json",
|
|
24
24
|
"clean": "rm -rf dist",
|
|
25
|
-
"
|
|
25
|
+
"prepack": "npm run clean && npm run build",
|
|
26
26
|
"lint": "eslint",
|
|
27
27
|
"typecheck": "tsc --noEmit --incremental --tsBuildInfoFile ./typecheck.tsbuildinfo"
|
|
28
28
|
},
|