@descix/cli 1.0.1 → 1.0.4

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 (100) hide show
  1. package/LICENSE.md +671 -0
  2. package/README.md +43 -4
  3. package/agent-assets/guides/README.md +2 -2
  4. package/agent-assets/guides/dynamic-site-pattern.md +29 -14
  5. package/agent-assets/guides/knowledge-only-pattern.md +5 -5
  6. package/agent-assets/guides/microservice-pattern.md +48 -4
  7. package/agent-assets/guides/static-site-pattern.md +8 -0
  8. package/agent-assets/instructions/app-asset-validation.md +5 -5
  9. package/agent-assets/instructions/onboarding-flow.md +4 -4
  10. package/agent-assets/instructions/standard-folder-structure.md +20 -11
  11. package/agent-assets/reference/app-creation.md +42 -20
  12. package/agent-assets/reference/architecture.md +24 -22
  13. package/agent-assets/reference/local-dev.md +230 -67
  14. package/agent-assets/reference/mcp-integration.md +21 -15
  15. package/agent-assets/reference/platform-api-consumer.md +244 -0
  16. package/agent-assets/reference/workspace-config.md +7 -7
  17. package/agent-assets/rules/descix_mcp.template.mdc +8 -8
  18. package/bin/descix.js +2559 -1435
  19. package/bin/kb-deny-lint.mjs +91 -0
  20. package/bin/mcp-server.js +175 -91
  21. package/lib/agent-files.js +37 -25
  22. package/lib/api-client.js +223 -56
  23. package/lib/auth-guard.js +13 -3
  24. package/lib/chat-session.js +187 -0
  25. package/lib/cli-version.js +59 -0
  26. package/lib/commands/airdrop.js +251 -0
  27. package/lib/commands/auth.js +254 -72
  28. package/lib/commands/briefer/errors.js +88 -0
  29. package/lib/commands/briefer/index.js +319 -0
  30. package/lib/commands/briefer/sources/canonical-sources.js +129 -0
  31. package/lib/commands/briefer/sources/entitlements.js +220 -0
  32. package/lib/commands/briefer/sources/environments.js +205 -0
  33. package/lib/commands/briefer/sources/identifiers.js +214 -0
  34. package/lib/commands/briefer/sources/microservice-deploy.js +267 -0
  35. package/lib/commands/briefer/sources/routing.js +275 -0
  36. package/lib/commands/briefer/sources/what-is-not.js +223 -0
  37. package/lib/commands/briefer/stitcher.js +134 -0
  38. package/lib/commands/briefer/util/source-reader.js +502 -0
  39. package/lib/commands/config.js +210 -90
  40. package/lib/commands/corpus.js +978 -0
  41. package/lib/commands/credits.js +122 -0
  42. package/lib/commands/health.js +627 -0
  43. package/lib/commands/kb-list-render.js +32 -0
  44. package/lib/commands/kb.js +190 -413
  45. package/lib/commands/mcp.js +5 -5
  46. package/lib/commands/microservice-restart.js +359 -0
  47. package/lib/commands/model-config.js +313 -0
  48. package/lib/commands/retired-kb-sync.js +134 -0
  49. package/lib/commands/serve.js +26 -1
  50. package/lib/commands/status.js +23 -51
  51. package/lib/commands/update.js +36 -107
  52. package/lib/core/Chunker.js +491 -17
  53. package/lib/core/CorpusDenyLint.js +326 -0
  54. package/lib/core/CorpusWalker.js +357 -0
  55. package/lib/core/Hydrator.js +75 -29
  56. package/lib/core/ManifestLoader.js +533 -0
  57. package/lib/core/SiteWalker.js +218 -0
  58. package/lib/core/Syncer.js +163 -11
  59. package/lib/diagnostic-log.js +41 -0
  60. package/lib/environment-report.js +100 -0
  61. package/lib/global-config.js +4 -1
  62. package/lib/oauth-client.js +164 -0
  63. package/lib/origin.js +143 -0
  64. package/lib/service-api-client.js +95 -0
  65. package/lib/wallet-environment.js +49 -0
  66. package/lib/wallet-file.js +16 -2
  67. package/lib/wizard/setup.js +10 -9
  68. package/lib/workspace-config.js +380 -235
  69. package/lib/workspace-identity.js +92 -0
  70. package/lib/workspace-utils.js +41 -61
  71. package/package.json +19 -6
  72. package/templates/agent-claude.md +157 -9
  73. package/templates/agent-cline.md +68 -8
  74. package/templates/agent-copilot.md +68 -8
  75. package/templates/agent-cursor.md +68 -8
  76. package/templates/alignment-schema.json +20 -0
  77. package/templates/cloud-brain-hook.sh +20 -0
  78. package/templates/drive/README.md +1 -1
  79. package/templates/drive_templates/README.md +2 -2
  80. package/templates/scaffolds/README.md +6 -10
  81. package/templates/scaffolds/microservice/SERVICE_README_sdk.md +104 -149
  82. package/templates/scaffolds/microservice/app.js +51 -10
  83. package/templates/scaffolds/microservice/app.yaml +2 -2
  84. package/templates/scaffolds/microservice/defaults-config-dev.json +4 -0
  85. package/templates/scaffolds/microservice/dev-overrides.example.json +5 -0
  86. package/templates/scaffolds/microservice/manifest.json +0 -1
  87. package/templates/scaffolds/microservice/package.json +3 -3
  88. package/templates/scaffolds/microservice/scripts/register.js +56 -41
  89. package/templates/scaffolds/microservice/services/mcpClient.js +55 -65
  90. package/templates/scaffolds/microservice/services/utils.js +20 -238
  91. package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +24 -0
  92. package/lib/commands/app-wizard.js +0 -627
  93. package/lib/commands/folder.js +0 -291
  94. package/lib/core/PathContext.js +0 -335
  95. package/templates/scaffolds/microservice/dev-overrides.json.example +0 -5
  96. package/templates/scaffolds/site/DeSciXAppSDK.js +0 -62
  97. package/templates/scaffolds/site/README.md +0 -51
  98. package/templates/scaffolds/site/app.js +0 -22
  99. package/templates/scaffolds/site/index.html +0 -42
  100. package/templates/scaffolds/site/styles.css +0 -123
