@lumerahq/cli 0.24.7-dev.0 → 0.25.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 +13 -1
- package/dist/{chunk-GFLMEXIK.js → chunk-VL5GDJKU.js} +27 -92
- package/dist/functions-NKI3ZKIV.js +212 -0
- package/dist/index.js +21 -5
- package/dist/{init-TRZMDKYB.js → init-CUB3KH6M.js} +40 -43
- package/dist/{skills-O3NZFM7V.js → skills-PXZHERAS.js} +31 -16
- package/package.json +1 -1
- package/templates/default/README.md +10 -0
- package/templates/default/_gitignore +3 -1
- package/templates/default/biome.json +11 -3
- package/templates/default/icon.svg +1 -0
- package/templates/default/package.json +1 -1
- package/templates/default/platform/functions/__init__.py +1 -0
- package/templates/default/pnpm-lock.yaml +37 -37
- package/templates/default/pyproject.toml +7 -2
- package/templates/default/src/styles.css +2 -8
- package/templates/default/template.json +1 -1
- package/templates/default/tests/functions/.gitkeep +1 -0
package/README.md
CHANGED
|
@@ -33,10 +33,22 @@ lumera destroy # Delete remote resources
|
|
|
33
33
|
|
|
34
34
|
lumera run <target> # Run script, automation, or invoke agent
|
|
35
35
|
|
|
36
|
+
lumera functions list # List locally configured Functions
|
|
37
|
+
lumera functions inspect invoices:approve
|
|
38
|
+
lumera functions invoke invoices:approve --input input.json --local
|
|
39
|
+
lumera functions test # Validate contracts and run Function tests
|
|
40
|
+
|
|
36
41
|
lumera flags list # List this sandbox's feature flags
|
|
37
42
|
lumera flags get <key> # Print one flag's value (--default <v> if unset)
|
|
38
43
|
```
|
|
39
44
|
|
|
45
|
+
Function commands are local-only in this release. Each command starts a fresh
|
|
46
|
+
SDK runner through `uv` and supplies the compatible Functions SDK as an
|
|
47
|
+
isolated overlay, so an older project lock cannot select a pre-Functions SDK.
|
|
48
|
+
`invoke` requires `--local` so it cannot be mistaken for a deployed invocation.
|
|
49
|
+
Function modules and test locations are configured under
|
|
50
|
+
`[tool.lumera.functions]` in `pyproject.toml`.
|
|
51
|
+
|
|
40
52
|
## Scaffolding Projects
|
|
41
53
|
|
|
42
54
|
### Interactive Mode
|
|
@@ -54,7 +66,7 @@ For CI/CD or scripted environments, use `-y` (or `--yes`) flag:
|
|
|
54
66
|
lumera init my-app -y # Creates ./my-app
|
|
55
67
|
lumera init my-app -y --dir ./apps # Creates ./apps
|
|
56
68
|
lumera init my-app -y --force # Overwrites if directory exists
|
|
57
|
-
lumera init my-app -y --no-install #
|
|
69
|
+
lumera init my-app -y --no-install # Defer Python and pnpm dependencies
|
|
58
70
|
```
|
|
59
71
|
|
|
60
72
|
### Init Options
|
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-FHHWIV4C.js";
|
|
4
|
-
import {
|
|
5
|
-
getBaseUrl,
|
|
6
|
-
getProjectId,
|
|
7
|
-
getToken,
|
|
8
|
-
init_auth
|
|
2
|
+
getBaseUrl
|
|
9
3
|
} from "./chunk-JLVVHTBY.js";
|
|
10
4
|
import {
|
|
11
5
|
fetchWithRetry
|
|
12
6
|
} from "./chunk-FJFIWC7G.js";
|
|
13
7
|
|
|
14
8
|
// src/lib/skills.ts
|
|
15
|
-
init_auth();
|
|
16
9
|
import { createHash } from "crypto";
|
|
17
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync,
|
|
18
|
-
import {
|
|
10
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, symlinkSync, lstatSync, statSync, writeFileSync } from "fs";
|
|
11
|
+
import { join, relative } from "path";
|
|
19
12
|
import pc from "picocolors";
|
|
20
13
|
function slugToDirName(slug) {
|
|
21
14
|
return slug.replace(/-/g, "_");
|
|
@@ -27,19 +20,7 @@ function slugToFilename(slug) {
|
|
|
27
20
|
return `${slug.replace(/-/g, "_")}.md`;
|
|
28
21
|
}
|
|
29
22
|
function hashContent(content) {
|
|
30
|
-
return createHash("
|
|
31
|
-
}
|
|
32
|
-
var SKILLS_HASH_MANIFEST_PATH = join(".lumera", "skills-hashes.json");
|
|
33
|
-
function writeSkillsHashManifest(projectRoot, skills) {
|
|
34
|
-
const hashes = {};
|
|
35
|
-
for (const skill of [...skills].sort((a, b) => a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0)) {
|
|
36
|
-
hashes[skill.slug] = hashContent(skill.content);
|
|
37
|
-
}
|
|
38
|
-
const manifestPath = join(projectRoot, SKILLS_HASH_MANIFEST_PATH);
|
|
39
|
-
mkdirSync(dirname(manifestPath), { recursive: true });
|
|
40
|
-
writeFileSync(manifestPath, `${JSON.stringify({ version: 1, skills: hashes }, null, 2)}
|
|
41
|
-
`);
|
|
42
|
-
rmSync(join(projectRoot, ".lumera", "skills-content-hash"), { force: true });
|
|
23
|
+
return createHash("md5").update(content).digest("hex");
|
|
43
24
|
}
|
|
44
25
|
async function fetchSkillsList() {
|
|
45
26
|
const baseUrl = getBaseUrl();
|
|
@@ -63,58 +44,6 @@ async function fetchSkillContent(slug) {
|
|
|
63
44
|
}
|
|
64
45
|
return mdRes.text();
|
|
65
46
|
}
|
|
66
|
-
async function fetchEffectiveSkills(projectRoot) {
|
|
67
|
-
const baseUrl = getBaseUrl();
|
|
68
|
-
const token = getToken(projectRoot);
|
|
69
|
-
const projectId = getProjectId(projectRoot)?.trim();
|
|
70
|
-
const url = new URL(`${baseUrl}/api/skills/effective`);
|
|
71
|
-
if (projectId) url.searchParams.set("project_id", projectId);
|
|
72
|
-
const response = await fetchWithRetry(url.toString(), {
|
|
73
|
-
headers: {
|
|
74
|
-
Authorization: `Bearer ${token}`,
|
|
75
|
-
"X-Lumera-Client": "lumera-cli",
|
|
76
|
-
...userContextHeaders()
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
if (!response.ok) {
|
|
80
|
-
const detail = (await response.text()).trim();
|
|
81
|
-
throw new Error(`Failed to fetch effective skills: ${response.status}${detail ? ` ${detail}` : ""}`);
|
|
82
|
-
}
|
|
83
|
-
const payload = await response.json();
|
|
84
|
-
return (payload.skills ?? []).map((skill) => ({
|
|
85
|
-
...skill,
|
|
86
|
-
summary: skill.summary || skill.name,
|
|
87
|
-
content_hash: skill.content_hash || hashContent(skill.content)
|
|
88
|
-
}));
|
|
89
|
-
}
|
|
90
|
-
async function fetchPublicSkillFiles() {
|
|
91
|
-
const skills = await fetchSkillsList();
|
|
92
|
-
const results = await Promise.allSettled(
|
|
93
|
-
skills.map(async (skill) => ({
|
|
94
|
-
...skill,
|
|
95
|
-
content: await fetchSkillContent(skill.slug)
|
|
96
|
-
}))
|
|
97
|
-
);
|
|
98
|
-
const out = [];
|
|
99
|
-
for (const result of results) {
|
|
100
|
-
if (result.status !== "fulfilled" || !result.value.content) continue;
|
|
101
|
-
out.push({
|
|
102
|
-
...result.value,
|
|
103
|
-
content: result.value.content,
|
|
104
|
-
content_hash: hashContent(result.value.content),
|
|
105
|
-
managed: true
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
return out;
|
|
109
|
-
}
|
|
110
|
-
async function fetchSkillsForProject(projectRoot) {
|
|
111
|
-
try {
|
|
112
|
-
getToken(projectRoot);
|
|
113
|
-
} catch {
|
|
114
|
-
return fetchPublicSkillFiles();
|
|
115
|
-
}
|
|
116
|
-
return fetchEffectiveSkills(projectRoot);
|
|
117
|
-
}
|
|
118
47
|
function getLocalSkills(skillsDir) {
|
|
119
48
|
const localSkills = /* @__PURE__ */ new Map();
|
|
120
49
|
if (!existsSync(skillsDir)) {
|
|
@@ -197,30 +126,37 @@ function ensureSkillSymlinks(projectRoot) {
|
|
|
197
126
|
}
|
|
198
127
|
async function installAllSkills(targetDir, options) {
|
|
199
128
|
const verbose = options?.verbose ?? false;
|
|
200
|
-
const skills = await
|
|
129
|
+
const skills = await fetchSkillsList();
|
|
201
130
|
const skillsDir = join(targetDir, ".agents", "skills");
|
|
202
131
|
mkdirSync(skillsDir, { recursive: true });
|
|
132
|
+
const results = await Promise.allSettled(
|
|
133
|
+
skills.map(async (skill) => {
|
|
134
|
+
const content = await fetchSkillContent(skill.slug);
|
|
135
|
+
return { skill, content };
|
|
136
|
+
})
|
|
137
|
+
);
|
|
203
138
|
let installed = 0;
|
|
204
139
|
let failed = 0;
|
|
205
|
-
for (const
|
|
206
|
-
if (
|
|
207
|
-
|
|
140
|
+
for (const result of results) {
|
|
141
|
+
if (result.status === "fulfilled" && result.value.content) {
|
|
142
|
+
const { skill, content } = result.value;
|
|
143
|
+
const dirName = slugToDirName(skill.slug);
|
|
144
|
+
const skillDir = join(skillsDir, dirName);
|
|
145
|
+
mkdirSync(skillDir, { recursive: true });
|
|
146
|
+
writeFileSync(join(skillDir, "SKILL.md"), content);
|
|
208
147
|
if (verbose) {
|
|
209
|
-
console.log(pc.
|
|
148
|
+
console.log(pc.green(" \u2713"), pc.dim(`${dirName}/SKILL.md`));
|
|
210
149
|
}
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
console.log(pc.green(" \u2713"), pc.dim(`${dirName}/SKILL.md`));
|
|
150
|
+
installed++;
|
|
151
|
+
} else {
|
|
152
|
+
if (verbose) {
|
|
153
|
+
const slug = result.status === "fulfilled" ? result.value.skill.slug : "unknown";
|
|
154
|
+
console.log(pc.yellow(" \u26A0"), pc.dim(`Failed to fetch ${slug}`));
|
|
155
|
+
}
|
|
156
|
+
failed++;
|
|
219
157
|
}
|
|
220
|
-
installed++;
|
|
221
158
|
}
|
|
222
159
|
ensureSkillSymlinks(targetDir);
|
|
223
|
-
if (failed === 0) writeSkillsHashManifest(targetDir, skills);
|
|
224
160
|
return { installed, failed };
|
|
225
161
|
}
|
|
226
162
|
var SKILLS_START_MARKER = "<!-- LUMERA_SKILLS_START -->";
|
|
@@ -281,9 +217,8 @@ export {
|
|
|
281
217
|
slugToDirName,
|
|
282
218
|
slugToFilename,
|
|
283
219
|
hashContent,
|
|
284
|
-
writeSkillsHashManifest,
|
|
285
220
|
fetchSkillsList,
|
|
286
|
-
|
|
221
|
+
fetchSkillContent,
|
|
287
222
|
getLocalSkills,
|
|
288
223
|
ensureSkillSymlinks,
|
|
289
224
|
installAllSkills,
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import "./chunk-PNKVD2UK.js";
|
|
2
|
+
|
|
3
|
+
// src/commands/functions.ts
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { join, resolve } from "path";
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
var SUPPORTED_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "inspect", "invoke", "test"]);
|
|
9
|
+
var FUNCTIONS_SDK_REQUIREMENT = "lumera[functions]>=0.29.0,<0.30.0";
|
|
10
|
+
var FUNCTIONS_RUNNER_PROTOCOL_VERSION = 1;
|
|
11
|
+
function errorMessage(error, fallback) {
|
|
12
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
13
|
+
}
|
|
14
|
+
function isFunctionProtocolEnvelope(output) {
|
|
15
|
+
try {
|
|
16
|
+
const payload = JSON.parse(output);
|
|
17
|
+
return typeof payload === "object" && payload !== null && !Array.isArray(payload) && "protocol_version" in payload && payload.protocol_version === FUNCTIONS_RUNNER_PROTOCOL_VERSION && "ok" in payload && typeof payload.ok === "boolean";
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function emitFunctionCliFailure(code, message, write = (value) => {
|
|
23
|
+
process.stdout.write(value);
|
|
24
|
+
}) {
|
|
25
|
+
write(
|
|
26
|
+
`${JSON.stringify({
|
|
27
|
+
protocol_version: FUNCTIONS_RUNNER_PROTOCOL_VERSION,
|
|
28
|
+
ok: false,
|
|
29
|
+
error: { code, message, retryable: false }
|
|
30
|
+
})}
|
|
31
|
+
`
|
|
32
|
+
);
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
}
|
|
35
|
+
function findFunctionsProjectRoot(startDir = process.cwd()) {
|
|
36
|
+
let directory = resolve(startDir);
|
|
37
|
+
while (true) {
|
|
38
|
+
if (existsSync(join(directory, "pyproject.toml"))) {
|
|
39
|
+
return directory;
|
|
40
|
+
}
|
|
41
|
+
const parent = resolve(directory, "..");
|
|
42
|
+
if (parent === directory) break;
|
|
43
|
+
directory = parent;
|
|
44
|
+
}
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Could not find Functions project root (no pyproject.toml found)"
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
function showHelp() {
|
|
50
|
+
console.log(`
|
|
51
|
+
${pc.dim("Usage:")}
|
|
52
|
+
lumera functions list
|
|
53
|
+
lumera functions inspect <function-id>
|
|
54
|
+
lumera functions invoke <function-id> --input <path|-> --local
|
|
55
|
+
lumera functions test [-- <pytest-options>]
|
|
56
|
+
|
|
57
|
+
${pc.dim("Description:")}
|
|
58
|
+
Discover, inspect, invoke, and test configured Functions in a fresh local
|
|
59
|
+
Python process. Phase 1 does not deploy or invoke Functions remotely.
|
|
60
|
+
|
|
61
|
+
${pc.dim("Options:")}
|
|
62
|
+
--local Required for invoke; remote invocation is unavailable
|
|
63
|
+
--input <path|-> Read a JSON object from a project file or stdin (-)
|
|
64
|
+
--help, -h Show this help
|
|
65
|
+
|
|
66
|
+
${pc.dim("Examples:")}
|
|
67
|
+
lumera functions list
|
|
68
|
+
lumera functions inspect invoices:approve
|
|
69
|
+
lumera functions invoke invoices:approve --input input.json --local
|
|
70
|
+
cat input.json | lumera functions invoke invoices:approve --input - --local
|
|
71
|
+
lumera functions test
|
|
72
|
+
lumera functions test -- -k approve
|
|
73
|
+
`);
|
|
74
|
+
}
|
|
75
|
+
function buildFunctionRunnerArgs(subcommand, args, projectRoot) {
|
|
76
|
+
if (!SUPPORTED_SUBCOMMANDS.has(subcommand)) {
|
|
77
|
+
throw new Error(`Unknown functions subcommand: ${subcommand}`);
|
|
78
|
+
}
|
|
79
|
+
if (subcommand === "invoke" && !args.includes("--local")) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
"Function invocation is local-only in Phase 1; pass --local explicitly."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const runnerPrefix = ["run"];
|
|
85
|
+
if (existsSync(join(projectRoot, "uv.lock"))) {
|
|
86
|
+
runnerPrefix.push("--locked");
|
|
87
|
+
}
|
|
88
|
+
runnerPrefix.push("--with", FUNCTIONS_SDK_REQUIREMENT);
|
|
89
|
+
if (subcommand === "test") {
|
|
90
|
+
runnerPrefix.push("--with", "pytest>=8.0,<10.0");
|
|
91
|
+
}
|
|
92
|
+
return [
|
|
93
|
+
...runnerPrefix,
|
|
94
|
+
"python",
|
|
95
|
+
"-m",
|
|
96
|
+
"lumera.functions",
|
|
97
|
+
"--project-root",
|
|
98
|
+
projectRoot,
|
|
99
|
+
subcommand,
|
|
100
|
+
...args
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
async function runFunctionRunner(runnerArgs, projectRoot, spawnRunner = (command, args, options) => spawn(command, args, options), writeOutput = (value) => {
|
|
104
|
+
process.stdout.write(value);
|
|
105
|
+
}, writeDiagnostic = (value) => {
|
|
106
|
+
process.stderr.write(value);
|
|
107
|
+
}) {
|
|
108
|
+
return await new Promise((resolve2, reject) => {
|
|
109
|
+
const child = spawnRunner("uv", runnerArgs, {
|
|
110
|
+
cwd: projectRoot,
|
|
111
|
+
env: process.env,
|
|
112
|
+
shell: false,
|
|
113
|
+
// Buffer the single protocol value so a uv failure before Python starts
|
|
114
|
+
// cannot leave machine callers with stderr and no JSON response.
|
|
115
|
+
stdio: ["inherit", "pipe", "inherit"]
|
|
116
|
+
});
|
|
117
|
+
let protocolOutput = "";
|
|
118
|
+
child.stdout?.setEncoding("utf8");
|
|
119
|
+
child.stdout?.on("data", (chunk) => {
|
|
120
|
+
protocolOutput += chunk;
|
|
121
|
+
});
|
|
122
|
+
let settled = false;
|
|
123
|
+
const forwardSigint = () => child.kill("SIGINT");
|
|
124
|
+
const forwardSigterm = () => child.kill("SIGTERM");
|
|
125
|
+
const cleanup = () => {
|
|
126
|
+
process.off("SIGINT", forwardSigint);
|
|
127
|
+
process.off("SIGTERM", forwardSigterm);
|
|
128
|
+
};
|
|
129
|
+
process.once("SIGINT", forwardSigint);
|
|
130
|
+
process.once("SIGTERM", forwardSigterm);
|
|
131
|
+
child.once("error", (error) => {
|
|
132
|
+
if (settled) return;
|
|
133
|
+
settled = true;
|
|
134
|
+
cleanup();
|
|
135
|
+
if (error.code === "ENOENT") {
|
|
136
|
+
reject(
|
|
137
|
+
new Error(
|
|
138
|
+
"uv is not installed. Install it from https://docs.astral.sh/uv/"
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
reject(new Error(`Failed to start the Function runner: ${error.message}`));
|
|
144
|
+
});
|
|
145
|
+
child.once("close", (code) => {
|
|
146
|
+
if (settled) return;
|
|
147
|
+
settled = true;
|
|
148
|
+
cleanup();
|
|
149
|
+
if (!isFunctionProtocolEnvelope(protocolOutput)) {
|
|
150
|
+
if (protocolOutput) writeDiagnostic(protocolOutput);
|
|
151
|
+
reject(
|
|
152
|
+
new Error(
|
|
153
|
+
`Function runner exited with status ${code ?? 1} before emitting a valid protocol envelope`
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
writeOutput(protocolOutput);
|
|
159
|
+
resolve2(code ?? 1);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
async function functions(subcommand, args, dependencies = {}) {
|
|
164
|
+
if (subcommand === void 0 || subcommand === "help" || subcommand === "--help" || subcommand === "-h" || args.includes("--help") || args.includes("-h")) {
|
|
165
|
+
showHelp();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const findProjectRoot = dependencies.findProjectRoot ?? (() => findFunctionsProjectRoot());
|
|
169
|
+
const buildRunnerArgs = dependencies.buildRunnerArgs ?? buildFunctionRunnerArgs;
|
|
170
|
+
const runRunner = dependencies.runRunner ?? runFunctionRunner;
|
|
171
|
+
const emitFailure = dependencies.emitFailure ?? emitFunctionCliFailure;
|
|
172
|
+
let projectRoot;
|
|
173
|
+
try {
|
|
174
|
+
projectRoot = findProjectRoot();
|
|
175
|
+
} catch (error) {
|
|
176
|
+
emitFailure(
|
|
177
|
+
"function_discovery_error",
|
|
178
|
+
errorMessage(error, "Could not locate the Functions project")
|
|
179
|
+
);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
let runnerArgs;
|
|
183
|
+
try {
|
|
184
|
+
runnerArgs = buildRunnerArgs(subcommand, args, projectRoot);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
emitFailure(
|
|
187
|
+
"invalid_arguments",
|
|
188
|
+
errorMessage(error, "Invalid Functions command arguments")
|
|
189
|
+
);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
let exitCode;
|
|
193
|
+
try {
|
|
194
|
+
exitCode = await runRunner(runnerArgs, projectRoot);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
emitFailure(
|
|
197
|
+
"function_runner_unavailable",
|
|
198
|
+
errorMessage(error, "The local Function runner is unavailable")
|
|
199
|
+
);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (exitCode !== 0) {
|
|
203
|
+
process.exitCode = exitCode;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export {
|
|
207
|
+
buildFunctionRunnerArgs,
|
|
208
|
+
emitFunctionCliFailure,
|
|
209
|
+
findFunctionsProjectRoot,
|
|
210
|
+
functions,
|
|
211
|
+
runFunctionRunner
|
|
212
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,11 @@ import { dirname as dirname2, join as join2 } from "path";
|
|
|
10
10
|
import { fileURLToPath } from "url";
|
|
11
11
|
import pc from "picocolors";
|
|
12
12
|
|
|
13
|
+
// src/lib/command-output.ts
|
|
14
|
+
function commandOwnsStdout(command2) {
|
|
15
|
+
return command2 === "functions";
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
// src/lib/update-check.ts
|
|
14
19
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
15
20
|
import { homedir } from "os";
|
|
@@ -79,6 +84,7 @@ if (jsonMode) process.env.LUMERA_JSON = "1";
|
|
|
79
84
|
var args = rawArgs.filter((a) => a !== "--json");
|
|
80
85
|
var command = args[0];
|
|
81
86
|
var subcommand = args[1];
|
|
87
|
+
var ownsStdout = commandOwnsStdout(command);
|
|
82
88
|
var COMMANDS = [
|
|
83
89
|
"plan",
|
|
84
90
|
"apply",
|
|
@@ -89,6 +95,7 @@ var COMMANDS = [
|
|
|
89
95
|
"diff",
|
|
90
96
|
"dev",
|
|
91
97
|
"run",
|
|
98
|
+
"functions",
|
|
92
99
|
"init",
|
|
93
100
|
"register",
|
|
94
101
|
"templates",
|
|
@@ -150,6 +157,7 @@ ${pc.dim("Resource Commands:")}
|
|
|
150
157
|
${pc.dim("Development:")}
|
|
151
158
|
${pc.cyan("dev")} Start dev server
|
|
152
159
|
${pc.cyan("run")} <target> Run script, trigger automation, or invoke agent
|
|
160
|
+
${pc.cyan("functions")} <command> Develop and test Functions locally
|
|
153
161
|
|
|
154
162
|
${pc.dim("Project:")}
|
|
155
163
|
${pc.cyan("init")} [name] Scaffold a new project
|
|
@@ -194,6 +202,9 @@ ${pc.dim("Examples:")}
|
|
|
194
202
|
lumera run scripts/seed.py # Run a script
|
|
195
203
|
lumera run automations/sync # Trigger automation
|
|
196
204
|
lumera run agents/support "Hello" # Invoke an agent
|
|
205
|
+
lumera functions list # List locally configured Functions
|
|
206
|
+
lumera functions invoke invoices:approve --input input.json --local
|
|
207
|
+
lumera functions test # Validate Function contracts and tests
|
|
197
208
|
lumera dev # Start dev server
|
|
198
209
|
lumera flags list # List this sandbox's feature flags
|
|
199
210
|
lumera flags get studio_browser --default false # one flag (fallback if unset)
|
|
@@ -222,7 +233,7 @@ async function main() {
|
|
|
222
233
|
}
|
|
223
234
|
}
|
|
224
235
|
const startTime = performance.now();
|
|
225
|
-
const updateCheck = checkForUpdate(VERSION);
|
|
236
|
+
const updateCheck = ownsStdout ? Promise.resolve(null) : checkForUpdate(VERSION);
|
|
226
237
|
try {
|
|
227
238
|
switch (command) {
|
|
228
239
|
// Resource commands
|
|
@@ -254,9 +265,14 @@ async function main() {
|
|
|
254
265
|
case "run":
|
|
255
266
|
await import("./run-WHVUVIYB.js").then((m) => m.run(args.slice(1)));
|
|
256
267
|
break;
|
|
268
|
+
case "functions":
|
|
269
|
+
await import("./functions-NKI3ZKIV.js").then(
|
|
270
|
+
(m) => m.functions(subcommand, args.slice(2))
|
|
271
|
+
);
|
|
272
|
+
break;
|
|
257
273
|
// Project
|
|
258
274
|
case "init":
|
|
259
|
-
await import("./init-
|
|
275
|
+
await import("./init-CUB3KH6M.js").then((m) => m.init(args.slice(1)));
|
|
260
276
|
break;
|
|
261
277
|
case "register":
|
|
262
278
|
await import("./register-HRLBT4FI.js").then((m) => m.register(args.slice(1)));
|
|
@@ -272,7 +288,7 @@ async function main() {
|
|
|
272
288
|
break;
|
|
273
289
|
// Skills
|
|
274
290
|
case "skills":
|
|
275
|
-
await import("./skills-
|
|
291
|
+
await import("./skills-PXZHERAS.js").then((m) => m.skills(subcommand, args.slice(2)));
|
|
276
292
|
break;
|
|
277
293
|
// Dependencies
|
|
278
294
|
case "deps":
|
|
@@ -309,14 +325,14 @@ async function main() {
|
|
|
309
325
|
process.exit(1);
|
|
310
326
|
}
|
|
311
327
|
}
|
|
312
|
-
if (!jsonMode) {
|
|
328
|
+
if (!jsonMode && !ownsStdout) {
|
|
313
329
|
const elapsed = performance.now() - startTime;
|
|
314
330
|
if (elapsed >= 500) {
|
|
315
331
|
console.log(pc.dim(`
|
|
316
332
|
Done in ${formatElapsed(elapsed)}`));
|
|
317
333
|
}
|
|
318
334
|
}
|
|
319
|
-
if (!jsonMode) {
|
|
335
|
+
if (!jsonMode && !ownsStdout) {
|
|
320
336
|
try {
|
|
321
337
|
const update = await Promise.race([
|
|
322
338
|
updateCheck,
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listAllTemplates,
|
|
3
|
+
resolveTemplate
|
|
4
|
+
} from "./chunk-H357NP7T.js";
|
|
1
5
|
import {
|
|
2
6
|
installAllSkills,
|
|
3
7
|
syncClaudeMd
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VL5GDJKU.js";
|
|
5
9
|
import {
|
|
6
10
|
spinner
|
|
7
11
|
} from "./chunk-BHYDYR75.js";
|
|
@@ -15,10 +19,6 @@ import {
|
|
|
15
19
|
setProjectId
|
|
16
20
|
} from "./chunk-JLVVHTBY.js";
|
|
17
21
|
import "./chunk-FJFIWC7G.js";
|
|
18
|
-
import {
|
|
19
|
-
listAllTemplates,
|
|
20
|
-
resolveTemplate
|
|
21
|
-
} from "./chunk-H357NP7T.js";
|
|
22
22
|
import "./chunk-PNKVD2UK.js";
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
@@ -130,14 +130,11 @@ function installUv() {
|
|
|
130
130
|
return false;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
function createPythonVenv(targetDir) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
} catch {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
133
|
+
function createPythonVenv(targetDir, runCommand = execSync) {
|
|
134
|
+
runCommand("uv sync --extra dev --no-install-project", {
|
|
135
|
+
cwd: targetDir,
|
|
136
|
+
stdio: "ignore"
|
|
137
|
+
});
|
|
141
138
|
}
|
|
142
139
|
function detectEditor() {
|
|
143
140
|
const envEditor = process.env.VISUAL || process.env.EDITOR;
|
|
@@ -377,6 +374,7 @@ async function init(args) {
|
|
|
377
374
|
copyDir(templateDir, targetDir, replacements);
|
|
378
375
|
ensureClaudeInstructionsLink(targetDir);
|
|
379
376
|
const installCommand = existsSync(join(targetDir, "pnpm-lock.yaml")) ? "pnpm install --frozen-lockfile" : "pnpm install";
|
|
377
|
+
const hasPythonProject = existsSync(join(targetDir, "pyproject.toml"));
|
|
380
378
|
function listFiles(dir, prefix = "") {
|
|
381
379
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
382
380
|
const relativePath = prefix + entry.name;
|
|
@@ -388,6 +386,31 @@ async function init(args) {
|
|
|
388
386
|
}
|
|
389
387
|
}
|
|
390
388
|
listFiles(targetDir);
|
|
389
|
+
if (opts.install && hasPythonProject) {
|
|
390
|
+
if (!isUvInstalled()) {
|
|
391
|
+
const stopUv = spinner("Installing uv (Python package manager)...");
|
|
392
|
+
if (installUv()) {
|
|
393
|
+
stopUv(pc.green("\u2713") + pc.dim(" uv installed successfully"));
|
|
394
|
+
} else {
|
|
395
|
+
stopUv(pc.red("\u2717") + pc.dim(" Failed to install uv"));
|
|
396
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
397
|
+
throw new Error(
|
|
398
|
+
"uv is required to resolve this Python project, so the incomplete generated directory was removed. Install uv from https://docs.astral.sh/uv/ and retry initialization, or pass --no-install to defer Python and JavaScript dependencies."
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const stopVenv = spinner("Creating Python venv with Lumera SDK...");
|
|
403
|
+
try {
|
|
404
|
+
createPythonVenv(targetDir);
|
|
405
|
+
stopVenv(pc.green("\u2713") + pc.dim(" Python venv created (.venv/) with lumera SDK"));
|
|
406
|
+
} catch {
|
|
407
|
+
stopVenv(pc.red("\u2717") + pc.dim(" Failed to resolve the Python environment"));
|
|
408
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
409
|
+
throw new Error(
|
|
410
|
+
"Python dependency resolution failed, so the incomplete generated directory was removed. Verify the declared Lumera SDK is available and retry initialization, or pass --no-install to defer Python and JavaScript dependencies."
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
391
414
|
if (isGitInstalled()) {
|
|
392
415
|
const stopGit = spinner("Initializing git repository...");
|
|
393
416
|
if (initGitRepo(targetDir, finalProjectName)) {
|
|
@@ -398,24 +421,6 @@ async function init(args) {
|
|
|
398
421
|
} else {
|
|
399
422
|
console.log(pc.yellow(" \u26A0"), pc.dim("Git not found \u2014 skipping repository initialization"));
|
|
400
423
|
}
|
|
401
|
-
let uvAvailable = isUvInstalled();
|
|
402
|
-
if (!uvAvailable) {
|
|
403
|
-
const stopUv = spinner("Installing uv (Python package manager)...");
|
|
404
|
-
if (installUv()) {
|
|
405
|
-
stopUv(pc.green("\u2713") + pc.dim(" uv installed successfully"));
|
|
406
|
-
uvAvailable = true;
|
|
407
|
-
} else {
|
|
408
|
-
stopUv(pc.yellow("\u26A0") + pc.dim(" Failed to install uv \u2014 install manually: https://docs.astral.sh/uv/"));
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
if (uvAvailable) {
|
|
412
|
-
const stopVenv = spinner("Creating Python venv with Lumera SDK...");
|
|
413
|
-
if (createPythonVenv(targetDir)) {
|
|
414
|
-
stopVenv(pc.green("\u2713") + pc.dim(" Python venv created (.venv/) with lumera SDK"));
|
|
415
|
-
} else {
|
|
416
|
-
stopVenv(pc.yellow("\u26A0") + pc.dim(" Failed to create Python venv"));
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
424
|
if (opts.install) {
|
|
420
425
|
const stopInstall = spinner("Installing dependencies...");
|
|
421
426
|
try {
|
|
@@ -456,18 +461,6 @@ async function init(args) {
|
|
|
456
461
|
setProjectId(targetDir, project.id);
|
|
457
462
|
stopRegister(pc.green("\u2713") + pc.dim(` Project registered (${project.id})`));
|
|
458
463
|
registered = true;
|
|
459
|
-
const stopProjectSkills = spinner("Syncing project skills...");
|
|
460
|
-
try {
|
|
461
|
-
const { installed, failed } = await installAllSkills(targetDir);
|
|
462
|
-
syncClaudeMd(targetDir);
|
|
463
|
-
if (failed > 0) {
|
|
464
|
-
stopProjectSkills(pc.yellow("\u26A0") + pc.dim(` Synced ${installed} project skills (${failed} failed)`));
|
|
465
|
-
} else {
|
|
466
|
-
stopProjectSkills(pc.green("\u2713") + pc.dim(` ${installed} project skills synced`));
|
|
467
|
-
}
|
|
468
|
-
} catch (skillError) {
|
|
469
|
-
stopProjectSkills(pc.yellow("\u26A0") + pc.dim(` Failed to sync project skills: ${skillError}`));
|
|
470
|
-
}
|
|
471
464
|
} catch (e) {
|
|
472
465
|
stopRegister(pc.yellow("\u26A0") + pc.dim(` Could not register project: ${e instanceof Error ? e.message : e}`));
|
|
473
466
|
}
|
|
@@ -479,6 +472,9 @@ async function init(args) {
|
|
|
479
472
|
console.log();
|
|
480
473
|
console.log(pc.cyan(` cd ${finalDirectory}`));
|
|
481
474
|
if (!opts.install) {
|
|
475
|
+
if (hasPythonProject) {
|
|
476
|
+
console.log(pc.cyan(" uv sync --extra dev --no-install-project"));
|
|
477
|
+
}
|
|
482
478
|
console.log(pc.cyan(` ${installCommand}`));
|
|
483
479
|
}
|
|
484
480
|
if (!registered) {
|
|
@@ -493,5 +489,6 @@ async function init(args) {
|
|
|
493
489
|
}
|
|
494
490
|
}
|
|
495
491
|
export {
|
|
492
|
+
createPythonVenv,
|
|
496
493
|
init
|
|
497
494
|
};
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureSkillSymlinks,
|
|
3
|
-
|
|
3
|
+
fetchSkillContent,
|
|
4
4
|
fetchSkillsList,
|
|
5
5
|
getLocalSkills,
|
|
6
6
|
hashContent,
|
|
7
7
|
installAllSkills,
|
|
8
8
|
slugToDirName,
|
|
9
9
|
slugToFilename,
|
|
10
|
-
syncClaudeMd
|
|
11
|
-
|
|
12
|
-
} from "./chunk-GFLMEXIK.js";
|
|
13
|
-
import "./chunk-FHHWIV4C.js";
|
|
10
|
+
syncClaudeMd
|
|
11
|
+
} from "./chunk-VL5GDJKU.js";
|
|
14
12
|
import "./chunk-JLVVHTBY.js";
|
|
15
13
|
import "./chunk-FJFIWC7G.js";
|
|
16
14
|
import "./chunk-PNKVD2UK.js";
|
|
@@ -29,25 +27,32 @@ function findProjectRoot() {
|
|
|
29
27
|
}
|
|
30
28
|
return null;
|
|
31
29
|
}
|
|
32
|
-
async function computeDiff(
|
|
33
|
-
const skills2 = await
|
|
30
|
+
async function computeDiff(skillsDir, filterSlug) {
|
|
31
|
+
const skills2 = await fetchSkillsList();
|
|
34
32
|
const localSkills = getLocalSkills(skillsDir);
|
|
35
33
|
const remoteSkillSlugs = /* @__PURE__ */ new Set();
|
|
36
34
|
const diff = {
|
|
37
|
-
resolved: skills2,
|
|
38
35
|
added: [],
|
|
39
36
|
updated: [],
|
|
40
37
|
removed: [],
|
|
41
38
|
unchanged: []
|
|
42
39
|
};
|
|
43
40
|
const skillsToCheck = filterSlug ? skills2.filter((skill) => skill.slug === filterSlug) : skills2;
|
|
44
|
-
|
|
41
|
+
const remoteResults = await Promise.allSettled(
|
|
42
|
+
skillsToCheck.map(async (skill) => {
|
|
43
|
+
const content = await fetchSkillContent(skill.slug);
|
|
44
|
+
return { skill, content };
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
for (const result of remoteResults) {
|
|
48
|
+
if (result.status !== "fulfilled" || !result.value.content) continue;
|
|
49
|
+
const { skill, content } = result.value;
|
|
45
50
|
remoteSkillSlugs.add(skill.slug);
|
|
46
51
|
const localHash = localSkills.get(skill.slug);
|
|
47
52
|
if (!localHash) {
|
|
48
53
|
diff.added.push(skill);
|
|
49
54
|
} else {
|
|
50
|
-
const remoteHash = hashContent(
|
|
55
|
+
const remoteHash = hashContent(content);
|
|
51
56
|
if (localHash !== remoteHash) {
|
|
52
57
|
diff.updated.push(skill);
|
|
53
58
|
} else {
|
|
@@ -256,7 +261,10 @@ async function update(args, flags) {
|
|
|
256
261
|
}
|
|
257
262
|
const skillsDir = join(projectRoot, ".agents", "skills");
|
|
258
263
|
if (!existsSync(skillsDir)) {
|
|
259
|
-
|
|
264
|
+
console.log(pc.yellow(" \u26A0"), "No skills installed yet");
|
|
265
|
+
console.log(pc.dim(' Run "lumera skills install" first'));
|
|
266
|
+
console.log();
|
|
267
|
+
return;
|
|
260
268
|
}
|
|
261
269
|
if (verbose) {
|
|
262
270
|
console.log(pc.dim(` Project root: ${projectRoot}`));
|
|
@@ -267,14 +275,13 @@ async function update(args, flags) {
|
|
|
267
275
|
console.log();
|
|
268
276
|
}
|
|
269
277
|
try {
|
|
270
|
-
const diff = await computeDiff(
|
|
278
|
+
const diff = await computeDiff(skillsDir, filterSlug);
|
|
271
279
|
const hasChanges = diff.added.length > 0 || diff.updated.length > 0 || diff.removed.length > 0;
|
|
272
280
|
if (!hasChanges) {
|
|
273
281
|
console.log(pc.green(" \u2713"), "All skills are up to date");
|
|
274
282
|
if (verbose && diff.unchanged.length > 0) {
|
|
275
283
|
console.log(pc.dim(` ${diff.unchanged.length} skills unchanged`));
|
|
276
284
|
}
|
|
277
|
-
if (!filterSlug) writeSkillsHashManifest(projectRoot, diff.resolved);
|
|
278
285
|
syncClaudeMd(projectRoot);
|
|
279
286
|
console.log();
|
|
280
287
|
return;
|
|
@@ -322,11 +329,20 @@ async function update(args, flags) {
|
|
|
322
329
|
}
|
|
323
330
|
console.log(pc.dim(" Applying changes..."));
|
|
324
331
|
console.log();
|
|
325
|
-
|
|
332
|
+
const toFetch = [...diff.added, ...diff.updated];
|
|
333
|
+
const fetchResults = await Promise.allSettled(
|
|
334
|
+
toFetch.map(async (skill) => {
|
|
335
|
+
const content = await fetchSkillContent(skill.slug);
|
|
336
|
+
return { skill, content };
|
|
337
|
+
})
|
|
338
|
+
);
|
|
339
|
+
for (const result of fetchResults) {
|
|
340
|
+
if (result.status !== "fulfilled" || !result.value.content) continue;
|
|
341
|
+
const { skill, content } = result.value;
|
|
326
342
|
const dirName = slugToDirName(skill.slug);
|
|
327
343
|
const skillDir = join(skillsDir, dirName);
|
|
328
344
|
mkdirSync(skillDir, { recursive: true });
|
|
329
|
-
writeFileSync(join(skillDir, "SKILL.md"),
|
|
345
|
+
writeFileSync(join(skillDir, "SKILL.md"), content);
|
|
330
346
|
const legacyFile = join(skillsDir, slugToFilename(skill.slug));
|
|
331
347
|
if (existsSync(legacyFile)) {
|
|
332
348
|
rmSync(legacyFile);
|
|
@@ -353,7 +369,6 @@ async function update(args, flags) {
|
|
|
353
369
|
}
|
|
354
370
|
console.log();
|
|
355
371
|
console.log(pc.green(" \u2713"), `Update complete (${changes.join(", ")})`);
|
|
356
|
-
if (!filterSlug) writeSkillsHashManifest(projectRoot, diff.resolved);
|
|
357
372
|
syncClaudeMd(projectRoot);
|
|
358
373
|
ensureSkillSymlinks(projectRoot);
|
|
359
374
|
console.log();
|
package/package.json
CHANGED
|
@@ -13,6 +13,16 @@ lumera apply # Deploy resources
|
|
|
13
13
|
|
|
14
14
|
- `platform/collections/` — Collection schemas (JSON)
|
|
15
15
|
- `platform/automations/` — Python automations
|
|
16
|
+
- `platform/functions/` — Typed Python Functions configured in `pyproject.toml`
|
|
16
17
|
- `platform/hooks/` — JavaScript hooks
|
|
17
18
|
- `src/` — React frontend (TanStack Router + Query)
|
|
18
19
|
- `scripts/` — Utility scripts
|
|
20
|
+
|
|
21
|
+
Add each Function module to `[tool.lumera.functions].modules`, then use
|
|
22
|
+
`lumera functions list`, `lumera functions invoke <id> --local`, and
|
|
23
|
+
`lumera functions test` for the local development loop. With dependency
|
|
24
|
+
installation enabled, scaffolding runs `uv sync` and includes `uv.lock` in the
|
|
25
|
+
initial commit. `--no-install` (used by Studio bootstrap) defers this until the
|
|
26
|
+
first Functions command or an explicit `uv sync`. Refresh and commit the lock
|
|
27
|
+
after changing Python dependencies so every developer and later release build
|
|
28
|
+
resolves the same SDK and dependency graph.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/2.
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.5.6/schema.json",
|
|
3
3
|
"vcs": {
|
|
4
4
|
"enabled": true,
|
|
5
5
|
"clientKind": "git",
|
|
@@ -7,7 +7,15 @@
|
|
|
7
7
|
},
|
|
8
8
|
"files": {
|
|
9
9
|
"ignoreUnknown": false,
|
|
10
|
-
"includes": [
|
|
10
|
+
"includes": [
|
|
11
|
+
"**",
|
|
12
|
+
"!!**/node_modules",
|
|
13
|
+
"!!**/dist",
|
|
14
|
+
"!!**/*routeTree.gen.ts",
|
|
15
|
+
"!!output",
|
|
16
|
+
"!!tmp",
|
|
17
|
+
"!!uploads"
|
|
18
|
+
]
|
|
11
19
|
},
|
|
12
20
|
"formatter": {
|
|
13
21
|
"enabled": true,
|
|
@@ -18,7 +26,7 @@
|
|
|
18
26
|
"linter": {
|
|
19
27
|
"enabled": true,
|
|
20
28
|
"rules": {
|
|
21
|
-
"
|
|
29
|
+
"preset": "recommended",
|
|
22
30
|
"suspicious": { "noExplicitAny": "off" },
|
|
23
31
|
"style": { "noNonNullAssertion": "off" }
|
|
24
32
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Project Function modules."""
|
|
@@ -40,8 +40,8 @@ importers:
|
|
|
40
40
|
version: 3.6.0
|
|
41
41
|
devDependencies:
|
|
42
42
|
'@biomejs/biome':
|
|
43
|
-
specifier:
|
|
44
|
-
version: 2.
|
|
43
|
+
specifier: 2.5.6
|
|
44
|
+
version: 2.5.6
|
|
45
45
|
'@tailwindcss/vite':
|
|
46
46
|
specifier: ^4.2.2
|
|
47
47
|
version: 4.3.0(vite@8.0.16(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4))
|
|
@@ -192,59 +192,59 @@ packages:
|
|
|
192
192
|
'@types/react':
|
|
193
193
|
optional: true
|
|
194
194
|
|
|
195
|
-
'@biomejs/biome@2.
|
|
196
|
-
resolution: {integrity: sha512-
|
|
195
|
+
'@biomejs/biome@2.5.6':
|
|
196
|
+
resolution: {integrity: sha512-lxVNjv7UF6KfhMJfL9gaUHbWdJdHbsAj6OSmwSYNdhRuG67NxNQ4Xdvh3TUxsSK9sBzJBQhEJj3AopmmNJ5pSA==}
|
|
197
197
|
engines: {node: '>=14.21.3'}
|
|
198
198
|
hasBin: true
|
|
199
199
|
|
|
200
|
-
'@biomejs/cli-darwin-arm64@2.
|
|
201
|
-
resolution: {integrity: sha512-
|
|
200
|
+
'@biomejs/cli-darwin-arm64@2.5.6':
|
|
201
|
+
resolution: {integrity: sha512-zMOLZP4oMrjh6m1zcSj1ud2awUPgTuMVbmQhYYWL7J8HwCnbHHBvTm7VBTRuY7epT5bez76IpKYQ11ZAqHFlnw==}
|
|
202
202
|
engines: {node: '>=14.21.3'}
|
|
203
203
|
cpu: [arm64]
|
|
204
204
|
os: [darwin]
|
|
205
205
|
|
|
206
|
-
'@biomejs/cli-darwin-x64@2.
|
|
207
|
-
resolution: {integrity: sha512-
|
|
206
|
+
'@biomejs/cli-darwin-x64@2.5.6':
|
|
207
|
+
resolution: {integrity: sha512-JAC1VqzvO7Th5ZplU0G2uGfkZbxEe9uDDektPAhF0JLusoz1w+T4okp2bkykI0bbaO2vslKiRfj4gU43JaGreA==}
|
|
208
208
|
engines: {node: '>=14.21.3'}
|
|
209
209
|
cpu: [x64]
|
|
210
210
|
os: [darwin]
|
|
211
211
|
|
|
212
|
-
'@biomejs/cli-linux-arm64-musl@2.
|
|
213
|
-
resolution: {integrity: sha512-
|
|
212
|
+
'@biomejs/cli-linux-arm64-musl@2.5.6':
|
|
213
|
+
resolution: {integrity: sha512-eUa3jeeYvfMt19LBeh6E5PUZpxnTC4JqNWo+EDjTtQjAr2xLGnWaxACtVU1DQqmHYbvThlJzLX+ZsYgrqh2qVw==}
|
|
214
214
|
engines: {node: '>=14.21.3'}
|
|
215
215
|
cpu: [arm64]
|
|
216
216
|
os: [linux]
|
|
217
217
|
libc: [musl]
|
|
218
218
|
|
|
219
|
-
'@biomejs/cli-linux-arm64@2.
|
|
220
|
-
resolution: {integrity: sha512-
|
|
219
|
+
'@biomejs/cli-linux-arm64@2.5.6':
|
|
220
|
+
resolution: {integrity: sha512-6XsYwCFkp5sMxl85ffhgeGpGgs6A7dRYFnkceZ7WVxvycuTnGdD5xa534Z3xfrBQ0JCMK/mujT6ZNPJoghedwg==}
|
|
221
221
|
engines: {node: '>=14.21.3'}
|
|
222
222
|
cpu: [arm64]
|
|
223
223
|
os: [linux]
|
|
224
224
|
libc: [glibc]
|
|
225
225
|
|
|
226
|
-
'@biomejs/cli-linux-x64-musl@2.
|
|
227
|
-
resolution: {integrity: sha512-
|
|
226
|
+
'@biomejs/cli-linux-x64-musl@2.5.6':
|
|
227
|
+
resolution: {integrity: sha512-2Vp13QdKysH3HIWLaYLhUUwbK+jbZonJD1K+Lr0d0RO4wH7mkYd43vJixEDm8cUWrowoRz4UUHF1nm9Ae7ym8A==}
|
|
228
228
|
engines: {node: '>=14.21.3'}
|
|
229
229
|
cpu: [x64]
|
|
230
230
|
os: [linux]
|
|
231
231
|
libc: [musl]
|
|
232
232
|
|
|
233
|
-
'@biomejs/cli-linux-x64@2.
|
|
234
|
-
resolution: {integrity: sha512-
|
|
233
|
+
'@biomejs/cli-linux-x64@2.5.6':
|
|
234
|
+
resolution: {integrity: sha512-Pop9VXCFUhFTMfFefZ39S+u2rOPyNp5iHlxbZRwXGACHLy2r0jjiRgJHmaEKJzL3SyxlVeGShXhvvElvWowonA==}
|
|
235
235
|
engines: {node: '>=14.21.3'}
|
|
236
236
|
cpu: [x64]
|
|
237
237
|
os: [linux]
|
|
238
238
|
libc: [glibc]
|
|
239
239
|
|
|
240
|
-
'@biomejs/cli-win32-arm64@2.
|
|
241
|
-
resolution: {integrity: sha512-
|
|
240
|
+
'@biomejs/cli-win32-arm64@2.5.6':
|
|
241
|
+
resolution: {integrity: sha512-tDGshcm6BdkZOCGnTDX0Y8/U4IfBSlnUU7T56nNDuPEfed+aHg+u8G36NB43fJVl0Os6+QURXIE1yuD7AaEofA==}
|
|
242
242
|
engines: {node: '>=14.21.3'}
|
|
243
243
|
cpu: [arm64]
|
|
244
244
|
os: [win32]
|
|
245
245
|
|
|
246
|
-
'@biomejs/cli-win32-x64@2.
|
|
247
|
-
resolution: {integrity: sha512-
|
|
246
|
+
'@biomejs/cli-win32-x64@2.5.6':
|
|
247
|
+
resolution: {integrity: sha512-WN05KwXnTO/2J45RQPvzZMXf7tZUIofHoR35xIPfCo7pQ2RFidxI8sfb5mGsaTxdMmEOzHzOPRCdA5/fCpc7xQ==}
|
|
248
248
|
engines: {node: '>=14.21.3'}
|
|
249
249
|
cpu: [x64]
|
|
250
250
|
os: [win32]
|
|
@@ -2134,39 +2134,39 @@ snapshots:
|
|
|
2134
2134
|
optionalDependencies:
|
|
2135
2135
|
'@types/react': 19.2.16
|
|
2136
2136
|
|
|
2137
|
-
'@biomejs/biome@2.
|
|
2137
|
+
'@biomejs/biome@2.5.6':
|
|
2138
2138
|
optionalDependencies:
|
|
2139
|
-
'@biomejs/cli-darwin-arm64': 2.
|
|
2140
|
-
'@biomejs/cli-darwin-x64': 2.
|
|
2141
|
-
'@biomejs/cli-linux-arm64': 2.
|
|
2142
|
-
'@biomejs/cli-linux-arm64-musl': 2.
|
|
2143
|
-
'@biomejs/cli-linux-x64': 2.
|
|
2144
|
-
'@biomejs/cli-linux-x64-musl': 2.
|
|
2145
|
-
'@biomejs/cli-win32-arm64': 2.
|
|
2146
|
-
'@biomejs/cli-win32-x64': 2.
|
|
2139
|
+
'@biomejs/cli-darwin-arm64': 2.5.6
|
|
2140
|
+
'@biomejs/cli-darwin-x64': 2.5.6
|
|
2141
|
+
'@biomejs/cli-linux-arm64': 2.5.6
|
|
2142
|
+
'@biomejs/cli-linux-arm64-musl': 2.5.6
|
|
2143
|
+
'@biomejs/cli-linux-x64': 2.5.6
|
|
2144
|
+
'@biomejs/cli-linux-x64-musl': 2.5.6
|
|
2145
|
+
'@biomejs/cli-win32-arm64': 2.5.6
|
|
2146
|
+
'@biomejs/cli-win32-x64': 2.5.6
|
|
2147
2147
|
|
|
2148
|
-
'@biomejs/cli-darwin-arm64@2.
|
|
2148
|
+
'@biomejs/cli-darwin-arm64@2.5.6':
|
|
2149
2149
|
optional: true
|
|
2150
2150
|
|
|
2151
|
-
'@biomejs/cli-darwin-x64@2.
|
|
2151
|
+
'@biomejs/cli-darwin-x64@2.5.6':
|
|
2152
2152
|
optional: true
|
|
2153
2153
|
|
|
2154
|
-
'@biomejs/cli-linux-arm64-musl@2.
|
|
2154
|
+
'@biomejs/cli-linux-arm64-musl@2.5.6':
|
|
2155
2155
|
optional: true
|
|
2156
2156
|
|
|
2157
|
-
'@biomejs/cli-linux-arm64@2.
|
|
2157
|
+
'@biomejs/cli-linux-arm64@2.5.6':
|
|
2158
2158
|
optional: true
|
|
2159
2159
|
|
|
2160
|
-
'@biomejs/cli-linux-x64-musl@2.
|
|
2160
|
+
'@biomejs/cli-linux-x64-musl@2.5.6':
|
|
2161
2161
|
optional: true
|
|
2162
2162
|
|
|
2163
|
-
'@biomejs/cli-linux-x64@2.
|
|
2163
|
+
'@biomejs/cli-linux-x64@2.5.6':
|
|
2164
2164
|
optional: true
|
|
2165
2165
|
|
|
2166
|
-
'@biomejs/cli-win32-arm64@2.
|
|
2166
|
+
'@biomejs/cli-win32-arm64@2.5.6':
|
|
2167
2167
|
optional: true
|
|
2168
2168
|
|
|
2169
|
-
'@biomejs/cli-win32-x64@2.
|
|
2169
|
+
'@biomejs/cli-win32-x64@2.5.6':
|
|
2170
2170
|
optional: true
|
|
2171
2171
|
|
|
2172
2172
|
'@braintree/sanitize-url@7.1.2': {}
|
|
@@ -4,11 +4,16 @@ version = "0.1.0"
|
|
|
4
4
|
description = "{{projectTitle}} - Lumera custom app"
|
|
5
5
|
requires-python = ">=3.11"
|
|
6
6
|
dependencies = [
|
|
7
|
-
"lumera",
|
|
7
|
+
"lumera[functions]>=0.29.0,<0.30.0",
|
|
8
8
|
]
|
|
9
9
|
|
|
10
10
|
[project.optional-dependencies]
|
|
11
11
|
dev = [
|
|
12
12
|
"ruff",
|
|
13
|
-
"pytest",
|
|
13
|
+
"pytest>=8.0,<10.0",
|
|
14
14
|
]
|
|
15
|
+
|
|
16
|
+
[tool.lumera.functions]
|
|
17
|
+
source-root = "platform"
|
|
18
|
+
modules = []
|
|
19
|
+
test-paths = ["tests/functions"]
|
|
@@ -7,19 +7,13 @@
|
|
|
7
7
|
height: 0;
|
|
8
8
|
}
|
|
9
9
|
to {
|
|
10
|
-
height: var(
|
|
11
|
-
--radix-accordion-content-height,
|
|
12
|
-
var(--accordion-panel-height, auto)
|
|
13
|
-
);
|
|
10
|
+
height: var(--radix-accordion-content-height, var(--accordion-panel-height, auto));
|
|
14
11
|
}
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
@keyframes accordion-up {
|
|
18
15
|
from {
|
|
19
|
-
height: var(
|
|
20
|
-
--radix-accordion-content-height,
|
|
21
|
-
var(--accordion-panel-height, auto)
|
|
22
|
-
);
|
|
16
|
+
height: var(--radix-accordion-content-height, var(--accordion-panel-height, auto));
|
|
23
17
|
}
|
|
24
18
|
to {
|
|
25
19
|
height: 0;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|