octwin-cli 0.1.3 → 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/dist/index.js CHANGED
@@ -210,17 +210,91 @@ async function cmdWhoami(flags) {
210
210
  : await res.text();
211
211
  die(`token check failed for '${tenant}' (HTTP ${res.status}) — ${why}`);
212
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
+ }
213
273
  async function cmdDeploy(flags) {
214
274
  const packDir = resolve(flags.dir ?? '.');
215
275
  const { url, tenant, project, token } = resolveTarget(flags, packDir);
216
276
  const { id, version, files } = localValidate(packDir);
217
277
  const endpoint = `${url}/api/admin/tenants/${tenant}/projects/${project}/packs/deploy`;
218
- 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' : ''} …`);
219
280
  const res = await fetch(endpoint, {
220
281
  method: 'POST',
221
- headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` },
222
- 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 }),
223
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).
224
298
  const text = await res.text();
225
299
  let json;
226
300
  try {
@@ -234,10 +308,7 @@ async function cmdDeploy(flags) {
234
308
  console.error(typeof json === 'string' ? json : JSON.stringify(json, null, 2));
235
309
  process.exit(1);
236
310
  }
237
- console.log(`✓ Deployed ${id}@${version} and installed onto ${tenant}/${project}`);
238
- if (json?.warning)
239
- console.log(` ⚠ ${json.warning}`);
240
- console.log(`\nChat with it on your tenant (web widget / console test page for ${tenant}/${project}).`);
311
+ printDeploySuccess(id, version, tenant, project, json);
241
312
  }
242
313
  async function cmdStatus(flags) {
243
314
  const packDir = resolve(flags.dir ?? '.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octwin-cli",
3
- "version": "0.1.3",
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'