studiograph 1.0.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.
Files changed (101) hide show
  1. package/README.md +18 -0
  2. package/dist/agent/orchestrator.d.ts +69 -0
  3. package/dist/agent/orchestrator.js +211 -0
  4. package/dist/agent/orchestrator.js.map +1 -0
  5. package/dist/agent/tools/graph-tools.d.ts +30 -0
  6. package/dist/agent/tools/graph-tools.js +536 -0
  7. package/dist/agent/tools/graph-tools.js.map +1 -0
  8. package/dist/auth/github.d.ts +53 -0
  9. package/dist/auth/github.js +180 -0
  10. package/dist/auth/github.js.map +1 -0
  11. package/dist/cli/commands/auth.d.ts +10 -0
  12. package/dist/cli/commands/auth.js +63 -0
  13. package/dist/cli/commands/auth.js.map +1 -0
  14. package/dist/cli/commands/init.d.ts +7 -0
  15. package/dist/cli/commands/init.js +299 -0
  16. package/dist/cli/commands/init.js.map +1 -0
  17. package/dist/cli/commands/join.d.ts +14 -0
  18. package/dist/cli/commands/join.js +230 -0
  19. package/dist/cli/commands/join.js.map +1 -0
  20. package/dist/cli/commands/members.d.ts +11 -0
  21. package/dist/cli/commands/members.js +230 -0
  22. package/dist/cli/commands/members.js.map +1 -0
  23. package/dist/cli/commands/serve.d.ts +17 -0
  24. package/dist/cli/commands/serve.js +90 -0
  25. package/dist/cli/commands/serve.js.map +1 -0
  26. package/dist/cli/commands/start.d.ts +7 -0
  27. package/dist/cli/commands/start.js +381 -0
  28. package/dist/cli/commands/start.js.map +1 -0
  29. package/dist/cli/commands/sync.d.ts +10 -0
  30. package/dist/cli/commands/sync.js +121 -0
  31. package/dist/cli/commands/sync.js.map +1 -0
  32. package/dist/cli/index.d.ts +7 -0
  33. package/dist/cli/index.js +31 -0
  34. package/dist/cli/index.js.map +1 -0
  35. package/dist/core/graph.d.ts +169 -0
  36. package/dist/core/graph.js +558 -0
  37. package/dist/core/graph.js.map +1 -0
  38. package/dist/core/types.d.ts +216 -0
  39. package/dist/core/types.js +71 -0
  40. package/dist/core/types.js.map +1 -0
  41. package/dist/core/user-config.d.ts +31 -0
  42. package/dist/core/user-config.js +50 -0
  43. package/dist/core/user-config.js.map +1 -0
  44. package/dist/core/validation.d.ts +2371 -0
  45. package/dist/core/validation.js +432 -0
  46. package/dist/core/validation.js.map +1 -0
  47. package/dist/core/workspace-manager.d.ts +104 -0
  48. package/dist/core/workspace-manager.js +432 -0
  49. package/dist/core/workspace-manager.js.map +1 -0
  50. package/dist/core/workspace.d.ts +103 -0
  51. package/dist/core/workspace.js +306 -0
  52. package/dist/core/workspace.js.map +1 -0
  53. package/dist/server/index.d.ts +25 -0
  54. package/dist/server/index.js +84 -0
  55. package/dist/server/index.js.map +1 -0
  56. package/dist/server/plugin-loader.d.ts +31 -0
  57. package/dist/server/plugin-loader.js +81 -0
  58. package/dist/server/plugin-loader.js.map +1 -0
  59. package/dist/server/routes/chat.d.ts +11 -0
  60. package/dist/server/routes/chat.js +66 -0
  61. package/dist/server/routes/chat.js.map +1 -0
  62. package/dist/server/routes/graph-api.d.ts +9 -0
  63. package/dist/server/routes/graph-api.js +72 -0
  64. package/dist/server/routes/graph-api.js.map +1 -0
  65. package/dist/server/routes/webhook.d.ts +14 -0
  66. package/dist/server/routes/webhook.js +69 -0
  67. package/dist/server/routes/webhook.js.map +1 -0
  68. package/dist/services/assets/base.d.ts +69 -0
  69. package/dist/services/assets/base.js +113 -0
  70. package/dist/services/assets/base.js.map +1 -0
  71. package/dist/services/assets/index.d.ts +36 -0
  72. package/dist/services/assets/index.js +89 -0
  73. package/dist/services/assets/index.js.map +1 -0
  74. package/dist/services/assets/local.d.ts +42 -0
  75. package/dist/services/assets/local.js +161 -0
  76. package/dist/services/assets/local.js.map +1 -0
  77. package/dist/services/assets/r2.d.ts +36 -0
  78. package/dist/services/assets/r2.js +182 -0
  79. package/dist/services/assets/r2.js.map +1 -0
  80. package/dist/services/csv-service.d.ts +36 -0
  81. package/dist/services/csv-service.js +143 -0
  82. package/dist/services/csv-service.js.map +1 -0
  83. package/dist/services/git.d.ts +99 -0
  84. package/dist/services/git.js +306 -0
  85. package/dist/services/git.js.map +1 -0
  86. package/dist/services/github-provisioner.d.ts +30 -0
  87. package/dist/services/github-provisioner.js +89 -0
  88. package/dist/services/github-provisioner.js.map +1 -0
  89. package/dist/services/markdown.d.ts +82 -0
  90. package/dist/services/markdown.js +338 -0
  91. package/dist/services/markdown.js.map +1 -0
  92. package/dist/services/memory-service.d.ts +74 -0
  93. package/dist/services/memory-service.js +183 -0
  94. package/dist/services/memory-service.js.map +1 -0
  95. package/dist/utils/git.d.ts +28 -0
  96. package/dist/utils/git.js +55 -0
  97. package/dist/utils/git.js.map +1 -0
  98. package/dist/utils/preflight.d.ts +44 -0
  99. package/dist/utils/preflight.js +95 -0
  100. package/dist/utils/preflight.js.map +1 -0
  101. package/package.json +55 -0
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Workspace management for Studiograph
3
+ *
4
+ * A workspace is a directory with .studiograph/ folder
5
+ * Similar to how Git works with .git/
6
+ */
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
8
+ import { join } from 'path';
9
+ import * as yaml from 'js-yaml';
10
+ import { RepoType } from './types.js';
11
+ import { setUserModelConfig } from './user-config.js';
12
+ export class Workspace {
13
+ workspacePath;
14
+ configPath;
15
+ jsonConfigPath;
16
+ membersPath;
17
+ config = null;
18
+ constructor(workspacePath = process.cwd()) {
19
+ this.workspacePath = workspacePath;
20
+ this.configPath = join(workspacePath, '.studiograph', 'config.yml'); // Legacy YAML format
21
+ this.jsonConfigPath = join(workspacePath, '.studiograph', 'workspace.json'); // New JSON format
22
+ this.membersPath = join(workspacePath, '.studiograph', 'team', 'members.json');
23
+ }
24
+ /**
25
+ * Check if current directory is a Studiograph workspace
26
+ * Supports both workspace.json (new) and config.yml (legacy)
27
+ */
28
+ isWorkspace() {
29
+ return existsSync(this.jsonConfigPath) || existsSync(this.configPath);
30
+ }
31
+ /**
32
+ * Initialize new workspace in current directory
33
+ */
34
+ init(teamName) {
35
+ if (this.isWorkspace()) {
36
+ throw new Error('Already a Studiograph workspace');
37
+ }
38
+ // Create .studiograph directory and team subdirectory
39
+ const studiographDir = join(this.workspacePath, '.studiograph');
40
+ mkdirSync(join(studiographDir, 'team'), { recursive: true });
41
+ // Create initial config
42
+ const config = {
43
+ version: '1.0.0',
44
+ team_name: teamName,
45
+ created_at: new Date().toISOString(),
46
+ repos: [],
47
+ };
48
+ // Write config as workspace.json
49
+ this.writeConfig(config);
50
+ this.config = config;
51
+ // Create initial empty team members file
52
+ const initialMembers = { members: [] };
53
+ writeFileSync(this.membersPath, JSON.stringify(initialMembers, null, 2), 'utf-8');
54
+ // Create .gitignore for .studiograph
55
+ const gitignorePath = join(studiographDir, '.gitignore');
56
+ writeFileSync(gitignorePath, '# Ignore sensitive local config\n*.local\n');
57
+ }
58
+ /**
59
+ * Load workspace config
60
+ * Reads workspace.json (new format) or falls back to config.yml (legacy)
61
+ */
62
+ loadConfig() {
63
+ if (!this.isWorkspace()) {
64
+ throw new Error('Not a Studiograph workspace. Run `studiograph init` first.');
65
+ }
66
+ if (this.config) {
67
+ return this.config;
68
+ }
69
+ if (existsSync(this.jsonConfigPath)) {
70
+ const configJson = readFileSync(this.jsonConfigPath, 'utf-8');
71
+ this.config = JSON.parse(configJson);
72
+ }
73
+ else {
74
+ // Legacy YAML fallback
75
+ const configYaml = readFileSync(this.configPath, 'utf-8');
76
+ this.config = yaml.load(configYaml);
77
+ }
78
+ return this.config;
79
+ }
80
+ /**
81
+ * Write workspace config as workspace.json
82
+ */
83
+ writeConfig(config) {
84
+ writeFileSync(this.jsonConfigPath, JSON.stringify(config, null, 2), 'utf-8');
85
+ }
86
+ /**
87
+ * Load team members from team/members.json
88
+ */
89
+ loadMembers() {
90
+ if (!existsSync(this.membersPath)) {
91
+ return [];
92
+ }
93
+ const membersJson = readFileSync(this.membersPath, 'utf-8');
94
+ const data = JSON.parse(membersJson);
95
+ return data.members;
96
+ }
97
+ /**
98
+ * Write team members to team/members.json
99
+ */
100
+ writeMembers(members) {
101
+ const membersDir = join(this.workspacePath, '.studiograph', 'team');
102
+ mkdirSync(membersDir, { recursive: true });
103
+ writeFileSync(this.membersPath, JSON.stringify({ members }, null, 2), 'utf-8');
104
+ }
105
+ /**
106
+ * Get the GitHub org stored in workspace.json
107
+ */
108
+ getGitHubOrg() {
109
+ const config = this.loadConfig();
110
+ return config.github?.org;
111
+ }
112
+ /**
113
+ * Set the GitHub org in workspace.json
114
+ */
115
+ setGitHubOrg(org) {
116
+ const config = this.loadConfig();
117
+ config.github = { ...config.github, org };
118
+ this.writeConfig(config);
119
+ this.config = config;
120
+ }
121
+ /**
122
+ * Update model configuration.
123
+ *
124
+ * Provider and model ID are written to user config (~/.studiograph/user.json)
125
+ * so they are personal and never committed to the shared workspace repo.
126
+ * The API key is also written only to user config — it is never stored in
127
+ * workspace.json.
128
+ */
129
+ setModelConfig(provider, modelId, apiKey) {
130
+ setUserModelConfig(provider, modelId, apiKey);
131
+ }
132
+ /**
133
+ * Generate README content for a repo
134
+ */
135
+ generateRepoReadme(repo) {
136
+ const repoTypeDescriptions = {
137
+ project: 'Project Repository',
138
+ function: 'Function Repository',
139
+ 'shared-resource': 'Shared Resource Repository',
140
+ };
141
+ const repoTypeName = repoTypeDescriptions[repo.type] || 'Repository';
142
+ return `# ${repo.name}
143
+
144
+ **Type:** ${repoTypeName}
145
+ **Access:** ${repo.access}
146
+
147
+ ${repo.description || 'No description provided.'}
148
+
149
+ ## About This Repository
150
+
151
+ ${this.getRepoTypeGuidance(repo.type)}
152
+
153
+ ## Getting Started
154
+
155
+ This repository is part of a Studiograph workspace. Entities are stored as markdown files with YAML frontmatter.
156
+
157
+ ### File Structure
158
+
159
+ Organize your files however makes sense for your team. Common patterns:
160
+ - By entity type (e.g., \`clients/\`, \`projects/\`, \`meetings/\`)
161
+ - By date (e.g., \`2026/02/\`)
162
+ - By project or topic
163
+ - Flat structure (all files in root)
164
+
165
+ ### Creating Entities
166
+
167
+ Each entity is a markdown file with YAML frontmatter:
168
+
169
+ \`\`\`markdown
170
+ ---
171
+ entity_type: example
172
+ entity_id: my-entity
173
+ created_at: 2026-02-15T00:00:00Z
174
+ updated_at: 2026-02-15T00:00:00Z
175
+ created_by: username
176
+ updated_by: username
177
+ tags: [tag1, tag2]
178
+ ---
179
+
180
+ # Entity content goes here
181
+
182
+ Use [[wikilinks]] to reference other entities.
183
+ \`\`\`
184
+ `;
185
+ }
186
+ /**
187
+ * Get guidance text for a repo type
188
+ */
189
+ getRepoTypeGuidance(type) {
190
+ switch (type) {
191
+ case RepoType.PROJECT:
192
+ return `This is a **Project Repository** - typically one per client or major initiative.
193
+
194
+ Store project-specific entities like:
195
+ - Project overview and status
196
+ - Client information
197
+ - Meeting notes
198
+ - Decisions and rationale
199
+ - Deliverables and artifacts
200
+ - Project-specific resources`;
201
+ case RepoType.FUNCTION:
202
+ return `This is a **Function Repository** - for business operations and internal functions.
203
+
204
+ Store operational entities like:
205
+ - Processes and workflows
206
+ - Policies and guidelines
207
+ - Team documentation
208
+ - Internal meetings and decisions
209
+ - Function-specific resources`;
210
+ case RepoType.SHARED_RESOURCE:
211
+ return `This is a **Shared Resource Repository** - for team-wide knowledge and resources.
212
+
213
+ Store shared entities like:
214
+ - Templates and boilerplates
215
+ - Best practices and standards
216
+ - Research and references
217
+ - Techniques and methods
218
+ - Frameworks and methodologies
219
+ - External examples and inspiration`;
220
+ default:
221
+ return 'This repository can store any type of entity relevant to your team.';
222
+ }
223
+ }
224
+ /**
225
+ * Register a new repo in the workspace
226
+ */
227
+ registerRepo(repo) {
228
+ const config = this.loadConfig();
229
+ // Check if repo already exists
230
+ if (config.repos.find(r => r.name === repo.name)) {
231
+ throw new Error(`Repo '${repo.name}' already registered`);
232
+ }
233
+ // Create repo config with path and default access
234
+ const repoConfig = {
235
+ ...repo,
236
+ path: repo.name, // Path relative to workspace root
237
+ access: repo.access ?? 'team', // Default to 'team' if not specified
238
+ };
239
+ // Add to config
240
+ config.repos.push(repoConfig);
241
+ this.writeConfig(config);
242
+ this.config = config;
243
+ // Create repo directory
244
+ const repoPath = join(this.workspacePath, repo.name);
245
+ mkdirSync(repoPath, { recursive: true });
246
+ // Create .studiograph marker in repo
247
+ const repoMarkerPath = join(repoPath, '.studiograph');
248
+ writeFileSync(repoMarkerPath, yaml.dump({ repo_type: repo.type, repo_name: repo.name }), 'utf-8');
249
+ // Create README.md with repo description
250
+ const readmePath = join(repoPath, 'README.md');
251
+ const readmeContent = this.generateRepoReadme(repoConfig);
252
+ writeFileSync(readmePath, readmeContent, 'utf-8');
253
+ return repoConfig;
254
+ }
255
+ /**
256
+ * Get all registered repos
257
+ */
258
+ getRepos() {
259
+ const config = this.loadConfig();
260
+ return config.repos;
261
+ }
262
+ /**
263
+ * Get repo by name
264
+ */
265
+ getRepo(name) {
266
+ const config = this.loadConfig();
267
+ return config.repos.find(r => r.name === name);
268
+ }
269
+ /**
270
+ * Get absolute path to repo
271
+ */
272
+ getRepoPath(repoName) {
273
+ const repo = this.getRepo(repoName);
274
+ if (!repo) {
275
+ throw new Error(`Repo '${repoName}' not found`);
276
+ }
277
+ return join(this.workspacePath, repo.path);
278
+ }
279
+ /**
280
+ * Get workspace root path
281
+ */
282
+ getWorkspacePath() {
283
+ return this.workspacePath;
284
+ }
285
+ /**
286
+ * Find workspace root by walking up directory tree
287
+ * (like git does with .git/)
288
+ */
289
+ static findWorkspaceRoot(startPath = process.cwd()) {
290
+ let currentPath = startPath;
291
+ while (currentPath !== '/') {
292
+ const jsonConfigPath = join(currentPath, '.studiograph', 'workspace.json');
293
+ const yamlConfigPath = join(currentPath, '.studiograph', 'config.yml');
294
+ if (existsSync(jsonConfigPath) || existsSync(yamlConfigPath)) {
295
+ return currentPath;
296
+ }
297
+ // Go up one directory
298
+ const parentPath = join(currentPath, '..');
299
+ if (parentPath === currentPath)
300
+ break; // Reached root
301
+ currentPath = parentPath;
302
+ }
303
+ return null;
304
+ }
305
+ }
306
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/core/workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Studiograph HTTP Server
3
+ *
4
+ * Hosts:
5
+ * - Graph API (read-only REST over WorkspaceManager)
6
+ * - Agent chat via POST /api/chat and SSE GET /api/chat/stream
7
+ * - GitHub webhook for git pull on push (Config 2 deployments)
8
+ * - App plugins mounted via PluginLoader
9
+ */
10
+ import { type FastifyInstance } from 'fastify';
11
+ import { WorkspaceManager } from '../core/workspace-manager.js';
12
+ import { GitUser } from '../services/git.js';
13
+ import { WorkspaceConfig } from '../core/types.js';
14
+ export interface ServerConfig {
15
+ workspaceManager: WorkspaceManager;
16
+ workspacePath: string;
17
+ workspaceConfig: WorkspaceConfig;
18
+ gitUser: GitUser;
19
+ appsDir: string;
20
+ port: number;
21
+ host: string;
22
+ apiKeys: string[];
23
+ webhookSecret?: string;
24
+ }
25
+ export declare function createServer(config: ServerConfig): Promise<FastifyInstance>;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Studiograph HTTP Server
3
+ *
4
+ * Hosts:
5
+ * - Graph API (read-only REST over WorkspaceManager)
6
+ * - Agent chat via POST /api/chat and SSE GET /api/chat/stream
7
+ * - GitHub webhook for git pull on push (Config 2 deployments)
8
+ * - App plugins mounted via PluginLoader
9
+ */
10
+ import Fastify from 'fastify';
11
+ import cors from '@fastify/cors';
12
+ import { AgentOrchestrator } from '../agent/orchestrator.js';
13
+ import { PluginLoader } from './plugin-loader.js';
14
+ import { registerGraphApiRoutes } from './routes/graph-api.js';
15
+ import { registerChatRoutes } from './routes/chat.js';
16
+ import { registerWebhookRoutes } from './routes/webhook.js';
17
+ /**
18
+ * Auth middleware — validates Authorization: Bearer <key> header.
19
+ * Applied to all write-adjacent or sensitive routes.
20
+ */
21
+ function requireApiKey(apiKeys) {
22
+ return async (req, reply) => {
23
+ if (apiKeys.length === 0)
24
+ return; // no keys configured — open (dev mode)
25
+ const auth = req.headers['authorization'];
26
+ const token = auth?.startsWith('Bearer ') ? auth.slice(7) : undefined;
27
+ if (!token || !apiKeys.includes(token)) {
28
+ return reply.status(401).send({ error: 'Unauthorized' });
29
+ }
30
+ };
31
+ }
32
+ export async function createServer(config) {
33
+ const fastify = Fastify({ logger: true });
34
+ // CORS
35
+ await fastify.register(cors, {
36
+ origin: true,
37
+ });
38
+ // Parse raw body for webhook HMAC validation
39
+ fastify.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => {
40
+ try {
41
+ req.rawBody = body;
42
+ done(null, JSON.parse(body.toString()));
43
+ }
44
+ catch (err) {
45
+ done(err, undefined);
46
+ }
47
+ });
48
+ // Auth hook — protect chat and webhook routes
49
+ const authHook = requireApiKey(config.apiKeys);
50
+ fastify.addHook('preHandler', async (req, reply) => {
51
+ const protectedPaths = ['/api/chat', '/webhooks/'];
52
+ if (protectedPaths.some(p => req.url.startsWith(p))) {
53
+ await authHook(req, reply);
54
+ }
55
+ });
56
+ // Register graph API routes (unauthenticated read-only)
57
+ await registerGraphApiRoutes(fastify, config.workspaceManager);
58
+ // Initialize agent for chat routes
59
+ const agent = new AgentOrchestrator({
60
+ workspacePath: config.workspacePath,
61
+ workspaceConfig: config.workspaceConfig,
62
+ gitUser: config.gitUser,
63
+ });
64
+ await registerChatRoutes(fastify, agent);
65
+ // GitHub webhook route
66
+ await registerWebhookRoutes(fastify, config.workspaceManager, config.webhookSecret);
67
+ // Load and mount app plugins
68
+ const loader = new PluginLoader();
69
+ const manifests = loader.loadApps(config.appsDir);
70
+ for (const manifest of manifests) {
71
+ try {
72
+ await loader.mountPlugin(fastify, manifest, config.appsDir, {
73
+ workspaceManager: config.workspaceManager,
74
+ gitUser: config.gitUser,
75
+ });
76
+ fastify.log.info(`Mounted app "${manifest.name}" at ${manifest.mountPath}`);
77
+ }
78
+ catch (err) {
79
+ fastify.log.error(err, `Failed to mount app "${manifest.name}"`);
80
+ }
81
+ }
82
+ return fastify;
83
+ }
84
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Plugin Loader
3
+ *
4
+ * Scans .studiograph/apps/ for installed apps, parses their manifests,
5
+ * and mounts their Fastify plugins under the declared mountPath.
6
+ */
7
+ import type { FastifyInstance } from 'fastify';
8
+ import { WorkspaceManager } from '../core/workspace-manager.js';
9
+ import { GitUser } from '../services/git.js';
10
+ export interface AppManifest {
11
+ name: string;
12
+ version: string;
13
+ description?: string;
14
+ plugin: string;
15
+ entityTypes: string;
16
+ mountPath: string;
17
+ }
18
+ export declare class PluginLoader {
19
+ /**
20
+ * Scan appsDir for installed apps and return their manifests.
21
+ * Returns empty array if the directory does not exist.
22
+ */
23
+ loadApps(appsDir: string): AppManifest[];
24
+ /**
25
+ * Dynamically import and register a plugin under its mountPath.
26
+ */
27
+ mountPlugin(fastify: FastifyInstance, manifest: AppManifest, appDir: string, opts: {
28
+ workspaceManager: WorkspaceManager;
29
+ gitUser: GitUser;
30
+ }): Promise<void>;
31
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Plugin Loader
3
+ *
4
+ * Scans .studiograph/apps/ for installed apps, parses their manifests,
5
+ * and mounts their Fastify plugins under the declared mountPath.
6
+ */
7
+ import { existsSync, readdirSync, readFileSync } from 'fs';
8
+ import { join } from 'path';
9
+ export class PluginLoader {
10
+ /**
11
+ * Scan appsDir for installed apps and return their manifests.
12
+ * Returns empty array if the directory does not exist.
13
+ */
14
+ loadApps(appsDir) {
15
+ if (!existsSync(appsDir)) {
16
+ return [];
17
+ }
18
+ const manifests = [];
19
+ let entries;
20
+ try {
21
+ entries = readdirSync(appsDir);
22
+ }
23
+ catch {
24
+ return [];
25
+ }
26
+ for (const entry of entries) {
27
+ const manifestPath = join(appsDir, entry, 'app.json');
28
+ if (!existsSync(manifestPath)) {
29
+ continue;
30
+ }
31
+ try {
32
+ const raw = readFileSync(manifestPath, 'utf-8');
33
+ const manifest = JSON.parse(raw);
34
+ if (!manifest.plugin) {
35
+ throw new Error(`app.json in "${entry}" is missing required field: plugin`);
36
+ }
37
+ if (!manifest.mountPath) {
38
+ throw new Error(`app.json in "${entry}" is missing required field: mountPath`);
39
+ }
40
+ manifests.push({
41
+ name: manifest.name || entry,
42
+ version: manifest.version || '0.0.0',
43
+ description: manifest.description,
44
+ plugin: manifest.plugin,
45
+ entityTypes: manifest.entityTypes || './entity-types.json',
46
+ mountPath: manifest.mountPath,
47
+ });
48
+ }
49
+ catch (error) {
50
+ // Re-throw validation errors; skip JSON parse errors gracefully
51
+ if (error instanceof Error && error.message.includes('missing required field')) {
52
+ throw error;
53
+ }
54
+ console.warn(`Failed to load app manifest from ${manifestPath}:`, error);
55
+ }
56
+ }
57
+ return manifests;
58
+ }
59
+ /**
60
+ * Dynamically import and register a plugin under its mountPath.
61
+ */
62
+ async mountPlugin(fastify, manifest, appDir, opts) {
63
+ const pluginPath = join(appDir, manifest.name, manifest.plugin);
64
+ if (!existsSync(pluginPath)) {
65
+ throw new Error(`Plugin file not found: ${pluginPath}`);
66
+ }
67
+ // Dynamic import — plugin must export a default Fastify plugin function
68
+ const mod = await import(pluginPath);
69
+ const pluginFn = mod.default ?? mod;
70
+ if (typeof pluginFn !== 'function') {
71
+ throw new Error(`Plugin at ${pluginPath} does not export a function`);
72
+ }
73
+ await fastify.register(pluginFn, {
74
+ prefix: manifest.mountPath,
75
+ workspaceManager: opts.workspaceManager,
76
+ gitUser: opts.gitUser,
77
+ appsDir: appDir,
78
+ });
79
+ }
80
+ }
81
+ //# sourceMappingURL=plugin-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-loader.js","sourceRoot":"","sources":["../../src/server/plugin-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Chat / Agent SSE routes
3
+ *
4
+ * POST /api/chat — send a message, await full response
5
+ * GET /api/chat/stream — SSE stream of agent events (future)
6
+ *
7
+ * Single agent instance per server process (single-user v1).
8
+ */
9
+ import type { FastifyInstance } from 'fastify';
10
+ import { AgentOrchestrator } from '../../agent/orchestrator.js';
11
+ export declare function registerChatRoutes(fastify: FastifyInstance, agent: AgentOrchestrator): Promise<void>;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Chat / Agent SSE routes
3
+ *
4
+ * POST /api/chat — send a message, await full response
5
+ * GET /api/chat/stream — SSE stream of agent events (future)
6
+ *
7
+ * Single agent instance per server process (single-user v1).
8
+ */
9
+ export async function registerChatRoutes(fastify, agent) {
10
+ // POST /api/chat — send message, get full response
11
+ fastify.post('/api/chat', async (req, reply) => {
12
+ const { message } = req.body;
13
+ if (!message || typeof message !== 'string' || !message.trim()) {
14
+ return reply.status(400).send({ error: 'message is required' });
15
+ }
16
+ try {
17
+ const response = await agent.chat(message);
18
+ return reply.send(response);
19
+ }
20
+ catch (err) {
21
+ fastify.log.error(err, 'Agent chat error');
22
+ return reply.status(500).send({ error: err.message || 'Agent error' });
23
+ }
24
+ });
25
+ // GET /api/chat/stream — SSE stream of agent events
26
+ fastify.get('/api/chat/stream', async (req, reply) => {
27
+ const { message } = req.query;
28
+ if (!message || !message.trim()) {
29
+ return reply.status(400).send({ error: 'message query param is required' });
30
+ }
31
+ // Set SSE headers
32
+ reply.raw.setHeader('Content-Type', 'text/event-stream');
33
+ reply.raw.setHeader('Cache-Control', 'no-cache');
34
+ reply.raw.setHeader('Connection', 'keep-alive');
35
+ reply.raw.flushHeaders();
36
+ const sendEvent = (data) => {
37
+ reply.raw.write(`data: ${JSON.stringify(data)}\n\n`);
38
+ };
39
+ try {
40
+ // Subscribe to agent events before sending message
41
+ const unsubscribe = agent.agent?.subscribe?.((event) => {
42
+ if (event.type === 'message_update') {
43
+ const msgEvent = event.assistantMessageEvent;
44
+ if (msgEvent?.type === 'text_delta') {
45
+ sendEvent({ type: 'token', text: msgEvent.delta });
46
+ }
47
+ }
48
+ else if (event.type === 'tool_start') {
49
+ sendEvent({ type: 'tool_start', name: event.name });
50
+ }
51
+ else if (event.type === 'tool_end') {
52
+ sendEvent({ type: 'tool_end', name: event.name });
53
+ }
54
+ });
55
+ const response = await agent.chat(message);
56
+ if (unsubscribe)
57
+ unsubscribe();
58
+ sendEvent({ type: 'done', message: response.content });
59
+ }
60
+ catch (err) {
61
+ sendEvent({ type: 'error', message: err.message || 'Agent error' });
62
+ }
63
+ reply.raw.end();
64
+ });
65
+ }
66
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../src/server/routes/chat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Graph API routes
3
+ *
4
+ * Read-only REST façade over WorkspaceManager.
5
+ * All writes go through the agent (/api/chat).
6
+ */
7
+ import type { FastifyInstance } from 'fastify';
8
+ import { WorkspaceManager } from '../../core/workspace-manager.js';
9
+ export declare function registerGraphApiRoutes(fastify: FastifyInstance, workspaceManager: WorkspaceManager): Promise<void>;