@jhytabest/plashboard 0.1.8 → 0.1.9

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
@@ -76,6 +76,8 @@ Use `fill_provider: "command"` only if you need a custom external runner.
76
76
  ```text
77
77
  /plashboard setup [openclaw [agent_id]|mock|command <fill_command>]
78
78
  /plashboard quickstart <description>
79
+ /plashboard doctor [local_url] [https_port] [repo_dir]
80
+ /plashboard web-guide [local_url] [repo_dir]
79
81
  /plashboard expose-guide [local_url] [https_port]
80
82
  /plashboard expose-check [local_url] [https_port]
81
83
  /plashboard init
@@ -94,6 +96,14 @@ Recommended first run:
94
96
  /plashboard quickstart "Focus on service health, priorities, blockers, and next actions."
95
97
  ```
96
98
 
99
+ If `quickstart` returns web/exposure warnings:
100
+
101
+ ```text
102
+ /plashboard web-guide
103
+ /plashboard expose-guide
104
+ /plashboard doctor
105
+ ```
106
+
97
107
  Tailscale helper flow:
98
108
 
99
109
  ```text
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "plashboard",
3
3
  "name": "Plashboard",
4
- "version": "0.1.8",
4
+ "version": "0.1.9",
5
5
  "description": "Template-driven dashboard runtime with scheduled OpenClaw fills and safe publish.",
6
6
  "entry": "./src/index.ts",
7
7
  "skills": ["./skills/plashboard-admin"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jhytabest/plashboard",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "private": false,
5
5
  "description": "Plashboard OpenClaw plugin runtime",
6
6
  "license": "MIT",
@@ -20,6 +20,8 @@ Always use plugin tools:
20
20
  - `plashboard_setup`
21
21
  - `plashboard_exposure_guide`
22
22
  - `plashboard_exposure_check`
23
+ - `plashboard_web_guide`
24
+ - `plashboard_doctor`
23
25
  - `plashboard_init`
24
26
  - `plashboard_quickstart`
25
27
  - `plashboard_template_create`
@@ -42,6 +44,8 @@ Always use plugin tools:
42
44
  ## Command Shortcuts
43
45
  - `/plashboard quickstart <description>`
44
46
  - `/plashboard setup [openclaw [agent_id]|mock|command <fill_command>]`
47
+ - `/plashboard doctor [local_url] [https_port] [repo_dir]`
48
+ - `/plashboard web-guide [local_url] [repo_dir]`
45
49
  - `/plashboard expose-guide [local_url] [https_port]`
46
50
  - `/plashboard expose-check [local_url] [https_port]`
47
51
  - `/plashboard init`
package/src/plugin.ts CHANGED
@@ -117,6 +117,15 @@ type ExposureParams = {
117
117
  dashboard_output_path?: string;
118
118
  };
119
119
 
120
+ type WebGuideParams = {
121
+ local_url?: string;
122
+ repo_dir?: string;
123
+ };
124
+
125
+ type DoctorParams = ExposureParams & {
126
+ repo_dir?: string;
127
+ };
128
+
120
129
  type CommandExecResult = {
121
130
  ok: boolean;
122
131
  stdout: string;
@@ -230,6 +239,40 @@ async function buildExposureGuide(resolvedConfig: ReturnType<typeof resolveConfi
230
239
  } satisfies ToolResponse<Record<string, unknown>>;
231
240
  }
232
241
 
242
+ function deriveRepoDir(raw?: string): string {
243
+ const value = (raw || '').trim();
244
+ return value || '/opt/plashboard';
245
+ }
246
+
247
+ async function buildWebGuide(resolvedConfig: ReturnType<typeof resolveConfig>, params: WebGuideParams = {}) {
248
+ const localUrl = normalizeLocalUrl(params.local_url);
249
+ const repoDir = deriveRepoDir(params.repo_dir);
250
+ const dashboardPath = resolvedConfig.dashboard_output_path;
251
+
252
+ return {
253
+ ok: true,
254
+ errors: [],
255
+ data: {
256
+ local_url: localUrl,
257
+ repo_dir: repoDir,
258
+ dashboard_output_path: dashboardPath,
259
+ commands: [
260
+ `git clone https://github.com/jhytabest/plashboard.git ${repoDir} || true`,
261
+ `git -C ${repoDir} pull --ff-only`,
262
+ `docker compose -f ${repoDir}/docker-compose.yml up -d`,
263
+ `docker ps --format "{{.Names}} {{.Ports}}" | grep -E "plash-web|18888"`,
264
+ `curl -I ${localUrl}`,
265
+ `curl -I ${localUrl}/healthz`
266
+ ],
267
+ notes: [
268
+ 'Plashboard writes dashboard JSON; a local web server must serve the UI and /data/dashboard.json.',
269
+ 'The bundled docker-compose stack exposes nginx at 127.0.0.1:18888 by default.',
270
+ 'If you host UI differently, update local_url in expose-check/expose-guide.'
271
+ ]
272
+ }
273
+ } satisfies ToolResponse<Record<string, unknown>>;
274
+ }
275
+
233
276
  async function runExposureCheck(resolvedConfig: ReturnType<typeof resolveConfig>, params: ExposureParams = {}) {
234
277
  const localUrl = normalizeLocalUrl(params.local_url);
235
278
  const httpsPort = normalizePort(asNumber(params.tailscale_https_port), 8444);
@@ -469,6 +512,101 @@ async function runSetup(api: UnknownApi, resolvedConfig: ReturnType<typeof resol
469
512
  } satisfies ToolResponse<Record<string, unknown>>;
470
513
  }
