@sodikinnaa/smart-report-plugin 2100.11.3 → 2100.11.5

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
@@ -4,7 +4,7 @@ Plugin integrasi resmi untuk menghubungkan OpenClaw dengan ekosistem **Smart Rep
4
4
 
5
5
  ## 🚀 Quick Start
6
6
  ```bash
7
- # 1. Instal Plugin
7
+ # 1. Instal Plugin (NPM)
8
8
  openclaw plugins install @sodikinnaa/smart-report-plugin
9
9
 
10
10
  # 2. Aktivasi Token
@@ -14,6 +14,54 @@ openclaw smart-auth YOUR_SECRET_TOKEN
14
14
  openclaw smart-status
15
15
  ```
16
16
 
17
+ ## 🛠️ Instalasi Manual via `install.sh` (Recommended untuk server custom)
18
+ Script `install.sh` akan:
19
+ - clone source plugin dari GitHub,
20
+ - install dependency + build,
21
+ - verifikasi export `register/activate`,
22
+ - copy plugin ke `~/.openclaw/extensions/smart-report-plugin`,
23
+ - update `plugins.allow` (jika `~/.openclaw/config.json` tersedia),
24
+ - restart OpenClaw Gateway.
25
+
26
+ ### 1) Jalankan langsung dari GitHub
27
+ ```bash
28
+ bash <(curl -fsSL https://raw.githubusercontent.com/sodikinnaa/smart-report-plugin/master/install.sh)
29
+ ```
30
+
31
+ ### 2) Opsi parameter
32
+ ```bash
33
+ # Install branch tertentu
34
+ bash <(curl -fsSL https://raw.githubusercontent.com/sodikinnaa/smart-report-plugin/master/install.sh) --branch master
35
+
36
+ # Custom repository
37
+ bash <(curl -fsSL https://raw.githubusercontent.com/sodikinnaa/smart-report-plugin/master/install.sh) --repo https://github.com/sodikinnaa/smart-report-plugin.git
38
+
39
+ # Jika repo private (token)
40
+ bash <(curl -fsSL https://raw.githubusercontent.com/sodikinnaa/smart-report-plugin/master/install.sh) --token <GITHUB_TOKEN>
41
+
42
+ # Skip build / restart
43
+ bash <(curl -fsSL https://raw.githubusercontent.com/sodikinnaa/smart-report-plugin/master/install.sh) --skip-build
44
+ bash <(curl -fsSL https://raw.githubusercontent.com/sodikinnaa/smart-report-plugin/master/install.sh) --no-restart
45
+ ```
46
+
47
+ ### 3) Verifikasi setelah instalasi
48
+ ```bash
49
+ openclaw smart-auth <TOKEN_SMART_REPORT>
50
+ openclaw smart-status
51
+ ```
52
+
53
+ ### 4) Troubleshooting cepat
54
+ ```bash
55
+ # Cek plugin terpasang
56
+ ls -la ~/.openclaw/extensions/smart-report-plugin
57
+
58
+ # Cek export register/activate
59
+ node -e "const m=require(process.env.HOME+'/.openclaw/extensions/smart-report-plugin/dist/openclaw.cjs'); console.log(typeof m, typeof m.register, typeof m.activate)"
60
+
61
+ # Restart gateway manual
62
+ openclaw gateway restart
63
+ ```
64
+
17
65
  ## ✅ Command Tambahan
18
66
  - `openclaw smart-auth <token>` → menyimpan dan validasi token Smart Report.
19
67
  - `openclaw smart-status` → health-check semua fungsi MCP utama:
package/dist/index.js CHANGED
@@ -8,11 +8,37 @@ exports.activate = exports.register = void 0;
8
8
  * Smart Report MCP Plugin for OpenClaw
9
9
  */
10
10
  const axios_1 = __importDefault(require("axios"));
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
11
13
  const PLUGIN_ID = 'smart-report-plugin';
