@descix/cli 1.0.1 → 1.0.3
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 +9 -9
- 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 +209 -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 +30 -0
- 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
|
@@ -1,78 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DeSciX
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* DeSciX service api-client — the CLI's REST api-client running in THIS microservice.
|
|
3
|
+
*
|
|
4
|
+
* AUTH MODEL — the microservice IS the CLI's api-client running in the cloud
|
|
5
|
+
* (CEO-D-2026-06-02-APP-MICROSERVICE-IS-CLI-CLIENT-WALLET-SIG):
|
|
6
|
+
* This service authenticates to /apifront EXACTLY like the CLI: it holds the DEVELOPER'S OWN
|
|
7
|
+
* durable credential (wallet_address + signature, from the developer's .descix/wallet.json),
|
|
8
|
+
* presents it to `reconnect_by_wallet` to mint a session access_token, and calls /apifront AS
|
|
9
|
+
* THE DEVELOPER. There is NO SA-OIDC, NO delegate signature, NO X-NFT-ID, NO SERVICE_KEY, NO
|
|
10
|
+
* Signer. The packaged `@descix/cli` `createServiceApiClient` does all of this.
|
|
11
|
+
*
|
|
12
|
+
* CREDENTIAL PLACEMENT (org rule — no checked-in credentials):
|
|
13
|
+
* The developer copies their wallet `signature` + `wallet_address` into the service config as
|
|
14
|
+
* `DEVELOPER_SIGNATURE` / `DEVELOPER_WALLET_ADDRESS`, in the GITIGNORED dev-overrides.json (dev)
|
|
15
|
+
* / a SECRET (prod) — NEVER the checked-in defaults-config.json (the signature is a credential).
|
|
16
|
+
* We HARD-FAIL (no fallback) if either is absent.
|
|
6
17
|
*/
|
|
7
18
|
|
|
8
|
-
import
|
|
19
|
+
import { createServiceApiClient } from '@descix/cli/service-api-client';
|
|
9
20
|
import { utils } from './utils.js';
|
|
10
|
-
import { Signer } from '@descix/sdk'; // Assuming descix-sdk is available in the container
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
let _service = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build (once) the developer-authed service api-client for this microservice.
|
|
26
|
+
* Reads the developer credential from config and HARD-FAILS if absent (no fallback).
|
|
27
|
+
*/
|
|
28
|
+
function getService() {
|
|
29
|
+
if (_service) return _service;
|
|
30
|
+
if (!utils.CORE_API_URL) {
|
|
31
|
+
throw new Error('CORE_API_URL not configured.');
|
|
18
32
|
}
|
|
33
|
+
if (!utils.DEVELOPER_WALLET_ADDRESS || !utils.DEVELOPER_SIGNATURE) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
'Developer credential missing: set DEVELOPER_WALLET_ADDRESS + DEVELOPER_SIGNATURE in the ' +
|
|
36
|
+
'GITIGNORED dev-overrides.json (dev) / a secret (prod) — never defaults-config.json. ' +
|
|
37
|
+
'These are the developer\'s own wallet_address + signature (from .descix/wallet.json). No fallback.'
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
_service = createServiceApiClient({
|
|
41
|
+
baseUrl: utils.CORE_API_URL,
|
|
42
|
+
walletAddress: utils.DEVELOPER_WALLET_ADDRESS,
|
|
43
|
+
signature: utils.DEVELOPER_SIGNATURE,
|
|
44
|
+
});
|
|
45
|
+
return _service;
|
|
46
|
+
}
|
|
19
47
|
|
|
48
|
+
class McpClient {
|
|
20
49
|
/**
|
|
21
|
-
* Call a
|
|
22
|
-
* @param {string}
|
|
23
|
-
* @param {Object} args
|
|
24
|
-
* @
|
|
25
|
-
* @returns {Promise<any>} - Tool result
|
|
50
|
+
* Call a Core command via /apifront, authenticated AS the developer.
|
|
51
|
+
* @param {string} command - Command name (e.g. 'app_records_put', 'get_app_asset')
|
|
52
|
+
* @param {Object} args - Command params
|
|
53
|
+
* @returns {Promise<any>} - Core's `message` payload on success
|
|
26
54
|
*/
|
|
27
|
-
async callTool(
|
|
28
|
-
|
|
29
|
-
throw new Error('CORE_API_URL not configured');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Construct the payload simulating a PWA or internal request
|
|
33
|
-
const payload = {
|
|
34
|
-
command: toolName,
|
|
35
|
-
params: {
|
|
36
|
-
...args,
|
|
37
|
-
// Critical: Pass the original user context back to Core
|
|
38
|
-
// The Core trusts this service (authenticated via Service Secret or VPC)
|
|
39
|
-
// In this simplified template, we pass the user ID we received
|
|
40
|
-
user_id: context?.user?.id || utils.OWNER_USER_ID,
|
|
41
|
-
// If we have owner credentials, use them as fallback for auth
|
|
42
|
-
wallet_address: utils.OWNER_WALLET_ADDRESS,
|
|
43
|
-
signature: utils.OWNER_SIGNATURE
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
console.log(`[McpClient] Calling ${toolName} on ${utils.CORE_API_URL}`);
|
|
49
|
-
|
|
50
|
-
// Get headers (Signature or Legacy Secret)
|
|
51
|
-
const headers = {
|
|
52
|
-
'Content-Type': 'application/json'
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
if (this.signer) {
|
|
56
|
-
// Sign the payload string
|
|
57
|
-
const signatureHeaders = this.signer.getHeaders(JSON.stringify(payload));
|
|
58
|
-
Object.assign(headers, signatureHeaders);
|
|
59
|
-
} else if (utils.SERVICE_SECRET) {
|
|
60
|
-
// Fallback to legacy secret
|
|
61
|
-
headers['Authorization'] = `Bearer ${utils.SERVICE_SECRET}`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const response = await axios.post(utils.CORE_API_URL, payload, { headers });
|
|
65
|
-
|
|
66
|
-
if (response.data?.status === 'OK') {
|
|
67
|
-
return response.data.message;
|
|
68
|
-
} else {
|
|
69
|
-
throw new Error(response.data?.message || 'Unknown error from Core');
|
|
70
|
-
}
|
|
71
|
-
} catch (error) {
|
|
72
|
-
console.error(`[McpClient] Error calling ${toolName}:`, error.message);
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
55
|
+
async callTool(command, args = {}) {
|
|
56
|
+
return getService().invoke(command, args);
|
|
75
57
|
}
|
|
76
58
|
}
|
|
77
59
|
|
|
78
60
|
export const mcpClient = new McpClient();
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The raw `invoke(command, params) => Promise<message>` — the exact shape `fetchAppAsset`
|
|
64
|
+
* (from `@descix/sdk`) and other mesh helpers expect. Prefer this for new code.
|
|
65
|
+
*/
|
|
66
|
+
export function getInvoke() {
|
|
67
|
+
return getService().invoke;
|
|
68
|
+
}
|
|
@@ -1,247 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Service Configuration
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
2
|
+
* Service Configuration — Thin wrapper over @descix/cloud-core
|
|
3
|
+
*
|
|
4
|
+
* Uses @descix/cloud-core for shared bootstrap logic:
|
|
5
|
+
* - Layered config: Env -> .env -> workspace.json
|
|
6
|
+
* - Secret Manager integration
|
|
7
|
+
* - Auto-port detection in dev
|
|
8
|
+
*
|
|
9
|
+
* This is the ONLY path to config values. Never use process.env directly.
|
|
10
|
+
* Never add fallback values to utils.CONFIG_VALUE — nulls surface misconfigurations.
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import { fileURLToPath } from 'url';
|
|
12
|
-
import { GoogleAuth } from 'google-auth-library';
|
|
13
|
-
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
|
14
|
-
import dotenv from 'dotenv';
|
|
15
14
|
import path from 'path';
|
|
16
|
-
import
|
|
17
|
-
|
|
15
|
+
import {
|
|
16
|
+
createCloudConfig,
|
|
17
|
+
getCloudConfig,
|
|
18
|
+
initializeCloudConfig,
|
|
19
|
+
} from '@descix/cloud-core';
|
|
18
20
|
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const require = createRequire(import.meta.url);
|
|
21
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const rootPath = path.resolve(__dirname, '..');
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (ServiceConfig.instance) return ServiceConfig.instance;
|
|
24
|
+
// Initialize CloudConfig with service root
|
|
25
|
+
createCloudConfig({ rootPath });
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
const utils = getCloudConfig();
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
if (process.env.DEPLOY_ENV !== 'production') {
|
|
31
|
-
dotenv.config({ path: path.resolve(this.__appDir, '.env') });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
this.CONFIG_SECRET_NAME = process.env.CONFIG_SECRET_NAME || null;
|
|
35
|
-
this.CONFIG_SECRET_VERSION = process.env.CONFIG_SECRET_VERSION || null;
|
|
36
|
-
this.DEPLOY_ENV = process.env.DEPLOY_ENV || null;
|
|
37
|
-
|
|
38
|
-
// 2. Initialize Config Keys (All NULL - no hardcoded defaults)
|
|
39
|
-
this.DEBUG_LOCAL = null;
|
|
40
|
-
this.LOCAL_PORT = null;
|
|
41
|
-
this.CORE_API_URL = null; // URL for DeSciX Core (for loopback)
|
|
42
|
-
this.SERVICE_SECRET = null; // Secret for authenticating loopback calls (Legacy)
|
|
43
|
-
this.SERVICE_KEY = null; // Service Delegate Key (New)
|
|
44
|
-
|
|
45
|
-
// Owner Credentials (for loopback API calls)
|
|
46
|
-
this.OWNER_WALLET_ADDRESS = null;
|
|
47
|
-
this.OWNER_SIGNATURE = null;
|
|
48
|
-
this.OWNER_USER_ID = null;
|
|
49
|
-
|
|
50
|
-
this.GOOGLE_PROJECT_ID = null;
|
|
51
|
-
this.secretClient = null;
|
|
52
|
-
this.PORT = process.env.PORT || null;
|
|
53
|
-
|
|
54
|
-
// Load instance defaults immediately
|
|
55
|
-
this._loadDefaults();
|
|
56
|
-
|
|
57
|
-
ServiceConfig.instance = this;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
_mergeConfig(config) {
|
|
61
|
-
for (const [key, value] of Object.entries(config)) {
|
|
62
|
-
if (key.startsWith('_')) continue;
|
|
63
|
-
// Allow dynamic expansion (undefined) or overwrite (null/value)
|
|
64
|
-
this[key] = value;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
_loadDefaults() {
|
|
69
|
-
const defaultsPath = path.resolve(this.__appDir, 'defaults-config.json');
|
|
70
|
-
if (fs.existsSync(defaultsPath)) {
|
|
71
|
-
try {
|
|
72
|
-
this._mergeConfig(require(defaultsPath));
|
|
73
|
-
console.log('[Config] Loaded defaults-config.json');
|
|
74
|
-
} catch (e) {
|
|
75
|
-
console.error('[Config] Error loading defaults-config.json:', e.message);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
_loadDevOverrides() {
|
|
81
|
-
if (this.DEPLOY_ENV !== 'dev') return;
|
|
82
|
-
const overridesPath = path.resolve(this.__appDir, 'dev-overrides.json');
|
|
83
|
-
if (fs.existsSync(overridesPath)) {
|
|
84
|
-
try {
|
|
85
|
-
this._mergeConfig(require(overridesPath));
|
|
86
|
-
console.log('[Config] Loaded dev-overrides.json');
|
|
87
|
-
} catch (e) {
|
|
88
|
-
console.error('[Config] Error loading dev-overrides.json:', e.message);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async accessSecretVersion(secretName, version) {
|
|
94
|
-
if (!version) throw new Error('[Config] CRITICAL: CONFIG_SECRET_VERSION not set');
|
|
95
|
-
if (!this.secretClient) this.secretClient = new SecretManagerServiceClient();
|
|
96
|
-
|
|
97
|
-
const name = `projects/${this.GOOGLE_PROJECT_ID}/secrets/${secretName}/versions/${version}`;
|
|
98
|
-
const [response] = await this.secretClient.accessSecretVersion({ name });
|
|
99
|
-
return response.payload.data.toString('utf8');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async _registerWithWorkspace() {
|
|
103
|
-
if (this.DEPLOY_ENV !== 'dev' && !this.DEBUG_LOCAL) return;
|
|
104
|
-
|
|
105
|
-
if (this.DEBUG_LOCAL) {
|
|
106
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
let communityId = null;
|
|
110
|
-
let appId = null;
|
|
111
|
-
let workspacePath = null;
|
|
112
|
-
|
|
113
|
-
// Walk up from the service directory to find .descix/workspace.json
|
|
114
|
-
let searchDir = this.__appDir;
|
|
115
|
-
for (let i = 0; i < 10; i++) {
|
|
116
|
-
const candidate = path.resolve(searchDir, '.descix/workspace.json');
|
|
117
|
-
if (fs.existsSync(candidate)) {
|
|
118
|
-
workspacePath = candidate;
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
const parent = path.dirname(searchDir);
|
|
122
|
-
if (parent === searchDir) break;
|
|
123
|
-
searchDir = parent;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Detect context from manifest.json
|
|
127
|
-
try {
|
|
128
|
-
const manifestPath = path.resolve(this.__appDir, 'manifest.json');
|
|
129
|
-
if (fs.existsSync(manifestPath)) {
|
|
130
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
131
|
-
communityId = manifest.service?.community_id || manifest.community_id;
|
|
132
|
-
appId = manifest.service?.app_id || manifest.app_id;
|
|
133
|
-
}
|
|
134
|
-
} catch (e) {
|
|
135
|
-
console.warn('[Config] Failed to detect app context from manifest:', e.message);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!communityId || !appId) {
|
|
139
|
-
console.log('[Config] No communityId/appId — skipping workspace registration');
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (!workspacePath) {
|
|
144
|
-
console.log('[Config] No .descix/workspace.json found — skipping workspace registration');
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Write service port into workspace.json for descix-serve to pick up
|
|
149
|
-
try {
|
|
150
|
-
const workspace = JSON.parse(fs.readFileSync(workspacePath, 'utf-8'));
|
|
151
|
-
if (!workspace.communities) workspace.communities = {};
|
|
152
|
-
if (!workspace.communities[communityId]) workspace.communities[communityId] = { apps: {} };
|
|
153
|
-
if (!workspace.communities[communityId].apps) workspace.communities[communityId].apps = {};
|
|
154
|
-
if (!workspace.communities[communityId].apps[appId]) workspace.communities[communityId].apps[appId] = {};
|
|
155
|
-
|
|
156
|
-
const appEntry = workspace.communities[communityId].apps[appId];
|
|
157
|
-
if (!appEntry.service) appEntry.service = {};
|
|
158
|
-
appEntry.service.port = parseInt(this.PORT, 10);
|
|
159
|
-
|
|
160
|
-
fs.writeFileSync(workspacePath, JSON.stringify(workspace, null, 2), 'utf-8');
|
|
161
|
-
console.log(`[Config] Registered ${communityId}/${appId} service port ${this.PORT} in workspace.json`);
|
|
162
|
-
|
|
163
|
-
this._workspacePath = workspacePath;
|
|
164
|
-
this._registeredCommunityId = communityId;
|
|
165
|
-
this._registeredAppId = appId;
|
|
166
|
-
} catch (e) {
|
|
167
|
-
console.warn('[Config] Failed to write workspace.json:', e.message);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
cleanupWorkspaceRegistration() {
|
|
172
|
-
if (!this._workspacePath || !this._registeredCommunityId || !this._registeredAppId) return;
|
|
173
|
-
try {
|
|
174
|
-
const workspace = JSON.parse(fs.readFileSync(this._workspacePath, 'utf-8'));
|
|
175
|
-
const appEntry = workspace.communities?.[this._registeredCommunityId]?.apps?.[this._registeredAppId];
|
|
176
|
-
if (appEntry?.service) {
|
|
177
|
-
delete appEntry.service.port;
|
|
178
|
-
}
|
|
179
|
-
fs.writeFileSync(this._workspacePath, JSON.stringify(workspace, null, 2), 'utf-8');
|
|
180
|
-
console.log(`[Config] Cleaned up workspace registration for ${this._registeredCommunityId}/${this._registeredAppId}`);
|
|
181
|
-
} catch {
|
|
182
|
-
// Best-effort cleanup on shutdown
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async initialize() {
|
|
187
|
-
// 1. Get Project ID
|
|
188
|
-
const auth = new GoogleAuth();
|
|
189
|
-
this.GOOGLE_PROJECT_ID = await auth.getProjectId();
|
|
190
|
-
|
|
191
|
-
// 2. Load from Secret Manager
|
|
192
|
-
if (this.CONFIG_SECRET_NAME) {
|
|
193
|
-
try {
|
|
194
|
-
const payload = await this.accessSecretVersion(
|
|
195
|
-
this.CONFIG_SECRET_NAME,
|
|
196
|
-
this.CONFIG_SECRET_VERSION
|
|
197
|
-
);
|
|
198
|
-
this._mergeConfig(JSON.parse(payload));
|
|
199
|
-
console.log(`[Config] Loaded from Secret Manager: ${this.CONFIG_SECRET_NAME}`);
|
|
200
|
-
} catch (error) {
|
|
201
|
-
console.warn(`[Config] Secret Manager unavailable:`, error.message);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// 3. Apply Dev Overrides
|
|
206
|
-
this._loadDevOverrides();
|
|
207
|
-
|
|
208
|
-
// 4. Validate Port (Allow 'auto' or 0)
|
|
209
|
-
if (!this.PORT && this.PORT !== 0) {
|
|
210
|
-
if (this.DEBUG_LOCAL && this.LOCAL_PORT) {
|
|
211
|
-
this.PORT = this.LOCAL_PORT;
|
|
212
|
-
} else {
|
|
213
|
-
// If dev, default to auto-assign
|
|
214
|
-
if (this.DEPLOY_ENV === 'dev' || this.DEBUG_LOCAL) {
|
|
215
|
-
this.PORT = 'auto';
|
|
216
|
-
} else {
|
|
217
|
-
throw new Error('[Config] CRITICAL: PORT not configured');
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// 5. Default Core API URL if not set
|
|
223
|
-
if (!this.CORE_API_URL) {
|
|
224
|
-
this.CORE_API_URL = this.DEBUG_LOCAL
|
|
225
|
-
? 'https://localhost:4000/apifront'
|
|
226
|
-
: 'https://descix-cloud.descix.net/apifront';
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// 6. Register service port in workspace.json for descix-serve gateway
|
|
230
|
-
await this._registerWithWorkspace();
|
|
231
|
-
|
|
232
|
-
// Clean up registration on shutdown
|
|
233
|
-
process.on('SIGINT', () => this.cleanupWorkspaceRegistration());
|
|
234
|
-
process.on('SIGTERM', () => this.cleanupWorkspaceRegistration());
|
|
235
|
-
|
|
236
|
-
console.log(`[Config] Final: port=${this.PORT}, core=${this.CORE_API_URL}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const serviceConfig = new ServiceConfig();
|
|
241
|
-
|
|
242
|
-
async function initializeServiceConfig() {
|
|
243
|
-
await serviceConfig.initialize();
|
|
244
|
-
return serviceConfig;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export { initializeServiceConfig, serviceConfig as utils };
|
|
29
|
+
export { initializeCloudConfig as initializeServiceConfig, utils };
|
|
@@ -6,6 +6,30 @@
|
|
|
6
6
|
Include the key use cases, benefits, and target users.
|
|
7
7
|
This section is critical for tell_me_how discovery - be descriptive about WHEN to use this service.}
|
|
8
8
|
|
|
9
|
+
## Onboarding — Canonical Mesh Microservice Setup
|
|
10
|
+
|
|
11
|
+
Run this sequence ONCE to stand up a microservice that can call the mesh
|
|
12
|
+
(Core Tools like `query_knowledge_base`, or other services' tools) over the
|
|
13
|
+
`/apifront` broker:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
descix app create -c {community} -a {app} # create the app (or hydrate an existing one)
|
|
17
|
+
descix app init -a {community}-{app} # scaffold site/ kb/ microservice/
|
|
18
|
+
descix app set-port -a {community}-{app} -p {port} # assign the microservice's loopback port in workspace.json
|
|
19
|
+
descix microservice register # register the service manifest with the Core gateway
|
|
20
|
+
# then: cp dev-overrides.example.json dev-overrides.json and fill in the developer credential
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> **Calling /apifront — the microservice IS the CLI's api-client in the cloud**
|
|
24
|
+
> (CEO-D-2026-06-02-APP-MICROSERVICE-IS-CLI-CLIENT-WALLET-SIG). The scaffold
|
|
25
|
+
> `mcpClient` authenticates AS the developer: it reads `DEVELOPER_WALLET_ADDRESS`
|
|
26
|
+
> + `DEVELOPER_SIGNATURE` (the developer's own wallet credential, from
|
|
27
|
+
> `.descix/wallet.json`) and calls `reconnect_by_wallet` to mint a session —
|
|
28
|
+
> exactly like the CLI. Put the credential in the GITIGNORED `dev-overrides.json`
|
|
29
|
+
> (dev) / a secret (prod), NEVER `defaults-config.json`. There is NO
|
|
30
|
+
> `register-delegate`, NO `SERVICE_KEY`, NO service account for the data plane.
|
|
31
|
+
> **HTTP 401?** The developer credential is missing or wrong — check those two keys.
|
|
32
|
+
|
|
9
33
|
## Prerequisites
|
|
10
34
|
|
|
11
35
|
- {Any setup requirements}
|