@@ -1,291 +0,0 @@
1
- /**
2
- * Folder Commands - Drive Folder Management
3
- *
4
- * Commands for managing Drive folder IDs for entities:
5
- * descix folder set --user/--community/--app/--kb <folder-id>
6
- * descix folder get --user/--community/--app/--kb
7
- * descix folder allocate --app/--kb
8
- * descix folder validate <folder-id>
9
- */
10
-
11
- import chalk from 'chalk';
12
- import ora from 'ora';
13
- import { DeSciXApiClient } from '../api-client.js';
14
- import { requireAuth } from '../auth-guard.js';
15
- import { GlobalConfig } from '../global-config.js';
16
-
17
- /**
18
- * Extract folder ID from Drive URL or return as-is
19
- */
20
- function extractFolderId(input) {
21
- if (!input) return null;
22
-
23
- const trimmed = input.trim();
24
-
25
- // Handle full URLs
26
- if (trimmed.includes('drive.google.com')) {
27
- const foldersMatch = trimmed.match(/\/folders\/([a-zA-Z0-9_-]+)/);
28
- if (foldersMatch && foldersMatch[1]) return foldersMatch[1];
29
-
30
- const dMatch = trimmed.match(/\/d\/([a-zA-Z0-9_-]+)/);
31
- if (dMatch && dMatch[1]) return dMatch[1];
32
-
33
- const idMatch = trimmed.match(/[?&]id=([a-zA-Z0-9_-]+)/);
34
- if (idMatch && idMatch[1]) return idMatch[1];
35
- }
36
-
37
- return trimmed;
38
- }
39
-
40
- /**
41
- * Set folder ID for an entity
42
- */
43
- export async function setFolder(options) {
44
- const apiClient = new DeSciXApiClient();
45
- await requireAuth(apiClient);
46
-
47
- const spinner = ora('Setting folder...').start();
48
-
49
- try {
50
- let entityType, entityIds, folderId;
51
-
52
- if (options.user) {
53
- entityType = 'user';
54
- folderId = extractFolderId(options.user);
55
- } else if (options.community) {
56
- entityType = 'community';
57
- entityIds = { community_id: options.community };
58
- folderId = extractFolderId(options.folderId);
59
- } else if (options.app) {
60
- entityType = 'app';
61
- // Parse "community app" format
62
- const parts = options.app.split(/\s+/);
63
- if (parts.length < 2) {
64
- throw new Error('App requires both community and app ID: --app "community app"');
65
- }
66
- entityIds = { community_id: parts[0], app_id: parts[1] };
67
- folderId = extractFolderId(options.folderId);
68
- } else if (options.kb) {
69
- entityType = 'kb';
70
- // Parse "community app kb" format
71
- const parts = options.kb.split(/\s+/);
72
- if (parts.length < 3) {
73
- throw new Error('KB requires community, app, and kb ID: --kb "community app kb"');
74
- }
75
- entityIds = { community_id: parts[0], app_id: parts[1], kb_id: parts[2] };
76
- folderId = extractFolderId(options.folderId);
77
- } else {
78
- throw new Error('Specify entity type: --user, --community, --app, or --kb');
79
- }
80
-
81
- if (!folderId) {
82
- throw new Error('Folder ID is required');
83
- }
84
-
85
- const response = await apiClient.invoke('set_folder_id', {
86
- entity_type: entityType,
87
- entity_ids: entityIds,
88
- folder_id: folderId
89
- });
90
-
91
- const result = response.message || response;
92
-
93
- spinner.succeed('Folder set successfully');
94
-
95
- console.log(chalk.green(`\n✓ ${result.message}`));
96
- console.log(chalk.gray(` Entity: ${entityType}`));
97
- console.log(chalk.gray(` Folder: ${folderId}`));
98
- console.log(chalk.cyan(` URL: https://drive.google.com/drive/folders/${folderId}\n`));
99
-
100
- // Update global config if setting user folder
101
- if (entityType === 'user') {
102
- const globalConfig = await GlobalConfig.load();
103
- globalConfig.user_base_folder_id = folderId;
104
- globalConfig.registered_at = new Date().toISOString();
105
- await globalConfig.save();
106
- }
107
-
108
- return result;
109
-
110
- } catch (error) {
111
- spinner.fail('Failed to set folder');
112
- console.error(chalk.red(`\n❌ ${error.message}\n`));
113
- throw error;
114
- }
115
- }
116
-
117
- /**
118
- * Get folder info for an entity
119
- */
120
- export async function getFolder(options) {
121
- const apiClient = new DeSciXApiClient();
122
- await requireAuth(apiClient);
123
-
124
- const spinner = ora('Getting folder info...').start();
125
-
126
- try {
127
- let entityType, entityIds;
128
-
129
- if (options.user) {
130
- entityType = 'user';
131
- } else if (options.community) {
132
- entityType = 'community';
133
- entityIds = { community_id: options.community };
134
- } else if (options.app) {
135
- entityType = 'app';
136
- const parts = options.app.split(/\s+/);
137
- if (parts.length < 2) {
138
- throw new Error('App requires both community and app ID: --app "community app"');
139
- }
140
- entityIds = { community_id: parts[0], app_id: parts[1] };
141
- } else if (options.kb) {
142
- entityType = 'kb';
143
- const parts = options.kb.split(/\s+/);
144
- if (parts.length < 3) {
145
- throw new Error('KB requires community, app, and kb ID: --kb "community app kb"');
146
- }
147
- entityIds = { community_id: parts[0], app_id: parts[1], kb_id: parts[2] };
148
- } else {
149
- throw new Error('Specify entity type: --user, --community, --app, or --kb');
150
- }
151
-
152
- const response = await apiClient.invoke('get_folder_info', {
153
- entity_type: entityType,
154
- entity_ids: entityIds
155
- });
156
-
157
- const result = response.message || response;
158
-
159
- spinner.succeed('Folder info retrieved');
160
-
161
- console.log(chalk.cyan(`\n📁 Folder Info: ${entityType}\n`));
162
-
163
- if (result.has_folder) {
164
- console.log(chalk.green(` ✓ Folder registered`));
165
- console.log(chalk.white(` ID: ${result.folder_id}`));
166
- if (result.folder_name) {
167
- console.log(chalk.white(` Name: ${result.folder_name}`));
168
- }
169
- if (result.kb_folder_id) {
170
- console.log(chalk.white(` KB Folder: ${result.kb_folder_id}`));
171
- }
172
- console.log(chalk.cyan(` URL: https://drive.google.com/drive/folders/${result.folder_id}`));
173
- } else {
174
- console.log(chalk.yellow(` ⚠ No folder registered`));
175
- console.log(chalk.gray(` Use 'descix folder set' to register a folder`));
176
- }
177
-
178
- console.log('');
179
- return result;
180
-
181
- } catch (error) {
182
- spinner.fail('Failed to get folder info');
183
- console.error(chalk.red(`\n❌ ${error.message}\n`));
184
- throw error;
185
- }
186
- }
187
-
188
- /**
189
- * Allocate a new folder for an entity
190
- */
191
- export async function allocateFolder(options) {
192
- const apiClient = new DeSciXApiClient();
193
- await requireAuth(apiClient);
194
-
195
- const spinner = ora('Allocating folder...').start();
196
-
197
- try {
198
- let entityType, entityIds, folderName;
199
-
200
- if (options.app) {
201
- entityType = 'app';
202
- const parts = options.app.split(/\s+/);
203
- if (parts.length < 2) {
204
- throw new Error('App requires both community and app ID: --app "community app"');
205
- }
206
- entityIds = { community_id: parts[0], app_id: parts[1] };
207
- folderName = options.name || `${parts[0]}_${parts[1]}`;
208
- } else if (options.kb) {
209
- entityType = 'kb';
210
- const parts = options.kb.split(/\s+/);
211
- if (parts.length < 3) {
212
- throw new Error('KB requires community, app, and kb ID: --kb "community app kb"');
213
- }
214
- entityIds = { community_id: parts[0], app_id: parts[1], kb_id: parts[2] };
215
- folderName = options.name || parts[2];
216
- } else {
217
- throw new Error('Specify entity type: --app or --kb');
218
- }
219
-
220
- const response = await apiClient.invoke('allocate_folder', {
221
- entity_type: entityType,
222
- entity_ids: entityIds,
223
- folder_name: folderName
224
- });
225
-
226
- const result = response.message || response;
227
-
228
- spinner.succeed('Folder allocated successfully');
229
-
230
- console.log(chalk.green(`\n✓ New folder created`));
231
- console.log(chalk.white(` Name: ${result.folder_name}`));
232
- console.log(chalk.white(` ID: ${result.folder_id}`));
233
- console.log(chalk.cyan(` URL: ${result.drive_url}\n`));
234
-
235
- return result;
236
-
237
- } catch (error) {
238
- spinner.fail('Failed to allocate folder');
239
- console.error(chalk.red(`\n❌ ${error.message}\n`));
240
- throw error;
241
- }
242
- }
243
-
244
- /**
245
- * Validate folder access
246
- */
247
- export async function validateFolder(folderId, options = {}) {
248
- const apiClient = new DeSciXApiClient();
249
- await requireAuth(apiClient);
250
-
251
- const spinner = ora('Validating folder...').start();
252
-
253
- try {
254
- const parsedFolderId = extractFolderId(folderId);
255
-
256
- if (!parsedFolderId) {
257
- throw new Error('Folder ID is required');
258
- }
259
-
260
- const response = await apiClient.invoke('validate_folder_access', {
261
- folder_id: parsedFolderId
262
- });
263
-
264
- const result = response.message || response;
265
-
266
- if (result.accessible) {
267
- spinner.succeed('Folder is accessible');
268
- console.log(chalk.green(`\n✓ ${result.message}`));
269
- console.log(chalk.white(` Name: ${result.folder_name}`));
270
- console.log(chalk.white(` ID: ${parsedFolderId}`));
271
- console.log(chalk.cyan(` URL: https://drive.google.com/drive/folders/${parsedFolderId}\n`));
272
- } else {
273
- spinner.fail('Folder not accessible');
274
- console.log(chalk.red(`\n❌ ${result.message}\n`));
275
- }
276
-
277
- return result;
278
-
279
- } catch (error) {
280
- spinner.fail('Validation failed');
281
- console.error(chalk.red(`\n❌ ${error.message}\n`));
282
- throw error;
283
- }
284
- }
285
-
286
- export default {
287
- setFolder,
288
- getFolder,
289
- allocateFolder,
290
- validateFolder
291
- };
@@ -1,335 +0,0 @@
1
- /**
2
- * PathContext - Unified filesystem context for DeSciX CLI
3
- *
4
- * Design principle: No "finding" after setup.
5
- * - Setup time: Detect IDE markers, persist absolute paths to workspace.json
6
- * - Runtime: Load workspace.json, all paths are simple lookups
7
- *
8
- * The workspace.json is the single source of truth for all paths.
9
- */
10
-
11
- import * as fs from 'fs/promises';
12
- import * as path from 'path';
13
-
14
- /**
15
- * Find IDE workspace root by searching for .cursor/ or .vscode/ markers
16
- * This is the only "searching" allowed - IDE markers are guaranteed in Cursor/VSCode
17
- *
18
- * @param {string} startDir - Directory to start searching from
19
- * @returns {Promise<string|null>} Workspace root path, or null if not in IDE workspace
20
- */
21
- export async function findIDEMarker(startDir = process.cwd()) {
22
- let currentDir = path.resolve(startDir);
23
- const root = path.parse(currentDir).root;
24
-
25
- // Only look for IDE markers (not .descix - that's created by setup)
26
- const ideMarkers = ['.cursor', '.vscode'];
27
-
28
- while (currentDir !== root) {
29
- for (const marker of ideMarkers) {
30
- const markerDir = path.join(currentDir, marker);
31
- try {
32
- const stat = await fs.stat(markerDir);
33
- if (stat.isDirectory()) {
34
- return currentDir;
35
- }
36
- } catch {
37
- // Marker not found, continue
38
- }
39
- }
40
-
41
- const parentDir = path.dirname(currentDir);
42
- if (parentDir === currentDir) break;
43
- currentDir = parentDir;
44
- }
45
-
46
- return null;
47
- }
48
-
49
- /**
50
- * PathContext - Unified path resolution from workspace.json
51
- *
52
- * All paths come from workspace.json - no runtime "finding" of app contexts.
53
- */
54
- export class PathContext {
55
- /**
56
- * @param {string} workspaceRoot - Absolute path to workspace root
57
- * @param {Object} config - Parsed workspace.json contents
58
- */
59
- constructor(workspaceRoot, config) {
60
- this.workspaceRoot = workspaceRoot;
61
- this.config = config;
62
- }
63
-
64
- /**
65
- * Load PathContext from workspace.json
66
- *
67
- * @param {string} startDir - Directory to start searching from (default: cwd)
68
- * @returns {Promise<PathContext>} Loaded context
69
- * @throws {Error} If not in IDE workspace or workspace not configured
70
- */
71
- static async load(startDir = process.cwd()) {
72
- // Find IDE marker (only .cursor/ or .vscode/)
73
- const wsRoot = await findIDEMarker(startDir);
74
- if (!wsRoot) {
75
- throw new Error(
76
- 'Not in a Cursor/VSCode workspace.\n' +
77
- 'Open this folder in Cursor or VSCode first.'
78
- );
79
- }
80
-
81
- // Load workspace.json
82
- const configPath = path.join(wsRoot, '.descix', 'workspace.json');
83
- let configData;
84
- try {
85
- const content = await fs.readFile(configPath, 'utf-8');
86
- configData = JSON.parse(content);
87
- } catch (error) {
88
- throw new Error(
89
- 'Workspace not configured.\n' +
90
- 'Run "npx descix init" first to configure your workspace.'
91
- );
92
- }
93
-
94
- return new PathContext(wsRoot, configData);
95
- }
96
-
97
- /**
98
- * Try to load PathContext, return null if not configured
99
- * Useful for commands that need to check if workspace exists
100
- *
101
- * @param {string} startDir - Directory to start searching from
102
- * @returns {Promise<PathContext|null>}
103
- */
104
- static async tryLoad(startDir = process.cwd()) {
105
- try {
106
- return await PathContext.load(startDir);
107
- } catch {
108
- return null;
109
- }
110
- }
111
-
112
- // ============ Path Getters (all from workspace.json) ============
113
-
114
- /**
115
- * Get workspace root path
116
- * @returns {string} Absolute path to workspace root
117
- */
118
- getWorkspaceRoot() {
119
- return this.config.workspaceRoot || this.workspaceRoot;
120
- }
121
-
122
- /**
123
- * Get community path
124
- * @param {string} communityId - Community identifier
125
- * @returns {string|null} Absolute path to community folder, or null if not found
126
- */
127
- getCommunityPath(communityId) {
128
- const community = this.config.communities?.[communityId];
129
- if (!community) return null;
130
-
131
- // Use absolutePath if available, otherwise compute from localPath
132
- if (community.absolutePath) {
133
- return community.absolutePath;
134
- }
135
- if (community.localPath) {
136
- return path.join(this.getWorkspaceRoot(), community.localPath);
137
- }
138
- return null;
139
- }
140
-
141
- /**
142
- * Get app path
143
- * @param {string} communityId - Community identifier
144
- * @param {string} appId - App identifier
145
- * @returns {string|null} Absolute path to app folder, or null if not found
146
- */
147
- getAppPath(communityId, appId) {
148
- const app = this.config.communities?.[communityId]?.apps?.[appId];
149
- if (!app) return null;
150
-
151
- // Use absolutePath if available, otherwise compute from localPath
152
- if (app.absolutePath) {
153
- return app.absolutePath;
154
- }
155
- if (app.localPath) {
156
- return path.join(this.getWorkspaceRoot(), app.localPath);
157
- }
158
- return null;
159
- }
160
-
161
- /**
162
- * Get KB path
163
- * @param {string} communityId - Community identifier
164
- * @param {string} appId - App identifier
165
- * @param {string} kbId - KB identifier (default: 'General')
166
- * @returns {string|null} Absolute path to KB folder, or null if app not found
167
- */
168
- getKbPath(communityId, appId, kbId = 'General') {
169
- const appPath = this.getAppPath(communityId, appId);
170
- return appPath ? path.join(appPath, 'kb', kbId) : null;
171
- }
172
-
173
- /**
174
- * Get site path
175
- * @param {string} communityId - Community identifier
176
- * @param {string} appId - App identifier
177
- * @returns {string|null} Absolute path to site folder, or null if app not found
178
- */
179
- getSitePath(communityId, appId) {
180
- const appPath = this.getAppPath(communityId, appId);
181
- return appPath ? path.join(appPath, 'site') : null;
182
- }
183
-
184
- /**
185
- * Get microservice path
186
- * @param {string} communityId - Community identifier
187
- * @param {string} appId - App identifier
188
- * @returns {string|null} Absolute path to microservice folder, or null if app not found
189
- */
190
- getMicroservicePath(communityId, appId) {
191
- const appPath = this.getAppPath(communityId, appId);
192
- return appPath ? path.join(appPath, 'microservice') : null;
193
- }
194
-
195
- // ============ Context Detection (match cwd against known paths) ============
196
-
197
- /**
198
- * Detect community/app context from current working directory
199
- * Matches cwd against known absolutePaths in workspace.json
200
- *
201
- * @returns {{ communityId: string, appId: string, kbId: string }|null}
202
- */
203
- detectContext() {
204
- const cwd = process.cwd();
205
-
206
- for (const [commId, comm] of Object.entries(this.config.communities || {})) {
207
- for (const [appId, app] of Object.entries(comm.apps || {})) {
208
- const appPath = app.absolutePath ||
209
- (app.localPath ? path.join(this.getWorkspaceRoot(), app.localPath) : null);
210
-
211
- if (appPath && cwd.startsWith(appPath)) {
212
- return {
213
- communityId: commId,
214
- appId,
215
- kbId: app.kbId || 'General'
216
- };
217
- }
218
- }
219
- }
220
-
221
- return null;
222
- }
223
-
224
- /**
225
- * Resolve context by merging CLI options with detected context
226
- * CLI options take priority over detected context
227
- *
228
- * @param {Object} options - CLI options { community, app, kb }
229
- * @returns {{ communityId: string|null, appId: string|null, kbId: string }}
230
- */
231
- resolveContext(options = {}) {
232
- const detected = this.detectContext();
233
-
234
- const resolved = {
235
- communityId: options.community || detected?.communityId || null,
236
- appId: options.app || detected?.appId || null,
237
- kbId: options.kb || detected?.kbId || 'General'
238
- };
239
-
240
- return resolved;
241
- }
242
-
243
- /**
244
- * Resolve context and throw if community/app not determined
245
- *
246
- * @param {Object} options - CLI options { community, app, kb }
247
- * @returns {{ communityId: string, appId: string, kbId: string }}
248
- * @throws {Error} If community or app cannot be determined
249
- */
250
- requireContext(options = {}) {
251
- const ctx = this.resolveContext(options);
252
-
253
- if (!ctx.communityId || !ctx.appId) {
254
- throw new Error(
255
- 'Could not determine app context.\n\n' +
256
- 'Options:\n' +
257
- ' 1. cd into an app directory\n' +
258
- ' 2. Use flags: -c <community> -a <app>\n\n' +
259
- 'Example: npx descix kb build -c descix -a appsdk'
260
- );
261
- }
262
-
263
- return ctx;
264
- }
265
-
266
- // ============ Config Access ============
267
-
268
- /**
269
- * Get raw workspace.json config
270
- * @returns {Object}
271
- */
272
- getWorkspaceConfig() {
273
- return this.config;
274
- }
275
-
276
- /**
277
- * Get Drive configuration
278
- * @returns {{ base_folder_id: string }|null}
279
- */
280
- getDriveConfig() {
281
- return this.config.driveConfig || null;
282
- }
283
-
284
- /**
285
- * Get API URL
286
- * Derives from env.platform.microservice.port in DEV mode (v2.1 workspace format).
287
- * @returns {string}
288
- */
289
- getApiUrl() {
290
- if (this.config.apiUrl) return this.config.apiUrl;
291
- const env = this.config.env || {};
292
- const platformPort = env.platform?.microservice?.port;
293
- if (platformPort && env.environment === 'DEV') return `https://localhost:${platformPort}`;
294
- return 'https://descix.net';
295
- }
296
-
297
- /**
298
- * Get environment
299
- * @returns {string}
300
- */
301
- getEnvironment() {
302
- return this.config.environment || 'production';
303
- }
304
-
305
- // ============ Listing ============
306
-
307
- /**
308
- * List all community IDs
309
- * @returns {string[]}
310
- */
311
- listCommunities() {
312
- return Object.keys(this.config.communities || {});
313
- }
314
-
315
- /**
316
- * List all app IDs (optionally filtered by community)
317
- * @param {string} communityId - Optional community filter
318
- * @returns {Array<{ communityId: string, appId: string }>}
319
- */
320
- listApps(communityId = null) {
321
- const apps = [];
322
-
323
- for (const [commId, comm] of Object.entries(this.config.communities || {})) {
324
- if (communityId && commId !== communityId) continue;
325
-
326
- for (const appId of Object.keys(comm.apps || {})) {
327
- apps.push({ communityId: commId, appId });
328
- }
329
- }
330
-
331
- return apps;
332
- }
333
- }
334
-
335
- export default PathContext;
@@ -1,5 +0,0 @@
1
- # Local dev overrides - DO NOT COMMIT
2
- # Rename to dev-overrides.json
3
- {
4
- "SERVICE_SECRET": "local-dev-secret"
5
- }
@@ -1,62 +0,0 @@
1
- /**
2
- * DeSciXAppSDK.js (V2 - App Shell Edition)
3
- *
4
- * A thin wrapper for CodeSites and ChatWidgets running within the DeSciX PWA.
5
- * Leverages Same-Origin interframe scripting to provide direct access to
6
- * PWA helpers and the backend API.
7
- */
8
-
9
- window.DeSciX = (() => {
10
- const isEmbedded = window.top !== window;
11
-
12
- // Check for same-origin access
13
- const hasParentAccess = () => {
14
- try {
15
- return isEmbedded && !!window.top.DeSciX;
16
- } catch (e) {
17
- return false;
18
- }
19
- };
20
-
21
- if (!hasParentAccess()) {
22
- console.warn('[DeSciX SDK] Running in standalone mode or cross-origin. Direct PWA helpers unavailable.');
23
- return {
24
- mode: 'standalone',
25
- call: async (command, params) => {
26
- throw new Error('SDK not initialized in PWA App Shell mode.');
27
- }
28
- };
29
- }
30
-
31
- console.log('[DeSciX SDK] Initialized in App Shell mode.');
32
-
33
- // Proxy to the PWA's global DeSciX object
34
- return {
35
- mode: 'shell',
36
-
37
- /**
38
- * Access to global application data (Communities, Apps, User Profile)
39
- */
40
- get AppData() { return window.top.DeSciX.AppData; },
41
-
42
- /**
43
- * Access to the PWA state machine and navigation
44
- */
45
- get AppContext() { return window.top.DeSciX.AppContext; },
46
-
47
- /**
48
- * Direct API call helper (proxied through PWA session)
49
- */
50
- call: (command, params) => window.top.DeSciX.call(command, params),
51
-
52
- /**
53
- * Chat interaction helpers
54
- */
55
- Chat: {
56
- suggestPrompt: (text) => {
57
- // Future: trigger text injection into ChatWidget
58
- console.log('[DeSciX SDK] Suggesting prompt:', text);
59
- }
60
- }
61
- };
62
- })();