@kya-os/create-molti 0.1.0-canary.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.
Files changed (117) hide show
  1. package/dist/cli.d.ts +10 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +83 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/helpers/generate-identity.d.ts +22 -0
  6. package/dist/helpers/generate-identity.d.ts.map +1 -0
  7. package/dist/helpers/generate-identity.js +63 -0
  8. package/dist/helpers/generate-identity.js.map +1 -0
  9. package/dist/helpers/generate-molti-project.d.ts +31 -0
  10. package/dist/helpers/generate-molti-project.d.ts.map +1 -0
  11. package/dist/helpers/generate-molti-project.js +200 -0
  12. package/dist/helpers/generate-molti-project.js.map +1 -0
  13. package/dist/helpers/get-package-versions.d.ts +22 -0
  14. package/dist/helpers/get-package-versions.d.ts.map +1 -0
  15. package/dist/helpers/get-package-versions.js +60 -0
  16. package/dist/helpers/get-package-versions.js.map +1 -0
  17. package/dist/helpers/index.d.ts +12 -0
  18. package/dist/helpers/index.d.ts.map +1 -0
  19. package/dist/helpers/index.js +14 -0
  20. package/dist/helpers/index.js.map +1 -0
  21. package/dist/helpers/validate-name.d.ts +16 -0
  22. package/dist/helpers/validate-name.d.ts.map +1 -0
  23. package/dist/helpers/validate-name.js +31 -0
  24. package/dist/helpers/validate-name.js.map +1 -0
  25. package/dist/index.d.ts +12 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +16 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/templates/auth-module.d.ts +7 -0
  30. package/dist/templates/auth-module.d.ts.map +1 -0
  31. package/dist/templates/auth-module.js +68 -0
  32. package/dist/templates/auth-module.js.map +1 -0
  33. package/dist/templates/dockerfile.d.ts +8 -0
  34. package/dist/templates/dockerfile.d.ts.map +1 -0
  35. package/dist/templates/dockerfile.js +40 -0
  36. package/dist/templates/dockerfile.js.map +1 -0
  37. package/dist/templates/gateway.d.ts +8 -0
  38. package/dist/templates/gateway.d.ts.map +1 -0
  39. package/dist/templates/gateway.js +112 -0
  40. package/dist/templates/gateway.js.map +1 -0
  41. package/dist/templates/github-workflow.d.ts +7 -0
  42. package/dist/templates/github-workflow.d.ts.map +1 -0
  43. package/dist/templates/github-workflow.js +48 -0
  44. package/dist/templates/github-workflow.js.map +1 -0
  45. package/dist/templates/health-module.d.ts +7 -0
  46. package/dist/templates/health-module.d.ts.map +1 -0
  47. package/dist/templates/health-module.js +62 -0
  48. package/dist/templates/health-module.js.map +1 -0
  49. package/dist/templates/identity-json.d.ts +15 -0
  50. package/dist/templates/identity-json.d.ts.map +1 -0
  51. package/dist/templates/identity-json.js +18 -0
  52. package/dist/templates/identity-json.js.map +1 -0
  53. package/dist/templates/identity-module.d.ts +8 -0
  54. package/dist/templates/identity-module.d.ts.map +1 -0
  55. package/dist/templates/identity-module.js +89 -0
  56. package/dist/templates/identity-module.js.map +1 -0
  57. package/dist/templates/moltbot-config.d.ts +7 -0
  58. package/dist/templates/moltbot-config.d.ts.map +1 -0
  59. package/dist/templates/moltbot-config.js +19 -0
  60. package/dist/templates/moltbot-config.js.map +1 -0
  61. package/dist/templates/package-json.d.ts +13 -0
  62. package/dist/templates/package-json.d.ts.map +1 -0
  63. package/dist/templates/package-json.js +34 -0
  64. package/dist/templates/package-json.js.map +1 -0
  65. package/dist/templates/readme.d.ts +14 -0
  66. package/dist/templates/readme.d.ts.map +1 -0
  67. package/dist/templates/readme.js +92 -0
  68. package/dist/templates/readme.js.map +1 -0
  69. package/dist/templates/startup-script.d.ts +8 -0
  70. package/dist/templates/startup-script.d.ts.map +1 -0
  71. package/dist/templates/startup-script.js +54 -0
  72. package/dist/templates/startup-script.js.map +1 -0
  73. package/dist/templates/tsconfig-template.d.ts +7 -0
  74. package/dist/templates/tsconfig-template.d.ts.map +1 -0
  75. package/dist/templates/tsconfig-template.js +22 -0
  76. package/dist/templates/tsconfig-template.js.map +1 -0
  77. package/dist/templates/types-module.d.ts +7 -0
  78. package/dist/templates/types-module.d.ts.map +1 -0
  79. package/dist/templates/types-module.js +73 -0
  80. package/dist/templates/types-module.js.map +1 -0
  81. package/dist/templates/worker-index.d.ts +8 -0
  82. package/dist/templates/worker-index.d.ts.map +1 -0
  83. package/dist/templates/worker-index.js +99 -0
  84. package/dist/templates/worker-index.js.map +1 -0
  85. package/dist/templates/wrangler.d.ts +16 -0
  86. package/dist/templates/wrangler.d.ts.map +1 -0
  87. package/dist/templates/wrangler.js +77 -0
  88. package/dist/templates/wrangler.js.map +1 -0
  89. package/dist/types.d.ts +61 -0
  90. package/dist/types.d.ts.map +1 -0
  91. package/dist/types.js +8 -0
  92. package/dist/types.js.map +1 -0
  93. package/package.json +50 -0
  94. package/scripts/validate-dependencies.js +38 -0
  95. package/src/cli.ts +98 -0
  96. package/src/helpers/generate-identity.ts +90 -0
  97. package/src/helpers/generate-molti-project.ts +239 -0
  98. package/src/helpers/get-package-versions.ts +78 -0
  99. package/src/helpers/index.ts +24 -0
  100. package/src/helpers/validate-name.ts +42 -0
  101. package/src/index.ts +18 -0
  102. package/src/templates/auth-module.ts +68 -0
  103. package/src/templates/dockerfile.ts +40 -0
  104. package/src/templates/gateway.ts +112 -0
  105. package/src/templates/github-workflow.ts +48 -0
  106. package/src/templates/health-module.ts +62 -0
  107. package/src/templates/identity-json.ts +29 -0
  108. package/src/templates/identity-module.ts +89 -0
  109. package/src/templates/moltbot-config.ts +23 -0
  110. package/src/templates/package-json.ts +46 -0
  111. package/src/templates/readme.ts +101 -0
  112. package/src/templates/startup-script.ts +54 -0
  113. package/src/templates/tsconfig-template.ts +26 -0
  114. package/src/templates/types-module.ts +73 -0
  115. package/src/templates/worker-index.ts +99 -0
  116. package/src/templates/wrangler.ts +89 -0
  117. package/src/types.ts +64 -0
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@kya-os/create-molti",
3
+ "version": "0.1.0-canary.1",
4
+ "description": "Scaffold a Moltworker project with MCP-I identity",
5
+ "type": "module",
6
+ "main": "./dist/helpers/index.js",
7
+ "types": "./dist/helpers/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/helpers/index.d.ts",
11
+ "import": "./dist/helpers/index.js"
12
+ },
13
+ "./helpers": {
14
+ "types": "./dist/helpers/index.d.ts",
15
+ "import": "./dist/helpers/index.js"
16
+ }
17
+ },
18
+ "bin": {
19
+ "create-molti": "./dist/index.js"
20
+ },
21
+ "scripts": {
22
+ "build": "tsc && chmod +x dist/index.js",
23
+ "test": "vitest run",
24
+ "test:coverage": "vitest run --coverage",
25
+ "lint": "eslint .",
26
+ "clean": "rm -rf dist .turbo node_modules",
27
+ "prepublishOnly": "npm run build && node scripts/validate-dependencies.js"
28
+ },
29
+ "dependencies": {
30
+ "@kya-os/contracts": "^1.7.20",
31
+ "@kya-os/mcp-i-core": "^1.4.18",
32
+ "@kya-os/mcp-i-cloudflare": "^1.7.70",
33
+ "base-x": "^5.0.0",
34
+ "chalk": "^4.1.2",
35
+ "commander": "^12.1.0",
36
+ "fs-extra": "^11.2.0",
37
+ "validate-npm-package-name": "^5.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/fs-extra": "^11.0.4",
41
+ "@types/node": "^20.14.9",
42
+ "@types/validate-npm-package-name": "^3.0.0",
43
+ "@vitest/coverage-v8": "^4.0.5",
44
+ "typescript": "^5.5.3",
45
+ "vitest": "^4.0.5"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ }
50
+ }
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Pre-publish validation: ensures no workspace:* references
5
+ * exist in the published package.json.
6
+ */
7
+
8
+ import { readFileSync } from 'fs';
9
+ import { resolve, dirname } from 'path';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+ const packageJsonPath = resolve(__dirname, '../package.json');
15
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
16
+
17
+ let hasErrors = false;
18
+
19
+ function checkDeps(section, deps) {
20
+ if (!deps) return;
21
+ for (const [name, version] of Object.entries(deps)) {
22
+ if (typeof version === 'string' && version.startsWith('workspace:')) {
23
+ console.error(`ERROR: ${section}.${name} uses workspace reference: ${version}`);
24
+ hasErrors = true;
25
+ }
26
+ }
27
+ }
28
+
29
+ checkDeps('dependencies', packageJson.dependencies);
30
+ checkDeps('devDependencies', packageJson.devDependencies);
31
+ checkDeps('peerDependencies', packageJson.peerDependencies);
32
+
33
+ if (hasErrors) {
34
+ console.error('\nFailed: workspace references found. Replace with published versions before publishing.');
35
+ process.exit(1);
36
+ } else {
37
+ console.log('Validation passed: no workspace references found.');
38
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * CLI for @kya-os/create-molti
3
+ *
4
+ * Usage: npx @kya-os/create-molti my-agent
5
+ */
6
+
7
+ import { Command } from 'commander';
8
+ import chalk from 'chalk';
9
+ import fs from 'fs-extra';
10
+ import path from 'path';
11
+ import { generateMoltiProject } from './helpers/generate-molti-project.js';
12
+ import { validateProjectName } from './helpers/validate-name.js';
13
+
14
+ const program = new Command();
15
+
16
+ program
17
+ .name('create-molti')
18
+ .description('Scaffold a Moltworker project with MCP-I identity')
19
+ .version('0.1.0')
20
+ .argument('[directory]', 'Project directory name')
21
+ .option('--name <name>', 'Project name (defaults to directory name)')
22
+ .option('--agent-name <name>', 'Human-readable agent name')
23
+ .option('--description <desc>', 'Agent description')
24
+ .option('--project-id <id>', 'AgentShield project ID')
25
+ .option('--api-key <key>', 'AgentShield API key')
26
+ .option(
27
+ '--instance-type <type>',
28
+ 'Container instance type (lite, basic, standard-1..4)',
29
+ 'basic'
30
+ )
31
+ .option('--skip-identity', 'Skip identity generation')
32
+ .option('--skip-install', 'Skip dependency installation')
33
+ .action(async (directory: string | undefined, opts: Record<string, string | boolean | undefined>) => {
34
+ const projectDir = directory || '.';
35
+ const projectName = (opts.name as string) || path.basename(path.resolve(projectDir));
36
+
37
+ // Validate name
38
+ const validation = validateProjectName(projectName);
39
+ if (!validation.valid) {
40
+ console.error(chalk.red('Invalid project name:'));
41
+ validation.errors.forEach((err) => console.error(chalk.red(` - ${err}`)));
42
+ process.exit(1);
43
+ }
44
+
45
+ console.log(chalk.bold(`\nCreating Molti project: ${chalk.cyan(projectName)}\n`));
46
+
47
+ // Generate project files
48
+ const result = await generateMoltiProject({
49
+ projectName,
50
+ agentName: opts['agent-name'] as string | undefined,
51
+ agentDescription: opts.description as string | undefined,
52
+ agentShieldProjectId: opts['project-id'] as string | undefined,
53
+ agentShieldApiKey: opts['api-key'] as string | undefined,
54
+ instanceType: (opts['instance-type'] as 'lite' | 'basic' | 'standard-1') || 'basic',
55
+ skipIdentity: !!opts['skip-identity'],
56
+ });
57
+
58
+ // Write files to disk
59
+ const outputDir = path.resolve(projectDir);
60
+ await fs.ensureDir(outputDir);
61
+
62
+ for (const file of result.files) {
63
+ const filePath = path.join(outputDir, file.path);
64
+ await fs.ensureDir(path.dirname(filePath));
65
+ await fs.writeFile(filePath, file.content, 'utf-8');
66
+ }
67
+
68
+ // Make startup script executable
69
+ const startScript = path.join(outputDir, 'start-moltbot.sh');
70
+ if (await fs.pathExists(startScript)) {
71
+ await fs.chmod(startScript, 0o755);
72
+ }
73
+
74
+ // Print success
75
+ console.log(chalk.green('Project created successfully!\n'));
76
+ console.log(chalk.bold('Identity:'));
77
+ console.log(` DID: ${chalk.cyan(result.identity.did)}`);
78
+ console.log(` Public Key: ${result.identity.publicKey.slice(0, 20)}...`);
79
+ console.log();
80
+ console.log(chalk.bold('Secrets (add to .dev.vars or wrangler secret):'));
81
+ Object.keys(result.secrets).forEach((key) => {
82
+ console.log(` ${chalk.yellow(key)}`);
83
+ });
84
+ console.log();
85
+ console.log(chalk.bold('Next steps:'));
86
+ console.log(` cd ${projectDir}`);
87
+ console.log(' npm install');
88
+ console.log(' cp .dev.vars.example .dev.vars');
89
+ console.log(' # Edit .dev.vars with your keys');
90
+ console.log(' npm run dev');
91
+ console.log();
92
+ });
93
+
94
+ export { program };
95
+
96
+ export async function run(): Promise<void> {
97
+ await program.parseAsync(process.argv);
98
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Identity Generation for Molti Projects
3
+ *
4
+ * Reuses the same Ed25519 keypair generation and DID:key creation
5
+ * as create-mcpi-app, ensuring cryptographic compatibility across
6
+ * the KYA-OS ecosystem.
7
+ */
8
+
9
+ import { webcrypto } from 'crypto';
10
+ import baseX from 'base-x';
11
+ import type { MoltiIdentity } from '../types.js';
12
+
13
+ /**
14
+ * Web Crypto API key pair type
15
+ */
16
+ interface WebCryptoKeyPair {
17
+ privateKey: webcrypto.CryptoKey;
18
+ publicKey: webcrypto.CryptoKey;
19
+ }
20
+
21
+ /**
22
+ * Generate a new Ed25519 identity with DID:key
23
+ *
24
+ * Uses the same algorithm as create-mcpi-app's generateIdentity()
25
+ * and mcp-i-core's WebCryptoProvider to ensure compatibility.
26
+ *
27
+ * @returns MoltiIdentity with DID, public key, and private key
28
+ */
29
+ export async function generateIdentity(): Promise<MoltiIdentity> {
30
+ const keyPair = (await webcrypto.subtle.generateKey(
31
+ { name: 'Ed25519' },
32
+ true,
33
+ ['sign', 'verify']
34
+ )) as WebCryptoKeyPair;
35
+
36
+ const privateKeyPKCS8 = await webcrypto.subtle.exportKey('pkcs8', keyPair.privateKey);
37
+ const publicKeySPKI = await webcrypto.subtle.exportKey('spki', keyPair.publicKey);
38
+
39
+ const privateKeyRaw = extractEd25519PrivateKey(new Uint8Array(privateKeyPKCS8 as ArrayBuffer));
40
+ const publicKeyRaw = extractEd25519PublicKey(
41
+ Buffer.from(publicKeySPKI as ArrayBuffer).toString('base64')
42
+ );
43
+
44
+ const privateKey = Buffer.from(privateKeyRaw).toString('base64');
45
+ const publicKey = Buffer.from(publicKeyRaw).toString('base64');
46
+ const did = generateDIDFromPublicKeyBytes(publicKeyRaw);
47
+
48
+ return { did, publicKey, privateKey };
49
+ }
50
+
51
+ /**
52
+ * Generate a DID:key from raw Ed25519 public key bytes
53
+ *
54
+ * Format: did:key:z<multibase-base58btc(<multicodec-ed25519-pub><publicKey>)>
55
+ */
56
+ function generateDIDFromPublicKeyBytes(publicKeyBytes: Uint8Array): string {
57
+ const multicodecPrefix = new Uint8Array([0xed, 0x01]);
58
+
59
+ const multicodecKey = new Uint8Array(multicodecPrefix.length + publicKeyBytes.length);
60
+ multicodecKey.set(multicodecPrefix);
61
+ multicodecKey.set(publicKeyBytes, multicodecPrefix.length);
62
+
63
+ const base58 = baseX('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
64
+ const base58Encoded = base58.encode(multicodecKey);
65
+
66
+ return `did:key:z${base58Encoded}`;
67
+ }
68
+
69
+ /**
70
+ * Extract raw 32-byte Ed25519 private key from PKCS#8 format
71
+ */
72
+ function extractEd25519PrivateKey(pkcs8: Uint8Array): Uint8Array {
73
+ return pkcs8.slice(16, 48);
74
+ }
75
+
76
+ /**
77
+ * Extract raw 32-byte Ed25519 public key from SPKI format
78
+ */
79
+ function extractEd25519PublicKey(publicKey: string): Uint8Array {
80
+ const spkiBytes = Buffer.from(publicKey, 'base64');
81
+ return spkiBytes.slice(-32);
82
+ }
83
+
84
+ /**
85
+ * Validate that a string is a valid DID:key identifier
86
+ */
87
+ export function isValidDIDKey(did: string): boolean {
88
+ const didKeyRegex = /^did:key:z[1-9A-HJ-NP-Za-km-z]{47,}$/;
89
+ return didKeyRegex.test(did);
90
+ }
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Molti Project Generator
3
+ *
4
+ * Generates all files for a Moltworker project with MCP-I identity.
5
+ * Returns files in-memory for programmatic use (e.g., GitHub API commits).
6
+ */
7
+
8
+ import type {
9
+ MoltiProjectOptions,
10
+ MoltiGenerateResult,
11
+ MoltiIdentity,
12
+ GeneratedFile,
13
+ } from '../types.js';
14
+ import { generateIdentity } from './generate-identity.js';
15
+ import { getPackageVersions } from './get-package-versions.js';
16
+ import { generateDockerfile } from '../templates/dockerfile.js';
17
+ import { generateWranglerJsonc } from '../templates/wrangler.js';
18
+ import { generateWorkerIndex } from '../templates/worker-index.js';
19
+ import { generateTypesModule } from '../templates/types-module.js';
20
+ import { generateGatewayModule } from '../templates/gateway.js';
21
+ import { generateAuthModule } from '../templates/auth-module.js';
22
+ import { generateIdentityModule } from '../templates/identity-module.js';
23
+ import { generateHealthModule } from '../templates/health-module.js';
24
+ import { generateStartupScript } from '../templates/startup-script.js';
25
+ import { generateMoltbotConfig } from '../templates/moltbot-config.js';
26
+ import { generatePackageJson } from '../templates/package-json.js';
27
+ import { generateTsconfig } from '../templates/tsconfig-template.js';
28
+ import { generateDeployWorkflow } from '../templates/github-workflow.js';
29
+ import { generateIdentityJson } from '../templates/identity-json.js';
30
+ import { generateReadme } from '../templates/readme.js';
31
+
32
+ /**
33
+ * Generate a complete Moltworker project with MCP-I identity
34
+ *
35
+ * @param options - Project configuration options
36
+ * @returns Generated files, identity, and secrets
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const result = await generateMoltiProject({
41
+ * projectName: "my-agent",
42
+ * agentShieldProjectId: "proj-abc123",
43
+ * });
44
+ *
45
+ * // Commit files to GitHub
46
+ * for (const file of result.files) {
47
+ * await github.commitFile(file.path, file.content);
48
+ * }
49
+ *
50
+ * // Store secrets
51
+ * await github.addSecret("MCP_IDENTITY_PRIVATE_KEY", result.secrets.MCP_IDENTITY_PRIVATE_KEY);
52
+ * ```
53
+ */
54
+ export async function generateMoltiProject(
55
+ options: MoltiProjectOptions
56
+ ): Promise<MoltiGenerateResult> {
57
+ const {
58
+ projectName,
59
+ agentName,
60
+ agentDescription,
61
+ agentShieldProjectId,
62
+ agentShieldApiKey,
63
+ instanceType = 'basic',
64
+ skipIdentity = false,
65
+ } = options;
66
+
67
+ const versions = await getPackageVersions();
68
+
69
+ // Generate or mock identity
70
+ let identity: MoltiIdentity;
71
+ if (skipIdentity) {
72
+ identity = {
73
+ did: 'did:key:zTestMockIdentity',
74
+ publicKey: 'mock-public-key',
75
+ privateKey: 'mock-private-key',
76
+ };
77
+ } else {
78
+ identity = await generateIdentity();
79
+ }
80
+
81
+ const files: GeneratedFile[] = [];
82
+
83
+ // Root files
84
+ files.push({
85
+ path: 'Dockerfile',
86
+ content: generateDockerfile(),
87
+ encoding: 'utf-8',
88
+ });
89
+
90
+ files.push({
91
+ path: 'start-moltbot.sh',
92
+ content: generateStartupScript(),
93
+ encoding: 'utf-8',
94
+ });
95
+
96
+ files.push({
97
+ path: 'moltbot.json.template',
98
+ content: generateMoltbotConfig(),
99
+ encoding: 'utf-8',
100
+ });
101
+
102
+ files.push({
103
+ path: 'wrangler.jsonc',
104
+ content: generateWranglerJsonc({
105
+ projectName,
106
+ agentDid: identity.did,
107
+ publicKey: identity.publicKey,
108
+ agentShieldProjectId,
109
+ instanceType,
110
+ }),
111
+ encoding: 'utf-8',
112
+ });
113
+
114
+ files.push({
115
+ path: 'package.json',
116
+ content: generatePackageJson({ projectName, versions }),
117
+ encoding: 'utf-8',
118
+ });
119
+
120
+ files.push({
121
+ path: 'tsconfig.json',
122
+ content: generateTsconfig(),
123
+ encoding: 'utf-8',
124
+ });
125
+
126
+ // Source files
127
+ files.push({
128
+ path: 'src/index.ts',
129
+ content: generateWorkerIndex(projectName),
130
+ encoding: 'utf-8',
131
+ });
132
+
133
+ files.push({
134
+ path: 'src/types.ts',
135
+ content: generateTypesModule(),
136
+ encoding: 'utf-8',
137
+ });
138
+
139
+ files.push({
140
+ path: 'src/gateway.ts',
141
+ content: generateGatewayModule(),
142
+ encoding: 'utf-8',
143
+ });
144
+
145
+ files.push({
146
+ path: 'src/auth.ts',
147
+ content: generateAuthModule(),
148
+ encoding: 'utf-8',
149
+ });
150
+
151
+ files.push({
152
+ path: 'src/identity.ts',
153
+ content: generateIdentityModule(),
154
+ encoding: 'utf-8',
155
+ });
156
+
157
+ files.push({
158
+ path: 'src/routes/public.ts',
159
+ content: generateHealthModule(projectName),
160
+ encoding: 'utf-8',
161
+ });
162
+
163
+ // GitHub Actions
164
+ files.push({
165
+ path: '.github/workflows/deploy.yml',
166
+ content: generateDeployWorkflow(),
167
+ encoding: 'utf-8',
168
+ });
169
+
170
+ // Identity file (public, safe to commit)
171
+ files.push({
172
+ path: '.mcpi/identity.json',
173
+ content: generateIdentityJson({
174
+ did: identity.did,
175
+ publicKey: identity.publicKey,
176
+ agentName,
177
+ agentDescription,
178
+ }),
179
+ encoding: 'utf-8',
180
+ });
181
+
182
+ // .gitignore
183
+ files.push({
184
+ path: '.gitignore',
185
+ content: `node_modules/
186
+ dist/
187
+ .wrangler/
188
+ .dev.vars
189
+ *.log
190
+ .DS_Store
191
+ `,
192
+ encoding: 'utf-8',
193
+ });
194
+
195
+ // .dev.vars.example
196
+ files.push({
197
+ path: '.dev.vars.example',
198
+ content: `# Copy this to .dev.vars for local development
199
+ # DO NOT COMMIT .dev.vars — it contains secrets
200
+
201
+ # Agent Identity
202
+ MCP_IDENTITY_PRIVATE_KEY="your-private-key-here"
203
+
204
+ # AI Provider (at least one required)
205
+ ANTHROPIC_API_KEY="your-anthropic-key"
206
+ # OPENAI_API_KEY="your-openai-key"
207
+
208
+ # AgentShield Integration
209
+ AGENTSHIELD_API_KEY="sk_your_api_key_here"
210
+
211
+ # Development mode (skips CF Access auth)
212
+ DEV_MODE="true"
213
+ `,
214
+ encoding: 'utf-8',
215
+ });
216
+
217
+ // README
218
+ files.push({
219
+ path: 'README.md',
220
+ content: generateReadme({
221
+ projectName,
222
+ agentName,
223
+ agentDescription,
224
+ agentShieldProjectId,
225
+ }),
226
+ encoding: 'utf-8',
227
+ });
228
+
229
+ // Build secrets
230
+ const secrets: Record<string, string> = {
231
+ MCP_IDENTITY_PRIVATE_KEY: identity.privateKey,
232
+ };
233
+
234
+ if (agentShieldApiKey) {
235
+ secrets.AGENTSHIELD_API_KEY = agentShieldApiKey;
236
+ }
237
+
238
+ return { files, identity, secrets };
239
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Package Version Helper
3
+ *
4
+ * Reads dependency versions from this package's own package.json
5
+ * to ensure scaffolded projects use consistent, tested versions.
6
+ *
7
+ * Single source of truth for @kya-os/* package versions in Molti templates.
8
+ */
9
+
10
+ import fs from 'fs-extra';
11
+ import path from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+
17
+ export interface MoltiPackageVersions {
18
+ '@kya-os/mcp-i-core': string;
19
+ '@kya-os/mcp-i-cloudflare': string;
20
+ '@kya-os/contracts': string;
21
+ }
22
+
23
+ let cachedVersions: MoltiPackageVersions | null = null;
24
+
25
+ /**
26
+ * Normalize a version string for scaffolded templates
27
+ *
28
+ * - Stable versions: keep caret for minor updates
29
+ * - Canary versions: pin exact (no caret)
30
+ */
31
+ function normalizeVersion(version: string): string {
32
+ const cleanVersion = version.replace(/^[\^~]/, '');
33
+
34
+ if (cleanVersion.includes('canary')) {
35
+ return cleanVersion;
36
+ }
37
+
38
+ return `^${cleanVersion}`;
39
+ }
40
+
41
+ /**
42
+ * Get package versions from this package's package.json
43
+ */
44
+ export async function getPackageVersions(): Promise<MoltiPackageVersions> {
45
+ if (cachedVersions) {
46
+ return cachedVersions;
47
+ }
48
+
49
+ const packageJsonPath = path.resolve(__dirname, '../../package.json');
50
+
51
+ try {
52
+ const packageJson = await fs.readJson(packageJsonPath);
53
+ const deps = packageJson.dependencies || {};
54
+
55
+ cachedVersions = {
56
+ '@kya-os/mcp-i-core': normalizeVersion(deps['@kya-os/mcp-i-core'] || '^1.4.18'),
57
+ '@kya-os/mcp-i-cloudflare': normalizeVersion(
58
+ deps['@kya-os/mcp-i-cloudflare'] || '^1.7.70'
59
+ ),
60
+ '@kya-os/contracts': normalizeVersion(deps['@kya-os/contracts'] || '^1.7.20'),
61
+ };
62
+
63
+ return cachedVersions;
64
+ } catch {
65
+ return {
66
+ '@kya-os/mcp-i-core': '^1.4.18',
67
+ '@kya-os/mcp-i-cloudflare': '^1.7.70',
68
+ '@kya-os/contracts': '^1.7.20',
69
+ };
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Clear cached versions (for testing)
75
+ */
76
+ export function clearVersionCache(): void {
77
+ cachedVersions = null;
78
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Helper Exports for @kya-os/create-molti
3
+ *
4
+ * This module exports helpers for programmatic use.
5
+ * The primary export is generateMoltiProject for one-click deployment.
6
+ */
7
+
8
+ // Main generator
9
+ export { generateMoltiProject } from './generate-molti-project.js';
10
+
11
+ // Identity generation
12
+ export { generateIdentity, isValidDIDKey } from './generate-identity.js';
13
+
14
+ // Types
15
+ export type {
16
+ MoltiProjectOptions,
17
+ MoltiGenerateResult,
18
+ MoltiIdentity,
19
+ GeneratedFile,
20
+ } from '../types.js';
21
+
22
+ // Utilities
23
+ export { validateProjectName, type ValidationResult } from './validate-name.js';
24
+ export { getPackageVersions, clearVersionCache, type MoltiPackageVersions } from './get-package-versions.js';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Project Name Validation
3
+ *
4
+ * Validates project names for npm compatibility and Cloudflare Workers naming.
5
+ */
6
+
7
+ import validate from 'validate-npm-package-name';
8
+
9
+ export interface ValidationResult {
10
+ valid: boolean;
11
+ errors: string[];
12
+ }
13
+
14
+ /**
15
+ * Validate a project name for use as a Molti project
16
+ *
17
+ * Checks npm naming rules and Cloudflare Workers compatibility.
18
+ */
19
+ export function validateProjectName(name: string): ValidationResult {
20
+ const errors: string[] = [];
21
+
22
+ if (!name || name.trim().length === 0) {
23
+ return { valid: false, errors: ['Project name is required'] };
24
+ }
25
+
26
+ const trimmed = name.trim();
27
+
28
+ if (trimmed.length > 100) {
29
+ errors.push('Project name must be 100 characters or less');
30
+ }
31
+
32
+ const npmResult = validate(trimmed);
33
+ if (!npmResult.validForNewPackages) {
34
+ const npmErrors = [...(npmResult.errors || []), ...(npmResult.warnings || [])];
35
+ errors.push(...npmErrors);
36
+ }
37
+
38
+ return {
39
+ valid: errors.length === 0,
40
+ errors,
41
+ };
42
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @kya-os/create-molti
5
+ *
6
+ * Scaffold a Moltworker project with MCP-I identity.
7
+ *
8
+ * Usage:
9
+ * npx @kya-os/create-molti my-agent
10
+ * npx @kya-os/create-molti my-agent --instance-type standard-1
11
+ */
12
+
13
+ import { run } from './cli.js';
14
+
15
+ run().catch((error) => {
16
+ console.error('Error:', error.message);
17
+ process.exit(1);
18
+ });