octwin-cli 0.1.2 → 0.1.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/README.md +1 -0
- package/dist/index.js +128 -8
- package/package.json +1 -1
- package/templates/starter/commands.yaml +1 -1
package/README.md
CHANGED
|
@@ -66,6 +66,7 @@ octwin status # "✓ live and current" once it's warm
|
|
|
66
66
|
| `octwin whoami` | Verify the saved/passed token is valid for a tenant. `--url`, `--tenant`. |
|
|
67
67
|
| `octwin deploy` | Upload + install the pack onto your tenant's project. `--seed` also runs the pack's demo seed. |
|
|
68
68
|
| `octwin status` | Report what the platform has live for this pack — installed vs. loaded version, and its flows. |
|
|
69
|
+
| `octwin platform-kb pull` | Pull the platform's capability reference (built-ins, primitives, render intents, flow-DSL — as markdown + JSON) into `.octwin/platform-kb/`, for the **`octwin-pack`** Claude Code authoring plugin to consult. |
|
|
69
70
|
| `octwin test` | Validate locally and print how to try the pack on your tenant. |
|
|
70
71
|
| `octwin help` | Show usage. |
|
|
71
72
|
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* octwin whoami [--url <url>] [--tenant <slug>]
|
|
14
14
|
* octwin deploy [--dir .] [--url <url>] [--tenant <slug>] [--project <slug>] [--token <t>] [--seed]
|
|
15
15
|
* octwin status [--dir .] # did my deploy land? which version is live?
|
|
16
|
+
* octwin platform-kb [pull] [--dir .] # pull the platform capability reference for the authoring skill
|
|
16
17
|
* octwin test [--dir .] # local validate + how to chat on your tenant
|
|
17
18
|
*
|
|
18
19
|
* Config resolution (deploy): flags > pack.json (in the pack dir) > env
|
|
@@ -142,7 +143,7 @@ function cmdInit(flags) {
|
|
|
142
143
|
tenant: 'your-tenant-slug',
|
|
143
144
|
project: 'main',
|
|
144
145
|
}, null, 2) + '\n', 'utf8');
|
|
145
|
-
writeFileSync(join(dir, '.gitignore'), 'node_modules/\n.pack-bundles/\n', 'utf8');
|
|
146
|
+
writeFileSync(join(dir, '.gitignore'), 'node_modules/\n.pack-bundles/\n.octwin/\n', 'utf8');
|
|
146
147
|
if (!existsSync(join(dir, 'README.md'))) {
|
|
147
148
|
writeFileSync(join(dir, 'README.md'), `# ${id}\n\nA pure-YAML pack for the Octwin platform.\n\n- Edit \`manifest.yaml\`, \`flows/tools/main.flow.yaml\`, \`prompts/identity.md\`\n- \`octwin validate\` — check it locally\n- \`octwin deploy\` — deploy + install onto your tenant\n`, 'utf8');
|
|
148
149
|
}
|
|
@@ -209,17 +210,91 @@ async function cmdWhoami(flags) {
|
|
|
209
210
|
: await res.text();
|
|
210
211
|
die(`token check failed for '${tenant}' (HTTP ${res.status}) — ${why}`);
|
|
211
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Read the deploy SSE stream, printing each progress frame's message live, and
|
|
215
|
+
* return the terminal `done`/`error` event (or null if the stream ended without
|
|
216
|
+
* one). The platform emits `data: {json}\n\n` frames — install + seed progress,
|
|
217
|
+
* with per-record / per-image lines during a `--seed` (image generation is slow).
|
|
218
|
+
*/
|
|
219
|
+
async function readDeployProgress(body) {
|
|
220
|
+
const reader = body.getReader();
|
|
221
|
+
const decoder = new TextDecoder();
|
|
222
|
+
let buf = '';
|
|
223
|
+
let terminal = null;
|
|
224
|
+
for (;;) {
|
|
225
|
+
const { done, value } = await reader.read();
|
|
226
|
+
if (done)
|
|
227
|
+
break;
|
|
228
|
+
buf += decoder.decode(value, { stream: true });
|
|
229
|
+
let idx;
|
|
230
|
+
while ((idx = buf.indexOf('\n\n')) >= 0) {
|
|
231
|
+
const frame = buf.slice(0, idx);
|
|
232
|
+
buf = buf.slice(idx + 2);
|
|
233
|
+
const dataLine = frame.split('\n').find((l) => l.startsWith('data:'));
|
|
234
|
+
if (!dataLine)
|
|
235
|
+
continue;
|
|
236
|
+
let ev;
|
|
237
|
+
try {
|
|
238
|
+
ev = JSON.parse(dataLine.slice(5).trim());
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (ev.stage === 'done' || ev.stage === 'error') {
|
|
244
|
+
terminal = ev;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (ev.message)
|
|
248
|
+
console.log(` ${ev.status === 'error' ? '⚠' : '·'} ${ev.message}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return terminal;
|
|
252
|
+
}
|
|
253
|
+
function printDeploySuccess(id, version, tenant, project, r) {
|
|
254
|
+
console.log(`✓ Deployed ${id}@${version} and installed onto ${tenant}/${project}`);
|
|
255
|
+
if (r?.warning)
|
|
256
|
+
console.log(` ⚠ ${r.warning}`);
|
|
257
|
+
const s = r?.summary;
|
|
258
|
+
if (s) {
|
|
259
|
+
const parts = [];
|
|
260
|
+
if (s.records != null)
|
|
261
|
+
parts.push(`${s.records} record(s) created`);
|
|
262
|
+
if (s.updated)
|
|
263
|
+
parts.push(`${s.updated} updated`);
|
|
264
|
+
if (s.images)
|
|
265
|
+
parts.push(`${s.images} image(s) generated`);
|
|
266
|
+
if (s.rules)
|
|
267
|
+
parts.push(`${s.rules} availability rule(s)`);
|
|
268
|
+
if (parts.length)
|
|
269
|
+
console.log(` Seeded: ${parts.join(', ')}`);
|
|
270
|
+
}
|
|
271
|
+
console.log(`\nChat with it on your tenant (web widget / console test page for ${tenant}/${project}).`);
|
|
272
|
+
}
|
|
212
273
|
async function cmdDeploy(flags) {
|
|
213
274
|
const packDir = resolve(flags.dir ?? '.');
|
|
214
275
|
const { url, tenant, project, token } = resolveTarget(flags, packDir);
|
|
215
276
|
const { id, version, files } = localValidate(packDir);
|
|
216
277
|
const endpoint = `${url}/api/admin/tenants/${tenant}/projects/${project}/packs/deploy`;
|
|
217
|
-
|
|
278
|
+
const seed = flags.seed === true;
|
|
279
|
+
console.log(`→ Deploying ${id}@${version} (${Object.keys(files).length} files) to ${tenant}/${project}${seed ? ' — with demo seed' : ''} …`);
|
|
218
280
|
const res = await fetch(endpoint, {
|
|
219
281
|
method: 'POST',
|
|
220
|
-
|
|
221
|
-
|
|
282
|
+
// Ask for a progress stream; the platform falls back to plain JSON if it
|
|
283
|
+
// (or an error before any progress) can't stream — handled below.
|
|
284
|
+
headers: { 'content-type': 'application/json', accept: 'text/event-stream', authorization: `Bearer ${token}` },
|
|
285
|
+
body: JSON.stringify({ files, seed }),
|
|
222
286
|
});
|
|
287
|
+
// Streaming path — live install + seed progress (image generation can take a
|
|
288
|
+
// while, so `--seed` prints per-record / per-image lines as they happen).
|
|
289
|
+
if (res.ok && (res.headers.get('content-type') ?? '').includes('text/event-stream') && res.body) {
|
|
290
|
+
const final = await readDeployProgress(res.body);
|
|
291
|
+
if (!final || final.stage === 'error')
|
|
292
|
+
die(`deploy failed${final?.message ? `: ${final.message}` : ' (stream ended early)'}`);
|
|
293
|
+
printDeploySuccess(id, version, tenant, project, final);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
// Non-streaming path — plain JSON (older platform, or an error thrown before
|
|
297
|
+
// any progress frame, which the platform returns as clean JSON).
|
|
223
298
|
const text = await res.text();
|
|
224
299
|
let json;
|
|
225
300
|
try {
|
|
@@ -233,10 +308,7 @@ async function cmdDeploy(flags) {
|
|
|
233
308
|
console.error(typeof json === 'string' ? json : JSON.stringify(json, null, 2));
|
|
234
309
|
process.exit(1);
|
|
235
310
|
}
|
|
236
|
-
|
|
237
|
-
if (json?.warning)
|
|
238
|
-
console.log(` ⚠ ${json.warning}`);
|
|
239
|
-
console.log(`\nChat with it on your tenant (web widget / console test page for ${tenant}/${project}).`);
|
|
311
|
+
printDeploySuccess(id, version, tenant, project, json);
|
|
240
312
|
}
|
|
241
313
|
async function cmdStatus(flags) {
|
|
242
314
|
const packDir = resolve(flags.dir ?? '.');
|
|
@@ -284,6 +356,49 @@ async function cmdStatus(flags) {
|
|
|
284
356
|
console.log(` (local manifest is ${localVersion}; deployed is ${json.installed_version} — \`octwin deploy\` to push local edits.)`);
|
|
285
357
|
}
|
|
286
358
|
}
|
|
359
|
+
async function cmdPlatformKb(flags) {
|
|
360
|
+
const packDir = resolve(flags.dir ?? '.');
|
|
361
|
+
const { url, tenant, token } = resolveTarget(flags, packDir);
|
|
362
|
+
const res = await fetch(`${url}/api/admin/tenants/${tenant}/octwin-platform-kb`, {
|
|
363
|
+
headers: { authorization: `Bearer ${token}` },
|
|
364
|
+
});
|
|
365
|
+
const text = await res.text();
|
|
366
|
+
if (!res.ok) {
|
|
367
|
+
let j;
|
|
368
|
+
try {
|
|
369
|
+
j = JSON.parse(text);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
j = text;
|
|
373
|
+
}
|
|
374
|
+
console.error(`✗ platform-kb pull failed (HTTP ${res.status})`);
|
|
375
|
+
console.error(typeof j === 'string' ? j : JSON.stringify(j, null, 2));
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
const bundle = JSON.parse(text);
|
|
379
|
+
// Write the reference into <packDir>/.octwin/platform-kb/ — markdown docs (the
|
|
380
|
+
// skill reads these first) + JSON catalogs (precise field schemas). Gitignored.
|
|
381
|
+
const outDir = join(packDir, '.octwin', 'platform-kb');
|
|
382
|
+
mkdirSync(outDir, { recursive: true });
|
|
383
|
+
let mdCount = 0;
|
|
384
|
+
let jsonCount = 0;
|
|
385
|
+
for (const [key, val] of Object.entries(bundle.docs ?? {})) {
|
|
386
|
+
if (val == null)
|
|
387
|
+
continue;
|
|
388
|
+
writeFileSync(join(outDir, `${key}.md`), val, 'utf8');
|
|
389
|
+
mdCount++;
|
|
390
|
+
}
|
|
391
|
+
for (const [key, val] of Object.entries(bundle.sources ?? {})) {
|
|
392
|
+
if (val == null)
|
|
393
|
+
continue;
|
|
394
|
+
writeFileSync(join(outDir, `${key}.json`), JSON.stringify(val, null, 2) + '\n', 'utf8');
|
|
395
|
+
jsonCount++;
|
|
396
|
+
}
|
|
397
|
+
writeFileSync(join(outDir, 'index.json'), JSON.stringify({ version: bundle.version, generated_at: bundle.generated_at, index: bundle.index }, null, 2) + '\n', 'utf8');
|
|
398
|
+
console.log(`✓ Pulled the Octwin platform KB → ${outDir}`);
|
|
399
|
+
console.log(` ${mdCount} markdown docs + ${jsonCount} JSON catalogs (reference version ${bundle.version ?? '?'})`);
|
|
400
|
+
console.log(' The octwin-pack authoring skill reads these as the source of truth for what the platform supports.');
|
|
401
|
+
}
|
|
287
402
|
function cmdTest(flags) {
|
|
288
403
|
const packDir = resolve(flags.dir ?? '.');
|
|
289
404
|
const { id, version } = localValidate(packDir);
|
|
@@ -300,9 +415,11 @@ function help() {
|
|
|
300
415
|
octwin whoami [--url <url>] [--tenant <slug>] # verify the token works
|
|
301
416
|
octwin deploy [--dir .] [--url <url>] [--tenant <slug>] [--project <slug>] [--token <t>] [--seed]
|
|
302
417
|
octwin status [--dir .] [--url <url>] [--tenant <slug>] [--project <slug>] [--token <t>]
|
|
418
|
+
octwin platform-kb [pull] [--dir .] [--url <url>] [--tenant <slug>] [--token <t>]
|
|
303
419
|
octwin test [--dir .]
|
|
304
420
|
|
|
305
421
|
Get a deploy token: console → your workspace → Settings → API tokens → Generate.
|
|
422
|
+
octwin platform-kb pull → writes the platform capability reference into .octwin/platform-kb/ (for the octwin-pack skill).
|
|
306
423
|
Config (deploy): flags > pack.json > env (PACK_PLATFORM_URL/PACK_TENANT/PACK_PROJECT/PACK_TOKEN) > saved login.`);
|
|
307
424
|
}
|
|
308
425
|
async function main() {
|
|
@@ -327,6 +444,9 @@ async function main() {
|
|
|
327
444
|
case 'status':
|
|
328
445
|
await cmdStatus(flags);
|
|
329
446
|
break;
|
|
447
|
+
case 'platform-kb':
|
|
448
|
+
await cmdPlatformKb(flags);
|
|
449
|
+
break;
|
|
330
450
|
case 'test':
|
|
331
451
|
cmdTest(flags);
|
|
332
452
|
break;
|
package/package.json
CHANGED