12
- const API_BASE = 'https://member.smartreport.my.id/api/mcp';
14
+ const API_BASE = 'https://smartreport.siapdigital.my.id/api/mcp';
15
+ function resolveToken(api) {
16
+ return api?.pluginConfig?.apiToken
17
+ || api?.config?.apiToken
18
+ || api?.config?.plugins?.entries?.[PLUGIN_ID]?.config?.apiToken;
19
+ }
20
+ function saveTokenFallback(token, companyInfo) {
21
+ const cfgPath = path_1.default.join(process.env.HOME || '/root', '.openclaw', 'openclaw.json');
22
+ if (!fs_1.default.existsSync(cfgPath))
23
+ return;
24
+ const cfg = JSON.parse(fs_1.default.readFileSync(cfgPath, 'utf8'));
25
+ cfg.plugins = cfg.plugins || {};
26
+ cfg.plugins.entries = cfg.plugins.entries || {};
27
+ cfg.plugins.entries[PLUGIN_ID] = cfg.plugins.entries[PLUGIN_ID] || {};
28
+ cfg.plugins.entries[PLUGIN_ID].enabled = true;
29
+ cfg.plugins.entries[PLUGIN_ID].config = cfg.plugins.entries[PLUGIN_ID].config || {};
30
+ cfg.plugins.entries[PLUGIN_ID].config.apiToken = token;
31
+ if (companyInfo?.name)
32
+ cfg.plugins.entries[PLUGIN_ID].config.companyName = companyInfo.name;
33
+ if (companyInfo?.domain)
34
+ cfg.plugins.entries[PLUGIN_ID].config.companyDomain = companyInfo.domain;
35
+ cfg.plugins.allow = Array.isArray(cfg.plugins.allow) ? cfg.plugins.allow : [];
36
+ if (!cfg.plugins.allow.includes(PLUGIN_ID))
37
+ cfg.plugins.allow.push(PLUGIN_ID);
38
+ fs_1.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
39
+ }
13
40
  async function callMcp(api, method, params = {}) {
14
- const config = api.config;
15
- const token = config?.apiToken;
41
+ const token = resolveToken(api);
16
42
  if (!token) {
17
43
  throw new Error('API Token not found. Please run "openclaw smart-auth <token>" first.');
18
44
  }
@@ -36,7 +62,7 @@ async function callMcp(api, method, params = {}) {
36
62
  const plugin = {
37
63
  id: PLUGIN_ID,
38
64
  name: "Smart Report Integration",
39
- version: "2100.11.3",
65
+ version: "2100.11.5",
40
66
  register(api) {
41
67
  // 1. CLI Commands
42
68
  api.registerCli(({ program }) => {
@@ -47,13 +73,19 @@ const plugin = {
47
73
  process.stdout.write('🔍 Verifying token and fetching company info...');
48
74
  try {
49
75
  // Temporarily set token to verify
50
- api.config.apiToken = token;
76
+ api.pluginConfig = api.pluginConfig || {};
77
+ api.pluginConfig.apiToken = token;
51
78
  const companyInfo = await callMcp(api, 'company/info', {});
52
- await api.saveConfig({
53
- apiToken: token,
54
- companyName: companyInfo.name,
55
- companyDomain: companyInfo.domain
56
- });
79
+ if (typeof api.saveConfig === 'function') {
80
+ await api.saveConfig({
81
+ apiToken: token,
82
+ companyName: companyInfo.name,
83
+ companyDomain: companyInfo.domain
84
+ });
85
+ }
86
+ else {
87
+ saveTokenFallback(token, companyInfo);
88
+ }
57
89
  console.log(`\r✅ Authenticated for: ${companyInfo.name} (${companyInfo.domain})`);
58
90
  console.log(' Smart Report API Token saved successfully.');
59
91
  }
@@ -103,56 +135,61 @@ const plugin = {
103
135
  });
104
136
  }, { commands: ['smart-auth', 'smart-status'] });
105
137
  // 2. Resources
106
- api.registerResource({
107
- uri: 'smartreport://reports',
108
- name: 'Recent Reports',
109
- description: 'Stream of latest submitted reports',
110
- mimeType: 'application/json',
111
- read: async () => {
112
- const data = await callMcp(api, 'reports/list', { per_page: 10 });
113
- return { content: JSON.stringify(data, null, 2) };
114
- }
115
- });
116
- api.registerResource({
117
- uri: 'smartreport://employees',
118
- name: 'Employee List',
119
- description: 'Complete list of active employees with division names',
120
- mimeType: 'application/json',
121
- read: async () => {
122
- const data = await callMcp(api, 'employees/list', {});
123
- return { content: JSON.stringify(data, null, 2) };
124
- }
125
- });
126
- api.registerResource({
127
- uri: 'smartreport://divisions',
128
- name: 'Division List',
129
- description: 'List of all divisions in the company',
130
- mimeType: 'application/json',
131
- read: async () => {
132
- const data = await callMcp(api, 'divisions/list', {});
133
- return { content: JSON.stringify(data, null, 2) };
134
- }
135
- });
136
- api.registerResource({
137
- uri: 'smartreport://guides',
138
- name: 'Guides List',
139
- description: 'List of all available dynamic guides',
140
- mimeType: 'application/json',
141
- read: async () => {
142
- const data = await callMcp(api, 'guides/list', {});
143
- return { content: JSON.stringify(data, null, 2) };
144
- }
145
- });
146
- api.registerResource({
147
- uri: 'smartreport://dashboard',
148
- name: 'Daily Dashboard',
149
- description: 'Real-time KPI dashboard (stats, highlights, alerts)',
150
- mimeType: 'application/json',
151
- read: async (params) => {
152
- const data = await callMcp(api, 'smartreport/dashboard', params || {});
153
- return { content: JSON.stringify(data, null, 2) };
154
- }
155
- });
138
+ if (typeof api.registerResource === 'function')
139
+ api.registerResource({
140
+ uri: 'smartreport://reports',
141
+ name: 'Recent Reports',
142
+ description: 'Stream of latest submitted reports',
143
+ mimeType: 'application/json',
144
+ read: async () => {
145
+ const data = await callMcp(api, 'reports/list', { per_page: 10 });
146
+ return { content: JSON.stringify(data, null, 2) };
147
+ }
148
+ });
149
+ if (typeof api.registerResource === 'function')
150
+ api.registerResource({
151
+ uri: 'smartreport://employees',
152
+ name: 'Employee List',
153
+ description: 'Complete list of active employees with division names',
154
+ mimeType: 'application/json',
155
+ read: async () => {
156
+ const data = await callMcp(api, 'employees/list', {});
157
+ return { content: JSON.stringify(data, null, 2) };
158
+ }
159
+ });
160
+ if (typeof api.registerResource === 'function')
161
+ api.registerResource({
162
+ uri: 'smartreport://divisions',
163
+ name: 'Division List',
164
+ description: 'List of all divisions in the company',
165
+ mimeType: 'application/json',
166
+ read: async () => {
167
+ const data = await callMcp(api, 'divisions/list', {});
168
+ return { content: JSON.stringify(data, null, 2) };
169
+ }
170
+ });
171
+ if (typeof api.registerResource === 'function')
172
+ api.registerResource({
173
+ uri: 'smartreport://guides',
174
+ name: 'Guides List',
175
+ description: 'List of all available dynamic guides',
176
+ mimeType: 'application/json',
177
+ read: async () => {
178
+ const data = await callMcp(api, 'guides/list', {});
179
+ return { content: JSON.stringify(data, null, 2) };
180
+ }
181
+ });
182
+ if (typeof api.registerResource === 'function')
183
+ api.registerResource({
184
+ uri: 'smartreport://dashboard',
185
+ name: 'Daily Dashboard',
186
+ description: 'Real-time KPI dashboard (stats, highlights, alerts)',
187
+ mimeType: 'application/json',
188
+ read: async (params) => {
189
+ const data = await callMcp(api, 'smartreport/dashboard', params || {});
190
+ return { content: JSON.stringify(data, null, 2) };
191
+ }
192
+ });
156
193
  // 3. Agent Tools
157
194
  api.registerTool({
158
195
  name: 'get_daily_dashboard',
package/index.js ADDED
@@ -0,0 +1,17 @@
1
+ // Compatibility entry for OpenClaw loaders across versions.
2
+ // Always expose register/activate as direct CommonJS exports.
3
+ const mod = require('./dist/index.js');
4
+
5
+ const register =
6
+ mod.register ||
7
+ mod.activate ||
8
+ (mod.default && (mod.default.register || mod.default.activate)) ||
9
+ (typeof mod === 'function' ? mod : undefined);
10
+
11
+ if (typeof register !== 'function') {
12
+ throw new Error('smart-report-plugin: register/activate export not found');
13
+ }
14
+
15
+ module.exports = register;
16
+ module.exports.register = register;
17
+ module.exports.activate = register;
package/install.sh ADDED
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env bash
2
+ # install.sh — Manual installer for Smart Report Plugin (OpenClaw)
3
+ # Usage:
4
+ # bash install.sh
5
+ # bash install.sh --branch master
6
+ # bash install.sh --repo https://github.com/sodikinnaa/smart-report-plugin.git --token <gh_token>
7
+
8
+ set -euo pipefail
9
+
10
+ # -----------------------------
11
+ # Defaults
12
+ # -----------------------------
13
+ PLUGIN_ID="smart-report-plugin"
14
+ REPO_URL="https://github.com/sodikinnaa/smart-report-plugin.git"
15
+ BRANCH="master"
16
+ TARGET_DIR="${HOME}/.openclaw/extensions/${PLUGIN_ID}"
17
+ SKIP_BUILD="0"
18
+ NO_RESTART="0"
19
+ GITHUB_TOKEN="${GITHUB_TOKEN:-}"
20
+
21
+ # -----------------------------
22
+ # UI helpers
23
+ # -----------------------------
24
+ info() { echo " $*"; }
25
+ success() { echo "✅ $*"; }
26
+ warn() { echo "⚠️ $*"; }
27
+ fatal() { echo "❌ $*" >&2; exit 1; }
28
+
29
+ usage() {
30
+ cat <<EOF
31
+ Usage: install.sh [options]
32
+
33
+ Options:
34
+ --repo <url> Git repo URL (default: ${REPO_URL})
35
+ --branch <name> Branch/tag to install (default: ${BRANCH})
36
+ --target <path> Target plugin dir (default: ${TARGET_DIR})
37
+ --token <token> GitHub token for private repo (or set GITHUB_TOKEN env)
38
+ --skip-build Skip npm install/build step
39
+ --no-restart Skip openclaw gateway restart
40
+ -h, --help Show this help
41
+
42
+ Examples:
43
+ bash install.sh
44
+ bash install.sh --branch master
45
+ bash install.sh --repo https://github.com/sodikinnaa/smart-report-plugin.git --token ghp_xxx
46
+ EOF
47
+ }
48
+
49
+ parse_args() {
50
+ while [[ $# -gt 0 ]]; do
51
+ case "$1" in
52
+ --repo)
53
+ [[ $# -ge 2 ]] || fatal "Missing value for --repo"
54
+ REPO_URL="$2"; shift 2 ;;
55
+ --branch)
56
+ [[ $# -ge 2 ]] || fatal "Missing value for --branch"
57
+ BRANCH="$2"; shift 2 ;;
58
+ --target)
59
+ [[ $# -ge 2 ]] || fatal "Missing value for --target"
60
+ TARGET_DIR="$2"; shift 2 ;;
61
+ --token)
62
+ [[ $# -ge 2 ]] || fatal "Missing value for --token"
63
+ GITHUB_TOKEN="$2"; shift 2 ;;
64
+ --skip-build)
65
+ SKIP_BUILD="1"; shift ;;
66
+ --no-restart)
67
+ NO_RESTART="1"; shift ;;
68
+ -h|--help)
69
+ usage; exit 0 ;;
70
+ *)
71
+ fatal "Unknown option: $1"
72
+ ;;
73
+ esac
74
+ done
75
+ }
76
+
77
+ need_cmd() {
78
+ command -v "$1" >/dev/null 2>&1 || fatal "Command not found: $1"
79
+ }
80
+
81
+ with_token_repo_url() {
82
+ local url="$1"
83
+ local token="$2"
84
+
85
+ if [[ -z "$token" ]]; then
86
+ printf '%s\n' "$url"
87
+ return
88
+ fi
89
+
90
+ if [[ "$url" =~ ^https://github.com/ ]]; then
91
+ printf '%s\n' "${url/https:\/\/github.com\//https:\/\/x-access-token:${token}@github.com\/}"
92
+ return
93
+ fi
94
+
95
+ printf '%s\n' "$url"
96
+ }
97
+
98
+ cleanup_legacy_backups_in_extensions() {
99
+ local ext_dir="${HOME}/.openclaw/extensions"
100
+ if [[ -d "$ext_dir" ]]; then
101
+ find "$ext_dir" -maxdepth 1 -type d -name "${PLUGIN_ID}.backup.*" -print0 2>/dev/null | while IFS= read -r -d '' d; do
102
+ rm -rf "$d" || true
103
+ warn "Removed legacy backup inside extensions: $d"
104
+ done
105
+ fi
106
+ }
107
+
108
+ sync_openclaw_plugin_config() {
109
+ local plugin_id="$1"
110
+ local found_any="0"
111
+
112
+ # Candidate locations across common OpenClaw setups (prioritize openclaw.json)
113
+ local candidates=(
114
+ "${HOME}/.openclaw/openclaw.json"
115
+ "/root/.openclaw/openclaw.json"
116
+ "/etc/openclaw/openclaw.json"
117
+ "${HOME}/.openclaw/config.json"
118
+ "${HOME}/.config/openclaw/config.json"
119
+ "/root/.openclaw/config.json"
120
+ "/root/.config/openclaw/config.json"
121
+ "/etc/openclaw/config.json"
122
+ )
123
+
124
+ # Also discover additional openclaw*.json files (depth-limited, safe)
125
+ while IFS= read -r f; do
126
+ [[ -n "$f" ]] || continue
127
+ candidates+=("$f")
128
+ done < <(find /root /etc -maxdepth 6 -type f \( -name 'openclaw.json' -o -name 'config.json' \) -path '*openclaw*' 2>/dev/null | sort -u)
129
+
130
+ # De-duplicate candidates
131
+ local uniq=()
132
+ local c
133
+ for c in "${candidates[@]}"; do
134
+ [[ -n "$c" ]] || continue
135
+ if [[ ! " ${uniq[*]} " =~ " ${c} " ]]; then
136
+ uniq+=("$c")
137
+ fi
138
+ done
139
+
140
+ for cfg in "${uniq[@]}"; do
141
+ [[ -f "$cfg" ]] || continue
142
+ found_any="1"
143
+
144
+ node <<'NODE' "$cfg" "$plugin_id"
145
+ const fs = require('fs');
146
+ const cfgPath = process.argv[2];
147
+ const pluginId = process.argv[3];
148
+
149
+ try {
150
+ const raw = fs.readFileSync(cfgPath, 'utf8');
151
+ const cfg = JSON.parse(raw || '{}');
152
+
153
+ cfg.plugins = cfg.plugins || {};
154
+
155
+ // 1) pin trust allowlist
156
+ cfg.plugins.allow = Array.isArray(cfg.plugins.allow) ? cfg.plugins.allow : [];
157
+ if (!cfg.plugins.allow.includes(pluginId)) cfg.plugins.allow.push(pluginId);
158
+
159
+ // 2) hard-clean stale records (entries + installs)
160
+ if (cfg.plugins.entries && typeof cfg.plugins.entries === 'object') {
161
+ delete cfg.plugins.entries[pluginId];
162
+ }
163
+ if (cfg.plugins.installs && typeof cfg.plugins.installs === 'object') {
164
+ delete cfg.plugins.installs[pluginId];
165
+ }
166
+
167
+ // 3) force discovery path for manual fallback installs
168
+ cfg.plugins.load = cfg.plugins.load || {};
169
+ cfg.plugins.load.paths = Array.isArray(cfg.plugins.load.paths) ? cfg.plugins.load.paths : [];
170
+ const manualPath = `${process.env.HOME || '/root'}/.openclaw/extensions/${pluginId}`;
171
+ if (!cfg.plugins.load.paths.includes(manualPath)) cfg.plugins.load.paths.push(manualPath);
172
+
173
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
174
+ console.log('synced plugin config:', cfgPath);
175
+ } catch (e) {
176
+ console.error('failed to sync config', cfgPath, e.message);
177
+ process.exit(1);
178
+ }
179
+ NODE
180
+ done
181
+
182
+ if [[ "$found_any" == "1" ]]; then
183
+ success "Konfigurasi plugin disinkronkan (allowlist + stale cleanup)"
184
+ else
185
+ warn "Tidak menemukan config OpenClaw; skip sync config otomatis"
186
+ fi
187
+ }
188
+
189
+ TMP_DIR=""
190
+ cleanup() {
191
+ if [[ -n "${TMP_DIR:-}" && -d "${TMP_DIR:-}" ]]; then
192
+ rm -rf "${TMP_DIR}" || true
193
+ fi
194
+ }
195
+
196
+ main() {
197
+ parse_args "$@"
198
+
199
+ need_cmd git
200
+ need_cmd node
201
+ need_cmd npm
202
+
203
+ TMP_DIR="$(mktemp -d /tmp/smart-report-plugin-install-XXXXXX)"
204
+ trap cleanup EXIT
205
+
206
+ local clone_url
207
+ clone_url="$(with_token_repo_url "$REPO_URL" "$GITHUB_TOKEN")"
208
+
209
+ info "Cloning repo..."
210
+ git clone --depth 1 --branch "$BRANCH" "$clone_url" "$TMP_DIR/repo" >/dev/null 2>&1 || fatal "Gagal clone repo: $REPO_URL (branch: $BRANCH)"
211
+ success "Repo cloned (${BRANCH})"
212
+
213
+ if [[ "$SKIP_BUILD" != "1" ]]; then
214
+ info "Installing npm dependencies..."
215
+ (cd "$TMP_DIR/repo" && npm ci >/dev/null 2>&1) || (cd "$TMP_DIR/repo" && npm install >/dev/null 2>&1) || fatal "npm install gagal"
216
+
217
+ info "Building plugin..."
218
+ (cd "$TMP_DIR/repo" && npm run build >/dev/null 2>&1) || fatal "npm run build gagal"
219
+ success "Build selesai"
220
+ else
221
+ warn "Build di-skip (--skip-build)"
222
+ fi
223
+
224
+ [[ -f "$TMP_DIR/repo/openclaw.plugin.json" ]] || fatal "openclaw.plugin.json tidak ditemukan"
225
+ [[ -f "$TMP_DIR/repo/index.js" ]] || fatal "index.js tidak ditemukan"
226
+ [[ -f "$TMP_DIR/repo/dist/index.js" ]] || fatal "dist/index.js tidak ditemukan"
227
+
228
+ info "Verifying register/activate export..."
229
+ node -e "const m=require('$TMP_DIR/repo/index.js'); const ok=(typeof m==='function'||typeof m.register==='function'||typeof m.activate==='function'); if(!ok){throw new Error('register/activate export not found')} console.log('OK');" >/dev/null || fatal "Plugin export invalid (register/activate tidak ada)"
230
+ success "Export register/activate valid"
231
+
232
+ mkdir -p "$(dirname "$TARGET_DIR")"
233
+
234
+ # Preferred path: let OpenClaw register install metadata itself.
235
+ if command -v openclaw >/dev/null 2>&1; then
236
+ info "Installing via OpenClaw plugin manager..."
237
+
238
+ # Remove stale install record if present (safe if plugin not installed)
239
+ if command -v timeout >/dev/null 2>&1; then
240
+ timeout 20 openclaw plugins uninstall "$PLUGIN_ID" >/dev/null 2>&1 || true
241
+ else
242
+ openclaw plugins uninstall "$PLUGIN_ID" >/dev/null 2>&1 || true
243
+ fi
244
+
245
+ # Install from local prepared repo path (contains built dist)
246
+ # Remove existing dir first to avoid "plugin already exists" hard-fail.
247
+ rm -rf "$TARGET_DIR" >/dev/null 2>&1 || true
248
+
249
+ local install_ok="0"
250
+ local install_log="$TMP_DIR/openclaw-install.log"
251
+ if command -v timeout >/dev/null 2>&1; then
252
+ if timeout 45 openclaw plugins install "$TMP_DIR/repo" >"$install_log" 2>&1; then
253
+ install_ok="1"
254
+ else
255
+ warn "openclaw plugins install timeout/gagal (>45s), fallback ke manual copy"
256
+ sed -n '1,80p' "$install_log" || true
257
+ fi
258
+ else
259
+ if openclaw plugins install "$TMP_DIR/repo" >"$install_log" 2>&1; then
260
+ install_ok="1"
261
+ else
262
+ warn "openclaw plugins install gagal, fallback ke manual copy"
263
+ sed -n '1,80p' "$install_log" || true
264
+ fi
265
+ fi
266
+
267
+ if [[ "$install_ok" == "1" ]]; then
268
+ success "Plugin installed via openclaw plugins install"
269
+
270
+ # Validate installed artifact, because some OpenClaw versions may resolve stale package contents.
271
+ if ! node -e "const p='$TARGET_DIR/dist/index.js'; const m=require(p); const ok=(typeof m.register==='function'||typeof m.activate==='function'||typeof m==='function'); if(!ok) process.exit(1);" >/dev/null 2>&1; then
272
+ warn "Installed artifact invalid (register/activate not found), forcing manual overwrite from built source"
273
+ install_ok="0"
274
+ fi
275
+ fi
276
+
277
+ if [[ "$install_ok" != "1" ]]; then
278
+ if [[ -d "$TARGET_DIR" ]]; then
279
+ local backup_root backup_dir
280
+ backup_root="${HOME}/.openclaw/extensions-backup/${PLUGIN_ID}"
281
+ mkdir -p "$backup_root"
282
+ backup_dir="${backup_root}/$(date +%Y%m%d-%H%M%S)"
283
+ mv "$TARGET_DIR" "$backup_dir"
284
+ warn "Backup plugin lama -> ${backup_dir}"
285
+ fi
286
+
287
+ mkdir -p "$TARGET_DIR"
288
+ cp -R \
289
+ "$TMP_DIR/repo/openclaw.plugin.json" \
290
+ "$TMP_DIR/repo/package.json" \
291
+ "$TMP_DIR/repo/package-lock.json" \
292
+ "$TMP_DIR/repo/index.js" \
293
+ "$TMP_DIR/repo/dist" \
294
+ "$TMP_DIR/repo/src" \
295
+ "$TMP_DIR/repo/skills" \
296
+ "$TMP_DIR/repo/README.md" \
297
+ "$TARGET_DIR/"
298
+
299
+ (cd "$TARGET_DIR" && npm install --omit=dev >/dev/null 2>&1) || warn "npm install dependency plugin gagal"
300
+ success "Plugin installed to: ${TARGET_DIR}"
301
+ fi
302
+ else
303
+ # Fallback for environments without openclaw CLI in PATH.
304
+ if [[ -d "$TARGET_DIR" ]]; then
305
+ local backup_root backup_dir
306
+ backup_root="${HOME}/.openclaw/extensions-backup/${PLUGIN_ID}"
307
+ mkdir -p "$backup_root"
308
+ backup_dir="${backup_root}/$(date +%Y%m%d-%H%M%S)"
309
+ mv "$TARGET_DIR" "$backup_dir"
310
+ warn "Backup plugin lama -> ${backup_dir}"
311
+ fi
312
+
313
+ mkdir -p "$TARGET_DIR"
314
+ cp -R \
315
+ "$TMP_DIR/repo/openclaw.plugin.json" \
316
+ "$TMP_DIR/repo/package.json" \
317
+ "$TMP_DIR/repo/package-lock.json" \
318
+ "$TMP_DIR/repo/index.js" \
319
+ "$TMP_DIR/repo/dist" \
320
+ "$TMP_DIR/repo/skills" \
321
+ "$TMP_DIR/repo/README.md" \
322
+ "$TARGET_DIR/"
323
+
324
+ (cd "$TARGET_DIR" && npm install --omit=dev >/dev/null 2>&1) || warn "npm install dependency plugin gagal"
325
+ success "Plugin installed to: ${TARGET_DIR}"
326
+ fi
327
+
328
+ cleanup_legacy_backups_in_extensions
329
+
330
+ if command -v openclaw >/dev/null 2>&1; then
331
+ sync_openclaw_plugin_config "$PLUGIN_ID" || warn "Tidak bisa sinkronisasi config otomatis"
332
+
333
+ if [[ "$NO_RESTART" != "1" ]]; then
334
+ info "Restarting OpenClaw gateway..."
335
+ openclaw gateway restart >/dev/null 2>&1 || warn "Gateway restart gagal. Jalankan manual: openclaw gateway restart"
336
+ else
337
+ warn "Restart di-skip (--no-restart)"
338
+ fi
339
+ else
340
+ warn "openclaw command tidak ditemukan, skip config + restart"
341
+ fi
342
+
343
+ cat <<EOF
344
+
345
+ 🎉 Instalasi selesai.
346
+
347
+ Langkah verifikasi:
348
+ 1) openclaw plugins list --verbose
349
+ 2) openclaw smart-auth <TOKEN_SMART_REPORT>
350
+ 3) openclaw smart-status
351
+
352
+ Jika masih error:
353
+ - Jalankan: openclaw plugins doctor
354
+ - Cek entrypoint: ${TARGET_DIR}/openclaw.plugin.json
355
+ - Cek export: node -e "const m=require('${TARGET_DIR}/dist/index.js'); console.log(typeof m, typeof m.register, typeof m.activate)"
356
+ EOF
357
+ }
358
+
359
+ main "$@"
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "id": "smart-report-plugin",
3
3
  "name": "Smart Report Integration",
4
- "version": "2100.11.3",
4
+ "version": "2100.11.5",
5
5
  "description": "Integration plugin for Smart Report and AI Analytics with Daily Dashboard and Dynamic Guides",
6
- "entrypoint": "./dist/openclaw.cjs",
6
+ "entrypoint": "./index.js",
7
7
  "skills": [
8
8
  "skills/smart-report/SKILL.md"
9
9
  ],
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@sodikinnaa/smart-report-plugin",
3
- "version": "2100.11.3",
3
+ "version": "2100.11.5",
4
4
  "description": "OpenClaw plugin for Smart Report system integration with Daily Dashboard",
5
- "main": "./dist/openclaw.cjs",
5
+ "main": "./index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc && cp ./openclaw.cjs ./dist/openclaw.cjs",
@@ -18,7 +18,7 @@
18
18
  "license": "MIT",
19
19
  "openclaw": {
20
20
  "extensions": [
21
- "./openclaw.plugin.json"
21
+ "./index.js"
22
22
  ]
23
23
  },
24
24
  "devDependencies": {
package/src/index.ts CHANGED
@@ -2,14 +2,38 @@
2
2
  * Smart Report MCP Plugin for OpenClaw
3
3
  */
4
4
  import axios from 'axios';
5
+ import fs from 'fs';
6
+ import path from 'path';
5
7
 
6
8
  const PLUGIN_ID = 'smart-report-plugin';
7
- const API_BASE = 'https://member.smartreport.my.id/api/mcp';
9
+ const API_BASE = 'https://smartreport.siapdigital.my.id/api/mcp';
10
+
11
+ function resolveToken(api: any): string | undefined {
12
+ return api?.pluginConfig?.apiToken
13
+ || api?.config?.apiToken
14
+ || api?.config?.plugins?.entries?.[PLUGIN_ID]?.config?.apiToken;
15
+ }
16
+
17
+ function saveTokenFallback(token: string, companyInfo?: any) {
18
+ const cfgPath = path.join(process.env.HOME || '/root', '.openclaw', 'openclaw.json');
19
+ if (!fs.existsSync(cfgPath)) return;
20
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
21
+ cfg.plugins = cfg.plugins || {};
22
+ cfg.plugins.entries = cfg.plugins.entries || {};
23
+ cfg.plugins.entries[PLUGIN_ID] = cfg.plugins.entries[PLUGIN_ID] || {};
24
+ cfg.plugins.entries[PLUGIN_ID].enabled = true;
25
+ cfg.plugins.entries[PLUGIN_ID].config = cfg.plugins.entries[PLUGIN_ID].config || {};
26
+ cfg.plugins.entries[PLUGIN_ID].config.apiToken = token;
27
+ if (companyInfo?.name) cfg.plugins.entries[PLUGIN_ID].config.companyName = companyInfo.name;
28
+ if (companyInfo?.domain) cfg.plugins.entries[PLUGIN_ID].config.companyDomain = companyInfo.domain;
29
+ cfg.plugins.allow = Array.isArray(cfg.plugins.allow) ? cfg.plugins.allow : [];
30
+ if (!cfg.plugins.allow.includes(PLUGIN_ID)) cfg.plugins.allow.push(PLUGIN_ID);
31
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
32
+ }
8
33
 
9
34
  async function callMcp(api: any, method: string, params: any = {}) {
10
- const config: any = api.config;
11
- const token = config?.apiToken;
12
-
35
+ const token = resolveToken(api);
36
+
13
37
  if (!token) {
14
38
  throw new Error('API Token not found. Please run "openclaw smart-auth <token>" first.');
15
39
  }
@@ -37,7 +61,7 @@ async function callMcp(api: any, method: string, params: any = {}) {
37
61
  const plugin: any = {
38
62
  id: PLUGIN_ID,
39
63
  name: "Smart Report Integration",
40
- version: "2100.11.3",
64
+ version: "2100.11.5",
41
65
 
42
66
  register(api: any) {
43
67
  // 1. CLI Commands
@@ -49,15 +73,20 @@ const plugin: any = {
49
73
  process.stdout.write('🔍 Verifying token and fetching company info...');
50
74
  try {
51
75
  // Temporarily set token to verify
52
- api.config.apiToken = token;
76
+ api.pluginConfig = api.pluginConfig || {};
77
+ api.pluginConfig.apiToken = token;
53
78
  const companyInfo = await callMcp(api, 'company/info', {});
54
-
55
- await api.saveConfig({
56
- apiToken: token,
57
- companyName: companyInfo.name,
58
- companyDomain: companyInfo.domain
59
- });
60
-
79
+
80
+ if (typeof api.saveConfig === 'function') {
81
+ await api.saveConfig({
82
+ apiToken: token,
83
+ companyName: companyInfo.name,
84
+ companyDomain: companyInfo.domain
85
+ });
86
+ } else {
87
+ saveTokenFallback(token, companyInfo);
88
+ }
89
+
61
90
  console.log(`\r✅ Authenticated for: ${companyInfo.name} (${companyInfo.domain})`);
62
91
  console.log(' Smart Report API Token saved successfully.');
63
92
  } catch (err: any) {
@@ -110,7 +139,7 @@ const plugin: any = {
110
139
  }, { commands: ['smart-auth', 'smart-status'] });
111
140
 
112
141
  // 2. Resources
113
- api.registerResource({
142
+ if (typeof api.registerResource === 'function') api.registerResource({
114
143
  uri: 'smartreport://reports',
115
144
  name: 'Recent Reports',
116
145
  description: 'Stream of latest submitted reports',
@@ -121,7 +150,7 @@ const plugin: any = {
121
150
  }
122
151
  });
123
152
 
124
- api.registerResource({
153
+ if (typeof api.registerResource === 'function') api.registerResource({
125
154
  uri: 'smartreport://employees',
126
155
  name: 'Employee List',
127
156
  description: 'Complete list of active employees with division names',
@@ -132,7 +161,7 @@ const plugin: any = {
132
161
  }
133
162
  });
134
163
 
135
- api.registerResource({
164
+ if (typeof api.registerResource === 'function') api.registerResource({
136
165
  uri: 'smartreport://divisions',
137
166
  name: 'Division List',
138
167
  description: 'List of all divisions in the company',
@@ -143,7 +172,7 @@ const plugin: any = {
143
172
  }
144
173
  });
145
174
 
146
- api.registerResource({
175
+ if (typeof api.registerResource === 'function') api.registerResource({
147
176
  uri: 'smartreport://guides',
148
177
  name: 'Guides List',
149
178
  description: 'List of all available dynamic guides',
@@ -154,7 +183,7 @@ const plugin: any = {
154
183
  }
155
184
  });
156
185
 
157
- api.registerResource({
186
+ if (typeof api.registerResource === 'function') api.registerResource({
158
187
  uri: 'smartreport://dashboard',
159
188
  name: 'Daily Dashboard',
160
189
  description: 'Real-time KPI dashboard (stats, highlights, alerts)',