@descix/cli 1.0.5 → 1.0.6
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 +130 -34
- package/agent-assets/reference/local-dev.md +0 -4
- package/bin/descix.js +23 -24
- package/lib/commands/auth.js +17 -1
- package/lib/commands/init.js +8 -3
- package/lib/commands/kb.js +7 -4
- package/lib/commands/update.js +0 -1
- package/lib/core/Hydrator.js +88 -11
- package/lib/workspace-config.js +63 -7
- package/package.json +2 -2
- package/templates/agent-claude.md +0 -3
- package/templates/agent-cline.md +0 -3
- package/templates/agent-copilot.md +0 -3
- package/templates/agent-cursor.md +0 -3
- package/templates/drive/app/kb/General/README.md +1 -1
- package/templates/drive_templates/agent_app_template/kb/General/README.md +1 -1
- package/templates/scaffolds/microservice/SERVICE_README_sdk.md +3 -3
- package/templates/scaffolds/microservice/defaults-config.json +3 -1
- package/templates/scaffolds/microservice/manifest.json +9 -5
- package/templates/scaffolds/microservice/package.json +1 -1
- package/templates/scaffolds/microservice/services/commandHandlers/sampleCommands.js +2 -2
package/README.md
CHANGED
|
@@ -15,11 +15,12 @@ npm install -g @descix/cli
|
|
|
15
15
|
descix login
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
2. **
|
|
18
|
+
2. **Initialize your workspace**:
|
|
19
19
|
```bash
|
|
20
|
-
descix
|
|
20
|
+
descix init
|
|
21
21
|
```
|
|
22
|
-
This
|
|
22
|
+
This writes `.descix/workspace.json` for the current project. Pass `-c <community_id> -a <app_id>`
|
|
23
|
+
to pre-fill the app context, or `--from-invite <token>` to resolve an invite token.
|
|
23
24
|
|
|
24
25
|
**Choosing an environment**:
|
|
25
26
|
The `--dev` flag has been REMOVED. Its absence silently meant production, so the most common
|
|
@@ -58,40 +59,129 @@ Best for platform admins or agencies managing multiple communities.
|
|
|
58
59
|
## Core Commands
|
|
59
60
|
|
|
60
61
|
- `descix login`: Authenticate with the platform.
|
|
61
|
-
- `descix setup`: Initialize or repair your workspace configuration.
|
|
62
62
|
- `descix status`: View your current environment status and workspace mode.
|
|
63
|
-
- `descix init`:
|
|
64
|
-
- `descix app
|
|
65
|
-
- `descix
|
|
66
|
-
- `descix
|
|
63
|
+
- `descix init`: Initialize workspace for DeSciX app development (Git-aware) — writes `.descix/workspace.json`.
|
|
64
|
+
- `descix app init`: Create (when it does not exist yet) and initialize an app — the one path to an app. See [Creating an App](#creating-an-app).
|
|
65
|
+
- `descix site init` / `descix site upload`: Scaffold an app's site, then build and deploy it. See [Deploying a Site](#deploying-a-site).
|
|
66
|
+
- `descix app sync-assets`: Sync local assets (`system_instructions.md`, `app_description.md`, `icon.png`) to the platform.
|
|
67
|
+
- `descix drive pull` / `descix drive push`: Drive content authoring — pull from Drive to local markdown, push staging back to Drive.
|
|
68
|
+
- `descix kb`: Manage Knowledge Bases (`list`, `create`, `delete`, and `corpus sync`).
|
|
67
69
|
|
|
68
70
|
## Knowledge Base Management (Git Mode)
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
The CLI exposes exactly ONE knowledge-base sync surface: `descix kb corpus sync`, driven by a
|
|
73
|
+
git-tracked corpus manifest. The older four-step local pipeline has been REMOVED — its invocations
|
|
74
|
+
now exit non-zero and name this replacement.
|
|
71
75
|
|
|
72
|
-
1. **
|
|
76
|
+
1. **Create** the knowledge base (once per KB):
|
|
73
77
|
```bash
|
|
74
|
-
descix kb
|
|
78
|
+
descix kb create -c <community_id> -a <app_id> -k <kb_name>
|
|
75
79
|
```
|
|
76
|
-
2. **
|
|
80
|
+
2. **Author** a corpus manifest at `.descix/manifests/<kb_name>.json` describing the sources to sync.
|
|
81
|
+
3. **Sync** the corpus to the platform (Pinecone):
|
|
77
82
|
```bash
|
|
78
|
-
descix kb
|
|
79
|
-
```
|
|
80
|
-
3. **Sync**: Push chunks to the platform (Pinecone).
|
|
81
|
-
```bash
|
|
82
|
-
descix kb sync
|
|
83
|
-
```
|
|
84
|
-
4. **Build**: Run the full pipeline (Pull -> Chunk -> Sync).
|
|
85
|
-
```bash
|
|
86
|
-
descix kb build
|
|
83
|
+
descix kb corpus sync -a <app_id> [-k <kb_name>]
|
|
87
84
|
```
|
|
85
|
+
It walks the git blobs at the manifest's ref, so only changed files are re-vectorized. Useful
|
|
86
|
+
flags: `--dry-run` (report drift, write nothing), `--show-walk` (print the resolved ref and the
|
|
87
|
+
files it would walk), `--ref <ref>` (override the git ref for all manifest sources).
|
|
88
|
+
|
|
89
|
+
To fetch source documents from Google Drive into local markdown, use `descix drive pull`.
|
|
90
|
+
|
|
91
|
+
## Creating an App
|
|
92
|
+
|
|
93
|
+
`descix app init` is the one path to an app. It creates the app on the platform when it does not
|
|
94
|
+
exist yet, then registers and scaffolds it locally — one command, not two.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
descix app init -a <name> -c <community>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Pass `-c` to create a new app in that community. **Omit `-c` to initialize an app that already
|
|
101
|
+
exists** — the CLI resolves the app from the Products registry and only does the local half.
|
|
102
|
+
|
|
103
|
+
The command is idempotent, so re-running it on a half-made app finishes the job.
|
|
104
|
+
|
|
105
|
+
**Options:**
|
|
106
|
+
|
|
107
|
+
- `-a, --app <app_id>` (required): the app ID. With `-c`, this is the app NAME to create.
|
|
108
|
+
- `-c, --community <id>`: the community to create the app in. It must already be materialized in
|
|
109
|
+
this environment. Required only for creation.
|
|
110
|
+
- `-s, --short <short_name>`: the short id segment (no hyphens). The `app_id` is composed
|
|
111
|
+
**server-side** as `{community}-{short}`; defaults to `--app`.
|
|
112
|
+
- `--overwrite`: when creating, overwrite an existing app record intentionally.
|
|
113
|
+
- `--kb <name>`: knowledge base name (default: `General`).
|
|
114
|
+
- `-p, --path <dir>`: local app directory (default: auto-detected, else the current directory).
|
|
115
|
+
|
|
116
|
+
**What it does, in order:**
|
|
117
|
+
|
|
118
|
+
1. **Creates the app** (only when `-c` is given and the app does not exist): the server composes
|
|
119
|
+
the unique `app_id` and writes `Products/{app_id}` and `Community/{community}/Apps/{app_id}`.
|
|
120
|
+
Every app is guaranteed a default knowledge base at creation.
|
|
121
|
+
2. **Registers it in `.descix/workspace.json`** if it is not already mapped.
|
|
122
|
+
3. **Scaffolds the app folder**: creates `site/`, `microservice/` and `assets/`, and writes
|
|
123
|
+
`assets/system_instructions.md` and `assets/app_description.md` when they do not exist.
|
|
124
|
+
4. **Creates the knowledge base record** for Git-mode sync.
|
|
125
|
+
|
|
126
|
+
It fails loud rather than guessing: initializing an app that does not exist without `-c` names the
|
|
127
|
+
`-c` invocation that would create it; a `-c` that disagrees with the app's real community is
|
|
128
|
+
refused; and `-p` against an already-mapped app names `descix app set-localpath` instead.
|
|
88
129
|
|
|
89
|
-
|
|
130
|
+
**Next step** — author a corpus manifest at `.descix/manifests/<kb_name>.json`, then
|
|
131
|
+
`descix kb corpus sync -a <app_id>`.
|
|
90
132
|
|
|
91
|
-
|
|
133
|
+
Creating a **community** is a separate, admin-only operation: `descix community create` requires
|
|
134
|
+
platform-admin membership and deploys a real ERC-20 token contract to live Polygon. App developers
|
|
135
|
+
work inside a community that already exists.
|
|
92
136
|
|
|
93
|
-
|
|
94
|
-
|
|
137
|
+
## Deploying a Site
|
|
138
|
+
|
|
139
|
+
An app's site is scaffolded, then uploaded. `descix site upload` is the deploy verb — there is no
|
|
140
|
+
`descix site deploy`.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
descix site init # scaffold site/ (optional — upload works on any site directory)
|
|
144
|
+
descix site upload --env dev
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### `descix site init`
|
|
148
|
+
|
|
149
|
+
Copies the site scaffold — `index.html`, `app.js`, `styles.css`, `DeSciXAppSDK.js`, `README.md` —
|
|
150
|
+
into the app's `site/` directory. The app must already be mapped in `.descix/workspace.json`.
|
|
151
|
+
|
|
152
|
+
- `-f, --force`: overwrite an existing `site/` folder.
|
|
153
|
+
- `-c, --community <id>` / `-a, --app <id>`: auto-detected from context when omitted.
|
|
154
|
+
|
|
155
|
+
It scaffolds site content only; it writes no manifest. Skip it entirely if you already have a site.
|
|
156
|
+
|
|
157
|
+
### `descix site upload`
|
|
158
|
+
|
|
159
|
+
Builds (when a manifest declares a build) and uploads the site to GCS, then updates the app's
|
|
160
|
+
metadata. Files are uploaded straight to GCS with short-lived **signed URLs** issued by
|
|
161
|
+
`get_site_deploy_token`; `confirm_site_deploy` then records the deploy and returns the site URL.
|
|
162
|
+
|
|
163
|
+
- `-p, --path <localPath>`: directory to deploy (default `./site`). **Ignored when a site manifest
|
|
164
|
+
exists.**
|
|
165
|
+
- `--preview`: deploy to the preview path.
|
|
166
|
+
- `--full`: force a full upload, ignoring the unchanged-file delta.
|
|
167
|
+
- `--dry-run`: show what would be deployed.
|
|
168
|
+
- `--no-cache`: set `Cache-Control: no-cache`.
|
|
169
|
+
- `-c, --community <id>` / `-a, --app <id>`: auto-detected from context when omitted.
|
|
170
|
+
|
|
171
|
+
**Site deploys target a cloud environment.** An upload that resolves to a local backend without
|
|
172
|
+
this invocation naming its target is refused — pass `--env <dev|demo|prod>`.
|
|
173
|
+
|
|
174
|
+
**Manifest-driven builds.** When `{app_root}/.descix/manifests/site.json` exists it takes over from
|
|
175
|
+
`-p`. Author it yourself; nothing generates it for you. It declares the sources to walk and,
|
|
176
|
+
optionally, a `buildCommand`, which runs in the app root with the environment-specific `VITE_*`
|
|
177
|
+
build variables the server returns injected into its environment:
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"sources": [{ "path": "site/dist", "include": ["**/*"] }],
|
|
182
|
+
"buildCommand": "npm run build --prefix site"
|
|
183
|
+
}
|
|
184
|
+
```
|
|
95
185
|
|
|
96
186
|
## Airdrop Admin Operations (WS-ADMIN-B1)
|
|
97
187
|
|
|
@@ -103,25 +193,31 @@ Admin-only commands for triggering airdrop migration batch runs. Per CEO-D-MANUA
|
|
|
103
193
|
|
|
104
194
|
Manually trigger the server-side `airdrop_execute_queue` command on the target environment. Assembles `pending_migrations` rows into per-community batches and (when `BATCH_UPDATE_BALANCES_BROADCAST_ENABLED=true`) broadcasts to the Powch HD wallet. When the broadcast flag is off (default for DEV), batches are assembled but the downstream Powch handler returns `stopped_at_broadcast_boundary` — useful for verifying queue shape without spending MATIC.
|
|
105
195
|
|
|
196
|
+
**Read-only is the default.** Without `--apply`, the command runs as a dry run.
|
|
197
|
+
|
|
106
198
|
**Options:**
|
|
107
199
|
|
|
200
|
+
- `--dry-run`: Read-only preview — encode calldata, estimate gas, validate the net-zero invariant. No PK, no transaction, no state mutation. Mutually exclusive with `--apply`.
|
|
201
|
+
- `--apply`: Live execution. Requires `--signer-pk-file` or the interactive prompt, and requires `--community`.
|
|
202
|
+
- `--community <slug>`: Community slug for per-community batch scoping. **Required for `--apply`**; optional in dry-run mode, where omitting it returns an aggregate preview across communities.
|
|
203
|
+
- `--signer-pk-file <path>`: Path to a file containing the admin signer PK (`0x` + 64 hex). Used for `--apply`; when omitted, the PK is requested at an interactive no-echo prompt. Inline PK flags and environment variables are rejected on security grounds (process-listing leak).
|
|
108
204
|
- `--batch-size <n>`: Cap on users processed this run (server caps at `AIRDROP_MAX_RUN_USERS`; a larger `--batch-size` is clamped).
|
|
109
|
-
- `--dry-run`: Assemble batches but skip the broadcast call entirely. Emits an `on_chain_log` row with `status: dry_run` for audit.
|
|
110
205
|
|
|
111
206
|
**Examples:**
|
|
112
207
|
|
|
113
208
|
```bash
|
|
114
|
-
# Dry run against DEV — safe to run repeatedly; no
|
|
209
|
+
# Dry run against DEV — safe to run repeatedly; no PK, no tx, no state mutation
|
|
115
210
|
descix airdrop execute-queue --env dev --dry-run
|
|
116
211
|
|
|
117
|
-
#
|
|
118
|
-
descix airdrop execute-queue --env dev --
|
|
212
|
+
# Dry run scoped to one community
|
|
213
|
+
descix airdrop execute-queue --env dev --dry-run --community smile
|
|
119
214
|
|
|
120
|
-
#
|
|
121
|
-
descix airdrop execute-queue --env dev
|
|
122
|
-
```
|
|
215
|
+
# Live execution, capped at 10 users this invocation (prompts for the signer PK)
|
|
216
|
+
descix airdrop execute-queue --env dev --apply --community smile --batch-size 10
|
|
123
217
|
|
|
124
|
-
|
|
218
|
+
# Live execution reading the signer PK from a file
|
|
219
|
+
descix airdrop execute-queue --env dev --apply --community smile --signer-pk-file ./signer.key
|
|
220
|
+
```
|
|
125
221
|
|
|
126
222
|
**Testing:**
|
|
127
223
|
|
|
@@ -131,4 +227,4 @@ cd DeSciX_Core/descix-cli && npm test
|
|
|
131
227
|
|
|
132
228
|
## Documentation
|
|
133
229
|
|
|
134
|
-
For full documentation, please refer to the [DeSciX SDK Documentation](
|
|
230
|
+
For full documentation, please refer to the [DeSciX SDK Documentation](./SERVICE_README_sdk.md).
|
|
@@ -143,10 +143,6 @@ KB querying will surface. If you fix something there, the fix reaches readers of
|
|
|
143
143
|
nobody asking the KB — so do not assume KB coverage for a surface that has none. This is a known
|
|
144
144
|
boundary, not a defect.
|
|
145
145
|
|
|
146
|
-
`descix kb chunk`, `descix kb sync` and `descix update kb` still run but are **superseded
|
|
147
|
-
duplicates** — each names `kb corpus sync` as its replacement in its own `--help` and is slated
|
|
148
|
-
for removal. Do not put them in new instructions.
|
|
149
|
-
|
|
150
146
|
#### What you put in a KB changes what comes out of it
|
|
151
147
|
|
|
152
148
|
Retrieval is **vocabulary-dominated**, and when it goes wrong it does so silently. Measured against
|
package/bin/descix.js
CHANGED
|
@@ -11,7 +11,7 @@ import { Command } from 'commander';
|
|
|
11
11
|
import chalk from 'chalk';
|
|
12
12
|
import { DeSciXApiClient } from '../lib/api-client.js';
|
|
13
13
|
import { requireAuth } from '../lib/auth-guard.js';
|
|
14
|
-
import { WorkspaceConfig } from '../lib/workspace-config.js';
|
|
14
|
+
import { WorkspaceConfig, unmappedAppMessage } from '../lib/workspace-config.js';
|
|
15
15
|
import { CLI_VERSION } from '../lib/cli-version.js';
|
|
16
16
|
import { recordInvocationOrigin } from '../lib/origin.js';
|
|
17
17
|
// Chat session pointer + the ONE rule for when a dead pointer may be self-healed.
|
|
@@ -2507,8 +2507,7 @@ siteCommand
|
|
|
2507
2507
|
|
|
2508
2508
|
const appConfig = workspaceConfig.getAppByAppId(ctx.appId);
|
|
2509
2509
|
if (!appConfig) {
|
|
2510
|
-
console.error(chalk.red(
|
|
2511
|
-
console.log(chalk.gray(' Run "npx descix init" to set up your workspace.\n'));
|
|
2510
|
+
console.error(chalk.red(`\n❌ ${unmappedAppMessage(ctx.appId)}\n`));
|
|
2512
2511
|
process.exit(1);
|
|
2513
2512
|
}
|
|
2514
2513
|
|
|
@@ -2518,9 +2517,10 @@ siteCommand
|
|
|
2518
2517
|
console.log(chalk.cyan('\n📁 Adding site scaffold...\n'));
|
|
2519
2518
|
|
|
2520
2519
|
const { copyScaffold } = await import('../lib/core/Hydrator.js');
|
|
2521
|
-
const stats = await copyScaffold('site', appPath, {
|
|
2522
|
-
verbose: true,
|
|
2523
|
-
force: options.force
|
|
2520
|
+
const stats = await copyScaffold('site', appPath, {
|
|
2521
|
+
verbose: true,
|
|
2522
|
+
force: options.force,
|
|
2523
|
+
substitute: { appId: ctx.appId, communityId: ctx.communityId }
|
|
2524
2524
|
});
|
|
2525
2525
|
|
|
2526
2526
|
console.log(chalk.green(`\n✅ Site scaffold added (${stats.copied} files)\n`));
|
|
@@ -2874,8 +2874,7 @@ siteCommand
|
|
|
2874
2874
|
if (!port) {
|
|
2875
2875
|
const appConfig = workspaceConfig.getAppByAppId(appId);
|
|
2876
2876
|
if (!appConfig) {
|
|
2877
|
-
console.error(chalk.red(
|
|
2878
|
-
console.log(chalk.gray(' Run "npx descix init" to set up your workspace.\n'));
|
|
2877
|
+
console.error(chalk.red(`\n❌ ${unmappedAppMessage(appId)}\n`));
|
|
2879
2878
|
process.exit(1);
|
|
2880
2879
|
}
|
|
2881
2880
|
// Read site.port from the live env entry (not from the constructed copy)
|
|
@@ -3116,8 +3115,7 @@ microserviceCommand
|
|
|
3116
3115
|
|
|
3117
3116
|
const appConfig = workspaceConfig.getAppByAppId(ctx.appId);
|
|
3118
3117
|
if (!appConfig) {
|
|
3119
|
-
console.error(chalk.red(
|
|
3120
|
-
console.log(chalk.gray(' Run "npx descix init" to set up your workspace.\n'));
|
|
3118
|
+
console.error(chalk.red(`\n❌ ${unmappedAppMessage(ctx.appId)}\n`));
|
|
3121
3119
|
process.exit(1);
|
|
3122
3120
|
}
|
|
3123
3121
|
|
|
@@ -3149,7 +3147,8 @@ microserviceCommand
|
|
|
3149
3147
|
const { copyScaffold } = await import('../lib/core/Hydrator.js');
|
|
3150
3148
|
const stats = await copyScaffold('microservice', appPath, {
|
|
3151
3149
|
verbose: true,
|
|
3152
|
-
force: options.force
|
|
3150
|
+
force: options.force,
|
|
3151
|
+
substitute: { appId: ctx.appId, communityId: ctx.communityId }
|
|
3153
3152
|
});
|
|
3154
3153
|
|
|
3155
3154
|
// Configuration Injection
|
|
@@ -3158,15 +3157,16 @@ microserviceCommand
|
|
|
3158
3157
|
const manifestPath = path.join(microserviceDir, 'manifest.json');
|
|
3159
3158
|
const overridesPath = path.join(microserviceDir, 'dev-overrides.json');
|
|
3160
3159
|
|
|
3161
|
-
// 1. Inject
|
|
3160
|
+
// 1. Inject Port into defaults-config.json.
|
|
3161
|
+
// IDENTITY IS NOT SET HERE. copyScaffold resolves {{APP_ID}}/{{COMMUNITY_ID}} during the
|
|
3162
|
+
// copy, so re-deriving them here would be a second derivation of one fact. The port is a
|
|
3163
|
+
// workspace-derived RUNTIME value with no scaffold token, so it stays.
|
|
3162
3164
|
try {
|
|
3163
3165
|
const defaultsContent = await fs.readFile(defaultsPath, 'utf-8');
|
|
3164
3166
|
const defaults = JSON.parse(defaultsContent);
|
|
3165
|
-
defaults.community_id = ctx.communityId;
|
|
3166
|
-
defaults.app_id = ctx.appId;
|
|
3167
3167
|
defaults.LOCAL_PORT = microservicePort;
|
|
3168
3168
|
await fs.writeFile(defaultsPath, JSON.stringify(defaults, null, 2));
|
|
3169
|
-
console.log(chalk.gray(` ✓ Injected
|
|
3169
|
+
console.log(chalk.gray(` ✓ Injected port into defaults-config.json`));
|
|
3170
3170
|
} catch (err) {
|
|
3171
3171
|
console.warn(chalk.yellow(` ⚠ Could not update defaults-config.json: ${err.message}`));
|
|
3172
3172
|
}
|
|
@@ -3188,20 +3188,19 @@ microserviceCommand
|
|
|
3188
3188
|
console.warn(chalk.yellow(` ⚠ Could not claim package.json: ${err.message}`));
|
|
3189
3189
|
}
|
|
3190
3190
|
|
|
3191
|
-
// 2. Inject
|
|
3191
|
+
// 2. Inject Port into manifest.json.
|
|
3192
|
+
// IDENTITY IS NOT SET HERE. copyScaffold resolved {{APP_ID}}/{{COMMUNITY_ID}} during the
|
|
3193
|
+
// copy, so setting app_id/community_id/name again would be a second derivation of one fact —
|
|
3194
|
+
// the exact mirror-drift shape that let a scaffold ship `community_id: 'descix'` while this
|
|
3195
|
+
// block quietly wrote the right value into a different file.
|
|
3196
|
+
// No domain is injected. A service does not declare its own domain — the platform derives
|
|
3197
|
+
// it ({app_id}.{SITE_DOMAIN}) at registration, and refuses a manifest that declares one.
|
|
3192
3198
|
try {
|
|
3193
3199
|
const manifestContent = await fs.readFile(manifestPath, 'utf-8');
|
|
3194
3200
|
const manifest = JSON.parse(manifestContent);
|
|
3195
|
-
manifest.service.app_id = ctx.appId;
|
|
3196
|
-
manifest.service.community_id = ctx.communityId;
|
|
3197
|
-
manifest.service.name = ctx.appId;
|
|
3198
|
-
// No domain is injected. A service does not declare its own domain — the platform derives
|
|
3199
|
-
// it ({app_id}.{SITE_DOMAIN}) at registration, and refuses a manifest that declares one.
|
|
3200
|
-
// The literal this replaced was `${ctx.appId}.descix.net`: the PROD host, scaffolded into
|
|
3201
|
-
// every service regardless of the env it would be registered against.
|
|
3202
3201
|
manifest.service.debugPort = microservicePort;
|
|
3203
3202
|
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
3204
|
-
console.log(chalk.gray(` ✓ Injected
|
|
3203
|
+
console.log(chalk.gray(` ✓ Injected port into manifest.json`));
|
|
3205
3204
|
} catch (err) {
|
|
3206
3205
|
console.warn(chalk.yellow(` ⚠ Could not update manifest.json: ${err.message}`));
|
|
3207
3206
|
}
|
package/lib/commands/auth.js
CHANGED
|
@@ -275,6 +275,7 @@ export async function loginDevice(options = {}) {
|
|
|
275
275
|
diag(chalk.gray(` ${walletPath}\n`));
|
|
276
276
|
|
|
277
277
|
// Sync auto-purchase apps
|
|
278
|
+
let entitlementsSynced = true;
|
|
278
279
|
spinner.start('Syncing app entitlements...');
|
|
279
280
|
try {
|
|
280
281
|
apiClient.setCredentials({
|
|
@@ -293,10 +294,25 @@ export async function loginDevice(options = {}) {
|
|
|
293
294
|
spinner.succeed(chalk.gray('App entitlements up to date'));
|
|
294
295
|
}
|
|
295
296
|
} catch (error) {
|
|
297
|
+
entitlementsSynced = false;
|
|
296
298
|
spinner.warn(chalk.yellow('Could not sync app entitlements'));
|
|
297
299
|
diag(chalk.gray(` ${error.message}\n`));
|
|
298
300
|
}
|
|
299
|
-
|
|
301
|
+
|
|
302
|
+
// DO NOT CLAIM SUCCESS ON A PARTIAL LOGIN, AND DO NOT EXIT 0.
|
|
303
|
+
// Credentials are saved either way, so this is genuinely partial rather than a failed login —
|
|
304
|
+
// but entitlements are what grant app access, so "you can now use all DeSciX CLI commands" is
|
|
305
|
+
// false when the sync failed. A script branching on the exit code is the caller least able to
|
|
306
|
+
// read the yellow warning above, which is why the code is the sharper half of this fix.
|
|
307
|
+
if (!entitlementsSynced) {
|
|
308
|
+
diag(chalk.yellow(
|
|
309
|
+
'Logged in, but app entitlements did NOT sync. Credentials are saved; commands needing an ' +
|
|
310
|
+
'entitlement may fail until you re-run `descix login`.\n'
|
|
311
|
+
));
|
|
312
|
+
process.exitCode = 1;
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
300
316
|
diag(chalk.white('You can now use all DeSciX CLI commands!\n'));
|
|
301
317
|
|
|
302
318
|
} catch (error) {
|
package/lib/commands/init.js
CHANGED
|
@@ -13,6 +13,10 @@ import { dirname, join } from 'path';
|
|
|
13
13
|
// clone.js is imported dynamically inside the invite flow to avoid circular deps
|
|
14
14
|
import { WorkspaceConfig } from '../workspace-config.js';
|
|
15
15
|
import { generateAgentFiles } from '../agent-files.js';
|
|
16
|
+
// The canonical KB-sync surface is owned by retired-kb-sync.js. Consume the constant: a literal
|
|
17
|
+
// spelled here is a second derivation of the same fact, and the removed verb this replaced
|
|
18
|
+
// reached a developer who had done everything right.
|
|
19
|
+
import { CANONICAL_KB_SYNC } from './retired-kb-sync.js';
|
|
16
20
|
|
|
17
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
22
|
const __dirname = dirname(__filename);
|
|
@@ -208,8 +212,9 @@ export async function runInit(apiClient, options = {}) {
|
|
|
208
212
|
|
|
209
213
|
console.log(chalk.cyan('\n─── Next Steps ───\n'));
|
|
210
214
|
console.log(chalk.white(` 1. descix app init -a ${appId} -c ${communityId} # register app on platform`));
|
|
211
|
-
console.log(chalk.
|
|
212
|
-
console.log(chalk.white(` 3.
|
|
215
|
+
console.log(chalk.gray(` 2. Create a corpus manifest at .descix/manifests/General.json`));
|
|
216
|
+
console.log(chalk.white(` 3. ${CANONICAL_KB_SYNC} -a ${appId} # sync KB to Pinecone`));
|
|
217
|
+
console.log(chalk.white(` 4. descix chat -c ${communityId} -a ${appId} -q "test" # verify RAG\n`));
|
|
213
218
|
console.log(chalk.green('✅ Workspace initialized.\n'));
|
|
214
219
|
rl.close();
|
|
215
220
|
return { created: ['.descix/workspace.json'], skipped: [], warnings: [] };
|
|
@@ -250,7 +255,7 @@ export async function initWorkspace(options) {
|
|
|
250
255
|
created,
|
|
251
256
|
skipped: [],
|
|
252
257
|
warnings: [],
|
|
253
|
-
nextSteps: [`descix app init -a ${appId} -c ${communityId}`,
|
|
258
|
+
nextSteps: [`descix app init -a ${appId} -c ${communityId}`, `${CANONICAL_KB_SYNC} -a ${appId}`]
|
|
254
259
|
};
|
|
255
260
|
}
|
|
256
261
|
|
package/lib/commands/kb.js
CHANGED
|
@@ -27,6 +27,10 @@ import { hydrateKb, pushStaging, checkStagingFiles } from '../core/Hydrator.js';
|
|
|
27
27
|
import { processKb } from '../core/Chunker.js';
|
|
28
28
|
import { syncKb, getSyncStatus } from '../core/Syncer.js';
|
|
29
29
|
import * as driveADC from '../google-storage-adc.js';
|
|
30
|
+
// The one canonical KB-sync surface, from its owner. Never spell it as a literal here: these
|
|
31
|
+
// commands' next steps are read by a developer as instructions, so they must name the live verb
|
|
32
|
+
// and nothing else.
|
|
33
|
+
import { CANONICAL_KB_SYNC } from './retired-kb-sync.js';
|
|
30
34
|
|
|
31
35
|
// ============ Pull Command ============
|
|
32
36
|
|
|
@@ -110,8 +114,7 @@ export async function runKbPull(apiClient, options) {
|
|
|
110
114
|
if (!options.quiet) {
|
|
111
115
|
console.log(chalk.cyan('\n📋 Next steps:'));
|
|
112
116
|
console.log(chalk.gray(' 1. Review files in kb/' + kbId + '/'));
|
|
113
|
-
console.log(chalk.gray(
|
|
114
|
-
console.log(chalk.gray(' 3. Run "descix kb sync" to push to Pinecone\n'));
|
|
117
|
+
console.log(chalk.gray(` 2. Run "${CANONICAL_KB_SYNC} -a <app_id>" to push to Pinecone\n`));
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
return result;
|
|
@@ -220,8 +223,8 @@ export async function runKbPush(apiClient, options) {
|
|
|
220
223
|
// Show next steps
|
|
221
224
|
if (!options.quiet) {
|
|
222
225
|
console.log(chalk.cyan('\n📋 Next steps:'));
|
|
223
|
-
console.log(chalk.gray(' 1. Run "descix
|
|
224
|
-
console.log(chalk.gray(
|
|
226
|
+
console.log(chalk.gray(' 1. Run "descix drive pull" to sync all Drive content'));
|
|
227
|
+
console.log(chalk.gray(` 2. Run "${CANONICAL_KB_SYNC} -a <app_id>" to push to Pinecone\n`));
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
return result;
|
package/lib/commands/update.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Commands for updating resources based on workspace.json context detection:
|
|
5
5
|
* descix update # Auto-detect what to update
|
|
6
6
|
* descix update app # Sync app assets to Drive
|
|
7
|
-
* descix update kb # Sync KB + vectorize
|
|
8
7
|
* descix update site # Deploy site to GCS
|
|
9
8
|
* descix update all # Update everything
|
|
10
9
|
*
|
package/lib/core/Hydrator.js
CHANGED
|
@@ -726,11 +726,15 @@ export async function checkStagingFiles(stagingDir) {
|
|
|
726
726
|
*
|
|
727
727
|
* @param {string} scaffoldType - 'site' or 'microservice'
|
|
728
728
|
* @param {string} appPath - Path to the app directory
|
|
729
|
-
* @param {Object} options - { verbose, force }
|
|
729
|
+
* @param {Object} options - { verbose, force, substitute }
|
|
730
|
+
* @param {{appId: string, communityId: string, appName?: string}} [options.substitute] - the
|
|
731
|
+
* caller's real identity. Supply it for any scaffold a developer will actually use: without it
|
|
732
|
+
* the copy is verbatim and every {{TOKEN}} ships unresolved. Omitting it is only appropriate
|
|
733
|
+
* when materializing the raw template itself.
|
|
730
734
|
* @returns {Promise<{copied: number}>}
|
|
731
735
|
*/
|
|
732
736
|
export async function copyScaffold(scaffoldType, appPath, options = {}) {
|
|
733
|
-
const { verbose = false, force = false } = options;
|
|
737
|
+
const { verbose = false, force = false, substitute = null } = options;
|
|
734
738
|
|
|
735
739
|
const scaffoldDir = SCAFFOLD_DIRS[scaffoldType];
|
|
736
740
|
if (!scaffoldDir) {
|
|
@@ -767,8 +771,36 @@ export async function copyScaffold(scaffoldType, appPath, options = {}) {
|
|
|
767
771
|
|
|
768
772
|
// Copy scaffold recursively
|
|
769
773
|
const stats = { copied: 0 };
|
|
770
|
-
|
|
771
|
-
|
|
774
|
+
let values = null;
|
|
775
|
+
if (substitute) {
|
|
776
|
+
if (!substitute.appId || !substitute.communityId) {
|
|
777
|
+
throw new Error(
|
|
778
|
+
'copyScaffold: substitute requires both appId and communityId. Refusing to resolve a ' +
|
|
779
|
+
'scaffold against a partial identity — a half-substituted scaffold is harder to spot ' +
|
|
780
|
+
'than an unsubstituted one.'
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
values = {
|
|
784
|
+
APP_ID: substitute.appId,
|
|
785
|
+
COMMUNITY_ID: substitute.communityId,
|
|
786
|
+
APP_NAME: substitute.appName || substitute.appId,
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const unresolved = [];
|
|
791
|
+
await copyDir(scaffoldDir, targetDir, stats, verbose, values, unresolved);
|
|
792
|
+
|
|
793
|
+
// FAIL LOUD. A placeholder that survives the copy reaches the developer as literal braces, and
|
|
794
|
+
// that is exactly the class of defect this substitution exists to end — so it must never be a
|
|
795
|
+
// warning. Name every offender so a new token added to a template cannot ship unnoticed.
|
|
796
|
+
if (unresolved.length > 0) {
|
|
797
|
+
throw new Error(
|
|
798
|
+
`Scaffold "${scaffoldType}" has ${unresolved.length} unresolved placeholder(s): ` +
|
|
799
|
+
`${unresolved.join(', ')}. Every {{TOKEN}} in a scaffold needs a value in copyScaffold's ` +
|
|
800
|
+
`substitute map (see templates/scaffolds/README.md for the documented set).`
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
|
|
772
804
|
return stats;
|
|
773
805
|
}
|
|
774
806
|
|
|
@@ -785,21 +817,66 @@ async function hasContentInDir(dirPath) {
|
|
|
785
817
|
}
|
|
786
818
|
|
|
787
819
|
/**
|
|
788
|
-
*
|
|
820
|
+
* THE ONE OWNER of scaffold placeholder substitution.
|
|
821
|
+
*
|
|
822
|
+
* templates/scaffolds/README.md has documented this contract all along — "Scaffolds use these
|
|
823
|
+
* placeholders (replaced during copy): {{APP_NAME}}, {{APP_ID}}, {{COMMUNITY_ID}}" — but nothing
|
|
824
|
+
* ever performed the replacement. Both scaffolds shipped their placeholders and their sample
|
|
825
|
+
* literals verbatim, so a developer's site was titled `{{APP_NAME}}` and the sample handler
|
|
826
|
+
* queried a community that is not theirs.
|
|
827
|
+
*
|
|
828
|
+
* SUBSTITUTION, NOT A SECOND LITERAL. The fix resolves each token to the caller's real values;
|
|
829
|
+
* it does not swap one hardcoded community for another. That is what lets the same scaffold serve
|
|
830
|
+
* every community without the CLI knowing any of their names.
|
|
831
|
+
*
|
|
832
|
+
* @param {string} text - file contents
|
|
833
|
+
* @param {Record<string,string>} values - token name (without braces) -> replacement
|
|
834
|
+
* @returns {string}
|
|
789
835
|
*/
|
|
790
|
-
|
|
836
|
+
function applyScaffoldTokens(text, values) {
|
|
837
|
+
return text.replace(/\{\{([A-Z_]+)\}\}/g, (whole, token) =>
|
|
838
|
+
Object.prototype.hasOwnProperty.call(values, token) ? values[token] : whole
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/** A NUL byte is the cheap, dependency-free binary tell. Never rewrite bytes we cannot read. */
|
|
843
|
+
function looksBinary(buf) {
|
|
844
|
+
return buf.includes(0);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Recursively copy a directory, optionally substituting scaffold tokens on the way.
|
|
849
|
+
*
|
|
850
|
+
* @param {Record<string,string>|null} values - when null, the copy is byte-for-byte verbatim
|
|
851
|
+
* @param {string[]} unresolved - collects `file:{{TOKEN}}` for tokens no value was supplied for
|
|
852
|
+
*/
|
|
853
|
+
async function copyDir(src, dest, stats, verbose, values = null, unresolved = [], relBase = '') {
|
|
791
854
|
await fs.mkdir(dest, { recursive: true });
|
|
792
|
-
|
|
855
|
+
|
|
793
856
|
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
794
|
-
|
|
857
|
+
|
|
795
858
|
for (const entry of entries) {
|
|
796
859
|
const srcPath = path.join(src, entry.name);
|
|
797
860
|
const destPath = path.join(dest, entry.name);
|
|
798
|
-
|
|
861
|
+
const rel = relBase ? `${relBase}/${entry.name}` : entry.name;
|
|
862
|
+
|
|
799
863
|
if (entry.isDirectory()) {
|
|
800
|
-
await copyDir(srcPath, destPath, stats, verbose);
|
|
864
|
+
await copyDir(srcPath, destPath, stats, verbose, values, unresolved, rel);
|
|
801
865
|
} else {
|
|
802
|
-
|
|
866
|
+
if (values) {
|
|
867
|
+
const buf = await fs.readFile(srcPath);
|
|
868
|
+
if (looksBinary(buf)) {
|
|
869
|
+
await fs.copyFile(srcPath, destPath);
|
|
870
|
+
} else {
|
|
871
|
+
const out = applyScaffoldTokens(buf.toString('utf8'), values);
|
|
872
|
+
// Anything still wearing braces had no value supplied. Collect it rather than shipping
|
|
873
|
+
// it: an unresolved placeholder reaching a developer is the defect this exists to end.
|
|
874
|
+
for (const m of out.matchAll(/\{\{([A-Z_]+)\}\}/g)) unresolved.push(`${rel}:{{${m[1]}}}`);
|
|
875
|
+
await fs.writeFile(destPath, out);
|
|
876
|
+
}
|
|
877
|
+
} else {
|
|
878
|
+
await fs.copyFile(srcPath, destPath);
|
|
879
|
+
}
|
|
803
880
|
stats.copied++;
|
|
804
881
|
if (verbose) console.log(` Copied: ${entry.name}`);
|
|
805
882
|
}
|
package/lib/workspace-config.js
CHANGED
|
@@ -2,6 +2,29 @@ import * as fs from 'fs/promises';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { ENV_ORIGINS } from '@descix/app-sdk/dev';
|
|
4
4
|
import { resolveOrigin } from './origin.js';
|
|
5
|
+
// The one canonical KB-sync surface, from its owner. The example in requireContext() is a remedy
|
|
6
|
+
// a stuck developer will copy verbatim, so it must resolve to the live verb rather than a literal
|
|
7
|
+
// that can go stale where nobody is watching.
|
|
8
|
+
import { CANONICAL_KB_SYNC } from './commands/retired-kb-sync.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* THE ONE OWNER of "this app is not mapped in workspace.json, here is how to fix it".
|
|
12
|
+
*
|
|
13
|
+
* This text existed as three byte-identical copies in this file, and TWO OTHER call sites in
|
|
14
|
+
* bin/descix.js answered the same condition with a DIFFERENT and wrong remedy — `npx descix init`,
|
|
15
|
+
* which initialises a WORKSPACE and cannot map an app, so a developer who followed it stayed
|
|
16
|
+
* exactly as stuck. That is the mirror-drift tell: the same condition explained two ways, and the
|
|
17
|
+
* wrong one was the one a developer actually hit on the onboarding path.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} appId - the app that is not mapped
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*/
|
|
22
|
+
export function unmappedAppMessage(appId) {
|
|
23
|
+
return (
|
|
24
|
+
`App "${appId}" is not mapped in workspace.json. ` +
|
|
25
|
+
'Use `descix app init` to register, or `descix app set-localpath -a <id> -p <path>` to repoint.'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
5
28
|
|
|
6
29
|
/**
|
|
7
30
|
* ONE OWNER for turning a workspace-relative localPath into an absolute path.
|
|
@@ -248,6 +271,17 @@ export class WorkspaceConfig {
|
|
|
248
271
|
/**
|
|
249
272
|
* Save workspace configuration in v2.1 format.
|
|
250
273
|
* Always writes env.platform / env.products structure.
|
|
274
|
+
*
|
|
275
|
+
* PRESERVES KEYS IT DOES NOT UNDERSTAND. This method used to serialize a fixed field list from
|
|
276
|
+
* memory over the whole file, so ANY top-level key it did not itself enumerate was silently
|
|
277
|
+
* destroyed by the next `descix app set-port` — a writer re-serializing a known schema and
|
|
278
|
+
* discarding the rest. That is the same defect class as a record writer dropping a field it was
|
|
279
|
+
* never taught about, and it is invisible: the write succeeds, the file looks well-formed, and
|
|
280
|
+
* the loss is only discovered by whoever needed the missing key.
|
|
281
|
+
*
|
|
282
|
+
* The on-disk file is re-read at save time rather than trusted from construction, because the
|
|
283
|
+
* instance may have been held across another writer's save.
|
|
284
|
+
*
|
|
251
285
|
* @param {string} [workspaceRoot] - Workspace root directory (uses stored root if not provided)
|
|
252
286
|
* @returns {Promise<string>} Path to saved config
|
|
253
287
|
*/
|
|
@@ -266,7 +300,32 @@ export class WorkspaceConfig {
|
|
|
266
300
|
// Build env block for v2.1 output
|
|
267
301
|
const envBlock = (this.env && Object.keys(this.env).length > 0) ? this.env : null;
|
|
268
302
|
|
|
303
|
+
// The keys this writer OWNS. Everything else on disk is carried through untouched.
|
|
304
|
+
const OWNED = ['version', 'workspaceRoot', 'type', 'env', 'driveConfig'];
|
|
305
|
+
|
|
306
|
+
let onDisk = {};
|
|
307
|
+
try {
|
|
308
|
+
const existing = await fs.readFile(configPath, 'utf-8');
|
|
309
|
+
const parsed = JSON.parse(existing);
|
|
310
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) onDisk = parsed;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
// ENOENT is the ordinary first save. A PRESENT-BUT-UNPARSEABLE file is different: carrying
|
|
313
|
+
// on would overwrite content we could not read, so refuse and name the file.
|
|
314
|
+
if (err.code !== 'ENOENT') {
|
|
315
|
+
throw new Error(
|
|
316
|
+
`Refusing to overwrite ${configPath}: it exists but could not be read as JSON ` +
|
|
317
|
+
`(${err.message}). Fix or remove it — saving now would destroy whatever it holds.`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const carried = {};
|
|
323
|
+
for (const [k, v] of Object.entries(onDisk)) {
|
|
324
|
+
if (!OWNED.includes(k)) carried[k] = v;
|
|
325
|
+
}
|
|
326
|
+
|
|
269
327
|
const configData = {
|
|
328
|
+
...carried,
|
|
270
329
|
version: '2.1',
|
|
271
330
|
workspaceRoot: path.resolve(root),
|
|
272
331
|
type: this.type,
|
|
@@ -508,7 +567,7 @@ export class WorkspaceConfig {
|
|
|
508
567
|
'Options:\n' +
|
|
509
568
|
' 1. cd into an app directory\n' +
|
|
510
569
|
' 2. Use flag: -a <app_id>\n\n' +
|
|
511
|
-
|
|
570
|
+
`Example: npx ${CANONICAL_KB_SYNC} -a daita`
|
|
512
571
|
);
|
|
513
572
|
}
|
|
514
573
|
|
|
@@ -637,8 +696,7 @@ export class WorkspaceConfig {
|
|
|
637
696
|
|
|
638
697
|
if (!entry) {
|
|
639
698
|
throw new Error(
|
|
640
|
-
|
|
641
|
-
'Use `descix app init` to register, or `descix app set-localpath -a <id> -p <path>` to repoint.'
|
|
699
|
+
unmappedAppMessage(appId)
|
|
642
700
|
);
|
|
643
701
|
}
|
|
644
702
|
|
|
@@ -687,8 +745,7 @@ export class WorkspaceConfig {
|
|
|
687
745
|
|
|
688
746
|
if (!entry) {
|
|
689
747
|
throw new Error(
|
|
690
|
-
|
|
691
|
-
'Use `descix app init` to register, or `descix app set-localpath -a <id> -p <path>` to repoint.'
|
|
748
|
+
unmappedAppMessage(appId)
|
|
692
749
|
);
|
|
693
750
|
}
|
|
694
751
|
|
|
@@ -743,8 +800,7 @@ export class WorkspaceConfig {
|
|
|
743
800
|
|
|
744
801
|
if (!entry) {
|
|
745
802
|
throw new Error(
|
|
746
|
-
|
|
747
|
-
'Use `descix app init` to register, or `descix app set-localpath -a <id> -p <path>` to repoint.'
|
|
803
|
+
unmappedAppMessage(appId)
|
|
748
804
|
);
|
|
749
805
|
}
|
|
750
806
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@descix/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "DeSciX CLI and MCP Server - Unified command-line interface for DeSciX platform",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@descix/app-sdk": "^0.1.2",
|
|
33
|
-
"@descix/sdk": "^
|
|
33
|
+
"@descix/sdk": "^2.0.0",
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
35
35
|
"axios": "^1.7.9",
|
|
36
36
|
"chalk": "^5.6.2",
|
|
@@ -151,9 +151,6 @@ descix kb corpus sync -c <community> -a <app> -k <KB>
|
|
|
151
151
|
descix kb corpus status -c <community> -a <app>
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
`descix update kb`, `descix kb chunk` and `descix kb sync` still run, but each names
|
|
155
|
-
`kb corpus sync` as its replacement in its own `--help` and is slated for removal. Do not put
|
|
156
|
-
them in new instructions.
|
|
157
154
|
|
|
158
155
|
## Local Development
|
|
159
156
|
|
package/templates/agent-cline.md
CHANGED
|
@@ -115,9 +115,6 @@ descix kb corpus sync -c <community> -a <app> -k <KB>
|
|
|
115
115
|
descix kb corpus status -c <community> -a <app>
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
`descix update kb`, `descix kb chunk` and `descix kb sync` still run, but each names
|
|
119
|
-
`kb corpus sync` as its replacement in its own `--help` and is slated for removal. Do not put
|
|
120
|
-
them in new instructions.
|
|
121
118
|
|
|
122
119
|
## Local Development
|
|
123
120
|
|
|
@@ -115,9 +115,6 @@ descix kb corpus sync -c <community> -a <app> -k <KB>
|
|
|
115
115
|
descix kb corpus status -c <community> -a <app>
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
`descix update kb`, `descix kb chunk` and `descix kb sync` still run, but each names
|
|
119
|
-
`kb corpus sync` as its replacement in its own `--help` and is slated for removal. Do not put
|
|
120
|
-
them in new instructions.
|
|
121
118
|
|
|
122
119
|
## Local Development
|
|
123
120
|
|
|
@@ -115,9 +115,6 @@ descix kb corpus sync -c <community> -a <app> -k <KB>
|
|
|
115
115
|
descix kb corpus status -c <community> -a <app>
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
`descix update kb`, `descix kb chunk` and `descix kb sync` still run, but each names
|
|
119
|
-
`kb corpus sync` as its replacement in its own `--help` and is slated for removal. Do not put
|
|
120
|
-
them in new instructions.
|
|
121
118
|
|
|
122
119
|
## Local Development
|
|
123
120
|
|
|
@@ -9,7 +9,7 @@ This folder contains the documents that power the AI assistant. Add your content
|
|
|
9
9
|
## How It Works
|
|
10
10
|
|
|
11
11
|
1. **Add Documents**: Upload markdown, text, or PDF files to this folder
|
|
12
|
-
2. **Sync**: Use `descix kb sync
|
|
12
|
+
2. **Sync**: Use `descix kb corpus sync -a <app_id>` to process and vectorize your documents
|
|
13
13
|
3. **Query**: Users can now ask questions and get answers based on your content
|
|
14
14
|
|
|
15
15
|
## Folder Structure
|
|
@@ -9,7 +9,7 @@ This folder contains the documents that power the AI assistant. Add your content
|
|
|
9
9
|
## How It Works
|
|
10
10
|
|
|
11
11
|
1. **Add Documents**: Upload markdown, text, or PDF files to this folder
|
|
12
|
-
2. **Sync**: Use `descix kb sync
|
|
12
|
+
2. **Sync**: Use `descix kb corpus sync -a <app_id>` to process and vectorize your documents
|
|
13
13
|
3. **Query**: Users can now ask questions and get answers based on your content
|
|
14
14
|
|
|
15
15
|
## Folder Structure
|
|
@@ -237,7 +237,7 @@ tell_me_how({
|
|
|
237
237
|
execute_remote_command({
|
|
238
238
|
command: "create_app_for_community",
|
|
239
239
|
params: {
|
|
240
|
-
community_id: "
|
|
240
|
+
community_id: "{{COMMUNITY_ID}}",
|
|
241
241
|
app_name: "My New App"
|
|
242
242
|
}
|
|
243
243
|
})
|
|
@@ -285,9 +285,9 @@ ask_question_to_app({
|
|
|
285
285
|
{
|
|
286
286
|
"version": "2.1",
|
|
287
287
|
"env": {
|
|
288
|
-
"platform": { "appId": "daita", "communityId": "
|
|
288
|
+
"platform": { "appId": "daita", "communityId": "daita", "localPath": ".", "kbId": "General" },
|
|
289
289
|
"products": [
|
|
290
|
-
{ "appId": "docs", "communityId": "
|
|
290
|
+
{ "appId": "docs", "communityId": "daita", "kbId": "sdk", "localPath": "descix-docs" }
|
|
291
291
|
]
|
|
292
292
|
}
|
|
293
293
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"service": {
|
|
3
|
-
"name": "
|
|
3
|
+
"name": "{{APP_ID}}",
|
|
4
4
|
"version": "1.0.0",
|
|
5
5
|
"description": "Sample DeSciX Add-on Service",
|
|
6
6
|
"healthEndpoint": "/health",
|
|
7
7
|
"debugPort": 3003,
|
|
8
|
-
"app_id": "
|
|
9
|
-
"community_id": "
|
|
8
|
+
"app_id": "{{APP_ID}}",
|
|
9
|
+
"community_id": "{{COMMUNITY_ID}}"
|
|
10
10
|
},
|
|
11
11
|
"commands": {
|
|
12
12
|
"sample_hello": {
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"description": "Message to echo"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
|
-
"required": [
|
|
24
|
+
"required": [
|
|
25
|
+
"message"
|
|
26
|
+
]
|
|
25
27
|
}
|
|
26
28
|
},
|
|
27
29
|
"sample_analyze": {
|
|
@@ -36,7 +38,9 @@
|
|
|
36
38
|
"description": "Topic to analyze"
|
|
37
39
|
}
|
|
38
40
|
},
|
|
39
|
-
"required": [
|
|
41
|
+
"required": [
|
|
42
|
+
"query"
|
|
43
|
+
]
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
},
|
|
@@ -33,8 +33,8 @@ export const commands = {
|
|
|
33
33
|
// CALL CORE TOOL: query_knowledge_base
|
|
34
34
|
// This goes Service -> Cloud -> Vector DB -> Cloud -> Service
|
|
35
35
|
const ragResult = await mcpClient.callTool('query_knowledge_base', {
|
|
36
|
-
community_id: '
|
|
37
|
-
app_id: '
|
|
36
|
+
community_id: '{{COMMUNITY_ID}}',
|
|
37
|
+
app_id: '{{APP_ID}}',
|
|
38
38
|
kb_id: 'sdk',
|
|
39
39
|
query: query
|
|
40
40
|
}, _descix);
|