@robono/server 0.19.0 → 0.20.0
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/CHANGELOG.md +4 -0
- package/README.md +30 -0
- package/SBOM.spdx.json +8 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/messenger-auth.d.ts +16 -0
- package/dist/messenger-auth.d.ts.map +1 -0
- package/dist/messenger-auth.js +37 -0
- package/dist/messenger-auth.js.map +1 -0
- package/dist/messenger-connected-templates.d.ts +4 -0
- package/dist/messenger-connected-templates.d.ts.map +1 -0
- package/dist/messenger-connected-templates.js +135 -0
- package/dist/messenger-connected-templates.js.map +1 -0
- package/dist/messenger-init-cli.js +3 -3
- package/dist/messenger-init-cli.js.map +1 -1
- package/dist/messenger-init.d.ts +2 -0
- package/dist/messenger-init.d.ts.map +1 -1
- package/dist/messenger-init.js +12 -7
- package/dist/messenger-init.js.map +1 -1
- package/dist/messenger-install-cli.d.ts +3 -0
- package/dist/messenger-install-cli.d.ts.map +1 -0
- package/dist/messenger-install-cli.js +82 -0
- package/dist/messenger-install-cli.js.map +1 -0
- package/dist/messenger-install.d.ts +35 -0
- package/dist/messenger-install.d.ts.map +1 -0
- package/dist/messenger-install.js +216 -0
- package/dist/messenger-install.js.map +1 -0
- package/dist/messenger-preview-template.d.ts +6 -0
- package/dist/messenger-preview-template.d.ts.map +1 -0
- package/dist/messenger-preview-template.js +43 -0
- package/dist/messenger-preview-template.js.map +1 -0
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 0.20.0 — release candidate
|
|
2
|
+
|
|
3
|
+
Add robono-messenger-install for existing Next.js App Router and Expo 55–56 projects, safe project/package-manager detection, optional Supabase/Firebase account verification, generated account lifecycle handling, a development-only two-person preview, and a combined setup checklist. Keep customer permissions fail-closed, credentials server-only, and existing files protected. The original generated-file command remains available for other frameworks. Provider clients initialize at request time so builds do not need production secrets.
|
|
4
|
+
|
|
1
5
|
## 0.19.0 — release candidate
|
|
2
6
|
|
|
3
7
|
Add the guided web/Expo starter command and safe JSON connection report. Authorize sender-private scheduled messages and atomic attachment batches through the existing server route. Keep identity and media ownership checks on the server.
|
package/README.md
CHANGED
|
@@ -411,3 +411,33 @@ Current membership, blocks, suspension, API-key access, feature rules, screening
|
|
|
411
411
|
and spending limits are checked again when sending. Your account system must
|
|
412
412
|
synchronize revocations and suspension to Robono promptly: a queued message
|
|
413
413
|
cannot detect an unsynchronized change known only to your server.
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
## Install into an existing app (0.20+)
|
|
417
|
+
|
|
418
|
+
Run from a Next.js App Router project with Node.js 22 or later:
|
|
419
|
+
|
|
420
|
+
```sh
|
|
421
|
+
npx --package @robono/server robono-messenger-install --origin https://your-company.com --company "Your company"
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
For Expo 55–56, run from the phone project and add `--server-project ../your-next-server`. The installer detects Supabase or Firebase when present; use `--auth supabase`, `--auth firebase` or `--auth custom` to select explicitly. It detects npm, pnpm or Yarn 1, shows the exact file and dependency plan, and asks before applying it. `--dry-run` changes nothing, `--yes` applies the plan, and `--no-install` writes files only. Existing integration files and `.env` files are never overwritten. Static Expo `app.json` plugin additions preserve existing entries and save the original under `.robono/`. Dynamic Expo configurations, other server frameworks and Yarn 2+ use `robono-messenger-init` instead.
|
|
425
|
+
|
|
426
|
+
The installer preserves existing provider package versions. Package installation scripts are disabled; run any required dependency-specific build steps yourself. Expo uses its installed SDK's version selection for native peers. Rebuild both mobile apps after native changes.
|
|
427
|
+
|
|
428
|
+
Open `.robono/SETUP.md`. Provider sign-in verification is supplied; connect your own current messaging eligibility, suspension and conversation permission checks in `robono-access.ts`. They intentionally deny access until connected. Synchronize relationships and groups from your server. Import the generated `robonoAccounts` and `ConnectedRobonoChat`, create the accounts adapter once with `useMemo`, and supply your current contact names and avatars. Keep the connected screen mounted in your signed-in application. The messenger account menu unregisters notifications before ending provider sign-in; provider events also clear local chat on account changes. If a session is already invalid, remote device removal may fail, so account revocation must also be synchronized server-side.
|
|
429
|
+
|
|
430
|
+
The development-only `/robono-preview` page shows the actual website messenger for fictional Alice and Bob. Text stays in page memory, disappears on reload, and never reaches Robono. It demonstrates text only and returns 404 outside development. It is not evidence that your live connection or permissions work.
|
|
431
|
+
|
|
432
|
+
```sh
|
|
433
|
+
npx --package @robono/server robono-messenger-install --check
|
|
434
|
+
# Set ROBONO_MESSENGER_URL, ROBONO_WEBSITE_ORIGIN and ROBONO_TEST_USER_TOKEN
|
|
435
|
+
# locally to include live checks. Use a disposable user token, never a network key.
|
|
436
|
+
npx --package @robono/server robono-messenger-install --check --json
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
The checklist distinguishes files, configured permission code, required credentials, contacts and live connection results. It does not infer security from edited source or claim all customer permission rules were tested. Reports exclude tokens and message content.
|
|
440
|
+
|
|
441
|
+
Supabase uses the official client's `auth.getUser(token)` on the server. Firebase uses the official Admin client's `verifyIdToken(token, true)`, including revocation/disabled-account checks. Exported `supabaseMessengerAuthentication` and `firebaseMessengerAuthentication` accept your configured provider clients; no provider secrets belong in client code. Continue checking app-specific suspension and memberships in `authorize`.
|
|
442
|
+
|
|
443
|
+
Notifications remain a separate credential step: website origin/chat path for browsers; APNs key, Team ID, Key ID and Bundle ID for iPhone; app `google-services.json` and a privately uploaded FCM service account for Android. Upload private notification credentials in Robono's setup screen. Chat can work before alerts are configured. You still need real background alert checks.
|
package/SBOM.spdx.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"spdxVersion": "SPDX-2.3",
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
|
-
"name": "@robono/server@0.
|
|
6
|
-
"documentNamespace": "http://spdx.org/spdxdocs/@robono%2fserver-0.
|
|
5
|
+
"name": "@robono/server@0.20.0",
|
|
6
|
+
"documentNamespace": "http://spdx.org/spdxdocs/@robono%2fserver-0.20.0-***",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-11-
|
|
8
|
+
"created": "2026-11-04T05:04:06.010Z",
|
|
9
9
|
"creators": [
|
|
10
10
|
"Tool: npm/cli-11.6.2"
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"documentDescribes": [
|
|
14
|
-
"SPDXRef-Package-robono.server-0.
|
|
14
|
+
"SPDXRef-Package-robono.server-0.20.0"
|
|
15
15
|
],
|
|
16
16
|
"packages": [
|
|
17
17
|
{
|
|
18
18
|
"name": "@robono/server",
|
|
19
|
-
"SPDXID": "SPDXRef-Package-robono.server-0.
|
|
20
|
-
"versionInfo": "0.
|
|
19
|
+
"SPDXID": "SPDXRef-Package-robono.server-0.20.0",
|
|
20
|
+
"versionInfo": "0.20.0",
|
|
21
21
|
"packageFileName": "",
|
|
22
22
|
"description": "Provider-neutral server SDK for the Robono Bridge API.",
|
|
23
23
|
"primaryPackagePurpose": "LIBRARY",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
{
|
|
30
30
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
31
31
|
"referenceType": "purl",
|
|
32
|
-
"referenceLocator": "pkg:npm/%40robono/server@0.
|
|
32
|
+
"referenceLocator": "pkg:npm/%40robono/server@0.20.0"
|
|
33
33
|
}
|
|
34
34
|
]
|
|
35
35
|
}
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"relationships": [
|
|
38
38
|
{
|
|
39
39
|
"spdxElementId": "SPDXRef-DOCUMENT",
|
|
40
|
-
"relatedSpdxElement": "SPDXRef-Package-robono.server-0.
|
|
40
|
+
"relatedSpdxElement": "SPDXRef-Package-robono.server-0.20.0",
|
|
41
41
|
"relationshipType": "DESCRIBES"
|
|
42
42
|
}
|
|
43
43
|
]
|
package/dist/index.d.ts
CHANGED
|
@@ -27,4 +27,5 @@ export * from './messaging-notification-postgres.js';
|
|
|
27
27
|
export { RobonoScreening } from './screening.js';
|
|
28
28
|
export type { ScreeningFile, ScreeningResult } from './screening.js';
|
|
29
29
|
export { checkRobonoMessengerSetup, type MessengerSetupCheck } from './messenger-check.js';
|
|
30
|
+
export { supabaseMessengerAuthentication, firebaseMessengerAuthentication } from './messenger-auth.js';
|
|
30
31
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACrI,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC1D,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtE,mBAAmB,YAAY,CAAC;AAEhC,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AACvF,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEpF,OAAO,EAAC,kCAAkC,EAAC,MAAM,sBAAsB,CAAC;AACxE,YAAY,EAAC,uBAAuB,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AACtI,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAC,sCAAsC,EAAC,MAAM,2BAA2B,CAAC;AAEjF,OAAO,EAAC,qBAAqB,EAAC,4BAA4B,EAAC,MAAM,eAAe,CAAC;AACjF,YAAY,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACtD,OAAO,EAAC,yBAAyB,EAAC,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAC,2BAA2B,EAAC,KAAK,qBAAqB,EAAC,MAAM,gBAAgB,CAAC;AACtF,YAAY,EAAC,4BAA4B,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAEjF,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AAErD,OAAO,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAC,aAAa,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAElE,OAAO,EAAC,yBAAyB,EAAC,KAAK,mBAAmB,EAAC,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACrI,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC1D,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtE,mBAAmB,YAAY,CAAC;AAEhC,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AACvF,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEpF,OAAO,EAAC,kCAAkC,EAAC,MAAM,sBAAsB,CAAC;AACxE,YAAY,EAAC,uBAAuB,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AACtI,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAC,sCAAsC,EAAC,MAAM,2BAA2B,CAAC;AAEjF,OAAO,EAAC,qBAAqB,EAAC,4BAA4B,EAAC,MAAM,eAAe,CAAC;AACjF,YAAY,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACtD,OAAO,EAAC,yBAAyB,EAAC,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAC,2BAA2B,EAAC,KAAK,qBAAqB,EAAC,MAAM,gBAAgB,CAAC;AACtF,YAAY,EAAC,4BAA4B,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAEjF,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AAErD,OAAO,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAC,aAAa,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAElE,OAAO,EAAC,yBAAyB,EAAC,KAAK,mBAAmB,EAAC,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAC,+BAA+B,EAAC,+BAA+B,EAAC,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,4 +17,5 @@ export * from './messaging-notification-providers.js';
|
|
|
17
17
|
export * from './messaging-notification-postgres.js';
|
|
18
18
|
export { RobonoScreening } from './screening.js';
|
|
19
19
|
export { checkRobonoMessengerSetup } from './messenger-check.js';
|
|
20
|
+
export { supabaseMessengerAuthentication, firebaseMessengerAuthentication } from './messenger-auth.js';
|
|
20
21
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAM1D,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGtE,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAGvF,OAAO,EAAC,kCAAkC,EAAC,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAC,sCAAsC,EAAC,MAAM,2BAA2B,CAAC;AAEjF,OAAO,EAAC,qBAAqB,EAAC,4BAA4B,EAAC,MAAM,eAAe,CAAC;AAEjF,OAAO,EAAC,yBAAyB,EAAC,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAC,2BAA2B,EAA4B,MAAM,gBAAgB,CAAC;AAGtF,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AAErD,OAAO,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAG/C,OAAO,EAAC,yBAAyB,EAA0B,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAM1D,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGtE,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAGvF,OAAO,EAAC,kCAAkC,EAAC,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAC,sCAAsC,EAAC,MAAM,2BAA2B,CAAC;AAEjF,OAAO,EAAC,qBAAqB,EAAC,4BAA4B,EAAC,MAAM,eAAe,CAAC;AAEjF,OAAO,EAAC,yBAAyB,EAAC,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAC,2BAA2B,EAA4B,MAAM,gBAAgB,CAAC;AAGtF,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AAErD,OAAO,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAG/C,OAAO,EAAC,yBAAyB,EAA0B,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAC,+BAA+B,EAAC,+BAA+B,EAAC,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function supabaseMessengerAuthentication(auth: {
|
|
2
|
+
getUser(token: string): Promise<{
|
|
3
|
+
data: {
|
|
4
|
+
user: {
|
|
5
|
+
id: string;
|
|
6
|
+
} | null;
|
|
7
|
+
};
|
|
8
|
+
error: unknown;
|
|
9
|
+
}>;
|
|
10
|
+
}): (request: Request) => Promise<string | null>;
|
|
11
|
+
export declare function firebaseMessengerAuthentication(auth: {
|
|
12
|
+
verifyIdToken(token: string, checkRevoked: boolean): Promise<{
|
|
13
|
+
uid: string;
|
|
14
|
+
}>;
|
|
15
|
+
}): (request: Request) => Promise<string | null>;
|
|
16
|
+
//# sourceMappingURL=messenger-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-auth.d.ts","sourceRoot":"","sources":["../src/messenger-auth.ts"],"names":[],"mappings":"AAQA,wBAAgB,+BAA+B,CAAC,IAAI,EAAC;IAAC,OAAO,CAAC,KAAK,EAAC,MAAM,GAAE,OAAO,CAAC;QAAC,IAAI,EAAC;YAAC,IAAI,EAAC;gBAAC,EAAE,EAAC,MAAM,CAAA;aAAC,GAAC,IAAI,CAAA;SAAC,CAAC;QAAA,KAAK,EAAC,OAAO,CAAA;KAAC,CAAC,CAAA;CAAC,IACpH,SAAQ,OAAO,KAAE,OAAO,CAAC,MAAM,GAAC,IAAI,CAAC,CAIlD;AACD,wBAAgB,+BAA+B,CAAC,IAAI,EAAC;IAAC,aAAa,CAAC,KAAK,EAAC,MAAM,EAAC,YAAY,EAAC,OAAO,GAAE,OAAO,CAAC;QAAC,GAAG,EAAC,MAAM,CAAA;KAAC,CAAC,CAAA;CAAC,IAC/G,SAAQ,OAAO,KAAE,OAAO,CAAC,MAAM,GAAC,IAAI,CAAC,CAKlD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Use the customer's configured official provider client. No JWT decoding shortcuts. */
|
|
2
|
+
function bearer(request) {
|
|
3
|
+
const match = /^Bearer ([^\s,]+)$/i.exec(request.headers.get('authorization') ?? '');
|
|
4
|
+
return match?.[1] ?? null;
|
|
5
|
+
}
|
|
6
|
+
function accountId(value) {
|
|
7
|
+
return typeof value === 'string' && value.length > 0 && value.length <= 200 && value.trim() === value && !/[\x00-\x1f\x7f]/.test(value) ? value : null;
|
|
8
|
+
}
|
|
9
|
+
export function supabaseMessengerAuthentication(auth) {
|
|
10
|
+
return async (request) => {
|
|
11
|
+
const token = bearer(request);
|
|
12
|
+
if (!token)
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
const result = await auth.getUser(token);
|
|
16
|
+
return result.error ? null : accountId(result.data.user?.id);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function firebaseMessengerAuthentication(auth) {
|
|
24
|
+
return async (request) => {
|
|
25
|
+
const token = bearer(request);
|
|
26
|
+
if (!token)
|
|
27
|
+
return null;
|
|
28
|
+
// true checks revoked sessions and disabled users in the customer's Firebase project.
|
|
29
|
+
try {
|
|
30
|
+
return accountId((await auth.verifyIdToken(token, true)).uid);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=messenger-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-auth.js","sourceRoot":"","sources":["../src/messenger-auth.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,SAAS,MAAM,CAAC,OAAe;IAC9B,MAAM,KAAK,GAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAE,EAAE,CAAC,CAAC;IACjF,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAE,IAAI,CAAC;AACzB,CAAC;AACD,SAAS,SAAS,CAAC,KAAa;IAC/B,OAAO,OAAO,KAAK,KAAG,QAAQ,IAAE,KAAK,CAAC,MAAM,GAAC,CAAC,IAAE,KAAK,CAAC,MAAM,IAAE,GAAG,IAAE,KAAK,CAAC,IAAI,EAAE,KAAG,KAAK,IAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA,CAAC,CAAA,KAAK,CAAA,CAAC,CAAA,IAAI,CAAC;AACpI,CAAC;AACD,MAAM,UAAU,+BAA+B,CAAC,IAAkF;IACjI,OAAO,KAAK,EAAC,OAAe,EAAsB,EAAE;QACnD,MAAM,KAAK,GAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAAA,IAAG,CAAC,KAAK;YAAC,OAAO,IAAI,CAAC;QAClD,IAAG,CAAC;YAAA,MAAM,MAAM,GAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAAA,OAAO,MAAM,CAAC,KAAK,CAAA,CAAC,CAAA,IAAI,CAAA,CAAC,CAAA,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAAA,CAAC;QAAA,MAAK,CAAC;YAAA,OAAO,IAAI,CAAC;QAAA,CAAC;IACzH,CAAC,CAAC;AACH,CAAC;AACD,MAAM,UAAU,+BAA+B,CAAC,IAA6E;IAC5H,OAAO,KAAK,EAAC,OAAe,EAAsB,EAAE;QACnD,MAAM,KAAK,GAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAAA,IAAG,CAAC,KAAK;YAAC,OAAO,IAAI,CAAC;QAClD,sFAAsF;QACtF,IAAG,CAAC;YAAA,OAAO,SAAS,CAAC,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAAA,CAAC;QAAA,MAAK,CAAC;YAAA,OAAO,IAAI,CAAC;QAAA,CAAC;IACtF,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MessengerStarterOptions } from './messenger-init.js';
|
|
2
|
+
export type MessengerAuthProvider = 'custom' | 'supabase' | 'firebase';
|
|
3
|
+
export declare function connectStarterTemplates(files: Record<string, string>, options: MessengerStarterOptions): Record<string, string>;
|
|
4
|
+
//# sourceMappingURL=messenger-connected-templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-connected-templates.d.ts","sourceRoot":"","sources":["../src/messenger-connected-templates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,qBAAqB,CAAC;AACjE,MAAM,MAAM,qBAAqB,GAAC,QAAQ,GAAC,UAAU,GAAC,UAAU,CAAC;AACjE,wBAAgB,uBAAuB,CAAC,KAAK,EAAC,MAAM,CAAC,MAAM,EAAC,MAAM,CAAC,EAAC,OAAO,EAAC,uBAAuB,0BAkIlG"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export function connectStarterTemplates(files, options) {
|
|
2
|
+
const provider = options.auth ?? 'custom', web = options.platform === 'web', q = JSON.stringify;
|
|
3
|
+
if (!['custom', 'supabase', 'firebase'].includes(provider))
|
|
4
|
+
throw Error('Choose custom, supabase or firebase sign-in.');
|
|
5
|
+
if (provider !== 'custom') {
|
|
6
|
+
const setup = provider === 'supabase' ? `import {createClient} from '@supabase/supabase-js';
|
|
7
|
+
import {supabaseMessengerAuthentication} from '@robono/server';
|
|
8
|
+
let verify:ReturnType<typeof supabaseMessengerAuthentication>|undefined;
|
|
9
|
+
export async function authenticate(request:Request){
|
|
10
|
+
if(verify)return verify(request);
|
|
11
|
+
const url=process.env.SUPABASE_URL,key=process.env.SUPABASE_PUBLISHABLE_KEY;
|
|
12
|
+
if(!url||!key)throw Error('Set your existing Supabase URL and publishable key on the server.');
|
|
13
|
+
const provider=createClient(url,key,{auth:{persistSession:false,autoRefreshToken:false,detectSessionInUrl:false}});
|
|
14
|
+
verify=supabaseMessengerAuthentication(provider.auth);
|
|
15
|
+
return verify(request);
|
|
16
|
+
}` : `import {getApps,initializeApp,applicationDefault} from 'firebase-admin/app';
|
|
17
|
+
import {getAuth} from 'firebase-admin/auth';
|
|
18
|
+
import {firebaseMessengerAuthentication} from '@robono/server';
|
|
19
|
+
// Use the same Firebase project as your existing sign-in. Keep credentials on the server.
|
|
20
|
+
let verify:ReturnType<typeof firebaseMessengerAuthentication>|undefined;
|
|
21
|
+
export async function authenticate(request:Request){
|
|
22
|
+
if(verify)return verify(request);
|
|
23
|
+
const provider=getApps().find(app=>app.name==='robono-auth')??initializeApp({credential:applicationDefault()},'robono-auth');
|
|
24
|
+
verify=firebaseMessengerAuthentication(getAuth(provider));
|
|
25
|
+
return verify(request);
|
|
26
|
+
}`;
|
|
27
|
+
files['server/robono-access.ts'] = `import type {MessengerAccess} from '@robono/server';\n${setup}\n\n// Keep YOUR current eligibility, suspension and membership checks here.
|
|
28
|
+
// Signing in does not grant permission to every conversation.
|
|
29
|
+
export async function authorize(access:MessengerAccess):Promise<boolean>{
|
|
30
|
+
throw Error('Connect your existing messaging permission check in robono-access.ts');
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
files['.env.example'] += provider === 'supabase' ? 'SUPABASE_URL=\nSUPABASE_PUBLISHABLE_KEY=\n' : '# Server only: path to your existing Firebase service-account file, or workload identity.\nGOOGLE_APPLICATION_CREDENTIALS=\n';
|
|
34
|
+
}
|
|
35
|
+
files['client/RobonoChat.tsx'] = web ? `'use client';
|
|
36
|
+
import {useEffect,useRef} from 'react';
|
|
37
|
+
import {mountRobonoMessenger} from '@robono/messaging-web';
|
|
38
|
+
type Options=Parameters<typeof mountRobonoMessenger>[1];
|
|
39
|
+
export function RobonoChat({userId,getAccessToken,contacts,onReady,onSignOut}:{userId:string;getAccessToken:Options['getAccessToken'];contacts?:Options['contacts'];onSignOut?:()=>Promise<void>;onReady?:(chat:ReturnType<typeof mountRobonoMessenger>|null)=>void}){
|
|
40
|
+
const element=useRef<HTMLDivElement>(null),latest=useRef({userId,getAccessToken,onReady,onSignOut}),active=useRef<ReturnType<typeof mountRobonoMessenger>|null>(null);
|
|
41
|
+
latest.current={userId,getAccessToken,onReady,onSignOut};
|
|
42
|
+
useEffect(()=>{
|
|
43
|
+
if(!element.current)return;
|
|
44
|
+
// Capture this account's token callback; never send another account's token for it.
|
|
45
|
+
const token=async()=>{if(latest.current.userId!==userId)throw Error('Account changed.');const value=await latest.current.getAccessToken();if(latest.current.userId!==userId)throw Error('Account changed.');return value;};
|
|
46
|
+
const chat=mountRobonoMessenger(element.current,{endpoint:'/api/robono',companyName:${q(options.companyName)},userId,getAccessToken:token,contacts,account:{name:'Account',onSignOut:async()=>{await latest.current.onSignOut?.();}}});
|
|
47
|
+
active.current=chat;latest.current.onReady?.(chat);
|
|
48
|
+
return()=>{active.current=null;latest.current.onReady?.(null);chat.unmount();};
|
|
49
|
+
},[userId]);
|
|
50
|
+
useEffect(()=>{active.current?.updateContacts(contacts);},[contacts]);
|
|
51
|
+
return <div ref={element} style={{height:'100%',minHeight:400}}/>;
|
|
52
|
+
}
|
|
53
|
+
` : `import React,{useEffect,useRef,useState} from 'react';
|
|
54
|
+
import {createExpoMessenger,ExpoMessenger,type ExpoMessengerProps} from '@robono/messaging-react-native/expo';
|
|
55
|
+
type Connection=ReturnType<typeof createExpoMessenger>;
|
|
56
|
+
export function RobonoChat({userId,getAccessToken,contacts,isCallActive,onReady,onSignOut}:{userId:string;getAccessToken:()=>Promise<string>;contacts?:ExpoMessengerProps['contacts'];isCallActive?:()=>boolean;onSignOut?:()=>Promise<void>;onReady?:(connection:Connection|null)=>void}){
|
|
57
|
+
const latest=useRef({userId,getAccessToken,isCallActive,onReady});latest.current={userId,getAccessToken,isCallActive,onReady};
|
|
58
|
+
const [active,setActive]=useState<{userId:string;connection:Connection}|null>(null);
|
|
59
|
+
useEffect(()=>{
|
|
60
|
+
const token=async()=>{if(latest.current.userId!==userId)throw Error('Account changed.');const value=await latest.current.getAccessToken();if(latest.current.userId!==userId)throw Error('Account changed.');return value;};
|
|
61
|
+
const connection=createExpoMessenger({endpoint:${q(options.origin + '/api/robono')},userId,getAccessToken:token,isCallActive:()=>latest.current.isCallActive?.()??false});
|
|
62
|
+
setActive({userId,connection});latest.current.onReady?.(connection);
|
|
63
|
+
return()=>{latest.current.onReady?.(null);connection.dispose();};
|
|
64
|
+
},[userId]);
|
|
65
|
+
if(!active||active.userId!==userId)return null;
|
|
66
|
+
return <ExpoMessenger {...active.connection} userId={userId} companyName={${q(options.companyName)}} contacts={contacts} account={onSignOut?{name:'Account',onSignOut}:undefined}/>;
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
files['client/ConnectedRobonoChat.tsx'] = `'use client';
|
|
70
|
+
import React,{useEffect,useRef,useState} from 'react';
|
|
71
|
+
import {RobonoChat} from './RobonoChat';
|
|
72
|
+
import type {ComponentProps} from 'react';
|
|
73
|
+
type ChatProps=ComponentProps<typeof RobonoChat>;
|
|
74
|
+
export interface RobonoAccount {id:string;getAccessToken:()=>Promise<string>}
|
|
75
|
+
export interface RobonoAccounts {
|
|
76
|
+
// Call the listener immediately with the current account, then on every account change.
|
|
77
|
+
subscribe(listener:(account:RobonoAccount|null)=>void):()=>void;
|
|
78
|
+
signOut():Promise<unknown>;
|
|
79
|
+
}
|
|
80
|
+
/** Keep mounted within your signed-in app. No second login or copied session is required. */
|
|
81
|
+
export function ConnectedRobonoChat({accounts,...props}:Omit<ChatProps,'userId'|'getAccessToken'|'onReady'|'onSignOut'>&{accounts:RobonoAccounts}){
|
|
82
|
+
const [account,setAccount]=useState<RobonoAccount|null>(null),current=useRef<RobonoAccount|null>(null);
|
|
83
|
+
const sourceVersion=useRef(0),[version,setVersion]=useState(0);
|
|
84
|
+
const chat=useRef<Parameters<NonNullable<ChatProps['onReady']>>[0]>(null);
|
|
85
|
+
useEffect(()=>{
|
|
86
|
+
let live=true;setVersion(++sourceVersion.current);
|
|
87
|
+
if(current.current){void chat.current?.signOut().catch(()=>{});chat.current=null;current.current=null;setAccount(null);}
|
|
88
|
+
const off=accounts.subscribe(next=>{
|
|
89
|
+
if(!live)return;
|
|
90
|
+
if(current.current?.id!==next?.id){void chat.current?.signOut().catch(()=>{});chat.current=null;}
|
|
91
|
+
current.current=next;setAccount(next);
|
|
92
|
+
});
|
|
93
|
+
return()=>{live=false;sourceVersion.current++;current.current=null;off();};
|
|
94
|
+
},[accounts]);
|
|
95
|
+
const token=async()=>{const active=current.current;if(!active||active.id!==account?.id||sourceVersion.current!==version)throw Error('Account changed.');const value=await active.getAccessToken();if(current.current?.id!==active.id||sourceVersion.current!==version)throw Error('Account changed.');return value;};
|
|
96
|
+
return account?<RobonoChat key={version+':'+account.id} {...props} userId={account.id} getAccessToken={token} onReady={value=>{chat.current=value;}} onSignOut={async()=>{try{await chat.current?.signOut();}finally{await accounts.signOut();}}}/>:null;
|
|
97
|
+
}
|
|
98
|
+
// Before your app signs out, await its messenger handle's signOut when available.
|
|
99
|
+
// A provider sign-out/account-change event also clears chat immediately. Notification removal
|
|
100
|
+
// may require a valid old session; revoke that session and synchronize account suspension too.
|
|
101
|
+
`;
|
|
102
|
+
if (provider === 'supabase')
|
|
103
|
+
files['client/robono-accounts.ts'] = `import type {SupabaseClient} from '@supabase/supabase-js';
|
|
104
|
+
import type {RobonoAccounts,RobonoAccount} from './ConnectedRobonoChat';
|
|
105
|
+
export function robonoAccounts(client:SupabaseClient):RobonoAccounts{
|
|
106
|
+
const account=(id:string):RobonoAccount=>({id,getAccessToken:async()=>{
|
|
107
|
+
const {data,error}=await client.auth.getSession();
|
|
108
|
+
if(error||data.session?.user.id!==id)throw Error('Sign in again.');
|
|
109
|
+
return data.session.access_token;
|
|
110
|
+
}});
|
|
111
|
+
return {subscribe(listener){
|
|
112
|
+
let live=true,events=0;
|
|
113
|
+
const {data}=client.auth.onAuthStateChange((_event,session)=>{events++;if(live)listener(session?account(session.user.id):null);});
|
|
114
|
+
const initial=events;
|
|
115
|
+
void client.auth.getSession().then(({data,error})=>{if(live&&events===initial)listener(!error&&data.session?account(data.session.user.id):null);}).catch(()=>{if(live&&events===initial)listener(null);});
|
|
116
|
+
return()=>{live=false;data.subscription.unsubscribe();};
|
|
117
|
+
},signOut:()=>client.auth.signOut()};
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
if (provider === 'firebase')
|
|
121
|
+
files['client/robono-accounts.ts'] = `import {onIdTokenChanged,signOut,type Auth} from 'firebase/auth';
|
|
122
|
+
import type {RobonoAccounts} from './ConnectedRobonoChat';
|
|
123
|
+
export function robonoAccounts(auth:Auth):RobonoAccounts{
|
|
124
|
+
return {subscribe(listener){return onIdTokenChanged(auth,user=>listener(user?{id:user.uid,getAccessToken:async()=>{
|
|
125
|
+
if(auth.currentUser?.uid!==user.uid)throw Error('Sign in again.');
|
|
126
|
+
const token=await user.getIdToken();
|
|
127
|
+
if(auth.currentUser?.uid!==user.uid)throw Error('Sign in again.');
|
|
128
|
+
return token;
|
|
129
|
+
}}:null),()=>listener(null));},signOut:()=>signOut(auth)};
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
files['ROBONO-SETUP.md'] += `\n## Connected accounts\n${provider === 'custom' ? 'Implement the small RobonoAccounts interface using your existing verified sign-in client.' : 'Use robonoAccounts(yourExisting' + (provider === 'supabase' ? 'SupabaseClient' : 'FirebaseAuth') + '). Create it once with useMemo, then pass it as accounts to ConnectedRobonoChat.'} Pass your current contact dictionary as contacts; names and avatars update without restarting the chat. The connected screen listens for sign-in, sign-out and account switching. It uses a fresh token for each request and never substitutes one account’s token for another.\n\nKeep ConnectedRobonoChat mounted in your signed-in application. Keep your own account suspension and permission checks in robono-access.ts; provider identity verification does not replace them. Missing permission code intentionally denies access. For explicit host sign-out, unregister messaging before revoking your session when possible. If the provider session has already expired, local content is still cleared; synchronize revoked account access on the server so notifications are also blocked.\n`;
|
|
133
|
+
return files;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=messenger-connected-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-connected-templates.js","sourceRoot":"","sources":["../src/messenger-connected-templates.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CAAC,KAA2B,EAAC,OAA+B;IAClG,MAAM,QAAQ,GAAC,OAAO,CAAC,IAAI,IAAE,QAAQ,EAAC,GAAG,GAAC,OAAO,CAAC,QAAQ,KAAG,KAAK,EAAC,CAAC,GAAC,IAAI,CAAC,SAAS,CAAC;IACpF,IAAG,CAAC,CAAC,QAAQ,EAAC,UAAU,EAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAC,MAAM,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACpH,IAAG,QAAQ,KAAG,QAAQ,EAAC,CAAC;QACvB,MAAM,KAAK,GAAC,QAAQ,KAAG,UAAU,CAAA,CAAC,CAAA;;;;;;;;;;EAUlC,CAAA,CAAC,CAAA;;;;;;;;;;EAUD,CAAC;QACD,KAAK,CAAC,yBAAyB,CAAC,GAAC,yDAAyD,KAAK;;;;;CAKhG,CAAC;QACA,KAAK,CAAC,cAAc,CAAC,IAAE,QAAQ,KAAG,UAAU,CAAA,CAAC,CAAA,4CAA4C,CAAA,CAAC,CAAA,8HAA8H,CAAC;IAC1N,CAAC;IACD,KAAK,CAAC,uBAAuB,CAAC,GAAC,GAAG,CAAA,CAAC,CAAA;;;;;;;;;;;wFAWoD,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;;;;;;;CAO7G,CAAA,CAAC,CAAA;;;;;;;;mDAQiD,CAAC,CAAC,OAAO,CAAC,MAAM,GAAC,aAAa,CAAC;;;;;6EAKL,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;;CAElG,CAAC;IACD,KAAK,CAAC,gCAAgC,CAAC,GAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCxC,CAAC;IACD,IAAG,QAAQ,KAAG,UAAU;QAAC,KAAK,CAAC,2BAA2B,CAAC,GAAC;;;;;;;;;;;;;;;;CAgB5D,CAAC;IACD,IAAG,QAAQ,KAAG,UAAU;QAAC,KAAK,CAAC,2BAA2B,CAAC,GAAC;;;;;;;;;;CAU5D,CAAC;IACD,KAAK,CAAC,iBAAiB,CAAC,IAAE,4BAA4B,QAAQ,KAAG,QAAQ,CAAA,CAAC,CAAA,2FAA2F,CAAA,CAAC,CAAA,iCAAiC,GAAC,CAAC,QAAQ,KAAG,UAAU,CAAA,CAAC,CAAA,gBAAgB,CAAA,CAAC,CAAA,cAAc,CAAC,GAAC,kFAAkF,4wBAA4wB,CAAC;IAC/lC,OAAO,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -4,13 +4,13 @@ import { stdin, stdout } from 'node:process';
|
|
|
4
4
|
import { writeMessengerStarter } from './messenger-init.js';
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
6
|
if (args.includes('--help')) {
|
|
7
|
-
console.log('robono-messenger-init [--platform web|expo --framework next|fetch --origin https://your-site --company "Your company" --out ./robono-setup]\nCreates files in a NEW directory. No keys are requested, no packages are installed, and no existing application files are changed.');
|
|
7
|
+
console.log('robono-messenger-init [--platform web|expo --framework next|fetch --origin https://your-site --company "Your company" --out ./robono-setup --auth custom|supabase|firebase]\nCreates files in a NEW directory. No keys are requested, no packages are installed, and no existing application files are changed.');
|
|
8
8
|
process.exit(0);
|
|
9
9
|
}
|
|
10
10
|
const values = {};
|
|
11
11
|
for (let i = 0; i < args.length; i += 2) {
|
|
12
12
|
const key = args[i];
|
|
13
|
-
if (!key || !['--platform', '--framework', '--origin', '--company', '--out'].includes(key) || !args[i + 1] || values[key]) {
|
|
13
|
+
if (!key || !['--platform', '--framework', '--origin', '--company', '--out', '--auth'].includes(key) || !args[i + 1] || values[key]) {
|
|
14
14
|
console.error('Use --help for valid choices.');
|
|
15
15
|
process.exit(1);
|
|
16
16
|
}
|
|
@@ -26,7 +26,7 @@ try {
|
|
|
26
26
|
const destination = await ask('--out', 'New setup directory', './robono-setup');
|
|
27
27
|
if (!platform || !framework || !origin || !companyName || !destination)
|
|
28
28
|
throw Error('Supply the missing choices; use --help.');
|
|
29
|
-
const files = await writeMessengerStarter(destination, { platform, framework, origin, companyName });
|
|
29
|
+
const files = await writeMessengerStarter(destination, { platform, framework, origin, companyName, ...(values['--auth'] ? { auth: values['--auth'] } : {}) });
|
|
30
30
|
console.log(`Created ${files.length} integration files. Start with ROBONO-SETUP.md in the chosen directory. Connect the two account functions before running your app. No website was published.`);
|
|
31
31
|
}
|
|
32
32
|
catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messenger-init-cli.js","sourceRoot":"","sources":["../src/messenger-init-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAC,eAAe,EAAC,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAC,KAAK,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAC,qBAAqB,EAA8B,MAAM,qBAAqB,CAAC;AACvF,MAAM,IAAI,GAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACjC,IAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAC,CAAC;IAAA,OAAO,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"messenger-init-cli.js","sourceRoot":"","sources":["../src/messenger-init-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAC,eAAe,EAAC,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAC,KAAK,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAC,qBAAqB,EAA8B,MAAM,qBAAqB,CAAC;AACvF,MAAM,IAAI,GAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACjC,IAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAC,CAAC;IAAA,OAAO,CAAC,GAAG,CAAC,iTAAiT,CAAC,CAAC;IAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAAA,CAAC;AAC5W,MAAM,MAAM,GAAuB,EAAE,CAAC;AACtC,KAAI,IAAI,CAAC,GAAC,CAAC,EAAC,CAAC,GAAC,IAAI,CAAC,MAAM,EAAC,CAAC,IAAE,CAAC,EAAC,CAAC;IAAA,MAAM,GAAG,GAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAAA,IAAG,CAAC,GAAG,IAAE,CAAC,CAAC,YAAY,EAAC,aAAa,EAAC,UAAU,EAAC,WAAW,EAAC,OAAO,EAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAE,CAAC,IAAI,CAAC,CAAC,GAAC,CAAC,CAAC,IAAE,MAAM,CAAC,GAAG,CAAC,EAAC,CAAC;QAAA,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAAA,CAAC;IAAA,MAAM,CAAC,GAAG,CAAC,GAAC,IAAI,CAAC,CAAC,GAAC,CAAC,CAAE,CAAC;AAAA,CAAC;AACjQ,MAAM,MAAM,GAAC,KAAK,CAAC,KAAK,CAAA,CAAC,CAAA,eAAe,CAAC,EAAC,KAAK,EAAC,KAAK,EAAC,MAAM,EAAC,MAAM,EAAC,CAAC,CAAA,CAAC,CAAA,IAAI,CAAC;AAC3E,IAAG,CAAC;IACH,MAAM,GAAG,GAAC,KAAK,EAAC,GAAU,EAAC,KAAY,EAAC,QAAgB,EAAC,EAAE,CAAA,MAAM,CAAC,GAAG,CAAC,IAAE,CAAC,MAAM,CAAA,CAAC,CAAA,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,GAAC,CAAC,QAAQ,CAAA,CAAC,CAAA,KAAK,QAAQ,GAAG,CAAA,CAAC,CAAA,EAAE,CAAC,GAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,IAAE,QAAQ,CAAA,CAAC,CAAA,SAAS,CAAC,CAAC;IAC/K,MAAM,QAAQ,GAAC,MAAM,GAAG,CAAC,YAAY,EAAC,4CAA4C,EAAC,KAAK,CAAC,CAAC;IAC1F,MAAM,SAAS,GAAC,MAAM,GAAG,CAAC,aAAa,EAAC,yEAAyE,EAAC,MAAM,CAAC,CAAC;IAC1H,MAAM,MAAM,GAAC,MAAM,GAAG,CAAC,UAAU,EAAC,kEAAkE,CAAC,CAAC;IACtG,MAAM,WAAW,GAAC,MAAM,GAAG,CAAC,WAAW,EAAC,mBAAmB,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAC,MAAM,GAAG,CAAC,OAAO,EAAC,qBAAqB,EAAC,gBAAgB,CAAC,CAAC;IAC5E,IAAG,CAAC,QAAQ,IAAE,CAAC,SAAS,IAAE,CAAC,MAAM,IAAE,CAAC,WAAW,IAAE,CAAC,WAAW;QAAC,MAAM,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACrH,MAAM,KAAK,GAAC,MAAM,qBAAqB,CAAC,WAAW,EAAC,EAAC,QAAQ,EAAC,SAAS,EAAC,MAAM,EAAC,WAAW,EAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA,CAAC,CAAA,EAAC,IAAI,EAAC,MAAM,CAAC,QAAQ,CAAC,EAAC,CAAA,CAAC,CAAA,EAAE,CAAC,EAA4B,CAAC,CAAC;IACzK,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,MAAM,8JAA8J,CAAC,CAAC;AACpM,CAAC;AAAA,OAAM,CAAC,EAAC,CAAC;IAAA,OAAO,CAAC,KAAK,CAAC,CAAC,YAAY,KAAK,CAAA,CAAC,CAAA,CAAC,CAAC,OAAO,CAAA,CAAC,CAAA,mCAAmC,CAAC,CAAC;IAAA,OAAO,CAAC,QAAQ,GAAC,CAAC,CAAC;AAAA,CAAC;QAAO,CAAC;IAAA,MAAM,EAAE,KAAK,EAAE,CAAC;AAAA,CAAC"}
|
package/dist/messenger-init.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { type MessengerAuthProvider } from './messenger-connected-templates.js';
|
|
1
2
|
export interface MessengerStarterOptions {
|
|
2
3
|
platform: 'web' | 'expo';
|
|
3
4
|
framework: 'next' | 'fetch';
|
|
4
5
|
origin: string;
|
|
5
6
|
companyName: string;
|
|
7
|
+
auth?: MessengerAuthProvider;
|
|
6
8
|
}
|
|
7
9
|
/** Pure templates contain no credentials. Host authentication deliberately fails closed. */
|
|
8
10
|
export declare function messengerStarterFiles(options: MessengerStarterOptions): Record<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messenger-init.d.ts","sourceRoot":"","sources":["../src/messenger-init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"messenger-init.d.ts","sourceRoot":"","sources":["../src/messenger-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAGtG,MAAM,WAAW,uBAAuB;IAAE,QAAQ,EAAC,KAAK,GAAC,MAAM,CAAC;IAAA,SAAS,EAAC,MAAM,GAAC,OAAO,CAAC;IAAA,MAAM,EAAC,MAAM,CAAC;IAAA,WAAW,EAAC,MAAM,CAAC;IAAA,IAAI,CAAC,EAAC,qBAAqB,CAAA;CAAC;AACtJ,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,OAAO,EAAC,uBAAuB,GAAE,MAAM,CAAC,MAAM,EAAC,MAAM,CAAC,CA+E3F;AACD,uFAAuF;AACvF,wBAAsB,qBAAqB,CAAC,WAAW,EAAC,MAAM,EAAC,OAAO,EAAC,uBAAuB,qBAc7F"}
|
package/dist/messenger-init.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { connectStarterTemplates } from './messenger-connected-templates.js';
|
|
1
2
|
import { mkdir, writeFile, access, realpath, lstat, rm } from 'node:fs/promises';
|
|
2
3
|
import { resolve, dirname, relative, isAbsolute, join } from 'node:path';
|
|
3
4
|
/** Pure templates contain no credentials. Host authentication deliberately fails closed. */
|
|
@@ -29,13 +30,17 @@ export async function authorize(access:MessengerAccess):Promise<boolean>{
|
|
|
29
30
|
'server/robono-access.ts': auth,
|
|
30
31
|
'server/robono.ts': `import {createRobonoMessengerServer} from '@robono/server';
|
|
31
32
|
import {authenticate,authorize} from './robono-access';
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
let messenger:ReturnType<typeof createRobonoMessengerServer>|undefined;
|
|
34
|
+
export function getMessenger(){
|
|
35
|
+
if(messenger)return messenger;
|
|
36
|
+
const apiKey=process.env.ROBONO_API_KEY;
|
|
37
|
+
if(!apiKey)throw Error('Set ROBONO_API_KEY on your server.');
|
|
38
|
+
return messenger=createRobonoMessengerServer({apiKey,authenticate,authorize,allowedOrigins:[${q(options.origin)}]});
|
|
39
|
+
}
|
|
35
40
|
`,
|
|
36
|
-
[route]: `import {
|
|
37
|
-
${options.framework === 'next' ? "export const runtime = 'nodejs';\n" : ''}export const GET=(request:Request)=>
|
|
38
|
-
export const POST=(request:Request)=>
|
|
41
|
+
[route]: `import {getMessenger} from ${q(options.framework === 'next' ? '../../../server/robono' : './robono')};
|
|
42
|
+
${options.framework === 'next' ? "export const runtime = 'nodejs';\n" : ''}export const GET=(request:Request)=>getMessenger().handle(request);
|
|
43
|
+
export const POST=(request:Request)=>getMessenger().handle(request);
|
|
39
44
|
`,
|
|
40
45
|
'server/synchronize-contacts.ts': `// Run on your server when your own relationship record changes.
|
|
41
46
|
// Pass the server-only messenger instance from your application. Revisions come from YOUR database.
|
|
@@ -79,7 +84,7 @@ export function RobonoChat({userId,getAccessToken,contacts,isCallActive,onReady}
|
|
|
79
84
|
// Mount inside your app's safe area. Keep callbacks stable. Await connection.signOut()
|
|
80
85
|
// BEFORE ending your session; dispose only detaches the screen.
|
|
81
86
|
`;
|
|
82
|
-
return files;
|
|
87
|
+
return connectStarterTemplates(files, options);
|
|
83
88
|
}
|
|
84
89
|
/** All-or-nothing new directory; never edits a customer's existing app or env file. */
|
|
85
90
|
export async function writeMessengerStarter(destination, options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messenger-init.js","sourceRoot":"","sources":["../src/messenger-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,SAAS,EAAC,MAAM,EAAC,QAAQ,EAAC,KAAK,EAAC,EAAE,EAAC,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAC,OAAO,EAAC,OAAO,EAAC,QAAQ,EAAC,UAAU,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAEnE,4FAA4F;AAC5F,MAAM,UAAU,qBAAqB,CAAC,OAA+B;IACpE,MAAM,MAAM,GAAC,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,IAAG,MAAM,CAAC,MAAM,KAAG,OAAO,CAAC,MAAM,IAAE,MAAM,CAAC,QAAQ,IAAE,MAAM,CAAC,QAAQ,IAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAG,QAAQ,IAAE,MAAM,CAAC,QAAQ,KAAG,OAAO,IAAE,CAAC,WAAW,EAAC,WAAW,EAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAAC,MAAM,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAC7Q,IAAG,CAAC,CAAC,KAAK,EAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAE,CAAC,CAAC,MAAM,EAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,IAAE,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,IAAE,OAAO,CAAC,WAAW,CAAC,MAAM,GAAC,GAAG;QAAC,MAAM,KAAK,CAAC,4EAA4E,CAAC,CAAC;IACpP,IAAG,OAAO,CAAC,QAAQ,KAAG,MAAM,IAAE,MAAM,CAAC,QAAQ,KAAG,QAAQ;QAAC,MAAM,KAAK,CAAC,mEAAmE,CAAC,CAAC;IAC1I,MAAM,CAAC,GAAC,IAAI,CAAC,SAAS,EAAC,KAAK,GAAC,OAAO,CAAC,SAAS,KAAG,MAAM,CAAA,CAAC,CAAA,yBAAyB,CAAA,CAAC,CAAA,wBAAwB,CAAC;IAC3G,MAAM,IAAI,GAAC;;;;;;;;;;;;CAYX,CAAC;IACD,MAAM,KAAK,GAAuB;QACjC,YAAY,EAAC,mCAAmC;QAChD,cAAc,EAAC,+FAA+F;QAC9G,yBAAyB,EAAC,IAAI;QAC9B,kBAAkB,EAAC
|
|
1
|
+
{"version":3,"file":"messenger-init.js","sourceRoot":"","sources":["../src/messenger-init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-install-cli.d.ts","sourceRoot":"","sources":["../src/messenger-install-cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createInterface } from 'node:readline/promises';
|
|
3
|
+
import { stdin, stdout } from 'node:process';
|
|
4
|
+
import { planMessengerInstallation, applyMessengerInstallation, messengerInstallationChecklist } from './messenger-install.js';
|
|
5
|
+
import { checkRobonoMessengerSetup } from './messenger-check.js';
|
|
6
|
+
const args = process.argv.slice(2), values = {}, flags = new Set();
|
|
7
|
+
let prompt;
|
|
8
|
+
try {
|
|
9
|
+
for (let i = 0; i < args.length; i++) {
|
|
10
|
+
const arg = args[i];
|
|
11
|
+
if (['--help', '--yes', '--dry-run', '--no-install', '--check', '--json'].includes(arg)) {
|
|
12
|
+
flags.add(arg);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (!['--project', '--server-project', '--origin', '--company', '--auth'].includes(arg) || !args[i + 1] || args[i + 1].startsWith('--') || values[arg])
|
|
16
|
+
throw Error('Unknown or missing option. Use --help.');
|
|
17
|
+
values[arg] = args[++i];
|
|
18
|
+
}
|
|
19
|
+
if (flags.has('--help')) {
|
|
20
|
+
console.log(`robono-messenger-install [--project .] [--server-project ../server] [--auth supabase|firebase|custom] --origin https://your-company.com --company "Your company"
|
|
21
|
+
Detects Next.js App Router or Expo 55–56, places files, and installs matching packages.
|
|
22
|
+
Expo also needs a separate Next.js server and a static app.json. Existing application screens and .env files are preserved.
|
|
23
|
+
--dry-run lists the exact files and commands without changing anything.
|
|
24
|
+
--yes applies that plan without an interactive prompt. --no-install writes files only.
|
|
25
|
+
--check shows one setup checklist; set the three ROBONO_* test variables to include live checks. --json prints a shareable checklist.
|
|
26
|
+
The development-only /robono-preview route uses fictional people and never sends messages outside the page.`);
|
|
27
|
+
}
|
|
28
|
+
else if (flags.has('--check')) {
|
|
29
|
+
let checks = await messengerInstallationChecklist(values['--project'] ?? '.');
|
|
30
|
+
const endpoint = process.env.ROBONO_MESSENGER_URL, origin = process.env.ROBONO_WEBSITE_ORIGIN, accessToken = process.env.ROBONO_TEST_USER_TOKEN;
|
|
31
|
+
if (endpoint && origin && accessToken) {
|
|
32
|
+
try {
|
|
33
|
+
const live = await checkRobonoMessengerSetup({ endpoint, origin, accessToken });
|
|
34
|
+
checks = [...checks.filter(c => !['Live connection', 'Server key', 'Contacts', 'Notifications'].includes(c.name)), ...live];
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
checks.push({ name: 'Live connection', status: 'failed', detail: 'Check the exact endpoint, website origin and test-user token. No secret was included in this report.' });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (flags.has('--json'))
|
|
41
|
+
console.log(JSON.stringify({ schemaVersion: 1, checks, scope: 'Setup checks; not a complete security or delivery test.' }, null, 2));
|
|
42
|
+
else
|
|
43
|
+
for (const check of checks)
|
|
44
|
+
console.log(`${check.status === 'passed' ? 'PASS' : check.status === 'failed' ? 'FIX' : 'NEXT'} — ${check.name}: ${check.detail}`);
|
|
45
|
+
if (checks.some(c => c.status === 'failed'))
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
if (stdin.isTTY && !flags.has('--yes'))
|
|
50
|
+
prompt = createInterface({ input: stdin, output: stdout });
|
|
51
|
+
const ask = async (key, label) => values[key] ?? (prompt ? (await prompt.question(label + ': ')).trim() : undefined);
|
|
52
|
+
const origin = await ask('--origin', 'Website/server origin (for example https://your-company.com)'), companyName = await ask('--company', 'Company name');
|
|
53
|
+
if (!origin || !companyName)
|
|
54
|
+
throw Error('Supply --origin and --company, or run interactively.');
|
|
55
|
+
const auth = values['--auth'];
|
|
56
|
+
if (auth && !['custom', 'supabase', 'firebase'].includes(auth))
|
|
57
|
+
throw Error('Choose custom, supabase or firebase.');
|
|
58
|
+
const plan = await planMessengerInstallation({ project: values['--project'] ?? '.', origin, companyName, ...(values['--server-project'] ? { serverProject: values['--server-project'] } : {}), ...(auth ? { auth: auth } : {}) });
|
|
59
|
+
for (const file of plan.files)
|
|
60
|
+
console.log('Create: ' + file.path);
|
|
61
|
+
for (const file of plan.replacements)
|
|
62
|
+
console.log('Merge missing Expo plugins: ' + file.path + ' (original saved in .robono)');
|
|
63
|
+
for (const command of plan.commands)
|
|
64
|
+
console.log(`Install in ${command.cwd}: ${command.command} ${command.args.join(' ')}`);
|
|
65
|
+
for (const note of plan.notes)
|
|
66
|
+
console.log(note);
|
|
67
|
+
if (!flags.has('--dry-run')) {
|
|
68
|
+
if (!flags.has('--yes') && (!prompt || !/^y(es)?$/i.test((await prompt.question('Apply this plan? [y/N] ')).trim())))
|
|
69
|
+
throw Error('Nothing changed. Use --yes to apply the displayed plan.');
|
|
70
|
+
await applyMessengerInstallation(plan, !flags.has('--no-install'));
|
|
71
|
+
console.log('Integration files are ready. Open .robono/SETUP.md, then run robono-messenger-install --check. Nothing was deployed.');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
console.error(e instanceof Error ? e.message : 'Installation failed.');
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
prompt?.close();
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=messenger-install-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-install-cli.js","sourceRoot":"","sources":["../src/messenger-install-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface MessengerInstallOptions {
|
|
2
|
+
project: string;
|
|
3
|
+
serverProject?: string;
|
|
4
|
+
origin: string;
|
|
5
|
+
companyName: string;
|
|
6
|
+
auth?: 'custom' | 'supabase' | 'firebase';
|
|
7
|
+
}
|
|
8
|
+
export interface MessengerInstallPlan {
|
|
9
|
+
platform: 'web' | 'expo';
|
|
10
|
+
auth: 'custom' | 'supabase' | 'firebase';
|
|
11
|
+
files: {
|
|
12
|
+
path: string;
|
|
13
|
+
content: string;
|
|
14
|
+
}[];
|
|
15
|
+
replacements: {
|
|
16
|
+
path: string;
|
|
17
|
+
before: string;
|
|
18
|
+
after: string;
|
|
19
|
+
}[];
|
|
20
|
+
commands: {
|
|
21
|
+
cwd: string;
|
|
22
|
+
command: string;
|
|
23
|
+
args: string[];
|
|
24
|
+
}[];
|
|
25
|
+
notes: string[];
|
|
26
|
+
}
|
|
27
|
+
export declare function planMessengerInstallation(options: MessengerInstallOptions): Promise<MessengerInstallPlan>;
|
|
28
|
+
/** Preflight all files; exclusive writes and rollback avoid overwriting customer work. */
|
|
29
|
+
export declare function applyMessengerInstallation(plan: MessengerInstallPlan, install?: boolean): Promise<void>;
|
|
30
|
+
export declare function messengerInstallationChecklist(directory: string): Promise<{
|
|
31
|
+
name: string;
|
|
32
|
+
status: string;
|
|
33
|
+
detail: string;
|
|
34
|
+
}[]>;
|
|
35
|
+
//# sourceMappingURL=messenger-install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-install.d.ts","sourceRoot":"","sources":["../src/messenger-install.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,uBAAuB;IAAE,OAAO,EAAC,MAAM,CAAC;IAAA,aAAa,CAAC,EAAC,MAAM,CAAC;IAAA,MAAM,EAAC,MAAM,CAAC;IAAA,WAAW,EAAC,MAAM,CAAC;IAAA,IAAI,CAAC,EAAC,QAAQ,GAAC,UAAU,GAAC,UAAU,CAAA;CAAC;AAErJ,MAAM,WAAW,oBAAoB;IAAE,QAAQ,EAAC,KAAK,GAAC,MAAM,CAAC;IAAA,IAAI,EAAC,QAAQ,GAAC,UAAU,GAAC,UAAU,CAAC;IAAA,KAAK,EAAC;QAAC,IAAI,EAAC,MAAM,CAAC;QAAA,OAAO,EAAC,MAAM,CAAA;KAAC,EAAE,CAAC;IAAA,YAAY,EAAC;QAAC,IAAI,EAAC,MAAM,CAAC;QAAA,MAAM,EAAC,MAAM,CAAC;QAAA,KAAK,EAAC,MAAM,CAAA;KAAC,EAAE,CAAC;IAAA,QAAQ,EAAC;QAAC,GAAG,EAAC,MAAM,CAAC;QAAA,OAAO,EAAC,MAAM,CAAC;QAAA,IAAI,EAAC,MAAM,EAAE,CAAA;KAAC,EAAE,CAAC;IAAA,KAAK,EAAC,MAAM,EAAE,CAAA;CAAC;AA6BlQ,wBAAsB,yBAAyB,CAAC,OAAO,EAAC,uBAAuB,GAAE,OAAO,CAAC,oBAAoB,CAAC,CAgE7G;AACD,0FAA0F;AAC1F,wBAAsB,0BAA0B,CAAC,IAAI,EAAC,oBAAoB,EAAC,OAAO,UAAK,iBAYtF;AACD,wBAAsB,8BAA8B,CAAC,SAAS,EAAC,MAAM;;;;KAkBpE"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { messengerPreviewTemplates } from './messenger-preview-template.js';
|
|
2
|
+
import { readFile, writeFile, mkdir, lstat, realpath, unlink } from 'node:fs/promises';
|
|
3
|
+
import { resolve, join, dirname, relative } from 'node:path';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { messengerStarterFiles } from './messenger-init.js';
|
|
6
|
+
async function exists(file) { try {
|
|
7
|
+
await lstat(file);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch (e) {
|
|
11
|
+
if (e.code === 'ENOENT')
|
|
12
|
+
return false;
|
|
13
|
+
throw e;
|
|
14
|
+
} }
|
|
15
|
+
async function project(directory) {
|
|
16
|
+
const root = resolve(directory);
|
|
17
|
+
if (await realpath(root) !== root)
|
|
18
|
+
throw Error('Use a project path without symbolic links.');
|
|
19
|
+
const stat = await lstat(join(root, 'package.json'));
|
|
20
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
21
|
+
throw Error('Use a regular package.json.');
|
|
22
|
+
const pkg = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
|
|
23
|
+
return { root, pkg, deps: { ...pkg.devDependencies, ...pkg.dependencies } };
|
|
24
|
+
}
|
|
25
|
+
async function safePath(file) {
|
|
26
|
+
let part = resolve(file);
|
|
27
|
+
while (true) {
|
|
28
|
+
if (await exists(part)) {
|
|
29
|
+
const s = await lstat(part);
|
|
30
|
+
if (s.isSymbolicLink())
|
|
31
|
+
throw Error('Installation paths cannot contain symbolic links.');
|
|
32
|
+
}
|
|
33
|
+
const parent = dirname(part);
|
|
34
|
+
if (parent === part)
|
|
35
|
+
break;
|
|
36
|
+
part = parent;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function appRoot(root) {
|
|
40
|
+
for (const base of ['src', ''])
|
|
41
|
+
if (await exists(join(root, base, 'app'))) {
|
|
42
|
+
await safePath(join(root, base, 'app'));
|
|
43
|
+
return base;
|
|
44
|
+
}
|
|
45
|
+
throw Error('Automatic website installation supports Next.js App Router. Create its app directory first, or use robono-messenger-init for another server.');
|
|
46
|
+
}
|
|
47
|
+
function installCommand(root, manager, packages) {
|
|
48
|
+
if (!['npm', 'pnpm', 'yarn'].includes(manager))
|
|
49
|
+
throw Error('Supported package managers: npm, pnpm, yarn.');
|
|
50
|
+
return { cwd: root, command: manager, args: manager === 'npm' ? ['install', '--ignore-scripts', '--', ...packages] : manager === 'pnpm' ? ['add', '--ignore-scripts', '--', ...packages] : ['add', '--ignore-scripts', ...packages] };
|
|
51
|
+
}
|
|
52
|
+
async function manager(root, pkg) {
|
|
53
|
+
const locks = [];
|
|
54
|
+
for (const [file, name] of [['package-lock.json', 'npm'], ['pnpm-lock.yaml', 'pnpm'], ['yarn.lock', 'yarn']])
|
|
55
|
+
if (await exists(join(root, file)))
|
|
56
|
+
locks.push(name);
|
|
57
|
+
const named = pkg.packageManager?.split('@')[0];
|
|
58
|
+
if (locks.length > 1 || named && locks.length && locks[0] !== named)
|
|
59
|
+
throw Error('Conflicting package-manager files. Keep the lockfile used by this project before installing.');
|
|
60
|
+
const result = named ?? locks[0] ?? 'npm';
|
|
61
|
+
// Yarn Berry has different script controls; do not silently run hooks or rewrite its configuration.
|
|
62
|
+
if (result === 'yarn' && pkg.packageManager && !pkg.packageManager.startsWith('yarn@1.'))
|
|
63
|
+
throw Error('Use the generated-file command for Yarn 2+; automatic installation currently supports Yarn 1, npm and pnpm.');
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
export async function planMessengerInstallation(options) {
|
|
67
|
+
if (Number(process.versions.node.split('.')[0]) < 22)
|
|
68
|
+
throw Error('Use Node.js 22 or later for automatic installation.');
|
|
69
|
+
const client = await project(options.project), platform = client.deps.expo ? 'expo' : client.deps.next ? 'web' : null;
|
|
70
|
+
if (!platform)
|
|
71
|
+
throw Error('Automatic installation supports Next.js App Router and Expo. For other projects use robono-messenger-init.');
|
|
72
|
+
if (platform === 'expo' && !/^[~^]?(55|56)\./.test(client.deps.expo))
|
|
73
|
+
throw Error('The supplied Expo messenger supports Expo 55–56. Keep your current app unchanged and check the mobile installation guide.');
|
|
74
|
+
if (platform === 'expo' && !options.serverProject)
|
|
75
|
+
throw Error('Pass --server-project with your existing Next.js server folder. A phone app must not contain the Robono server key.');
|
|
76
|
+
const server = platform === 'web' ? client : await project(options.serverProject);
|
|
77
|
+
if (!server.deps.next)
|
|
78
|
+
throw Error('The automatic server connection needs Next.js App Router. Use robono-messenger-init for other servers.');
|
|
79
|
+
if (platform === 'expo' && server.root === client.root)
|
|
80
|
+
throw Error('Use a separate server project; never install server credentials inside the phone app.');
|
|
81
|
+
const serverBase = await appRoot(server.root), clientBase = platform === 'web' ? serverBase : (await exists(join(client.root, 'src')) ? 'src' : '');
|
|
82
|
+
const detected = client.deps['@supabase/supabase-js'] ? 'supabase' : client.deps.firebase ? 'firebase' : 'custom';
|
|
83
|
+
if (!options.auth && client.deps['@supabase/supabase-js'] && client.deps.firebase)
|
|
84
|
+
throw Error('Both sign-in providers are installed. Choose --auth supabase, firebase or custom.');
|
|
85
|
+
const auth = options.auth ?? detected;
|
|
86
|
+
const providerName = auth === 'supabase' ? '@supabase/supabase-js' : auth === 'firebase' ? 'firebase' : null;
|
|
87
|
+
if (providerName && client.deps[providerName]) {
|
|
88
|
+
const installed = await readFile(join(client.root, 'node_modules', providerName, 'package.json'), 'utf8').then(value => JSON.parse(value).version).catch(() => client.deps[providerName]);
|
|
89
|
+
const major = Number(installed.match(/^[~^]?(\d+)/)?.[1]);
|
|
90
|
+
if (!major || major < (auth === 'supabase' ? 2 : 9))
|
|
91
|
+
throw Error('This sign-in package needs a newer API. Keep your current sign-in unchanged and use --auth custom, or update it separately.');
|
|
92
|
+
}
|
|
93
|
+
const templates = messengerStarterFiles({ platform, framework: 'next', origin: options.origin, companyName: options.companyName, auth });
|
|
94
|
+
const files = [], replacements = [];
|
|
95
|
+
for (const [name, content] of Object.entries(templates)) {
|
|
96
|
+
if (name.startsWith('client/'))
|
|
97
|
+
files.push({ path: join(client.root, clientBase, 'robono', name), content });
|
|
98
|
+
else if (name.startsWith('server/'))
|
|
99
|
+
files.push({ path: join(server.root, serverBase, 'robono', name), content });
|
|
100
|
+
}
|
|
101
|
+
files.push({ path: join(server.root, serverBase, 'app/api/robono/route.ts'), content: templates['app/api/robono/route.ts'].replace('../../../server/robono', '../../../robono/server/robono') });
|
|
102
|
+
const preview = messengerPreviewTemplates();
|
|
103
|
+
files.push({ path: join(server.root, serverBase, 'app/robono-preview/page.tsx'), content: preview.page }, { path: join(server.root, serverBase, 'robono/client/RobonoPreview.tsx'), content: preview.client });
|
|
104
|
+
const notes = [`Detected ${platform === 'web' ? 'Next.js website' : 'Expo phone app'} and ${auth} sign-in.`,
|
|
105
|
+
'Start your Next.js development server and open /robono-preview for the two-person local example. It returns 404 in production.',
|
|
106
|
+
'Connect your current messaging permission check in robono/server/robono-access.ts. Access stays blocked until then.',
|
|
107
|
+
'Keep provider credentials and the Robono key on the server. The installer never reads or edits your .env files.',
|
|
108
|
+
'Supply your existing sign-in client once to robonoAccounts, then mount ConnectedRobonoChat with your contacts.',
|
|
109
|
+
'Connect website alerts or Apple/Google credentials in Robono → Your app → Message notifications. Chat works before alerts are configured.'];
|
|
110
|
+
const setup = { schema: 1, platform, auth, origin: options.origin, serverProject: relative(client.root, server.root) || '.', checks: { files: 'installed', permissions: 'needs-connection', serverKey: 'needs-server-environment', contacts: 'needs-connection', notifications: 'needs-configuration', liveConnection: 'not-tested' } };
|
|
111
|
+
files.push({ path: join(client.root, '.robono/setup.json'), content: JSON.stringify(setup, null, 2) + '\n' });
|
|
112
|
+
files.push({ path: join(client.root, '.robono/SETUP.md'), content: '# Connect your messenger\n\n' + notes.map(x => '- ' + x).join('\n') + '\n\n## Connect your screen\n' + (auth === 'custom' ? 'Implement RobonoAccounts from client/ConnectedRobonoChat.tsx using your existing sign-in.' : 'Create accounts once: useMemo(() => robonoAccounts(yourExistingSignInClient), [yourExistingSignInClient]).') + ' Then render <ConnectedRobonoChat accounts={accounts} contacts={yourContacts}/> inside an area with a height. Keep it mounted in your signed-in application. It follows account changes; names and avatars update without restarting chat. For Supabase use its client; for Firebase use its Auth instance.\n\n## Allow the right people\nConnect authorize in robono-access.ts to current account eligibility, suspension and conversation permissions. It denies access until implemented. When a friendship changes, call synchronizeFriendship(getMessenger(), friendship) on your server. Use revisions from your database; group membership uses getMessenger().robono.setGroup. Never use an always-allow function.\n\n## Sign-out\nThe messenger account menu clears local content and unregisters notifications before provider sign-out. If your own account menu signs out first, provider events still clear the visible chat. Keep account suspension and relationship revocation synchronized with Robono; an expired session cannot reliably unregister a remote device. Never substitute another account’s token.\n\n## One checklist\nRun robono-messenger-install --check. To include live checks, set ROBONO_MESSENGER_URL, ROBONO_WEBSITE_ORIGIN and ROBONO_TEST_USER_TOKEN locally. Add --json for a shareable report without secrets. No local checker can prove your complete permission rules; test denied accounts and cross-account access too.\n\n## Notifications\nFor website alerts configure your HTTPS origin and chat path. For iPhone, upload your APNs .p8 key, key ID, Team ID and Bundle ID in Robono. For Android, configure google-services.json in your app and upload the Firebase messaging service account privately in Robono. Never put private keys in client code. Build both apps after native dependency changes.\n\n## Local preview\nOpen /robono-preview on your Next.js development server. It uses the actual web messenger with fictional Alice and Bob and local text only. It sends no customer requests and returns 404 in production.\n' });
|
|
113
|
+
files.push({ path: join(server.root, '.robono/server.env.example'), content: templates['.env.example'] });
|
|
114
|
+
const serverPackages = ['@robono/server@^0.20.0', ...(platform === 'expo' ? ['@robono/messaging@^0.4.1', '@robono/messaging-web@^0.6.0'] : []), ...(auth === 'supabase' ? ['@supabase/supabase-js@^2'] : auth === 'firebase' ? ['firebase-admin@^14'] : [])];
|
|
115
|
+
const clientPackages = ['@robono/messaging@^0.4.1', `@robono/messaging-${platform === 'web' ? 'web' : 'react-native'}@^0.6.0`, ...(auth === 'supabase' ? ['@supabase/supabase-js@^2'] : auth === 'firebase' ? ['firebase@^13'] : [])];
|
|
116
|
+
const packageName = (spec) => spec.slice(0, spec.lastIndexOf('@'));
|
|
117
|
+
const keepExistingProvider = (spec, deps) => !['firebase', 'firebase-admin', '@supabase/supabase-js'].includes(packageName(spec)) || !deps[packageName(spec)];
|
|
118
|
+
const serverInstall = serverPackages.filter(spec => keepExistingProvider(spec, server.deps)), clientInstall = clientPackages.filter(spec => keepExistingProvider(spec, client.deps));
|
|
119
|
+
const commands = [];
|
|
120
|
+
if (platform === 'web')
|
|
121
|
+
commands.push(installCommand(client.root, await manager(client.root, client.pkg), [...new Set([...serverInstall, ...clientInstall])]));
|
|
122
|
+
else {
|
|
123
|
+
commands.push(installCommand(server.root, await manager(server.root, server.pkg), serverInstall));
|
|
124
|
+
const cm = await manager(client.root, client.pkg);
|
|
125
|
+
commands.push(installCommand(client.root, cm, clientInstall));
|
|
126
|
+
// Expo selects versions compatible with the customer's installed SDK, not arbitrary latest peers.
|
|
127
|
+
commands.push({ cwd: client.root, command: process.execPath, args: [join(client.root, 'node_modules/expo/bin/cli'), 'install', 'expo-audio', 'expo-image-picker', 'expo-document-picker', 'expo-file-system', 'expo-sharing', 'expo-video', 'expo-crypto', 'react-native-safe-area-context', 'react-native-svg', 'expo-clipboard', 'expo-image-manipulator', 'expo-video-thumbnails', 'react-native-webview', 'expo-blur', 'expo-notifications', 'expo-secure-store', '--' + cm] });
|
|
128
|
+
if (await exists(join(client.root, 'app.config.js')) || await exists(join(client.root, 'app.config.ts')))
|
|
129
|
+
throw Error('Your Expo configuration is code. Use robono-messenger-init and the mobile guide so your existing configuration is preserved.');
|
|
130
|
+
const configPath = join(client.root, 'app.json');
|
|
131
|
+
await safePath(configPath);
|
|
132
|
+
const before = await readFile(configPath, 'utf8'), config = JSON.parse(before);
|
|
133
|
+
if (!config.expo || typeof config.expo !== 'object' || Array.isArray(config.expo))
|
|
134
|
+
throw Error('Expected an expo configuration object in app.json.');
|
|
135
|
+
const plugins = config.expo?.plugins ?? [];
|
|
136
|
+
if (!Array.isArray(plugins))
|
|
137
|
+
throw Error('Expected an Expo plugins array in app.json.');
|
|
138
|
+
const names = plugins.map(p => Array.isArray(p) ? p[0] : p);
|
|
139
|
+
const required = [['expo-audio', { microphonePermission: 'Allow microphone access to record voice messages.', enableBackgroundRecording: false }], ['expo-image-picker', { cameraPermission: 'Allow camera access to take photos and videos for messages.' }], 'expo-notifications', '@robono/messaging-react-native'];
|
|
140
|
+
const added = required.filter(p => !names.includes(Array.isArray(p) ? p[0] : p));
|
|
141
|
+
if (added.length) {
|
|
142
|
+
config.expo.plugins = [...plugins, ...added];
|
|
143
|
+
replacements.push({ path: configPath, before, after: JSON.stringify(config, null, 2) + '\n' });
|
|
144
|
+
files.push({ path: join(client.root, '.robono/app.json.before-install'), content: before });
|
|
145
|
+
}
|
|
146
|
+
notes.push('Added missing Expo plugins; existing permission wording is preserved. Rebuild both iPhone and Android after native dependency changes.');
|
|
147
|
+
}
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
await safePath(file.path);
|
|
150
|
+
if (await exists(file.path))
|
|
151
|
+
throw Error(`Already exists: ${file.path}. Nothing was changed. Use --check for an existing installation.`);
|
|
152
|
+
}
|
|
153
|
+
return { platform, auth, files, replacements, commands, notes };
|
|
154
|
+
}
|
|
155
|
+
/** Preflight all files; exclusive writes and rollback avoid overwriting customer work. */
|
|
156
|
+
export async function applyMessengerInstallation(plan, install = true) {
|
|
157
|
+
for (const file of plan.files) {
|
|
158
|
+
await safePath(file.path);
|
|
159
|
+
if (await exists(file.path))
|
|
160
|
+
throw Error(`Already exists: ${file.path}. Nothing was changed.`);
|
|
161
|
+
}
|
|
162
|
+
for (const file of plan.replacements) {
|
|
163
|
+
await safePath(file.path);
|
|
164
|
+
if (await readFile(file.path, 'utf8') !== file.before)
|
|
165
|
+
throw Error('Project configuration changed after planning. Run the installer again.');
|
|
166
|
+
}
|
|
167
|
+
const written = [], replaced = [];
|
|
168
|
+
try {
|
|
169
|
+
for (const file of plan.files) {
|
|
170
|
+
await mkdir(dirname(file.path), { recursive: true });
|
|
171
|
+
await writeFile(file.path, file.content, { flag: 'wx', mode: 0o600 });
|
|
172
|
+
written.push(file.path);
|
|
173
|
+
}
|
|
174
|
+
for (const file of plan.replacements) {
|
|
175
|
+
if (await readFile(file.path, 'utf8') !== file.before)
|
|
176
|
+
throw Error('Project configuration changed during installation. Existing configuration was preserved.');
|
|
177
|
+
await writeFile(file.path, file.after);
|
|
178
|
+
replaced.push(file);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
for (const file of replaced)
|
|
183
|
+
await writeFile(file.path, file.before);
|
|
184
|
+
for (const file of written)
|
|
185
|
+
await unlink(file);
|
|
186
|
+
throw e;
|
|
187
|
+
}
|
|
188
|
+
if (install)
|
|
189
|
+
for (const command of plan.commands)
|
|
190
|
+
await new Promise((resolve, reject) => {
|
|
191
|
+
const child = spawn(command.command, command.args, { cwd: command.cwd, stdio: 'inherit', shell: false, env: { ...process.env, npm_config_ignore_scripts: 'true', YARN_ENABLE_SCRIPTS: 'false' } });
|
|
192
|
+
child.on('error', () => reject(Error('Package installation could not start. Files are saved; run the printed install command to retry.')));
|
|
193
|
+
child.on('exit', code => code === 0 ? resolve() : reject(Error('Package installation did not finish. Files are saved; fix the package-manager error and retry the printed command.')));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
export async function messengerInstallationChecklist(directory) {
|
|
197
|
+
const root = resolve(directory), file = join(root, '.robono/setup.json');
|
|
198
|
+
if (!await exists(file))
|
|
199
|
+
return [{ name: 'Install files', status: 'needs-action', detail: 'Run robono-messenger-install in your Next.js or Expo project.' }];
|
|
200
|
+
const setup = JSON.parse(await readFile(file, 'utf8'));
|
|
201
|
+
const server = resolve(root, setup.serverProject), base = await appRoot(server);
|
|
202
|
+
const permissionFile = join(server, base, 'robono/server/robono-access.ts');
|
|
203
|
+
const permission = await readFile(permissionFile, 'utf8').catch(() => 'Connect your existing messaging permission');
|
|
204
|
+
const missingPermission = permission.includes('Connect your existing messaging permission');
|
|
205
|
+
const clientBase = setup.platform === 'web' ? base : (await exists(join(root, 'src')) ? 'src' : '');
|
|
206
|
+
const complete = await exists(join(root, clientBase, 'robono/client/ConnectedRobonoChat.tsx')) && await exists(join(server, base, 'app/api/robono/route.ts'));
|
|
207
|
+
return [
|
|
208
|
+
{ name: 'Prepared files', status: complete ? 'passed' : 'failed', detail: `${setup.platform === 'expo' ? 'Expo' : 'Next.js'}; ${setup.auth} sign-in.` },
|
|
209
|
+
{ name: 'Account permissions', status: missingPermission ? 'needs-action' : 'configured', detail: missingPermission ? 'Connect robono-access.ts to your current permission rules. The live checker verifies a test account.' : 'Permission code was edited. Verify allowed and denied accounts; file inspection cannot prove your rules.' },
|
|
210
|
+
{ name: 'Server key', status: 'needs-action', detail: 'Set ROBONO_API_KEY on the server. This command does not read or print secrets.' },
|
|
211
|
+
{ name: 'Contacts', status: 'needs-action', detail: 'Synchronize permitted friendships and group membership; pass names/avatars to ConnectedRobonoChat.' },
|
|
212
|
+
{ name: 'Notifications', status: 'needs-action', detail: setup.platform === 'expo' ? 'Add Apple/Google credentials in Robono and rebuild both apps with the installed Expo plugins.' : 'Connect your website origin and chat path in Robono.' },
|
|
213
|
+
{ name: 'Live connection', status: 'needs-action', detail: 'Set ROBONO_MESSENGER_URL, ROBONO_WEBSITE_ORIGIN and ROBONO_TEST_USER_TOKEN locally, then run robono-messenger-check.' },
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=messenger-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-install.js","sourceRoot":"","sources":["../src/messenger-install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-preview-template.d.ts","sourceRoot":"","sources":["../src/messenger-preview-template.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,wBAAgB,yBAAyB;;;EAqCrC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Development-only, in-memory demonstration using the actual shipped messenger. */
|
|
2
|
+
export function messengerPreviewTemplates() {
|
|
3
|
+
return {
|
|
4
|
+
page: `import {notFound} from 'next/navigation';
|
|
5
|
+
import Preview from '../../robono/client/RobonoPreview';
|
|
6
|
+
export const dynamic='force-dynamic';
|
|
7
|
+
export default function RobonoPreviewPage(){if(process.env.NODE_ENV!=='development')notFound();return <Preview/>;}
|
|
8
|
+
`,
|
|
9
|
+
client: `'use client';
|
|
10
|
+
import React,{useEffect,useRef} from 'react';
|
|
11
|
+
import {mountRobonoMessenger} from '@robono/messaging-web';
|
|
12
|
+
import {defaultMessagingSettings} from '@robono/messaging';
|
|
13
|
+
export default function RobonoPreview(){
|
|
14
|
+
const alice=useRef<HTMLDivElement>(null),bob=useRef<HTMLDivElement>(null);
|
|
15
|
+
useEffect(()=>{
|
|
16
|
+
if(process.env.NODE_ENV!=='development'||!alice.current||!bob.current)return;
|
|
17
|
+
const settings=defaultMessagingSettings();settings.enabled=true;
|
|
18
|
+
const messages:any[]=[];
|
|
19
|
+
const transport=(userId:string):typeof fetch=>async(_request,init)=>{
|
|
20
|
+
const body=JSON.parse(String(init?.body??'{}')),other=userId==='alice'?'bob':'alice';let result:unknown;
|
|
21
|
+
switch(body.operation){
|
|
22
|
+
case 'settings':result={settings};break;
|
|
23
|
+
case 'conversations':result={appearance:settings.appearance,conversations:[{id:'demo-chat',kind:'direct',contactId:other,lastMessage:messages.at(-1)??null,unreadCount:0,canSend:true}],hasMore:false,nextCursor:null};break;
|
|
24
|
+
case 'list':result={settings,messages:[...messages],hasMore:false,nextCursor:null,conversation:{kind:'direct',canSend:true}};break;
|
|
25
|
+
case 'send':{
|
|
26
|
+
let message=messages.find(m=>m.senderId===userId&&m.clientMessageId===body.clientMessageId);
|
|
27
|
+
if(!message){message={...body,id:String(messages.length+1),senderId:userId,createdAt:new Date().toISOString(),readAt:null};messages.push(message);}
|
|
28
|
+
result={message};break;
|
|
29
|
+
}
|
|
30
|
+
case 'read':for(const m of messages)if(m.senderId!==userId)m.readAt=new Date().toISOString();result={ok:true};break;
|
|
31
|
+
default:return Response.json({error:'This local preview demonstrates text only. Connect your test account to try other features.'},{status:400});
|
|
32
|
+
}
|
|
33
|
+
return Response.json(result);
|
|
34
|
+
};
|
|
35
|
+
const mount=(element:HTMLDivElement,userId:string)=>mountRobonoMessenger(element,{companyName:'Example company',userId,getAccessToken:async()=>'local-preview-only',contacts:{alice:{name:'Alice'},bob:{name:'Bob'}},endpoint:'/local-preview-never-sent',fetch:transport(userId),notifications:false,mediaUrl:false,persistence:false});
|
|
36
|
+
const a=mount(alice.current,'alice'),b=mount(bob.current,'bob');return()=>{a.unmount();b.unmount();};
|
|
37
|
+
},[]);
|
|
38
|
+
return <main style={{padding:16,fontFamily:'system-ui'}}><h1>Try a conversation</h1><p>Local preview · Fictional Alice and Bob · Text stays in this page and disappears on reload. This does not test your live connection.</p><p>Open Bob on the left and Alice on the right, then send a message.</p><div style={{display:'flex',flexWrap:'wrap',gap:16}}><section style={{flex:'1 1 320px'}}><h2>Alice</h2><div ref={alice} style={{height:640}}/></section><section style={{flex:'1 1 320px'}}><h2>Bob</h2><div ref={bob} style={{height:640}}/></section></div></main>;
|
|
39
|
+
}
|
|
40
|
+
`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=messenger-preview-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messenger-preview-template.js","sourceRoot":"","sources":["../src/messenger-preview-template.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,UAAU,yBAAyB;IAAG,OAAO;QAClD,IAAI,EAAC;;;;CAIL;QACA,MAAM,EAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BP;KAAC,CAAC;AAAA,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robono/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Provider-neutral server SDK for the Robono Bridge API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"robono",
|
|
@@ -39,13 +39,18 @@
|
|
|
39
39
|
"./starter": {
|
|
40
40
|
"types": "./dist/messenger-init.d.ts",
|
|
41
41
|
"import": "./dist/messenger-init.js"
|
|
42
|
+
},
|
|
43
|
+
"./installer": {
|
|
44
|
+
"types": "./dist/messenger-install.d.ts",
|
|
45
|
+
"import": "./dist/messenger-install.js"
|
|
42
46
|
}
|
|
43
47
|
},
|
|
44
48
|
"bin": {
|
|
45
49
|
"robono-sandbox-test": "dist/sandbox-test.js",
|
|
46
50
|
"robono-adapter-test": "dist/adapter-smoke-test.js",
|
|
47
51
|
"robono-messenger-check": "dist/messenger-setup-test.js",
|
|
48
|
-
"robono-messenger-init": "dist/messenger-init-cli.js"
|
|
52
|
+
"robono-messenger-init": "dist/messenger-init-cli.js",
|
|
53
|
+
"robono-messenger-install": "dist/messenger-install-cli.js"
|
|
49
54
|
},
|
|
50
55
|
"files": [
|
|
51
56
|
"dist",
|