471
514
 
515
+ async function runQuickstart(
516
+ runtime: PlashboardRuntime,
517
+ resolvedConfig: ReturnType<typeof resolveConfig>,
518
+ params: QuickstartParams = {}
519
+ ): Promise<ToolResponse<Record<string, unknown>>> {
520
+ const quickstart = await runtime.quickstart(params);
521
+ const exposure = await runExposureCheck(resolvedConfig, {});
522
+ const guide = await buildExposureGuide(resolvedConfig, {});
523
+ const webGuide = await buildWebGuide(resolvedConfig, {});
524
+
525
+ const warnings: string[] = [];
526
+ if (!exposure.ok) {
527
+ warnings.push(...exposure.errors);
528
+ }
529
+
530
+ return {
531
+ ok: quickstart.ok,
532
+ errors: quickstart.errors,
533
+ data: {
534
+ ...(quickstart.data || {}),
535
+ postcheck: {
536
+ local_url: exposure.data?.local_url,
537
+ local_url_ok: exposure.data?.local_url_ok,
538
+ tailscale_port_configured: exposure.data?.tailscale_port_configured,
539
+ dashboard_exists: exposure.data?.dashboard_exists
540
+ },
541
+ warnings,
542
+ next_steps: warnings.length
543
+ ? [
544
+ 'run /plashboard web-guide and execute its commands',
545
+ 'run /plashboard expose-guide and apply tailscale mapping',
546
+ 'run /plashboard doctor'
547
+ ]
548
+ : [
549
+ 'dashboard generation is working',
550
+ 'run /plashboard doctor for full readiness check'
551
+ ],
552
+ exposure_guide: guide.data,
553
+ web_guide: webGuide.data
554
+ }
555
+ };
556
+ }
557
+
558
+ async function runDoctor(
559
+ runtime: PlashboardRuntime,
560
+ resolvedConfig: ReturnType<typeof resolveConfig>,
561
+ params: DoctorParams = {}
562
+ ): Promise<ToolResponse<Record<string, unknown>>> {
563
+ const status = await runtime.status();
564
+ const exposure = await runExposureCheck(resolvedConfig, params);
565
+ const exposureGuide = await buildExposureGuide(resolvedConfig, params);
566
+ const webGuide = await buildWebGuide(resolvedConfig, params);
567
+
568
+ const issues: string[] = [];
569
+ const statusData = status.data;
570
+ const templateCount = Number(statusData?.template_count ?? 0);
571
+ const activeTemplateId = statusData?.active_template_id || null;
572
+
573
+ if (!status.ok) issues.push(...status.errors);
574
+ if (templateCount === 0) issues.push('no templates exist; run /plashboard quickstart "<description>"');
575
+ if (!activeTemplateId) issues.push('no active template; activate one with /plashboard activate <template-id>');
576
+ if (exposure.data?.dashboard_exists !== true) {
577
+ issues.push(`dashboard output missing at ${resolvedConfig.dashboard_output_path}`);
578
+ }
579
+ if (exposure.data?.local_url_ok !== true) {
580
+ issues.push(`local dashboard server is not reachable at ${String(exposure.data?.local_url || 'http://127.0.0.1:18888')}`);
581
+ }
582
+ if (exposure.data?.tailscale_status_ok !== true) {
583
+ issues.push('tailscale serve status failed');
584
+ } else if (exposure.data?.tailscale_port_configured !== true) {
585
+ issues.push(`tailscale serve mapping missing for port ${String(exposure.data?.tailscale_https_port || 8444)}`);
586
+ }
587
+
588
+ const ready = issues.length === 0;
589
+ return {
590
+ ok: ready,
591
+ errors: issues,
592
+ data: {
593
+ ready,
594
+ status: statusData,
595
+ exposure: exposure.data,
596
+ exposure_guide: exposureGuide.data,
597
+ web_guide: webGuide.data,
598
+ next_steps: ready
599
+ ? ['dashboard runtime + web exposure look healthy']
600
+ : [
601
+ 'run /plashboard quickstart "<description>" if no templates exist',
602
+ 'run /plashboard web-guide and start local UI server',
603
+ 'run /plashboard expose-guide and apply tailscale mapping',
604
+ 're-run /plashboard doctor'
605
+ ]
606
+ }
607
+ };
608
+ }
609
+
472
610
  export function registerPlashboardPlugin(api: UnknownApi): void {
473
611
  const config = resolveConfig(api);
474
612
  const runtimeCommand = api.runtime?.system?.runCommandWithTimeout;
@@ -547,6 +685,40 @@ export function registerPlashboardPlugin(api: UnknownApi): void {
547
685
  toToolResult(await runExposureCheck(config, params))
548
686
  });
