flowdular 0.1.0 → 0.2.1
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 +68 -12
- package/assets/flowdular-banner.png +0 -0
- package/dist/distribution.js +30 -48
- package/dist/index.js +591 -281
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -6,7 +6,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
-
|
|
9
|
+
try {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
} catch (e) {
|
|
12
|
+
throw mod = 0, e;
|
|
13
|
+
}
|
|
10
14
|
};
|
|
11
15
|
var __copyProps = (to, from, except, desc) => {
|
|
12
16
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -1995,15 +1999,42 @@ function isCapabilityList(values2) {
|
|
|
1995
1999
|
}
|
|
1996
2000
|
function renderOutput(envelope, json2) {
|
|
1997
2001
|
if (json2) return JSON.stringify(envelope, null, 2);
|
|
1998
|
-
if (!envelope.ok)
|
|
1999
|
-
|
|
2002
|
+
if (!envelope.ok) {
|
|
2003
|
+
const heading = `ERROR ${envelope.error?.code ?? "UNKNOWN"}
|
|
2000
2004
|
${envelope.error?.message ?? "Command failed."}`;
|
|
2005
|
+
if (envelope.error?.code !== "DOCTOR_FAILED") return heading;
|
|
2006
|
+
const details = envelope.error.details;
|
|
2007
|
+
if (!Array.isArray(details?.checks)) return heading;
|
|
2008
|
+
const failures = details.checks.filter(
|
|
2009
|
+
(check) => check && check.status === "fail" && typeof check.id === "string" && typeof check.message === "string"
|
|
2010
|
+
);
|
|
2011
|
+
return [
|
|
2012
|
+
heading,
|
|
2013
|
+
...failures.map((check) => `FAIL ${check.id} ${check.message}`)
|
|
2014
|
+
].join("\n");
|
|
2015
|
+
}
|
|
2001
2016
|
const decorate = (body) => [
|
|
2002
2017
|
body,
|
|
2003
2018
|
...envelope.warnings.map((warning) => `WARNING ${warning}`),
|
|
2004
2019
|
...envelope.evidence.map((item) => `EVIDENCE ${item}`)
|
|
2005
2020
|
].join("\n");
|
|
2006
2021
|
const data2 = envelope.data;
|
|
2022
|
+
if (data2?.cancelled === true) return decorate("Setup cancelled.");
|
|
2023
|
+
if (data2?.setup === "postgresql")
|
|
2024
|
+
return decorate(
|
|
2025
|
+
"PostgreSQL settings saved to .env.\nRun pnpm dev, then open http://localhost:4310 to create an account."
|
|
2026
|
+
);
|
|
2027
|
+
if (data2?.setup === "local") {
|
|
2028
|
+
const admin = data2.accounts?.admin;
|
|
2029
|
+
return decorate(
|
|
2030
|
+
[
|
|
2031
|
+
"Local demo is ready.",
|
|
2032
|
+
"Run pnpm dev, then open http://localhost:4310.",
|
|
2033
|
+
...admin?.email ? [`Demo administrator: ${admin.email}`] : [],
|
|
2034
|
+
...admin?.password ? [`Public demo password: ${admin.password}`] : []
|
|
2035
|
+
].join("\n")
|
|
2036
|
+
);
|
|
2037
|
+
}
|
|
2007
2038
|
if (data2 && Array.isArray(data2.checks)) {
|
|
2008
2039
|
return decorate(
|
|
2009
2040
|
data2.checks.map((check) => {
|
|
@@ -2022,143 +2053,53 @@ ${envelope.error?.message ?? "Command failed."}`;
|
|
|
2022
2053
|
return decorate(JSON.stringify(data2, null, 2));
|
|
2023
2054
|
}
|
|
2024
2055
|
|
|
2025
|
-
//
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
}
|
|
2044
|
-
function assertModuleCompatibility(manifest2, platformVersion = PLATFORM_API_VERSION) {
|
|
2045
|
-
if (!(0, import_semver.valid)(manifest2.version))
|
|
2046
|
-
throw new RegistryError(
|
|
2047
|
-
"MODULE_VERSION_INVALID",
|
|
2048
|
-
`Invalid version for ${manifest2.id}: ${manifest2.version}`
|
|
2049
|
-
);
|
|
2050
|
-
if (manifest2.platformApi !== void 0 && (!(0, import_semver.validRange)(manifest2.platformApi) || platformVersion !== null && !satisfiesModuleVersion(platformVersion, manifest2.platformApi))) {
|
|
2051
|
-
throw new RegistryError(
|
|
2052
|
-
"MODULE_PLATFORM_INCOMPATIBLE",
|
|
2053
|
-
`${manifest2.id} requires platform API ${manifest2.platformApi}; available ${PLATFORM_API_VERSION}.`
|
|
2054
|
-
);
|
|
2055
|
-
}
|
|
2056
|
-
const ids = /* @__PURE__ */ new Set();
|
|
2057
|
-
for (const dependency of manifest2.dependencies) {
|
|
2058
|
-
if (!dependency.range.trim() || !(0, import_semver.validRange)(dependency.range))
|
|
2059
|
-
throw new RegistryError(
|
|
2060
|
-
"MODULE_RANGE_INVALID",
|
|
2061
|
-
`${manifest2.id} declares an invalid range for ${dependency.id}: ${dependency.range}`
|
|
2062
|
-
);
|
|
2063
|
-
if (ids.has(dependency.id))
|
|
2064
|
-
throw new RegistryError(
|
|
2065
|
-
"MODULE_DEPENDENCY_DUPLICATE",
|
|
2066
|
-
`${manifest2.id} declares ${dependency.id} more than once.`
|
|
2067
|
-
);
|
|
2068
|
-
ids.add(dependency.id);
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
function assertModuleDependency(consumer, dependency, version) {
|
|
2072
|
-
if (!satisfiesModuleVersion(version, dependency.range)) {
|
|
2073
|
-
throw new RegistryError(
|
|
2074
|
-
"MODULE_DEPENDENCY_INCOMPATIBLE",
|
|
2075
|
-
`${consumer} requires ${dependency.id} ${dependency.range}; available ${version}.`
|
|
2076
|
-
);
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
|
|
2080
|
-
// ../kernel/src/module-registry.ts
|
|
2081
|
-
function compareModules(left, right) {
|
|
2082
|
-
return left.manifest.id.localeCompare(right.manifest.id);
|
|
2083
|
-
}
|
|
2084
|
-
function visitModule(id, modules, visiting, visited, ordered) {
|
|
2085
|
-
if (visited.has(id)) return;
|
|
2086
|
-
if (visiting.has(id)) {
|
|
2087
|
-
throw new RegistryError(
|
|
2088
|
-
"MODULE_DEPENDENCY_CYCLE",
|
|
2089
|
-
`Dependency cycle detected at "${id}".`
|
|
2090
|
-
);
|
|
2091
|
-
}
|
|
2092
|
-
const current = modules.get(id);
|
|
2093
|
-
if (!current) {
|
|
2094
|
-
throw new RegistryError(
|
|
2095
|
-
"MODULE_NOT_FOUND",
|
|
2096
|
-
`Module "${id}" is not registered.`
|
|
2097
|
-
);
|
|
2098
|
-
}
|
|
2099
|
-
visiting.add(id);
|
|
2100
|
-
for (const dependency of [...current.manifest.dependencies].sort(
|
|
2101
|
-
(a, b) => a.id.localeCompare(b.id)
|
|
2102
|
-
)) {
|
|
2103
|
-
if (!modules.has(dependency.id)) {
|
|
2104
|
-
throw new RegistryError(
|
|
2105
|
-
"MODULE_DEPENDENCY_MISSING",
|
|
2106
|
-
`Module "${id}" requires missing module "${dependency.id}".`
|
|
2056
|
+
// src/workspace.ts
|
|
2057
|
+
import { access, readFile, realpath } from "node:fs/promises";
|
|
2058
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
2059
|
+
async function findWorkspace(start) {
|
|
2060
|
+
let directory = resolve(start);
|
|
2061
|
+
for (; ; ) {
|
|
2062
|
+
const configPath = resolve(directory, "flowdular.json");
|
|
2063
|
+
try {
|
|
2064
|
+
await access(configPath);
|
|
2065
|
+
const config = JSON.parse(await readFile(configPath, "utf8"));
|
|
2066
|
+
return { root: directory, configPath, config };
|
|
2067
|
+
} catch (error) {
|
|
2068
|
+
if (error instanceof SyntaxError) throw error;
|
|
2069
|
+
}
|
|
2070
|
+
const parent = dirname(directory);
|
|
2071
|
+
if (parent === directory) {
|
|
2072
|
+
throw new Error(
|
|
2073
|
+
"No flowdular.json was found in this directory or any parent."
|
|
2107
2074
|
);
|
|
2108
2075
|
}
|
|
2109
|
-
|
|
2110
|
-
id,
|
|
2111
|
-
dependency,
|
|
2112
|
-
modules.get(dependency.id).manifest.version
|
|
2113
|
-
);
|
|
2114
|
-
visitModule(dependency.id, modules, visiting, visited, ordered);
|
|
2076
|
+
directory = parent;
|
|
2115
2077
|
}
|
|
2116
|
-
visiting.delete(id);
|
|
2117
|
-
visited.add(id);
|
|
2118
|
-
ordered.push(current);
|
|
2119
2078
|
}
|
|
2120
|
-
function
|
|
2121
|
-
const
|
|
2122
|
-
const
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
if (byId.has(module.manifest.id)) {
|
|
2126
|
-
throw new RegistryError(
|
|
2127
|
-
"MODULE_DUPLICATE",
|
|
2128
|
-
`Module "${module.manifest.id}" is registered more than once.`
|
|
2129
|
-
);
|
|
2130
|
-
}
|
|
2131
|
-
byId.set(module.manifest.id, module);
|
|
2132
|
-
for (const item of module.navigation ?? []) {
|
|
2133
|
-
if (navigationIds.has(item.id)) {
|
|
2134
|
-
throw new RegistryError(
|
|
2135
|
-
"NAVIGATION_DUPLICATE",
|
|
2136
|
-
`Navigation contribution "${item.id}" is registered more than once.`
|
|
2137
|
-
);
|
|
2138
|
-
}
|
|
2139
|
-
navigationIds.add(item.id);
|
|
2140
|
-
}
|
|
2079
|
+
function resolveInside(root, candidate) {
|
|
2080
|
+
const absolute = isAbsolute(candidate) ? resolve(candidate) : resolve(root, candidate);
|
|
2081
|
+
const pathFromRoot = relative(root, absolute);
|
|
2082
|
+
if (pathFromRoot.startsWith("..") || isAbsolute(pathFromRoot)) {
|
|
2083
|
+
throw new Error(`Path escapes the workspace: ${candidate}`);
|
|
2141
2084
|
}
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2085
|
+
return absolute;
|
|
2086
|
+
}
|
|
2087
|
+
async function resolveExistingInside(root, candidate) {
|
|
2088
|
+
const canonicalRoot = await realpath(root);
|
|
2089
|
+
const canonicalCandidate = await realpath(resolveInside(root, candidate));
|
|
2090
|
+
const pathFromRoot = relative(canonicalRoot, canonicalCandidate);
|
|
2091
|
+
if (pathFromRoot.startsWith("..") || isAbsolute(pathFromRoot)) {
|
|
2092
|
+
throw new Error(`Path escapes the workspace through a link: ${candidate}`);
|
|
2147
2093
|
}
|
|
2148
|
-
return
|
|
2149
|
-
modules: Object.freeze(ordered),
|
|
2150
|
-
get: (id) => byId.get(id),
|
|
2151
|
-
has: (id) => byId.has(id),
|
|
2152
|
-
navigation: (permissions) => Object.freeze(
|
|
2153
|
-
ordered.flatMap((module) => module.navigation ?? []).filter(
|
|
2154
|
-
(item) => !item.permission || permissions?.has(item.permission)
|
|
2155
|
-
).sort(
|
|
2156
|
-
(left, right) => (left.order ?? 1e3) - (right.order ?? 1e3) || left.id.localeCompare(right.id)
|
|
2157
|
-
)
|
|
2158
|
-
)
|
|
2159
|
-
});
|
|
2094
|
+
return canonicalCandidate;
|
|
2160
2095
|
}
|
|
2161
2096
|
|
|
2097
|
+
// src/validation.ts
|
|
2098
|
+
import { readdir, readFile as readFile2 } from "node:fs/promises";
|
|
2099
|
+
import { basename, dirname as dirname2, join, relative as relative2 } from "node:path";
|
|
2100
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
2101
|
+
import { parse as parseYaml } from "yaml";
|
|
2102
|
+
|
|
2162
2103
|
// ../contracts/schemas/blueprint.schema.json
|
|
2163
2104
|
var blueprint_schema_default = {
|
|
2164
2105
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -2619,10 +2560,7 @@ var project_schema_default = {
|
|
|
2619
2560
|
specs: {
|
|
2620
2561
|
type: "object",
|
|
2621
2562
|
additionalProperties: false,
|
|
2622
|
-
required: [
|
|
2623
|
-
"platformRoot",
|
|
2624
|
-
"moduleDirectory"
|
|
2625
|
-
],
|
|
2563
|
+
required: ["platformRoot", "moduleDirectory"],
|
|
2626
2564
|
properties: {
|
|
2627
2565
|
platformRoot: {
|
|
2628
2566
|
type: "string",
|
|
@@ -2637,10 +2575,7 @@ var project_schema_default = {
|
|
|
2637
2575
|
modules: {
|
|
2638
2576
|
type: "object",
|
|
2639
2577
|
additionalProperties: false,
|
|
2640
|
-
required: [
|
|
2641
|
-
"roots",
|
|
2642
|
-
"enabled"
|
|
2643
|
-
],
|
|
2578
|
+
required: ["roots", "enabled"],
|
|
2644
2579
|
properties: {
|
|
2645
2580
|
roots: {
|
|
2646
2581
|
type: "array",
|
|
@@ -2673,11 +2608,7 @@ var project_schema_default = {
|
|
|
2673
2608
|
agent: {
|
|
2674
2609
|
type: "object",
|
|
2675
2610
|
additionalProperties: false,
|
|
2676
|
-
required: [
|
|
2677
|
-
"policy",
|
|
2678
|
-
"blueprints",
|
|
2679
|
-
"modelRouting"
|
|
2680
|
-
],
|
|
2611
|
+
required: ["policy", "blueprints", "modelRouting"],
|
|
2681
2612
|
properties: {
|
|
2682
2613
|
policy: {
|
|
2683
2614
|
type: "string",
|
|
@@ -2729,17 +2660,10 @@ var project_schema_default = {
|
|
|
2729
2660
|
$ref: "#/$defs/gitName"
|
|
2730
2661
|
},
|
|
2731
2662
|
provider: {
|
|
2732
|
-
enum: [
|
|
2733
|
-
"github",
|
|
2734
|
-
"none"
|
|
2735
|
-
]
|
|
2663
|
+
enum: ["github", "none"]
|
|
2736
2664
|
},
|
|
2737
2665
|
mode: {
|
|
2738
|
-
enum: [
|
|
2739
|
-
"auto",
|
|
2740
|
-
"direct",
|
|
2741
|
-
"fork"
|
|
2742
|
-
]
|
|
2666
|
+
enum: ["auto", "direct", "fork"]
|
|
2743
2667
|
},
|
|
2744
2668
|
forkOwner: {
|
|
2745
2669
|
$ref: "#/$defs/githubAccount"
|
|
@@ -2765,9 +2689,7 @@ var project_schema_default = {
|
|
|
2765
2689
|
web: {
|
|
2766
2690
|
type: "object",
|
|
2767
2691
|
additionalProperties: false,
|
|
2768
|
-
required: [
|
|
2769
|
-
"mounts"
|
|
2770
|
-
],
|
|
2692
|
+
required: ["mounts"],
|
|
2771
2693
|
properties: {
|
|
2772
2694
|
mounts: {
|
|
2773
2695
|
type: "array",
|
|
@@ -2775,13 +2697,7 @@ var project_schema_default = {
|
|
|
2775
2697
|
items: {
|
|
2776
2698
|
type: "object",
|
|
2777
2699
|
additionalProperties: false,
|
|
2778
|
-
required: [
|
|
2779
|
-
"id",
|
|
2780
|
-
"moduleId",
|
|
2781
|
-
"surfaceId",
|
|
2782
|
-
"path",
|
|
2783
|
-
"tenantId"
|
|
2784
|
-
],
|
|
2700
|
+
required: ["id", "moduleId", "surfaceId", "path", "tenantId"],
|
|
2785
2701
|
properties: {
|
|
2786
2702
|
id: {
|
|
2787
2703
|
type: "string",
|
|
@@ -2816,9 +2732,7 @@ var project_schema_default = {
|
|
|
2816
2732
|
application: {
|
|
2817
2733
|
type: "object",
|
|
2818
2734
|
additionalProperties: false,
|
|
2819
|
-
required: [
|
|
2820
|
-
"path"
|
|
2821
|
-
],
|
|
2735
|
+
required: ["path"],
|
|
2822
2736
|
properties: {
|
|
2823
2737
|
path: {
|
|
2824
2738
|
type: "string",
|
|
@@ -2832,20 +2746,21 @@ var project_schema_default = {
|
|
|
2832
2746
|
"/sign-up",
|
|
2833
2747
|
"/forgot-password",
|
|
2834
2748
|
"/reset-password",
|
|
2835
|
-
"/accept-invitation"
|
|
2749
|
+
"/accept-invitation",
|
|
2750
|
+
"/setup",
|
|
2751
|
+
"/health",
|
|
2752
|
+
"/ready",
|
|
2753
|
+
"/assets"
|
|
2836
2754
|
]
|
|
2837
|
-
}
|
|
2755
|
+
},
|
|
2756
|
+
maxLength: 64
|
|
2838
2757
|
}
|
|
2839
2758
|
}
|
|
2840
2759
|
}
|
|
2841
2760
|
},
|
|
2842
2761
|
$defs: {
|
|
2843
2762
|
deliveryTarget: {
|
|
2844
|
-
enum: [
|
|
2845
|
-
"workspace",
|
|
2846
|
-
"git-pr",
|
|
2847
|
-
"official-modules"
|
|
2848
|
-
]
|
|
2763
|
+
enum: ["workspace", "git-pr", "official-modules"]
|
|
2849
2764
|
},
|
|
2850
2765
|
gitName: {
|
|
2851
2766
|
type: "string",
|
|
@@ -3007,19 +2922,7 @@ var projectSchema = project_schema_default;
|
|
|
3007
2922
|
var moduleCatalogSchema = module_catalog_schema_default;
|
|
3008
2923
|
var moduleArtifactSchema = module_artifact_schema_default;
|
|
3009
2924
|
|
|
3010
|
-
// src/module-catalog.ts
|
|
3011
|
-
import { readFile as readFile3, stat } from "node:fs/promises";
|
|
3012
|
-
import { dirname as dirname3, resolve as resolve2 } from "node:path";
|
|
3013
|
-
|
|
3014
|
-
// src/module-artifact.ts
|
|
3015
|
-
import { parse as parseYaml2 } from "yaml";
|
|
3016
|
-
import { createHash } from "node:crypto";
|
|
3017
|
-
|
|
3018
2925
|
// src/validation.ts
|
|
3019
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
3020
|
-
import { basename, dirname, join, relative } from "node:path";
|
|
3021
|
-
import Ajv2020 from "ajv/dist/2020.js";
|
|
3022
|
-
import { parse as parseYaml } from "yaml";
|
|
3023
2926
|
var ajv = new Ajv2020({ allErrors: true, strict: true });
|
|
3024
2927
|
var validators = {
|
|
3025
2928
|
application: ajv.compile(projectSchema.properties.application),
|
|
@@ -3042,7 +2945,7 @@ function issuesFrom(errors) {
|
|
|
3042
2945
|
}));
|
|
3043
2946
|
}
|
|
3044
2947
|
async function parseFile(path) {
|
|
3045
|
-
const source = await
|
|
2948
|
+
const source = await readFile2(path, "utf8");
|
|
3046
2949
|
return path.endsWith(".yaml") || path.endsWith(".yml") ? parseYaml(source) : JSON.parse(source);
|
|
3047
2950
|
}
|
|
3048
2951
|
async function validateFile(path, validator) {
|
|
@@ -3106,7 +3009,7 @@ var blueprintFiles = [
|
|
|
3106
3009
|
];
|
|
3107
3010
|
async function validateBlueprint(path) {
|
|
3108
3011
|
const report = await validateFile(path, validators.blueprint);
|
|
3109
|
-
const directory =
|
|
3012
|
+
const directory = dirname2(path);
|
|
3110
3013
|
const entries = new Set(
|
|
3111
3014
|
(await readdir(directory)).filter((name) => basename(name) === name)
|
|
3112
3015
|
);
|
|
@@ -3117,14 +3020,177 @@ async function validateBlueprint(path) {
|
|
|
3117
3020
|
...missing.map((name) => ({
|
|
3118
3021
|
code: "BLUEPRINT_FILE_MISSING",
|
|
3119
3022
|
message: `Required blueprint file is missing: ${name}`,
|
|
3120
|
-
path:
|
|
3023
|
+
path: relative2(process.cwd(), join(directory, name)),
|
|
3121
3024
|
severity: "error"
|
|
3122
3025
|
}))
|
|
3123
3026
|
];
|
|
3124
3027
|
return { ...report, valid: false, issues };
|
|
3125
3028
|
}
|
|
3126
3029
|
|
|
3030
|
+
// src/agent-resources.ts
|
|
3031
|
+
function agentResource(workspace, key) {
|
|
3032
|
+
const defaults = {
|
|
3033
|
+
policy: ".ai/policies/capabilities.yaml",
|
|
3034
|
+
modelRouting: ".ai/policies/model-routing.yaml",
|
|
3035
|
+
blueprints: ".ai/blueprints"
|
|
3036
|
+
};
|
|
3037
|
+
const agent = workspace.config.agent;
|
|
3038
|
+
const path = agent?.[key] ?? defaults[key];
|
|
3039
|
+
if (typeof path !== "string" || !path)
|
|
3040
|
+
throw new Error(`Invalid agent.${key} path.`);
|
|
3041
|
+
return resolveInside(workspace.root, path);
|
|
3042
|
+
}
|
|
3043
|
+
function findBlueprintFiles(workspace) {
|
|
3044
|
+
return findNamedFiles(
|
|
3045
|
+
agentResource(workspace, "blueprints"),
|
|
3046
|
+
"blueprint.json"
|
|
3047
|
+
);
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
// ../kernel/src/errors.ts
|
|
3051
|
+
var RegistryError = class extends Error {
|
|
3052
|
+
code;
|
|
3053
|
+
constructor(code, message) {
|
|
3054
|
+
super(message);
|
|
3055
|
+
this.name = "RegistryError";
|
|
3056
|
+
this.code = code;
|
|
3057
|
+
}
|
|
3058
|
+
};
|
|
3059
|
+
|
|
3060
|
+
// ../kernel/src/module-compatibility.ts
|
|
3061
|
+
var import_semver = __toESM(require_semver2(), 1);
|
|
3062
|
+
var PLATFORM_API_VERSION = "0.1.0";
|
|
3063
|
+
function satisfiesModuleVersion(version, range) {
|
|
3064
|
+
return (0, import_semver.valid)(version) !== null && (0, import_semver.validRange)(range) !== null && (0, import_semver.satisfies)(version, range);
|
|
3065
|
+
}
|
|
3066
|
+
function compareModuleVersions(left, right) {
|
|
3067
|
+
return (0, import_semver.rcompare)(left, right);
|
|
3068
|
+
}
|
|
3069
|
+
function assertModuleCompatibility(manifest2, platformVersion = PLATFORM_API_VERSION) {
|
|
3070
|
+
if (!(0, import_semver.valid)(manifest2.version))
|
|
3071
|
+
throw new RegistryError(
|
|
3072
|
+
"MODULE_VERSION_INVALID",
|
|
3073
|
+
`Invalid version for ${manifest2.id}: ${manifest2.version}`
|
|
3074
|
+
);
|
|
3075
|
+
if (manifest2.platformApi !== void 0 && (!(0, import_semver.validRange)(manifest2.platformApi) || platformVersion !== null && !satisfiesModuleVersion(platformVersion, manifest2.platformApi))) {
|
|
3076
|
+
throw new RegistryError(
|
|
3077
|
+
"MODULE_PLATFORM_INCOMPATIBLE",
|
|
3078
|
+
`${manifest2.id} requires platform API ${manifest2.platformApi}; available ${PLATFORM_API_VERSION}.`
|
|
3079
|
+
);
|
|
3080
|
+
}
|
|
3081
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3082
|
+
for (const dependency of manifest2.dependencies) {
|
|
3083
|
+
if (!dependency.range.trim() || !(0, import_semver.validRange)(dependency.range))
|
|
3084
|
+
throw new RegistryError(
|
|
3085
|
+
"MODULE_RANGE_INVALID",
|
|
3086
|
+
`${manifest2.id} declares an invalid range for ${dependency.id}: ${dependency.range}`
|
|
3087
|
+
);
|
|
3088
|
+
if (ids.has(dependency.id))
|
|
3089
|
+
throw new RegistryError(
|
|
3090
|
+
"MODULE_DEPENDENCY_DUPLICATE",
|
|
3091
|
+
`${manifest2.id} declares ${dependency.id} more than once.`
|
|
3092
|
+
);
|
|
3093
|
+
ids.add(dependency.id);
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
function assertModuleDependency(consumer, dependency, version) {
|
|
3097
|
+
if (!satisfiesModuleVersion(version, dependency.range)) {
|
|
3098
|
+
throw new RegistryError(
|
|
3099
|
+
"MODULE_DEPENDENCY_INCOMPATIBLE",
|
|
3100
|
+
`${consumer} requires ${dependency.id} ${dependency.range}; available ${version}.`
|
|
3101
|
+
);
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
// ../kernel/src/module-registry.ts
|
|
3106
|
+
function compareModules(left, right) {
|
|
3107
|
+
return left.manifest.id.localeCompare(right.manifest.id);
|
|
3108
|
+
}
|
|
3109
|
+
function visitModule(id, modules, visiting, visited, ordered) {
|
|
3110
|
+
if (visited.has(id)) return;
|
|
3111
|
+
if (visiting.has(id)) {
|
|
3112
|
+
throw new RegistryError(
|
|
3113
|
+
"MODULE_DEPENDENCY_CYCLE",
|
|
3114
|
+
`Dependency cycle detected at "${id}".`
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
3117
|
+
const current = modules.get(id);
|
|
3118
|
+
if (!current) {
|
|
3119
|
+
throw new RegistryError(
|
|
3120
|
+
"MODULE_NOT_FOUND",
|
|
3121
|
+
`Module "${id}" is not registered.`
|
|
3122
|
+
);
|
|
3123
|
+
}
|
|
3124
|
+
visiting.add(id);
|
|
3125
|
+
for (const dependency of [...current.manifest.dependencies].sort(
|
|
3126
|
+
(a, b) => a.id.localeCompare(b.id)
|
|
3127
|
+
)) {
|
|
3128
|
+
if (!modules.has(dependency.id)) {
|
|
3129
|
+
throw new RegistryError(
|
|
3130
|
+
"MODULE_DEPENDENCY_MISSING",
|
|
3131
|
+
`Module "${id}" requires missing module "${dependency.id}".`
|
|
3132
|
+
);
|
|
3133
|
+
}
|
|
3134
|
+
assertModuleDependency(
|
|
3135
|
+
id,
|
|
3136
|
+
dependency,
|
|
3137
|
+
modules.get(dependency.id).manifest.version
|
|
3138
|
+
);
|
|
3139
|
+
visitModule(dependency.id, modules, visiting, visited, ordered);
|
|
3140
|
+
}
|
|
3141
|
+
visiting.delete(id);
|
|
3142
|
+
visited.add(id);
|
|
3143
|
+
ordered.push(current);
|
|
3144
|
+
}
|
|
3145
|
+
function createModuleRegistry(input) {
|
|
3146
|
+
const byId = /* @__PURE__ */ new Map();
|
|
3147
|
+
const navigationIds = /* @__PURE__ */ new Set();
|
|
3148
|
+
for (const module of input) {
|
|
3149
|
+
assertModuleCompatibility(module.manifest);
|
|
3150
|
+
if (byId.has(module.manifest.id)) {
|
|
3151
|
+
throw new RegistryError(
|
|
3152
|
+
"MODULE_DUPLICATE",
|
|
3153
|
+
`Module "${module.manifest.id}" is registered more than once.`
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
3156
|
+
byId.set(module.manifest.id, module);
|
|
3157
|
+
for (const item of module.navigation ?? []) {
|
|
3158
|
+
if (navigationIds.has(item.id)) {
|
|
3159
|
+
throw new RegistryError(
|
|
3160
|
+
"NAVIGATION_DUPLICATE",
|
|
3161
|
+
`Navigation contribution "${item.id}" is registered more than once.`
|
|
3162
|
+
);
|
|
3163
|
+
}
|
|
3164
|
+
navigationIds.add(item.id);
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
const ordered = [];
|
|
3168
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
3169
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3170
|
+
for (const module of [...input].sort(compareModules)) {
|
|
3171
|
+
visitModule(module.manifest.id, byId, visiting, visited, ordered);
|
|
3172
|
+
}
|
|
3173
|
+
return Object.freeze({
|
|
3174
|
+
modules: Object.freeze(ordered),
|
|
3175
|
+
get: (id) => byId.get(id),
|
|
3176
|
+
has: (id) => byId.has(id),
|
|
3177
|
+
navigation: (permissions) => Object.freeze(
|
|
3178
|
+
ordered.flatMap((module) => module.navigation ?? []).filter(
|
|
3179
|
+
(item) => !item.permission || permissions?.has(item.permission)
|
|
3180
|
+
).sort(
|
|
3181
|
+
(left, right) => (left.order ?? 1e3) - (right.order ?? 1e3) || left.id.localeCompare(right.id)
|
|
3182
|
+
)
|
|
3183
|
+
)
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
// src/module-catalog.ts
|
|
3188
|
+
import { readFile as readFile3, stat } from "node:fs/promises";
|
|
3189
|
+
import { dirname as dirname3, resolve as resolve2 } from "node:path";
|
|
3190
|
+
|
|
3127
3191
|
// src/module-artifact.ts
|
|
3192
|
+
import { parse as parseYaml2 } from "yaml";
|
|
3193
|
+
import { createHash } from "node:crypto";
|
|
3128
3194
|
var MAX_ARTIFACT_BYTES = 48 * 1024 * 1024;
|
|
3129
3195
|
var MAX_SOURCE_BYTES = 32 * 1024 * 1024;
|
|
3130
3196
|
var MAX_FILES = 4096;
|
|
@@ -3145,6 +3211,7 @@ var ModuleDistributionError = class extends Error {
|
|
|
3145
3211
|
super(message);
|
|
3146
3212
|
this.code = code;
|
|
3147
3213
|
}
|
|
3214
|
+
code;
|
|
3148
3215
|
};
|
|
3149
3216
|
function distributionAssert(condition, code, message) {
|
|
3150
3217
|
if (!condition) throw new ModuleDistributionError(code, message);
|
|
@@ -3282,47 +3349,6 @@ function validateModuleArtifact(value) {
|
|
|
3282
3349
|
return artifact;
|
|
3283
3350
|
}
|
|
3284
3351
|
|
|
3285
|
-
// src/workspace.ts
|
|
3286
|
-
import { access, readFile as readFile2, realpath } from "node:fs/promises";
|
|
3287
|
-
import { dirname as dirname2, isAbsolute, relative as relative2, resolve } from "node:path";
|
|
3288
|
-
async function findWorkspace(start) {
|
|
3289
|
-
let directory = resolve(start);
|
|
3290
|
-
for (; ; ) {
|
|
3291
|
-
const configPath = resolve(directory, "flowdular.json");
|
|
3292
|
-
try {
|
|
3293
|
-
await access(configPath);
|
|
3294
|
-
const config = JSON.parse(await readFile2(configPath, "utf8"));
|
|
3295
|
-
return { root: directory, configPath, config };
|
|
3296
|
-
} catch (error) {
|
|
3297
|
-
if (error instanceof SyntaxError) throw error;
|
|
3298
|
-
}
|
|
3299
|
-
const parent = dirname2(directory);
|
|
3300
|
-
if (parent === directory) {
|
|
3301
|
-
throw new Error(
|
|
3302
|
-
"No flowdular.json was found in this directory or any parent."
|
|
3303
|
-
);
|
|
3304
|
-
}
|
|
3305
|
-
directory = parent;
|
|
3306
|
-
}
|
|
3307
|
-
}
|
|
3308
|
-
function resolveInside(root, candidate) {
|
|
3309
|
-
const absolute = isAbsolute(candidate) ? resolve(candidate) : resolve(root, candidate);
|
|
3310
|
-
const pathFromRoot = relative2(root, absolute);
|
|
3311
|
-
if (pathFromRoot.startsWith("..") || isAbsolute(pathFromRoot)) {
|
|
3312
|
-
throw new Error(`Path escapes the workspace: ${candidate}`);
|
|
3313
|
-
}
|
|
3314
|
-
return absolute;
|
|
3315
|
-
}
|
|
3316
|
-
async function resolveExistingInside(root, candidate) {
|
|
3317
|
-
const canonicalRoot = await realpath(root);
|
|
3318
|
-
const canonicalCandidate = await realpath(resolveInside(root, candidate));
|
|
3319
|
-
const pathFromRoot = relative2(canonicalRoot, canonicalCandidate);
|
|
3320
|
-
if (pathFromRoot.startsWith("..") || isAbsolute(pathFromRoot)) {
|
|
3321
|
-
throw new Error(`Path escapes the workspace through a link: ${candidate}`);
|
|
3322
|
-
}
|
|
3323
|
-
return canonicalCandidate;
|
|
3324
|
-
}
|
|
3325
|
-
|
|
3326
3352
|
// src/module-catalog.ts
|
|
3327
3353
|
var OFFICIAL_CATALOG = "https://raw.githubusercontent.com/Flowdular/official-modules/main/registry/index.json";
|
|
3328
3354
|
var OFFICIAL_PREFIX = "https://raw.githubusercontent.com/Flowdular/official-modules/";
|
|
@@ -3520,12 +3546,11 @@ import { dirname as dirname6, join as join5, relative as relative5 } from "node:
|
|
|
3520
3546
|
import { createRequire } from "node:module";
|
|
3521
3547
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
3522
3548
|
import { dirname as dirname4, join as join2 } from "node:path";
|
|
3523
|
-
var SDK_VERSION = "0.1
|
|
3549
|
+
var SDK_VERSION = "0.2.1";
|
|
3524
3550
|
var LIBRARIES = /* @__PURE__ */ new Set([
|
|
3525
3551
|
"ai-provider",
|
|
3526
3552
|
"cli-protocol",
|
|
3527
3553
|
"client",
|
|
3528
|
-
"coding-agent",
|
|
3529
3554
|
"contracts",
|
|
3530
3555
|
"database",
|
|
3531
3556
|
"database-pglite",
|
|
@@ -3533,7 +3558,6 @@ var LIBRARIES = /* @__PURE__ */ new Set([
|
|
|
3533
3558
|
"dev-console",
|
|
3534
3559
|
"harness",
|
|
3535
3560
|
"kernel",
|
|
3536
|
-
"sandbox",
|
|
3537
3561
|
"server",
|
|
3538
3562
|
"ui"
|
|
3539
3563
|
]);
|
|
@@ -3614,14 +3638,14 @@ import { createRequire as createRequire2 } from "node:module";
|
|
|
3614
3638
|
import { lstat, readFile as readFile5 } from "node:fs/promises";
|
|
3615
3639
|
import { join as join3, relative as relative3 } from "node:path";
|
|
3616
3640
|
function moduleRoots(workspace) {
|
|
3617
|
-
const
|
|
3618
|
-
if (!Array.isArray(
|
|
3641
|
+
const roots2 = workspace.config.modules?.roots ?? ["modules"];
|
|
3642
|
+
if (!Array.isArray(roots2) || !roots2.length || roots2.some(
|
|
3619
3643
|
(root) => typeof root !== "string" || !root || root.startsWith(".") || root.includes("\\") || root.split("/").some((part) => part === ".." || !part)
|
|
3620
3644
|
))
|
|
3621
3645
|
throw new Error(
|
|
3622
3646
|
"Invalid modules.roots. Use relative workspace directories."
|
|
3623
3647
|
);
|
|
3624
|
-
return
|
|
3648
|
+
return roots2.map((root) => {
|
|
3625
3649
|
const path = resolveInside(workspace.root, root);
|
|
3626
3650
|
if (!relative3(workspace.root, path))
|
|
3627
3651
|
throw new Error("The workspace root cannot be a module root.");
|
|
@@ -4030,10 +4054,10 @@ async function validateModules(workspace, options = {}) {
|
|
|
4030
4054
|
);
|
|
4031
4055
|
}
|
|
4032
4056
|
try {
|
|
4033
|
-
const
|
|
4057
|
+
const registered2 = manifests.map(
|
|
4034
4058
|
(manifest2) => ({ manifest: manifest2 })
|
|
4035
4059
|
);
|
|
4036
|
-
const registry = createModuleRegistry(
|
|
4060
|
+
const registry = createModuleRegistry(registered2);
|
|
4037
4061
|
const enabled = config.modules?.enabled ?? [];
|
|
4038
4062
|
const missingEnabled = enabled.filter((id) => !registry.has(id));
|
|
4039
4063
|
if (missingEnabled.length > 0) {
|
|
@@ -4514,6 +4538,7 @@ var DatabaseError = class extends Error {
|
|
|
4514
4538
|
this.code = code;
|
|
4515
4539
|
this.name = "DatabaseError";
|
|
4516
4540
|
}
|
|
4541
|
+
code;
|
|
4517
4542
|
};
|
|
4518
4543
|
function assertStatement(statement) {
|
|
4519
4544
|
if (statement.text.trim().length === 0) {
|
|
@@ -4687,6 +4712,8 @@ var DatabaseMigrationError = class extends Error {
|
|
|
4687
4712
|
this.migrationId = migrationId;
|
|
4688
4713
|
this.name = "DatabaseMigrationError";
|
|
4689
4714
|
}
|
|
4715
|
+
code;
|
|
4716
|
+
migrationId;
|
|
4690
4717
|
};
|
|
4691
4718
|
function assertMigrationId(id) {
|
|
4692
4719
|
if (!/^[0-9]{4}_[a-z][a-z0-9_]{0,122}$/.test(id)) {
|
|
@@ -5030,6 +5057,9 @@ var PostgresSchemaIntrospector = class {
|
|
|
5030
5057
|
this.baseSignal = baseSignal;
|
|
5031
5058
|
this.ensureActive = ensureActive;
|
|
5032
5059
|
}
|
|
5060
|
+
client;
|
|
5061
|
+
baseSignal;
|
|
5062
|
+
ensureActive;
|
|
5033
5063
|
async hasTable(name, options = {}) {
|
|
5034
5064
|
this.ensureActive();
|
|
5035
5065
|
assertSchemaName(name);
|
|
@@ -5091,6 +5121,10 @@ var PostgresSession = class {
|
|
|
5091
5121
|
ensureActive
|
|
5092
5122
|
);
|
|
5093
5123
|
}
|
|
5124
|
+
client;
|
|
5125
|
+
baseSignal;
|
|
5126
|
+
ensureActive;
|
|
5127
|
+
capabilities;
|
|
5094
5128
|
adapterId = DATABASE_ADAPTER_IDS.postgresql;
|
|
5095
5129
|
dialectId = DATABASE_DIALECT_IDS.postgresql;
|
|
5096
5130
|
schema;
|
|
@@ -6017,19 +6051,35 @@ function createPgliteCluster(options = {}) {
|
|
|
6017
6051
|
const close = () => {
|
|
6018
6052
|
closePromise ??= (async () => {
|
|
6019
6053
|
if (!databasePromise) return;
|
|
6020
|
-
const instance = await databasePromise;
|
|
6054
|
+
const instance = await databasePromise.catch(() => void 0);
|
|
6021
6055
|
databasePromise = void 0;
|
|
6022
|
-
await instance
|
|
6056
|
+
await instance?.close();
|
|
6023
6057
|
})();
|
|
6024
6058
|
return closePromise;
|
|
6025
6059
|
};
|
|
6026
6060
|
const database = async () => {
|
|
6027
6061
|
databasePromise ??= (async () => {
|
|
6062
|
+
if (options.dataDirectory) {
|
|
6063
|
+
const { mkdir: mkdir6 } = await import("node:fs/promises");
|
|
6064
|
+
await mkdir6(options.dataDirectory, { recursive: true, mode: 448 });
|
|
6065
|
+
}
|
|
6028
6066
|
const created = options.dataDirectory ? await PGlite.create({
|
|
6029
6067
|
dataDir: options.dataDirectory,
|
|
6030
6068
|
parsers: SERVER_PARSERS
|
|
6031
6069
|
}) : await PGlite.create({ parsers: SERVER_PARSERS });
|
|
6032
|
-
|
|
6070
|
+
try {
|
|
6071
|
+
if (options.bootstrap) await created.exec(options.bootstrap);
|
|
6072
|
+
} catch (error) {
|
|
6073
|
+
try {
|
|
6074
|
+
await created.close();
|
|
6075
|
+
} catch (closeError) {
|
|
6076
|
+
throw new AggregateError(
|
|
6077
|
+
[error, closeError],
|
|
6078
|
+
"PGlite bootstrap failed and cleanup also failed."
|
|
6079
|
+
);
|
|
6080
|
+
}
|
|
6081
|
+
throw error;
|
|
6082
|
+
}
|
|
6033
6083
|
return created;
|
|
6034
6084
|
})();
|
|
6035
6085
|
return databasePromise;
|
|
@@ -6352,9 +6402,10 @@ function generateServerComposition(modules, sdk = false, mounts = [], applicatio
|
|
|
6352
6402
|
PlatformServerComposition,
|
|
6353
6403
|
PlatformServerContext,
|
|
6354
6404
|
} from '${sdk ? "@flowdular/sdk/modules/auth" : "@flowdular/module-auth"}/server';
|
|
6405
|
+
import type { WebMount } from '${sdk ? "@flowdular/sdk/server" : "@flowdular/server"}';
|
|
6355
6406
|
`;
|
|
6356
6407
|
if (server.length === 0) {
|
|
6357
|
-
return GENERATED_HEADER + typeImport + "\nexport function composeModuleServer(\n _context: PlatformServerContext,\n): readonly PlatformServerComposition[] {\n return [];\n}\n\nexport const moduleWebMounts = " + JSON.stringify(mounts) + " as const;\nexport const applicationBasePath = " + JSON.stringify(applicationPath) + ";\n";
|
|
6408
|
+
return GENERATED_HEADER + typeImport + "\nexport function composeModuleServer(\n _context: PlatformServerContext,\n): readonly PlatformServerComposition[] {\n return [];\n}\n\nexport const moduleWebMounts: readonly WebMount[] = " + JSON.stringify(mounts) + " as const;\nexport const applicationBasePath = " + JSON.stringify(applicationPath) + ";\n";
|
|
6358
6409
|
}
|
|
6359
6410
|
const imports = server.map(
|
|
6360
6411
|
(module) => platformImport(module.id, module.package, "createServerComposition")
|
|
@@ -6365,7 +6416,7 @@ function generateServerComposition(modules, sdk = false, mounts = [], applicatio
|
|
|
6365
6416
|
agentDefinitions: context.agentDefinitions.forModule('${module.id}'),
|
|
6366
6417
|
}), moduleId: '${module.id}' },`
|
|
6367
6418
|
).join("\n");
|
|
6368
|
-
return GENERATED_HEADER + typeImport + imports + "\n\nexport function composeModuleServer(\n context: PlatformServerContext,\n): readonly PlatformServerComposition[] {\n return [\n" + calls + "\n ];\n}\n\nexport const moduleWebMounts = " + JSON.stringify(mounts) + " as const;\nexport const applicationBasePath = " + JSON.stringify(applicationPath) + ";\n";
|
|
6419
|
+
return GENERATED_HEADER + typeImport + imports + "\n\nexport function composeModuleServer(\n context: PlatformServerContext,\n): readonly PlatformServerComposition[] {\n return [\n" + calls + "\n ];\n}\n\nexport const moduleWebMounts: readonly WebMount[] = " + JSON.stringify(mounts) + " as const;\nexport const applicationBasePath = " + JSON.stringify(applicationPath) + ";\n";
|
|
6369
6420
|
}
|
|
6370
6421
|
function generateClientComposition(modules, sdk = false, applicationPath = "/app") {
|
|
6371
6422
|
const client = modules.filter((module) => module.client);
|
|
@@ -6406,8 +6457,8 @@ async function loadManifests(workspace) {
|
|
|
6406
6457
|
}
|
|
6407
6458
|
return descriptors;
|
|
6408
6459
|
}
|
|
6409
|
-
function moduleOrder(manifests,
|
|
6410
|
-
const requested = new Set(
|
|
6460
|
+
function moduleOrder(manifests, roots2, includeDependencies) {
|
|
6461
|
+
const requested = new Set(roots2);
|
|
6411
6462
|
const visiting = [];
|
|
6412
6463
|
const visited = /* @__PURE__ */ new Set();
|
|
6413
6464
|
const ordered = [];
|
|
@@ -6492,7 +6543,8 @@ async function restoreSnapshots(snapshots) {
|
|
|
6492
6543
|
async function syncPlatformModules(workspace, apply, enabledOverride) {
|
|
6493
6544
|
if (workspace.config.web !== void 0 && !validators.web(workspace.config.web))
|
|
6494
6545
|
throw new Error("Invalid web mount configuration in flowdular.json.");
|
|
6495
|
-
if (workspace.config.application !== void 0 && !validators.application(workspace.config.application))
|
|
6546
|
+
if (workspace.config.application !== void 0 && !validators.application(workspace.config.application))
|
|
6547
|
+
throw new Error("Invalid application path in flowdular.json.");
|
|
6496
6548
|
const applicationPath = workspace.config.application?.path ?? "/app";
|
|
6497
6549
|
const enabled = enabledOverride ?? enabledModules(workspace);
|
|
6498
6550
|
const sdk = (await sdkModules(workspace)).size > 0;
|
|
@@ -6505,7 +6557,8 @@ async function syncPlatformModules(workspace, apply, enabledOverride) {
|
|
|
6505
6557
|
content: generateServerComposition(
|
|
6506
6558
|
modules,
|
|
6507
6559
|
sdk,
|
|
6508
|
-
workspace.config.web?.mounts ?? []
|
|
6560
|
+
workspace.config.web?.mounts ?? [],
|
|
6561
|
+
applicationPath
|
|
6509
6562
|
)
|
|
6510
6563
|
},
|
|
6511
6564
|
{
|
|
@@ -6749,14 +6802,15 @@ async function runDoctor(workspace) {
|
|
|
6749
6802
|
message: project.valid ? "Project contract is valid." : project.issues.map((issue2) => issue2.message).join("; "),
|
|
6750
6803
|
evidence: relative6(workspace.root, workspace.configPath)
|
|
6751
6804
|
});
|
|
6752
|
-
for (const [id,
|
|
6753
|
-
["policy.capabilities", "
|
|
6754
|
-
["policy.model-routing", "
|
|
6755
|
-
["blueprints.root", "
|
|
6756
|
-
["modules.root", "modules"],
|
|
6757
|
-
["packages.root", "packages"],
|
|
6758
|
-
["platform.root", "platform"]
|
|
6805
|
+
for (const [id, resource] of [
|
|
6806
|
+
["policy.capabilities", agentResource(workspace, "policy")],
|
|
6807
|
+
["policy.model-routing", agentResource(workspace, "modelRouting")],
|
|
6808
|
+
["blueprints.root", agentResource(workspace, "blueprints")],
|
|
6809
|
+
["modules.root", join9(workspace.root, "modules")],
|
|
6810
|
+
...(await sdkModules(workspace)).size ? [] : [["packages.root", join9(workspace.root, "packages")]],
|
|
6811
|
+
["platform.root", join9(workspace.root, "platform")]
|
|
6759
6812
|
]) {
|
|
6813
|
+
const path = relative6(workspace.root, resource);
|
|
6760
6814
|
checks.push({
|
|
6761
6815
|
id,
|
|
6762
6816
|
status: await exists3(join9(workspace.root, path)) ? "pass" : "fail",
|
|
@@ -6773,7 +6827,7 @@ async function runDoctor(workspace) {
|
|
|
6773
6827
|
message: packageJson2.packageManager ? `Pinned package manager: ${packageJson2.packageManager}` : "packageManager must pin pnpm.",
|
|
6774
6828
|
evidence: "package.json"
|
|
6775
6829
|
});
|
|
6776
|
-
const blueprints = await
|
|
6830
|
+
const blueprints = await findBlueprintFiles(workspace);
|
|
6777
6831
|
checks.push({
|
|
6778
6832
|
id: "blueprints.discovered",
|
|
6779
6833
|
status: blueprints.length > 0 ? "pass" : "fail",
|
|
@@ -6802,8 +6856,56 @@ async function runDoctor(workspace) {
|
|
|
6802
6856
|
|
|
6803
6857
|
// src/extensions.ts
|
|
6804
6858
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
6805
|
-
import { dirname as
|
|
6806
|
-
|
|
6859
|
+
import { dirname as dirname10, relative as relative7 } from "node:path";
|
|
6860
|
+
|
|
6861
|
+
// src/module-import.ts
|
|
6862
|
+
import { readFileSync as readFileSync2, realpathSync } from "node:fs";
|
|
6863
|
+
import {
|
|
6864
|
+
createRequire as createRequire4,
|
|
6865
|
+
registerHooks,
|
|
6866
|
+
stripTypeScriptTypes
|
|
6867
|
+
} from "node:module";
|
|
6868
|
+
import { dirname as dirname9, sep as sep2 } from "node:path";
|
|
6869
|
+
import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
6870
|
+
var roots = /* @__PURE__ */ new Set();
|
|
6871
|
+
var registered = false;
|
|
6872
|
+
async function importModuleSource(entry, moduleRoot) {
|
|
6873
|
+
roots.add(realpathSync(moduleRoot));
|
|
6874
|
+
try {
|
|
6875
|
+
const require2 = createRequire4(pathToFileURL2(entry));
|
|
6876
|
+
roots.add(
|
|
6877
|
+
realpathSync(dirname9(require2.resolve("@flowdular/sdk/package.json")))
|
|
6878
|
+
);
|
|
6879
|
+
} catch (error) {
|
|
6880
|
+
if (!["MODULE_NOT_FOUND", "ERR_PACKAGE_PATH_NOT_EXPORTED"].includes(
|
|
6881
|
+
error.code ?? ""
|
|
6882
|
+
))
|
|
6883
|
+
throw error;
|
|
6884
|
+
}
|
|
6885
|
+
if (!registered) {
|
|
6886
|
+
registerHooks({
|
|
6887
|
+
load(url, context, nextLoad) {
|
|
6888
|
+
if (!url.startsWith("file:") || !fileURLToPath(url).endsWith(".ts"))
|
|
6889
|
+
return nextLoad(url, context);
|
|
6890
|
+
const path = realpathSync(fileURLToPath(url));
|
|
6891
|
+
if (![...roots].some((root) => path.startsWith(root + sep2)))
|
|
6892
|
+
return nextLoad(url, context);
|
|
6893
|
+
return {
|
|
6894
|
+
format: "module",
|
|
6895
|
+
source: stripTypeScriptTypes(readFileSync2(path, "utf8"), {
|
|
6896
|
+
mode: "transform",
|
|
6897
|
+
sourceUrl: url
|
|
6898
|
+
}),
|
|
6899
|
+
shortCircuit: true
|
|
6900
|
+
};
|
|
6901
|
+
}
|
|
6902
|
+
});
|
|
6903
|
+
registered = true;
|
|
6904
|
+
}
|
|
6905
|
+
return import(pathToFileURL2(entry).href);
|
|
6906
|
+
}
|
|
6907
|
+
|
|
6908
|
+
// src/extensions.ts
|
|
6807
6909
|
var reservedGroups = /* @__PURE__ */ new Set([
|
|
6808
6910
|
"help",
|
|
6809
6911
|
"doctor",
|
|
@@ -6859,7 +6961,10 @@ function commandKey(command) {
|
|
|
6859
6961
|
return JSON.stringify({ path: command.path, capability: command.capability });
|
|
6860
6962
|
}
|
|
6861
6963
|
async function loadCliCommand(loaded) {
|
|
6862
|
-
const imported = await
|
|
6964
|
+
const imported = await importModuleSource(
|
|
6965
|
+
loaded.entry,
|
|
6966
|
+
loaded.moduleRoot
|
|
6967
|
+
);
|
|
6863
6968
|
const extension = imported.default ?? imported.cliExtension;
|
|
6864
6969
|
if (!extension) {
|
|
6865
6970
|
throw new Error(
|
|
@@ -6892,7 +6997,7 @@ async function loadCliExtensions(workspace) {
|
|
|
6892
6997
|
await readFile11(manifestPath, "utf8")
|
|
6893
6998
|
);
|
|
6894
6999
|
if (!enabled.has(manifest2.id) || !manifest2.cli) continue;
|
|
6895
|
-
const moduleRoot =
|
|
7000
|
+
const moduleRoot = dirname10(manifestPath);
|
|
6896
7001
|
const entry = await resolveExistingInside(moduleRoot, manifest2.cli.entry);
|
|
6897
7002
|
const catalogPath = await resolveExistingInside(
|
|
6898
7003
|
moduleRoot,
|
|
@@ -6929,9 +7034,8 @@ async function loadCliExtensions(workspace) {
|
|
|
6929
7034
|
}
|
|
6930
7035
|
|
|
6931
7036
|
// src/migration.ts
|
|
6932
|
-
import { readFile as readFile13 } from "node:fs/promises";
|
|
6933
|
-
import { dirname as
|
|
6934
|
-
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
7037
|
+
import { readFile as readFile13, stat as stat2 } from "node:fs/promises";
|
|
7038
|
+
import { dirname as dirname11, join as join11 } from "node:path";
|
|
6935
7039
|
|
|
6936
7040
|
// src/migration-audit.ts
|
|
6937
7041
|
import { readdir as readdir5, readFile as readFile12 } from "node:fs/promises";
|
|
@@ -7095,13 +7199,17 @@ async function loadMigrationModules(workspace, moduleId) {
|
|
|
7095
7199
|
);
|
|
7096
7200
|
if (!enabled.has(manifest2.id)) continue;
|
|
7097
7201
|
if (moduleId && manifest2.id !== moduleId) continue;
|
|
7098
|
-
const entry = join11(
|
|
7099
|
-
let databaseMigrations;
|
|
7202
|
+
const entry = join11(dirname11(manifestPath), "src/services/migration.ts");
|
|
7100
7203
|
try {
|
|
7101
|
-
|
|
7102
|
-
} catch {
|
|
7103
|
-
continue;
|
|
7204
|
+
await stat2(entry);
|
|
7205
|
+
} catch (error) {
|
|
7206
|
+
if (error.code === "ENOENT") continue;
|
|
7207
|
+
throw error;
|
|
7104
7208
|
}
|
|
7209
|
+
const { databaseMigrations } = await importModuleSource(
|
|
7210
|
+
entry,
|
|
7211
|
+
dirname11(manifestPath)
|
|
7212
|
+
);
|
|
7105
7213
|
if (!Array.isArray(databaseMigrations)) {
|
|
7106
7214
|
unmanaged.push({
|
|
7107
7215
|
moduleId: manifest2.id,
|
|
@@ -7111,7 +7219,7 @@ async function loadMigrationModules(workspace, moduleId) {
|
|
|
7111
7219
|
}
|
|
7112
7220
|
managed.push({
|
|
7113
7221
|
moduleId: manifest2.id,
|
|
7114
|
-
moduleDirectory:
|
|
7222
|
+
moduleDirectory: dirname11(manifestPath),
|
|
7115
7223
|
databaseMigrations
|
|
7116
7224
|
});
|
|
7117
7225
|
}
|
|
@@ -7338,7 +7446,7 @@ import {
|
|
|
7338
7446
|
rm as rm3,
|
|
7339
7447
|
writeFile as writeFile4
|
|
7340
7448
|
} from "node:fs/promises";
|
|
7341
|
-
import { dirname as
|
|
7449
|
+
import { dirname as dirname12, join as join12, relative as relative8, resolve as resolve5 } from "node:path";
|
|
7342
7450
|
import { parse as parseYaml4 } from "yaml";
|
|
7343
7451
|
|
|
7344
7452
|
// src/module-templates.ts
|
|
@@ -7549,7 +7657,7 @@ function packageJson(model) {
|
|
|
7549
7657
|
...hasClient ? { "@tsrx/typescript-plugin": "0.3.120" } : {},
|
|
7550
7658
|
"@types/node": "24.13.3",
|
|
7551
7659
|
typescript: "5.9.3",
|
|
7552
|
-
vitest: "4.1.
|
|
7660
|
+
vitest: "4.1.11"
|
|
7553
7661
|
}
|
|
7554
7662
|
});
|
|
7555
7663
|
}
|
|
@@ -8707,7 +8815,7 @@ async function writeScaffold(moduleDirectory, files, target) {
|
|
|
8707
8815
|
for (const [path, source] of files) {
|
|
8708
8816
|
if (target.present.has(path)) continue;
|
|
8709
8817
|
const absolute = join12(moduleDirectory, path);
|
|
8710
|
-
const created = await mkdir4(
|
|
8818
|
+
const created = await mkdir4(dirname12(absolute), { recursive: true });
|
|
8711
8819
|
if (created) createdDirectories.push(created);
|
|
8712
8820
|
await writeFile4(absolute, source, { encoding: "utf8", flag: "wx" });
|
|
8713
8821
|
written.push(absolute);
|
|
@@ -8807,9 +8915,9 @@ import {
|
|
|
8807
8915
|
mkdir as mkdir5,
|
|
8808
8916
|
readFile as readFile15,
|
|
8809
8917
|
rm as rm4,
|
|
8810
|
-
stat as
|
|
8918
|
+
stat as stat3
|
|
8811
8919
|
} from "node:fs/promises";
|
|
8812
|
-
import { dirname as
|
|
8920
|
+
import { dirname as dirname13, join as join13, relative as relative9 } from "node:path";
|
|
8813
8921
|
import { createHash as createHash3 } from "node:crypto";
|
|
8814
8922
|
|
|
8815
8923
|
// ../kernel/src/legacy-local-state.ts
|
|
@@ -8933,7 +9041,7 @@ async function ensurePrivateDirectory(path) {
|
|
|
8933
9041
|
await chmod(path, 448);
|
|
8934
9042
|
}
|
|
8935
9043
|
async function copyPlan(plan) {
|
|
8936
|
-
await ensurePrivateDirectory(
|
|
9044
|
+
await ensurePrivateDirectory(dirname13(plan.destinationDirectory));
|
|
8937
9045
|
await ensurePrivateDirectory(plan.destinationDirectory);
|
|
8938
9046
|
const created = [];
|
|
8939
9047
|
try {
|
|
@@ -8942,14 +9050,14 @@ async function copyPlan(plan) {
|
|
|
8942
9050
|
if (sourceState.isSymbolicLink() || !sourceState.isFile()) {
|
|
8943
9051
|
throw new Error(`${file.name} is no longer a regular file.`);
|
|
8944
9052
|
}
|
|
8945
|
-
const before = await
|
|
9053
|
+
const before = await stat3(file.source);
|
|
8946
9054
|
await copyFile(file.source, file.destination, constants.COPYFILE_EXCL);
|
|
8947
9055
|
created.push(file.destination);
|
|
8948
9056
|
await chmod(file.destination, 384);
|
|
8949
9057
|
const [sourceDigest2, destinationDigest, after] = await Promise.all([
|
|
8950
9058
|
digest(file.source),
|
|
8951
9059
|
digest(file.destination),
|
|
8952
|
-
|
|
9060
|
+
stat3(file.source)
|
|
8953
9061
|
]);
|
|
8954
9062
|
if (sourceDigest2 !== destinationDigest || before.size !== after.size || before.mtimeMs !== after.mtimeMs) {
|
|
8955
9063
|
throw new Error(`Source changed while copying ${file.name}.`);
|
|
@@ -8989,7 +9097,7 @@ function data(plan, applied) {
|
|
|
8989
9097
|
ready: plan.blockers.length === 0,
|
|
8990
9098
|
source: LEGACY_DATA_DIRECTORY,
|
|
8991
9099
|
destination: relative9(
|
|
8992
|
-
|
|
9100
|
+
dirname13(dirname13(plan.destinationDirectory)),
|
|
8993
9101
|
plan.destinationDirectory
|
|
8994
9102
|
),
|
|
8995
9103
|
files: plan.files.map((file) => file.name),
|
|
@@ -9173,7 +9281,7 @@ async function runCommand(arguments_) {
|
|
|
9173
9281
|
"module list|validate [--locked]|sync [--apply]|enable <id> [--apply]|disable <id> [--apply]|new <id> --spec <path> [--apply]",
|
|
9174
9282
|
"migration status [--module <id>]|apply --module <id> [--apply]|verify|new <name> --module <id> [--apply]",
|
|
9175
9283
|
"database reset [--module <id>] [--apply --confirm reset-database]",
|
|
9176
|
-
"setup check|quick [--apply --confirm reset-local-auth]|migrate-state [--apply --confirm migrate-legacy-state]",
|
|
9284
|
+
"setup (interactive)|check|quick [--apply --confirm reset-local-auth]|migrate-state [--apply --confirm migrate-legacy-state]",
|
|
9177
9285
|
...extensionCommands.map((entry) => entry.command.path.join(" "))
|
|
9178
9286
|
],
|
|
9179
9287
|
options: [
|
|
@@ -9291,13 +9399,13 @@ async function runCommand(arguments_) {
|
|
|
9291
9399
|
);
|
|
9292
9400
|
}
|
|
9293
9401
|
if (group === "blueprint" && action === "list") {
|
|
9294
|
-
const files = await
|
|
9402
|
+
const files = await findBlueprintFiles(workspace);
|
|
9295
9403
|
return success({
|
|
9296
9404
|
blueprints: files.map((file) => relative10(workspace.root, file))
|
|
9297
9405
|
});
|
|
9298
9406
|
}
|
|
9299
9407
|
if (group === "blueprint" && action === "validate") {
|
|
9300
|
-
const files = await
|
|
9408
|
+
const files = await findBlueprintFiles(workspace);
|
|
9301
9409
|
const reports = await Promise.all(files.map(validateBlueprint));
|
|
9302
9410
|
const data2 = relativeReports2(workspace.root, reports);
|
|
9303
9411
|
return reports.every((report) => report.valid) ? success(
|
|
@@ -9594,11 +9702,213 @@ async function runProgram(arguments_) {
|
|
|
9594
9702
|
}
|
|
9595
9703
|
}
|
|
9596
9704
|
|
|
9705
|
+
// src/setup-wizard.ts
|
|
9706
|
+
import { stat as stat4 } from "node:fs/promises";
|
|
9707
|
+
|
|
9708
|
+
// src/setup-environment.ts
|
|
9709
|
+
import { randomUUID } from "node:crypto";
|
|
9710
|
+
import { lstat as lstat4, open, readFile as readFile17, rename as rename2, rm as rm5, writeFile as writeFile5 } from "node:fs/promises";
|
|
9711
|
+
import { join as join15 } from "node:path";
|
|
9712
|
+
async function readSetupEnvironment(root) {
|
|
9713
|
+
try {
|
|
9714
|
+
return await readFile17(join15(root, ".env"), "utf8");
|
|
9715
|
+
} catch (error) {
|
|
9716
|
+
if (error.code === "ENOENT") return "";
|
|
9717
|
+
throw error;
|
|
9718
|
+
}
|
|
9719
|
+
}
|
|
9720
|
+
async function saveSetupEnvironment(root, before, updates) {
|
|
9721
|
+
const path = join15(root, ".env");
|
|
9722
|
+
try {
|
|
9723
|
+
if (!(await lstat4(path)).isFile())
|
|
9724
|
+
throw new Error("Setup requires .env to be a regular file.");
|
|
9725
|
+
} catch (error) {
|
|
9726
|
+
if (error.code !== "ENOENT") throw error;
|
|
9727
|
+
}
|
|
9728
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
9729
|
+
if (!/^FD_DATABASE_[A-Z_]+$/.test(key) || /[\r\n'\0]/.test(value))
|
|
9730
|
+
throw new Error("Invalid database setting.");
|
|
9731
|
+
}
|
|
9732
|
+
const lines = before.split(/\r?\n/).filter((line) => {
|
|
9733
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(
|
|
9734
|
+
line
|
|
9735
|
+
);
|
|
9736
|
+
if (!match || !(match[1] in updates)) return true;
|
|
9737
|
+
const value = match[2].trim();
|
|
9738
|
+
if (value.startsWith('"') && !value.endsWith('"') || value.startsWith("'") && !value.endsWith("'"))
|
|
9739
|
+
throw new Error(
|
|
9740
|
+
"Edit multiline database settings in .env before using setup."
|
|
9741
|
+
);
|
|
9742
|
+
return false;
|
|
9743
|
+
});
|
|
9744
|
+
const source = lines.join("\n").trimEnd() + "\n" + Object.entries(updates).map(([key, value]) => `${key}='${value}'`).join("\n") + "\n";
|
|
9745
|
+
const temporary = join15(root, `.env.setup-${randomUUID()}.tmp`);
|
|
9746
|
+
const lockPath = join15(root, ".env.setup.lock");
|
|
9747
|
+
const lock = await open(lockPath, "wx", 384);
|
|
9748
|
+
try {
|
|
9749
|
+
await writeFile5(temporary, source, { mode: 384, flag: "wx" });
|
|
9750
|
+
if (await readSetupEnvironment(root) !== before)
|
|
9751
|
+
throw new Error("The .env file changed during setup. Run setup again.");
|
|
9752
|
+
await rename2(temporary, path);
|
|
9753
|
+
} finally {
|
|
9754
|
+
try {
|
|
9755
|
+
await rm5(temporary, { force: true });
|
|
9756
|
+
} finally {
|
|
9757
|
+
await lock.close();
|
|
9758
|
+
await rm5(lockPath, { force: true });
|
|
9759
|
+
}
|
|
9760
|
+
}
|
|
9761
|
+
}
|
|
9762
|
+
|
|
9763
|
+
// src/setup-wizard.ts
|
|
9764
|
+
var prompts = {
|
|
9765
|
+
async choose() {
|
|
9766
|
+
const { select } = await import("@inquirer/prompts");
|
|
9767
|
+
return select({
|
|
9768
|
+
message: "How would you like to set up this application?",
|
|
9769
|
+
choices: [
|
|
9770
|
+
{
|
|
9771
|
+
name: "Local demo with embedded PostgreSQL (PGlite)",
|
|
9772
|
+
value: "local"
|
|
9773
|
+
},
|
|
9774
|
+
{
|
|
9775
|
+
name: "Configure an existing PostgreSQL database",
|
|
9776
|
+
value: "postgresql"
|
|
9777
|
+
},
|
|
9778
|
+
{ name: "Check configuration only", value: "check" }
|
|
9779
|
+
]
|
|
9780
|
+
});
|
|
9781
|
+
},
|
|
9782
|
+
async confirm(message) {
|
|
9783
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
9784
|
+
return confirm({ message, default: false });
|
|
9785
|
+
},
|
|
9786
|
+
async connection(message) {
|
|
9787
|
+
const { password } = await import("@inquirer/prompts");
|
|
9788
|
+
return password({
|
|
9789
|
+
message,
|
|
9790
|
+
mask: "*",
|
|
9791
|
+
validate: (value) => validConnection(value) ? true : "Use postgres://user:password@host/database without SSL query parameters."
|
|
9792
|
+
});
|
|
9793
|
+
}
|
|
9794
|
+
};
|
|
9795
|
+
function validConnection(value) {
|
|
9796
|
+
if (value.length > 4096 || /[\r\n'\0]/.test(value)) return false;
|
|
9797
|
+
try {
|
|
9798
|
+
const url = new URL(value);
|
|
9799
|
+
return ["postgres:", "postgresql:"].includes(url.protocol) && Boolean(url.hostname && url.username && url.pathname.length > 1) && !["sslmode", "sslcert", "sslkey", "sslrootcert"].some(
|
|
9800
|
+
(key) => url.searchParams.has(key)
|
|
9801
|
+
);
|
|
9802
|
+
} catch {
|
|
9803
|
+
return false;
|
|
9804
|
+
}
|
|
9805
|
+
}
|
|
9806
|
+
async function runSetupWizard(arguments_, io = prompts, execute = runProgram) {
|
|
9807
|
+
try {
|
|
9808
|
+
const flags = new Map(arguments_.flags);
|
|
9809
|
+
flags.delete("apply");
|
|
9810
|
+
flags.delete("confirm");
|
|
9811
|
+
const check = await execute({ positionals: ["setup", "check"], flags });
|
|
9812
|
+
if (!check.ok) return check;
|
|
9813
|
+
const workspace = await findWorkspace(
|
|
9814
|
+
stringFlag(arguments_, "root") ?? process.cwd()
|
|
9815
|
+
);
|
|
9816
|
+
const before = await readSetupEnvironment(workspace.root);
|
|
9817
|
+
const choice = await io.choose();
|
|
9818
|
+
if (choice === "check") return check;
|
|
9819
|
+
if (choice === "postgresql") {
|
|
9820
|
+
const urls = {
|
|
9821
|
+
FD_DATABASE_URL: await io.connection("Runtime role connection URL"),
|
|
9822
|
+
FD_DATABASE_MIGRATOR_URL: await io.connection(
|
|
9823
|
+
"Migration role connection URL"
|
|
9824
|
+
),
|
|
9825
|
+
FD_DATABASE_BACKGROUND_URL: await io.connection(
|
|
9826
|
+
"Background role connection URL"
|
|
9827
|
+
)
|
|
9828
|
+
};
|
|
9829
|
+
if (!Object.values(urls).every(validConnection))
|
|
9830
|
+
return failure(
|
|
9831
|
+
"INVALID_DATABASE_CONFIGURATION",
|
|
9832
|
+
"Invalid PostgreSQL connection settings."
|
|
9833
|
+
);
|
|
9834
|
+
const updates = {
|
|
9835
|
+
...urls,
|
|
9836
|
+
FD_DATABASE_ADAPTER: "postgresql",
|
|
9837
|
+
FD_DATABASE_TLS: "verify-full"
|
|
9838
|
+
};
|
|
9839
|
+
databaseProviderConfigFromEnvironment(
|
|
9840
|
+
{ ...process.env, ...updates, NODE_ENV: "production" },
|
|
9841
|
+
workspace.root
|
|
9842
|
+
);
|
|
9843
|
+
if (!await io.confirm(
|
|
9844
|
+
"Save PostgreSQL settings in .env? This does not reset or modify the hosted database."
|
|
9845
|
+
))
|
|
9846
|
+
return success({ cancelled: true });
|
|
9847
|
+
await saveSetupEnvironment(workspace.root, before, updates);
|
|
9848
|
+
return success(
|
|
9849
|
+
{
|
|
9850
|
+
setup: "postgresql",
|
|
9851
|
+
configured: true,
|
|
9852
|
+
adapter: "postgresql",
|
|
9853
|
+
next: "Run pnpm dev, then open http://localhost:4310 and create an account."
|
|
9854
|
+
},
|
|
9855
|
+
{
|
|
9856
|
+
warnings: [
|
|
9857
|
+
"TLS certificate verification is enabled. The database must provide separate migration, runtime and background roles."
|
|
9858
|
+
]
|
|
9859
|
+
}
|
|
9860
|
+
);
|
|
9861
|
+
}
|
|
9862
|
+
const config = databaseProviderConfigFromEnvironment(
|
|
9863
|
+
process.env,
|
|
9864
|
+
workspace.root
|
|
9865
|
+
);
|
|
9866
|
+
if (config.adapter !== "pglite" || process.env.FD_DATABASE_PGLITE_DIRECTORY || process.env.CORELOOM_DATABASE_PGLITE_DIRECTORY || process.env.FD_DATABASE_URL || process.env.CORELOOM_DATABASE_URL)
|
|
9867
|
+
return failure(
|
|
9868
|
+
"LOCAL_DEMO_UNAVAILABLE",
|
|
9869
|
+
"Local demo setup requires the default embedded database. The configured database will not be reset."
|
|
9870
|
+
);
|
|
9871
|
+
const preview = await execute({ positionals: ["setup", "quick"], flags });
|
|
9872
|
+
if (!preview.ok) return preview;
|
|
9873
|
+
const directory = config.pglite?.dataDirectory;
|
|
9874
|
+
let existing = false;
|
|
9875
|
+
if (directory)
|
|
9876
|
+
try {
|
|
9877
|
+
await stat4(directory);
|
|
9878
|
+
existing = true;
|
|
9879
|
+
} catch (error) {
|
|
9880
|
+
if (error.code !== "ENOENT") throw error;
|
|
9881
|
+
}
|
|
9882
|
+
const message = existing ? `Reset the local demo database in ${directory}? All existing local data will be deleted. Confirm that the app is stopped.` : "Create a local demo with admin@example.com? Confirm that the app is stopped.";
|
|
9883
|
+
if (!await io.confirm(message)) return success({ cancelled: true });
|
|
9884
|
+
if (await readSetupEnvironment(workspace.root) !== before)
|
|
9885
|
+
return failure(
|
|
9886
|
+
"SETUP_CONFIGURATION_CHANGED",
|
|
9887
|
+
"The .env file changed during setup. Run setup again."
|
|
9888
|
+
);
|
|
9889
|
+
flags.set("apply", true);
|
|
9890
|
+
flags.set("confirm", "reset-local-auth");
|
|
9891
|
+
const applied = await execute({ positionals: ["setup", "quick"], flags });
|
|
9892
|
+
return applied.ok ? success(
|
|
9893
|
+
{ ...applied.data, setup: "local" },
|
|
9894
|
+
{ warnings: applied.warnings, evidence: applied.evidence }
|
|
9895
|
+
) : applied;
|
|
9896
|
+
} catch (error) {
|
|
9897
|
+
if (error instanceof Error && ["ExitPromptError", "AbortPromptError"].includes(error.name))
|
|
9898
|
+
return success({ cancelled: true });
|
|
9899
|
+
return failure(
|
|
9900
|
+
"SETUP_FAILED",
|
|
9901
|
+
error instanceof Error ? error.message : "Setup failed."
|
|
9902
|
+
);
|
|
9903
|
+
}
|
|
9904
|
+
}
|
|
9905
|
+
|
|
9597
9906
|
// src/index.ts
|
|
9598
9907
|
var invokedAsProgram = process.argv[1]?.endsWith("/index.ts") || process.argv[1]?.endsWith("/index.js");
|
|
9599
9908
|
if (invokedAsProgram) {
|
|
9600
9909
|
const arguments_ = parseArguments(process.argv.slice(2));
|
|
9601
|
-
const
|
|
9910
|
+
const interactive = arguments_.positionals.length === 1 && arguments_.positionals[0] === "setup" && !arguments_.flags.has("json") && !arguments_.flags.has("help") && process.stdin.isTTY && process.stdout.isTTY;
|
|
9911
|
+
const envelope = await (interactive ? runSetupWizard(arguments_) : runProgram(arguments_));
|
|
9602
9912
|
process.stdout.write(
|
|
9603
9913
|
`${renderOutput(envelope, arguments_.flags.has("json"))}
|
|
9604
9914
|
`
|