create-cogitator-app 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -10
- package/dist/index.js +655 -273
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +42 -4
- package/dist/lib.js +554 -263
- package/dist/lib.js.map +1 -1
- package/package.json +23 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import * as p3 from "@clack/prompts";
|
|
5
|
-
import { readFileSync } from "fs";
|
|
5
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
6
|
+
import path5 from "path";
|
|
6
7
|
import pc2 from "picocolors";
|
|
7
8
|
|
|
8
9
|
// src/utils/logger.ts
|
|
@@ -34,74 +35,170 @@ function banner(version) {
|
|
|
34
35
|
|
|
35
36
|
// src/prompts.ts
|
|
36
37
|
import * as p from "@clack/prompts";
|
|
37
|
-
import
|
|
38
|
+
import path3 from "path";
|
|
38
39
|
|
|
39
40
|
// src/utils/providers.ts
|
|
40
41
|
var defaultModels = {
|
|
41
|
-
ollama: "qwen3:
|
|
42
|
+
ollama: "qwen3.5:9b",
|
|
42
43
|
openai: "gpt-6.1-sol",
|
|
43
44
|
anthropic: "claude-sonnet-5-5",
|
|
44
45
|
google: "gemini-3.8-flash"
|
|
45
46
|
};
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
` anthropic: { apiKey: process.env.ANTHROPIC_API_KEY! },`,
|
|
72
|
-
` },`,
|
|
73
|
-
` },`
|
|
74
|
-
].join("\n");
|
|
75
|
-
case "google":
|
|
76
|
-
return [
|
|
77
|
-
` llm: {`,
|
|
78
|
-
` defaultProvider: 'google',`,
|
|
79
|
-
` providers: {`,
|
|
80
|
-
` google: { apiKey: process.env.GOOGLE_API_KEY! },`,
|
|
81
|
-
` },`,
|
|
82
|
-
` },`
|
|
83
|
-
].join("\n");
|
|
47
|
+
function modelFor(options) {
|
|
48
|
+
return options.model?.trim() || defaultModels[options.provider];
|
|
49
|
+
}
|
|
50
|
+
function providerEnvKey(provider) {
|
|
51
|
+
const map = {
|
|
52
|
+
ollama: "OLLAMA_BASE_URL",
|
|
53
|
+
openai: "OPENAI_API_KEY",
|
|
54
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
55
|
+
google: "GOOGLE_API_KEY"
|
|
56
|
+
};
|
|
57
|
+
return map[provider];
|
|
58
|
+
}
|
|
59
|
+
function apiKeyExpression(envKey, access) {
|
|
60
|
+
return access === "requireEnv" ? `requireEnv('${envKey}')` : `process.env.${envKey} ?? ''`;
|
|
61
|
+
}
|
|
62
|
+
function providerConfig(provider, access = "process-env") {
|
|
63
|
+
if (provider === "ollama") {
|
|
64
|
+
return [
|
|
65
|
+
` llm: {`,
|
|
66
|
+
` defaultProvider: 'ollama',`,
|
|
67
|
+
` providers: {`,
|
|
68
|
+
` ollama: { baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434' },`,
|
|
69
|
+
` },`,
|
|
70
|
+
` },`
|
|
71
|
+
].join("\n");
|
|
84
72
|
}
|
|
73
|
+
return [
|
|
74
|
+
` llm: {`,
|
|
75
|
+
` defaultProvider: '${provider}',`,
|
|
76
|
+
` providers: {`,
|
|
77
|
+
` ${provider}: { apiKey: ${apiKeyExpression(providerEnvKey(provider), access)} },`,
|
|
78
|
+
` },`,
|
|
79
|
+
` },`
|
|
80
|
+
].join("\n");
|
|
85
81
|
}
|
|
86
82
|
|
|
87
83
|
// src/templates/versions.ts
|
|
84
|
+
import { existsSync, readFileSync } from "fs";
|
|
85
|
+
import path from "path";
|
|
86
|
+
import { fileURLToPath } from "url";
|
|
88
87
|
var ZOD_VERSION = "^4.6.5";
|
|
88
|
+
var IOREDIS_VERSION = "^6.0.0";
|
|
89
|
+
var OWN_NAME = "create-cogitator-app";
|
|
90
|
+
var SCOPE = "@cogitator-ai/";
|
|
91
|
+
function isRecord(value) {
|
|
92
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
93
|
+
}
|
|
94
|
+
function readManifest(file) {
|
|
95
|
+
const parsed = JSON.parse(readFileSync(file, "utf-8"));
|
|
96
|
+
if (!isRecord(parsed)) throw new Error(`${file} is not a package manifest`);
|
|
97
|
+
return parsed;
|
|
98
|
+
}
|
|
99
|
+
function findOwnRoot(from) {
|
|
100
|
+
let dir = from;
|
|
101
|
+
while (true) {
|
|
102
|
+
const manifest = path.join(dir, "package.json");
|
|
103
|
+
if (existsSync(manifest) && readManifest(manifest).name === OWN_NAME) return dir;
|
|
104
|
+
const parent = path.dirname(dir);
|
|
105
|
+
if (parent === dir) throw new Error(`Cannot find the ${OWN_NAME} package.json above ${from}`);
|
|
106
|
+
dir = parent;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function linkedVersion(packageRoot, name) {
|
|
110
|
+
const manifest = path.join(packageRoot, "node_modules", name, "package.json");
|
|
111
|
+
if (!existsSync(manifest)) {
|
|
112
|
+
throw new Error(`Cannot resolve the version of ${name}: ${manifest} does not exist`);
|
|
113
|
+
}
|
|
114
|
+
const version = readManifest(manifest).version;
|
|
115
|
+
if (typeof version !== "string") throw new Error(`${manifest} has no version`);
|
|
116
|
+
return version;
|
|
117
|
+
}
|
|
118
|
+
function readCogitatorVersions(packageRoot) {
|
|
119
|
+
const devDependencies = readManifest(path.join(packageRoot, "package.json")).devDependencies;
|
|
120
|
+
const versions = {};
|
|
121
|
+
if (!isRecord(devDependencies)) return versions;
|
|
122
|
+
for (const [name, range] of Object.entries(devDependencies)) {
|
|
123
|
+
if (!name.startsWith(SCOPE) || typeof range !== "string") continue;
|
|
124
|
+
versions[name] = range.startsWith("workspace:") ? `^${linkedVersion(packageRoot, name)}` : range;
|
|
125
|
+
}
|
|
126
|
+
return versions;
|
|
127
|
+
}
|
|
128
|
+
var resolved;
|
|
129
|
+
function cogitatorVersion(name) {
|
|
130
|
+
resolved ??= readCogitatorVersions(findOwnRoot(path.dirname(fileURLToPath(import.meta.url))));
|
|
131
|
+
const range = resolved[name];
|
|
132
|
+
if (!range) {
|
|
133
|
+
throw new Error(`${name} is not a devDependency of ${OWN_NAME}, so its version is unknown`);
|
|
134
|
+
}
|
|
135
|
+
return range;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/templates/shared.ts
|
|
139
|
+
function tsString(value) {
|
|
140
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\p{Zl}/gu, "\\u2028").replace(/\p{Zp}/gu, "\\u2029");
|
|
141
|
+
return `'${escaped}'`;
|
|
142
|
+
}
|
|
143
|
+
function needsApiKey(provider) {
|
|
144
|
+
return provider !== "ollama";
|
|
145
|
+
}
|
|
146
|
+
function envHelperFiles(provider) {
|
|
147
|
+
if (!needsApiKey(provider)) return [];
|
|
148
|
+
const content = [
|
|
149
|
+
`export function requireEnv(name: string): string {`,
|
|
150
|
+
` const value = process.env[name]`,
|
|
151
|
+
` if (!value) {`,
|
|
152
|
+
` throw new Error(\`\${name} is not set. Copy .env.example to .env and fill it in.\`)`,
|
|
153
|
+
` }`,
|
|
154
|
+
` return value`,
|
|
155
|
+
`}`,
|
|
156
|
+
``
|
|
157
|
+
].join("\n");
|
|
158
|
+
return [{ path: "src/env.ts", content }];
|
|
159
|
+
}
|
|
160
|
+
function envHelperImport(provider) {
|
|
161
|
+
return needsApiKey(provider) ? [`import { requireEnv } from './env.js'`] : [];
|
|
162
|
+
}
|
|
163
|
+
var RUN_MAIN = [
|
|
164
|
+
`main().catch((error: unknown) => {`,
|
|
165
|
+
` console.error(error)`,
|
|
166
|
+
` process.exitCode = 1`,
|
|
167
|
+
`})`,
|
|
168
|
+
``
|
|
169
|
+
];
|
|
170
|
+
function scriptTemplateScripts() {
|
|
171
|
+
return {
|
|
172
|
+
dev: "tsx watch --env-file-if-exists=.env src/index.ts",
|
|
173
|
+
start: "tsx --env-file-if-exists=.env src/index.ts",
|
|
174
|
+
build: "tsc",
|
|
175
|
+
typecheck: "tsc --noEmit"
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function scriptTemplateDevDependencies() {
|
|
179
|
+
return {
|
|
180
|
+
typescript: "^5.8.0",
|
|
181
|
+
tsx: "^4.19.0",
|
|
182
|
+
"@types/node": "^22.0.0"
|
|
183
|
+
};
|
|
184
|
+
}
|
|
89
185
|
|
|
90
186
|
// src/templates/basic.ts
|
|
91
187
|
var basicTemplate = {
|
|
92
188
|
files(options) {
|
|
93
|
-
const model =
|
|
189
|
+
const model = modelFor(options);
|
|
94
190
|
const indexTs = [
|
|
95
191
|
`import { Cogitator, Agent } from '@cogitator-ai/core'`,
|
|
96
192
|
`import { searchTool, summarizeTool } from './tools.js'`,
|
|
193
|
+
...envHelperImport(options.provider),
|
|
97
194
|
``,
|
|
98
195
|
`const cogitator = new Cogitator({`,
|
|
99
|
-
providerConfig(options.provider),
|
|
196
|
+
providerConfig(options.provider, "requireEnv"),
|
|
100
197
|
`})`,
|
|
101
198
|
``,
|
|
102
199
|
`const agent = new Agent({`,
|
|
103
|
-
` name:
|
|
104
|
-
` model:
|
|
200
|
+
` name: ${tsString(`${options.name}-agent`)},`,
|
|
201
|
+
` model: ${tsString(model)},`,
|
|
105
202
|
` instructions: 'You are a helpful AI assistant. Use your tools to help the user.',`,
|
|
106
203
|
` tools: [searchTool, summarizeTool],`,
|
|
107
204
|
` temperature: 0.7,`,
|
|
@@ -115,8 +212,7 @@ var basicTemplate = {
|
|
|
115
212
|
` console.log(result.output)`,
|
|
116
213
|
`}`,
|
|
117
214
|
``,
|
|
118
|
-
|
|
119
|
-
``
|
|
215
|
+
...RUN_MAIN
|
|
120
216
|
].join("\n");
|
|
121
217
|
const toolsTs = [
|
|
122
218
|
`import { tool } from '@cogitator-ai/core'`,
|
|
@@ -149,42 +245,36 @@ var basicTemplate = {
|
|
|
149
245
|
].join("\n");
|
|
150
246
|
return [
|
|
151
247
|
{ path: "src/index.ts", content: indexTs },
|
|
152
|
-
{ path: "src/tools.ts", content: toolsTs }
|
|
248
|
+
{ path: "src/tools.ts", content: toolsTs },
|
|
249
|
+
...envHelperFiles(options.provider)
|
|
153
250
|
];
|
|
154
251
|
},
|
|
155
252
|
dependencies() {
|
|
156
253
|
return {
|
|
157
|
-
"@cogitator-ai/core": "
|
|
254
|
+
"@cogitator-ai/core": cogitatorVersion("@cogitator-ai/core"),
|
|
158
255
|
zod: ZOD_VERSION
|
|
159
256
|
};
|
|
160
257
|
},
|
|
161
258
|
devDependencies() {
|
|
162
|
-
return
|
|
163
|
-
typescript: "^5.8.0",
|
|
164
|
-
tsx: "^4.19.0",
|
|
165
|
-
"@types/node": "^22.0.0"
|
|
166
|
-
};
|
|
259
|
+
return scriptTemplateDevDependencies();
|
|
167
260
|
},
|
|
168
261
|
scripts() {
|
|
169
|
-
return
|
|
170
|
-
dev: "tsx watch src/index.ts",
|
|
171
|
-
start: "tsx src/index.ts",
|
|
172
|
-
build: "tsc",
|
|
173
|
-
typecheck: "tsc --noEmit"
|
|
174
|
-
};
|
|
262
|
+
return scriptTemplateScripts();
|
|
175
263
|
}
|
|
176
264
|
};
|
|
177
265
|
|
|
178
266
|
// src/templates/memory.ts
|
|
179
267
|
var memoryTemplate = {
|
|
268
|
+
memoryAdapter: "redis",
|
|
180
269
|
files(options) {
|
|
181
|
-
const model =
|
|
270
|
+
const model = modelFor(options);
|
|
182
271
|
const indexTs = [
|
|
183
272
|
`import { Cogitator, Agent } from '@cogitator-ai/core'`,
|
|
184
273
|
`import { searchTool, noteTool } from './tools.js'`,
|
|
274
|
+
...envHelperImport(options.provider),
|
|
185
275
|
``,
|
|
186
276
|
`const cogitator = new Cogitator({`,
|
|
187
|
-
providerConfig(options.provider),
|
|
277
|
+
providerConfig(options.provider, "requireEnv"),
|
|
188
278
|
` memory: {`,
|
|
189
279
|
` adapter: 'redis',`,
|
|
190
280
|
` redis: { url: process.env.REDIS_URL || 'redis://localhost:6379' },`,
|
|
@@ -192,8 +282,8 @@ var memoryTemplate = {
|
|
|
192
282
|
`})`,
|
|
193
283
|
``,
|
|
194
284
|
`const agent = new Agent({`,
|
|
195
|
-
` name:
|
|
196
|
-
` model:
|
|
285
|
+
` name: ${tsString(`${options.name}-agent`)},`,
|
|
286
|
+
` model: ${tsString(model)},`,
|
|
197
287
|
` instructions: [`,
|
|
198
288
|
` 'You are an AI assistant with persistent memory.',`,
|
|
199
289
|
` 'You remember past conversations and user preferences.',`,
|
|
@@ -206,25 +296,26 @@ var memoryTemplate = {
|
|
|
206
296
|
`async function main() {`,
|
|
207
297
|
` const threadId = 'demo-thread'`,
|
|
208
298
|
``,
|
|
209
|
-
`
|
|
210
|
-
`
|
|
211
|
-
`
|
|
212
|
-
`
|
|
213
|
-
`
|
|
214
|
-
`
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
`
|
|
218
|
-
`
|
|
219
|
-
`
|
|
220
|
-
`
|
|
221
|
-
`
|
|
222
|
-
|
|
223
|
-
`
|
|
299
|
+
` try {`,
|
|
300
|
+
` const result1 = await cogitator.run(agent, {`,
|
|
301
|
+
` input: 'My name is Alex and I prefer TypeScript.',`,
|
|
302
|
+
` threadId,`,
|
|
303
|
+
` useMemory: true,`,
|
|
304
|
+
` })`,
|
|
305
|
+
` console.log('Response 1:', result1.output)`,
|
|
306
|
+
``,
|
|
307
|
+
` const result2 = await cogitator.run(agent, {`,
|
|
308
|
+
` input: 'What is my name and preferred language?',`,
|
|
309
|
+
` threadId,`,
|
|
310
|
+
` useMemory: true,`,
|
|
311
|
+
` })`,
|
|
312
|
+
` console.log('Response 2:', result2.output)`,
|
|
313
|
+
` } finally {`,
|
|
314
|
+
` await cogitator.close()`,
|
|
315
|
+
` }`,
|
|
224
316
|
`}`,
|
|
225
317
|
``,
|
|
226
|
-
|
|
227
|
-
``
|
|
318
|
+
...RUN_MAIN
|
|
228
319
|
].join("\n");
|
|
229
320
|
const toolsTs = [
|
|
230
321
|
`import { tool } from '@cogitator-ai/core'`,
|
|
@@ -256,45 +347,38 @@ var memoryTemplate = {
|
|
|
256
347
|
].join("\n");
|
|
257
348
|
return [
|
|
258
349
|
{ path: "src/index.ts", content: indexTs },
|
|
259
|
-
{ path: "src/tools.ts", content: toolsTs }
|
|
350
|
+
{ path: "src/tools.ts", content: toolsTs },
|
|
351
|
+
...envHelperFiles(options.provider)
|
|
260
352
|
];
|
|
261
353
|
},
|
|
262
354
|
dependencies() {
|
|
263
355
|
return {
|
|
264
|
-
"@cogitator-ai/core": "
|
|
265
|
-
"@cogitator-ai/memory": "
|
|
266
|
-
"@cogitator-ai/redis": "
|
|
356
|
+
"@cogitator-ai/core": cogitatorVersion("@cogitator-ai/core"),
|
|
357
|
+
"@cogitator-ai/memory": cogitatorVersion("@cogitator-ai/memory"),
|
|
358
|
+
"@cogitator-ai/redis": cogitatorVersion("@cogitator-ai/redis"),
|
|
359
|
+
ioredis: IOREDIS_VERSION,
|
|
267
360
|
zod: ZOD_VERSION
|
|
268
361
|
};
|
|
269
362
|
},
|
|
270
363
|
devDependencies() {
|
|
271
|
-
return
|
|
272
|
-
typescript: "^5.8.0",
|
|
273
|
-
tsx: "^4.19.0",
|
|
274
|
-
"@types/node": "^22.0.0"
|
|
275
|
-
};
|
|
364
|
+
return scriptTemplateDevDependencies();
|
|
276
365
|
},
|
|
277
366
|
scripts() {
|
|
278
|
-
return
|
|
279
|
-
dev: "tsx watch src/index.ts",
|
|
280
|
-
start: "tsx src/index.ts",
|
|
281
|
-
build: "tsc",
|
|
282
|
-
typecheck: "tsc --noEmit"
|
|
283
|
-
};
|
|
367
|
+
return scriptTemplateScripts();
|
|
284
368
|
}
|
|
285
369
|
};
|
|
286
370
|
|
|
287
371
|
// src/templates/swarm.ts
|
|
288
372
|
var swarmTemplate = {
|
|
289
373
|
files(options) {
|
|
290
|
-
const model =
|
|
374
|
+
const model = modelFor(options);
|
|
291
375
|
const researcherTs = [
|
|
292
376
|
`import { Agent } from '@cogitator-ai/core'`,
|
|
293
377
|
`import { searchTool } from '../tools.js'`,
|
|
294
378
|
``,
|
|
295
379
|
`export const researcher = new Agent({`,
|
|
296
380
|
` name: 'researcher',`,
|
|
297
|
-
` model:
|
|
381
|
+
` model: ${tsString(model)},`,
|
|
298
382
|
` instructions: [`,
|
|
299
383
|
` 'You are a research specialist.',`,
|
|
300
384
|
` 'Use the search tool to find information.',`,
|
|
@@ -310,7 +394,7 @@ var swarmTemplate = {
|
|
|
310
394
|
``,
|
|
311
395
|
`export const writer = new Agent({`,
|
|
312
396
|
` name: 'writer',`,
|
|
313
|
-
` model:
|
|
397
|
+
` model: ${tsString(model)},`,
|
|
314
398
|
` instructions: [`,
|
|
315
399
|
` 'You are a skilled content writer.',`,
|
|
316
400
|
` 'Take research findings and craft well-structured, engaging content.',`,
|
|
@@ -325,7 +409,7 @@ var swarmTemplate = {
|
|
|
325
409
|
``,
|
|
326
410
|
`export const reviewer = new Agent({`,
|
|
327
411
|
` name: 'reviewer',`,
|
|
328
|
-
` model:
|
|
412
|
+
` model: ${tsString(model)},`,
|
|
329
413
|
` instructions: [`,
|
|
330
414
|
` 'You are a meticulous content reviewer.',`,
|
|
331
415
|
` 'Check for accuracy, grammar, and overall quality.',`,
|
|
@@ -357,13 +441,14 @@ var swarmTemplate = {
|
|
|
357
441
|
`import { researcher } from './agents/researcher.js'`,
|
|
358
442
|
`import { writer } from './agents/writer.js'`,
|
|
359
443
|
`import { reviewer } from './agents/reviewer.js'`,
|
|
444
|
+
...envHelperImport(options.provider),
|
|
360
445
|
``,
|
|
361
446
|
`const cogitator = new Cogitator({`,
|
|
362
|
-
providerConfig(options.provider),
|
|
447
|
+
providerConfig(options.provider, "requireEnv"),
|
|
363
448
|
`})`,
|
|
364
449
|
``,
|
|
365
450
|
`const team = new Swarm(cogitator, {`,
|
|
366
|
-
` name:
|
|
451
|
+
` name: ${tsString(`${options.name}-team`)},`,
|
|
367
452
|
` strategy: 'hierarchical',`,
|
|
368
453
|
` supervisor: reviewer,`,
|
|
369
454
|
` workers: [researcher, writer],`,
|
|
@@ -380,75 +465,68 @@ var swarmTemplate = {
|
|
|
380
465
|
`async function main() {`,
|
|
381
466
|
` console.log('Starting swarm...')`,
|
|
382
467
|
``,
|
|
383
|
-
`
|
|
384
|
-
`
|
|
385
|
-
`
|
|
468
|
+
` try {`,
|
|
469
|
+
` const result = await team.run({`,
|
|
470
|
+
` input: 'Write a comprehensive article about AI agent orchestration.',`,
|
|
471
|
+
` })`,
|
|
386
472
|
``,
|
|
387
|
-
`
|
|
388
|
-
`
|
|
389
|
-
|
|
390
|
-
`
|
|
473
|
+
` console.log('Result:', result.output)`,
|
|
474
|
+
` console.log('Strategy:', team.strategyType)`,
|
|
475
|
+
` } finally {`,
|
|
476
|
+
` await team.close()`,
|
|
477
|
+
` }`,
|
|
391
478
|
`}`,
|
|
392
479
|
``,
|
|
393
|
-
|
|
394
|
-
``
|
|
480
|
+
...RUN_MAIN
|
|
395
481
|
].join("\n");
|
|
396
482
|
return [
|
|
397
483
|
{ path: "src/index.ts", content: indexTs },
|
|
398
484
|
{ path: "src/tools.ts", content: toolsTs },
|
|
399
485
|
{ path: "src/agents/researcher.ts", content: researcherTs },
|
|
400
486
|
{ path: "src/agents/writer.ts", content: writerTs },
|
|
401
|
-
{ path: "src/agents/reviewer.ts", content: reviewerTs }
|
|
487
|
+
{ path: "src/agents/reviewer.ts", content: reviewerTs },
|
|
488
|
+
...envHelperFiles(options.provider)
|
|
402
489
|
];
|
|
403
490
|
},
|
|
404
491
|
dependencies() {
|
|
405
492
|
return {
|
|
406
|
-
"@cogitator-ai/core": "
|
|
407
|
-
"@cogitator-ai/swarms": "
|
|
493
|
+
"@cogitator-ai/core": cogitatorVersion("@cogitator-ai/core"),
|
|
494
|
+
"@cogitator-ai/swarms": cogitatorVersion("@cogitator-ai/swarms"),
|
|
408
495
|
zod: ZOD_VERSION
|
|
409
496
|
};
|
|
410
497
|
},
|
|
411
498
|
devDependencies() {
|
|
412
|
-
return
|
|
413
|
-
typescript: "^5.8.0",
|
|
414
|
-
tsx: "^4.19.0",
|
|
415
|
-
"@types/node": "^22.0.0"
|
|
416
|
-
};
|
|
499
|
+
return scriptTemplateDevDependencies();
|
|
417
500
|
},
|
|
418
501
|
scripts() {
|
|
419
|
-
return
|
|
420
|
-
dev: "tsx watch src/index.ts",
|
|
421
|
-
start: "tsx src/index.ts",
|
|
422
|
-
build: "tsc",
|
|
423
|
-
typecheck: "tsc --noEmit"
|
|
424
|
-
};
|
|
502
|
+
return scriptTemplateScripts();
|
|
425
503
|
}
|
|
426
504
|
};
|
|
427
505
|
|
|
428
506
|
// src/templates/workflow.ts
|
|
429
507
|
var workflowTemplate = {
|
|
430
508
|
files(options) {
|
|
431
|
-
const model =
|
|
509
|
+
const model = modelFor(options);
|
|
432
510
|
const agentsTs = [
|
|
433
511
|
`import { Agent } from '@cogitator-ai/core'`,
|
|
434
512
|
``,
|
|
435
513
|
`export const analyzer = new Agent({`,
|
|
436
514
|
` name: 'analyzer',`,
|
|
437
|
-
` model:
|
|
515
|
+
` model: ${tsString(model)},`,
|
|
438
516
|
` instructions: 'You analyze input data and extract key information. Be thorough and structured.',`,
|
|
439
517
|
` temperature: 0.3,`,
|
|
440
518
|
`})`,
|
|
441
519
|
``,
|
|
442
520
|
`export const processor = new Agent({`,
|
|
443
521
|
` name: 'processor',`,
|
|
444
|
-
` model:
|
|
522
|
+
` model: ${tsString(model)},`,
|
|
445
523
|
` instructions: 'You process analyzed data and transform it into actionable insights.',`,
|
|
446
524
|
` temperature: 0.5,`,
|
|
447
525
|
`})`,
|
|
448
526
|
``,
|
|
449
527
|
`export const formatter = new Agent({`,
|
|
450
528
|
` name: 'formatter',`,
|
|
451
|
-
` model:
|
|
529
|
+
` model: ${tsString(model)},`,
|
|
452
530
|
` instructions: 'You format processed data into a clean, readable report.',`,
|
|
453
531
|
` temperature: 0.7,`,
|
|
454
532
|
`})`,
|
|
@@ -463,9 +541,10 @@ var workflowTemplate = {
|
|
|
463
541
|
` functionNode,`,
|
|
464
542
|
`} from '@cogitator-ai/workflows'`,
|
|
465
543
|
`import { analyzer, processor, formatter } from './agents.js'`,
|
|
544
|
+
...envHelperImport(options.provider),
|
|
466
545
|
``,
|
|
467
546
|
`const cogitator = new Cogitator({`,
|
|
468
|
-
providerConfig(options.provider),
|
|
547
|
+
providerConfig(options.provider, "requireEnv"),
|
|
469
548
|
`})`,
|
|
470
549
|
``,
|
|
471
550
|
`type WorkflowState = {`,
|
|
@@ -495,7 +574,7 @@ var workflowTemplate = {
|
|
|
495
574
|
` async (state) => state.report,`,
|
|
496
575
|
`)`,
|
|
497
576
|
``,
|
|
498
|
-
`const workflow = new WorkflowBuilder<WorkflowState>(
|
|
577
|
+
`const workflow = new WorkflowBuilder<WorkflowState>(${tsString(`${options.name}-workflow`)})`,
|
|
499
578
|
` .initialState({ input: '' })`,
|
|
500
579
|
` .entryPoint(analyzeNode.name)`,
|
|
501
580
|
` .addNode(analyzeNode.name, analyzeNode.fn)`,
|
|
@@ -513,48 +592,45 @@ var workflowTemplate = {
|
|
|
513
592
|
` input: 'Analyze the current state of AI agent frameworks and their adoption in enterprise.',`,
|
|
514
593
|
` })`,
|
|
515
594
|
``,
|
|
516
|
-
`
|
|
517
|
-
|
|
595
|
+
` if (result.error) throw result.error`,
|
|
596
|
+
``,
|
|
597
|
+
` console.log(\`Workflow completed in \${result.duration}ms\`)`,
|
|
598
|
+
` console.log('Report:', result.state.report)`,
|
|
599
|
+
` console.log('Nodes:', JSON.stringify(Object.fromEntries(result.nodeResults), null, 2))`,
|
|
518
600
|
`}`,
|
|
519
601
|
``,
|
|
520
|
-
|
|
521
|
-
``
|
|
602
|
+
...RUN_MAIN
|
|
522
603
|
].join("\n");
|
|
523
604
|
return [
|
|
524
605
|
{ path: "src/index.ts", content: indexTs },
|
|
525
|
-
{ path: "src/agents.ts", content: agentsTs }
|
|
606
|
+
{ path: "src/agents.ts", content: agentsTs },
|
|
607
|
+
...envHelperFiles(options.provider)
|
|
526
608
|
];
|
|
527
609
|
},
|
|
528
610
|
dependencies() {
|
|
529
611
|
return {
|
|
530
|
-
"@cogitator-ai/core": "
|
|
531
|
-
"@cogitator-ai/workflows": "
|
|
612
|
+
"@cogitator-ai/core": cogitatorVersion("@cogitator-ai/core"),
|
|
613
|
+
"@cogitator-ai/workflows": cogitatorVersion("@cogitator-ai/workflows"),
|
|
532
614
|
zod: ZOD_VERSION
|
|
533
615
|
};
|
|
534
616
|
},
|
|
535
617
|
devDependencies() {
|
|
536
|
-
return
|
|
537
|
-
typescript: "^5.8.0",
|
|
538
|
-
tsx: "^4.19.0",
|
|
539
|
-
"@types/node": "^22.0.0"
|
|
540
|
-
};
|
|
618
|
+
return scriptTemplateDevDependencies();
|
|
541
619
|
},
|
|
542
620
|
scripts() {
|
|
543
|
-
return
|
|
544
|
-
dev: "tsx watch src/index.ts",
|
|
545
|
-
start: "tsx src/index.ts",
|
|
546
|
-
build: "tsc",
|
|
547
|
-
typecheck: "tsc --noEmit"
|
|
548
|
-
};
|
|
621
|
+
return scriptTemplateScripts();
|
|
549
622
|
}
|
|
550
623
|
};
|
|
551
624
|
|
|
552
625
|
// src/templates/api-server.ts
|
|
553
626
|
var BASE_PATH = "/api";
|
|
627
|
+
var PUBLIC_PATHS = ["/health", "/ready", "/docs", "/openapi.json"];
|
|
554
628
|
var apiServerTemplate = {
|
|
629
|
+
memoryAdapter: "memory",
|
|
630
|
+
secrets: ["API_TOKEN"],
|
|
555
631
|
healthPath: `${BASE_PATH}/health`,
|
|
556
632
|
files(options) {
|
|
557
|
-
const model =
|
|
633
|
+
const model = modelFor(options);
|
|
558
634
|
const agentsTs = [
|
|
559
635
|
`import { Agent, tool } from '@cogitator-ai/core'`,
|
|
560
636
|
`import { z } from 'zod'`,
|
|
@@ -572,7 +648,7 @@ var apiServerTemplate = {
|
|
|
572
648
|
``,
|
|
573
649
|
`export const assistant = new Agent({`,
|
|
574
650
|
` name: 'assistant',`,
|
|
575
|
-
` model:
|
|
651
|
+
` model: ${tsString(model)},`,
|
|
576
652
|
` instructions: 'You are a helpful API assistant. Answer questions clearly and concisely.',`,
|
|
577
653
|
` tools: [searchTool],`,
|
|
578
654
|
` temperature: 0.7,`,
|
|
@@ -580,23 +656,45 @@ var apiServerTemplate = {
|
|
|
580
656
|
``,
|
|
581
657
|
`export const coder = new Agent({`,
|
|
582
658
|
` name: 'coder',`,
|
|
583
|
-
` model:
|
|
659
|
+
` model: ${tsString(model)},`,
|
|
584
660
|
` instructions: 'You are an expert programmer. Write clean, well-structured code.',`,
|
|
585
661
|
` temperature: 0.3,`,
|
|
586
662
|
`})`,
|
|
587
663
|
``
|
|
588
664
|
].join("\n");
|
|
589
665
|
const indexTs = [
|
|
590
|
-
`import
|
|
666
|
+
`import { timingSafeEqual } from 'node:crypto'`,
|
|
667
|
+
`import express, { type Request } from 'express'`,
|
|
591
668
|
`import { Cogitator } from '@cogitator-ai/core'`,
|
|
592
669
|
`import { CogitatorServer } from '@cogitator-ai/express'`,
|
|
593
670
|
`import { assistant, coder } from './agents.js'`,
|
|
671
|
+
...envHelperImport(options.provider),
|
|
672
|
+
``,
|
|
673
|
+
`const production = process.env.NODE_ENV === 'production'`,
|
|
674
|
+
`const port = Number(process.env.PORT ?? 3000)`,
|
|
675
|
+
`const host = process.env.HOST ?? (production ? '0.0.0.0' : '127.0.0.1')`,
|
|
676
|
+
`const apiToken = process.env.API_TOKEN`,
|
|
677
|
+
`const corsOrigin = process.env.CORS_ORIGIN`,
|
|
678
|
+
`const publicPaths = new Set([${PUBLIC_PATHS.map(tsString).join(", ")}])`,
|
|
679
|
+
``,
|
|
680
|
+
`function hasToken(header: string | undefined): boolean {`,
|
|
681
|
+
` if (!apiToken || !header) return false`,
|
|
682
|
+
` const expected = Buffer.from(\`Bearer \${apiToken}\`)`,
|
|
683
|
+
` const given = Buffer.from(header)`,
|
|
684
|
+
` return given.length === expected.length && timingSafeEqual(given, expected)`,
|
|
685
|
+
`}`,
|
|
686
|
+
``,
|
|
687
|
+
`function authenticate(req: Request) {`,
|
|
688
|
+
` if (!apiToken || publicPaths.has(req.path)) return undefined`,
|
|
689
|
+
` if (!hasToken(req.get('authorization'))) throw new Error('Unauthorized')`,
|
|
690
|
+
` return { userId: 'api-token' }`,
|
|
691
|
+
`}`,
|
|
594
692
|
``,
|
|
595
693
|
`const app = express()`,
|
|
596
|
-
`const port = process.env.PORT || 3000`,
|
|
597
694
|
``,
|
|
598
695
|
`const cogitator = new Cogitator({`,
|
|
599
|
-
providerConfig(options.provider),
|
|
696
|
+
providerConfig(options.provider, "requireEnv"),
|
|
697
|
+
` memory: { adapter: 'memory' },`,
|
|
600
698
|
`})`,
|
|
601
699
|
``,
|
|
602
700
|
`const server = new CogitatorServer({`,
|
|
@@ -606,62 +704,62 @@ var apiServerTemplate = {
|
|
|
606
704
|
` config: {`,
|
|
607
705
|
` basePath: '${BASE_PATH}',`,
|
|
608
706
|
` enableSwagger: true,`,
|
|
609
|
-
`
|
|
707
|
+
` auth: authenticate,`,
|
|
708
|
+
` ...(corsOrigin ? { cors: { origin: corsOrigin.split(',').map((o) => o.trim()) } } : {}),`,
|
|
610
709
|
` swagger: {`,
|
|
611
|
-
` title:
|
|
710
|
+
` title: ${tsString(`${options.name} API`)},`,
|
|
612
711
|
` version: '1.0.0',`,
|
|
613
712
|
` },`,
|
|
614
713
|
` },`,
|
|
615
714
|
`})`,
|
|
616
715
|
``,
|
|
617
716
|
`async function main() {`,
|
|
717
|
+
` if (production && !apiToken) {`,
|
|
718
|
+
` throw new Error('Set API_TOKEN in production: it guards every route except health and docs')`,
|
|
719
|
+
` }`,
|
|
720
|
+
``,
|
|
618
721
|
` await server.init()`,
|
|
619
722
|
``,
|
|
620
|
-
` app.listen(port, () => {`,
|
|
621
|
-
` console.log(\`Server running at http
|
|
622
|
-
` console.log(\`Swagger docs at http
|
|
723
|
+
` app.listen(port, host, () => {`,
|
|
724
|
+
` console.log(\`Server running at http://\${host}:\${port}\`)`,
|
|
725
|
+
` console.log(\`Swagger docs at http://\${host}:\${port}${BASE_PATH}/docs\`)`,
|
|
726
|
+
` if (!apiToken) console.log('API_TOKEN is not set, so the API is open to anyone who can reach it')`,
|
|
623
727
|
` })`,
|
|
624
728
|
`}`,
|
|
625
729
|
``,
|
|
626
|
-
|
|
627
|
-
``
|
|
730
|
+
...RUN_MAIN
|
|
628
731
|
].join("\n");
|
|
629
732
|
return [
|
|
630
733
|
{ path: "src/index.ts", content: indexTs },
|
|
631
|
-
{ path: "src/agents.ts", content: agentsTs }
|
|
734
|
+
{ path: "src/agents.ts", content: agentsTs },
|
|
735
|
+
...envHelperFiles(options.provider)
|
|
632
736
|
];
|
|
633
737
|
},
|
|
634
738
|
dependencies() {
|
|
635
739
|
return {
|
|
636
|
-
"@cogitator-ai/core": "
|
|
637
|
-
"@cogitator-ai/express": "
|
|
740
|
+
"@cogitator-ai/core": cogitatorVersion("@cogitator-ai/core"),
|
|
741
|
+
"@cogitator-ai/express": cogitatorVersion("@cogitator-ai/express"),
|
|
638
742
|
express: "^5.2.1",
|
|
639
743
|
zod: ZOD_VERSION
|
|
640
744
|
};
|
|
641
745
|
},
|
|
642
746
|
devDependencies() {
|
|
643
747
|
return {
|
|
644
|
-
|
|
645
|
-
tsx: "^4.19.0",
|
|
646
|
-
"@types/node": "^22.0.0",
|
|
748
|
+
...scriptTemplateDevDependencies(),
|
|
647
749
|
"@types/express": "^5.0.6"
|
|
648
750
|
};
|
|
649
751
|
},
|
|
650
752
|
scripts() {
|
|
651
|
-
return
|
|
652
|
-
dev: "tsx watch src/index.ts",
|
|
653
|
-
start: "tsx src/index.ts",
|
|
654
|
-
build: "tsc",
|
|
655
|
-
typecheck: "tsc --noEmit"
|
|
656
|
-
};
|
|
753
|
+
return scriptTemplateScripts();
|
|
657
754
|
}
|
|
658
755
|
};
|
|
659
756
|
|
|
660
757
|
// src/templates/nextjs.ts
|
|
661
758
|
var nextjsTemplate = {
|
|
759
|
+
memoryAdapter: "memory",
|
|
662
760
|
healthPath: "/api/health",
|
|
663
761
|
files(options) {
|
|
664
|
-
const model =
|
|
762
|
+
const model = modelFor(options);
|
|
665
763
|
const agentTs = [
|
|
666
764
|
`import { Cogitator, Agent, tool } from '@cogitator-ai/core'`,
|
|
667
765
|
`import { z } from 'zod'`,
|
|
@@ -679,11 +777,12 @@ var nextjsTemplate = {
|
|
|
679
777
|
``,
|
|
680
778
|
`export const cogitator = new Cogitator({`,
|
|
681
779
|
providerConfig(options.provider),
|
|
780
|
+
` memory: { adapter: 'memory' },`,
|
|
682
781
|
`})`,
|
|
683
782
|
``,
|
|
684
783
|
`export const agent = new Agent({`,
|
|
685
|
-
` name:
|
|
686
|
-
` model:
|
|
784
|
+
` name: ${tsString(`${options.name}-chat`)},`,
|
|
785
|
+
` model: ${tsString(model)},`,
|
|
687
786
|
` instructions: 'You are a helpful AI chat assistant. Be conversational and helpful.',`,
|
|
688
787
|
` tools: [searchTool],`,
|
|
689
788
|
` temperature: 0.7,`,
|
|
@@ -710,7 +809,7 @@ var nextjsTemplate = {
|
|
|
710
809
|
`import { useState, useRef, useEffect } from 'react'`,
|
|
711
810
|
``,
|
|
712
811
|
`export default function Home() {`,
|
|
713
|
-
` const { messages, input, setInput, send, isLoading } = useCogitatorChat({`,
|
|
812
|
+
` const { messages, input, setInput, send, stop, isLoading, error } = useCogitatorChat({`,
|
|
714
813
|
` api: '/api/chat',`,
|
|
715
814
|
` })`,
|
|
716
815
|
` const messagesEndRef = useRef<HTMLDivElement>(null)`,
|
|
@@ -730,27 +829,29 @@ var nextjsTemplate = {
|
|
|
730
829
|
` <main className="flex min-h-screen flex-col items-center p-4 bg-gray-950 text-white">`,
|
|
731
830
|
` <div className="w-full max-w-2xl flex flex-col h-screen">`,
|
|
732
831
|
` <header className="py-6 text-center">`,
|
|
733
|
-
` <h1 className="text-2xl font-bold"
|
|
832
|
+
` <h1 className="text-2xl font-bold">{${tsString(options.name)}}</h1>`,
|
|
734
833
|
` <p className="text-gray-400 text-sm mt-1">Powered by Cogitator</p>`,
|
|
735
834
|
` </header>`,
|
|
736
835
|
``,
|
|
737
836
|
` <div className="flex-1 overflow-y-auto space-y-4 pb-4">`,
|
|
738
|
-
` {messages
|
|
739
|
-
`
|
|
740
|
-
`
|
|
741
|
-
` className={\`flex \${msg.role === 'user' ? 'justify-end' : 'justify-start'}\`}`,
|
|
742
|
-
` >`,
|
|
837
|
+
` {messages`,
|
|
838
|
+
` .filter((msg) => msg.content)`,
|
|
839
|
+
` .map((msg) => (`,
|
|
743
840
|
` <div`,
|
|
744
|
-
`
|
|
745
|
-
`
|
|
746
|
-
` ? 'bg-blue-600 text-white'`,
|
|
747
|
-
` : 'bg-gray-800 text-gray-100'`,
|
|
748
|
-
` }\`}`,
|
|
841
|
+
` key={msg.id}`,
|
|
842
|
+
` className={\`flex \${msg.role === 'user' ? 'justify-end' : 'justify-start'}\`}`,
|
|
749
843
|
` >`,
|
|
750
|
-
`
|
|
844
|
+
` <div`,
|
|
845
|
+
` className={\`max-w-[80%] rounded-2xl px-4 py-2 \${`,
|
|
846
|
+
` msg.role === 'user'`,
|
|
847
|
+
` ? 'bg-blue-600 text-white'`,
|
|
848
|
+
` : 'bg-gray-800 text-gray-100'`,
|
|
849
|
+
` }\`}`,
|
|
850
|
+
` >`,
|
|
851
|
+
` {msg.content}`,
|
|
852
|
+
` </div>`,
|
|
751
853
|
` </div>`,
|
|
752
|
-
`
|
|
753
|
-
` ))}`,
|
|
854
|
+
` ))}`,
|
|
754
855
|
` {isLoading && (`,
|
|
755
856
|
` <div className="flex justify-start">`,
|
|
756
857
|
` <div className="bg-gray-800 rounded-2xl px-4 py-2 text-gray-400">`,
|
|
@@ -758,6 +859,11 @@ var nextjsTemplate = {
|
|
|
758
859
|
` </div>`,
|
|
759
860
|
` </div>`,
|
|
760
861
|
` )}`,
|
|
862
|
+
` {error && (`,
|
|
863
|
+
` <div className="rounded-xl border border-red-800 bg-red-950 px-4 py-2 text-sm text-red-200">`,
|
|
864
|
+
` {error.message}`,
|
|
865
|
+
` </div>`,
|
|
866
|
+
` )}`,
|
|
761
867
|
` <div ref={messagesEndRef} />`,
|
|
762
868
|
` </div>`,
|
|
763
869
|
``,
|
|
@@ -768,13 +874,23 @@ var nextjsTemplate = {
|
|
|
768
874
|
` placeholder="Type a message..."`,
|
|
769
875
|
` className="flex-1 bg-gray-800 border border-gray-700 rounded-xl px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:border-blue-500"`,
|
|
770
876
|
` />`,
|
|
771
|
-
`
|
|
772
|
-
`
|
|
773
|
-
`
|
|
774
|
-
`
|
|
775
|
-
`
|
|
776
|
-
`
|
|
777
|
-
`
|
|
877
|
+
` {isLoading ? (`,
|
|
878
|
+
` <button`,
|
|
879
|
+
` type="button"`,
|
|
880
|
+
` onClick={stop}`,
|
|
881
|
+
` className="bg-gray-700 hover:bg-gray-600 text-white rounded-xl px-6 py-3 font-medium transition-colors"`,
|
|
882
|
+
` >`,
|
|
883
|
+
` Stop`,
|
|
884
|
+
` </button>`,
|
|
885
|
+
` ) : (`,
|
|
886
|
+
` <button`,
|
|
887
|
+
` type="submit"`,
|
|
888
|
+
` disabled={!input.trim()}`,
|
|
889
|
+
` className="bg-blue-600 hover:bg-blue-500 disabled:opacity-50 disabled:cursor-not-allowed text-white rounded-xl px-6 py-3 font-medium transition-colors"`,
|
|
890
|
+
` >`,
|
|
891
|
+
` Send`,
|
|
892
|
+
` </button>`,
|
|
893
|
+
` )}`,
|
|
778
894
|
` </form>`,
|
|
779
895
|
` </div>`,
|
|
780
896
|
` </main>`,
|
|
@@ -787,7 +903,7 @@ var nextjsTemplate = {
|
|
|
787
903
|
`import './globals.css'`,
|
|
788
904
|
``,
|
|
789
905
|
`export const metadata: Metadata = {`,
|
|
790
|
-
` title:
|
|
906
|
+
` title: ${tsString(options.name)},`,
|
|
791
907
|
` description: 'AI chat app powered by Cogitator',`,
|
|
792
908
|
`}`,
|
|
793
909
|
``,
|
|
@@ -881,8 +997,8 @@ var nextjsTemplate = {
|
|
|
881
997
|
},
|
|
882
998
|
dependencies() {
|
|
883
999
|
return {
|
|
884
|
-
"@cogitator-ai/core": "
|
|
885
|
-
"@cogitator-ai/next": "
|
|
1000
|
+
"@cogitator-ai/core": cogitatorVersion("@cogitator-ai/core"),
|
|
1001
|
+
"@cogitator-ai/next": cogitatorVersion("@cogitator-ai/next"),
|
|
886
1002
|
next: "^15.0.0",
|
|
887
1003
|
react: "^19.0.0",
|
|
888
1004
|
"react-dom": "^19.0.0",
|
|
@@ -905,7 +1021,7 @@ var nextjsTemplate = {
|
|
|
905
1021
|
dev: "next dev",
|
|
906
1022
|
build: "next build",
|
|
907
1023
|
start: "next start",
|
|
908
|
-
|
|
1024
|
+
typecheck: "tsc --noEmit"
|
|
909
1025
|
};
|
|
910
1026
|
}
|
|
911
1027
|
};
|
|
@@ -946,12 +1062,8 @@ var templateChoices = [
|
|
|
946
1062
|
// src/utils/package-manager.ts
|
|
947
1063
|
import { execSync } from "child_process";
|
|
948
1064
|
function detectPackageManager() {
|
|
949
|
-
const
|
|
950
|
-
if (
|
|
951
|
-
if (ua.startsWith("pnpm")) return "pnpm";
|
|
952
|
-
if (ua.startsWith("yarn")) return "yarn";
|
|
953
|
-
if (ua.startsWith("bun")) return "bun";
|
|
954
|
-
}
|
|
1065
|
+
const name = process.env.npm_config_user_agent?.split("/")[0];
|
|
1066
|
+
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
|
|
955
1067
|
return "pnpm";
|
|
956
1068
|
}
|
|
957
1069
|
function installDependencies(cwd, pm) {
|
|
@@ -962,6 +1074,112 @@ function devCommand(pm) {
|
|
|
962
1074
|
return `${pm} dev`;
|
|
963
1075
|
}
|
|
964
1076
|
|
|
1077
|
+
// src/utils/project-name.ts
|
|
1078
|
+
import path2 from "path";
|
|
1079
|
+
var PACKAGE_NAME = /^[a-z0-9][a-z0-9._-]*$/;
|
|
1080
|
+
var MAX_LENGTH = 214;
|
|
1081
|
+
function validateProjectName(name) {
|
|
1082
|
+
const trimmed = name.trim();
|
|
1083
|
+
if (!trimmed) return "Project name is required";
|
|
1084
|
+
if (trimmed.length > MAX_LENGTH) return `Use at most ${MAX_LENGTH} characters`;
|
|
1085
|
+
if (!PACKAGE_NAME.test(trimmed)) {
|
|
1086
|
+
return 'Use lowercase letters, digits, ".", "_" or "-", starting with a letter or digit';
|
|
1087
|
+
}
|
|
1088
|
+
return void 0;
|
|
1089
|
+
}
|
|
1090
|
+
function projectNameFromDirectory(dir, cwd = process.cwd()) {
|
|
1091
|
+
return path2.basename(path2.resolve(cwd, dir.trim()));
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// src/utils/ollama.ts
|
|
1095
|
+
var DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
1096
|
+
function isRecord2(value) {
|
|
1097
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1098
|
+
}
|
|
1099
|
+
function trimTrailingSlashes(value) {
|
|
1100
|
+
let end = value.length;
|
|
1101
|
+
while (end > 0 && value[end - 1] === "/") end--;
|
|
1102
|
+
return value.slice(0, end);
|
|
1103
|
+
}
|
|
1104
|
+
function resolveOllamaUrl(env = process.env) {
|
|
1105
|
+
const raw = env.OLLAMA_BASE_URL?.trim() || env.OLLAMA_HOST?.trim();
|
|
1106
|
+
if (!raw) return DEFAULT_OLLAMA_URL;
|
|
1107
|
+
const withScheme = /^https?:\/\//i.test(raw) ? raw : `http://${raw}`;
|
|
1108
|
+
return trimTrailingSlashes(withScheme);
|
|
1109
|
+
}
|
|
1110
|
+
async function listOllamaModels(baseUrl, timeoutMs = 2e3) {
|
|
1111
|
+
try {
|
|
1112
|
+
const res = await fetch(`${baseUrl}/api/tags`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
1113
|
+
if (!res.ok) return void 0;
|
|
1114
|
+
const data = await res.json();
|
|
1115
|
+
if (!isRecord2(data) || !Array.isArray(data.models)) return void 0;
|
|
1116
|
+
return data.models.flatMap(
|
|
1117
|
+
(model) => isRecord2(model) && typeof model.name === "string" ? [{ name: model.name, size: typeof model.size === "number" ? model.size : 0 }] : []
|
|
1118
|
+
);
|
|
1119
|
+
} catch {
|
|
1120
|
+
return void 0;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
function withTag(name) {
|
|
1124
|
+
return name.includes(":") ? name : `${name}:latest`;
|
|
1125
|
+
}
|
|
1126
|
+
function hasOllamaModel(installed, model) {
|
|
1127
|
+
const wanted = withTag(model);
|
|
1128
|
+
return installed.some((m) => withTag(m.name) === wanted);
|
|
1129
|
+
}
|
|
1130
|
+
function describeProgress(event, status) {
|
|
1131
|
+
const { completed, total } = event;
|
|
1132
|
+
if (typeof completed === "number" && typeof total === "number" && total > 0) {
|
|
1133
|
+
return `${status} ${Math.floor(completed / total * 100)}%`;
|
|
1134
|
+
}
|
|
1135
|
+
return status;
|
|
1136
|
+
}
|
|
1137
|
+
async function* ndjson(stream) {
|
|
1138
|
+
const reader = stream.getReader();
|
|
1139
|
+
const decoder = new TextDecoder();
|
|
1140
|
+
let buffer = "";
|
|
1141
|
+
try {
|
|
1142
|
+
while (true) {
|
|
1143
|
+
const { done, value } = await reader.read();
|
|
1144
|
+
if (done) break;
|
|
1145
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1146
|
+
let newline = buffer.indexOf("\n");
|
|
1147
|
+
while (newline >= 0) {
|
|
1148
|
+
const line = buffer.slice(0, newline).trim();
|
|
1149
|
+
buffer = buffer.slice(newline + 1);
|
|
1150
|
+
if (line) yield JSON.parse(line);
|
|
1151
|
+
newline = buffer.indexOf("\n");
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
const rest = (buffer + decoder.decode()).trim();
|
|
1155
|
+
if (rest) yield JSON.parse(rest);
|
|
1156
|
+
} finally {
|
|
1157
|
+
reader.releaseLock();
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
async function pullOllamaModel(baseUrl, model, onStatus) {
|
|
1161
|
+
const res = await fetch(`${baseUrl}/api/pull`, {
|
|
1162
|
+
method: "POST",
|
|
1163
|
+
headers: { "Content-Type": "application/json" },
|
|
1164
|
+
body: JSON.stringify({ model, stream: true })
|
|
1165
|
+
});
|
|
1166
|
+
if (!res.ok || !res.body) {
|
|
1167
|
+
const detail = await res.text().catch(() => "");
|
|
1168
|
+
throw new Error(`Failed to pull ${model}: HTTP ${res.status}${detail ? ` ${detail}` : ""}`);
|
|
1169
|
+
}
|
|
1170
|
+
let succeeded = false;
|
|
1171
|
+
for await (const event of ndjson(res.body)) {
|
|
1172
|
+
if (!isRecord2(event)) continue;
|
|
1173
|
+
if (typeof event.error === "string") throw new Error(`Failed to pull ${model}: ${event.error}`);
|
|
1174
|
+
if (typeof event.status !== "string") continue;
|
|
1175
|
+
if (event.status === "success") succeeded = true;
|
|
1176
|
+
onStatus(describeProgress(event, event.status));
|
|
1177
|
+
}
|
|
1178
|
+
if (!succeeded) {
|
|
1179
|
+
throw new Error(`Failed to pull ${model}: the download stopped before it finished`);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
|
|
965
1183
|
// src/prompts.ts
|
|
966
1184
|
var validTemplates = ["basic", "memory", "swarm", "workflow", "api-server", "nextjs"];
|
|
967
1185
|
var validProviders = ["ollama", "openai", "anthropic", "google"];
|
|
@@ -980,6 +1198,12 @@ function parseArgs(args) {
|
|
|
980
1198
|
const val = args[++i];
|
|
981
1199
|
if (validProviders.includes(val)) parsed.provider = val;
|
|
982
1200
|
}
|
|
1201
|
+
} else if (arg === "--model") {
|
|
1202
|
+
const val = args[i + 1];
|
|
1203
|
+
if (val !== void 0 && !val.startsWith("-")) {
|
|
1204
|
+
parsed.model = val;
|
|
1205
|
+
i++;
|
|
1206
|
+
}
|
|
983
1207
|
} else if (arg === "--pm") {
|
|
984
1208
|
if (i + 1 < args.length) {
|
|
985
1209
|
const val = args[++i];
|
|
@@ -1019,22 +1243,54 @@ function unlessCancelled(answer) {
|
|
|
1019
1243
|
}
|
|
1020
1244
|
return answer;
|
|
1021
1245
|
}
|
|
1246
|
+
function validateDirectory(value) {
|
|
1247
|
+
if (!value) return void 0;
|
|
1248
|
+
if (!value.trim()) return "Project name is required";
|
|
1249
|
+
return validateProjectName(projectNameFromDirectory(value));
|
|
1250
|
+
}
|
|
1251
|
+
function formatSize(bytes) {
|
|
1252
|
+
return bytes > 0 ? `${(bytes / 1e9).toFixed(1)} GB` : void 0;
|
|
1253
|
+
}
|
|
1254
|
+
async function promptOllamaModel() {
|
|
1255
|
+
const installed = await listOllamaModels(resolveOllamaUrl());
|
|
1256
|
+
if (!installed) return void 0;
|
|
1257
|
+
const recommended = defaultModels.ollama;
|
|
1258
|
+
const others = installed.filter((model) => !hasOllamaModel([model], recommended));
|
|
1259
|
+
return unlessCancelled(
|
|
1260
|
+
await p.select({
|
|
1261
|
+
message: "Which Ollama model?",
|
|
1262
|
+
options: [
|
|
1263
|
+
{
|
|
1264
|
+
value: recommended,
|
|
1265
|
+
label: recommended,
|
|
1266
|
+
hint: hasOllamaModel(installed, recommended) ? "recommended, installed" : "recommended, not installed yet"
|
|
1267
|
+
},
|
|
1268
|
+
...others.map((model) => ({
|
|
1269
|
+
value: model.name,
|
|
1270
|
+
label: model.name,
|
|
1271
|
+
hint: formatSize(model.size)
|
|
1272
|
+
}))
|
|
1273
|
+
],
|
|
1274
|
+
initialValue: recommended
|
|
1275
|
+
})
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1022
1278
|
async function collectOptions(args) {
|
|
1023
1279
|
const interactive = !args.yes;
|
|
1024
|
-
const
|
|
1280
|
+
const directory = (args.name || (interactive ? unlessCancelled(
|
|
1025
1281
|
await p.text({
|
|
1026
1282
|
message: "Where should we create your project?",
|
|
1027
1283
|
placeholder: `./${defaultAnswers.name}`,
|
|
1028
1284
|
defaultValue: defaultAnswers.name,
|
|
1029
|
-
validate:
|
|
1030
|
-
if (value && !value.trim()) return "Project name is required";
|
|
1031
|
-
return void 0;
|
|
1032
|
-
}
|
|
1285
|
+
validate: validateDirectory
|
|
1033
1286
|
})
|
|
1034
|
-
) : defaultAnswers.name);
|
|
1035
|
-
const
|
|
1036
|
-
const projectName =
|
|
1037
|
-
const
|
|
1287
|
+
) : defaultAnswers.name)).trim();
|
|
1288
|
+
const projectPath = path3.resolve(process.cwd(), directory);
|
|
1289
|
+
const projectName = projectNameFromDirectory(directory);
|
|
1290
|
+
const nameError = validateProjectName(projectName);
|
|
1291
|
+
if (nameError) {
|
|
1292
|
+
throw new Error(`Invalid project name "${projectName}": ${nameError}`);
|
|
1293
|
+
}
|
|
1038
1294
|
const template = args.template ?? (interactive ? unlessCancelled(
|
|
1039
1295
|
await p.select({
|
|
1040
1296
|
message: "Which template would you like?",
|
|
@@ -1049,7 +1305,7 @@ async function collectOptions(args) {
|
|
|
1049
1305
|
{
|
|
1050
1306
|
value: "ollama",
|
|
1051
1307
|
label: "Ollama",
|
|
1052
|
-
hint: `${defaultModels.ollama}
|
|
1308
|
+
hint: `${defaultModels.ollama}, local, free, requires Ollama installed`
|
|
1053
1309
|
},
|
|
1054
1310
|
{ value: "openai", label: "OpenAI", hint: defaultModels.openai },
|
|
1055
1311
|
{ value: "anthropic", label: "Anthropic", hint: defaultModels.anthropic },
|
|
@@ -1058,6 +1314,7 @@ async function collectOptions(args) {
|
|
|
1058
1314
|
initialValue: defaultAnswers.provider
|
|
1059
1315
|
})
|
|
1060
1316
|
) : defaultAnswers.provider);
|
|
1317
|
+
const model = args.model ?? (interactive && provider === "ollama" ? await promptOllamaModel() : void 0);
|
|
1061
1318
|
const packageManager = args.packageManager ?? (interactive ? unlessCancelled(
|
|
1062
1319
|
await p.select({
|
|
1063
1320
|
message: "Package manager?",
|
|
@@ -1087,6 +1344,7 @@ async function collectOptions(args) {
|
|
|
1087
1344
|
path: projectPath,
|
|
1088
1345
|
template,
|
|
1089
1346
|
provider,
|
|
1347
|
+
...model !== void 0 && { model },
|
|
1090
1348
|
packageManager,
|
|
1091
1349
|
docker,
|
|
1092
1350
|
git,
|
|
@@ -1096,7 +1354,7 @@ async function collectOptions(args) {
|
|
|
1096
1354
|
|
|
1097
1355
|
// src/scaffold.ts
|
|
1098
1356
|
import fs from "fs";
|
|
1099
|
-
import
|
|
1357
|
+
import path4 from "path";
|
|
1100
1358
|
import * as p2 from "@clack/prompts";
|
|
1101
1359
|
|
|
1102
1360
|
// src/templates/base/tsconfig.ts
|
|
@@ -1127,15 +1385,19 @@ function generateTsconfig() {
|
|
|
1127
1385
|
}
|
|
1128
1386
|
|
|
1129
1387
|
// src/templates/base/gitignore.ts
|
|
1130
|
-
|
|
1388
|
+
var NEXT_OUTPUT = [".next/", "out/", "next-env.d.ts"];
|
|
1389
|
+
function generateGitignore(template) {
|
|
1131
1390
|
return {
|
|
1132
1391
|
path: ".gitignore",
|
|
1133
1392
|
content: [
|
|
1134
1393
|
"node_modules/",
|
|
1135
1394
|
"dist/",
|
|
1395
|
+
...template === "nextjs" ? NEXT_OUTPUT : [],
|
|
1136
1396
|
".env",
|
|
1137
|
-
".env
|
|
1397
|
+
".env.*",
|
|
1398
|
+
"!.env.example",
|
|
1138
1399
|
"*.log",
|
|
1400
|
+
"*.tsbuildinfo",
|
|
1139
1401
|
".DS_Store",
|
|
1140
1402
|
"coverage/",
|
|
1141
1403
|
".turbo/",
|
|
@@ -1153,16 +1415,16 @@ function generateEnvExample(provider, template) {
|
|
|
1153
1415
|
lines.push("# OLLAMA_BASE_URL=http://localhost:11434");
|
|
1154
1416
|
break;
|
|
1155
1417
|
case "openai":
|
|
1156
|
-
lines.push("# OpenAI");
|
|
1157
|
-
lines.push("OPENAI_API_KEY=
|
|
1418
|
+
lines.push("# OpenAI, https://platform.openai.com/api-keys");
|
|
1419
|
+
lines.push("OPENAI_API_KEY=");
|
|
1158
1420
|
break;
|
|
1159
1421
|
case "anthropic":
|
|
1160
|
-
lines.push("# Anthropic");
|
|
1161
|
-
lines.push("ANTHROPIC_API_KEY=
|
|
1422
|
+
lines.push("# Anthropic, https://console.anthropic.com/settings/keys");
|
|
1423
|
+
lines.push("ANTHROPIC_API_KEY=");
|
|
1162
1424
|
break;
|
|
1163
1425
|
case "google":
|
|
1164
|
-
lines.push("# Google AI");
|
|
1165
|
-
lines.push("GOOGLE_API_KEY
|
|
1426
|
+
lines.push("# Google AI, https://aistudio.google.com/apikey");
|
|
1427
|
+
lines.push("GOOGLE_API_KEY=");
|
|
1166
1428
|
break;
|
|
1167
1429
|
}
|
|
1168
1430
|
if (template === "memory") {
|
|
@@ -1170,25 +1432,54 @@ function generateEnvExample(provider, template) {
|
|
|
1170
1432
|
lines.push("# Redis");
|
|
1171
1433
|
lines.push("REDIS_URL=redis://localhost:6379");
|
|
1172
1434
|
}
|
|
1435
|
+
if (template === "api-server") {
|
|
1436
|
+
lines.push("");
|
|
1437
|
+
lines.push("# Bearer token that every API route except health and docs requires.");
|
|
1438
|
+
lines.push("# Optional in development, where the server listens on 127.0.0.1 only,");
|
|
1439
|
+
lines.push("# required in production (NODE_ENV=production), where it listens on 0.0.0.0.");
|
|
1440
|
+
lines.push("API_TOKEN=");
|
|
1441
|
+
lines.push("");
|
|
1442
|
+
lines.push("# Comma-separated browser origins allowed to call the API, CORS is off without it");
|
|
1443
|
+
lines.push("# CORS_ORIGIN=http://localhost:5173");
|
|
1444
|
+
lines.push("");
|
|
1445
|
+
lines.push("# HOST=127.0.0.1");
|
|
1446
|
+
lines.push("# PORT=3000");
|
|
1447
|
+
}
|
|
1173
1448
|
lines.push("");
|
|
1174
1449
|
return { path: ".env.example", content: lines.join("\n") };
|
|
1175
1450
|
}
|
|
1176
1451
|
|
|
1177
1452
|
// src/templates/base/docker-compose.ts
|
|
1178
|
-
function
|
|
1453
|
+
function ollamaServices(model) {
|
|
1454
|
+
return [
|
|
1455
|
+
" ollama:",
|
|
1456
|
+
" image: ollama/ollama:latest",
|
|
1457
|
+
" ports:",
|
|
1458
|
+
' - "11434:11434"',
|
|
1459
|
+
" volumes:",
|
|
1460
|
+
" - ollama_data:/root/.ollama",
|
|
1461
|
+
" healthcheck:",
|
|
1462
|
+
' test: ["CMD", "ollama", "list"]',
|
|
1463
|
+
" interval: 5s",
|
|
1464
|
+
" timeout: 5s",
|
|
1465
|
+
" retries: 20",
|
|
1466
|
+
"",
|
|
1467
|
+
" ollama-pull:",
|
|
1468
|
+
" image: ollama/ollama:latest",
|
|
1469
|
+
" depends_on:",
|
|
1470
|
+
" ollama:",
|
|
1471
|
+
" condition: service_healthy",
|
|
1472
|
+
" environment:",
|
|
1473
|
+
" OLLAMA_HOST: http://ollama:11434",
|
|
1474
|
+
` entrypoint: ["ollama", "pull", ${JSON.stringify(model)}]`,
|
|
1475
|
+
' restart: "no"',
|
|
1476
|
+
""
|
|
1477
|
+
];
|
|
1478
|
+
}
|
|
1479
|
+
function generateDockerCompose(provider, model = defaultModels[provider]) {
|
|
1179
1480
|
const services = [];
|
|
1180
1481
|
if (provider === "ollama") {
|
|
1181
|
-
services.push(
|
|
1182
|
-
...[
|
|
1183
|
-
" ollama:",
|
|
1184
|
-
" image: ollama/ollama:latest",
|
|
1185
|
-
" ports:",
|
|
1186
|
-
' - "11434:11434"',
|
|
1187
|
-
" volumes:",
|
|
1188
|
-
" - ollama_data:/root/.ollama",
|
|
1189
|
-
""
|
|
1190
|
-
]
|
|
1191
|
-
);
|
|
1482
|
+
services.push(...ollamaServices(model));
|
|
1192
1483
|
}
|
|
1193
1484
|
services.push(
|
|
1194
1485
|
...[
|
|
@@ -1219,12 +1510,15 @@ function generateDockerCompose(provider) {
|
|
|
1219
1510
|
}
|
|
1220
1511
|
|
|
1221
1512
|
// src/templates/base/cogitator-yml.ts
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1513
|
+
var HEADER = [
|
|
1514
|
+
"# Read by `cogitator run` and `cogitator deploy`: the provider and model, the",
|
|
1515
|
+
"# memory to provision and the deploy settings. The code in src/ configures",
|
|
1516
|
+
"# Cogitator itself, so change the provider, model or memory in both places.",
|
|
1517
|
+
""
|
|
1518
|
+
];
|
|
1519
|
+
function generateCogitatorYml(provider, template, model = defaultModels[provider]) {
|
|
1520
|
+
const generator = template ? getTemplate(template) : void 0;
|
|
1521
|
+
const lines = [...HEADER, "llm:", ` defaultProvider: ${provider}`, ` defaultModel: ${model}`];
|
|
1228
1522
|
if (provider === "ollama") {
|
|
1229
1523
|
lines.push(
|
|
1230
1524
|
" providers:",
|
|
@@ -1232,7 +1526,7 @@ function generateCogitatorYml(provider, template) {
|
|
|
1232
1526
|
" baseUrl: ${OLLAMA_BASE_URL:-http://localhost:11434}"
|
|
1233
1527
|
);
|
|
1234
1528
|
}
|
|
1235
|
-
if (
|
|
1529
|
+
if (generator?.memoryAdapter === "redis") {
|
|
1236
1530
|
lines.push(
|
|
1237
1531
|
"",
|
|
1238
1532
|
"memory:",
|
|
@@ -1240,15 +1534,56 @@ function generateCogitatorYml(provider, template) {
|
|
|
1240
1534
|
" redis:",
|
|
1241
1535
|
" url: ${REDIS_URL:-redis://localhost:6379}"
|
|
1242
1536
|
);
|
|
1537
|
+
} else if (generator?.memoryAdapter === "memory") {
|
|
1538
|
+
lines.push("", "memory:", " adapter: memory");
|
|
1539
|
+
}
|
|
1540
|
+
const deploy = [];
|
|
1541
|
+
if (generator?.healthPath) {
|
|
1542
|
+
deploy.push(" health:", ` path: ${generator.healthPath}`);
|
|
1543
|
+
}
|
|
1544
|
+
if (generator?.secrets?.length) {
|
|
1545
|
+
const secrets = [
|
|
1546
|
+
...provider === "ollama" ? [] : [providerEnvKey(provider)],
|
|
1547
|
+
...generator.secrets
|
|
1548
|
+
];
|
|
1549
|
+
deploy.push(" secrets:", ...secrets.map((name) => ` - ${name}`));
|
|
1243
1550
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
lines.push("", "deploy:", " health:", ` path: ${healthPath}`);
|
|
1551
|
+
if (deploy.length > 0) {
|
|
1552
|
+
lines.push("", "deploy:", ...deploy);
|
|
1247
1553
|
}
|
|
1248
1554
|
lines.push("");
|
|
1249
1555
|
return { path: "cogitator.yml", content: lines.join("\n") };
|
|
1250
1556
|
}
|
|
1251
1557
|
|
|
1558
|
+
// src/next-steps.ts
|
|
1559
|
+
function nextSteps(options, state) {
|
|
1560
|
+
const steps = [];
|
|
1561
|
+
if (state.directory) {
|
|
1562
|
+
const dir = /^[\w./-]+$/.test(state.directory) ? state.directory : JSON.stringify(state.directory);
|
|
1563
|
+
steps.push({ command: `cd ${dir}` });
|
|
1564
|
+
}
|
|
1565
|
+
if (!state.installed) {
|
|
1566
|
+
steps.push({ command: `${options.packageManager} install` });
|
|
1567
|
+
}
|
|
1568
|
+
if (options.provider !== "ollama") {
|
|
1569
|
+
steps.push({
|
|
1570
|
+
command: "cp .env.example .env",
|
|
1571
|
+
note: `then set ${providerEnvKey(options.provider)} in .env`
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
if (options.provider === "ollama" && !state.modelReady) {
|
|
1575
|
+
steps.push({
|
|
1576
|
+
command: `ollama pull ${modelFor(options)}`,
|
|
1577
|
+
note: options.docker ? "download the model the agents use, or let `docker compose up -d` pull it into the Ollama container" : "download the model the agents use"
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
if (options.template === "memory" && options.docker) {
|
|
1581
|
+
steps.push({ command: "docker compose up -d redis", note: "the memory lives in Redis" });
|
|
1582
|
+
}
|
|
1583
|
+
steps.push({ command: devCommand(options.packageManager) });
|
|
1584
|
+
return steps;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1252
1587
|
// src/utils/links.ts
|
|
1253
1588
|
var DOCS_URL = "https://cogitator.app/docs";
|
|
1254
1589
|
var REPO_URL = "https://github.com/cogitator-ai/Cogitator-AI";
|
|
@@ -1263,24 +1598,33 @@ var templateNames = {
|
|
|
1263
1598
|
nextjs: "Next.js Chat App"
|
|
1264
1599
|
};
|
|
1265
1600
|
function generateReadme(options) {
|
|
1266
|
-
const dev = devCommand(options.packageManager);
|
|
1267
1601
|
const templateName = templateNames[options.template] || options.template;
|
|
1602
|
+
const steps = nextSteps(options, { installed: false, modelReady: false });
|
|
1268
1603
|
const content = [
|
|
1269
1604
|
`# ${options.name}`,
|
|
1270
1605
|
"",
|
|
1271
|
-
`> Created with [create-cogitator-app](${REPO_URL})
|
|
1606
|
+
`> Created with [create-cogitator-app](${REPO_URL}) - ${templateName} template`,
|
|
1272
1607
|
"",
|
|
1273
1608
|
"## Getting Started",
|
|
1274
1609
|
"",
|
|
1275
1610
|
"```bash",
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1611
|
+
...steps.flatMap((step, i) => [
|
|
1612
|
+
...i > 0 ? [""] : [],
|
|
1613
|
+
...step.note ? [`# ${step.note}`] : [],
|
|
1614
|
+
step.command
|
|
1615
|
+
]),
|
|
1281
1616
|
"```",
|
|
1282
1617
|
"",
|
|
1283
|
-
...options.docker ? [
|
|
1618
|
+
...options.docker ? [
|
|
1619
|
+
"## Docker Services",
|
|
1620
|
+
"",
|
|
1621
|
+
"```bash",
|
|
1622
|
+
"docker compose up -d",
|
|
1623
|
+
"```",
|
|
1624
|
+
"",
|
|
1625
|
+
options.provider === "ollama" ? "Starts Redis, Postgres and Ollama, and pulls the model into the Ollama container." : "Starts Redis and Postgres.",
|
|
1626
|
+
""
|
|
1627
|
+
] : [],
|
|
1284
1628
|
"## Learn More",
|
|
1285
1629
|
"",
|
|
1286
1630
|
`- [Cogitator Documentation](${DOCS_URL})`,
|
|
@@ -1337,9 +1681,10 @@ function initGitRepo(cwd) {
|
|
|
1337
1681
|
}
|
|
1338
1682
|
|
|
1339
1683
|
// src/scaffold.ts
|
|
1684
|
+
var NODE_ENGINES = ">=22.12.0";
|
|
1340
1685
|
function writeFile(basePath, file) {
|
|
1341
|
-
const fullPath =
|
|
1342
|
-
fs.mkdirSync(
|
|
1686
|
+
const fullPath = path4.join(basePath, file.path);
|
|
1687
|
+
fs.mkdirSync(path4.dirname(fullPath), { recursive: true });
|
|
1343
1688
|
fs.writeFileSync(fullPath, file.content, "utf-8");
|
|
1344
1689
|
}
|
|
1345
1690
|
function buildPackageJson(options) {
|
|
@@ -1350,6 +1695,7 @@ function buildPackageJson(options) {
|
|
|
1350
1695
|
version: "0.1.0",
|
|
1351
1696
|
private: true,
|
|
1352
1697
|
...isNextjs ? {} : { type: "module" },
|
|
1698
|
+
engines: { node: NODE_ENGINES },
|
|
1353
1699
|
scripts: template.scripts(),
|
|
1354
1700
|
dependencies: template.dependencies(),
|
|
1355
1701
|
devDependencies: template.devDependencies()
|
|
@@ -1361,9 +1707,9 @@ function collectFiles(options) {
|
|
|
1361
1707
|
const files = [];
|
|
1362
1708
|
files.push({ path: "package.json", content: buildPackageJson(options) });
|
|
1363
1709
|
files.push(...template.files(options));
|
|
1364
|
-
files.push(generateGitignore());
|
|
1710
|
+
files.push(generateGitignore(options.template));
|
|
1365
1711
|
files.push(generateEnvExample(options.provider, options.template));
|
|
1366
|
-
files.push(generateCogitatorYml(options.provider, options.template));
|
|
1712
|
+
files.push(generateCogitatorYml(options.provider, options.template, modelFor(options)));
|
|
1367
1713
|
files.push(generateReadme(options));
|
|
1368
1714
|
if (options.template !== "nextjs") {
|
|
1369
1715
|
files.push(generateTsconfig());
|
|
@@ -1372,22 +1718,26 @@ function collectFiles(options) {
|
|
|
1372
1718
|
files.push(generatePnpmWorkspace());
|
|
1373
1719
|
}
|
|
1374
1720
|
if (options.docker) {
|
|
1375
|
-
files.push(generateDockerCompose(options.provider));
|
|
1721
|
+
files.push(generateDockerCompose(options.provider, modelFor(options)));
|
|
1376
1722
|
}
|
|
1377
1723
|
return files;
|
|
1378
1724
|
}
|
|
1379
|
-
function runStep(
|
|
1380
|
-
|
|
1725
|
+
function runStep(spinner3, messages, step) {
|
|
1726
|
+
spinner3.start(messages.start);
|
|
1381
1727
|
try {
|
|
1382
1728
|
step();
|
|
1383
|
-
|
|
1729
|
+
spinner3.stop(messages.done);
|
|
1384
1730
|
return { status: "done" };
|
|
1385
1731
|
} catch (error) {
|
|
1386
|
-
|
|
1732
|
+
spinner3.stop(messages.failed);
|
|
1387
1733
|
return { status: "failed", error: error instanceof Error ? error : new Error(String(error)) };
|
|
1388
1734
|
}
|
|
1389
1735
|
}
|
|
1390
1736
|
async function scaffold(options) {
|
|
1737
|
+
const nameError = validateProjectName(options.name);
|
|
1738
|
+
if (nameError) {
|
|
1739
|
+
throw new Error(`Invalid project name "${options.name}": ${nameError}`);
|
|
1740
|
+
}
|
|
1391
1741
|
if (fs.existsSync(options.path) && fs.readdirSync(options.path).length > 0) {
|
|
1392
1742
|
throw new Error(`Directory "${options.path}" already exists and is not empty`);
|
|
1393
1743
|
}
|
|
@@ -1421,7 +1771,35 @@ async function scaffold(options) {
|
|
|
1421
1771
|
}
|
|
1422
1772
|
|
|
1423
1773
|
// src/index.ts
|
|
1424
|
-
var pkg = JSON.parse(
|
|
1774
|
+
var pkg = JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf-8"));
|
|
1775
|
+
async function prepareOllamaModel(options, interactive) {
|
|
1776
|
+
const model = modelFor(options);
|
|
1777
|
+
const baseUrl = resolveOllamaUrl();
|
|
1778
|
+
const installed = await listOllamaModels(baseUrl);
|
|
1779
|
+
if (!installed) {
|
|
1780
|
+
p3.log.warn(
|
|
1781
|
+
`Ollama is not running at ${baseUrl}. Start it and pull ${model} before the first run.`
|
|
1782
|
+
);
|
|
1783
|
+
return false;
|
|
1784
|
+
}
|
|
1785
|
+
if (hasOllamaModel(installed, model)) return true;
|
|
1786
|
+
if (!interactive) return false;
|
|
1787
|
+
const pull = await p3.confirm({
|
|
1788
|
+
message: `${model} is not installed in Ollama. Pull it now?`,
|
|
1789
|
+
initialValue: true
|
|
1790
|
+
});
|
|
1791
|
+
if (p3.isCancel(pull) || !pull) return false;
|
|
1792
|
+
const s = p3.spinner();
|
|
1793
|
+
s.start(`Pulling ${model}`);
|
|
1794
|
+
try {
|
|
1795
|
+
await pullOllamaModel(baseUrl, model, (status) => s.message(`Pulling ${model}: ${status}`));
|
|
1796
|
+
s.stop(`Pulled ${model}`);
|
|
1797
|
+
return true;
|
|
1798
|
+
} catch (error) {
|
|
1799
|
+
s.stop(error instanceof Error ? error.message : `Failed to pull ${model}`);
|
|
1800
|
+
return false;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1425
1803
|
async function main() {
|
|
1426
1804
|
banner(pkg.version);
|
|
1427
1805
|
p3.intro(pc2.cyan("Let's build something with AI agents"));
|
|
@@ -1434,15 +1812,19 @@ async function main() {
|
|
|
1434
1812
|
if (result.git.status === "failed") {
|
|
1435
1813
|
p3.log.warn(`git init failed: ${result.git.error.message}`);
|
|
1436
1814
|
}
|
|
1437
|
-
const
|
|
1438
|
-
const
|
|
1815
|
+
const modelReady = options.provider === "ollama" ? await prepareOllamaModel(options, !args.yes) : true;
|
|
1816
|
+
const steps = nextSteps(options, {
|
|
1817
|
+
installed: result.install.status === "done",
|
|
1818
|
+
modelReady,
|
|
1819
|
+
directory: path5.relative(process.cwd(), options.path) || void 0
|
|
1820
|
+
});
|
|
1439
1821
|
p3.outro(
|
|
1440
1822
|
[
|
|
1441
1823
|
pc2.green("Done! ") + "Next steps:",
|
|
1442
1824
|
"",
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1825
|
+
...steps.map(
|
|
1826
|
+
(step) => ` ${pc2.cyan(step.command)}${step.note ? pc2.dim(` # ${step.note}`) : ""}`
|
|
1827
|
+
),
|
|
1446
1828
|
"",
|
|
1447
1829
|
pc2.dim(`Docs: ${DOCS_URL}`)
|
|
1448
1830
|
].join("\n")
|