549
687
 
688
+ api.registerTool?.({
689
+ name: 'plashboard_web_guide',
690
+ description: 'Return exact commands to start the local plashboard web UI server.',
691
+ optional: true,
692
+ parameters: {
693
+ type: 'object',
694
+ properties: {
695
+ local_url: { type: 'string' },
696
+ repo_dir: { type: 'string' }
697
+ },
698
+ additionalProperties: false
699
+ },
700
+ execute: async (_toolCallId: unknown, params: WebGuideParams = {}) =>
701
+ toToolResult(await buildWebGuide(config, params))
702
+ });
703
+
704
+ api.registerTool?.({
705
+ name: 'plashboard_doctor',
706
+ description: 'Run full plashboard readiness checks (templates, local UI, and tailscale mapping).',
707
+ optional: true,
708
+ parameters: {
709
+ type: 'object',
710
+ properties: {
711
+ local_url: { type: 'string' },
712
+ tailscale_https_port: { type: 'number' },
713
+ dashboard_output_path: { type: 'string' },
714
+ repo_dir: { type: 'string' }
715
+ },
716
+ additionalProperties: false
717
+ },
718
+ execute: async (_toolCallId: unknown, params: DoctorParams = {}) =>
719
+ toToolResult(await runDoctor(runtime, config, params))
720
+ });
721
+
550
722
  api.registerTool?.({
551
723
  name: 'plashboard_setup',
552
724
  description: 'Bootstrap or update plashboard plugin configuration in openclaw.json.',
@@ -603,7 +775,7 @@ export function registerPlashboardPlugin(api: UnknownApi): void {
603
775
  additionalProperties: false
604
776
  },
605
777
  execute: async (_toolCallId: unknown, params: QuickstartParams = {}) =>
606
- toToolResult(await runtime.quickstart(params))
778
+ toToolResult(await runQuickstart(runtime, config, params))
607
779
  });
608
780
 
609
781
  api.registerTool?.({
@@ -809,6 +981,28 @@ export function registerPlashboardPlugin(api: UnknownApi): void {
809
981
  })
810
982
  );
811
983
  }
984
+ if (cmd === 'web-guide') {
985
+ const localUrl = rest.find((token) => token.startsWith('http://') || token.startsWith('https://'));
986
+ const repoDir = rest.find((token) => token.startsWith('/'));
987
+ return toCommandResult(
988
+ await buildWebGuide(config, {
989
+ local_url: localUrl,
990
+ repo_dir: repoDir
991
+ })
992
+ );
993
+ }
994
+ if (cmd === 'doctor') {
995
+ const localUrl = rest.find((token) => token.startsWith('http://') || token.startsWith('https://'));
996
+ const portToken = rest.find((token) => /^[0-9]+$/.test(token));
997
+ const repoDir = rest.find((token) => token.startsWith('/'));
998
+ return toCommandResult(
999
+ await runDoctor(runtime, config, {
1000
+ local_url: localUrl,
1001
+ tailscale_https_port: portToken ? Number(portToken) : undefined,
1002
+ repo_dir: repoDir
1003
+ })
1004
+ );
1005
+ }
812
1006
  if (cmd === 'setup') {
813
1007
  const mode = asString(rest[0]).toLowerCase();
814
1008
  const fillProvider = mode === 'command' || mode === 'mock' || mode === 'openclaw' ? mode : undefined;
@@ -824,7 +1018,7 @@ export function registerPlashboardPlugin(api: UnknownApi): void {
824
1018
  }
825
1019
  if (cmd === 'quickstart') {
826
1020
  const description = rest.join(' ').trim() || undefined;
827
- return toCommandResult(await runtime.quickstart({ description }));
1021
+ return toCommandResult(await runQuickstart(runtime, config, { description }));
828
1022
  }
829
1023
  if (cmd === 'init') return toCommandResult(await runtime.init());
830
1024
  if (cmd === 'status') return toCommandResult(await runtime.status());
@@ -850,7 +1044,7 @@ export function registerPlashboardPlugin(api: UnknownApi): void {
850
1044
  return toCommandResult({
851
1045
  ok: false,
852
1046
  errors: [
853
- 'unknown command. supported: setup [openclaw [agent_id]|mock|command <fill_command>], quickstart <description>, expose-guide [local_url] [https_port], expose-check [local_url] [https_port], init, status, list, activate <id>, delete <id>, copy <src> <new-id> [new-name] [activate], run <id>, set-display <width> <height> <top> <bottom>'
1047
+ 'unknown command. supported: setup [openclaw [agent_id]|mock|command <fill_command>], quickstart <description>, doctor [local_url] [https_port] [repo_dir], web-guide [local_url] [repo_dir], expose-guide [local_url] [https_port], expose-check [local_url] [https_port], init, status, list, activate <id>, delete <id>, copy <src> <new-id> [new-name] [activate], run <id>, set-display <width> <height> <top> <bottom>'
854
1048
  ]
855
1049
  });
856
1050
  }