trucontext 0.4.1 → 0.5.1
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/bin/cli.js +5 -4
- package/package.json +1 -1
- package/src/client.js +8 -6
- package/src/commands/recipes.js +24 -7
package/bin/cli.js
CHANGED
|
@@ -139,10 +139,11 @@ recipes.command('get <recipeId>')
|
|
|
139
139
|
.description('Get recipe details')
|
|
140
140
|
.action(recipesGetCommand);
|
|
141
141
|
recipes.command('create')
|
|
142
|
-
.description('Create a recipe')
|
|
143
|
-
.requiredOption('--id <recipeId>', 'Recipe ID')
|
|
144
|
-
.requiredOption('--name <name>', 'Recipe name')
|
|
145
|
-
.
|
|
142
|
+
.description('Create a custom recipe. --id and --name are always required. Provide the interpretation via --file (JSON) or inline flags.')
|
|
143
|
+
.requiredOption('--id <recipeId>', 'Recipe ID (e.g., my-health-score)')
|
|
144
|
+
.requiredOption('--name <name>', 'Recipe display name')
|
|
145
|
+
.option('--file <path>', 'JSON file containing the interpretation block (the WHY)')
|
|
146
|
+
.option('--purpose <text>', 'Recipe purpose (inline alternative to --file)')
|
|
146
147
|
.option('--decay-profile <text>', 'Decay profile description')
|
|
147
148
|
.option('--confidence-bias <text>', 'Confidence bias description')
|
|
148
149
|
.action(recipesCreateCommand);
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
// client.js — HTTP client for both API planes
|
|
2
|
-
// Auto-refreshes JWT,
|
|
2
|
+
// Auto-refreshes JWT, auto-prefixes appId in data plane paths.
|
|
3
3
|
|
|
4
4
|
import { ensureFreshToken } from './auth.js';
|
|
5
5
|
import { getActiveApp, DATA_PLANE_URL, CONTROL_PLANE_URL } from './config.js';
|
|
6
6
|
|
|
7
7
|
async function request(baseUrl, method, path, body, retry = true) {
|
|
8
8
|
const token = await ensureFreshToken();
|
|
9
|
-
const activeApp = getActiveApp();
|
|
10
9
|
|
|
11
10
|
const headers = {
|
|
12
11
|
'Authorization': `Bearer ${token}`,
|
|
13
12
|
'Content-Type': 'application/json',
|
|
14
13
|
};
|
|
15
|
-
if (activeApp) {
|
|
16
|
-
headers['X-TruContext-App'] = activeApp;
|
|
17
|
-
}
|
|
18
14
|
|
|
19
15
|
const ac = new AbortController();
|
|
20
16
|
const timeout = setTimeout(() => ac.abort(), 15000);
|
|
@@ -69,7 +65,13 @@ async function handleResponse(res) {
|
|
|
69
65
|
// --- Public API ---
|
|
70
66
|
|
|
71
67
|
export function dataPlane(method, path, body) {
|
|
72
|
-
|
|
68
|
+
const appId = getActiveApp();
|
|
69
|
+
if (!appId) {
|
|
70
|
+
throw new Error('No active app. Run: trucontext use <app-id>');
|
|
71
|
+
}
|
|
72
|
+
// Auto-prefix: /v1/entities → /v1/apps/{appId}/entities
|
|
73
|
+
const appPath = path.replace(/^\/v1\//, `/v1/apps/${appId}/`);
|
|
74
|
+
return request(DATA_PLANE_URL, method, appPath, body);
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
export function controlPlane(method, path, body) {
|
package/src/commands/recipes.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
3
|
import { controlPlane } from '../client.js';
|
|
3
4
|
import { getActiveApp } from '../config.js';
|
|
@@ -107,19 +108,35 @@ export async function recipesCreateCommand(options) {
|
|
|
107
108
|
console.error(chalk.red('--name is required'));
|
|
108
109
|
process.exit(1);
|
|
109
110
|
}
|
|
110
|
-
if (!options.purpose) {
|
|
111
|
-
console.error(chalk.red('--purpose is required'));
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
111
|
|
|
115
112
|
const body = {
|
|
116
113
|
recipeId: options.id,
|
|
117
114
|
name: options.name,
|
|
118
|
-
purpose: options.purpose,
|
|
119
115
|
};
|
|
120
116
|
|
|
121
|
-
|
|
122
|
-
if (options.
|
|
117
|
+
// --file provides the full interpretation block as JSON
|
|
118
|
+
if (options.file) {
|
|
119
|
+
try {
|
|
120
|
+
const raw = readFileSync(options.file, 'utf8');
|
|
121
|
+
body.interpretation = JSON.parse(raw);
|
|
122
|
+
if (!body.interpretation.purpose) {
|
|
123
|
+
console.error(chalk.red('File must contain a "purpose" field'));
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error(chalk.red(`Failed to read --file: ${e.message}`));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
// Inline flags → build interpretation object
|
|
132
|
+
if (!options.purpose) {
|
|
133
|
+
console.error(chalk.red('--purpose is required (or use --file to provide the full interpretation)'));
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
body.interpretation = { purpose: options.purpose };
|
|
137
|
+
if (options.decayProfile) body.interpretation.decay_profile = options.decayProfile;
|
|
138
|
+
if (options.confidenceBias) body.interpretation.confidence_bias = options.confidenceBias;
|
|
139
|
+
}
|
|
123
140
|
|
|
124
141
|
const res = await controlPlane('POST', `/apps/${appId}/recipes`, body);
|
|
125
142
|
const r = res.data || res;
|