@loom-framework/core 0.1.0-alpha.8 → 0.1.0-alpha.80
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/dist/adapter-base.d.ts +29 -0
- package/dist/adapter-base.d.ts.map +1 -0
- package/dist/adapter-base.js +62 -0
- package/dist/adapter-base.js.map +1 -0
- package/dist/adapter-factory.d.ts +8 -0
- package/dist/adapter-factory.d.ts.map +1 -0
- package/dist/adapter-factory.js +25 -0
- package/dist/adapter-factory.js.map +1 -0
- package/dist/adapter-filesystem.d.ts +6 -11
- package/dist/adapter-filesystem.d.ts.map +1 -1
- package/dist/adapter-filesystem.js +56 -41
- package/dist/adapter-filesystem.js.map +1 -1
- package/dist/adapter-sqlite.d.ts +6 -23
- package/dist/adapter-sqlite.d.ts.map +1 -1
- package/dist/adapter-sqlite.js +65 -50
- package/dist/adapter-sqlite.js.map +1 -1
- package/dist/backend/ai/button-resolver.d.ts +18 -0
- package/dist/backend/ai/button-resolver.d.ts.map +1 -0
- package/dist/backend/ai/button-resolver.js +58 -0
- package/dist/backend/ai/button-resolver.js.map +1 -0
- package/dist/backend/ai/engine.d.ts +52 -0
- package/dist/backend/ai/engine.d.ts.map +1 -0
- package/dist/backend/ai/engine.js +186 -0
- package/dist/backend/ai/engine.js.map +1 -0
- package/dist/backend/ai/index.d.ts +11 -0
- package/dist/backend/ai/index.d.ts.map +1 -0
- package/dist/backend/ai/index.js +8 -0
- package/dist/backend/ai/index.js.map +1 -0
- package/dist/backend/ai/output-parser.d.ts +29 -0
- package/dist/backend/ai/output-parser.d.ts.map +1 -0
- package/dist/backend/ai/output-parser.js +247 -0
- package/dist/backend/ai/output-parser.js.map +1 -0
- package/dist/backend/ai/session-manager.d.ts +103 -0
- package/dist/backend/ai/session-manager.d.ts.map +1 -0
- package/dist/backend/ai/session-manager.js +298 -0
- package/dist/backend/ai/session-manager.js.map +1 -0
- package/dist/backend/index.d.ts +61 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +161 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/observe/index.d.ts +6 -0
- package/dist/backend/observe/index.d.ts.map +1 -0
- package/dist/backend/observe/index.js +5 -0
- package/dist/backend/observe/index.js.map +1 -0
- package/dist/backend/observe/logger.d.ts +28 -0
- package/dist/backend/observe/logger.d.ts.map +1 -0
- package/dist/backend/observe/logger.js +80 -0
- package/dist/backend/observe/logger.js.map +1 -0
- package/dist/backend/observe/types.d.ts +26 -0
- package/dist/backend/observe/types.d.ts.map +1 -0
- package/dist/backend/observe/types.js +7 -0
- package/dist/backend/observe/types.js.map +1 -0
- package/dist/backend/routes/chat.d.ts +31 -0
- package/dist/backend/routes/chat.d.ts.map +1 -0
- package/dist/backend/routes/chat.js +426 -0
- package/dist/backend/routes/chat.js.map +1 -0
- package/dist/backend/routes/data.d.ts +13 -0
- package/dist/backend/routes/data.d.ts.map +1 -0
- package/dist/backend/routes/data.js +134 -0
- package/dist/backend/routes/data.js.map +1 -0
- package/dist/backend/routes/health.d.ts +7 -0
- package/dist/backend/routes/health.d.ts.map +1 -0
- package/dist/backend/routes/health.js +15 -0
- package/dist/backend/routes/health.js.map +1 -0
- package/dist/backend/routes/index.d.ts +11 -0
- package/dist/backend/routes/index.d.ts.map +1 -0
- package/dist/backend/routes/index.js +9 -0
- package/dist/backend/routes/index.js.map +1 -0
- package/dist/backend/routes/skills.d.ts +16 -0
- package/dist/backend/routes/skills.d.ts.map +1 -0
- package/dist/backend/routes/skills.js +590 -0
- package/dist/backend/routes/skills.js.map +1 -0
- package/dist/backend/routes/upload.d.ts +24 -0
- package/dist/backend/routes/upload.d.ts.map +1 -0
- package/dist/backend/routes/upload.js +67 -0
- package/dist/backend/routes/upload.js.map +1 -0
- package/dist/bin.d.ts +8 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +12 -0
- package/dist/bin.js.map +1 -0
- package/dist/capability-generator.d.ts +21 -6
- package/dist/capability-generator.d.ts.map +1 -1
- package/dist/capability-generator.js +88 -261
- package/dist/capability-generator.js.map +1 -1
- package/dist/cli/commands/build.d.ts +11 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +170 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/data.d.ts +12 -0
- package/dist/cli/commands/data.d.ts.map +1 -0
- package/dist/cli/commands/data.js +158 -0
- package/dist/cli/commands/data.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +9 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +114 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate-capabilities.d.ts +8 -0
- package/dist/cli/commands/generate-capabilities.d.ts.map +1 -0
- package/dist/cli/commands/generate-capabilities.js +40 -0
- package/dist/cli/commands/generate-capabilities.js.map +1 -0
- package/dist/cli/commands/generate-cli-command.d.ts +8 -0
- package/dist/cli/commands/generate-cli-command.d.ts.map +1 -0
- package/dist/cli/commands/generate-cli-command.js +64 -0
- package/dist/cli/commands/generate-cli-command.js.map +1 -0
- package/dist/cli/commands/generate-page.d.ts +9 -0
- package/dist/cli/commands/generate-page.d.ts.map +1 -0
- package/dist/cli/commands/generate-page.js +641 -0
- package/dist/cli/commands/generate-page.js.map +1 -0
- package/dist/cli/commands/generate-skill.d.ts +8 -0
- package/dist/cli/commands/generate-skill.d.ts.map +1 -0
- package/dist/cli/commands/generate-skill.js +75 -0
- package/dist/cli/commands/generate-skill.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +6 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +17 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +539 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/observe.d.ts +9 -0
- package/dist/cli/commands/observe.d.ts.map +1 -0
- package/dist/cli/commands/observe.js +142 -0
- package/dist/cli/commands/observe.js.map +1 -0
- package/dist/cli/commands/skill.d.ts +9 -0
- package/dist/cli/commands/skill.d.ts.map +1 -0
- package/dist/cli/commands/skill.js +186 -0
- package/dist/cli/commands/skill.js.map +1 -0
- package/dist/cli/helpers/duration.d.ts +5 -0
- package/dist/cli/helpers/duration.d.ts.map +1 -0
- package/dist/cli/helpers/duration.js +19 -0
- package/dist/cli/helpers/duration.js.map +1 -0
- package/dist/cli/helpers/field-template.d.ts +10 -0
- package/dist/cli/helpers/field-template.d.ts.map +1 -0
- package/dist/cli/helpers/field-template.js +100 -0
- package/dist/cli/helpers/field-template.js.map +1 -0
- package/dist/cli/helpers/naming.d.ts +12 -0
- package/dist/cli/helpers/naming.d.ts.map +1 -0
- package/dist/cli/helpers/naming.js +25 -0
- package/dist/cli/helpers/naming.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +33 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils.d.ts +10 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +31 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/config.d.ts +8 -40
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -8
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/server-bin.d.ts +12 -0
- package/dist/server-bin.d.ts.map +1 -0
- package/dist/server-bin.js +75 -0
- package/dist/server-bin.js.map +1 -0
- package/dist/types.d.ts +33 -20
- package/dist/types.d.ts.map +1 -1
- package/package.json +25 -10
- package/templates/app-skill/SKILL.md +27 -0
- package/templates/app-skill/references/data-semantics.md +44 -0
- package/templates/app-skill/references/models.md +31 -0
- package/templates/loom-skill/SKILL.md +140 -0
- package/templates/loom-skill/references/README.md +128 -0
- package/templates/loom-skill/references/data-model.md +78 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom generate skill <name>
|
|
3
|
+
*
|
|
4
|
+
* Create .claude/skills/<name>/SKILL.md skeleton and references/ directory.
|
|
5
|
+
*/
|
|
6
|
+
import type { Command } from 'commander';
|
|
7
|
+
export declare function registerGenerateSkillCommand(program: Command): void;
|
|
8
|
+
//# sourceMappingURL=generate-skill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-skill.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate-skill.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCzC,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyCnE"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom generate skill <name>
|
|
3
|
+
*
|
|
4
|
+
* Create .claude/skills/<name>/SKILL.md skeleton and references/ directory.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { promises as fs } from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { resolveProjectRoot } from '../utils.js';
|
|
10
|
+
const SKILL_MD_TEMPLATE = (name) => `---
|
|
11
|
+
name: ${name}
|
|
12
|
+
version: 0.1.0
|
|
13
|
+
description: ""
|
|
14
|
+
tags: []
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# ${name}
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
TODO: Describe what this skill does.
|
|
22
|
+
|
|
23
|
+
## Instructions
|
|
24
|
+
|
|
25
|
+
TODO: Step-by-step instructions for the skill.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
TODO: Example inputs and outputs.
|
|
30
|
+
|
|
31
|
+
## References
|
|
32
|
+
|
|
33
|
+
TODO: List reference files in the references/ directory.
|
|
34
|
+
`;
|
|
35
|
+
export function registerGenerateSkillCommand(program) {
|
|
36
|
+
program
|
|
37
|
+
.command('skill <name>')
|
|
38
|
+
.description('Create a new skill skeleton with SKILL.md and references directory')
|
|
39
|
+
.action(async (name) => {
|
|
40
|
+
try {
|
|
41
|
+
const projectRoot = await resolveProjectRoot();
|
|
42
|
+
const skillDir = path.join(projectRoot, '.claude', 'skills', name);
|
|
43
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
44
|
+
const referencesDir = path.join(skillDir, 'references');
|
|
45
|
+
// Check if skill already exists
|
|
46
|
+
try {
|
|
47
|
+
await fs.access(skillDir);
|
|
48
|
+
console.error(chalk.red(`Skill "${name}" already exists at ${skillDir}`));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Directory does not exist, proceed
|
|
53
|
+
}
|
|
54
|
+
await fs.mkdir(referencesDir, { recursive: true });
|
|
55
|
+
await fs.writeFile(skillMdPath, SKILL_MD_TEMPLATE(name), 'utf-8');
|
|
56
|
+
// Create a .gitkeep in references so the directory is tracked
|
|
57
|
+
await fs.writeFile(path.join(referencesDir, '.gitkeep'), '', 'utf-8');
|
|
58
|
+
console.log(chalk.green('Skill created successfully!'));
|
|
59
|
+
console.log();
|
|
60
|
+
console.log(chalk.bold(' Skill:'), name);
|
|
61
|
+
console.log(chalk.bold(' Path:'), skillDir);
|
|
62
|
+
console.log(chalk.bold(' Files:'));
|
|
63
|
+
console.log(chalk.gray(' -'), 'SKILL.md');
|
|
64
|
+
console.log(chalk.gray(' -'), 'references/');
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(chalk.dim(` Edit ${path.join('.claude/skills', name, 'SKILL.md')} to define the skill.`));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
70
|
+
console.error(chalk.red('Failed to create skill:'), message);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=generate-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-skill.js","sourceRoot":"","sources":["../../../src/cli/commands/generate-skill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAS9D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom generate - Code generation commands
|
|
3
|
+
*/
|
|
4
|
+
import { registerGenerateCapabilitiesCommand } from './generate-capabilities.js';
|
|
5
|
+
import { registerGenerateSkillCommand } from './generate-skill.js';
|
|
6
|
+
import { registerGeneratePageCommand } from './generate-page.js';
|
|
7
|
+
import { registerGenerateCliCommandCommand } from './generate-cli-command.js';
|
|
8
|
+
export function registerGenerateCommand(program) {
|
|
9
|
+
const generate = program
|
|
10
|
+
.command('generate')
|
|
11
|
+
.description('Generate code from config and templates');
|
|
12
|
+
registerGenerateCapabilitiesCommand(generate);
|
|
13
|
+
registerGenerateSkillCommand(generate);
|
|
14
|
+
registerGeneratePageCommand(generate);
|
|
15
|
+
registerGenerateCliCommandCommand(generate);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,mCAAmC,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAE9E,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,MAAM,QAAQ,GAAG,OAAO;SACrB,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,yCAAyC,CAAC,CAAC;IAE1D,mCAAmC,CAAC,QAAQ,CAAC,CAAC;IAC9C,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACvC,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IACtC,iCAAiC,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom init <name>
|
|
3
|
+
*
|
|
4
|
+
* Create a new Loom project. All options via CLI flags, no interactive prompts.
|
|
5
|
+
*/
|
|
6
|
+
import type { Command } from 'commander';
|
|
7
|
+
export declare function registerInitCommand(program: Command): void;
|
|
8
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAe1D"}
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom init <name>
|
|
3
|
+
*
|
|
4
|
+
* Create a new Loom project. All options via CLI flags, no interactive prompts.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { promises as fs } from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
export function registerInitCommand(program) {
|
|
11
|
+
program
|
|
12
|
+
.command('init <name>')
|
|
13
|
+
.description('Create a new Loom project')
|
|
14
|
+
.option('-d, --description <desc>', 'Project description', '')
|
|
15
|
+
.option('-a, --adapter <adapter>', 'Data adapter (filesystem|sqlite)', 'filesystem')
|
|
16
|
+
.action(async (name, cmdOptions) => {
|
|
17
|
+
const options = {
|
|
18
|
+
name,
|
|
19
|
+
description: cmdOptions.description || '',
|
|
20
|
+
adapter: cmdOptions.adapter === 'sqlite' ? 'sqlite' : 'filesystem',
|
|
21
|
+
};
|
|
22
|
+
await createProject(options);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function createProject(options) {
|
|
26
|
+
const cwd = process.cwd();
|
|
27
|
+
const cwdName = path.basename(cwd);
|
|
28
|
+
// If current directory name matches project name, initialize in place (like npm init)
|
|
29
|
+
const targetDir = cwdName === options.name
|
|
30
|
+
? cwd
|
|
31
|
+
: path.resolve(cwd, options.name);
|
|
32
|
+
// Check if directory already exists (only for non-cwd case)
|
|
33
|
+
if (targetDir !== cwd) {
|
|
34
|
+
try {
|
|
35
|
+
await fs.access(targetDir);
|
|
36
|
+
console.error(chalk.red(`Directory "${options.name}" already exists.`));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Directory does not exist, proceed
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
console.log();
|
|
44
|
+
console.log(chalk.bold.cyan(' Welcome to Loom!'));
|
|
45
|
+
console.log(chalk.dim(' Weave AI capabilities into your application'));
|
|
46
|
+
console.log();
|
|
47
|
+
console.log(chalk.dim(` Creating project in ${targetDir}...`));
|
|
48
|
+
console.log();
|
|
49
|
+
// Create directory structure
|
|
50
|
+
await createDirectoryStructure(targetDir);
|
|
51
|
+
// Copy SKILL.md + references from templates
|
|
52
|
+
await copySkillTemplates(targetDir);
|
|
53
|
+
// Create loom.config.ts
|
|
54
|
+
await createLoomConfig(targetDir, options);
|
|
55
|
+
// Create package.json
|
|
56
|
+
await createPackageJson(targetDir, options);
|
|
57
|
+
// Create README
|
|
58
|
+
await createReadme(targetDir, options);
|
|
59
|
+
// Create tsconfig
|
|
60
|
+
await createTsconfig(targetDir);
|
|
61
|
+
// Create vite config
|
|
62
|
+
await createViteConfig(targetDir);
|
|
63
|
+
// Create frontend entry files
|
|
64
|
+
await createFrontendEntry(targetDir, options);
|
|
65
|
+
// Create backend entry file
|
|
66
|
+
await createBackendEntry(targetDir);
|
|
67
|
+
// Create .gitignore
|
|
68
|
+
await createGitignore(targetDir);
|
|
69
|
+
// Install dependencies
|
|
70
|
+
await installDependencies(targetDir);
|
|
71
|
+
// Verify better-sqlite3 native module if sqlite adapter selected
|
|
72
|
+
if (options.adapter === 'sqlite') {
|
|
73
|
+
await verifySqliteNativeModule(targetDir);
|
|
74
|
+
}
|
|
75
|
+
console.log();
|
|
76
|
+
console.log(chalk.green.bold(' Project created successfully!'));
|
|
77
|
+
console.log();
|
|
78
|
+
console.log(chalk.dim(' Next steps:'));
|
|
79
|
+
if (targetDir !== cwd) {
|
|
80
|
+
console.log(chalk.cyan(` cd ${options.name}`));
|
|
81
|
+
}
|
|
82
|
+
console.log(chalk.cyan(` loom generate page <Name> --model <model-name>`));
|
|
83
|
+
console.log(chalk.cyan(' loom dev'));
|
|
84
|
+
console.log();
|
|
85
|
+
}
|
|
86
|
+
async function createDirectoryStructure(targetDir) {
|
|
87
|
+
const dirs = [
|
|
88
|
+
'',
|
|
89
|
+
'data',
|
|
90
|
+
'.claude',
|
|
91
|
+
'.claude/skills',
|
|
92
|
+
'.claude/skills/loom',
|
|
93
|
+
'.claude/skills/loom/references',
|
|
94
|
+
'.loom',
|
|
95
|
+
'cli',
|
|
96
|
+
'cli/src',
|
|
97
|
+
'cli/src/commands',
|
|
98
|
+
'frontend',
|
|
99
|
+
'frontend/src',
|
|
100
|
+
'frontend/src/components',
|
|
101
|
+
'frontend/src/components/pages',
|
|
102
|
+
'backend',
|
|
103
|
+
'backend/src',
|
|
104
|
+
];
|
|
105
|
+
for (const dir of dirs) {
|
|
106
|
+
await fs.mkdir(path.join(targetDir, dir), { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
// Create .gitkeep files for empty directories
|
|
109
|
+
const gitkeepDirs = ['data', '.loom', 'cli/src/commands', 'frontend/src/components/pages', '.claude/skills/loom/references'];
|
|
110
|
+
for (const dir of gitkeepDirs) {
|
|
111
|
+
await fs.writeFile(path.join(targetDir, dir, '.gitkeep'), '', 'utf-8');
|
|
112
|
+
}
|
|
113
|
+
console.log(chalk.green(' ✓'), 'Directory structure');
|
|
114
|
+
}
|
|
115
|
+
async function copySkillTemplates(targetDir) {
|
|
116
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
117
|
+
// From dist/cli/commands/ → go up to package root → templates/loom-skill/
|
|
118
|
+
const templatesDir = path.resolve(__dirname, '../../../templates/loom-skill');
|
|
119
|
+
// Copy SKILL.md
|
|
120
|
+
const skillMd = await fs.readFile(path.join(templatesDir, 'SKILL.md'), 'utf-8');
|
|
121
|
+
await fs.writeFile(path.join(targetDir, '.claude/skills/loom/SKILL.md'), skillMd, 'utf-8');
|
|
122
|
+
console.log(chalk.green(' ✓'), '.claude/skills/loom/SKILL.md');
|
|
123
|
+
// Copy references
|
|
124
|
+
const refsDir = path.join(templatesDir, 'references');
|
|
125
|
+
const refFiles = await fs.readdir(refsDir);
|
|
126
|
+
for (const file of refFiles) {
|
|
127
|
+
if (file.endsWith('.md')) {
|
|
128
|
+
const content = await fs.readFile(path.join(refsDir, file), 'utf-8');
|
|
129
|
+
await fs.writeFile(path.join(targetDir, '.claude/skills/loom/references', file), content, 'utf-8');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
console.log(chalk.green(' ✓'), `.claude/skills/loom/references/ (${refFiles.filter(f => f.endsWith('.md')).length} files)`);
|
|
133
|
+
}
|
|
134
|
+
async function createLoomConfig(targetDir, options) {
|
|
135
|
+
const config = `import { defineConfig } from '@loom-framework/core';
|
|
136
|
+
|
|
137
|
+
export default defineConfig({
|
|
138
|
+
project: {
|
|
139
|
+
name: '${options.name}',
|
|
140
|
+
description: '${options.description}',
|
|
141
|
+
},
|
|
142
|
+
data: {
|
|
143
|
+
defaultAdapter: '${options.adapter}',
|
|
144
|
+
models: [
|
|
145
|
+
{
|
|
146
|
+
name: 'items',
|
|
147
|
+
fields: [
|
|
148
|
+
{ name: 'id', type: 'string', required: true },
|
|
149
|
+
{ name: 'name', type: 'string', required: true },
|
|
150
|
+
{ name: 'description', type: 'string' },
|
|
151
|
+
{ name: 'createdAt', type: 'date' },
|
|
152
|
+
],
|
|
153
|
+
// 示例数据实体 - 实体名将作为 React 组件名,运行 loom generate page Items --model items 后可自定义
|
|
154
|
+
description: 'Example items model - customize or remove',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
`;
|
|
160
|
+
await fs.writeFile(path.join(targetDir, 'loom.config.ts'), config, 'utf-8');
|
|
161
|
+
console.log(chalk.green(' ✓'), 'loom.config.ts');
|
|
162
|
+
}
|
|
163
|
+
async function createPackageJson(targetDir, options) {
|
|
164
|
+
const pkg = {
|
|
165
|
+
name: options.name,
|
|
166
|
+
version: '0.1.0',
|
|
167
|
+
type: 'module',
|
|
168
|
+
scripts: {
|
|
169
|
+
dev: 'loom dev',
|
|
170
|
+
build: 'loom build',
|
|
171
|
+
generate: 'loom generate capabilities',
|
|
172
|
+
},
|
|
173
|
+
dependencies: {
|
|
174
|
+
'@loom-framework/core': '^0.1.0-alpha.80',
|
|
175
|
+
'@loom-framework/frontend-antd': '^0.1.0-alpha.80',
|
|
176
|
+
'fastify': '^5.2.0',
|
|
177
|
+
'@ant-design/x': '^2.5.0',
|
|
178
|
+
'@ant-design/x-sdk': '^2.5.0',
|
|
179
|
+
'@ant-design/x-markdown': '^2.5.0',
|
|
180
|
+
'@ant-design/icons': '^6.1.1',
|
|
181
|
+
'@fastify/cors': '^10.0.0',
|
|
182
|
+
'@fastify/static': '^8.0.0',
|
|
183
|
+
'react': '^19.0.0',
|
|
184
|
+
'react-dom': '^19.0.0',
|
|
185
|
+
'antd': '^6.3.5',
|
|
186
|
+
'dayjs': '^1.11.0',
|
|
187
|
+
},
|
|
188
|
+
devDependencies: {
|
|
189
|
+
'typescript': '^5.6.0',
|
|
190
|
+
'tsx': '^4.7.0',
|
|
191
|
+
'vite': '^6.0.0',
|
|
192
|
+
'@types/react': '^19.0.0',
|
|
193
|
+
'@types/react-dom': '^19.0.0',
|
|
194
|
+
'@vitejs/plugin-react': '^4.3.0',
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
await fs.writeFile(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2), 'utf-8');
|
|
198
|
+
console.log(chalk.green(' ✓'), 'package.json');
|
|
199
|
+
}
|
|
200
|
+
async function createReadme(targetDir, options) {
|
|
201
|
+
const readme = `# ${options.name}
|
|
202
|
+
|
|
203
|
+
${options.description || 'A Loom application.'}
|
|
204
|
+
|
|
205
|
+
## Getting Started
|
|
206
|
+
|
|
207
|
+
\`\`\`bash
|
|
208
|
+
# Generate a CRUD page from your data model
|
|
209
|
+
loom generate page <Name> --model <model-name>
|
|
210
|
+
|
|
211
|
+
# Start development servers (frontend + backend, hot-reload)
|
|
212
|
+
loom dev
|
|
213
|
+
|
|
214
|
+
# Build for production
|
|
215
|
+
loom build
|
|
216
|
+
\`\`\`
|
|
217
|
+
|
|
218
|
+
## Project Structure
|
|
219
|
+
|
|
220
|
+
\`\`\`
|
|
221
|
+
${options.name}/
|
|
222
|
+
├── loom.config.ts # Project configuration
|
|
223
|
+
├── data/ # Data storage (filesystem adapter)
|
|
224
|
+
├── .claude/skills/ # AI skill definitions
|
|
225
|
+
├── cli/ # CLI commands
|
|
226
|
+
├── frontend/ # Frontend application
|
|
227
|
+
├── backend/ # Backend server
|
|
228
|
+
└── .loom/ # Generated files (MCP server, etc.)
|
|
229
|
+
\`\`\`
|
|
230
|
+
|
|
231
|
+
## Commands
|
|
232
|
+
|
|
233
|
+
- \`loom init <name>\` - Create a new project
|
|
234
|
+
- \`loom generate page <Name> --model <model>\` - Generate CRUD page (includes capabilities + wiring)
|
|
235
|
+
- \`loom generate skill <name>\` - Create a new skill
|
|
236
|
+
- \`loom dev\` - Start development servers
|
|
237
|
+
- \`loom build\` - Build all components
|
|
238
|
+
- \`loom data <command>\` - Data operations
|
|
239
|
+
- \`loom skill list\` - List skills
|
|
240
|
+
- \`loom skill validate\` - Validate skill definitions
|
|
241
|
+
`;
|
|
242
|
+
await fs.writeFile(path.join(targetDir, 'README.md'), readme, 'utf-8');
|
|
243
|
+
console.log(chalk.green(' ✓'), 'README.md');
|
|
244
|
+
}
|
|
245
|
+
async function createTsconfig(targetDir) {
|
|
246
|
+
const tsconfig = {
|
|
247
|
+
compilerOptions: {
|
|
248
|
+
target: 'ES2022',
|
|
249
|
+
module: 'Node16',
|
|
250
|
+
moduleResolution: 'Node16',
|
|
251
|
+
strict: true,
|
|
252
|
+
esModuleInterop: true,
|
|
253
|
+
skipLibCheck: true,
|
|
254
|
+
forceConsistentCasingInFileNames: true,
|
|
255
|
+
resolveJsonModule: true,
|
|
256
|
+
declaration: true,
|
|
257
|
+
sourceMap: true,
|
|
258
|
+
},
|
|
259
|
+
exclude: ['node_modules', 'dist', 'frontend'],
|
|
260
|
+
};
|
|
261
|
+
await fs.writeFile(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2), 'utf-8');
|
|
262
|
+
console.log(chalk.green(' ✓'), 'tsconfig.json');
|
|
263
|
+
}
|
|
264
|
+
async function createViteConfig(targetDir) {
|
|
265
|
+
const viteConfig = `import { defineConfig } from 'vite';
|
|
266
|
+
import react from '@vitejs/plugin-react';
|
|
267
|
+
|
|
268
|
+
export default defineConfig({
|
|
269
|
+
plugins: [react()],
|
|
270
|
+
root: 'frontend',
|
|
271
|
+
build: {
|
|
272
|
+
outDir: '../dist/frontend',
|
|
273
|
+
emptyOutDir: true,
|
|
274
|
+
},
|
|
275
|
+
server: {
|
|
276
|
+
port: 5173,
|
|
277
|
+
proxy: {
|
|
278
|
+
'/api': 'http://localhost:3000',
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
`;
|
|
283
|
+
await fs.writeFile(path.join(targetDir, 'vite.config.ts'), viteConfig, 'utf-8');
|
|
284
|
+
console.log(chalk.green(' ✓'), 'vite.config.ts');
|
|
285
|
+
}
|
|
286
|
+
async function createFrontendEntry(targetDir, options) {
|
|
287
|
+
const mainTsx = `import React from 'react';
|
|
288
|
+
import ReactDOM from 'react-dom/client';
|
|
289
|
+
import App from './App';
|
|
290
|
+
|
|
291
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
292
|
+
<React.StrictMode>
|
|
293
|
+
<App />
|
|
294
|
+
</React.StrictMode>,
|
|
295
|
+
);
|
|
296
|
+
`;
|
|
297
|
+
const appTsx = `import React, { useState } from 'react';
|
|
298
|
+
import { AppShell } from '@loom-framework/frontend-antd';
|
|
299
|
+
|
|
300
|
+
const navItems = [
|
|
301
|
+
{ key: 'home', label: '首页' },
|
|
302
|
+
// Add more nav items as you generate pages:
|
|
303
|
+
// { key: 'model-name', label: '数据实体名' },
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
export default function App() {
|
|
307
|
+
const [selectedKey, setSelectedKey] = useState('home');
|
|
308
|
+
|
|
309
|
+
const renderPage = () => {
|
|
310
|
+
switch (selectedKey) {
|
|
311
|
+
case 'home':
|
|
312
|
+
return (
|
|
313
|
+
<div style={{ padding: 24 }}>
|
|
314
|
+
<h2>${options.name}</h2>
|
|
315
|
+
<p>${options.description || 'A Loom application.'}</p>
|
|
316
|
+
<p>运行 <code>loom generate page <Name> --model <model></code> 生成页面后,在此处引入。</p>
|
|
317
|
+
</div>
|
|
318
|
+
);
|
|
319
|
+
default:
|
|
320
|
+
return (
|
|
321
|
+
<div style={{ padding: 24 }}>
|
|
322
|
+
<h2>${options.name}</h2>
|
|
323
|
+
</div>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<AppShell
|
|
330
|
+
title="${options.name}"
|
|
331
|
+
navItems={navItems}
|
|
332
|
+
selectedNavKey={selectedKey}
|
|
333
|
+
onNavClick={setSelectedKey}
|
|
334
|
+
baseUrl=""
|
|
335
|
+
>
|
|
336
|
+
{renderPage()}
|
|
337
|
+
</AppShell>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
`;
|
|
341
|
+
const indexHtml = `<!DOCTYPE html>
|
|
342
|
+
<html lang="en">
|
|
343
|
+
<head>
|
|
344
|
+
<meta charset="UTF-8" />
|
|
345
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
346
|
+
<title>${options.name}</title>
|
|
347
|
+
</head>
|
|
348
|
+
<body>
|
|
349
|
+
<div id="root"></div>
|
|
350
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
351
|
+
</body>
|
|
352
|
+
</html>
|
|
353
|
+
`;
|
|
354
|
+
await fs.writeFile(path.join(targetDir, 'frontend/src/main.tsx'), mainTsx, 'utf-8');
|
|
355
|
+
await fs.writeFile(path.join(targetDir, 'frontend/src/App.tsx'), appTsx, 'utf-8');
|
|
356
|
+
await fs.writeFile(path.join(targetDir, 'frontend/index.html'), indexHtml, 'utf-8');
|
|
357
|
+
const frontendTsconfig = `{
|
|
358
|
+
"compilerOptions": {
|
|
359
|
+
"target": "ES2022",
|
|
360
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
361
|
+
"module": "ESNext",
|
|
362
|
+
"moduleResolution": "bundler",
|
|
363
|
+
"jsx": "react-jsx",
|
|
364
|
+
"strict": true,
|
|
365
|
+
"esModuleInterop": true,
|
|
366
|
+
"skipLibCheck": true,
|
|
367
|
+
"forceConsistentCasingInFileNames": true,
|
|
368
|
+
"resolveJsonModule": true,
|
|
369
|
+
"isolatedModules": true,
|
|
370
|
+
"noEmit": true
|
|
371
|
+
},
|
|
372
|
+
"include": ["src"]
|
|
373
|
+
}
|
|
374
|
+
`;
|
|
375
|
+
await fs.writeFile(path.join(targetDir, 'frontend/tsconfig.json'), frontendTsconfig, 'utf-8');
|
|
376
|
+
console.log(chalk.green(' ✓'), 'frontend entry files');
|
|
377
|
+
}
|
|
378
|
+
async function createBackendEntry(targetDir) {
|
|
379
|
+
const serverTs = `import { fileURLToPath } from 'url';
|
|
380
|
+
import path from 'path';
|
|
381
|
+
import { LoomServer } from '@loom-framework/core';
|
|
382
|
+
|
|
383
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
384
|
+
const projectRoot = path.resolve(__dirname, '../..');
|
|
385
|
+
|
|
386
|
+
const server = new LoomServer({ projectRoot });
|
|
387
|
+
await server.initialize();
|
|
388
|
+
await server.start();
|
|
389
|
+
`;
|
|
390
|
+
await fs.writeFile(path.join(targetDir, 'backend/src/index.ts'), serverTs, 'utf-8');
|
|
391
|
+
console.log(chalk.green(' ✓'), 'backend/src/index.ts');
|
|
392
|
+
}
|
|
393
|
+
async function createGitignore(targetDir) {
|
|
394
|
+
const gitignore = `node_modules/
|
|
395
|
+
dist/
|
|
396
|
+
.loom/
|
|
397
|
+
*.local
|
|
398
|
+
.env
|
|
399
|
+
data/*.db
|
|
400
|
+
`;
|
|
401
|
+
await fs.writeFile(path.join(targetDir, '.gitignore'), gitignore, 'utf-8');
|
|
402
|
+
console.log(chalk.green(' ✓'), '.gitignore');
|
|
403
|
+
}
|
|
404
|
+
async function detectPackageManager(targetDir) {
|
|
405
|
+
const { existsSync } = await import('fs');
|
|
406
|
+
const path = await import('path');
|
|
407
|
+
// 1. Check lockfiles in target directory (most reliable)
|
|
408
|
+
if (existsSync(path.join(targetDir, 'pnpm-lock.yaml')))
|
|
409
|
+
return 'pnpm';
|
|
410
|
+
if (existsSync(path.join(targetDir, 'yarn.lock')))
|
|
411
|
+
return 'yarn';
|
|
412
|
+
if (existsSync(path.join(targetDir, 'bun.lockb')) || existsSync(path.join(targetDir, 'bun.lock')))
|
|
413
|
+
return 'bun';
|
|
414
|
+
// 2. Check lockfiles in current working directory (user may be in a monorepo)
|
|
415
|
+
const cwd = process.cwd();
|
|
416
|
+
if (cwd !== targetDir) {
|
|
417
|
+
if (existsSync(path.join(cwd, 'pnpm-lock.yaml')))
|
|
418
|
+
return 'pnpm';
|
|
419
|
+
if (existsSync(path.join(cwd, 'yarn.lock')))
|
|
420
|
+
return 'yarn';
|
|
421
|
+
if (existsSync(path.join(cwd, 'bun.lockb')) || existsSync(path.join(cwd, 'bun.lock')))
|
|
422
|
+
return 'bun';
|
|
423
|
+
}
|
|
424
|
+
// 3. Check which package managers are globally available
|
|
425
|
+
const { execSync } = await import('child_process');
|
|
426
|
+
for (const pm of ['pnpm', 'yarn', 'bun']) {
|
|
427
|
+
try {
|
|
428
|
+
execSync(`${pm} --version`, { stdio: 'pipe', timeout: 5000 });
|
|
429
|
+
return pm;
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return 'npm';
|
|
436
|
+
}
|
|
437
|
+
async function installDependencies(targetDir) {
|
|
438
|
+
const { execSync } = await import('child_process');
|
|
439
|
+
const { existsSync } = await import('fs');
|
|
440
|
+
const path = await import('path');
|
|
441
|
+
const pm = await detectPackageManager(targetDir);
|
|
442
|
+
const installCmd = pm === 'npm' ? 'npm install' : `${pm} install`;
|
|
443
|
+
// For pnpm inside a workspace, add --ignore-workspace flag
|
|
444
|
+
const cwd = process.cwd();
|
|
445
|
+
let finalCmd = installCmd;
|
|
446
|
+
if (pm === 'pnpm' && cwd !== targetDir) {
|
|
447
|
+
// If CWD has pnpm-workspace.yaml, the new project is inside a monorepo
|
|
448
|
+
if (existsSync(path.join(cwd, 'pnpm-workspace.yaml'))) {
|
|
449
|
+
finalCmd = 'pnpm install --ignore-workspace';
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
console.log(chalk.dim(` Installing dependencies (${pm})...`));
|
|
453
|
+
try {
|
|
454
|
+
execSync(finalCmd, {
|
|
455
|
+
cwd: targetDir,
|
|
456
|
+
stdio: 'pipe',
|
|
457
|
+
timeout: 300000,
|
|
458
|
+
});
|
|
459
|
+
console.log(chalk.green(' ✓'), `Dependencies installed (${pm})`);
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
console.log(chalk.yellow(' ⚠'), `Dependency installation failed. Run \`${finalCmd}\` manually.`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async function verifySqliteNativeModule(targetDir) {
|
|
466
|
+
const { execSync } = await import('child_process');
|
|
467
|
+
const path = await import('path');
|
|
468
|
+
// Step 1: Check if native module loads
|
|
469
|
+
try {
|
|
470
|
+
execSync('node -e "require(\'better-sqlite3\')(\':memory:\').close()"', {
|
|
471
|
+
cwd: targetDir,
|
|
472
|
+
stdio: 'pipe',
|
|
473
|
+
timeout: 10000,
|
|
474
|
+
});
|
|
475
|
+
return; // OK, no fix needed
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
// Native module missing, proceed to auto-fix
|
|
479
|
+
}
|
|
480
|
+
// Step 2: Auto-rebuild from source
|
|
481
|
+
console.log(chalk.yellow(' ⚠ better-sqlite3 native binary not found, rebuilding from source...'));
|
|
482
|
+
// Find the actual better-sqlite3 package dir (pnpm uses .pnpm store)
|
|
483
|
+
let bs3Dir;
|
|
484
|
+
try {
|
|
485
|
+
const resolved = execSync('node -e "console.log(require.resolve(\'better-sqlite3/package.json\'))"', {
|
|
486
|
+
cwd: targetDir,
|
|
487
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
488
|
+
timeout: 10000,
|
|
489
|
+
}).toString().trim();
|
|
490
|
+
bs3Dir = path.dirname(resolved);
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
// Fallback: try direct node_modules path
|
|
494
|
+
const directPath = path.join(targetDir, 'node_modules', 'better-sqlite3');
|
|
495
|
+
const { existsSync } = await import('fs');
|
|
496
|
+
if (existsSync(directPath)) {
|
|
497
|
+
bs3Dir = directPath;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (bs3Dir) {
|
|
501
|
+
try {
|
|
502
|
+
execSync('npx --yes node-gyp rebuild', {
|
|
503
|
+
cwd: bs3Dir,
|
|
504
|
+
stdio: 'pipe',
|
|
505
|
+
timeout: 120000,
|
|
506
|
+
});
|
|
507
|
+
// Verify the fix worked
|
|
508
|
+
try {
|
|
509
|
+
execSync('node -e "require(\'better-sqlite3\')(\':memory:\').close()"', {
|
|
510
|
+
cwd: targetDir,
|
|
511
|
+
stdio: 'pipe',
|
|
512
|
+
timeout: 10000,
|
|
513
|
+
});
|
|
514
|
+
console.log(chalk.green(' ✓'), 'better-sqlite3 native module rebuilt successfully');
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
// Rebuild succeeded but still can't load — fall through to fallback
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
// Rebuild failed — fall through to fallback
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// Step 3: Fallback — switch to filesystem adapter
|
|
526
|
+
console.log(chalk.yellow(' ⚠ Could not rebuild better-sqlite3, switching to filesystem adapter'));
|
|
527
|
+
const configPath = path.join(targetDir, 'loom.config.ts');
|
|
528
|
+
const { readFile, writeFile } = await import('fs/promises');
|
|
529
|
+
try {
|
|
530
|
+
let config = await readFile(configPath, 'utf-8');
|
|
531
|
+
config = config.replace('defaultAdapter: \'sqlite\'', 'defaultAdapter: \'filesystem\'');
|
|
532
|
+
await writeFile(configPath, config, 'utf-8');
|
|
533
|
+
console.log(chalk.green(' ✓'), 'Switched defaultAdapter to "filesystem" in loom.config.ts');
|
|
534
|
+
}
|
|
535
|
+
catch {
|
|
536
|
+
console.log(chalk.dim(' Please manually change defaultAdapter to "filesystem" in loom.config.ts'));
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
//# sourceMappingURL=init.js.map
|