@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.
- package/LICENSE.md +671 -0
- package/README.md +43 -4
- package/agent-assets/guides/README.md +2 -2
- package/agent-assets/guides/dynamic-site-pattern.md +29 -14
- package/agent-assets/guides/knowledge-only-pattern.md +5 -5
- package/agent-assets/guides/microservice-pattern.md +48 -4
- package/agent-assets/guides/static-site-pattern.md +8 -0
- package/agent-assets/instructions/app-asset-validation.md +5 -5
- package/agent-assets/instructions/onboarding-flow.md +4 -4
- package/agent-assets/instructions/standard-folder-structure.md +20 -11
- package/agent-assets/reference/app-creation.md +42 -20
- package/agent-assets/reference/architecture.md +24 -22
- package/agent-assets/reference/local-dev.md +230 -67
- package/agent-assets/reference/mcp-integration.md +21 -15
- package/agent-assets/reference/platform-api-consumer.md +244 -0
- package/agent-assets/reference/workspace-config.md +7 -7
- package/agent-assets/rules/descix_mcp.template.mdc +8 -8
- package/bin/descix.js +2559 -1435
- package/bin/kb-deny-lint.mjs +91 -0
- package/bin/mcp-server.js +175 -91
- package/lib/agent-files.js +37 -25
- package/lib/api-client.js +223 -56
- package/lib/auth-guard.js +13 -3
- package/lib/chat-session.js +187 -0
- package/lib/cli-version.js +59 -0
- package/lib/commands/airdrop.js +251 -0
- package/lib/commands/auth.js +254 -72
- package/lib/commands/briefer/errors.js +88 -0
- package/lib/commands/briefer/index.js +319 -0
- package/lib/commands/briefer/sources/canonical-sources.js +129 -0
- package/lib/commands/briefer/sources/entitlements.js +220 -0
- package/lib/commands/briefer/sources/environments.js +205 -0
- package/lib/commands/briefer/sources/identifiers.js +214 -0
- package/lib/commands/briefer/sources/microservice-deploy.js +267 -0
- package/lib/commands/briefer/sources/routing.js +275 -0
- package/lib/commands/briefer/sources/what-is-not.js +223 -0
- package/lib/commands/briefer/stitcher.js +134 -0
- package/lib/commands/briefer/util/source-reader.js +502 -0
- package/lib/commands/config.js +210 -90
- package/lib/commands/corpus.js +978 -0
- package/lib/commands/credits.js +122 -0
- package/lib/commands/health.js +627 -0
- package/lib/commands/kb-list-render.js +32 -0
- package/lib/commands/kb.js +190 -413
- package/lib/commands/mcp.js +5 -5
- package/lib/commands/microservice-restart.js +359 -0
- package/lib/commands/model-config.js +313 -0
- package/lib/commands/retired-kb-sync.js +134 -0
- package/lib/commands/serve.js +26 -1
- package/lib/commands/status.js +23 -51
- package/lib/commands/update.js +36 -107
- package/lib/core/Chunker.js +491 -17
- package/lib/core/CorpusDenyLint.js +326 -0
- package/lib/core/CorpusWalker.js +357 -0
- package/lib/core/Hydrator.js +75 -29
- package/lib/core/ManifestLoader.js +533 -0
- package/lib/core/SiteWalker.js +218 -0
- package/lib/core/Syncer.js +163 -11
- package/lib/diagnostic-log.js +41 -0
- package/lib/environment-report.js +100 -0
- package/lib/global-config.js +4 -1
- package/lib/oauth-client.js +164 -0
- package/lib/origin.js +143 -0
- package/lib/service-api-client.js +95 -0
- package/lib/wallet-environment.js +49 -0
- package/lib/wallet-file.js +16 -2
- package/lib/wizard/setup.js +10 -9
- package/lib/workspace-config.js +380 -235
- package/lib/workspace-identity.js +92 -0
- package/lib/workspace-utils.js +41 -61
- package/package.json +19 -6
- package/templates/agent-claude.md +157 -9
- package/templates/agent-cline.md +68 -8
- package/templates/agent-copilot.md +68 -8
- package/templates/agent-cursor.md +68 -8
- package/templates/alignment-schema.json +20 -0
- package/templates/cloud-brain-hook.sh +20 -0
- package/templates/drive/README.md +1 -1
- package/templates/drive_templates/README.md +2 -2
- package/templates/scaffolds/README.md +6 -10
- package/templates/scaffolds/microservice/SERVICE_README_sdk.md +104 -149
- package/templates/scaffolds/microservice/app.js +51 -10
- package/templates/scaffolds/microservice/app.yaml +2 -2
- package/templates/scaffolds/microservice/defaults-config-dev.json +4 -0
- package/templates/scaffolds/microservice/dev-overrides.example.json +5 -0
- package/templates/scaffolds/microservice/manifest.json +0 -1
- package/templates/scaffolds/microservice/package.json +3 -3
- package/templates/scaffolds/microservice/scripts/register.js +56 -41
- package/templates/scaffolds/microservice/services/mcpClient.js +55 -65
- package/templates/scaffolds/microservice/services/utils.js +20 -238
- package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +24 -0
- package/lib/commands/app-wizard.js +0 -627
- package/lib/commands/folder.js +0 -291
- package/lib/core/PathContext.js +0 -335
- package/templates/scaffolds/microservice/dev-overrides.json.example +0 -5
- package/templates/scaffolds/site/DeSciXAppSDK.js +0 -62
- package/templates/scaffolds/site/README.md +0 -51
- package/templates/scaffolds/site/app.js +0 -22
- package/templates/scaffolds/site/index.html +0 -42
- package/templates/scaffolds/site/styles.css +0 -123
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
The DeSciX SDK provides command-line tools and MCP (Model Context Protocol) integration for building applications on the DeSciX platform. The SDK enables developers to authenticate, manage workspaces, create apps, and leverage AI-assisted development through
|
|
5
|
+
The DeSciX SDK provides command-line tools and MCP (Model Context Protocol) integration for building applications on the DeSciX platform. The SDK enables developers to authenticate, manage workspaces, create apps, and leverage AI-assisted development through editor MCP integration (Claude Code, Copilot, Cursor, Cline).
|
|
6
6
|
|
|
7
7
|
**Key capabilities:**
|
|
8
8
|
- CLI commands for all platform operations
|
|
9
|
-
- MCP server for Cursor
|
|
9
|
+
- MCP server integration for Claude Code, Copilot, Cursor and Cline
|
|
10
10
|
- Intelligent tool discovery via `tell_me_how`
|
|
11
11
|
- Entitlements-driven workspace setup
|
|
12
12
|
- Git-aware project initialization
|
|
@@ -28,23 +28,22 @@ npm link
|
|
|
28
28
|
|
|
29
29
|
## CLI Commands
|
|
30
30
|
|
|
31
|
-
### descix
|
|
31
|
+
### descix quickstart
|
|
32
32
|
|
|
33
|
-
**Description:** One-
|
|
34
|
-
**Use when:** First time setting up DeSciX in a new workspace
|
|
33
|
+
**Description:** One-command setup: auth → workspace → agent files → MCP config
|
|
34
|
+
**Use when:** First time setting up DeSciX in a new workspace
|
|
35
35
|
**What it does:**
|
|
36
|
-
1.
|
|
37
|
-
2.
|
|
38
|
-
3.
|
|
39
|
-
4.
|
|
40
|
-
5.
|
|
36
|
+
1. Authenticates via device login if no valid session exists
|
|
37
|
+
2. Initializes `.descix/workspace.json` if missing
|
|
38
|
+
3. Generates agent instruction files (`CLAUDE.md`, `.github/copilot-instructions.md`, `.cursorrules`, `.clinerules`) from the workspace's registered app/community
|
|
39
|
+
4. Generates `.vscode/mcp.json` for MCP server config (skipped if the DeSciX VS Code extension already handles MCP)
|
|
40
|
+
5. Copies SDK assets into `.descix/sdk-assets/`
|
|
41
41
|
|
|
42
42
|
**Example:**
|
|
43
43
|
```bash
|
|
44
44
|
cd ~/Projects/MyApp
|
|
45
|
-
descix
|
|
45
|
+
descix quickstart
|
|
46
46
|
# Follow the prompts
|
|
47
|
-
# Restart Cursor after completion
|
|
48
47
|
```
|
|
49
48
|
|
|
50
49
|
---
|
|
@@ -54,8 +53,11 @@ descix setup
|
|
|
54
53
|
**Description:** Authenticate with the DeSciX platform
|
|
55
54
|
**Use when:** Session expired or first-time authentication
|
|
56
55
|
**Options:**
|
|
57
|
-
-
|
|
58
|
-
- `--wallet`: Direct wallet signature authentication
|
|
56
|
+
- (default, no flag needed): Device login — opens a browser for Powch authentication
|
|
57
|
+
- `--wallet`: Direct wallet signature authentication (advanced, not yet implemented)
|
|
58
|
+
- `-u, --url <url>`: API URL override
|
|
59
|
+
- `--no-oauth`: Skip the OAuth long-lived token leg (wallet-signature login only)
|
|
60
|
+
- `--scope <scope>`: OAuth scope to request (default: `mcp:read mcp:tools mcp:write mcp:admin`)
|
|
59
61
|
|
|
60
62
|
**Example:**
|
|
61
63
|
```bash
|
|
@@ -107,45 +109,71 @@ descix tell-me-how --scope discovery "What AI training tools exist?"
|
|
|
107
109
|
|
|
108
110
|
---
|
|
109
111
|
|
|
110
|
-
### descix app
|
|
112
|
+
### descix app init
|
|
111
113
|
|
|
112
|
-
**Description:** Create
|
|
113
|
-
**Use when:** Starting a new project or hydrating an existing app
|
|
114
|
+
**Description:** Create (if needed) and initialize an app: platform record + default KB + local workspace registration and scaffold. Idempotent.
|
|
115
|
+
**Use when:** Starting a new project or hydrating an existing app. This is the single canonical path — the old two-step `app create` then `app init` is deleted; `descix app create` now hard-fails naming this replacement.
|
|
114
116
|
**Options:**
|
|
115
|
-
- `-
|
|
116
|
-
- `-
|
|
117
|
-
-
|
|
118
|
-
- `--
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
- `-a, --app <app_id>` (required): App ID. With `-c` this is the app NAME to create.
|
|
118
|
+
- `-c, --community <id>`: Community ID — required to create an app that does not exist yet
|
|
119
|
+
- `-s, --short <short_name>`: Short id segment (no hyphens) used when creating; defaults to `--app`
|
|
120
|
+
- `--overwrite`: When creating, overwrite an existing app record intentionally
|
|
121
|
+
- `--kb <name>`: Knowledge base name (default: `General`)
|
|
122
|
+
- `-p, --path <dir>`: Local app directory (default: auto-detected or cwd)
|
|
121
123
|
|
|
122
124
|
**Example:**
|
|
123
125
|
```bash
|
|
124
|
-
|
|
125
|
-
descix app create --wizard
|
|
126
|
+
descix app init -a myapp -c descix
|
|
126
127
|
```
|
|
127
128
|
|
|
128
129
|
---
|
|
129
130
|
|
|
130
131
|
### descix kb
|
|
131
132
|
|
|
132
|
-
**Description:** Manage Knowledge Base
|
|
133
|
-
**Use when:**
|
|
133
|
+
**Description:** Manage Knowledge Base registration, corpus sync and per-KB configuration
|
|
134
|
+
**Use when:** Creating a KB, syncing its corpus to Pinecone, or managing per-KB settings
|
|
134
135
|
|
|
135
136
|
**Subcommands:**
|
|
136
|
-
- `
|
|
137
|
-
- `
|
|
138
|
-
- `
|
|
139
|
-
- `
|
|
137
|
+
- `create` / `list` / `delete`: Register, list or remove a KB
|
|
138
|
+
- `corpus sync`: The one KB sync surface — walks the corpus manifest, chunks in-memory, pushes to Pinecone
|
|
139
|
+
- `set-override-model` / `clear-override-model`: Pin or clear a per-KB model override
|
|
140
|
+
- `status`: Show corpus sync state (files, chunks, last sync, resolved ref)
|
|
141
|
+
- `doctor`: Detect drift between local sync-state and live Pinecone vector count
|
|
142
|
+
- `records put|query|get|delete`: Structured record CRUD on a KB
|
|
143
|
+
|
|
144
|
+
**Drive content authoring is a separate command:** `descix drive pull` / `descix drive push` (not under `kb`) move source documents between Drive and the local working tree.
|
|
140
145
|
|
|
141
146
|
**Example:**
|
|
142
147
|
```bash
|
|
143
|
-
#
|
|
144
|
-
descix kb
|
|
148
|
+
# Sync a KB's corpus manifest to Pinecone
|
|
149
|
+
descix kb corpus sync -a myapp -k General
|
|
145
150
|
```
|
|
146
151
|
|
|
147
152
|
---
|
|
148
153
|
|
|
154
|
+
### Calling /apifront from your microservice (data plane)
|
|
155
|
+
|
|
156
|
+
**The microservice IS the CLI's api-client running in the cloud** (CEO-D-2026-06-02-APP-MICROSERVICE-IS-CLI-CLIENT-WALLET-SIG). To call Core commands (`app_records_*`, `get_app_asset`, `get_asset_upload_token`, RAG, ...) the scaffold's `mcpClient` authenticates to `/apifront` **exactly like the CLI**: it holds the **developer's own durable credential** (`wallet_address` + `signature`) and calls `reconnect_by_wallet` to mint a session — then makes calls **as the developer**. There is NO `register-delegate`, NO `SERVICE_KEY`, NO service account, NO OIDC.
|
|
157
|
+
|
|
158
|
+
**Provide the developer credential (gitignored / secret — never checked in):**
|
|
159
|
+
1. Copy `dev-overrides.example.json` → `dev-overrides.json` (this file is `.gitignore`d).
|
|
160
|
+
2. Fill in `DEVELOPER_WALLET_ADDRESS` + `DEVELOPER_SIGNATURE` — your own values from `.descix/wallet.json` (the durable `signature`, not the expiring session token).
|
|
161
|
+
3. In prod these come from a **secret**, never a checked-in file. They must NOT live in `defaults-config.json` (the signature is a credential).
|
|
162
|
+
|
|
163
|
+
The scaffold reads them via `utils.DEVELOPER_WALLET_ADDRESS` / `utils.DEVELOPER_SIGNATURE` and HARD-FAILS (no fallback) if absent.
|
|
164
|
+
|
|
165
|
+
**Canonical microservice onboarding sequence:**
|
|
166
|
+
```bash
|
|
167
|
+
descix app init -a <app> -c <community>
|
|
168
|
+
descix app set-port -a <community>-<app> -p <port>
|
|
169
|
+
descix microservice register
|
|
170
|
+
# then: cp dev-overrides.example.json dev-overrides.json and fill in the developer credential
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
> `descix microservice register-delegate` still exists for genuine **service-slot** mesh calls (one service calling another's tools as a delegate). It is NOT used for the app **data plane** — that authenticates as the developer per the model above.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
149
177
|
### descix microservice vectorize
|
|
150
178
|
|
|
151
179
|
**Description:** Vectorize a SERVICE_README for tell_me_how discovery
|
|
@@ -176,14 +204,15 @@ descix purchases
|
|
|
176
204
|
|
|
177
205
|
---
|
|
178
206
|
|
|
179
|
-
## MCP Tools
|
|
207
|
+
## MCP Tools
|
|
180
208
|
|
|
181
|
-
After running `descix
|
|
209
|
+
After running `descix quickstart` and reloading your editor/session, these MCP tools are available to the AI agent. `descix quickstart` authenticates via device login as its first step (opens a browser device-code flow) before it writes workspace, agent-instruction and MCP config files; there is no unauthenticated form of any of this.
|
|
182
210
|
|
|
183
211
|
### tell_me_how
|
|
184
212
|
|
|
185
213
|
**Description:** Primary discovery tool - finds relevant commands for any task
|
|
186
|
-
**
|
|
214
|
+
**Prerequisite:** the caller is signed in — it returns caller-specific context and is refused loud (`Authentication required`, exit 1) for a credential-free caller, never a silent partial answer
|
|
215
|
+
**Use first:** Once signed in, always use this before attempting platform operations
|
|
187
216
|
|
|
188
217
|
```javascript
|
|
189
218
|
tell_me_how({
|
|
@@ -206,7 +235,7 @@ tell_me_how({
|
|
|
206
235
|
|
|
207
236
|
```javascript
|
|
208
237
|
execute_remote_command({
|
|
209
|
-
command: "
|
|
238
|
+
command: "create_app_for_community",
|
|
210
239
|
params: {
|
|
211
240
|
community_id: "descix",
|
|
212
241
|
app_name: "My New App"
|
|
@@ -216,90 +245,31 @@ execute_remote_command({
|
|
|
216
245
|
|
|
217
246
|
---
|
|
218
247
|
|
|
219
|
-
###
|
|
220
|
-
|
|
221
|
-
**Description:** Initialize a local project for DeSciX deployment
|
|
222
|
-
**Use when:** Setting up a new project folder
|
|
223
|
-
|
|
224
|
-
```javascript
|
|
225
|
-
descix_init({
|
|
226
|
-
projectPath: "/Users/dev/Projects/MyApp",
|
|
227
|
-
appName: "my-app",
|
|
228
|
-
communityId: "descix"
|
|
229
|
-
})
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
**Creates:**
|
|
233
|
-
- `.descix/workspace.json` or `.descix/context.json`
|
|
234
|
-
- `app_description.md` from README if missing
|
|
235
|
-
- Folder structure for knowledge base
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
### descix_wizard_step
|
|
240
|
-
|
|
241
|
-
**Description:** Execute multi-step wizard workflows
|
|
242
|
-
**Use when:** Creating apps, communities, or complex operations
|
|
243
|
-
|
|
244
|
-
```javascript
|
|
245
|
-
descix_wizard_step({
|
|
246
|
-
wizard_type: "create_app",
|
|
247
|
-
step: "init",
|
|
248
|
-
params: {
|
|
249
|
-
community_id: "descix",
|
|
250
|
-
app_name: "My App"
|
|
251
|
-
}
|
|
252
|
-
})
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
**Wizard types:**
|
|
256
|
-
- `create_app`: App creation with folder setup
|
|
257
|
-
- `create_community`: Community creation with token
|
|
258
|
-
- `publish_kb`: Knowledge base sync and publish
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
### git_sync_ops
|
|
248
|
+
### query_knowledge_base
|
|
263
249
|
|
|
264
|
-
**Description:**
|
|
265
|
-
**Use when:**
|
|
250
|
+
**Description:** Vector-similarity search over a knowledge base — returns raw source chunks with dereferenceable citations (retrieval, no synthesis, stateless)
|
|
251
|
+
**Use when:** You want primary-source passages/citations, or to ground/check an `ask_question_to_app` answer
|
|
266
252
|
|
|
267
253
|
```javascript
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
})
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
---
|
|
275
|
-
|
|
276
|
-
### search_knowledge_base
|
|
277
|
-
|
|
278
|
-
**Description:** Search a specific knowledge base via RAG
|
|
279
|
-
**Use when:** You know the exact KB to search
|
|
280
|
-
|
|
281
|
-
```javascript
|
|
282
|
-
search_knowledge_base({
|
|
283
|
-
communityId: "descix",
|
|
284
|
-
appId: "docs",
|
|
285
|
-
kbId: "sdk",
|
|
254
|
+
query_knowledge_base({
|
|
255
|
+
app_id: "docs",
|
|
256
|
+
kb_id: "sdk",
|
|
286
257
|
query: "How do I authenticate?"
|
|
287
258
|
})
|
|
288
259
|
```
|
|
289
260
|
|
|
290
261
|
---
|
|
291
262
|
|
|
292
|
-
###
|
|
263
|
+
### ask_question_to_app
|
|
293
264
|
|
|
294
|
-
**Description:**
|
|
295
|
-
**Use when:** Complex questions requiring AI reasoning
|
|
265
|
+
**Description:** Ask a natural-language question against an app's knowledge base and get a synthesized, cited answer (stateful — pass `previous_interaction_id` to continue a thread)
|
|
266
|
+
**Use when:** Complex questions requiring AI reasoning over a community's own sources
|
|
296
267
|
|
|
297
268
|
```javascript
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
message: "Explain the authentication flow step by step"
|
|
269
|
+
ask_question_to_app({
|
|
270
|
+
app_id: "docs",
|
|
271
|
+
knowledgebase_name: "sdk",
|
|
272
|
+
user_input: "Explain the authentication flow step by step"
|
|
303
273
|
})
|
|
304
274
|
```
|
|
305
275
|
|
|
@@ -307,65 +277,44 @@ chat_with_kb({
|
|
|
307
277
|
|
|
308
278
|
## Workspace Configuration
|
|
309
279
|
|
|
310
|
-
### workspace.json (
|
|
280
|
+
### workspace.json (v2.1 — canonical)
|
|
311
281
|
|
|
312
|
-
|
|
282
|
+
`.descix/workspace.json` is written and mutated by `descix init` / `descix app init` / `descix app set-*` — never hand-edited. The v2.1 format is `env.platform` (the shell/store app, served at root) plus `env.products[]` (apps hosted inside the shell); the older `communities`-based v1 format is not supported and hard-errors on load ("v1 workspace format is not supported. Migrate to v2.1."):
|
|
313
283
|
|
|
314
284
|
```json
|
|
315
285
|
{
|
|
316
|
-
"version": "2.
|
|
317
|
-
"
|
|
318
|
-
|
|
319
|
-
"
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
},
|
|
323
|
-
"defaultContext": {
|
|
324
|
-
"communityId": "descix",
|
|
325
|
-
"appId": "docs",
|
|
326
|
-
"kbId": "sdk"
|
|
286
|
+
"version": "2.1",
|
|
287
|
+
"env": {
|
|
288
|
+
"platform": { "appId": "daita", "communityId": "descix", "localPath": ".", "kbId": "General" },
|
|
289
|
+
"products": [
|
|
290
|
+
{ "appId": "docs", "communityId": "descix", "kbId": "sdk", "localPath": "descix-docs" }
|
|
291
|
+
]
|
|
327
292
|
}
|
|
328
293
|
}
|
|
329
294
|
```
|
|
330
295
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
For individual app folders, create `.descix/context.json`:
|
|
334
|
-
|
|
335
|
-
```json
|
|
336
|
-
{
|
|
337
|
-
"version": "2.0",
|
|
338
|
-
"type": "app",
|
|
339
|
-
"community_id": "descix",
|
|
340
|
-
"app_id": "myapp",
|
|
341
|
-
"app_name": "My Application",
|
|
342
|
-
"folders": {
|
|
343
|
-
"app": { "local_path": ".", "sync_type": "assets" },
|
|
344
|
-
"kb": { "local_path": "docs", "sync_type": "rag" }
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
```
|
|
296
|
+
A single-app (non-monorepo) workspace uses the same v2.1 shape with only `env.platform` set and an empty (or omitted) `env.products[]` — there is no separate single-app file format.
|
|
348
297
|
|
|
349
298
|
---
|
|
350
299
|
|
|
351
300
|
## Common Workflows
|
|
352
301
|
|
|
353
|
-
### New Developer Onboarding
|
|
302
|
+
### New Developer Onboarding
|
|
354
303
|
|
|
355
304
|
1. Install CLI: `npm install -g @descix/cli`
|
|
356
|
-
2. Run
|
|
357
|
-
3.
|
|
305
|
+
2. Run quickstart: `descix quickstart` — authenticates, initializes `.descix/workspace.json`, generates agent instruction files (`CLAUDE.md`, `.github/copilot-instructions.md`, `.cursorrules`, `.clinerules`) for Claude Code, Copilot, Cursor and Cline, and writes `.vscode/mcp.json` (skipped if the DeSciX VS Code extension is installed — it registers MCP natively)
|
|
306
|
+
3. Reload the editor/session so it picks up the generated files
|
|
358
307
|
4. AI agent can now use MCP tools
|
|
359
308
|
5. Ask: "How do I create an app?"
|
|
360
309
|
6. AI uses `tell_me_how` → `execute_remote_command`
|
|
361
310
|
|
|
362
311
|
### Entitlements-First App Creation
|
|
363
312
|
|
|
364
|
-
1. AI calls `
|
|
313
|
+
1. AI calls `fetch_my_purchases()` directly (it is its own MCP tool, not an `execute_remote_command` target)
|
|
365
314
|
2. AI presents user's communities and apps
|
|
366
315
|
3. User selects community (or creates new)
|
|
367
|
-
4. AI calls `
|
|
368
|
-
5. AI
|
|
316
|
+
4. AI calls `execute_remote_command({ command: "create_app_for_community", params: {...} })` for app creation
|
|
317
|
+
5. AI runs `descix app init` to register the local workspace mapping in `.descix/workspace.json`
|
|
369
318
|
|
|
370
319
|
### Monorepo Setup
|
|
371
320
|
|
|
@@ -380,9 +329,15 @@ For individual app folders, create `.descix/context.json`:
|
|
|
380
329
|
## Troubleshooting
|
|
381
330
|
|
|
382
331
|
### "MCP tools not available"
|
|
383
|
-
1. Run `descix
|
|
384
|
-
2.
|
|
385
|
-
3. Check `.
|
|
332
|
+
1. Run `descix quickstart` in workspace root
|
|
333
|
+
2. Reload your editor/session completely
|
|
334
|
+
3. Check `.vscode/mcp.json` exists with a `descix` server (this file is skipped, by design, if the DeSciX VS Code extension is installed — it registers MCP natively)
|
|
335
|
+
|
|
336
|
+
### "401 Unauthorized on a loopback / mesh call"
|
|
337
|
+
For the app data plane, your service authenticates AS the developer (wallet_address + signature). A 401 means the
|
|
338
|
+
developer credential is missing or wrong: check `DEVELOPER_WALLET_ADDRESS` + `DEVELOPER_SIGNATURE` in the gitignored
|
|
339
|
+
`dev-overrides.json` (dev) / the secret (prod). Then restart the service.
|
|
340
|
+
Discover it via `descix tell-me-how "my service gets 401 on a loopback call"`.
|
|
386
341
|
|
|
387
342
|
### "Session expired"
|
|
388
343
|
Run `descix reconnect` or `descix login`
|
|
@@ -391,7 +346,7 @@ Run `descix reconnect` or `descix login`
|
|
|
391
346
|
Service READMEs may not be vectorized. Ask admin to run `descix microservice vectorize` for relevant services.
|
|
392
347
|
|
|
393
348
|
### "workspace.json not found" for project scope
|
|
394
|
-
Run `descix init` or
|
|
349
|
+
Run `descix init` (or `descix quickstart`, which runs it for you if missing).
|
|
395
350
|
|
|
396
351
|
---
|
|
397
352
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import express from 'express';
|
|
8
|
+
import http from 'http';
|
|
8
9
|
import cors from 'cors';
|
|
9
10
|
import { initializeServiceConfig, utils } from './services/utils.js';
|
|
10
11
|
import { killExistingProcess } from '@descix/cloud-core';
|
|
@@ -12,12 +13,55 @@ import apiRouter from './services/apiFront.js';
|
|
|
12
13
|
|
|
13
14
|
const app = express();
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Bind the http.Server with a bounded retry on EADDRINUSE.
|
|
18
|
+
*
|
|
19
|
+
* DEV RESTART RACE (CEO-D-2026-06-01-SCAFFOLD-RESIDUALS, fix #2):
|
|
20
|
+
* killExistingProcess() SIGKILLs the prior instance on THIS port and returns
|
|
21
|
+
* synchronously, then we bind immediately. The kernel may not have fully released
|
|
22
|
+
* the old listening socket yet (it can linger briefly after a kill -9), so the first
|
|
23
|
+
* bind can throw EADDRINUSE on a fast kill-then-restart. We retry the bind a few times
|
|
24
|
+
* with a short backoff so dev restarts don't flake. Any non-EADDRINUSE listen error
|
|
25
|
+
* fails loud (no swallowing); after the retries are exhausted we exit 1.
|
|
26
|
+
*
|
|
27
|
+
* NOTE: we bind an explicit http.Server (not app.listen) so the 'error'/'listening'
|
|
28
|
+
* handlers attach to the SAME object we retry on. A server that failed to bind is left
|
|
29
|
+
* unbound and can be re-listened safely.
|
|
30
|
+
*/
|
|
31
|
+
function listenWithRetry(server, port, { retries = 10, delayMs = 150 } = {}) {
|
|
32
|
+
let attempt = 0;
|
|
33
|
+
|
|
34
|
+
const onError = (err) => {
|
|
35
|
+
if (err && err.code === 'EADDRINUSE' && attempt < retries) {
|
|
36
|
+
attempt += 1;
|
|
37
|
+
console.warn(`[Service] Port ${port} busy (EADDRINUSE), retry ${attempt}/${retries} in ${delayMs}ms...`);
|
|
38
|
+
setTimeout(() => server.listen(port), delayMs);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Not a transient bind race (or retries exhausted) — fail loud.
|
|
42
|
+
console.error(`[Service] Failed to bind port ${port}:`, err?.message || err);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
server.on('error', onError);
|
|
47
|
+
server.on('listening', () => {
|
|
48
|
+
console.log(`[Service] Listening on port ${port}`);
|
|
49
|
+
console.log(`[Service] Environment: ${utils.DEPLOY_ENV || 'unknown'}`);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
server.listen(port);
|
|
53
|
+
}
|
|
54
|
+
|
|
15
55
|
// Initialize Config (Async)
|
|
16
56
|
// In a real app, we might want to block startup until config is loaded
|
|
17
57
|
initializeServiceConfig().then(() => {
|
|
18
58
|
|
|
19
|
-
//
|
|
20
|
-
|
|
59
|
+
// Resolve this service's own port up front so process cleanup is scoped to it.
|
|
60
|
+
const port = utils.PORT;
|
|
61
|
+
|
|
62
|
+
// Kill ONLY a prior instance of THIS service (scoped to its own port) in debug mode.
|
|
63
|
+
// Port-scoping guarantees we never kill an unrelated app.js (e.g. the Core :4000 backend).
|
|
64
|
+
killExistingProcess(import.meta.url, utils.DEBUG_LOCAL, port);
|
|
21
65
|
|
|
22
66
|
// Middleware
|
|
23
67
|
app.use(cors({ origin: true })); // Allow all origins (controlled by Core proxy)
|
|
@@ -32,17 +76,14 @@ initializeServiceConfig().then(() => {
|
|
|
32
76
|
app.use('/api', apiRouter);
|
|
33
77
|
|
|
34
78
|
// Start Server
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (port === 'auto') {
|
|
38
|
-
console.error('[Service] CRITICAL ERROR: Port is "auto". Gateway registration failed.');
|
|
79
|
+
if (!port || port === 'auto') {
|
|
80
|
+
console.error('[Service] CRITICAL ERROR: Port is missing or "auto". Gateway registration failed.');
|
|
39
81
|
process.exit(1);
|
|
40
82
|
}
|
|
41
83
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
84
|
+
// Bind with a bounded retry so a fast dev kill-then-restart doesn't flake on EADDRINUSE.
|
|
85
|
+
const server = http.createServer(app);
|
|
86
|
+
listenWithRetry(server, port);
|
|
46
87
|
|
|
47
88
|
}).catch(err => {
|
|
48
89
|
console.error('[Service] Failed to initialize config:', err);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Per-env NON-SECRET config for the dev environment. Layer 2 of the cloud-core bootstrap (process.env > defaults-config-{env}.json > defaults-config.json > Secret Manager). FIRESTORE_DATABASE_ID is a UNIVERSAL required key for every cloud-core consumer and is deliberately null here: cloud-core hard-fails at boot on a null required key, which is correct. Set it to your named Firestore database. It is left null rather than given a plausible default because a wrong database id writes silently to the wrong place — a hard failure at boot is the cheaper outcome.",
|
|
3
|
+
"FIRESTORE_DATABASE_ID": null
|
|
4
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Copy this file to dev-overrides.json (GITIGNORED) and fill in the developer credential. The microservice authenticates to /apifront AS the developer — exactly like the CLI — using these. In prod these come from a SECRET, never from a checked-in file. defaults-config.json must NOT contain the signature (it is a credential).",
|
|
3
|
+
"DEVELOPER_WALLET_ADDRESS": "0xYOUR_WALLET_ADDRESS",
|
|
4
|
+
"DEVELOPER_SIGNATURE": "PASTE_YOUR_DURABLE_SIGNATURE_FROM_.descix/wallet.json"
|
|
5
|
+
}
|
|
@@ -12,15 +12,15 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@descix/cloud-core": "^1.0.0",
|
|
15
|
-
"@
|
|
15
|
+
"@descix/platform-api": "^2.0.0",
|
|
16
16
|
"@google-cloud/secret-manager": "^5.0.1",
|
|
17
17
|
"@google-cloud/storage": "^7.7.0",
|
|
18
18
|
"axios": "^1.6.2",
|
|
19
19
|
"cors": "^2.8.5",
|
|
20
20
|
"dotenv": "^16.3.1",
|
|
21
21
|
"express": "^4.18.2",
|
|
22
|
-
"
|
|
23
|
-
"@descix/
|
|
22
|
+
"@descix/sdk": "^1.0.0",
|
|
23
|
+
"@descix/cli": "^1.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"nodemon": "^3.0.2"
|
|
@@ -1,55 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Service Registration Script
|
|
3
|
-
*
|
|
4
|
-
* Registers
|
|
3
|
+
*
|
|
4
|
+
* Registers this service's manifest with the DeSciX platform.
|
|
5
5
|
* Usage: npm run register
|
|
6
|
+
*
|
|
7
|
+
* THIS SCRIPT CONSUMES THE DOOR. It calls Cloud's `register_service` command over apifront,
|
|
8
|
+
* through `@descix/platform-api/service-bootstrap` — the ONE owner of "a microservice registers
|
|
9
|
+
* its manifest". It does NOT write the ServiceManifests collection directly.
|
|
10
|
+
*
|
|
11
|
+
* WHY THAT MATTERS (this is not a style preference — it was measured):
|
|
12
|
+
* The previous version of this script opened a Firestore client and `set()` the manifest
|
|
13
|
+
* verbatim into `ServiceManifests`. That bypassed every check the platform performs at
|
|
14
|
+
* registration, and the platform DERIVES a service's domain at that door. A verbatim write
|
|
15
|
+
* therefore stored a manifest with `service.domain === undefined`, and the router then composed
|
|
16
|
+
* its endpoint as `https://${service.domain}/api` — the literal string `https://undefined/api`.
|
|
17
|
+
* That is a valid URL naming a host called "undefined", so nothing threw, nothing warned, and
|
|
18
|
+
* the service simply never answered. Every service scaffolded from this template shipped that
|
|
19
|
+
* bypass. Going through the door also gets the manifest VECTORIZED, which is what makes the
|
|
20
|
+
* service's commands discoverable by `tell_me_how` — a direct write never did.
|
|
21
|
+
*
|
|
22
|
+
* AUTHORIZATION is a platform-runtime service-account OIDC identity token minted from ADC
|
|
23
|
+
* (audience = CORE_API_URL). On Cloud Run that works with no setup. Under local developer ADC,
|
|
24
|
+
* ID-token minting is unsupported without impersonation and this reports a named failure — it
|
|
25
|
+
* does not fall back to anything.
|
|
6
26
|
*/
|
|
7
27
|
|
|
8
28
|
import fs from 'fs';
|
|
9
29
|
import path from 'path';
|
|
10
30
|
import { fileURLToPath } from 'url';
|
|
11
|
-
import {
|
|
31
|
+
import { createServiceBootstrap } from '@descix/platform-api/service-bootstrap';
|
|
32
|
+
import { initializeServiceConfig, utils } from '../services/utils.js';
|
|
12
33
|
|
|
13
|
-
const
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
15
|
-
|
|
16
|
-
// Load Manifest
|
|
17
|
-
const manifestPath = path.resolve(__dirname, '../manifest.json');
|
|
18
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
34
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
35
|
|
|
20
36
|
async function register() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.
|
|
44
|
-
|
|
45
|
-
console.log(`[Register] Commands: ${Object.keys(manifest.commands).join(', ')}`);
|
|
46
|
-
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.error('[Register] FAILED:', error.message);
|
|
49
|
-
console.log('\n[Tip] If you don\'t have direct Firestore access, create a document in the "ServiceManifests" collection with this ID and content:');
|
|
50
|
-
console.log(`ID: ${manifest.service.name}`);
|
|
51
|
-
console.log(JSON.stringify(manifest, null, 2));
|
|
37
|
+
await initializeServiceConfig();
|
|
38
|
+
|
|
39
|
+
const manifestPath = path.resolve(__dirname, '../manifest.json');
|
|
40
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
41
|
+
|
|
42
|
+
const bootstrap = createServiceBootstrap({
|
|
43
|
+
manifest,
|
|
44
|
+
// Running this script IS the explicit opt-in; there is no config key to consult. The
|
|
45
|
+
// key name is still supplied so any refusal names the boot-time knob a service would use.
|
|
46
|
+
selfRegister: true,
|
|
47
|
+
selfRegisterConfigKey: 'npm run register',
|
|
48
|
+
// No hardcoded default: CORE_API_URL is the env-correct apifront endpoint from
|
|
49
|
+
// defaults-config-{env}.json, and it is also the OIDC audience.
|
|
50
|
+
coreApiUrl: utils.CORE_API_URL,
|
|
51
|
+
// Used ONLY to name the derived domain in a message. The platform, not this script,
|
|
52
|
+
// owns the derivation.
|
|
53
|
+
siteDomain: utils.SITE_DOMAIN,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const result = await bootstrap.register();
|
|
57
|
+
|
|
58
|
+
if (result.status !== 'ok') {
|
|
59
|
+
console.error(`[Register] ${manifest.service.name}: registration ${result.status} — ${result.error || result.reason}`);
|
|
60
|
+
process.exit(1);
|
|
52
61
|
}
|
|
62
|
+
console.log(`[Register] SUCCESS: ${manifest.service.name} registered via register_service`);
|
|
63
|
+
console.log(`[Register] Commands: ${Object.keys(manifest.commands).join(', ')}`);
|
|
53
64
|
}
|
|
54
65
|
|
|
55
|
-
register()
|
|
66
|
+
register().catch((error) => {
|
|
67
|
+
// Never silent: a registration this script could not even attempt is reported and exits non-zero.
|
|
68
|
+
console.error(`[Register] FAILED: ${error.message}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|