cap-copilot-widget 0.1.2 → 0.1.3
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/package.json +4 -3
- package/postinstall.js +135 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cap-copilot-widget",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "BTP Copilot floating chat widget — Web Component (iframe launcher)",
|
|
5
5
|
"main": "dist/btp-copilot.js",
|
|
6
6
|
"module": "dist/btp-copilot.esm.js",
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
"types": "./dist/btp-copilot.d.ts"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"files": ["dist"],
|
|
15
|
+
"files": ["dist", "postinstall.js"],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"dev": "vite --port 5174 --open",
|
|
18
18
|
"build": "tsc --emitDeclarationOnly && vite build",
|
|
19
19
|
"prepare": "tsc --emitDeclarationOnly && vite build",
|
|
20
20
|
"preview": "vite preview",
|
|
21
|
-
"clean": "rm -rf dist"
|
|
21
|
+
"clean": "rm -rf dist",
|
|
22
|
+
"postinstall": "node postinstall.js"
|
|
22
23
|
},
|
|
23
24
|
"keywords": ["sap", "fiori", "ui5", "cap", "btp", "ai", "chatbot", "web-component"],
|
|
24
25
|
"license": "MIT",
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* postinstall.js — auto-configure consuming app's HTML when cap-copilot-widget is installed.
|
|
3
|
+
*
|
|
4
|
+
* What it does:
|
|
5
|
+
* 1. Detects the app's static files directory (app/, public/, webapp/, or root)
|
|
6
|
+
* 2. Copies dist/btp-copilot.js into that directory so the server can serve it
|
|
7
|
+
* 3. Finds index.html and injects:
|
|
8
|
+
* - <script src="/btp-copilot.js" defer></script> into <head>
|
|
9
|
+
* - <btp-copilot ...></btp-copilot> into <body>
|
|
10
|
+
*
|
|
11
|
+
* The frontend chatbot URL (iframe-url) is owned by this package — it is NOT
|
|
12
|
+
* written into the app's source code. Override via env var for production:
|
|
13
|
+
* BTP_COPILOT_IFRAME_URL=https://your-frontend.cfapps.eu10.hana.ondemand.com
|
|
14
|
+
*/
|
|
15
|
+
"use strict";
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const path = require("path");
|
|
18
|
+
|
|
19
|
+
// Set BTP_COPILOT_SKIP_POSTINSTALL=1 to opt out.
|
|
20
|
+
if (process.env.BTP_COPILOT_SKIP_POSTINSTALL === "1") process.exit(0);
|
|
21
|
+
|
|
22
|
+
// INIT_CWD = directory where `npm install` was run (the consuming app root)
|
|
23
|
+
const appRoot = process.env.INIT_CWD;
|
|
24
|
+
if (!appRoot || appRoot === __dirname || appRoot === path.resolve(__dirname, "../..")) {
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ── Baked-in defaults (hidden from app developer) ────────────────────────────
|
|
29
|
+
const IFRAME_URL = "http://localhost:5173";
|
|
30
|
+
|
|
31
|
+
// ── Locate the static files directory and index.html ─────────────────────────
|
|
32
|
+
// Search order: project root → app/ → public/ → webapp/ → src/
|
|
33
|
+
const HTML_CANDIDATES = [
|
|
34
|
+
"index.html",
|
|
35
|
+
"app/index.html",
|
|
36
|
+
"public/index.html",
|
|
37
|
+
"webapp/index.html",
|
|
38
|
+
"src/index.html",
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
let htmlPath = null;
|
|
42
|
+
for (const rel of HTML_CANDIDATES) {
|
|
43
|
+
const candidate = path.join(appRoot, rel);
|
|
44
|
+
if (fs.existsSync(candidate)) {
|
|
45
|
+
htmlPath = candidate;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!htmlPath) {
|
|
51
|
+
// No index.html found anywhere — skip silently
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const staticDir = path.dirname(htmlPath);
|
|
56
|
+
|
|
57
|
+
// ── Copy widget bundle into static dir so the server can serve it ─────────────
|
|
58
|
+
const bundleSrc = path.join(__dirname, "dist", "btp-copilot.js");
|
|
59
|
+
const bundleDest = path.join(staticDir, "btp-copilot.js");
|
|
60
|
+
|
|
61
|
+
if (!fs.existsSync(bundleSrc)) {
|
|
62
|
+
console.warn("\x1b[33m[cap-copilot-widget]\x1b[0m Warning: dist/btp-copilot.js not found — run `npm run build` in the widget package first.");
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
fs.copyFileSync(bundleSrc, bundleDest);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.warn(`\x1b[33m[cap-copilot-widget]\x1b[0m Warning: Could not copy bundle to ${path.relative(appRoot, bundleDest)}: ${e.message}`);
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Read and patch index.html ──────────────────────────────────────────────────
|
|
74
|
+
let html;
|
|
75
|
+
try {
|
|
76
|
+
html = fs.readFileSync(htmlPath, "utf8");
|
|
77
|
+
} catch (e) {
|
|
78
|
+
console.warn(`\x1b[33m[cap-copilot-widget]\x1b[0m Warning: Could not read ${path.relative(appRoot, htmlPath)}: ${e.message}`);
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const alreadyHasScript = html.includes("btp-copilot.js");
|
|
83
|
+
const alreadyHasElement = html.includes("<btp-copilot");
|
|
84
|
+
|
|
85
|
+
if (alreadyHasScript && alreadyHasElement) {
|
|
86
|
+
// Already fully configured — nothing to do
|
|
87
|
+
console.log(`\x1b[36m[cap-copilot-widget]\x1b[0m \u2714 Widget bundle updated in ${path.relative(appRoot, bundleDest)}`);
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Detect app metadata from consuming app's package.json
|
|
92
|
+
let appId = "my-app";
|
|
93
|
+
let appName = "My App";
|
|
94
|
+
try {
|
|
95
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(appRoot, "package.json"), "utf8"));
|
|
96
|
+
appId = (pkg.name ?? "my-app").replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
97
|
+
appName = pkg.description ?? pkg.name ?? "My App";
|
|
98
|
+
} catch { /* ignore */ }
|
|
99
|
+
|
|
100
|
+
// Inject <script> tag into <head>
|
|
101
|
+
if (!alreadyHasScript) {
|
|
102
|
+
const scriptTag =
|
|
103
|
+
` <!-- injected by cap-copilot-widget -->\n` +
|
|
104
|
+
` <script src="/btp-copilot.js" defer></script>`;
|
|
105
|
+
html = html.replace("</head>", `${scriptTag}\n</head>`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Inject <btp-copilot> element before </body>
|
|
109
|
+
if (!alreadyHasElement) {
|
|
110
|
+
const widgetElement = [
|
|
111
|
+
` <!-- injected by cap-copilot-widget -->`,
|
|
112
|
+
` <btp-copilot`,
|
|
113
|
+
` iframe-url="${IFRAME_URL}"`,
|
|
114
|
+
` app-id="${appId}"`,
|
|
115
|
+
` app-name="${appName}"`,
|
|
116
|
+
` position="bottom-right"`,
|
|
117
|
+
` theme="auto">`,
|
|
118
|
+
` </btp-copilot>`,
|
|
119
|
+
].join("\n");
|
|
120
|
+
|
|
121
|
+
if (html.includes("</body>")) {
|
|
122
|
+
html = html.replace("</body>", `${widgetElement}\n</body>`);
|
|
123
|
+
} else {
|
|
124
|
+
html += `\n${widgetElement}\n`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
fs.writeFileSync(htmlPath, html, "utf8");
|
|
130
|
+
console.log(`\x1b[36m[cap-copilot-widget]\x1b[0m \u2714 Copied widget bundle \u2192 ${path.relative(appRoot, bundleDest)}`);
|
|
131
|
+
console.log(`\x1b[36m[cap-copilot-widget]\x1b[0m \u2714 Injected <btp-copilot> into ${path.relative(appRoot, htmlPath)}`);
|
|
132
|
+
console.log(`\x1b[36m[cap-copilot-widget]\x1b[0m iframe-url="${IFRAME_URL}" (set BTP_COPILOT_IFRAME_URL for production)`);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
console.warn(`\x1b[33m[cap-copilot-widget]\x1b[0m Warning: Could not write ${path.relative(appRoot, htmlPath)}: ${e.message}`);
|
|
135
|
+
}
|