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 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
- console.log(`→ Deploying ${id}@${version} (${Object.keys(files).length} files) to ${tenant}/${project} …`);
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
- headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` },
221
- body: JSON.stringify({ files, seed: flags.seed === true }),
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
- console.log(`✓ Deployed ${id}@${version} and installed onto ${tenant}/${project}`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octwin-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Octwin external-pack developer CLI (by CEQUENS) — scaffold, validate, deploy, and check pure-YAML packs on your tenant.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,5 +10,5 @@
10
10
 
11
11
  - match: '/clear'
12
12
  action: close_conversation
13
- reply_ar: 'تم مسح المحادثة ✅ تقدر تبدأ من جديد.'
13
+ reply: 'تم مسح المحادثة ✅ تقدر تبدأ من جديد.'
14
14
  source: '/clear'