@descix/cli 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.
- package/README.md +95 -0
- package/SERVICE_README_sdk.md +399 -0
- package/agent-assets/guides/README.md +61 -0
- package/agent-assets/guides/dynamic-site-pattern.md +125 -0
- package/agent-assets/guides/knowledge-only-pattern.md +93 -0
- package/agent-assets/guides/microservice-pattern.md +152 -0
- package/agent-assets/guides/static-site-pattern.md +111 -0
- package/agent-assets/instructions/app-asset-validation.md +477 -0
- package/agent-assets/instructions/onboarding-flow.md +408 -0
- package/agent-assets/instructions/standard-folder-structure.md +125 -0
- package/agent-assets/reference/app-creation.md +381 -0
- package/agent-assets/reference/architecture.md +406 -0
- package/agent-assets/reference/local-dev.md +138 -0
- package/agent-assets/reference/mcp-integration.md +424 -0
- package/agent-assets/reference/workspace-config.md +415 -0
- package/agent-assets/rules/descix_mcp.template.mdc +243 -0
- package/bin/descix.js +3930 -0
- package/bin/mcp-server.js +382 -0
- package/descix-cli-1.0.0.tgz +0 -0
- package/lib/agent-files.js +162 -0
- package/lib/api-client.js +458 -0
- package/lib/auth-guard.js +67 -0
- package/lib/commands/app-wizard.js +627 -0
- package/lib/commands/auth.js +407 -0
- package/lib/commands/buy.js +207 -0
- package/lib/commands/clone.js +91 -0
- package/lib/commands/config.js +155 -0
- package/lib/commands/doctor.js +277 -0
- package/lib/commands/folder.js +291 -0
- package/lib/commands/init.js +257 -0
- package/lib/commands/kb.js +654 -0
- package/lib/commands/mcp.js +273 -0
- package/lib/commands/serve.js +12 -0
- package/lib/commands/status.js +239 -0
- package/lib/commands/update.js +653 -0
- package/lib/core/Chunker.js +610 -0
- package/lib/core/Hydrator.js +801 -0
- package/lib/core/PathContext.js +335 -0
- package/lib/core/Syncer.js +310 -0
- package/lib/core/index.js +32 -0
- package/lib/global-config.js +86 -0
- package/lib/google-storage-adc.js +511 -0
- package/lib/wallet-file.js +232 -0
- package/lib/wizard/setup.js +502 -0
- package/lib/workspace-config.js +712 -0
- package/lib/workspace-utils.js +126 -0
- package/package.json +41 -0
- package/templates/agent-claude.md +226 -0
- package/templates/agent-cline.md +133 -0
- package/templates/agent-copilot.md +133 -0
- package/templates/agent-cursor.md +133 -0
- package/templates/descix_mcp.mdc +122 -0
- package/templates/drive/README.md +42 -0
- package/templates/drive/app/assets/app_description.md +13 -0
- package/templates/drive/app/assets/icon.png +0 -0
- package/templates/drive/app/assets/system_instructions.md +17 -0
- package/templates/drive/app/kb/General/README.md +25 -0
- package/templates/drive/community/Apps/.gitkeep +4 -0
- package/templates/drive/community/community_assets/community_description.md +15 -0
- package/templates/drive/community/community_assets/icon.png +0 -0
- package/templates/drive_templates/README.md +104 -0
- package/templates/drive_templates/agent_app_template/assets/app_description.txt +13 -0
- package/templates/drive_templates/agent_app_template/assets/system_instructions.txt +17 -0
- package/templates/drive_templates/agent_app_template/kb/General/README.md +25 -0
- package/templates/drive_templates/agent_app_template/microservice/.gitkeep +10 -0
- package/templates/drive_templates/agent_app_template/site/.gitkeep +4 -0
- package/templates/drive_templates/community_template/Apps/.gitkeep +4 -0
- package/templates/drive_templates/community_template/community_assets/community_description.txt +15 -0
- package/templates/scaffolds/README.md +62 -0
- package/templates/scaffolds/microservice/Dockerfile +19 -0
- package/templates/scaffolds/microservice/SERVICE_README_sdk.md +399 -0
- package/templates/scaffolds/microservice/app.js +50 -0
- package/templates/scaffolds/microservice/app.yaml +8 -0
- package/templates/scaffolds/microservice/defaults-config.json +5 -0
- package/templates/scaffolds/microservice/dev-overrides.json.example +5 -0
- package/templates/scaffolds/microservice/manifest.json +49 -0
- package/templates/scaffolds/microservice/package-lock.json +2645 -0
- package/templates/scaffolds/microservice/package.json +29 -0
- package/templates/scaffolds/microservice/scripts/register.js +55 -0
- package/templates/scaffolds/microservice/services/apiFront.js +75 -0
- package/templates/scaffolds/microservice/services/commandHandlers/sampleCommands.js +57 -0
- package/templates/scaffolds/microservice/services/mcpClient.js +78 -0
- package/templates/scaffolds/microservice/services/utils.js +247 -0
- package/templates/scaffolds/microservice/setup-schema.json +18 -0
- package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +143 -0
- package/templates/scaffolds/site/DeSciXAppSDK.js +62 -0
- package/templates/scaffolds/site/README.md +51 -0
- package/templates/scaffolds/site/app.js +22 -0
- package/templates/scaffolds/site/index.html +42 -0
- package/templates/scaffolds/site/styles.css +123 -0
- package/test-hydration.js +150 -0
- package/vendor/mcp/transports/http-sse-transport.js +196 -0
- package/vendor/mcp/transports/index.js +15 -0
- package/vendor/mcp/transports/stdio-transport.js +118 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# {Service Name} Service
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
{One paragraph description of what this service does and when to use it.
|
|
6
|
+
Include the key use cases, benefits, and target users.
|
|
7
|
+
This section is critical for tell_me_how discovery - be descriptive about WHEN to use this service.}
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- {Any setup requirements}
|
|
12
|
+
- {Required permissions or app purchases}
|
|
13
|
+
- {Environment configuration}
|
|
14
|
+
|
|
15
|
+
## Available Commands
|
|
16
|
+
|
|
17
|
+
### {command_name}
|
|
18
|
+
|
|
19
|
+
**Description:** {Detailed description of what this command does}
|
|
20
|
+
|
|
21
|
+
**Use when:** {Specific scenarios when this command should be used. This helps tell_me_how match user questions to the right tool.}
|
|
22
|
+
|
|
23
|
+
**Prerequisites:**
|
|
24
|
+
- {What must be true before calling this command}
|
|
25
|
+
- {Required prior steps or configurations}
|
|
26
|
+
|
|
27
|
+
**Parameters:**
|
|
28
|
+
- `param1` (required, string): {Description of this parameter}
|
|
29
|
+
- `param2` (optional, number): {Description with default value if any}
|
|
30
|
+
- `param3` (optional, object): {Description of nested structure}
|
|
31
|
+
|
|
32
|
+
**Example Request:**
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"command": "{command_name}",
|
|
36
|
+
"params": {
|
|
37
|
+
"param1": "example_value",
|
|
38
|
+
"param2": 42
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Example Response:**
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"status": "OK",
|
|
47
|
+
"message": {
|
|
48
|
+
"result_field": "value",
|
|
49
|
+
"another_field": 123
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Errors:**
|
|
55
|
+
- `{ERROR_CODE}`: {When this error occurs and how to fix it}
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### {another_command_name}
|
|
60
|
+
|
|
61
|
+
**Description:** {Description}
|
|
62
|
+
|
|
63
|
+
**Use when:** {Scenarios}
|
|
64
|
+
|
|
65
|
+
**Prerequisites:**
|
|
66
|
+
- {Requirements}
|
|
67
|
+
|
|
68
|
+
**Parameters:**
|
|
69
|
+
- `param1` (required, string): {Description}
|
|
70
|
+
|
|
71
|
+
**Example Request:**
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"command": "{another_command_name}",
|
|
75
|
+
"params": {
|
|
76
|
+
"param1": "value"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Common Workflows
|
|
84
|
+
|
|
85
|
+
### Workflow: {Workflow Name}
|
|
86
|
+
|
|
87
|
+
{Description of a common multi-step process using this service's commands}
|
|
88
|
+
|
|
89
|
+
1. **Step 1:** Call `{first_command}` to {action}
|
|
90
|
+
2. **Step 2:** Call `{second_command}` to {action}
|
|
91
|
+
3. **Step 3:** Verify with `{third_command}`
|
|
92
|
+
|
|
93
|
+
**Example:**
|
|
94
|
+
```bash
|
|
95
|
+
# Using the CLI
|
|
96
|
+
descix service call {command_name} --param1 value
|
|
97
|
+
|
|
98
|
+
# Or via tell_me_how
|
|
99
|
+
tell_me_how("I want to {describe the workflow goal}")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Integration with Other Services
|
|
103
|
+
|
|
104
|
+
{Describe how this service works with other DeSciX services}
|
|
105
|
+
|
|
106
|
+
- **RAG Integration:** {If this service uses knowledge bases}
|
|
107
|
+
- **Token Economy:** {If this service interacts with tokenomics}
|
|
108
|
+
- **Other Services:** {Any service-to-service communication}
|
|
109
|
+
|
|
110
|
+
## Troubleshooting
|
|
111
|
+
|
|
112
|
+
### Common Errors
|
|
113
|
+
|
|
114
|
+
**Error: `{Error message}`**
|
|
115
|
+
- **Cause:** {Why this happens}
|
|
116
|
+
- **Solution:** {How to fix it}
|
|
117
|
+
|
|
118
|
+
**Error: `{Another error}`**
|
|
119
|
+
- **Cause:** {Why this happens}
|
|
120
|
+
- **Solution:** {How to fix it}
|
|
121
|
+
|
|
122
|
+
### Getting Help
|
|
123
|
+
|
|
124
|
+
If you encounter issues:
|
|
125
|
+
1. Use `tell_me_how` to find related documentation
|
|
126
|
+
2. Check the app's knowledge base for detailed guides
|
|
127
|
+
3. Contact community support
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Service Metadata
|
|
132
|
+
|
|
133
|
+
| Field | Value |
|
|
134
|
+
|-------|-------|
|
|
135
|
+
| Service Name | {service_name} |
|
|
136
|
+
| Version | {version} |
|
|
137
|
+
| Community | {community_id} |
|
|
138
|
+
| App | {app_id} |
|
|
139
|
+
| Commands | {command_count} |
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
*This README is vectorized for `tell_me_how` discovery. Keep it up to date with your service's capabilities.*
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
})();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Site Folder
|
|
2
|
+
|
|
3
|
+
This folder contains your app's static site files that are deployed to the web.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
site/
|
|
9
|
+
├── index.html # Main entry point
|
|
10
|
+
├── styles.css # Stylesheet
|
|
11
|
+
├── app.js # Client-side JavaScript
|
|
12
|
+
└── README.md # This file
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Deployment
|
|
16
|
+
|
|
17
|
+
Your site is deployed when you run:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
descix site upload
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The site files are uploaded to Google Cloud Storage and served within the DeSciX PWA.
|
|
24
|
+
|
|
25
|
+
## Using the DeSciX SDK
|
|
26
|
+
|
|
27
|
+
The DeSciX SDK is automatically injected into your CodeSite when running within the PWA App Shell. You can access it via the global `DeSciX` object.
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// 1. Access global application data
|
|
31
|
+
const stats = await DeSciX.AppData.getCommunityStats();
|
|
32
|
+
|
|
33
|
+
// 2. Call DeSciX API (uses parent PWA session)
|
|
34
|
+
const result = await DeSciX.call('ask_question_to_app', {
|
|
35
|
+
community_id: 'egpt',
|
|
36
|
+
app_id: 'agent',
|
|
37
|
+
user_input: 'Explain the platform architecture'
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Adding More Files
|
|
42
|
+
|
|
43
|
+
Add any static assets (images, fonts, additional JS/CSS) to this folder. All files in `site/` are deployed together.
|
|
44
|
+
|
|
45
|
+
## Local Development
|
|
46
|
+
|
|
47
|
+
To test your site within the DeSciX PWA, use the `servelocal` command:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
descix site servelocal 3000
|
|
51
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{APP_NAME}} - Site JavaScript
|
|
3
|
+
*
|
|
4
|
+
* Add your client-side logic here.
|
|
5
|
+
*
|
|
6
|
+
* For DeSciX SDK integration, use the global DeSciX object:
|
|
7
|
+
*
|
|
8
|
+
* Example:
|
|
9
|
+
* const stats = await DeSciX.AppData.getCommunityStats();
|
|
10
|
+
* console.log('My Community Stats:', stats);
|
|
11
|
+
*
|
|
12
|
+
* // Call backend API directly
|
|
13
|
+
* const result = await DeSciX.call('get_app', { app_id: 'myapp' });
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
17
|
+
console.log('{{APP_NAME}} site loaded');
|
|
18
|
+
|
|
19
|
+
if (window.DeSciX) {
|
|
20
|
+
console.log('[App] DeSciX SDK detected. Mode:', window.DeSciX.mode);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{APP_NAME}}</title>
|
|
7
|
+
<link rel="stylesheet" href="styles.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<header>
|
|
12
|
+
<h1>{{APP_NAME}}</h1>
|
|
13
|
+
<p class="tagline">Your DeSciX App</p>
|
|
14
|
+
</header>
|
|
15
|
+
|
|
16
|
+
<main>
|
|
17
|
+
<section class="hero">
|
|
18
|
+
<h2>Welcome</h2>
|
|
19
|
+
<p>This is your app's static site. Edit this page to create your user interface.</p>
|
|
20
|
+
</section>
|
|
21
|
+
|
|
22
|
+
<section class="features">
|
|
23
|
+
<h3>Getting Started</h3>
|
|
24
|
+
<ul>
|
|
25
|
+
<li>Edit <code>index.html</code> to customize your page</li>
|
|
26
|
+
<li>Add styles in <code>styles.css</code></li>
|
|
27
|
+
<li>Add JavaScript in <code>app.js</code></li>
|
|
28
|
+
<li>Deploy with <code>descix kb sync</code></li>
|
|
29
|
+
</ul>
|
|
30
|
+
</section>
|
|
31
|
+
</main>
|
|
32
|
+
|
|
33
|
+
<footer>
|
|
34
|
+
<p>Powered by <a href="https://descix.net">DeSciX</a></p>
|
|
35
|
+
</footer>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- DeSciX App SDK -->
|
|
39
|
+
<script src="DeSciXAppSDK.js"></script>
|
|
40
|
+
<script src="app.js"></script>
|
|
41
|
+
</body>
|
|
42
|
+
</html>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/* DeSciX App Styles */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--primary-color: #6366f1;
|
|
5
|
+
--background-color: #f8fafc;
|
|
6
|
+
--text-color: #1e293b;
|
|
7
|
+
--text-muted: #64748b;
|
|
8
|
+
--border-color: #e2e8f0;
|
|
9
|
+
--card-background: #ffffff;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
* {
|
|
13
|
+
margin: 0;
|
|
14
|
+
padding: 0;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
20
|
+
background-color: var(--background-color);
|
|
21
|
+
color: var(--text-color);
|
|
22
|
+
line-height: 1.6;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.container {
|
|
26
|
+
max-width: 800px;
|
|
27
|
+
margin: 0 auto;
|
|
28
|
+
padding: 2rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
header {
|
|
32
|
+
text-align: center;
|
|
33
|
+
margin-bottom: 3rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
header h1 {
|
|
37
|
+
font-size: 2.5rem;
|
|
38
|
+
color: var(--primary-color);
|
|
39
|
+
margin-bottom: 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.tagline {
|
|
43
|
+
color: var(--text-muted);
|
|
44
|
+
font-size: 1.1rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
main {
|
|
48
|
+
background: var(--card-background);
|
|
49
|
+
border-radius: 12px;
|
|
50
|
+
padding: 2rem;
|
|
51
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
section {
|
|
55
|
+
margin-bottom: 2rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
section:last-child {
|
|
59
|
+
margin-bottom: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
h2 {
|
|
63
|
+
font-size: 1.5rem;
|
|
64
|
+
margin-bottom: 1rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
h3 {
|
|
68
|
+
font-size: 1.2rem;
|
|
69
|
+
margin-bottom: 0.75rem;
|
|
70
|
+
color: var(--text-muted);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
ul {
|
|
74
|
+
list-style: none;
|
|
75
|
+
padding: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
li {
|
|
79
|
+
padding: 0.5rem 0;
|
|
80
|
+
border-bottom: 1px solid var(--border-color);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
li:last-child {
|
|
84
|
+
border-bottom: none;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
code {
|
|
88
|
+
background: var(--background-color);
|
|
89
|
+
padding: 0.2rem 0.4rem;
|
|
90
|
+
border-radius: 4px;
|
|
91
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
92
|
+
font-size: 0.9em;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
footer {
|
|
96
|
+
text-align: center;
|
|
97
|
+
margin-top: 3rem;
|
|
98
|
+
color: var(--text-muted);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
footer a {
|
|
102
|
+
color: var(--primary-color);
|
|
103
|
+
text-decoration: none;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
footer a:hover {
|
|
107
|
+
text-decoration: underline;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Responsive */
|
|
111
|
+
@media (max-width: 600px) {
|
|
112
|
+
.container {
|
|
113
|
+
padding: 1rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
header h1 {
|
|
117
|
+
font-size: 2rem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
main {
|
|
121
|
+
padding: 1.5rem;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test Drive Hydration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { google } from 'googleapis';
|
|
7
|
+
import GoogleStorage from './lib/google-storage-adc.js';
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const DRIVE_FOLDER_MIME = 'application/vnd.google-apps.folder';
|
|
14
|
+
|
|
15
|
+
async function getAuth() {
|
|
16
|
+
return new google.auth.GoogleAuth({
|
|
17
|
+
scopes: ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file']
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function main() {
|
|
22
|
+
console.log('=== Drive Hydration Test ===\n');
|
|
23
|
+
|
|
24
|
+
// Step 1: Verify ADC
|
|
25
|
+
console.log('1. Verifying ADC authentication...');
|
|
26
|
+
const user = await GoogleStorage.verifyDriveAuth();
|
|
27
|
+
console.log(` ✓ Authenticated as: ${user.emailAddress}\n`);
|
|
28
|
+
|
|
29
|
+
// Step 2: Find DeSciX base folder
|
|
30
|
+
console.log('2. Looking for DeSciX base folder...');
|
|
31
|
+
const auth = await getAuth();
|
|
32
|
+
const drive = google.drive({ version: 'v3', auth });
|
|
33
|
+
|
|
34
|
+
const possibleNames = ['DeSciX', 'DeSciX_Apps', 'descix-apps', 'descix'];
|
|
35
|
+
let baseFolder = null;
|
|
36
|
+
|
|
37
|
+
for (const name of possibleNames) {
|
|
38
|
+
const res = await drive.files.list({
|
|
39
|
+
q: `name='${name}' and mimeType='${DRIVE_FOLDER_MIME}' and trashed=false`,
|
|
40
|
+
fields: 'files(id, name)'
|
|
41
|
+
});
|
|
42
|
+
if (res.data.files?.length) {
|
|
43
|
+
baseFolder = res.data.files[0];
|
|
44
|
+
console.log(` ✓ Found base folder: "${name}" (ID: ${baseFolder.id})\n`);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!baseFolder) {
|
|
50
|
+
console.log(' ✗ Could not find DeSciX base folder');
|
|
51
|
+
const rootRes = await drive.files.list({
|
|
52
|
+
q: `'root' in parents and mimeType='${DRIVE_FOLDER_MIME}' and trashed=false`,
|
|
53
|
+
fields: 'files(id, name)',
|
|
54
|
+
pageSize: 20
|
|
55
|
+
});
|
|
56
|
+
console.log(' Root folders:');
|
|
57
|
+
for (const f of rootRes.data.files || []) {
|
|
58
|
+
console.log(` - ${f.name} (${f.id})`);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Step 3: Find descix/agent
|
|
64
|
+
console.log('3. Locating descix/agent folder...');
|
|
65
|
+
const agentFolderId = await GoogleStorage.findFolderByPath(baseFolder.id, 'descix/agent');
|
|
66
|
+
|
|
67
|
+
if (!agentFolderId) {
|
|
68
|
+
console.log(' ✗ Could not find descix/agent folder');
|
|
69
|
+
console.log(' Contents of base folder:');
|
|
70
|
+
const contents = await GoogleStorage.listFolderWithMeta(baseFolder.id);
|
|
71
|
+
for (const f of contents) {
|
|
72
|
+
console.log(` - ${f.name} (${f.mimeType === DRIVE_FOLDER_MIME ? 'folder' : 'file'})`);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(` ✓ Found descix/agent folder (ID: ${agentFolderId})\n`);
|
|
78
|
+
|
|
79
|
+
// Step 4: List contents
|
|
80
|
+
console.log('4. Contents of descix/agent in Drive:');
|
|
81
|
+
const agentContents = await GoogleStorage.listFolderWithMeta(agentFolderId);
|
|
82
|
+
for (const f of agentContents) {
|
|
83
|
+
const type = f.mimeType === DRIVE_FOLDER_MIME ? '📁' : '📄';
|
|
84
|
+
console.log(` ${type} ${f.name}`);
|
|
85
|
+
|
|
86
|
+
// List subfolders
|
|
87
|
+
if (f.mimeType === DRIVE_FOLDER_MIME) {
|
|
88
|
+
const subContents = await GoogleStorage.listFolderWithMeta(f.id);
|
|
89
|
+
for (const sf of subContents) {
|
|
90
|
+
const subType = sf.mimeType === DRIVE_FOLDER_MIME ? '📁' : '📄';
|
|
91
|
+
console.log(` ${subType} ${sf.name}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log('');
|
|
96
|
+
|
|
97
|
+
// Step 5: Pull content using the new downloadFileFromDrive function
|
|
98
|
+
const targetDir = '/Users/essam/Documents/Code/DaitaERC20/descix/agent';
|
|
99
|
+
console.log(`5. Pulling content to ${targetDir}...`);
|
|
100
|
+
|
|
101
|
+
const pullRecursive = async (localPath, folderId, indent = ' ') => {
|
|
102
|
+
await fs.mkdir(localPath, { recursive: true });
|
|
103
|
+
const files = await GoogleStorage.listFolderWithMeta(folderId);
|
|
104
|
+
let count = 0;
|
|
105
|
+
|
|
106
|
+
for (const file of files) {
|
|
107
|
+
const targetPath = path.join(localPath, file.name);
|
|
108
|
+
|
|
109
|
+
if (file.mimeType === DRIVE_FOLDER_MIME) {
|
|
110
|
+
console.log(`${indent}📁 ${file.name}/`);
|
|
111
|
+
count += await pullRecursive(targetPath, file.id, indent + ' ');
|
|
112
|
+
} else {
|
|
113
|
+
console.log(`${indent}📄 Downloading: ${file.name}`);
|
|
114
|
+
await GoogleStorage.downloadFileFromDrive(file.id, targetPath);
|
|
115
|
+
count++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return count;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const downloaded = await pullRecursive(targetDir, agentFolderId);
|
|
122
|
+
console.log(`\n ✓ Downloaded ${downloaded} files\n`);
|
|
123
|
+
|
|
124
|
+
// Step 6: Verify
|
|
125
|
+
console.log('6. Verifying local content:');
|
|
126
|
+
const verifyDir = async (dir, indent = '') => {
|
|
127
|
+
try {
|
|
128
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
129
|
+
for (const e of entries) {
|
|
130
|
+
const type = e.isDirectory() ? '📁' : '📄';
|
|
131
|
+
console.log(` ${indent}${type} ${e.name}`);
|
|
132
|
+
if (e.isDirectory()) {
|
|
133
|
+
await verifyDir(path.join(dir, e.name), indent + ' ');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
console.log(` ${indent}(empty)`);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
await verifyDir(targetDir);
|
|
141
|
+
|
|
142
|
+
console.log('\n=== Test Complete ===');
|
|
143
|
+
console.log(`\nBase folder ID for workspace.json: ${baseFolder.id}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
main().catch(err => {
|
|
147
|
+
console.error('Error:', err.message);
|
|
148
|
+
console.error(err.stack);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
});
|